mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
nlmanager: support for bridge vlan and show commands
Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com> Reviewed-by: julien@cumulusnetworks.com Ticket: CM-12199 Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from collections import OrderedDict
|
||||
from ipaddr import IPv4Address, IPv6Address
|
||||
from nlpacket import *
|
||||
from select import select
|
||||
@@ -137,9 +138,8 @@ class NetlinkManager(object):
|
||||
self.tx_socket_allocate()
|
||||
self.tx_socket.sendall(nlpacket.message)
|
||||
|
||||
# If debugs are enabled we will print the contents of the
|
||||
# packet via the decode_packet call...so avoid printing
|
||||
# two messages for one packet.
|
||||
# If nlpacket.debug is True we already printed the following in the
|
||||
# build_message() call...so avoid printing two messages for one packet.
|
||||
if not nlpacket.debug:
|
||||
log.debug("TXed %12s, pid %d, seq %d, %d bytes" %
|
||||
(nlpacket.get_type_string(), nlpacket.pid, nlpacket.seq, nlpacket.length))
|
||||
@@ -223,11 +223,13 @@ class NetlinkManager(object):
|
||||
(pid, nlpacket.pid))
|
||||
data = data[length:]
|
||||
continue
|
||||
|
||||
if seq != nlpacket.seq:
|
||||
log.debug(debug_str + '...we are not interested in this seq %s since ours is %s' %
|
||||
(seq, nlpacket.seq))
|
||||
data = data[length:]
|
||||
continue
|
||||
|
||||
# See if we RXed an ACK for our RTM_GETXXXX
|
||||
if msgtype == NLMSG_DONE:
|
||||
log.debug(debug_str + '...this is an ACK')
|
||||
@@ -243,28 +245,23 @@ class NetlinkManager(object):
|
||||
# 0 is NLE_SUCCESS...everything else is a true error
|
||||
if error_code:
|
||||
error_code_str = msg.error_to_string.get(error_code)
|
||||
|
||||
if error_code_str != 'None':
|
||||
error_str = 'Operation failed with \'%s\' (%s)' % (error_code_str, debug_str)
|
||||
else:
|
||||
error_str = 'Operation failed with code %s (%s)' % (error_code, debug_str)
|
||||
error_str = "Operation failed with netlink error %s '%s', description '%s'" %\
|
||||
(error_code, error_code_str, debug_str)
|
||||
|
||||
if error_code == Error.NLE_NOADDR:
|
||||
raise NetlinkNoAddressError(error_str)
|
||||
|
||||
elif error_code == Error.NLE_INTR:
|
||||
nle_intr_count += 1
|
||||
log.info("%s: RXed NLE_INTR Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR))
|
||||
|
||||
if nle_intr_count >= MAX_ERROR_NLE_INTR:
|
||||
raise NetlinkInterruptedSystemCall(error_str)
|
||||
|
||||
else:
|
||||
if error_code_str == 'None':
|
||||
try:
|
||||
# os.strerror might raise ValueError
|
||||
raise NetlinkError('Operation failed with \'%s\' (%s)' % (os.strerror(error_code), debug_str))
|
||||
except ValueError:
|
||||
pass
|
||||
msg.dump()
|
||||
raise NetlinkError(error_str)
|
||||
|
||||
else:
|
||||
log.debug('%s code NLE_SUCCESS...this is an ACK' % debug_str)
|
||||
return msgs
|
||||
@@ -273,12 +270,6 @@ class NetlinkManager(object):
|
||||
else:
|
||||
nle_intr_count = 0
|
||||
|
||||
# If debugs are enabled we will print the contents of the
|
||||
# packet via the decode_packet call...so avoid printing
|
||||
# two messages for one packet.
|
||||
if not nlpacket.debug:
|
||||
log.debug(debug_str)
|
||||
|
||||
if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK:
|
||||
msg = Link(msgtype, nlpacket.debug)
|
||||
|
||||
@@ -297,6 +288,9 @@ class NetlinkManager(object):
|
||||
msg.decode_packet(length, flags, seq, pid, data)
|
||||
msgs.append(msg)
|
||||
|
||||
if nlpacket.debug:
|
||||
msg.dump()
|
||||
|
||||
data = data[length:]
|
||||
|
||||
def ip_to_afi(self, ip):
|
||||
@@ -539,40 +533,178 @@ class NetlinkManager(object):
|
||||
"""
|
||||
return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
|
||||
|
||||
def _link_bridge_vlan(self, msgtype, ifindex, vlanid, pvid, untagged, master):
|
||||
def vlan_get(self, filter_ifindex=(), filter_vlanid=(), compress_vlans=True):
|
||||
"""
|
||||
Build and TX a RTM_NEWLINK message to add the desired interface
|
||||
filter_ifindex should be a tuple if interface indexes, this is a whitelist filter
|
||||
filter_vlandid should be a tuple if VLAN IDs, this is a whitelist filter
|
||||
"""
|
||||
debug = RTM_GETLINK in self.debug
|
||||
|
||||
if master:
|
||||
flags = 0
|
||||
else:
|
||||
flags = Link.BRIDGE_FLAGS_SELF
|
||||
link = Link(RTM_GETLINK, debug)
|
||||
link.flags = NLM_F_DUMP | NLM_F_REQUEST
|
||||
link.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
|
||||
|
||||
if pvid:
|
||||
vflags = Link.BRIDGE_VLAN_INFO_PVID | Link.BRIDGE_VLAN_INFO_UNTAGGED
|
||||
elif untagged:
|
||||
vflags = Link.BRIDGE_VLAN_INFO_UNTAGGED
|
||||
if compress_vlans:
|
||||
link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN_COMPRESSED)
|
||||
else:
|
||||
vflags = 0
|
||||
link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN)
|
||||
|
||||
link.build_message(self.sequence.next(), self.pid)
|
||||
reply = self.tx_nlpacket_get_response(link)
|
||||
|
||||
iface_vlans = {}
|
||||
|
||||
for msg in reply:
|
||||
if msg.family != socket.AF_BRIDGE:
|
||||
continue
|
||||
|
||||
if filter_ifindex and msg.ifindex not in filter_ifindex:
|
||||
continue
|
||||
|
||||
ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC)
|
||||
|
||||
if not ifla_af_spec:
|
||||
continue
|
||||
|
||||
ifname = msg.get_attribute_value(Link.IFLA_IFNAME)
|
||||
|
||||
'''
|
||||
Example IFLA_AF_SPEC
|
||||
|
||||
20: 0x1c001a00 .... Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC
|
||||
21: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
|
||||
22: 0x00000a00 ....
|
||||
23: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
|
||||
24: 0x00001000 ....
|
||||
25: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
|
||||
26: 0x00001400 ....
|
||||
'''
|
||||
for (x_type, x_value) in ifla_af_spec.iteritems():
|
||||
if x_type == Link.IFLA_BRIDGE_VLAN_INFO:
|
||||
for (vlan_flag, vlan_id) in x_value:
|
||||
if filter_vlanid is None or vlan_id in filter_vlanid:
|
||||
|
||||
if ifname not in iface_vlans:
|
||||
iface_vlans[ifname] = []
|
||||
|
||||
# We store these in the tuple as (vlan, flag) instead (flag, vlan)
|
||||
# so that we can sort the list of tuples
|
||||
iface_vlans[ifname].append((vlan_id, vlan_flag))
|
||||
|
||||
return iface_vlans
|
||||
|
||||
def vlan_show(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
|
||||
|
||||
def vlan_flag_to_string(vlan_flag):
|
||||
flag_str = []
|
||||
if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID:
|
||||
flag_str.append('PVID')
|
||||
|
||||
if vlan_flag & Link.BRIDGE_VLAN_INFO_UNTAGGED:
|
||||
flag_str.append('Egress Untagged')
|
||||
|
||||
return ', '.join(flag_str)
|
||||
|
||||
iface_vlans = self.vlan_get(filter_ifindex, filter_vlanid, compress_vlans)
|
||||
log.debug("iface_vlans:\n%s\n" % pformat(iface_vlans))
|
||||
range_begin_vlan_id = None
|
||||
range_flag = None
|
||||
|
||||
print " Interface VLAN Flags"
|
||||
print " ========== ==== ====="
|
||||
|
||||
for (ifname, vlan_tuples) in sorted(iface_vlans.iteritems()):
|
||||
for (vlan_id, vlan_flag) in sorted(vlan_tuples):
|
||||
|
||||
if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
|
||||
range_begin_vlan_id = vlan_id
|
||||
range_flag = vlan_flag
|
||||
|
||||
elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END:
|
||||
range_flag |= vlan_flag
|
||||
|
||||
if not range_begin_vlan_id:
|
||||
log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id)
|
||||
range_begin_vlan_id = vlan_id
|
||||
|
||||
for x in xrange(range_begin_vlan_id, vlan_id + 1):
|
||||
print " %10s %4d %s" % (ifname, x, vlan_flag_to_string(vlan_flag))
|
||||
ifname = ''
|
||||
|
||||
range_begin_vlan_id = None
|
||||
range_flag = None
|
||||
|
||||
else:
|
||||
print " %10s %4d %s" % (ifname, vlan_id, vlan_flag_to_string(vlan_flag))
|
||||
ifname = ''
|
||||
|
||||
|
||||
def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False):
|
||||
"""
|
||||
iproute2 bridge/vlan.c vlan_modify()
|
||||
"""
|
||||
assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype
|
||||
assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start
|
||||
|
||||
if vlanid_end is None:
|
||||
vlanid_end = vlanid_start
|
||||
|
||||
assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end
|
||||
assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end)
|
||||
|
||||
debug = msgtype in self.debug
|
||||
bridge_flags = 0
|
||||
vlan_info_flags = 0
|
||||
|
||||
link = Link(msgtype, debug)
|
||||
link.flags = NLM_F_REQUEST | NLM_F_ACK
|
||||
link.body = pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0)
|
||||
link.add_attribute(Link.IFLA_AF_SPEC, {
|
||||
Link.IFLA_BRIDGE_FLAGS: flags,
|
||||
Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
|
||||
})
|
||||
|
||||
if bridge_self:
|
||||
bridge_flags |= Link.BRIDGE_FLAGS_SELF
|
||||
|
||||
if bridge_master:
|
||||
bridge_flags |= Link.BRIDGE_FLAGS_MASTER
|
||||
|
||||
if pvid:
|
||||
vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID
|
||||
|
||||
if untagged:
|
||||
vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED
|
||||
|
||||
ifla_af_spec = OrderedDict()
|
||||
|
||||
if bridge_flags:
|
||||
ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags
|
||||
|
||||
# just one VLAN
|
||||
if vlanid_start == vlanid_end:
|
||||
ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ]
|
||||
|
||||
# a range of VLANs
|
||||
else:
|
||||
ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [
|
||||
(vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start),
|
||||
(vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end)
|
||||
]
|
||||
|
||||
link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec)
|
||||
link.build_message(self.sequence.next(), self.pid)
|
||||
return self.tx_nlpacket_get_response(link)
|
||||
|
||||
def link_add_bridge_vlan(self, ifindex, vlanid, pvid=False, untagged=False, master=False):
|
||||
self._link_bridge_vlan(RTM_SETLINK, ifindex, vlanid, pvid, untagged, master)
|
||||
def link_add_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
|
||||
"""
|
||||
Add VLAN(s) to a bridge interface
|
||||
"""
|
||||
bridge_self = False if master else True
|
||||
self.vlan_modify(RTM_SETLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
|
||||
|
||||
def link_del_bridge_vlan(self, ifindex, vlanid, pvid=False, untagged=False, master=False):
|
||||
self._link_bridge_vlan(RTM_DELLINK, ifindex, vlanid, pvid, untagged, master)
|
||||
def link_del_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
|
||||
"""
|
||||
Delete VLAN(s) from a bridge interface
|
||||
"""
|
||||
bridge_self = False if master else True
|
||||
self.vlan_modify(RTM_DELLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
|
||||
|
||||
def link_set_updown(self, ifname, state):
|
||||
"""
|
||||
|
@@ -319,7 +319,7 @@ class AttributeString(Attribute):
|
||||
self.LEN = None
|
||||
|
||||
def encode(self):
|
||||
# some interface names come from JSON unicode strings
|
||||
# some interface names come from JSON as unicode strings
|
||||
# and cannot be packed as is so we must convert them to strings
|
||||
if isinstance(self.value, unicode):
|
||||
self.value = str(self.value)
|
||||
@@ -503,7 +503,22 @@ class AttributeIFLA_AF_SPEC(Attribute):
|
||||
#
|
||||
# Until we cross that bridge though we will keep things nice and simple and
|
||||
# pack everything via a single pack() call.
|
||||
sub_attr_to_add = []
|
||||
|
||||
for (sub_attr_type, sub_attr_value) in self.value.iteritems():
|
||||
|
||||
if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
|
||||
sub_attr_to_add.append((sub_attr_type, sub_attr_value))
|
||||
|
||||
elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
|
||||
for (vlan_flag, vlan_id) in sub_attr_value:
|
||||
sub_attr_to_add.append((sub_attr_type, (vlan_flag, vlan_id)))
|
||||
|
||||
else:
|
||||
self.log.debug('Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type)
|
||||
continue
|
||||
|
||||
for (sub_attr_type, sub_attr_value) in sub_attr_to_add:
|
||||
sub_attr_pack_layout = ['=', 'HH']
|
||||
sub_attr_payload = [0, sub_attr_type]
|
||||
sub_attr_length_index = 0
|
||||
@@ -517,10 +532,6 @@ class AttributeIFLA_AF_SPEC(Attribute):
|
||||
sub_attr_payload.append(sub_attr_value[0])
|
||||
sub_attr_payload.append(sub_attr_value[1])
|
||||
|
||||
else:
|
||||
self.log.debug('Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type)
|
||||
continue
|
||||
|
||||
sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
|
||||
sub_attr_payload[sub_attr_length_index] = sub_attr_length
|
||||
|
||||
@@ -574,7 +585,9 @@ class AttributeIFLA_AF_SPEC(Attribute):
|
||||
self.value[Link.IFLA_BRIDGE_FLAGS] = unpack("=H", sub_attr_data[0:2])[0]
|
||||
|
||||
elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
|
||||
self.value[Link.IFLA_INFO_DATA] = tuple(unpack("=HH", sub_attr_data[0:4]))
|
||||
if Link.IFLA_BRIDGE_VLAN_INFO not in self.value:
|
||||
self.value[Link.IFLA_BRIDGE_VLAN_INFO] = []
|
||||
self.value[Link.IFLA_BRIDGE_VLAN_INFO].append(tuple(unpack("=HH", sub_attr_data[0:4])))
|
||||
|
||||
else:
|
||||
self.log.debug('Add support for decoding IFLA_AF_SPEC sub-attribute type %s (%d), length %d, padded to %d' %
|
||||
@@ -1216,14 +1229,14 @@ class NetlinkPacket(object):
|
||||
|
||||
# Modifiers to GET query
|
||||
if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC):
|
||||
if flags & NLM_F_ROOT:
|
||||
foo.append('NLM_F_ROOT')
|
||||
|
||||
if flags & NLM_F_MATCH:
|
||||
foo.append('NLM_F_MATCH')
|
||||
|
||||
if flags & NLM_F_DUMP:
|
||||
foo.append('NLM_F_DUMP')
|
||||
else:
|
||||
if flags & NLM_F_MATCH:
|
||||
foo.append('NLM_F_MATCH')
|
||||
|
||||
if flags & NLM_F_ROOT:
|
||||
foo.append('NLM_F_ROOT')
|
||||
|
||||
if flags & NLM_F_ATOMIC:
|
||||
foo.append('NLM_F_ATOMIC')
|
||||
@@ -1724,7 +1737,7 @@ class Link(NetlinkPacket):
|
||||
IFLA_AF_SPEC : ('IFLA_AF_SPEC', AttributeIFLA_AF_SPEC),
|
||||
IFLA_GROUP : ('IFLA_GROUP', AttributeFourByteValue),
|
||||
IFLA_NET_NS_FD : ('IFLA_NET_NS_FD', AttributeGeneric),
|
||||
IFLA_EXT_MASK : ('IFLA_EXT_MASK', AttributeGeneric),
|
||||
IFLA_EXT_MASK : ('IFLA_EXT_MASK', AttributeFourByteValue),
|
||||
IFLA_PROMISCUITY : ('IFLA_PROMISCUITY', AttributeGeneric),
|
||||
IFLA_NUM_TX_QUEUES : ('IFLA_NUM_TX_QUEUES', AttributeGeneric),
|
||||
IFLA_NUM_RX_QUEUES : ('IFLA_NUM_RX_QUEUES', AttributeGeneric),
|
||||
@@ -2207,14 +2220,18 @@ class Link(NetlinkPacket):
|
||||
}
|
||||
|
||||
# BRIDGE_VLAN_INFO flags
|
||||
BRIDGE_VLAN_INFO_MASTER = 1
|
||||
BRIDGE_VLAN_INFO_PVID = 2
|
||||
BRIDGE_VLAN_INFO_UNTAGGED = 4
|
||||
BRIDGE_VLAN_INFO_MASTER = 1 << 0
|
||||
BRIDGE_VLAN_INFO_PVID = 1 << 1
|
||||
BRIDGE_VLAN_INFO_UNTAGGED = 1 << 2
|
||||
BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3
|
||||
BRIDGE_VLAN_INFO_RANGE_END = 1 << 4
|
||||
|
||||
bridge_vlan_to_string = {
|
||||
BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER',
|
||||
BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID',
|
||||
BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED'
|
||||
BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER',
|
||||
BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID',
|
||||
BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED',
|
||||
BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN',
|
||||
BRIDGE_VLAN_INFO_RANGE_END : 'BRIDGE_VLAN_INFO_RANGE_END'
|
||||
}
|
||||
|
||||
# Bridge flags
|
||||
@@ -2226,6 +2243,19 @@ class Link(NetlinkPacket):
|
||||
BRIDGE_FLAGS_SELF : 'BRIDGE_FLAGS_SELF'
|
||||
}
|
||||
|
||||
# filters for IFLA_EXT_MASK
|
||||
RTEXT_FILTER_VF = 1 << 0
|
||||
RTEXT_FILTER_BRVLAN = 1 << 1
|
||||
RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2
|
||||
RTEXT_FILTER_SKIP_STATS = 1 << 3
|
||||
|
||||
rtext_to_string = {
|
||||
RTEXT_FILTER_VF : 'RTEXT_FILTER_VF',
|
||||
RTEXT_FILTER_BRVLAN : 'RTEXT_FILTER_BRVLAN',
|
||||
RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED',
|
||||
RTEXT_FILTER_SKIP_STATS : 'RTEXT_FILTER_SKIP_STATS'
|
||||
}
|
||||
|
||||
def __init__(self, msgtype, debug=False, logger=None):
|
||||
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
||||
self.PACK = 'BxHiII'
|
||||
|
Reference in New Issue
Block a user