diff --git a/lib/linux/netlink/route.rb b/lib/linux/netlink/route.rb index f50ee22..c5c3f51 100644 --- a/lib/linux/netlink/route.rb +++ b/lib/linux/netlink/route.rb @@ -13,7 +13,8 @@ module Netlink autoload :VlanHandler, 'linux/netlink/route/vlan_handler' autoload :AddrHandler, 'linux/netlink/route/addr_handler' autoload :RouteHandler, 'linux/netlink/route/route_handler' - + autoload :RuleHandler, 'linux/netlink/route/rule_handler' + # This class formats and receives messages using NETLINK_ROUTE protocol class Socket < NLSocket def initialize(opt={}) @@ -24,22 +25,26 @@ module Netlink def link @link ||= LinkHandler.new(self) end - + # Return a VlanHandler object for manipulating vlans def vlan @vlan ||= VlanHandler.new(self) end - + # Return a AddrHandler object for manipulating addresses def addr @addr ||= AddrHandler.new(self) end - + # Return a RT object for manipulating routes def route @route ||= RouteHandler.new(self) end + def rule + @rule ||= RuleHandler.new(self) + end + # Convert an interface index into name string, or nil if the # index is nil or 0. Raises exception for unknown values. # @@ -52,7 +57,7 @@ module Netlink return nil if index.nil? || index == 0 link[index].ifname end - + # Convert an interface name into index. Returns 0 for nil or empty # string. Otherwise raises an exception for unknown values. def index(name) @@ -69,3 +74,4 @@ module Netlink end end end # module Linux + diff --git a/lib/linux/netlink/route/route_handler.rb b/lib/linux/netlink/route/route_handler.rb index 0992311..8b5302e 100644 --- a/lib/linux/netlink/route/route_handler.rb +++ b/lib/linux/netlink/route/route_handler.rb @@ -8,7 +8,8 @@ module Netlink # struct rtmsg class RT < RtattrMessage - code RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE + code RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE, + RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE field :family, :uchar # Socket::AF_* field :dst_len, :uchar @@ -59,7 +60,7 @@ module Netlink def clear_cache @route = nil end - + # Send message to download the kernel routing table. Either returns an # array of Netlink::RT objects, or yields them to the supplied block. # @@ -98,7 +99,7 @@ module Netlink filter(:oif) { |o,v| o.oif == v } filter(:iif) { |o,v| o.iif == v } end - + # Return the memoized route table, filtered according to # the optional criteria. Examples: # :family => Socket::AF_INET @@ -117,7 +118,7 @@ module Netlink filter_list(@route, filter, &blk) end alias :each :list - + def add(opt) iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, opt) end @@ -125,7 +126,7 @@ module Netlink def change(opt) iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, opt) end - + def replace(opt) iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, opt) end @@ -154,10 +155,10 @@ module Netlink msg.oif = index(msg.oif) if msg.oif.is_a?(String) @rtsocket.cmd RTM_GETROUTE, msg, NLM_F_REQUEST, RTM_NEWROUTE end - + def iproute_modify(code, flags, msg) #:nodoc: msg = RT.new(msg) - + if code != RTM_DELROUTE msg.protocol ||= RTPROT_BOOT msg.type ||= RTN_UNICAST @@ -197,3 +198,4 @@ module Netlink end end end # module Linux + diff --git a/lib/linux/netlink/route/rule_handler.rb b/lib/linux/netlink/route/rule_handler.rb new file mode 100644 index 0000000..ff130e6 --- /dev/null +++ b/lib/linux/netlink/route/rule_handler.rb @@ -0,0 +1,102 @@ +require 'linux/netlink/route' +require 'linux/netlink/route/route_handler' + +module Linux +module Netlink + + module Route + # This class manipulates the kernel routing table by adding and removing + # rules + class RuleHandler < Handler + def clear_cache + @rule = nil + end + + def read_rule(opt=nil, &blk) + @rtsocket.send_request RTM_GETRULE, RT.new(opt), + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST + @rtsocket.receive_until_done(RTM_NEWRULE, &blk) + end + + + Filter = RouteHandler::Filter + + # Return the memoized rule list, filtered according to + # the optional criteria. Examples: + # :family => Socket::AF_INET + # :table => Linux::RT_TABLE_DEFAULT + # :protocol => Linux::RTPROT_STATIC + # :type => Linux::RTN_UNICAST + # :scope => Linux::RT_SCOPE_HOST + # :flags => Linux::RTM_F_NOTIFY + # :noflags => Linux::RTM_F_CLONED + # :oif => "eth0" + # :iif => "eth1" + def list(filter=nil, &blk) + @rule ||= read_rule + filter[:oif] = index(filter[:oif]) if filter && filter.has_key?(:oif) + filter[:iif] = index(filter[:iif]) if filter && filter.has_key?(:iif) + filter_list(@rule, filter, &blk) + end + alias :each :list + + def add(opt) + iproute_modify(RTM_NEWRULE, NLM_F_CREATE|NLM_F_EXCL, opt) + end + + def delete(opt) + iproute_modify(RTM_DELRULE, 0, opt) + end + + # Get route matching given criteria + def get(msg) + msg = RT.new(msg) + raise "Missing :dst" unless msg.dst + msg.iif = index(msg.iif) if msg.iif.is_a?(String) + msg.oif = index(msg.oif) if msg.oif.is_a?(String) + @rtsocket.cmd RTM_GETRULE, msg, NLM_F_REQUEST, RTM_NEWRULE + end + + def iproute_modify(code, flags, msg) #:nodoc: + msg = RT.new(msg) + + if code != RTM_DELRULE + msg.protocol ||= RTPROT_BOOT + msg.type ||= RTN_UNICAST + end + # There is scary code in ip/iproute.c for setting defaults + unless msg.table + msg.table = case msg.type + when RTN_LOCAL, RTN_BROADCAST, RTN_NAT, RTN_ANYCAST + RT_TABLE_LOCAL + else + RT_TABLE_MAIN + end + end + unless msg.scope + msg.scope = (code != RTM_DELRULE) ? RT_SCOPE_UNIVERSE : RT_SCOPE_NOWHERE + case msg.type + when RTN_LOCAL, RTN_NAT + msg.scope = RT_SCOPE_HOST + when RTN_BROADCAST, RTN_MULTICAST, RTN_ANYCAST + msg.scope RT_SCOPE_LINK + when RTN_UNICAST, RTN_UNSPEC + if code == RTM_DELRULE + msg.scope = RT_SCOPE_NOWHERE + elsif !msg.gateway && !msg.multipath + msg.scope = RT_SCOPE_LINK + end + end + end + + msg.iif = index(msg.iif) if msg.iif.is_a?(String) + msg.oif = index(msg.oif) if msg.oif.is_a?(String) + + @rtsocket.cmd code, msg, flags|NLM_F_REQUEST + clear_cache + end + end + end +end +end # module Linux +