From 66791448f7a3dd4e0ca53d45cb86e29c2dc8f509 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 29 Apr 2011 16:33:08 +0100 Subject: [PATCH] Simple grouping by ifindex --- lib/netlink/message.rb | 4 +- lib/netlink/nlsocket.rb | 11 +++-- lib/netlink/rtsocket.rb | 97 +++++++++++++++++++++++++++++++---------- 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/lib/netlink/message.rb b/lib/netlink/message.rb index e68ef95..5957481 100644 --- a/lib/netlink/message.rb +++ b/lib/netlink/message.rb @@ -60,12 +60,12 @@ module Netlink # msg = Foo.new(:bar => 123) # or ("bar" => 123) # msg2 = Foo.new(msg) # msg3 = Foo.new(:qux => 999) # error, no method "qux=" - def initialize(h={}) + def initialize(h=nil) if h.instance_of?(self.class) @attrs = h.to_hash.dup else @attrs = self.class::DEFAULTS.dup - h.each { |k,v| self[k] = v } + h.each { |k,v| self[k] = v } if h end end diff --git a/lib/netlink/nlsocket.rb b/lib/netlink/nlsocket.rb index b908851..7f2d42d 100644 --- a/lib/netlink/nlsocket.rb +++ b/lib/netlink/nlsocket.rb @@ -109,14 +109,15 @@ module Netlink # are accepted. # # (Compare: rtnl_dump_filter_l in lib/libnetlink.c) - def receive_until_done(timeout=@timeout, junk_handler=nil, &blk) #:yields: type, flags, obj + def receive_until_done(expect_type=nil, timeout=@timeout, junk_handler=nil, &blk) #:yields: type, flags, obj res = [] blk ||= lambda { |type, flags, obj| res << obj if obj } - junk_handler ||= lambda { |obj| warn "Discarding junk message #{obj}" } if $VERBOSE + junk_handler ||= lambda { |type, flags, seq, pid, obj| + warn "Discarding junk message (#{type}) #{obj}" } if $VERBOSE loop do receive_response(timeout) do |type, flags, seq, pid, obj| if pid != @pid || seq != @seq - junk_handler[obj] if junk_handler + junk_handler[type, flags, seq, pid, obj] if junk_handler next end case type @@ -125,6 +126,10 @@ module Netlink when NLMSG_ERROR raise "Netlink Error received" end + if expect_type && type != expect_type + junk_handler[type, flags, seq, pid, obj] if junk_handler + next + end blk.call(type, flags, obj) end end diff --git a/lib/netlink/rtsocket.rb b/lib/netlink/rtsocket.rb index e9a573a..4acb337 100644 --- a/lib/netlink/rtsocket.rb +++ b/lib/netlink/rtsocket.rb @@ -8,38 +8,74 @@ module Netlink super(opt.merge(:protocol => Netlink::NETLINK_ROUTE)) end - # List links. Returns an array of Netlink::Link objects - def link_list(opt) + # List links (interfaces). Returns an array of Netlink::Link objects. + # res = nl.link_list + # 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", + # :stats=>#, + # :stats64=>#}>, ...] + def links(opt=nil) send_request RTM_GETLINK, Link.new(opt), NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done + receive_until_done(RTM_NEWLINK) end + # Return a Hash of Netlink::Link objects keyed by interface index, which + # is what the 'routes' and 'addrs' objects point to. + def links_by_index(opt=nil) + res = {} + links(opt).each { |obj| res[obj.index] = obj } + res + end + # List routes. Returns an array of Netlink::Route objects # res = nl.routes(:family => Socket::AF_INET) - # #=> [..., ...] - def route_list(opt) + # 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 routes(opt=nil) send_request RTM_GETROUTE, Route.new(opt), NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done + receive_until_done(RTM_NEWROUTE) + end + + # Return routes as a hash of {index=>[route,route], index=>[route,route]} + def routes_by_oif(opt=nil) + res = routes(opt).group_by { |obj| obj.oif } + res.default = [].freeze + res end - def addr_list(opt) + # List addresses. Return an array of Netlink::Addr objects. + # You will need to use the 'index' to cross reference to the interface. + # res = nl.addrs(:family => Socket::AF_INET) + # p res + # [#2, :prefixlen=>8, :flags=>128, :scope=>254, + # :index=>1, :address=>#, + # :local=>#, :label=>"lo"}>, ...] + def addrs(opt=nil) send_request RTM_GETADDR, Addr.new(opt), NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST - receive_until_done + receive_until_done(RTM_NEWADDR) end - # Add a route - # nl.add_route(:family => Socket::AF_INET, ...) - def route_add(r) - send_request RTM_NEWROUTE, Route.new(r) - # Do we get any success/fail? - end - - # Delete a route - def route_delete(r) - send_request RTM_DELROUTE, Route.new(r) + # Return addresses as a hash of {index=>[addr,addr], index=>[addr,addr]} + def addrs_by_index(opt=nil) + res = addrs(opt).group_by { |obj| obj.index } + res.default = [].freeze + res end end end @@ -47,10 +83,23 @@ end if __FILE__ == $0 require 'pp' nl = Netlink::RTSocket.new - puts "*** routes ***" - pp nl.route_list(:family => Socket::AF_INET) - puts "*** links ***" - pp nl.link_list(:family => Socket::AF_INET) - puts "*** addrs ***" - pp nl.addr_list(:family => Socket::AF_INET) + #puts "*** routes ***" + #pp nl.routes(:family => Socket::AF_INET) + #puts "*** links ***" + #pp nl.links(:family => Socket::AF_INET) + #puts "*** addrs ***" + #pp nl.addrs(:family => Socket::AF_INET) + links = nl.links + addrs = nl.addrs_by_index(:family=>Socket::AF_UNSPEC) + routes = nl.routes_by_oif(:family=>Socket::AF_UNSPEC) + links.each do |link| + puts "#{link.ifname}" + addrs[link.index].each do |addr| + puts " family=#{addr.family} #{addr.address}/#{addr.prefixlen} label=#{addr.label}" + end + routes[link.index].each do |route| + #p route + puts " >> family=#{route.family} #{route.dst}/#{route.dst_len} gw=#{route.gateway}" + end + end end