1
0
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:
Julien Fortin
2016-08-25 14:39:36 -07:00
parent 707aeb7378
commit 26d1e82b2f
2 changed files with 220 additions and 58 deletions

View File

@@ -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):
"""

View File

@@ -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'