diff --git a/examples/route_hi.rb b/examples/route_hi.rb index 8d08a5e..cca0f33 100644 --- a/examples/route_hi.rb +++ b/examples/route_hi.rb @@ -16,7 +16,7 @@ pp rt.links["eth0"] puts "\nAddresses on interface eth0:" pp rt.addrs.list(:index=>"eth0").to_a -puts "\nAll routes in main routing table:" +puts "\nAll v4 routes in main routing table:" pp rt.routes.list(:family=>Socket::AF_INET, :table=>Netlink::RT_TABLE_MAIN).to_a puts "\nDefault route is probably:" diff --git a/lib/netlink/route/addr_handler.rb b/lib/netlink/route/addr_handler.rb index a87c029..1d0aada 100644 --- a/lib/netlink/route/addr_handler.rb +++ b/lib/netlink/route/addr_handler.rb @@ -1,4 +1,5 @@ require 'netlink/route' +require 'netlink/route/handler' module Netlink # struct ifa_cacheinfo @@ -25,28 +26,12 @@ module Netlink end module Route - # This class provides an API for manipulating interfaces and addresses. - # Since we frequently need to map ifname to ifindex, or vice versa, - # we keep a memoized list of interfaces. If the interface list changes, - # you should create a new instance of this object. - class AddrHandler - def initialize(rtsocket = Netlink::Route::Socket.new) - @rtsocket = rtsocket - clear_cache - end - + # This class provides an API for manipulating iaddresses. + class AddrHandler < Handler def clear_cache @addrs = nil end - - def index(v) - @rtsocket.index(v) - end - - def ifname(v) - @rtsocket.ifname(v) - 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. @@ -65,6 +50,14 @@ module Netlink @rtsocket.receive_until_done(RTM_NEWADDR, &blk) end + class Filter < BaseFilter #:nodoc: + filter(:family) { |o,v| o.family == v } + filter(:scope) { |o,v| o.scope == scope } + filter(:flags) { |o,v| (o.flags & v) == v } + filter(:noflags) { |o,v| (o.flags & v) == 0 } + filter(:index) { |o,v| o.index == v } + end + # Iterate over all addresses, or addressees matching the given # criteria. Returns an Enumerator if no block given. # @@ -74,20 +67,10 @@ module Netlink # nl.addrs.list { |x| p x } # addrs_eth0 = nl.addrs.list(:index=>"eth0").to_a # addrs_eth0_v4 = nl.addrs.list(:index=>"eth0", :family=>Socket::AF_INET).to_a - # - # TODO: error on unknown filter conditions def list(filter=nil, &blk) @addrs ||= read_addrs - return @addrs.each(&blk) unless filter - return to_enum(:list, filter) unless block_given? - filter[:index] = index(filter[:index]) if filter.has_key?(:index) - @addrs.each do |o| - yield o if (!filter[:family] || o.family == filter[:family]) && - (!filter[:scope] || o.kind?(filter[:scope])) && - (!filter[:flags] || (o.flags & filter[:flags]) == filter[:flags]) && - (!filter[:noflags] || (o.flags & filter[:noflags]) == 0) && - (!filter[:index] || o.index == filter[:index]) - end + filter[:index] = index(filter[:index]) if filter && filter.has_key?(:index) + do_list(@addrs, filter, &blk) end alias :each :list diff --git a/lib/netlink/route/handler.rb b/lib/netlink/route/handler.rb new file mode 100644 index 0000000..79c0fc8 --- /dev/null +++ b/lib/netlink/route/handler.rb @@ -0,0 +1,46 @@ +module Netlink + module Route + # This class allows objects to be created representing the + # conditions given to the 'list' method + class BaseFilter #:nodoc: + def self.filter name, &blk + define_method "#{name}=" do |matchval| + @conds << [blk, matchval] + end + end + + def initialize(h) + @conds = [] + h.each { |k,v| send "#{k}=", v } + end + + def match(obj) + !@conds.find { |blk,matchval| !blk[obj,matchval] } + end + end + + # Code which is common to all the NETLINK_ROUTE handlers + class Handler + def initialize(rtsocket = Netlink::Route::Socket.new) + @rtsocket = rtsocket + clear_cache + end + + def index(v) + @rtsocket.index(v) + end + + def ifname(v) + @rtsocket.ifname(v) + end + + # Generic listing and filtering + def do_list(data, filter, &blk) + return data.each(&blk) unless filter + return to_enum(:list, filter) unless block_given? + fm = self.class::Filter.new(filter) + data.each { |o| yield o if fm.match(o) } + end + end + end +end diff --git a/lib/netlink/route/link_handler.rb b/lib/netlink/route/link_handler.rb index ddf5530..f8757ff 100644 --- a/lib/netlink/route/link_handler.rb +++ b/lib/netlink/route/link_handler.rb @@ -1,4 +1,5 @@ require 'netlink/route' +require 'netlink/route/handler' module Netlink # struct rtnl_link_stats / rtnl_link_stats64 @@ -119,21 +120,12 @@ module Netlink # Since we frequently need to map ifname to ifindex, or vice versa, # we keep a memoized list of interfaces. If the interface list changes, # you should create a new instance of this object. - class LinkHandler - def initialize(rtsocket = Netlink::Route::Socket.new) - @rtsocket = rtsocket - clear_cache - end - + class LinkHandler < Handler def clear_cache @links = nil @linkmap = nil end - def index(v) - @rtsocket.index(v) - end - # Download a list of links (interfaces). Either returns an array of # Netlink::IFInfo objects, or yields them to the supplied block. # @@ -150,7 +142,15 @@ module Netlink NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST @rtsocket.receive_until_done(RTM_NEWLINK, &blk) end - + + class Filter < BaseFilter #:nodoc: + filter(:type) { |o,v| o.type == v } + filter(:kind) { |o,v| o.kind?(v) } + filter(:flags) { |o,v| (o.flags & v) == v } + filter(:noflags) { |o,v| (o.flags & v) == 0 } + filter(:link) { |o,v| o.link == v } + end + # Iterate over all interfaces, or interfaces matching the given # criteria. Returns an Enumerator if no block given. # @@ -165,16 +165,8 @@ module Netlink # rt.links.list(:link => "lo") # vlan etc attached to this interface def list(filter=nil, &blk) @links ||= read_links - return @links.each(&blk) unless filter - return to_enum(:list, filter) unless block_given? - filter[:link] = index(filter[:link]) if filter.has_key?(:link) - @links.each do |o| - yield o if (!filter[:type] || o.type == filter[:type]) && - (!filter[:kind] || o.kind?(filter[:kind])) && - (!filter[:flags] || (o.flags & filter[:flags]) == filter[:flags]) && - (!filter[:noflags] || (o.flags & filter[:noflags]) == 0) && - (!filter[:link] || o.link == filter[:link]) - end + filter[:link] = index(filter[:link]) if filter && filter.has_key?(:link) + do_list(@links, filter, &blk) end alias :each :list diff --git a/lib/netlink/route/route_handler.rb b/lib/netlink/route/route_handler.rb index b2794fa..7b89576 100644 --- a/lib/netlink/route/route_handler.rb +++ b/lib/netlink/route/route_handler.rb @@ -1,4 +1,5 @@ require 'netlink/route' +require 'netlink/route/handler' module Netlink # struct rta_cacheinfo @@ -47,20 +48,11 @@ module Netlink module Route # This class manipulates the kernel routing table - class RouteHandler - def initialize(rtsocket = Netlink::Route::Socket.new) - @rtsocket = rtsocket - clear_cache - end - + class RouteHandler < Handler def clear_cache @routes = nil end - def index(v) - @rtsocket.index(v) - end - # Send message to download the kernel routing table. Either returns an # array of Netlink::RT objects, or yields them to the supplied block. # @@ -88,6 +80,18 @@ module Netlink @rtsocket.receive_until_done(RTM_NEWROUTE, &blk) end + class Filter < BaseFilter #:nodoc: + filter(:family) { |o,v| o.family == v } + filter(:table) { |o,v| o.table == v } + filter(:protocol) { |o,v| o.protocol == v } + filter(:type) { |o,v| o.type == v } + filter(:scope) { |o,v| o.scope == v } + filter(:flags) { |o,v| (o.flags & v) == v } + filter(:noflags) { |o,v| (o.flags & v) == 0 } + 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 @@ -100,22 +104,10 @@ module Netlink # :oif => "eth0" # :iif => "eth1" def list(filter=nil, &blk) - @routes = read_routes - return @routes.each(&blk) unless filter - return to_enum(:list, filter) unless block_given? - filter[:oif] = index(filter[:oif]) if filter.has_key?(:oif) - filter[:iif] = index(filter[:iif]) if filter.has_key?(:iif) - @routes.each do |o| - yield o if (!filter[:family] || o.family == filter[:family]) && - (!filter[:table] || o.table == filter[:table]) && - (!filter[:protocol] || o.protocol == filter[:protocol]) && - (!filter[:type] || o.scope == filter[:protocol]) && - (!filter[:scope] || o.type == filter[:type]) && - (!filter[:flags] || (o.flags & filter[:flags]) == filter[:flags]) && - (!filter[:noflags] || (o.flags & filter[:noflags]) == 0) && - (!filter[:oif] || o.oif == filter[:oif]) && - (!filter[:iif] || o.iif == filter[:iif]) - end + @routes ||= read_routes + filter[:oif] = index(filter[:oif]) if filter && filter.has_key?(:oif) + filter[:iif] = index(filter[:iif]) if filter && filter.has_key?(:iif) + do_list(@routes, filter, &blk) end alias :each :list diff --git a/lib/netlink/route/vlan_handler.rb b/lib/netlink/route/vlan_handler.rb index a9538ab..05c9a4f 100644 --- a/lib/netlink/route/vlan_handler.rb +++ b/lib/netlink/route/vlan_handler.rb @@ -1,14 +1,11 @@ require 'netlink/route' +require 'netlink/route/handler' module Netlink module Route - class VlanHandler - def initialize(rtsocket = Netlink::Route::Socket.new) - @rtsocket = rtsocket - end - - def index(v) - @rtsocket.index(v) + class VlanHandler < Handler + def clear_cache + # No cache end def list(filter={}, &blk)