diff --git a/examples/route_hi.rb b/examples/route_hi.rb index 9753a9e..074662b 100644 --- a/examples/route_hi.rb +++ b/examples/route_hi.rb @@ -8,7 +8,7 @@ require 'netlink/route' # The data is memoized - that is, it's downloaded from the kernel once # and then manipulated internally. -nl = Netlink::RTSocket.new +nl = Netlink::Route::Socket.new pp nl.link["eth0"] pp nl.addrs["eth0"] diff --git a/examples/route_lo.rb b/examples/route_lo.rb index 52fe645..9501bae 100644 --- a/examples/route_lo.rb +++ b/examples/route_lo.rb @@ -7,7 +7,7 @@ require 'netlink/route' # Example of use of low-level API for NETLINK_ROUTE socket. # Each of these method calls performs a netlink protocol exchange. -nl = Netlink::RTSocket.new +nl = Netlink::Route::Socket.new puts "*** links ***" pp nl.read_links puts "*** addrs ***" diff --git a/lib/netlink/route.rb b/lib/netlink/route.rb index 4a692c8..589d49d 100644 --- a/lib/netlink/route.rb +++ b/lib/netlink/route.rb @@ -136,152 +136,154 @@ module Netlink rtattr :table2, RTA_TABLE, :uint32 # NOTE: table in two places! end - # This is the medium and high-level API using a NETLINK_ROUTE protocol socket - class RTSocket < NLSocket - def initialize(opt={}) - super(opt.merge(:protocol => Netlink::NETLINK_ROUTE)) - clear_cache - end + module Route + # This is the medium and high-level API using a NETLINK_ROUTE protocol socket + class Socket < NLSocket + def initialize(opt={}) + super(opt.merge(:protocol => Netlink::NETLINK_ROUTE)) + clear_cache + end - # Download a list of links (interfaces). Either returns an array of - # Netlink::IFInfo objects, or yields them to the supplied block. - # - # res = nl.read_links - # p res - # [#0, :pad=>0, :type=>772, :index=>1, - # :flags=>65609, :change=>0, :ifname=>"lo", :txqlen=>0, :operstate=>0, - # :linkmode=>0, :mtu=>16436, :qdisc=>"noqueue", :map=>"...", - # :address=>"\x00\x00\x00\x00\x00\x00", :broadcast=>"\x00\x00\x00\x00\x00\x00", - # :stats32=>#, - # :stats64=>#}>, ...] - def read_links(opt=nil, &blk) - send_request RTM_GETLINK, IFInfo.new(opt), - NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done(RTM_NEWLINK, &blk) - end + # Download a list of links (interfaces). Either returns an array of + # Netlink::IFInfo objects, or yields them to the supplied block. + # + # res = nl.read_links + # p res + # [#0, :pad=>0, :type=>772, :index=>1, + # :flags=>65609, :change=>0, :ifname=>"lo", :txqlen=>0, :operstate=>0, + # :linkmode=>0, :mtu=>16436, :qdisc=>"noqueue", :map=>"...", + # :address=>"\x00\x00\x00\x00\x00\x00", :broadcast=>"\x00\x00\x00\x00\x00\x00", + # :stats32=>#, + # :stats64=>#}>, ...] + def read_links(opt=nil, &blk) + send_request RTM_GETLINK, IFInfo.new(opt), + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST + receive_until_done(RTM_NEWLINK, &blk) + end - # Download a list of routes. Either returns an array of - # Netlink::RT objects, or yields them to the supplied block. - # - # A hash of kernel options may be supplied, but you might also have - # to perform your own filtering. e.g. - # rt.read_routes(:family=>Socket::AF_INET) # works - # rt.read_routes(:protocol=>Netlink::RTPROT_STATIC) # ignored - # - # res = nl.read_routes(:family => Socket::AF_INET) - # p res - # [#2, :dst_len=>32, :src_len=>0, :tos=>0, - # :table=>255, :protocol=>2, :scope=>253, :type=>3, :flags=>0, :table2=>255, - # :dst=>#, - # :prefsrc=>#, :oif=>1}>, ...] - # - # Note that not all attributes will always be present. In particular, - # a defaultroute (dst_len=0) misses out the dst address completely: - # - # [#2, :dst_len=>0, :src_len=>0, :tos=>0, - # :table=>254, :protocol=>4, :scope=>0, :type=>1, :flags=>0, :table2=>254, - # :gateway=>#, :oif=>2}>, ...] - def read_routes(opt=nil, &blk) - send_request RTM_GETROUTE, RT.new(opt), - NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done(RTM_NEWROUTE, &blk) - end - - # Download a list of link addresses. Either returns an array of - # Netlink::IFAddr objects, or yields them to the supplied block. - # You will need to use the 'index' to cross reference to the interface. - # - # A hash of kernel options may be supplied, but likely only :family - # is honoured. - # - # res = nl.read_addrs(:family => Socket::AF_INET) - # p res - # [#2, :prefixlen=>8, :flags=>128, :scope=>254, - # :index=>1, :address=>#, - # :local=>#, :label=>"lo"}>, ...] - def read_addrs(opt=nil, &blk) - send_request RTM_GETADDR, IFAddr.new(opt), - NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done(RTM_NEWADDR, &blk) - end + # Download a list of routes. Either returns an array of + # Netlink::RT objects, or yields them to the supplied block. + # + # A hash of kernel options may be supplied, but you might also have + # to perform your own filtering. e.g. + # rt.read_routes(:family=>Socket::AF_INET) # works + # rt.read_routes(:protocol=>Netlink::RTPROT_STATIC) # ignored + # + # res = nl.read_routes(:family => Socket::AF_INET) + # p res + # [#2, :dst_len=>32, :src_len=>0, :tos=>0, + # :table=>255, :protocol=>2, :scope=>253, :type=>3, :flags=>0, :table2=>255, + # :dst=>#, + # :prefsrc=>#, :oif=>1}>, ...] + # + # Note that not all attributes will always be present. In particular, + # a defaultroute (dst_len=0) misses out the dst address completely: + # + # [#2, :dst_len=>0, :src_len=>0, :tos=>0, + # :table=>254, :protocol=>4, :scope=>0, :type=>1, :flags=>0, :table2=>254, + # :gateway=>#, :oif=>2}>, ...] + def read_routes(opt=nil, &blk) + send_request RTM_GETROUTE, RT.new(opt), + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST + receive_until_done(RTM_NEWROUTE, &blk) + end + + # Download a list of link addresses. Either returns an array of + # Netlink::IFAddr objects, or yields them to the supplied block. + # You will need to use the 'index' to cross reference to the interface. + # + # A hash of kernel options may be supplied, but likely only :family + # is honoured. + # + # res = nl.read_addrs(:family => Socket::AF_INET) + # p res + # [#2, :prefixlen=>8, :flags=>128, :scope=>254, + # :index=>1, :address=>#, + # :local=>#, :label=>"lo"}>, ...] + def read_addrs(opt=nil, &blk) + send_request RTM_GETADDR, IFAddr.new(opt), + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST + receive_until_done(RTM_NEWADDR, &blk) + end - # Download a list of addresses, grouped as {index=>[addr,addr], index=>[addr,addr]} - def read_addrs_by_ifindex(opt=nil) - res = read_addrs(opt).group_by { |obj| obj.index } - res.default = [].freeze - res - end + # Download a list of addresses, grouped as {index=>[addr,addr], index=>[addr,addr]} + def read_addrs_by_ifindex(opt=nil) + res = read_addrs(opt).group_by { |obj| obj.index } + res.default = [].freeze + res + end - # Clear the memoization cache - def clear_cache - @links = nil - @link = nil - @addrs = nil - @routes = nil - end - - # Return the memoized interface table as a flat array, suitable for - # iteration. e.g. - # rt.links.each { |link| puts link.ifname } - def links - @links ||= read_links - end + # Clear the memoization cache + def clear_cache + @links = nil + @link = nil + @addrs = nil + @routes = nil + end + + # Return the memoized interface table as a flat array, suitable for + # iteration. e.g. + # rt.links.each { |link| puts link.ifname } + def links + @links ||= read_links + end - # Return the memoized interface table, keyed by both ifname and ifindex. e.g. - # puts rt.link["eth0"].index - # puts rt.link[1].ifname - def link - @link ||= ( - h = {} - links.each { |link| h[link.index] = h[link.ifname] = link } - h - ) - end - - # Return the memoized address table, keyed by interface name and - # address family, containing an array of addresses for each - # interface/family combination. i.e. - # - # # {ifname=>{family=>[addr,addr,...], ...}, ...} - # puts rt.addrs["eth0"][Socket::AF_INET][0].address - # - # If there are no addresses for a particular family then it will - # return a (frozen) empty array, to make iteration eaiser. - def addrs - @addrs ||= ( - h = {} - links.each do |link| - h[link.ifname] = {} - end - read_addrs.each do |addr| - ifname = link[addr.index].ifname - h[ifname] ||= Hash.new(EMPTY_ARRAY) - (h[ifname][addr.family] ||= []) << addr - end - h - ) - end + # Return the memoized interface table, keyed by both ifname and ifindex. e.g. + # puts rt.link["eth0"].index + # puts rt.link[1].ifname + def link + @link ||= ( + h = {} + links.each { |link| h[link.index] = h[link.ifname] = link } + h + ) + end + + # Return the memoized address table, keyed by interface name and + # address family, containing an array of addresses for each + # interface/family combination. i.e. + # + # # {ifname=>{family=>[addr,addr,...], ...}, ...} + # puts rt.addrs["eth0"][Socket::AF_INET][0].address + # + # If there are no addresses for a particular family then it will + # return a (frozen) empty array, to make iteration eaiser. + def addrs + @addrs ||= ( + h = {} + links.each do |link| + h[link.ifname] = {} + end + read_addrs.each do |addr| + ifname = link[addr.index].ifname + h[ifname] ||= Hash.new(EMPTY_ARRAY) + (h[ifname][addr.family] ||= []) << addr + end + h + ) + end - # Return the memoized route table, keyed by address family, containing - # an array of routes for each address family. i.e. - # family combination. i.e. - # - # # {family=>[route,route,...], ...}, ...} - # puts rt.routes[Socket::AF_INET].first.dst - # - # If there are no routes for a particular family then it will - # return a (frozen) empty array. - def routes - @routes ||= ( - h = {} - links.each do |link| - h[link.ifname] = Hash.new(EMPTY_ARRAY) - end - read_routes.each do |route| - (h[route.family] ||= []) << route - end - h - ) + # Return the memoized route table, keyed by address family, containing + # an array of routes for each address family. i.e. + # family combination. i.e. + # + # # {family=>[route,route,...], ...}, ...} + # puts rt.routes[Socket::AF_INET].first.dst + # + # If there are no routes for a particular family then it will + # return a (frozen) empty array. + def routes + @routes ||= ( + h = {} + links.each do |link| + h[link.ifname] = Hash.new(EMPTY_ARRAY) + end + read_routes.each do |route| + (h[route.family] ||= []) << route + end + h + ) + end end end end