Higher-level VLAN API
This commit is contained in:
@@ -6,25 +6,16 @@ require 'pp'
|
|||||||
|
|
||||||
nl = Netlink::Route::Socket.new
|
nl = Netlink::Route::Socket.new
|
||||||
puts "\n*** Before adding VLAN"
|
puts "\n*** Before adding VLAN"
|
||||||
pp nl.if.select { |lnk| lnk.kind?("vlan") }
|
pp nl.if.links(:kind=>"vlan").to_a
|
||||||
|
|
||||||
puts "\n*** After adding VLAN on lo"
|
puts "\n*** After adding VLAN on lo"
|
||||||
begin
|
begin
|
||||||
nl.if.add_link(:link=>"lo",
|
nl.if.add_vlan(:link=>"lo", :vlan_id=>1234)
|
||||||
:linkinfo=>Netlink::LinkInfo.new(
|
|
||||||
:kind=>"vlan", :data=>Netlink::VlanInfo.new(
|
|
||||||
:id=>1234, #:flags => Netlink::VlanFlags.new(:flags=>Netlink::VLAN_FLAG_LOOSE_BINDING, :mask=>0xffffffff)
|
|
||||||
)))
|
|
||||||
rescue Errno::EEXIST
|
rescue Errno::EEXIST
|
||||||
puts "Already present"
|
puts "Already present"
|
||||||
end
|
end
|
||||||
pp nl.if.select { |lnk| lnk.kind?("vlan") }
|
pp nl.if.links(:kind=>"vlan").to_a
|
||||||
|
|
||||||
puts "\n*** After deleting VLANs from lo"
|
puts "\n*** After deleting VLANs from lo"
|
||||||
nl.if.each do |lnk|
|
nl.if.delete_vlan(:link=>"lo", :vlan_id=>1234)
|
||||||
if lnk.kind?('vlan') && nl.if.name(lnk.link) == 'lo'
|
pp nl.if.links(:kind=>"vlan").to_a
|
||||||
nl.if.delete_link(lnk.index)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pp nl.if.select { |lnk| lnk.kind?("vlan") }
|
|
||||||
|
|
||||||
|
@@ -22,11 +22,11 @@ module Netlink
|
|||||||
class IFInfo < RtattrMessage
|
class IFInfo < RtattrMessage
|
||||||
code RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
|
code RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
|
||||||
|
|
||||||
field :family, :uchar # Socket::AF_*
|
field :family, :uchar
|
||||||
field :type, :ushort # ARPHRD_*
|
field :type, :ushort # ARPHRD_*
|
||||||
field :index, :int
|
field :index, :int
|
||||||
field :flags, :uint # IFF_*
|
field :flags, :uint # IFF_*
|
||||||
field :change, :uint, :default=>0xffffffff
|
field :change, :uint, :default=>0xffffffff # flags to change
|
||||||
rtattr :address, IFLA_ADDRESS, :l2addr
|
rtattr :address, IFLA_ADDRESS, :l2addr
|
||||||
rtattr :broadcast, IFLA_BROADCAST, :l2addr
|
rtattr :broadcast, IFLA_BROADCAST, :l2addr
|
||||||
rtattr :ifname, IFLA_IFNAME, :cstring
|
rtattr :ifname, IFLA_IFNAME, :cstring
|
||||||
@@ -142,12 +142,7 @@ module Netlink
|
|||||||
# Since we frequently need to map ifname to ifindex, or vice versa,
|
# Since we frequently need to map ifname to ifindex, or vice versa,
|
||||||
# we keep a memoized list of interfaces. If the interface list changes,
|
# we keep a memoized list of interfaces. If the interface list changes,
|
||||||
# you should create a new instance of this object.
|
# you should create a new instance of this object.
|
||||||
#
|
|
||||||
# The object is Enumerable, so you can iterate over it directly
|
|
||||||
# (which will iterate over the interfaces, but not the addresses)
|
|
||||||
class IFHandler
|
class IFHandler
|
||||||
include Enumerable
|
|
||||||
|
|
||||||
def initialize(nlsocket = Netlink::Route::Socket.new)
|
def initialize(nlsocket = Netlink::Route::Socket.new)
|
||||||
@nlsocket = nlsocket
|
@nlsocket = nlsocket
|
||||||
clear_link_cache
|
clear_link_cache
|
||||||
@@ -205,21 +200,37 @@ module Netlink
|
|||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return memoized list of all interfaces
|
# Iterate over all interfaces, or interfaces matching the given
|
||||||
def links
|
# criteria. Returns an Enumerator if no block given.
|
||||||
|
#
|
||||||
|
# The full interface list is read once and memoized, so
|
||||||
|
# it is efficient to call this method multiple times.
|
||||||
|
#
|
||||||
|
# if.links { |x| p x }
|
||||||
|
# ethers = if.links(:type => Netlink::ARPHRD_ETHER).to_a
|
||||||
|
# vlans = if.links(:kind => "vlan").to_a
|
||||||
|
# if.links(:flags => Netlink::IFF_RUNNING)
|
||||||
|
# if.links(:noflags => Netlink::IFF_POINTOPOINT)
|
||||||
|
# if.links(:link => "lo") # vlan etc attached to this interface
|
||||||
|
def links(filter=nil, &blk)
|
||||||
|
return to_enum(:links, filter) unless block_given?
|
||||||
@links ||= read_links
|
@links ||= read_links
|
||||||
end
|
return @links.each(&blk) unless filter
|
||||||
|
filter[:link] = index(filter[:link]) if filter.has_key?(:link)
|
||||||
# Iterate over all interfaces
|
@links.each do |l|
|
||||||
def each(&blk)
|
yield l if (!filter[:type] || l.type == filter[:type]) &&
|
||||||
links.each(&blk)
|
(!filter[:kind] || l.kind?(filter[:kind])) &&
|
||||||
|
(!filter[:flags] || (l.flags & filter[:flags]) == filter[:flags]) &&
|
||||||
|
(!filter[:noflags] || (l.flags & filter[:noflags]) == 0) &&
|
||||||
|
(!filter[:link] || l.link == filter[:link])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a memoized Hash of interfaces, keyed by both index and name
|
# Return a memoized Hash of interfaces, keyed by both index and name
|
||||||
def linkmap
|
def linkmap
|
||||||
@linkmap ||= (
|
@linkmap ||= (
|
||||||
h = {}
|
h = {}
|
||||||
each { |link| h[link.index] = h[link.ifname] = link }
|
links { |link| h[link.index] = h[link.ifname] = link }
|
||||||
h
|
h
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@@ -246,15 +257,31 @@ module Netlink
|
|||||||
# Convert an interface name into index. Returns 0 for nil or empty
|
# Convert an interface name into index. Returns 0 for nil or empty
|
||||||
# string. Otherwise raises an exception for unknown values.
|
# string. Otherwise raises an exception for unknown values.
|
||||||
def index(name)
|
def index(name)
|
||||||
return 0 if name.nil? || name == EMPTY_STRING
|
case name
|
||||||
self[name].index
|
when Integer
|
||||||
|
name
|
||||||
|
when nil, EMPTY_STRING
|
||||||
|
0
|
||||||
|
else
|
||||||
|
self[name].index
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add an interface
|
# Add an interface (low-level)
|
||||||
#
|
#
|
||||||
# require 'netlink/route'
|
# require 'netlink/route'
|
||||||
# rt = Netlink::Route::Socket.new
|
# rt = Netlink::Route::Socket.new
|
||||||
# rt.if.add_link(:type=>, :index=>"eth0")
|
# rt.if.add_link(
|
||||||
|
# :link=>"lo",
|
||||||
|
# :linkinfo=>Netlink::LinkInfo.new(
|
||||||
|
# :kind=>"vlan",
|
||||||
|
# :data=>Netlink::VlanInfo.new(
|
||||||
|
# :id=>1234,
|
||||||
|
# :flags => Netlink::VlanFlags.new(
|
||||||
|
# :flags=>Netlink::VLAN_FLAG_LOOSE_BINDING,
|
||||||
|
# :mask=>0xffffffff
|
||||||
|
# ))))
|
||||||
|
|
||||||
def add_link(opt)
|
def add_link(opt)
|
||||||
iplink_modify(RTM_NEWLINK, NLM_F_CREATE|NLM_F_EXCL, opt)
|
iplink_modify(RTM_NEWLINK, NLM_F_CREATE|NLM_F_EXCL, opt)
|
||||||
end
|
end
|
||||||
@@ -285,6 +312,8 @@ module Netlink
|
|||||||
if (flags & NLM_F_CREATE) != 0
|
if (flags & NLM_F_CREATE) != 0
|
||||||
raise "Missing :linkinfo" unless msg.linkinfo
|
raise "Missing :linkinfo" unless msg.linkinfo
|
||||||
raise "Missing :kind" unless msg.linkinfo.kind
|
raise "Missing :kind" unless msg.linkinfo.kind
|
||||||
|
else
|
||||||
|
raise "Missing :index" if msg.index.nil? || msg.index == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
msg.index = index(msg.index) if msg.index.is_a?(String)
|
msg.index = index(msg.index) if msg.index.is_a?(String)
|
||||||
@@ -294,6 +323,54 @@ module Netlink
|
|||||||
clear_link_cache
|
clear_link_cache
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Higher-level API to manipulate VLAN interface.
|
||||||
|
# rt.if.add_vlan(
|
||||||
|
# :link=>"lo",
|
||||||
|
# :vlan_id=>1234,
|
||||||
|
# :vlan_flags=>Netlink::VLAN_FLAG_LOOSE_BINDING,
|
||||||
|
# :vlan_mask=>0xffffffff
|
||||||
|
# )
|
||||||
|
def add_vlan(opt)
|
||||||
|
add_link(vlan_options(opt))
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_vlan(opt)
|
||||||
|
change_link(vlan_options(opt))
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_vlan(opt)
|
||||||
|
replace_link(vlan_options(opt))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete vlan given :link and :vlan_id. If you want to delete
|
||||||
|
# by :index then call delete_link instead.
|
||||||
|
def delete_vlan(opt)
|
||||||
|
raise "Missing vlan_id" unless opt[:vlan_id]
|
||||||
|
raise "Missing link" unless opt[:link]
|
||||||
|
link = links(:kind=>"vlan", :link=>opt[:link]).find { |l|
|
||||||
|
l.linkinfo.data &&
|
||||||
|
l.linkinfo.data.id == opt[:vlan_id]
|
||||||
|
}
|
||||||
|
raise Errno::ENODEV unless link
|
||||||
|
delete_link(link.index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def vlan_options(orig) #:nodoc:
|
||||||
|
opt = orig.dup
|
||||||
|
opt[:link] = index(opt.fetch(:link))
|
||||||
|
li = opt[:linkinfo] ||= LinkInfo.new
|
||||||
|
li.kind = "vlan"
|
||||||
|
li.data ||= VlanInfo.new
|
||||||
|
li.data.id = opt.delete(:vlan_id) if opt.has_key?(:vlan_id)
|
||||||
|
if opt.has_key?(:vlan_flags)
|
||||||
|
li.data.flags ||= VlanFlags.new(:flags => opt.delete(:vlan_flags))
|
||||||
|
li.data.flags.mask = opt.delete(:vlan_mask) if opt.has_key?(:vlan_mask)
|
||||||
|
end
|
||||||
|
li.data.egress_qos = opt.delete(:egress_qos) if opt.has_key?(:egress_qos)
|
||||||
|
li.data.ingress_qos = opt.delete(:ingress_qos) if opt.has_key?(:ingress_qos)
|
||||||
|
opt
|
||||||
|
end
|
||||||
|
|
||||||
# Return the memoized address table, keyed by interface name and
|
# Return the memoized address table, keyed by interface name and
|
||||||
# address family, containing an array of addresses for each
|
# address family, containing an array of addresses for each
|
||||||
# interface/family combination. i.e.
|
# interface/family combination. i.e.
|
||||||
@@ -306,7 +383,7 @@ module Netlink
|
|||||||
def addrs
|
def addrs
|
||||||
@addrs ||= (
|
@addrs ||= (
|
||||||
h = {}
|
h = {}
|
||||||
links.each do |link|
|
links do |link|
|
||||||
h[link.ifname] = {}
|
h[link.ifname] = {}
|
||||||
end
|
end
|
||||||
read_addrs.each do |addr|
|
read_addrs.each do |addr|
|
||||||
|
Reference in New Issue
Block a user