mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
Ticket: CM-12302 Reviewed By: Daniel, Roopa, Nikhil G Testing Done: ifupdown2 smoke test With this pretty straight forward, we introduce a new Attribute in nlmanager, so that we have single check and no code redundancy for ifname length. This also prevent loosing time sending a netlink packet for an known error. Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
2721 lines
108 KiB
Python
2721 lines
108 KiB
Python
# Copyright (c) 2009-2013, Exa Networks Limited
|
|
# Copyright (c) 2009-2013, Thomas Mangin
|
|
# Copyright (c) 2015 Cumulus Networks, Inc.
|
|
#
|
|
# All rights reserved.
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# Redistributions of source code must retain the above copyright notice, this
|
|
# list of conditions and the following disclaimer.
|
|
#
|
|
# Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# The names of the Exa Networks Limited, Cumulus Networks, Inc. nor the names
|
|
# of its contributors may be used to endorse or promote products derived from
|
|
# this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import logging
|
|
import struct
|
|
from ipaddr import IPv4Address, IPv6Address
|
|
from binascii import hexlify
|
|
from pprint import pformat
|
|
from socket import AF_INET, AF_INET6, AF_BRIDGE
|
|
from string import printable
|
|
from struct import pack, unpack, calcsize
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# Interface name buffer size #define IFNAMSIZ 16 (kernel source)
|
|
IF_NAME_SIZE = 15 # 15 because python doesn't have \0
|
|
|
|
# Netlink message types
|
|
NLMSG_NOOP = 0x01
|
|
NLMSG_ERROR = 0x02
|
|
NLMSG_DONE = 0x03
|
|
NLMSG_OVERRUN = 0x04
|
|
|
|
RTM_NEWLINK = 0x10 # Create a new network interface
|
|
RTM_DELLINK = 0x11 # Destroy a network interface
|
|
RTM_GETLINK = 0x12 # Retrieve information about a network interface(ifinfomsg)
|
|
RTM_SETLINK = 0x13 #
|
|
|
|
RTM_NEWADDR = 0x14
|
|
RTM_DELADDR = 0x15
|
|
RTM_GETADDR = 0x16
|
|
|
|
RTM_NEWNEIGH = 0x1C
|
|
RTM_DELNEIGH = 0x1D
|
|
RTM_GETNEIGH = 0x1E
|
|
|
|
RTM_NEWROUTE = 0x18
|
|
RTM_DELROUTE = 0x19
|
|
RTM_GETROUTE = 0x1A
|
|
|
|
RTM_NEWQDISC = 0x24
|
|
RTM_DELQDISC = 0x25
|
|
RTM_GETQDISC = 0x26
|
|
|
|
# Netlink message flags
|
|
NLM_F_REQUEST = 0x01 # It is query message.
|
|
NLM_F_MULTI = 0x02 # Multipart message, terminated by NLMSG_DONE
|
|
NLM_F_ACK = 0x04 # Reply with ack, with zero or error code
|
|
NLM_F_ECHO = 0x08 # Echo this query
|
|
|
|
# Modifiers to GET query
|
|
NLM_F_ROOT = 0x100 # specify tree root
|
|
NLM_F_MATCH = 0x200 # return all matching
|
|
NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
|
|
NLM_F_ATOMIC = 0x400 # atomic GET
|
|
|
|
# Modifiers to NEW query
|
|
NLM_F_REPLACE = 0x100 # Override existing
|
|
NLM_F_EXCL = 0x200 # Do not touch, if it exists
|
|
NLM_F_CREATE = 0x400 # Create, if it does not exist
|
|
NLM_F_APPEND = 0x800 # Add to end of list
|
|
|
|
NLA_F_NESTED = 0x8000
|
|
NLA_F_NET_BYTEORDER = 0x4000
|
|
NLA_TYPE_MASK = ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
|
|
|
|
# Groups
|
|
RTMGRP_LINK = 0x1
|
|
RTMGRP_NOTIFY = 0x2
|
|
RTMGRP_NEIGH = 0x4
|
|
RTMGRP_TC = 0x8
|
|
RTMGRP_IPV4_IFADDR = 0x10
|
|
RTMGRP_IPV4_MROUTE = 0x20
|
|
RTMGRP_IPV4_ROUTE = 0x40
|
|
RTMGRP_IPV4_RULE = 0x80
|
|
RTMGRP_IPV6_IFADDR = 0x100
|
|
RTMGRP_IPV6_MROUTE = 0x200
|
|
RTMGRP_IPV6_ROUTE = 0x400
|
|
RTMGRP_IPV6_IFINFO = 0x800
|
|
RTMGRP_DECnet_IFADDR = 0x1000
|
|
RTMGRP_DECnet_ROUTE = 0x4000
|
|
RTMGRP_IPV6_PREFIX = 0x20000
|
|
|
|
RTMGRP_ALL = (RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_NEIGH | RTMGRP_TC |
|
|
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE |
|
|
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO |
|
|
RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE |
|
|
RTMGRP_IPV6_PREFIX)
|
|
|
|
# Colors for logging
|
|
red = 91
|
|
green = 92
|
|
yellow = 93
|
|
blue = 94
|
|
|
|
|
|
def zfilled_hex(value, digits):
|
|
return '0x' + hex(value)[2:].zfill(digits)
|
|
|
|
|
|
def remove_trailing_null(line):
|
|
"""
|
|
Remove the last character if it is a NULL...having that NULL
|
|
causes python to print a garbage character
|
|
"""
|
|
|
|
if ord(line[-1]) == 0:
|
|
line = line[:-1]
|
|
|
|
return line
|
|
|
|
|
|
def mac_int_to_str(mac_int):
|
|
"""
|
|
Return an integer in MAC string format
|
|
"""
|
|
|
|
# [2:] to remove the leading 0x, then fill out to 12 zeroes, then uppercase
|
|
all_caps = hex(int(mac_int))[2:].zfill(12).upper()
|
|
|
|
if all_caps[-1] == 'L':
|
|
all_caps = all_caps[:-1]
|
|
all_caps = all_caps.zfill(12).upper()
|
|
|
|
return "%s.%s.%s" % (all_caps[0:4], all_caps[4:8], all_caps[8:12])
|
|
|
|
|
|
def data_to_color_text(line_number, color, data, extra=''):
|
|
(c1, c2, c3, c4) = unpack('BBBB', data[0:4])
|
|
in_ascii = []
|
|
|
|
for c in (c1, c2, c3, c4):
|
|
char_c = chr(c)
|
|
|
|
if char_c in printable[:-5]:
|
|
in_ascii.append(char_c)
|
|
else:
|
|
in_ascii.append('.')
|
|
|
|
return ' %2d: \033[%dm0x%02x%02x%02x%02x\033[0m %s %s' % (line_number, color, c1, c2, c3, c4, ''.join(in_ascii), extra)
|
|
|
|
|
|
def padded_length(length):
|
|
return int((length + 3) / 4) * 4
|
|
|
|
|
|
class Attribute(object):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
self.atype = atype
|
|
self.string = string
|
|
self.HEADER_PACK = '=HH'
|
|
self.HEADER_LEN = calcsize(self.HEADER_PACK)
|
|
self.PACK = None
|
|
self.LEN = None
|
|
self.value = None
|
|
self.nested = False
|
|
self.net_byteorder = False
|
|
self.log = logger
|
|
|
|
def __str__(self):
|
|
return self.string
|
|
|
|
def set_value(self, value):
|
|
self.value = value
|
|
|
|
def set_nested(self, nested):
|
|
self.nested = nested
|
|
|
|
def set_net_byteorder(self, net_byteorder):
|
|
self.net_byteorder = net_byteorder
|
|
|
|
def pad_bytes_needed(self, length):
|
|
"""
|
|
Return the number of bytes that should be added to align on a 4-byte boundry
|
|
"""
|
|
remainder = length % 4
|
|
|
|
if remainder:
|
|
return 4 - remainder
|
|
|
|
return 0
|
|
|
|
def pad(self, length, raw):
|
|
pad = self.pad_bytes_needed(length)
|
|
|
|
if pad:
|
|
raw += '\0' * pad
|
|
|
|
return raw
|
|
|
|
def encode(self):
|
|
|
|
if not self.LEN:
|
|
raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric')
|
|
|
|
length = self.HEADER_LEN + self.LEN
|
|
attr_type_with_flags = self.atype
|
|
|
|
if self.nested:
|
|
attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED
|
|
|
|
if self.net_byteorder:
|
|
attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER
|
|
|
|
raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value)
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def decode_length_type(self, data):
|
|
"""
|
|
The first two bytes of an attribute are the length, the next two bytes are the type
|
|
"""
|
|
self.data = data
|
|
prev_atype = self.atype
|
|
(data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN])
|
|
self.length = int(data1)
|
|
self.atype = int(data2)
|
|
self.attr_end = padded_length(self.length)
|
|
|
|
self.nested = True if self.atype & NLA_F_NESTED else False
|
|
self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False
|
|
self.atype = self.atype & NLA_TYPE_MASK
|
|
|
|
# Should never happen
|
|
assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype)
|
|
|
|
def dump_first_line(self, dump_buffer, line_number, color):
|
|
"""
|
|
Add the "Length....Type..." line to the dump buffer
|
|
"""
|
|
if self.attr_end == self.length:
|
|
padded_to = ', '
|
|
else:
|
|
padded_to = ' padded to %d, ' % self.attr_end
|
|
|
|
extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \
|
|
(zfilled_hex(self.length, 4), self.length,
|
|
padded_to,
|
|
zfilled_hex(self.atype, 4),
|
|
" (NLA_F_NESTED set)" if self.nested else "",
|
|
" (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "",
|
|
self.atype,
|
|
self)
|
|
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra))
|
|
return line_number + 1
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
|
|
for x in xrange(1, self.attr_end/4):
|
|
start = x * 4
|
|
end = start + 4
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], ''))
|
|
line_number += 1
|
|
|
|
return line_number
|
|
|
|
def get_pretty_value(self):
|
|
return self.value
|
|
|
|
|
|
class AttributeFourByteValue(Attribute):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.PACK = '=L'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
|
|
|
|
try:
|
|
self.value = int(unpack(self.PACK, self.data[4:])[0])
|
|
except struct.error:
|
|
self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
|
|
raise
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
|
|
return line_number + 1
|
|
|
|
|
|
class AttributeString(Attribute):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.PACK = None
|
|
self.LEN = None
|
|
|
|
def encode(self):
|
|
# some interface names come from JSON 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)
|
|
self.PACK = '%ds' % len(self.value)
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
length = self.HEADER_LEN + self.LEN
|
|
raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, self.value)
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
self.PACK = '%ds' % (self.length - 4)
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
try:
|
|
self.value = remove_trailing_null(unpack(self.PACK, self.data[4:self.length])[0])
|
|
except struct.error:
|
|
self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:self.length])))
|
|
raise
|
|
|
|
|
|
class AttributeStringInterfaceName(AttributeString):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
AttributeString.__init__(self, atype, string, logger)
|
|
|
|
def set_value(self, value):
|
|
if value and len(value) > IF_NAME_SIZE:
|
|
raise Exception('interface name exceeds max length of %d' % IF_NAME_SIZE)
|
|
self.value = value
|
|
|
|
|
|
class AttributeIPAddress(Attribute):
|
|
|
|
def __init__(self, atype, string, family, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.value_int = None
|
|
self.value_int_str = None
|
|
self.family = family
|
|
|
|
if self.family == AF_INET:
|
|
self.PACK = '>L'
|
|
|
|
elif self.family == AF_INET6:
|
|
self.PACK = '>QQ'
|
|
|
|
elif self.family == AF_BRIDGE:
|
|
self.PACK = '>L'
|
|
|
|
else:
|
|
raise Exception("%s is not a supported address family" % self.family)
|
|
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
|
|
try:
|
|
if self.family == AF_INET:
|
|
self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
|
|
|
|
elif self.family == AF_INET6:
|
|
(data1, data2) = unpack(self.PACK, self.data[4:])
|
|
self.value = IPv6Address(data1 << 64 | data2)
|
|
|
|
elif self.family == AF_BRIDGE:
|
|
self.value = unpack(self.PACK, self.data[4:])[0]
|
|
|
|
self.value_int = int(self.value)
|
|
self.value_int_str = str(self.value_int)
|
|
|
|
except struct.error:
|
|
self.value = None
|
|
self.value_int = None
|
|
self.value_int_str = None
|
|
self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
|
|
raise
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
|
|
if self.family == AF_INET:
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
|
|
line_number += 1
|
|
|
|
elif self.family == AF_INET6:
|
|
|
|
for x in xrange(1, self.attr_end/4):
|
|
start = x * 4
|
|
end = start + 4
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], self.value))
|
|
line_number += 1
|
|
|
|
elif self.family == AF_BRIDGE:
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
|
|
line_number += 1
|
|
|
|
return line_number
|
|
|
|
|
|
class AttributeMACAddress(Attribute):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.PACK = '>LHxx'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
|
|
try:
|
|
(data1, data2) = unpack(self.PACK, self.data[4:])
|
|
self.value = mac_int_to_str(data1 << 16 | data2)
|
|
|
|
except struct.error:
|
|
self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
|
|
raise
|
|
|
|
def encode(self):
|
|
length = self.HEADER_LEN + self.LEN
|
|
mac_raw = int(self.value.replace('.', '').replace(':', ''), 16)
|
|
raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, mac_raw >> 16, mac_raw & 0x0000FF)
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[8:12], self.value))
|
|
return line_number + 1
|
|
|
|
|
|
class AttributeGeneric(Attribute):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.PACK = None
|
|
self.LEN = None
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
wordcount = (self.attr_end - 4)/4
|
|
self.PACK = '=%dL' % wordcount
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
try:
|
|
self.value = ''.join(map(str, unpack(self.PACK, self.data[4:])))
|
|
except struct.error:
|
|
self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
|
|
raise
|
|
|
|
|
|
class AttributeOneByteValue(AttributeGeneric):
|
|
|
|
def __init__(self, atype, string, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.PACK = '=B'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
|
|
class AttributeIFLA_AF_SPEC(Attribute):
|
|
"""
|
|
value will be a dictionary such as:
|
|
{
|
|
Link.IFLA_BRIDGE_FLAGS: flags,
|
|
Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
|
|
}
|
|
"""
|
|
|
|
def encode(self):
|
|
pack_layout = [self.HEADER_PACK]
|
|
payload = [0, self.atype]
|
|
attr_length_index = 0
|
|
|
|
# For now this assumes that all data will be packed in the native endian
|
|
# order (=). If a field is added that needs to be packed via network
|
|
# order (>) then some smarts will need to be added to split the pack_layout
|
|
# string at the >, split the payload and make the needed pack() calls.
|
|
#
|
|
# Until we cross that bridge though we will keep things nice and simple and
|
|
# pack everything via a single pack() call.
|
|
for (sub_attr_type, sub_attr_value) in self.value.iteritems():
|
|
sub_attr_pack_layout = ['=', 'HH']
|
|
sub_attr_payload = [0, sub_attr_type]
|
|
sub_attr_length_index = 0
|
|
|
|
if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
|
|
sub_attr_pack_layout.append('H')
|
|
sub_attr_payload.append(sub_attr_value)
|
|
|
|
elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
|
|
sub_attr_pack_layout.append('HH')
|
|
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
|
|
|
|
# add padding
|
|
for x in xrange(self.pad_bytes_needed(sub_attr_length)):
|
|
sub_attr_pack_layout.append('x')
|
|
|
|
# The [1:] is to remove the leading = so that when we do the ''.join() later
|
|
# we do not end up with an = in the middle of the pack layout string. There
|
|
# will be an = at the beginning via self.HEADER_PACK
|
|
sub_attr_pack_layout = sub_attr_pack_layout[1:]
|
|
|
|
# Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
|
|
pack_layout.extend(sub_attr_pack_layout)
|
|
payload.extend(sub_attr_payload)
|
|
|
|
pack_layout = ''.join(pack_layout)
|
|
|
|
# Fill in the length field
|
|
length = calcsize(pack_layout)
|
|
payload[attr_length_index] = length
|
|
|
|
raw = pack(pack_layout, *payload)
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def decode(self, parent_msg, data):
|
|
"""
|
|
value is a dictionary such as:
|
|
{
|
|
Link.IFLA_BRIDGE_FLAGS: flags,
|
|
Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
|
|
}
|
|
"""
|
|
self.decode_length_type(data)
|
|
self.value = {}
|
|
|
|
data = self.data[4:]
|
|
|
|
while data:
|
|
(sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
|
|
sub_attr_end = padded_length(sub_attr_length)
|
|
|
|
if not sub_attr_length:
|
|
self.log.error('parsed a zero length sub-attr')
|
|
return
|
|
|
|
sub_attr_data = data[4:sub_attr_end]
|
|
|
|
if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
|
|
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]))
|
|
|
|
else:
|
|
self.log.debug('Add support for decoding IFLA_AF_SPEC sub-attribute type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_bridge_af_spec_to_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
|
|
|
|
data = data[sub_attr_end:]
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
extra = ''
|
|
|
|
next_sub_attr_line = 0
|
|
sub_attr_line = True
|
|
|
|
for x in xrange(1, self.attr_end/4):
|
|
start = x * 4
|
|
end = start + 4
|
|
|
|
if line_number == next_sub_attr_line:
|
|
sub_attr_line = True
|
|
|
|
if sub_attr_line:
|
|
sub_attr_line = False
|
|
|
|
(sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
|
|
sub_attr_end = padded_length(sub_attr_length)
|
|
|
|
next_sub_attr_line = line_number + (sub_attr_end/4)
|
|
|
|
if sub_attr_end == sub_attr_length:
|
|
padded_to = ', '
|
|
else:
|
|
padded_to = ' padded to %d, ' % sub_attr_end
|
|
|
|
extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
|
|
(zfilled_hex(sub_attr_length, 4), sub_attr_length,
|
|
padded_to,
|
|
zfilled_hex(sub_attr_type, 4), sub_attr_type,
|
|
Link.ifla_bridge_af_spec_to_string.get(sub_attr_type))
|
|
else:
|
|
extra = ''
|
|
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
|
|
line_number += 1
|
|
|
|
return line_number
|
|
|
|
def get_pretty_value(self):
|
|
# We do this so we can print a more human readable dictionary
|
|
# with the names of the nested keys instead of their numbers
|
|
value_pretty = {}
|
|
|
|
for (sub_key, sub_value) in self.value.iteritems():
|
|
sub_key_pretty = "(%2d) % s" % (sub_key, Link.ifla_bridge_af_spec_to_string.get(sub_key))
|
|
value_pretty[sub_key_pretty] = sub_value
|
|
|
|
return value_pretty
|
|
|
|
|
|
|
|
class AttributeRTA_MULTIPATH(Attribute):
|
|
"""
|
|
/* RTA_MULTIPATH --- array of struct rtnexthop.
|
|
*
|
|
* "struct rtnexthop" describes all necessary nexthop information,
|
|
* i.e. parameters of path to a destination via this nexthop.
|
|
*
|
|
* At the moment it is impossible to set different prefsrc, mtu, window
|
|
* and rtt for different paths from multipath.
|
|
*/
|
|
|
|
struct rtnexthop {
|
|
unsigned short rtnh_len;
|
|
unsigned char rtnh_flags;
|
|
unsigned char rtnh_hops;
|
|
int rtnh_ifindex;
|
|
};
|
|
"""
|
|
|
|
def __init__(self, atype, string, family, logger):
|
|
Attribute.__init__(self, atype, string, logger)
|
|
self.family = family
|
|
self.PACK = None
|
|
self.LEN = None
|
|
self.RTNH_PACK = '=HBBL' # rtnh_len, flags, hops, ifindex
|
|
self.RTNH_LEN = calcsize(self.RTNH_PACK)
|
|
self.IPV4_LEN = 4
|
|
self.IPV6_LEN = 16
|
|
|
|
def encode(self):
|
|
|
|
# Calculate the length
|
|
if self.family == AF_INET:
|
|
ip_len = self.IPV4_LEN
|
|
elif self.family == AF_INET6:
|
|
ip_len = self.IPV6_LEN
|
|
|
|
# Attribute header
|
|
length = self.HEADER_LEN + ((self.RTNH_LEN + self.HEADER_LEN + ip_len) * len(self.value))
|
|
raw = pack(self.HEADER_PACK, length, self.atype)
|
|
|
|
rtnh_flags = 0
|
|
rtnh_hops = 0
|
|
rtnh_len = self.RTNH_LEN + self.HEADER_LEN + ip_len
|
|
|
|
for (nexthop, rtnh_ifindex) in self.value:
|
|
|
|
# rtnh structure
|
|
raw += pack(self.RTNH_PACK, rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex)
|
|
|
|
# Gateway
|
|
raw += pack(self.HEADER_PACK, self.HEADER_LEN + ip_len, Route.RTA_GATEWAY)
|
|
|
|
if self.family == AF_INET:
|
|
raw += pack('>L', nexthop)
|
|
elif self.family == AF_INET6:
|
|
raw += pack('>QQ', nexthop >> 64, nexthop & 0x0000000000000000FFFFFFFFFFFFFFFF)
|
|
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def decode(self, parent_msg, data):
|
|
self.decode_length_type(data)
|
|
self.value = []
|
|
|
|
data = self.data[4:]
|
|
|
|
while data:
|
|
(rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex) = unpack(self.RTNH_PACK, data[:self.RTNH_LEN])
|
|
data = data[self.RTNH_LEN:]
|
|
|
|
(attr_type, attr_length) = unpack(self.HEADER_PACK, self.data[:self.HEADER_LEN])
|
|
data = data[self.HEADER_LEN:]
|
|
|
|
if self.family == AF_INET:
|
|
nexthop = IPv4Address(unpack('>L', data[:self.IPV4_LEN])[0])
|
|
self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
|
|
data = data[self.IPV4_LEN:]
|
|
|
|
elif self.family == AF_INET6:
|
|
(data1, data2) = unpack('>QQ', data[:self.IPV6_LEN])
|
|
nexthop = IPv6Address(data1 << 64 | data2)
|
|
self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
|
|
data = data[self.IPV6_LEN:]
|
|
|
|
self.value = tuple(self.value)
|
|
|
|
|
|
class AttributeIFLA_LINKINFO(Attribute):
|
|
"""
|
|
value is a dictionary such as:
|
|
|
|
{
|
|
Link.IFLA_INFO_KIND : 'vlan',
|
|
Link.IFLA_INFO_DATA : {
|
|
Link.IFLA_VLAN_ID : vlanid,
|
|
}
|
|
}
|
|
"""
|
|
def encode(self):
|
|
pack_layout = [self.HEADER_PACK]
|
|
payload = [0, self.atype]
|
|
attr_length_index = 0
|
|
|
|
kind = self.value[Link.IFLA_INFO_KIND]
|
|
|
|
if kind not in ('vlan', 'macvlan', 'vxlan'):
|
|
raise Exception('Unsupported IFLA_INFO_KIND %s' % kind)
|
|
|
|
# For now this assumes that all data will be packed in the native endian
|
|
# order (=). If a field is added that needs to be packed via network
|
|
# order (>) then some smarts will need to be added to split the pack_layout
|
|
# string at the >, split the payload and make the needed pack() calls.
|
|
#
|
|
# Until we cross that bridge though we will keep things nice and simple and
|
|
# pack everything via a single pack() call.
|
|
for (sub_attr_type, sub_attr_value) in self.value.iteritems():
|
|
sub_attr_pack_layout = ['=', 'HH']
|
|
sub_attr_payload = [0, sub_attr_type]
|
|
sub_attr_length_index = 0
|
|
|
|
if sub_attr_type == Link.IFLA_INFO_KIND:
|
|
sub_attr_pack_layout.append('%ds' % len(sub_attr_value))
|
|
sub_attr_payload.append(sub_attr_value)
|
|
|
|
elif sub_attr_type == Link.IFLA_INFO_DATA:
|
|
|
|
for (info_data_type, info_data_value) in sub_attr_value.iteritems():
|
|
|
|
if kind == 'vlan':
|
|
if info_data_type == Link.IFLA_VLAN_ID:
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(6) # length
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
# The vlan-id
|
|
sub_attr_pack_layout.append('H')
|
|
sub_attr_payload.append(info_data_value)
|
|
|
|
# pad 2 bytes
|
|
sub_attr_pack_layout.extend('xx')
|
|
|
|
else:
|
|
self.log.debug('Add support for encoding IFLA_INFO_DATA vlan sub-attribute type %d' % info_data_type)
|
|
|
|
elif kind == 'macvlan':
|
|
if info_data_type == Link.IFLA_MACVLAN_MODE:
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(8) # length
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
# macvlan mode
|
|
sub_attr_pack_layout.append('L')
|
|
sub_attr_payload.append(info_data_value)
|
|
|
|
else:
|
|
self.log.debug('Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type)
|
|
|
|
elif kind == 'vxlan':
|
|
if info_data_type in (Link.IFLA_VXLAN_ID,
|
|
Link.IFLA_VXLAN_LINK,
|
|
Link.IFLA_VXLAN_AGEING,
|
|
Link.IFLA_VXLAN_LIMIT,
|
|
Link.IFLA_VXLAN_PORT_RANGE):
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(8) # length
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
sub_attr_pack_layout.append('L')
|
|
sub_attr_payload.append(info_data_value)
|
|
|
|
elif info_data_type in (Link.IFLA_VXLAN_GROUP,
|
|
Link.IFLA_VXLAN_LOCAL):
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(8) # length
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
sub_attr_pack_layout.append('L')
|
|
|
|
reorder = unpack('<L', IPv4Address(info_data_value).packed)[0]
|
|
sub_attr_payload.append(IPv4Address(reorder))
|
|
|
|
elif info_data_type in (Link.IFLA_VXLAN_PORT,):
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(6)
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
sub_attr_pack_layout.append('H')
|
|
|
|
# byte swap
|
|
swaped = pack(">H", info_data_value)
|
|
sub_attr_payload.append(unpack("<H", swaped)[0])
|
|
|
|
sub_attr_pack_layout.extend('xx')
|
|
|
|
elif info_data_type in (Link.IFLA_VXLAN_TTL,
|
|
Link.IFLA_VXLAN_TOS,
|
|
Link.IFLA_VXLAN_LEARNING,
|
|
Link.IFLA_VXLAN_PROXY,
|
|
Link.IFLA_VXLAN_RSC,
|
|
Link.IFLA_VXLAN_L2MISS,
|
|
Link.IFLA_VXLAN_L3MISS,
|
|
Link.IFLA_VXLAN_UDP_CSUM,
|
|
Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
|
|
Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
|
|
Link.IFLA_VXLAN_REMCSUM_TX,
|
|
Link.IFLA_VXLAN_REMCSUM_RX,
|
|
Link.IFLA_VXLAN_REPLICATION_TYPE):
|
|
sub_attr_pack_layout.append('HH')
|
|
sub_attr_payload.append(6)
|
|
sub_attr_payload.append(info_data_type)
|
|
|
|
sub_attr_pack_layout.append('B')
|
|
sub_attr_payload.append(info_data_value)
|
|
sub_attr_pack_layout.extend('xxx')
|
|
|
|
else:
|
|
self.log.debug('Add support for encoding IFLA_INFO_DATA vxlan sub-attribute type %d' % info_data_type)
|
|
|
|
else:
|
|
self.log.debug('Add support for encoding IFLA_LINKINFO 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
|
|
|
|
# add padding
|
|
for x in xrange(self.pad_bytes_needed(sub_attr_length)):
|
|
sub_attr_pack_layout.append('x')
|
|
|
|
# The [1:] is to remove the leading = so that when we do the ''.join() later
|
|
# we do not end up with an = in the middle of the pack layout string. There
|
|
# will be an = at the beginning via self.HEADER_PACK
|
|
sub_attr_pack_layout = sub_attr_pack_layout[1:]
|
|
|
|
# Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
|
|
pack_layout.extend(sub_attr_pack_layout)
|
|
payload.extend(sub_attr_payload)
|
|
|
|
pack_layout = ''.join(pack_layout)
|
|
|
|
# Fill in the length field
|
|
length = calcsize(pack_layout)
|
|
payload[attr_length_index] = length
|
|
|
|
raw = pack(pack_layout, *payload)
|
|
raw = self.pad(length, raw)
|
|
return raw
|
|
|
|
def decode(self, parent_msg, data):
|
|
"""
|
|
value is a dictionary such as:
|
|
|
|
{
|
|
Link.IFLA_INFO_KIND : 'vlan',
|
|
Link.IFLA_INFO_DATA : {
|
|
Link.IFLA_VLAN_ID : vlanid,
|
|
}
|
|
}
|
|
"""
|
|
self.decode_length_type(data)
|
|
self.value = {}
|
|
|
|
data = self.data[4:]
|
|
|
|
# IFLA_MACVLAN_MODE and IFLA_VLAN_ID both have a value of 1 and both are
|
|
# valid IFLA_INFO_DATA entries :( The sender must TX IFLA_INFO_KIND
|
|
# first in order for us to know if "1" is IFLA_MACVLAN_MODE vs IFLA_VLAN_ID.
|
|
while data:
|
|
(sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
|
|
sub_attr_end = padded_length(sub_attr_length)
|
|
|
|
if not sub_attr_length:
|
|
self.log.error('parsed a zero length sub-attr')
|
|
return
|
|
|
|
if sub_attr_type == Link.IFLA_INFO_KIND:
|
|
self.value[Link.IFLA_INFO_KIND] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
|
|
|
|
elif sub_attr_type == Link.IFLA_INFO_DATA:
|
|
|
|
sub_attr_data = data[4:sub_attr_end]
|
|
self.value[Link.IFLA_INFO_DATA] = {}
|
|
|
|
while sub_attr_data:
|
|
(info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
|
|
info_data_end = padded_length(info_data_length)
|
|
# self.log.info('sub attr length %d, end %d, type %d' % (info_data_length, info_data_end, info_data_type))
|
|
|
|
if not sub_attr_data:
|
|
self.log.error('RXed zero length sub-attribute')
|
|
break
|
|
|
|
if Link.IFLA_INFO_KIND not in self.value:
|
|
self.log.warning('IFLA_INFO_KIND is not known...we cannot parse IFLA_INFO_DATA')
|
|
|
|
elif self.value[Link.IFLA_INFO_KIND] == 'vlan':
|
|
if info_data_type == Link.IFLA_VLAN_ID:
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
|
|
else:
|
|
self.log.debug('Add support for decoding IFLA_INFO_KIND vlan type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_vlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
|
|
|
|
elif self.value[Link.IFLA_INFO_KIND] == 'macvlan':
|
|
if info_data_type == Link.IFLA_MACVLAN_MODE:
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
|
|
else:
|
|
self.log.debug('Add support for decoding IFLA_INFO_KIND macvlan type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_macvlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
|
|
|
|
elif self.value[Link.IFLA_INFO_KIND] == 'vxlan':
|
|
|
|
# IPv4Address
|
|
if info_data_type in (Link.IFLA_VXLAN_GROUP,
|
|
Link.IFLA_VXLAN_LOCAL):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack('>L', sub_attr_data[4:8])[0])
|
|
|
|
# 4-byte int
|
|
elif info_data_type in (Link.IFLA_VXLAN_ID,
|
|
Link.IFLA_VXLAN_LINK,
|
|
Link.IFLA_VXLAN_AGEING,
|
|
Link.IFLA_VXLAN_LIMIT,
|
|
Link.IFLA_VXLAN_PORT_RANGE):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
|
|
|
|
# 2-byte int
|
|
elif info_data_type in (Link.IFLA_VXLAN_PORT, ):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
|
|
|
|
# 1-byte int
|
|
elif info_data_type in (Link.IFLA_VXLAN_TTL,
|
|
Link.IFLA_VXLAN_TOS,
|
|
Link.IFLA_VXLAN_LEARNING,
|
|
Link.IFLA_VXLAN_PROXY,
|
|
Link.IFLA_VXLAN_RSC,
|
|
Link.IFLA_VXLAN_L2MISS,
|
|
Link.IFLA_VXLAN_L3MISS,
|
|
Link.IFLA_VXLAN_UDP_CSUM,
|
|
Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
|
|
Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
|
|
Link.IFLA_VXLAN_REMCSUM_TX,
|
|
Link.IFLA_VXLAN_REMCSUM_RX,
|
|
Link.IFLA_VXLAN_REPLICATION_TYPE):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
|
|
|
|
else:
|
|
# sub_attr_end = padded_length(sub_attr_length)
|
|
self.log.debug('Add support for decoding IFLA_INFO_KIND vxlan type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_vxlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
|
|
|
|
elif self.value[Link.IFLA_INFO_KIND] == 'bond':
|
|
self.log.debug('Add support for decoding IFLA_INFO_KIND bond type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_bond_string(info_data_type), info_data_type, info_data_length, info_data_end))
|
|
|
|
elif self.value[Link.IFLA_INFO_KIND] == 'bridge':
|
|
|
|
if info_data_type in (Link.IFLA_BRPORT_STATE,
|
|
Link.IFLA_BRPORT_PRIORITY,
|
|
Link.IFLA_BRPORT_COST):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
|
|
|
|
elif info_data_type in (Link.IFLA_BRPORT_FAST_LEAVE, ):
|
|
self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
|
|
|
|
else:
|
|
self.log.debug('Add support for decoding IFLA_INFO_KIND bridge type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_bridge_string(info_data_type), info_data_type, info_data_length, info_data_end))
|
|
|
|
else:
|
|
self.log.debug("Add support for decoding IFLA_INFO_KIND %s (%d), length %d, padded to %d" %
|
|
(self.value[Link.IFLA_INFO_KIND], info_data_type, info_data_length, info_data_end))
|
|
|
|
sub_attr_data = sub_attr_data[info_data_end:]
|
|
|
|
elif sub_attr_type == Link.IFLA_INFO_SLAVE_KIND:
|
|
self.value[Link.IFLA_INFO_SLAVE_KIND] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
|
|
|
|
else:
|
|
self.log.debug('Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' %
|
|
(parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
|
|
|
|
data = data[sub_attr_end:]
|
|
|
|
# self.log.info('IFLA_LINKINFO values %s' % pformat(self.value))
|
|
|
|
def dump_lines(self, dump_buffer, line_number, color):
|
|
line_number = self.dump_first_line(dump_buffer, line_number, color)
|
|
extra = ''
|
|
|
|
next_sub_attr_line = 0
|
|
sub_attr_line = True
|
|
|
|
for x in xrange(1, self.attr_end/4):
|
|
start = x * 4
|
|
end = start + 4
|
|
|
|
if line_number == next_sub_attr_line:
|
|
sub_attr_line = True
|
|
|
|
if sub_attr_line:
|
|
sub_attr_line = False
|
|
|
|
(sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
|
|
sub_attr_end = padded_length(sub_attr_length)
|
|
|
|
next_sub_attr_line = line_number + (sub_attr_end/4)
|
|
|
|
if sub_attr_end == sub_attr_length:
|
|
padded_to = ', '
|
|
else:
|
|
padded_to = ' padded to %d, ' % sub_attr_end
|
|
|
|
extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
|
|
(zfilled_hex(sub_attr_length, 4), sub_attr_length,
|
|
padded_to,
|
|
zfilled_hex(sub_attr_type, 4), sub_attr_type,
|
|
Link.ifla_info_to_string.get(sub_attr_type))
|
|
else:
|
|
extra = ''
|
|
|
|
dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
|
|
line_number += 1
|
|
|
|
return line_number
|
|
|
|
def get_pretty_value(self):
|
|
value_pretty = self.value
|
|
ifla_info_kind = self.value.get(Link.IFLA_INFO_KIND)
|
|
|
|
# We do this so we can print a more human readable dictionary
|
|
# with the names of the nested keys instead of their numbers
|
|
|
|
# Most of these are placeholders...we need to add support
|
|
# for more human readable dictionaries for bond, bridge, etc
|
|
if ifla_info_kind == 'bond':
|
|
pass
|
|
|
|
elif ifla_info_kind == 'bridge':
|
|
pass
|
|
|
|
elif ifla_info_kind == 'macvlan':
|
|
pass
|
|
|
|
elif ifla_info_kind == 'vlan':
|
|
pass
|
|
|
|
elif ifla_info_kind == 'vxlan':
|
|
value_pretty = {}
|
|
|
|
for (sub_key, sub_value) in self.value.iteritems():
|
|
sub_key_pretty = "(%2d) % s" % (sub_key, Link.ifla_info_to_string[sub_key])
|
|
sub_value_pretty = sub_value
|
|
|
|
if sub_key == Link.IFLA_INFO_DATA:
|
|
sub_value_pretty = {}
|
|
|
|
for (sub_sub_key, sub_sub_value) in sub_value.iteritems():
|
|
sub_sub_key_pretty = "(%2d) %s" % (sub_sub_key, Link.ifla_vxlan_to_string[sub_sub_key])
|
|
sub_value_pretty[sub_sub_key_pretty] = sub_sub_value
|
|
|
|
value_pretty[sub_key_pretty] = sub_value_pretty
|
|
|
|
return value_pretty
|
|
|
|
|
|
class NetlinkPacket(object):
|
|
"""
|
|
Netlink Header
|
|
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Length |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Type | Flags |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Sequence Number |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Process ID (PID) |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
"""
|
|
|
|
header_PACK = 'IHHII'
|
|
header_LEN = calcsize(header_PACK)
|
|
|
|
# Netlink packet types
|
|
# /usr/include/linux/rtnetlink.h
|
|
type_to_string = {
|
|
NLMSG_NOOP : 'NLMSG_NOOP',
|
|
NLMSG_ERROR : 'NLMSG_ERROR',
|
|
NLMSG_DONE : 'NLMSG_DONE',
|
|
NLMSG_OVERRUN : 'NLMSG_OVERRUN',
|
|
RTM_NEWLINK : 'RTM_NEWLINK',
|
|
RTM_DELLINK : 'RTM_DELLINK',
|
|
RTM_GETLINK : 'RTM_GETLINK',
|
|
RTM_SETLINK : 'RTM_SETLINK',
|
|
RTM_NEWADDR : 'RTM_NEWADDR',
|
|
RTM_DELADDR : 'RTM_DELADDR',
|
|
RTM_GETADDR : 'RTM_GETADDR',
|
|
RTM_NEWNEIGH : 'RTM_NEWNEIGH',
|
|
RTM_DELNEIGH : 'RTM_DELNEIGH',
|
|
RTM_GETNEIGH : 'RTM_GETNEIGH',
|
|
RTM_NEWROUTE : 'RTM_NEWROUTE',
|
|
RTM_DELROUTE : 'RTM_DELROUTE',
|
|
RTM_GETROUTE : 'RTM_GETROUTE',
|
|
RTM_NEWQDISC : 'RTM_NEWQDISC',
|
|
RTM_DELQDISC : 'RTM_DELQDISC',
|
|
RTM_GETQDISC : 'RTM_GETQDISC'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug, owner_logger=None):
|
|
self.msgtype = msgtype
|
|
self.attributes = {}
|
|
self.dump_buffer = ['']
|
|
self.line_number = 1
|
|
self.debug = debug
|
|
self.message = None
|
|
|
|
if owner_logger:
|
|
self.log = owner_logger
|
|
else:
|
|
self.log = log
|
|
|
|
def __str__(self):
|
|
return self.get_type_string()
|
|
|
|
def get_string(self, to_string, index):
|
|
"""
|
|
Used to do lookups in all of the various FOO_to_string dictionaries
|
|
but returns 'UNKNOWN' if the key is bogus
|
|
"""
|
|
if index in to_string:
|
|
return to_string[index]
|
|
return 'UNKNOWN'
|
|
|
|
def get_type_string(self, msgtype=None):
|
|
if not msgtype:
|
|
msgtype = self.msgtype
|
|
return self.get_string(self.type_to_string, msgtype)
|
|
|
|
def get_flags_string(self):
|
|
foo = []
|
|
|
|
for (flag, flag_string) in self.flag_to_string.iteritems():
|
|
if self.flags & flag:
|
|
foo.append(flag_string)
|
|
|
|
return ', '.join(foo)
|
|
|
|
def decode_packet(self, length, flags, seq, pid, data):
|
|
self.length = length
|
|
self.flags = flags
|
|
self.seq = seq
|
|
self.pid = pid
|
|
self.header_data = data[0:self.header_LEN]
|
|
self.msg_data = data[self.header_LEN:length]
|
|
|
|
self.decode_netlink_header()
|
|
self.decode_service_header()
|
|
|
|
# NLMSG_ERROR is special case, it does not have attributes to decode
|
|
if self.msgtype != NLMSG_ERROR:
|
|
self.decode_attributes()
|
|
|
|
def get_netlink_header_flags_string(self, msg_type, flags):
|
|
foo = []
|
|
|
|
if flags & NLM_F_REQUEST:
|
|
foo.append('NLM_F_REQUEST')
|
|
|
|
if flags & NLM_F_MULTI:
|
|
foo.append('NLM_F_MULTI')
|
|
|
|
if flags & NLM_F_ACK:
|
|
foo.append('NLM_F_ACK')
|
|
|
|
if flags & NLM_F_ECHO:
|
|
foo.append('NLM_F_ECHO')
|
|
|
|
# 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')
|
|
|
|
if flags & NLM_F_ATOMIC:
|
|
foo.append('NLM_F_ATOMIC')
|
|
|
|
# Modifiers to NEW query
|
|
elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC):
|
|
if flags & NLM_F_REPLACE:
|
|
foo.append('NLM_F_REPLACE')
|
|
|
|
if flags & NLM_F_EXCL:
|
|
foo.append('NLM_F_EXCL')
|
|
|
|
if flags & NLM_F_CREATE:
|
|
foo.append('NLM_F_CREATE')
|
|
|
|
if flags & NLM_F_APPEND:
|
|
foo.append('NLM_F_APPEND')
|
|
|
|
return ', '.join(foo)
|
|
|
|
# When we first RXed the netlink message we had to decode the header to
|
|
# determine what type of netlink message we were dealing with. So the
|
|
# header has actually already been decoded...what we do here is
|
|
# populate the dump_buffer lines with the header content.
|
|
def decode_netlink_header(self):
|
|
|
|
if not self.debug:
|
|
return
|
|
|
|
header_data = self.header_data
|
|
|
|
# Print the netlink header in red
|
|
color = red
|
|
netlink_header_length = 16
|
|
self.dump_buffer.append(" \033[%dmNetlink Header\033[0m" % color)
|
|
|
|
for x in range(0, netlink_header_length/4):
|
|
start = x * 4
|
|
end = start + 4
|
|
|
|
if self.line_number == 1:
|
|
data = unpack('=L', header_data[start:end])[0]
|
|
extra = "Length %s (%d)" % (zfilled_hex(data, 8), data)
|
|
|
|
elif self.line_number == 2:
|
|
(data1, data2) = unpack('HH', header_data[start:end])
|
|
extra = "Type %s (%d - %s), Flags %s (%s)" % \
|
|
(zfilled_hex(data1, 4), data1, self.get_type_string(data1),
|
|
zfilled_hex(data2, 4), self.get_netlink_header_flags_string(data1, data2))
|
|
|
|
elif self.line_number == 3:
|
|
data = unpack('=L', header_data[start:end])[0]
|
|
extra = "Sequence Number %s (%d)" % (zfilled_hex(data, 8), data)
|
|
|
|
elif self.line_number == 4:
|
|
data = unpack('=L', header_data[start:end])[0]
|
|
extra = "Process ID %s (%d)" % (zfilled_hex(data, 8), data)
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, header_data[start:end], extra))
|
|
self.line_number += 1
|
|
|
|
def decode_attributes(self):
|
|
"""
|
|
Decode the attributes and populate the dump_buffer
|
|
"""
|
|
|
|
if self.debug:
|
|
self.dump_buffer.append(" Attributes")
|
|
color = green
|
|
|
|
data = self.msg_data[self.LEN:]
|
|
|
|
while data:
|
|
(length, attr_type) = unpack('=HH', data[:4])
|
|
|
|
# If this is zero we will stay in this loop for forever
|
|
if not length:
|
|
self.log.error('Length is zero')
|
|
return
|
|
|
|
if len(data) < length:
|
|
self.log.error("Buffer underrun %d < %d" % (len(data), length))
|
|
return
|
|
|
|
attr = self.add_attribute(attr_type, None)
|
|
|
|
# Find the end of 'data' for this attribute and decode our section
|
|
# of 'data'. attributes are padded for alignment thus the attr_end.
|
|
#
|
|
# How the attribute is decoded/unpacked is specific per AttributeXXXX class.
|
|
attr_end = padded_length(length)
|
|
attr.decode(self, data[0:attr_end])
|
|
|
|
if self.debug:
|
|
self.line_number = attr.dump_lines(self.dump_buffer, self.line_number, color)
|
|
|
|
# Alternate back and forth between green and blue
|
|
if color == green:
|
|
color = blue
|
|
else:
|
|
color = green
|
|
|
|
data = data[attr_end:]
|
|
|
|
def add_attribute(self, attr_type, value):
|
|
nested = True if attr_type & NLA_F_NESTED else False
|
|
net_byteorder = True if attr_type & NLA_F_NET_BYTEORDER else False
|
|
attr_type = attr_type & NLA_TYPE_MASK
|
|
|
|
# Given an attr_type (say RTA_DST) find the type of AttributeXXXX class
|
|
# that we will use to store this attribute...AttributeIPAddress in the
|
|
# case of RTA_DST.
|
|
if attr_type in self.attribute_to_class:
|
|
(attr_string, attr_class) = self.attribute_to_class[attr_type]
|
|
else:
|
|
attr_string = "UNKNOWN_ATTRIBUTE_%d" % attr_type
|
|
attr_class = AttributeGeneric
|
|
self.log.debug("Attribute %d is not defined in %s.attribute_to_class, assuming AttributeGeneric" %
|
|
(attr_type, self.__class__.__name__))
|
|
|
|
# A few attribute classes must know self.family (family was extracted from
|
|
# the service header)
|
|
if attr_class == AttributeIPAddress or attr_class == AttributeRTA_MULTIPATH:
|
|
attr = attr_class(attr_type, attr_string, self.family, self.log)
|
|
else:
|
|
attr = attr_class(attr_type, attr_string, self.log)
|
|
|
|
attr.set_value(value)
|
|
attr.set_nested(nested)
|
|
attr.set_net_byteorder(net_byteorder)
|
|
|
|
# self.attributes is a dictionary keyed by the attribute type where
|
|
# the value is an instance of the corresponding AttributeXXXX class.
|
|
self.attributes[attr_type] = attr
|
|
|
|
return attr
|
|
|
|
def get_attribute_value(self, attr_type):
|
|
if attr_type not in self.attributes:
|
|
return None
|
|
|
|
return self.attributes[attr_type].value
|
|
|
|
def get_attr_string(self, attr_type):
|
|
"""
|
|
Example: If attr_type is Address.IFA_CACHEINFO return the string 'IFA_CACHEINFO'
|
|
"""
|
|
if attr_type in self.attribute_to_class:
|
|
(attr_string, attr_class) = self.attribute_to_class[attr_type]
|
|
return attr_string
|
|
return str(attr_type)
|
|
|
|
def build_message(self, seq, pid):
|
|
self.seq = seq
|
|
self.pid = pid
|
|
attrs = ''
|
|
|
|
for attr in self.attributes.itervalues():
|
|
attrs += attr.encode()
|
|
|
|
self.length = self.header_LEN + len(self.body) + len(attrs)
|
|
self.header_data = pack(self.header_PACK, self.length, self.msgtype, self.flags, self.seq, self.pid)
|
|
self.msg_data = self.body + attrs
|
|
self.message = self.header_data + self.msg_data
|
|
|
|
if self.debug:
|
|
self.decode_netlink_header()
|
|
self.decode_service_header()
|
|
self.decode_attributes()
|
|
self.dump("TXed %s, length %d, seq %d, pid %d, flags 0x%x (%s)" %
|
|
(self, self.length, self.seq, self.pid, self.flags,
|
|
self.get_netlink_header_flags_string(self.msgtype, self.flags)))
|
|
|
|
# Print the netlink message in hex. This is only used for debugging.
|
|
def dump(self, desc=None):
|
|
attr_string = {}
|
|
|
|
if desc is None:
|
|
desc = "RXed %s, length %d, seq %d, pid %d, flags 0x%x" % (self, self.length, self.seq, self.pid, self.flags)
|
|
|
|
for (attr_type, attr_obj) in self.attributes.iteritems():
|
|
key_string = "(%2d) %s" % (attr_type, self.get_attr_string(attr_type))
|
|
attr_string[key_string] = attr_obj.get_pretty_value()
|
|
|
|
self.log.debug("%s\n%s\n\nAttributes Summary\n%s\n" %
|
|
(desc, '\n'.join(self.dump_buffer), pformat(attr_string)))
|
|
|
|
|
|
class Address(NetlinkPacket):
|
|
"""
|
|
Service Header
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Family | Length | Flags | Scope |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Interface Index |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
"""
|
|
|
|
# Address attributes
|
|
# /usr/include/linux/if_addr.h
|
|
IFA_UNSPEC = 0x00
|
|
IFA_ADDRESS = 0x01
|
|
IFA_LOCAL = 0x02
|
|
IFA_LABEL = 0x03
|
|
IFA_BROADCAST = 0x04
|
|
IFA_ANYCAST = 0x05
|
|
IFA_CACHEINFO = 0x06
|
|
IFA_MULTICAST = 0x07
|
|
IFA_FLAGS = 0x08
|
|
|
|
attribute_to_class = {
|
|
IFA_UNSPEC : ('IFA_UNSPEC', AttributeGeneric),
|
|
IFA_ADDRESS : ('IFA_ADDRESS', AttributeIPAddress),
|
|
IFA_LOCAL : ('IFA_LOCAL', AttributeIPAddress),
|
|
IFA_LABEL : ('IFA_LABEL', AttributeString),
|
|
IFA_BROADCAST : ('IFA_BROADCAST', AttributeIPAddress),
|
|
IFA_ANYCAST : ('IFA_ANYCAST', AttributeIPAddress),
|
|
IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeGeneric),
|
|
IFA_MULTICAST : ('IFA_MULTICAST', AttributeIPAddress),
|
|
IFA_FLAGS : ('IFA_FLAGS', AttributeGeneric)
|
|
}
|
|
|
|
# Address flags
|
|
# /usr/include/linux/if_addr.h
|
|
IFA_F_SECONDARY = 0x01
|
|
IFA_F_NODAD = 0x02
|
|
IFA_F_OPTIMISTIC = 0x04
|
|
IFA_F_DADFAILED = 0x08
|
|
IFA_F_HOMEADDRESS = 0x10
|
|
IFA_F_DEPRECATED = 0x20
|
|
IFA_F_TENTATIVE = 0x40
|
|
IFA_F_PERMANENT = 0x80
|
|
|
|
flag_to_string = {
|
|
IFA_F_SECONDARY : 'IFA_F_SECONDARY',
|
|
IFA_F_NODAD : 'IFA_F_NODAD',
|
|
IFA_F_OPTIMISTIC : 'IFA_F_OPTIMISTIC',
|
|
IFA_F_DADFAILED : 'IFA_F_DADFAILED',
|
|
IFA_F_HOMEADDRESS : 'IFA_F_HOMEADDRESS',
|
|
IFA_F_DEPRECATED : 'IFA_F_DEPRECATED',
|
|
IFA_F_TENTATIVE : 'IFA_F_TENTATIVE',
|
|
IFA_F_PERMANENT : 'IFA_F_PERMANENT'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug=False, logger=None):
|
|
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
|
self.PACK = '4Bi'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def decode_service_header(self):
|
|
|
|
# Nothing to do if the message did not contain a service header
|
|
if self.length == self.header_LEN:
|
|
return
|
|
|
|
(self.family, self.prefixlen, self.flags, self.scope,
|
|
self.ifindex) = \
|
|
unpack(self.PACK, self.msg_data[:self.LEN])
|
|
|
|
if self.debug:
|
|
color = yellow
|
|
self.dump_buffer.append(" \033[%dmService Header\033[0m" % color)
|
|
|
|
for x in range(0, self.LEN/4):
|
|
if self.line_number == 5:
|
|
extra = "Family %s (%d), Length %s (%d), Flags %s, Scope %s (%d)" % \
|
|
(zfilled_hex(self.family, 2), self.family,
|
|
zfilled_hex(self.prefixlen, 2), self.prefixlen,
|
|
zfilled_hex(self.flags, 2),
|
|
zfilled_hex(self.scope, 2), self.scope)
|
|
elif self.line_number == 6:
|
|
extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
start = x * 4
|
|
end = start + 4
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
|
|
self.line_number += 1
|
|
|
|
|
|
class Error(NetlinkPacket):
|
|
|
|
# Error codes
|
|
# /include/netlink/errno.h
|
|
NLE_SUCCESS = 0x00
|
|
NLE_FAILURE = 0x01
|
|
NLE_INTR = 0x02
|
|
NLE_BAD_SOCK = 0x03
|
|
NLE_AGAIN = 0x04
|
|
NLE_NOMEM = 0x05
|
|
NLE_EXIST = 0x06
|
|
NLE_INVAL = 0x07
|
|
NLE_RANGE = 0x08
|
|
NLE_MSGSIZE = 0x09
|
|
NLE_OPNOTSUPP = 0x0A
|
|
NLE_AF_NOSUPPORT = 0x0B
|
|
NLE_OBJ_NOTFOUND = 0x0C
|
|
NLE_NOATTR = 0x0D
|
|
NLE_MISSING_ATTR = 0x0E
|
|
NLE_AF_MISMATCH = 0x0F
|
|
NLE_SEQ_MISMATCH = 0x10
|
|
NLE_MSG_OVERFLOW = 0x11
|
|
NLE_MSG_TRUNC = 0x12
|
|
NLE_NOADDR = 0x13
|
|
NLE_SRCRT_NOSUPPORT = 0x14
|
|
NLE_MSG_TOOSHORT = 0x15
|
|
NLE_MSGTYPE_NOSUPPORT = 0x16
|
|
NLE_OBJ_MISMATCH = 0x17
|
|
NLE_NOCACHE = 0x18
|
|
NLE_BUSY = 0x19
|
|
NLE_PROTO_MISMATCH = 0x1A
|
|
NLE_NOACCESS = 0x1B
|
|
NLE_PERM = 0x1C
|
|
NLE_PKTLOC_FILE = 0x1D
|
|
NLE_PARSE_ERR = 0x1E
|
|
NLE_NODEV = 0x1F
|
|
NLE_IMMUTABLE = 0x20
|
|
NLE_DUMP_INTR = 0x21
|
|
|
|
error_to_string = {
|
|
NLE_SUCCESS : 'NLE_SUCCESS',
|
|
NLE_FAILURE : 'NLE_FAILURE',
|
|
NLE_INTR : 'NLE_INTR',
|
|
NLE_BAD_SOCK : 'NLE_BAD_SOCK',
|
|
NLE_AGAIN : 'NLE_AGAIN',
|
|
NLE_NOMEM : 'NLE_NOMEM',
|
|
NLE_EXIST : 'NLE_EXIST',
|
|
NLE_INVAL : 'NLE_INVAL',
|
|
NLE_RANGE : 'NLE_RANGE',
|
|
NLE_MSGSIZE : 'NLE_MSGSIZE',
|
|
NLE_OPNOTSUPP : 'NLE_OPNOTSUPP',
|
|
NLE_AF_NOSUPPORT : 'NLE_AF_NOSUPPORT',
|
|
NLE_OBJ_NOTFOUND : 'NLE_OBJ_NOTFOUND',
|
|
NLE_NOATTR : 'NLE_NOATTR',
|
|
NLE_MISSING_ATTR : 'NLE_MISSING_ATTR',
|
|
NLE_AF_MISMATCH : 'NLE_AF_MISMATCH',
|
|
NLE_SEQ_MISMATCH : 'NLE_SEQ_MISMATCH',
|
|
NLE_MSG_OVERFLOW : 'NLE_MSG_OVERFLOW',
|
|
NLE_MSG_TRUNC : 'NLE_MSG_TRUNC',
|
|
NLE_NOADDR : 'NLE_NOADDR',
|
|
NLE_SRCRT_NOSUPPORT : 'NLE_SRCRT_NOSUPPORT',
|
|
NLE_MSG_TOOSHORT : 'NLE_MSG_TOOSHORT',
|
|
NLE_MSGTYPE_NOSUPPORT : 'NLE_MSGTYPE_NOSUPPORT',
|
|
NLE_OBJ_MISMATCH : 'NLE_OBJ_MISMATCH',
|
|
NLE_NOCACHE : 'NLE_NOCACHE',
|
|
NLE_BUSY : 'NLE_BUSY',
|
|
NLE_PROTO_MISMATCH : 'NLE_PROTO_MISMATCH',
|
|
NLE_NOACCESS : 'NLE_NOACCESS',
|
|
NLE_PERM : 'NLE_PERM',
|
|
NLE_PKTLOC_FILE : 'NLE_PKTLOC_FILE',
|
|
NLE_PARSE_ERR : 'NLE_PARSE_ERR',
|
|
NLE_NODEV : 'NLE_NODEV',
|
|
NLE_IMMUTABLE : 'NLE_IMMUTABLE',
|
|
NLE_DUMP_INTR : 'NLE_DUMP_INTR'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug=False, logger=None):
|
|
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
|
self.PACK = '=iLHHLL'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def decode_service_header(self):
|
|
|
|
# Nothing to do if the message did not contain a service header
|
|
if self.length == self.header_LEN:
|
|
return
|
|
|
|
(self.negative_errno, self.bad_msg_len, self.bad_msg_type,
|
|
self.bad_msg_flag, self.bad_msg_seq, self.bad_msg_pid) =\
|
|
unpack(self.PACK, self.msg_data[:self.LEN])
|
|
|
|
if self.debug:
|
|
color = yellow
|
|
self.dump_buffer.append(" \033[%dmService Header\033[0m" % color)
|
|
|
|
for x in range(0, self.LEN/4):
|
|
|
|
if self.line_number == 5:
|
|
extra = "Error Number %s is %s" % (self.negative_errno, self.error_to_string.get(abs(self.negative_errno)))
|
|
# zfilled_hex(self.negative_errno, 2)
|
|
|
|
elif self.line_number == 6:
|
|
extra = "Length %s (%d)" % (zfilled_hex(self.bad_msg_len, 8), self.bad_msg_len)
|
|
|
|
elif self.line_number == 7:
|
|
extra = "Type %s (%d - %s), Flags %s (%s)" % \
|
|
(zfilled_hex(self.bad_msg_type, 4), self.bad_msg_type, self.get_type_string(self.bad_msg_type),
|
|
zfilled_hex(self.bad_msg_flag, 4), self.get_netlink_header_flags_string(self.bad_msg_type, self.bad_msg_flag))
|
|
|
|
elif self.line_number == 8:
|
|
extra = "Sequence Number %s (%d)" % (zfilled_hex(self.bad_msg_seq, 8), self.bad_msg_seq)
|
|
|
|
elif self.line_number == 9:
|
|
extra = "Process ID %s (%d)" % (zfilled_hex(self.bad_msg_pid, 8), self.bad_msg_pid)
|
|
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
start = x * 4
|
|
end = start + 4
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
|
|
self.line_number += 1
|
|
|
|
|
|
class Link(NetlinkPacket):
|
|
"""
|
|
Service Header
|
|
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Family | Reserved | Device Type |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Interface Index |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Device Flags |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Change Mask |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
"""
|
|
|
|
# Link attributes
|
|
# /usr/include/linux/if_link.h
|
|
IFLA_UNSPEC = 0
|
|
IFLA_ADDRESS = 1
|
|
IFLA_BROADCAST = 2
|
|
IFLA_IFNAME = 3
|
|
IFLA_MTU = 4
|
|
IFLA_LINK = 5
|
|
IFLA_QDISC = 6
|
|
IFLA_STATS = 7
|
|
IFLA_COST = 8
|
|
IFLA_PRIORITY = 9
|
|
IFLA_MASTER = 10
|
|
IFLA_WIRELESS = 11
|
|
IFLA_PROTINFO = 12
|
|
IFLA_TXQLEN = 13
|
|
IFLA_MAP = 14
|
|
IFLA_WEIGHT = 15
|
|
IFLA_OPERSTATE = 16
|
|
IFLA_LINKMODE = 17
|
|
IFLA_LINKINFO = 18
|
|
IFLA_NET_NS_PID = 19
|
|
IFLA_IFALIAS = 20
|
|
IFLA_NUM_VF = 21
|
|
IFLA_VFINFO_LIST = 22
|
|
IFLA_STATS64 = 23
|
|
IFLA_VF_PORTS = 24
|
|
IFLA_PORT_SELF = 25
|
|
IFLA_AF_SPEC = 26
|
|
IFLA_GROUP = 27
|
|
IFLA_NET_NS_FD = 28
|
|
IFLA_EXT_MASK = 29
|
|
IFLA_PROMISCUITY = 30
|
|
IFLA_NUM_TX_QUEUES = 31
|
|
IFLA_NUM_RX_QUEUES = 32
|
|
IFLA_CARRIER = 33
|
|
IFLA_PHYS_PORT_ID = 34
|
|
IFLA_CARRIER_CHANGES = 35
|
|
IFLA_PHYS_SWITCH_ID = 36
|
|
IFLA_LINK_NETNSID = 37
|
|
IFLA_PHYS_PORT_NAME = 38
|
|
IFLA_PROTO_DOWN = 39
|
|
IFLA_LINKPROTODOWN = 200
|
|
|
|
attribute_to_class = {
|
|
IFLA_UNSPEC : ('IFLA_UNSPEC', AttributeGeneric),
|
|
IFLA_ADDRESS : ('IFLA_ADDRESS', AttributeMACAddress),
|
|
IFLA_BROADCAST : ('IFLA_BROADCAST', AttributeMACAddress),
|
|
IFLA_IFNAME : ('IFLA_IFNAME', AttributeStringInterfaceName),
|
|
IFLA_MTU : ('IFLA_MTU', AttributeFourByteValue),
|
|
IFLA_LINK : ('IFLA_LINK', AttributeFourByteValue),
|
|
IFLA_QDISC : ('IFLA_QDISC', AttributeString),
|
|
IFLA_STATS : ('IFLA_STATS', AttributeGeneric),
|
|
IFLA_COST : ('IFLA_COST', AttributeGeneric),
|
|
IFLA_PRIORITY : ('IFLA_PRIORITY', AttributeGeneric),
|
|
IFLA_MASTER : ('IFLA_MASTER', AttributeFourByteValue),
|
|
IFLA_WIRELESS : ('IFLA_WIRELESS', AttributeGeneric),
|
|
IFLA_PROTINFO : ('IFLA_PROTINFO', AttributeGeneric),
|
|
IFLA_TXQLEN : ('IFLA_TXQLEN', AttributeFourByteValue),
|
|
IFLA_MAP : ('IFLA_MAP', AttributeGeneric),
|
|
IFLA_WEIGHT : ('IFLA_WEIGHT', AttributeGeneric),
|
|
IFLA_OPERSTATE : ('IFLA_OPERSTATE', AttributeFourByteValue),
|
|
IFLA_LINKMODE : ('IFLA_LINKMODE', AttributeFourByteValue),
|
|
IFLA_LINKINFO : ('IFLA_LINKINFO', AttributeIFLA_LINKINFO),
|
|
IFLA_NET_NS_PID : ('IFLA_NET_NS_PID', AttributeGeneric),
|
|
IFLA_IFALIAS : ('IFLA_IFALIAS', AttributeGeneric),
|
|
IFLA_NUM_VF : ('IFLA_NUM_VF', AttributeGeneric),
|
|
IFLA_VFINFO_LIST : ('IFLA_VFINFO_LIST', AttributeGeneric),
|
|
IFLA_STATS64 : ('IFLA_STATS64', AttributeGeneric),
|
|
IFLA_VF_PORTS : ('IFLA_VF_PORTS', AttributeGeneric),
|
|
IFLA_PORT_SELF : ('IFLA_PORT_SELF', AttributeGeneric),
|
|
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_PROMISCUITY : ('IFLA_PROMISCUITY', AttributeGeneric),
|
|
IFLA_NUM_TX_QUEUES : ('IFLA_NUM_TX_QUEUES', AttributeGeneric),
|
|
IFLA_NUM_RX_QUEUES : ('IFLA_NUM_RX_QUEUES', AttributeGeneric),
|
|
IFLA_CARRIER : ('IFLA_CARRIER', AttributeGeneric),
|
|
IFLA_PHYS_PORT_ID : ('IFLA_PHYS_PORT_ID', AttributeGeneric),
|
|
IFLA_CARRIER_CHANGES : ('IFLA_CARRIER_CHANGES', AttributeGeneric),
|
|
IFLA_PHYS_SWITCH_ID : ('IFLA_PHYS_SWITCH_ID', AttributeGeneric),
|
|
IFLA_LINK_NETNSID : ('IFLA_LINK_NETNSID', AttributeGeneric),
|
|
IFLA_PHYS_PORT_NAME : ('IFLA_PHYS_PORT_NAME', AttributeGeneric),
|
|
IFLA_PROTO_DOWN : ('IFLA_PROTO_DOWN', AttributeOneByteValue),
|
|
IFLA_LINKPROTODOWN : ('IFLA_LINKPROTODOWN', AttributeGeneric)
|
|
}
|
|
|
|
# Link flags
|
|
# /usr/include/linux/if.h
|
|
IFF_UP = 0x0001 # Interface is administratively up.
|
|
IFF_BROADCAST = 0x0002 # Valid broadcast address set.
|
|
IFF_DEBUG = 0x0004 # Internal debugging flag.
|
|
IFF_LOOPBACK = 0x0008 # Interface is a loopback interface.
|
|
IFF_POINTOPOINT = 0x0010 # Interface is a point-to-point link.
|
|
IFF_NOTRAILERS = 0x0020 # Avoid use of trailers.
|
|
IFF_RUNNING = 0x0040 # Interface is operationally up.
|
|
IFF_NOARP = 0x0080 # No ARP protocol needed for this interface.
|
|
IFF_PROMISC = 0x0100 # Interface is in promiscuous mode.
|
|
IFF_ALLMULTI = 0x0200 # Receive all multicast packets.
|
|
IFF_MASTER = 0x0400 # Master of a load balancing bundle.
|
|
IFF_SLAVE = 0x0800 # Slave of a load balancing bundle.
|
|
IFF_MULTICAST = 0x1000 # Supports multicast.
|
|
IFF_PORTSEL = 0x2000 # Is able to select media type via ifmap.
|
|
IFF_AUTOMEDIA = 0x4000 # Auto media selection active.
|
|
IFF_DYNAMIC = 0x8000 # Interface was dynamically created.
|
|
IFF_LOWER_UP = 0x10000 # driver signals L1 up
|
|
IFF_DORMANT = 0x20000 # driver signals dormant
|
|
IFF_ECHO = 0x40000 # echo sent packet
|
|
IFF_PROTO_DOWN = 0x1000000 # protocol is down on the interface
|
|
|
|
flag_to_string = {
|
|
IFF_UP : 'IFF_UP',
|
|
IFF_BROADCAST : 'IFF_BROADCAST',
|
|
IFF_DEBUG : 'IFF_DEBUG',
|
|
IFF_LOOPBACK : 'IFF_LOOPBACK',
|
|
IFF_POINTOPOINT : 'IFF_POINTOPOINT',
|
|
IFF_NOTRAILERS : 'IFF_NOTRAILERS',
|
|
IFF_RUNNING : 'IFF_RUNNING',
|
|
IFF_NOARP : 'IFF_NOARP',
|
|
IFF_PROMISC : 'IFF_PROMISC',
|
|
IFF_ALLMULTI : 'IFF_ALLMULTI',
|
|
IFF_MASTER : 'IFF_MASTER',
|
|
IFF_SLAVE : 'IFF_SLAVE',
|
|
IFF_MULTICAST : 'IFF_MULTICAST',
|
|
IFF_PORTSEL : 'IFF_PORTSEL',
|
|
IFF_AUTOMEDIA : 'IFF_AUTOMEDIA',
|
|
IFF_DYNAMIC : 'IFF_DYNAMIC',
|
|
IFF_LOWER_UP : 'IFF_LOWER_UP',
|
|
IFF_DORMANT : 'IFF_DORMANT',
|
|
IFF_ECHO : 'IFF_ECHO',
|
|
IFF_PROTO_DOWN : 'IFF_PROTO_DOWN'
|
|
}
|
|
|
|
# RFC 2863 operational status
|
|
IF_OPER_UNKNOWN = 0
|
|
IF_OPER_NOTPRESENT = 1
|
|
IF_OPER_DOWN = 2
|
|
IF_OPER_LOWERLAYERDOWN = 3
|
|
IF_OPER_TESTING = 4
|
|
IF_OPER_DORMANT = 5
|
|
IF_OPER_UP = 6
|
|
|
|
oper_to_string = {
|
|
IF_OPER_UNKNOWN : 'IF_OPER_UNKNOWN',
|
|
IF_OPER_NOTPRESENT : 'IF_OPER_NOTPRESENT',
|
|
IF_OPER_DOWN : 'IF_OPER_DOWN',
|
|
IF_OPER_LOWERLAYERDOWN : 'IF_OPER_LOWERLAYERDOWN',
|
|
IF_OPER_TESTING : 'IF_OPER_TESTING',
|
|
IF_OPER_DORMANT : 'IF_OPER_DORMANT',
|
|
IF_OPER_UP : 'IF_OPER_UP'
|
|
}
|
|
|
|
# Link types
|
|
# /usr/include/linux/if_arp.h
|
|
# ARP protocol HARDWARE identifiers
|
|
ARPHRD_NETROM = 0 # from KA9Q: NET/ROM pseudo
|
|
ARPHRD_ETHER = 1 # Ethernet 10Mbps
|
|
ARPHRD_EETHER = 2 # Experimental Ethernet
|
|
ARPHRD_AX25 = 3 # AX.25 Level 2
|
|
ARPHRD_PRONET = 4 # PROnet token ring
|
|
ARPHRD_CHAOS = 5 # Chaosnet
|
|
ARPHRD_IEEE802 = 6 # IEEE 802.2 Ethernet/TR/TB
|
|
ARPHRD_ARCNET = 7 # ARCnet
|
|
ARPHRD_APPLETLK = 8 # APPLEtalk
|
|
ARPHRD_DLCI = 15 # Frame Relay DLCI
|
|
ARPHRD_ATM = 19 # ATM
|
|
ARPHRD_METRICOM = 23 # Metricom STRIP (new IANA id)
|
|
ARPHRD_IEEE1394 = 24 # IEEE 1394 IPv4 - RFC 2734
|
|
ARPHRD_EUI64 = 27 # EUI-64
|
|
ARPHRD_INFINIBAND = 32 # InfiniBand
|
|
# Dummy types for non ARP hardware
|
|
ARPHRD_SLIP = 256
|
|
ARPHRD_CSLIP = 257
|
|
ARPHRD_SLIP6 = 258
|
|
ARPHRD_CSLIP6 = 259
|
|
ARPHRD_RSRVD = 260 # Notional KISS type
|
|
ARPHRD_ADAPT = 264
|
|
ARPHRD_ROSE = 270
|
|
ARPHRD_X25 = 271 # CCITT X.25
|
|
ARPHRD_HWX25 = 272 # Boards with X.25 in firmware
|
|
ARPHRD_CAN = 280 # Controller Area Network
|
|
ARPHRD_PPP = 512
|
|
ARPHRD_CISCO = 513 # Cisco HDLC
|
|
ARPHRD_HDLC = ARPHRD_CISCO
|
|
ARPHRD_LAPB = 516 # LAPB
|
|
ARPHRD_DDCMP = 517 # Digital's DDCMP protocol
|
|
ARPHRD_RAWHDLC = 518 # Raw HDLC
|
|
ARPHRD_TUNNEL = 768 # IPIP tunnel
|
|
ARPHRD_TUNNEL6 = 769 # IP6IP6 tunnel
|
|
ARPHRD_FRAD = 770 # Frame Relay Access Device
|
|
ARPHRD_SKIP = 771 # SKIP vif
|
|
ARPHRD_LOOPBACK = 772 # Loopback device
|
|
ARPHRD_LOCALTLK = 773 # Localtalk device
|
|
ARPHRD_FDDI = 774 # Fiber Distributed Data Interface
|
|
ARPHRD_BIF = 775 # AP1000 BIF
|
|
ARPHRD_SIT = 776 # sit0 device - IPv6-in-IPv4
|
|
ARPHRD_IPDDP = 777 # IP over DDP tunneller
|
|
ARPHRD_IPGRE = 778 # GRE over IP
|
|
ARPHRD_PIMREG = 779 # PIMSM register interface
|
|
ARPHRD_HIPPI = 780 # High Performance Parallel Interface
|
|
ARPHRD_ASH = 781 # Nexus 64Mbps Ash
|
|
ARPHRD_ECONET = 782 # Acorn Econet
|
|
ARPHRD_IRDA = 783 # Linux-IrDA
|
|
ARPHRD_FCPP = 784 # Point to point fibrechannel
|
|
ARPHRD_FCAL = 785 # Fibrechannel arbitrated loop
|
|
ARPHRD_FCPL = 786 # Fibrechannel public loop
|
|
ARPHRD_FCFABRIC = 787 # Fibrechannel fabric
|
|
# 787->799 reserved for fibrechannel media types
|
|
ARPHRD_IEEE802_TR = 800 # Magic type ident for TR
|
|
ARPHRD_IEEE80211 = 801 # IEEE 802.11
|
|
ARPHRD_IEEE80211_PRISM = 802 # IEEE 802.11 + Prism2 header
|
|
ARPHRD_IEEE80211_RADIOTAP = 803 # IEEE 802.11 + radiotap header
|
|
ARPHRD_IEEE802154 = 804
|
|
ARPHRD_PHONET = 820 # PhoNet media type
|
|
ARPHRD_PHONET_PIPE = 821 # PhoNet pipe header
|
|
ARPHRD_CAIF = 822 # CAIF media type
|
|
ARPHRD_VOID = 0xFFFF # Void type, nothing is known
|
|
ARPHRD_NONE = 0xFFFE # zero header length
|
|
|
|
link_type_to_string = {
|
|
ARPHRD_NETROM : 'ARPHRD_NETROM',
|
|
ARPHRD_ETHER : 'ARPHRD_ETHER',
|
|
ARPHRD_EETHER : 'ARPHRD_EETHER',
|
|
ARPHRD_AX25 : 'ARPHRD_AX25',
|
|
ARPHRD_PRONET : 'ARPHRD_PRONET',
|
|
ARPHRD_CHAOS : 'ARPHRD_CHAOS',
|
|
ARPHRD_IEEE802 : 'ARPHRD_IEEE802',
|
|
ARPHRD_ARCNET : 'ARPHRD_ARCNET',
|
|
ARPHRD_APPLETLK : 'ARPHRD_APPLETLK',
|
|
ARPHRD_DLCI : 'ARPHRD_DLCI',
|
|
ARPHRD_ATM : 'ARPHRD_ATM',
|
|
ARPHRD_METRICOM : 'ARPHRD_METRICOM',
|
|
ARPHRD_IEEE1394 : 'ARPHRD_IEEE1394',
|
|
ARPHRD_EUI64 : 'ARPHRD_EUI64',
|
|
ARPHRD_INFINIBAND : 'ARPHRD_INFINIBAND',
|
|
ARPHRD_SLIP : 'ARPHRD_SLIP',
|
|
ARPHRD_CSLIP : 'ARPHRD_CSLIP',
|
|
ARPHRD_SLIP6 : 'ARPHRD_SLIP6',
|
|
ARPHRD_CSLIP6 : 'ARPHRD_CSLIP6',
|
|
ARPHRD_RSRVD : 'ARPHRD_RSRVD',
|
|
ARPHRD_ADAPT : 'ARPHRD_ADAPT',
|
|
ARPHRD_ROSE : 'ARPHRD_ROSE',
|
|
ARPHRD_X25 : 'ARPHRD_X25',
|
|
ARPHRD_HWX25 : 'ARPHRD_HWX25',
|
|
ARPHRD_CAN : 'ARPHRD_CAN',
|
|
ARPHRD_PPP : 'ARPHRD_PPP',
|
|
ARPHRD_CISCO : 'ARPHRD_CISCO',
|
|
ARPHRD_HDLC : 'ARPHRD_HDLC',
|
|
ARPHRD_LAPB : 'ARPHRD_LAPB',
|
|
ARPHRD_DDCMP : 'ARPHRD_DDCMP',
|
|
ARPHRD_RAWHDLC : 'ARPHRD_RAWHDLC',
|
|
ARPHRD_TUNNEL : 'ARPHRD_TUNNEL',
|
|
ARPHRD_TUNNEL6 : 'ARPHRD_TUNNEL6',
|
|
ARPHRD_FRAD : 'ARPHRD_FRAD',
|
|
ARPHRD_SKIP : 'ARPHRD_SKIP',
|
|
ARPHRD_LOOPBACK : 'ARPHRD_LOOPBACK',
|
|
ARPHRD_LOCALTLK : 'ARPHRD_LOCALTLK',
|
|
ARPHRD_FDDI : 'ARPHRD_FDDI',
|
|
ARPHRD_BIF : 'ARPHRD_BIF',
|
|
ARPHRD_SIT : 'ARPHRD_SIT',
|
|
ARPHRD_IPDDP : 'ARPHRD_IPDDP',
|
|
ARPHRD_IPGRE : 'ARPHRD_IPGRE',
|
|
ARPHRD_PIMREG : 'ARPHRD_PIMREG',
|
|
ARPHRD_HIPPI : 'ARPHRD_HIPPI',
|
|
ARPHRD_ASH : 'ARPHRD_ASH',
|
|
ARPHRD_ECONET : 'ARPHRD_ECONET',
|
|
ARPHRD_IRDA : 'ARPHRD_IRDA',
|
|
ARPHRD_FCPP : 'ARPHRD_FCPP',
|
|
ARPHRD_FCAL : 'ARPHRD_FCAL',
|
|
ARPHRD_FCPL : 'ARPHRD_FCPL',
|
|
ARPHRD_FCFABRIC : 'ARPHRD_FCFABRIC',
|
|
ARPHRD_IEEE802_TR : 'ARPHRD_IEEE802_TR',
|
|
ARPHRD_IEEE80211 : 'ARPHRD_IEEE80211',
|
|
ARPHRD_IEEE80211_PRISM : 'ARPHRD_IEEE80211_PRISM',
|
|
ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP',
|
|
ARPHRD_IEEE802154 : 'ARPHRD_IEEE802154',
|
|
ARPHRD_PHONET : 'ARPHRD_PHONET',
|
|
ARPHRD_PHONET_PIPE : 'ARPHRD_PHONET_PIPE',
|
|
ARPHRD_CAIF : 'ARPHRD_CAIF',
|
|
ARPHRD_VOID : 'ARPHRD_VOID',
|
|
ARPHRD_NONE : 'ARPHRD_NONE'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_LINKINFO attributes
|
|
# =========================================
|
|
IFLA_INFO_UNSPEC = 0
|
|
IFLA_INFO_KIND = 1
|
|
IFLA_INFO_DATA = 2
|
|
IFLA_INFO_XSTATS = 3
|
|
IFLA_INFO_SLAVE_KIND = 4
|
|
IFLA_INFO_SLAVE_DATA = 5
|
|
IFLA_INFO_MAX = 6
|
|
|
|
ifla_info_to_string = {
|
|
IFLA_INFO_UNSPEC : 'IFLA_INFO_UNSPEC',
|
|
IFLA_INFO_KIND : 'IFLA_INFO_KIND',
|
|
IFLA_INFO_DATA : 'IFLA_INFO_DATA',
|
|
IFLA_INFO_XSTATS : 'IFLA_INFO_XSTATS',
|
|
IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND',
|
|
IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA',
|
|
IFLA_INFO_MAX : 'IFLA_INFO_MAX'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_INFO_DATA attributes for vlan
|
|
# =========================================
|
|
IFLA_VLAN_UNSPEC = 0
|
|
IFLA_VLAN_ID = 1
|
|
IFLA_VLAN_FLAGS = 2
|
|
IFLA_VLAN_EGRESS_QOS = 3
|
|
IFLA_VLAN_INGRESS_QOS = 4
|
|
IFLA_VLAN_PROTOCOL = 5
|
|
|
|
ifla_vlan_to_string = {
|
|
IFLA_VLAN_UNSPEC : 'IFLA_VLAN_UNSPEC',
|
|
IFLA_VLAN_ID : 'IFLA_VLAN_ID',
|
|
IFLA_VLAN_FLAGS : 'IFLA_VLAN_FLAGS',
|
|
IFLA_VLAN_EGRESS_QOS : 'IFLA_VLAN_EGRESS_QOS',
|
|
IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS',
|
|
IFLA_VLAN_PROTOCOL : 'IFLA_VLAN_PROTOCOL'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_INFO_DATA attributes for macvlan
|
|
# =========================================
|
|
IFLA_MACVLAN_UNSPEC = 0
|
|
IFLA_MACVLAN_MODE = 1
|
|
|
|
ifla_macvlan_to_string = {
|
|
IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC',
|
|
IFLA_MACVLAN_MODE : 'IFLA_MACVLAN_MODE'
|
|
}
|
|
|
|
# macvlan modes
|
|
MACVLAN_MODE_PRIVATE = 1
|
|
MACVLAN_MODE_VEPA = 2
|
|
MACVLAN_MODE_BRIDGE = 3
|
|
MACVLAN_MODE_PASSTHRU = 4
|
|
|
|
macvlan_mode_to_string = {
|
|
MACVLAN_MODE_PRIVATE : 'MACVLAN_MODE_PRIVATE',
|
|
MACVLAN_MODE_VEPA : 'MACVLAN_MODE_VEPA',
|
|
MACVLAN_MODE_BRIDGE : 'MACVLAN_MODE_BRIDGE',
|
|
MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_INFO_DATA attributes for vxlan
|
|
# =========================================
|
|
IFLA_VXLAN_UNSPEC = 0
|
|
IFLA_VXLAN_ID = 1
|
|
IFLA_VXLAN_GROUP = 2
|
|
IFLA_VXLAN_LINK = 3
|
|
IFLA_VXLAN_LOCAL = 4
|
|
IFLA_VXLAN_TTL = 5
|
|
IFLA_VXLAN_TOS = 6
|
|
IFLA_VXLAN_LEARNING = 7
|
|
IFLA_VXLAN_AGEING = 8
|
|
IFLA_VXLAN_LIMIT = 9
|
|
IFLA_VXLAN_PORT_RANGE = 10
|
|
IFLA_VXLAN_PROXY = 11
|
|
IFLA_VXLAN_RSC = 12
|
|
IFLA_VXLAN_L2MISS = 13
|
|
IFLA_VXLAN_L3MISS = 14
|
|
IFLA_VXLAN_PORT = 15
|
|
IFLA_VXLAN_GROUP6 = 16
|
|
IFLA_VXLAN_LOCAL6 = 17
|
|
IFLA_VXLAN_UDP_CSUM = 18
|
|
IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19
|
|
IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20
|
|
IFLA_VXLAN_REMCSUM_TX = 21
|
|
IFLA_VXLAN_REMCSUM_RX = 22
|
|
IFLA_VXLAN_GBP = 23
|
|
IFLA_VXLAN_REMCSUM_NOPARTIAL = 24
|
|
IFLA_VXLAN_COLLECT_METADATA = 25
|
|
IFLA_VXLAN_REPLICATION_NODE = 253
|
|
IFLA_VXLAN_REPLICATION_TYPE = 254
|
|
|
|
ifla_vxlan_to_string = {
|
|
IFLA_VXLAN_UNSPEC : 'IFLA_VXLAN_UNSPEC',
|
|
IFLA_VXLAN_ID : 'IFLA_VXLAN_ID',
|
|
IFLA_VXLAN_GROUP : 'IFLA_VXLAN_GROUP',
|
|
IFLA_VXLAN_LINK : 'IFLA_VXLAN_LINK',
|
|
IFLA_VXLAN_LOCAL : 'IFLA_VXLAN_LOCAL',
|
|
IFLA_VXLAN_TTL : 'IFLA_VXLAN_TTL',
|
|
IFLA_VXLAN_TOS : 'IFLA_VXLAN_TOS',
|
|
IFLA_VXLAN_LEARNING : 'IFLA_VXLAN_LEARNING',
|
|
IFLA_VXLAN_AGEING : 'IFLA_VXLAN_AGEING',
|
|
IFLA_VXLAN_LIMIT : 'IFLA_VXLAN_LIMIT',
|
|
IFLA_VXLAN_PORT_RANGE : 'IFLA_VXLAN_PORT_RANGE',
|
|
IFLA_VXLAN_PROXY : 'IFLA_VXLAN_PROXY',
|
|
IFLA_VXLAN_RSC : 'IFLA_VXLAN_RSC',
|
|
IFLA_VXLAN_L2MISS : 'IFLA_VXLAN_L2MISS',
|
|
IFLA_VXLAN_L3MISS : 'IFLA_VXLAN_L3MISS',
|
|
IFLA_VXLAN_PORT : 'IFLA_VXLAN_PORT',
|
|
IFLA_VXLAN_GROUP6 : 'IFLA_VXLAN_GROUP6',
|
|
IFLA_VXLAN_LOCAL6 : 'IFLA_VXLAN_LOCAL6',
|
|
IFLA_VXLAN_UDP_CSUM : 'IFLA_VXLAN_UDP_CSUM',
|
|
IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX',
|
|
IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX',
|
|
IFLA_VXLAN_REMCSUM_TX : 'IFLA_VXLAN_REMCSUM_TX',
|
|
IFLA_VXLAN_REMCSUM_RX : 'IFLA_VXLAN_REMCSUM_RX',
|
|
IFLA_VXLAN_GBP : 'IFLA_VXLAN_GBP',
|
|
IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL',
|
|
IFLA_VXLAN_COLLECT_METADATA : 'IFLA_VXLAN_COLLECT_METADATA',
|
|
IFLA_VXLAN_REPLICATION_NODE : 'IFLA_VXLAN_REPLICATION_NODE',
|
|
IFLA_VXLAN_REPLICATION_TYPE : 'IFLA_VXLAN_REPLICATION_TYPE'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_INFO_DATA attributes for bonds
|
|
# =========================================
|
|
IFLA_BOND_UNSPEC = 0
|
|
IFLA_BOND_MODE = 1
|
|
IFLA_BOND_ACTIVE_SLAVE = 2
|
|
IFLA_BOND_MIIMON = 3
|
|
IFLA_BOND_UPDELAY = 4
|
|
IFLA_BOND_DOWNDELAY = 5
|
|
IFLA_BOND_USE_CARRIER = 6
|
|
IFLA_BOND_ARP_INTERVAL = 7
|
|
IFLA_BOND_ARP_IP_TARGET = 8
|
|
IFLA_BOND_ARP_VALIDATE = 9
|
|
IFLA_BOND_ARP_ALL_TARGETS = 10
|
|
IFLA_BOND_PRIMARY = 11
|
|
IFLA_BOND_PRIMARY_RESELECT = 12
|
|
IFLA_BOND_FAIL_OVER_MAC = 13
|
|
IFLA_BOND_XMIT_HASH_POLICY = 14
|
|
IFLA_BOND_RESEND_IGMP = 15
|
|
IFLA_BOND_NUM_PEER_NOTIF = 16
|
|
IFLA_BOND_ALL_SLAVES_ACTIVE = 17
|
|
IFLA_BOND_MIN_LINKS = 18
|
|
IFLA_BOND_LP_INTERVAL = 19
|
|
IFLA_BOND_PACKETS_PER_SLAVE = 20
|
|
IFLA_BOND_AD_LACP_RATE = 21
|
|
IFLA_BOND_AD_SELECT = 22
|
|
IFLA_BOND_AD_INFO = 23
|
|
IFLA_BOND_AD_ACTOR_SYS_PRIO = 24
|
|
IFLA_BOND_AD_USER_PORT_KEY = 25
|
|
IFLA_BOND_AD_ACTOR_SYSTEM = 26
|
|
IFLA_BOND_CL_LACP_BYPASS_ALLOW = 100
|
|
IFLA_BOND_CL_LACP_BYPASS_ACTIVE = 101
|
|
IFLA_BOND_CL_LACP_BYPASS_PERIOD = 102
|
|
IFLA_BOND_CL_CLAG_ENABLE = 103
|
|
IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE = 104
|
|
|
|
ifla_bond_to_string = {
|
|
IFLA_BOND_UNSPEC : 'IFLA_BOND_UNSPEC',
|
|
IFLA_BOND_MODE : 'IFLA_BOND_MODE',
|
|
IFLA_BOND_ACTIVE_SLAVE : 'IFLA_BOND_ACTIVE_SLAVE',
|
|
IFLA_BOND_MIIMON : 'IFLA_BOND_MIIMON',
|
|
IFLA_BOND_UPDELAY : 'IFLA_BOND_UPDELAY',
|
|
IFLA_BOND_DOWNDELAY : 'IFLA_BOND_DOWNDELAY',
|
|
IFLA_BOND_USE_CARRIER : 'IFLA_BOND_USE_CARRIER',
|
|
IFLA_BOND_ARP_INTERVAL : 'IFLA_BOND_ARP_INTERVAL',
|
|
IFLA_BOND_ARP_IP_TARGET : 'IFLA_BOND_ARP_IP_TARGET',
|
|
IFLA_BOND_ARP_VALIDATE : 'IFLA_BOND_ARP_VALIDATE',
|
|
IFLA_BOND_ARP_ALL_TARGETS : 'IFLA_BOND_ARP_ALL_TARGETS',
|
|
IFLA_BOND_PRIMARY : 'IFLA_BOND_PRIMARY',
|
|
IFLA_BOND_PRIMARY_RESELECT : 'IFLA_BOND_PRIMARY_RESELECT',
|
|
IFLA_BOND_FAIL_OVER_MAC : 'IFLA_BOND_FAIL_OVER_MAC',
|
|
IFLA_BOND_XMIT_HASH_POLICY : 'IFLA_BOND_XMIT_HASH_POLICY',
|
|
IFLA_BOND_RESEND_IGMP : 'IFLA_BOND_RESEND_IGMP',
|
|
IFLA_BOND_NUM_PEER_NOTIF : 'IFLA_BOND_NUM_PEER_NOTIF',
|
|
IFLA_BOND_ALL_SLAVES_ACTIVE : 'IFLA_BOND_ALL_SLAVES_ACTIVE',
|
|
IFLA_BOND_MIN_LINKS : 'IFLA_BOND_MIN_LINKS',
|
|
IFLA_BOND_LP_INTERVAL : 'IFLA_BOND_LP_INTERVAL',
|
|
IFLA_BOND_PACKETS_PER_SLAVE : 'IFLA_BOND_PACKETS_PER_SLAVE',
|
|
IFLA_BOND_AD_LACP_RATE : 'IFLA_BOND_AD_LACP_RATE',
|
|
IFLA_BOND_AD_SELECT : 'IFLA_BOND_AD_SELECT',
|
|
IFLA_BOND_AD_INFO : 'IFLA_BOND_AD_INFO',
|
|
IFLA_BOND_AD_ACTOR_SYS_PRIO : 'IFLA_BOND_AD_ACTOR_SYS_PRIO',
|
|
IFLA_BOND_AD_USER_PORT_KEY : 'IFLA_BOND_AD_USER_PORT_KEY',
|
|
IFLA_BOND_AD_ACTOR_SYSTEM : 'IFLA_BOND_AD_ACTOR_SYSTEM',
|
|
IFLA_BOND_CL_LACP_BYPASS_ALLOW : 'IFLA_BOND_CL_LACP_BYPASS_ALLOW',
|
|
IFLA_BOND_CL_LACP_BYPASS_ACTIVE : 'IFLA_BOND_CL_LACP_BYPASS_ACTIVE',
|
|
IFLA_BOND_CL_LACP_BYPASS_PERIOD : 'IFLA_BOND_CL_LACP_BYPASS_PERIOD',
|
|
IFLA_BOND_CL_CLAG_ENABLE : 'IFLA_BOND_CL_CLAG_ENABLE',
|
|
IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE : 'IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE'
|
|
}
|
|
|
|
# =========================================
|
|
# IFLA_INFO_DATA attributes for bridges
|
|
# =========================================
|
|
IFLA_BRPORT_UNSPEC = 0
|
|
IFLA_BRPORT_STATE = 1
|
|
IFLA_BRPORT_PRIORITY = 2
|
|
IFLA_BRPORT_COST = 3
|
|
IFLA_BRPORT_MODE = 4
|
|
IFLA_BRPORT_GUARD = 5
|
|
IFLA_BRPORT_PROTECT = 6
|
|
IFLA_BRPORT_FAST_LEAVE = 7
|
|
IFLA_BRPORT_LEARNING = 8
|
|
IFLA_BRPORT_UNICAST_FLOOD = 9
|
|
IFLA_BRPORT_PROXYARP = 10
|
|
IFLA_BRPORT_LEARNING_SYNC = 11
|
|
IFLA_BRPORT_PROXYARP_WIFI = 12
|
|
IFLA_BRPORT_ROOT_ID = 13
|
|
IFLA_BRPORT_BRIDGE_ID = 14
|
|
IFLA_BRPORT_DESIGNATED_PORT = 15
|
|
IFLA_BRPORT_DESIGNATED_COST = 16
|
|
IFLA_BRPORT_ID = 17
|
|
IFLA_BRPORT_NO = 18
|
|
IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19
|
|
IFLA_BRPORT_CONFIG_PENDING = 20
|
|
IFLA_BRPORT_MESSAGE_AGE_TIMER = 21
|
|
IFLA_BRPORT_FORWARD_DELAY_TIMER = 22
|
|
IFLA_BRPORT_HOLD_TIMER = 23
|
|
IFLA_BRPORT_FLUSH = 24
|
|
IFLA_BRPORT_MULTICAST_ROUTER = 25
|
|
IFLA_BRPORT_PEER_LINK = 150
|
|
IFLA_BRPORT_DUAL_LINK = 151
|
|
|
|
ifla_bridge_to_string = {
|
|
IFLA_BRPORT_UNSPEC : 'IFLA_BRPORT_UNSPEC',
|
|
IFLA_BRPORT_STATE : 'IFLA_BRPORT_STATE',
|
|
IFLA_BRPORT_PRIORITY : 'IFLA_BRPORT_PRIORITY',
|
|
IFLA_BRPORT_COST : 'IFLA_BRPORT_COST',
|
|
IFLA_BRPORT_MODE : 'IFLA_BRPORT_MODE',
|
|
IFLA_BRPORT_GUARD : 'IFLA_BRPORT_GUARD',
|
|
IFLA_BRPORT_PROTECT : 'IFLA_BRPORT_PROTECT',
|
|
IFLA_BRPORT_FAST_LEAVE : 'IFLA_BRPORT_FAST_LEAVE',
|
|
IFLA_BRPORT_LEARNING : 'IFLA_BRPORT_LEARNING',
|
|
IFLA_BRPORT_UNICAST_FLOOD : 'IFLA_BRPORT_UNICAST_FLOOD',
|
|
IFLA_BRPORT_PROXYARP : 'IFLA_BRPORT_PROXYARP',
|
|
IFLA_BRPORT_LEARNING_SYNC : 'IFLA_BRPORT_LEARNING_SYNC',
|
|
IFLA_BRPORT_PROXYARP_WIFI : 'IFLA_BRPORT_PROXYARP_WIFI',
|
|
IFLA_BRPORT_ROOT_ID : 'IFLA_BRPORT_ROOT_ID',
|
|
IFLA_BRPORT_BRIDGE_ID : 'IFLA_BRPORT_BRIDGE_ID',
|
|
IFLA_BRPORT_DESIGNATED_PORT : 'IFLA_BRPORT_DESIGNATED_PORT',
|
|
IFLA_BRPORT_DESIGNATED_COST : 'IFLA_BRPORT_DESIGNATED_COST',
|
|
IFLA_BRPORT_ID : 'IFLA_BRPORT_ID',
|
|
IFLA_BRPORT_NO : 'IFLA_BRPORT_NO',
|
|
IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK',
|
|
IFLA_BRPORT_CONFIG_PENDING : 'IFLA_BRPORT_CONFIG_PENDING',
|
|
IFLA_BRPORT_MESSAGE_AGE_TIMER : 'IFLA_BRPORT_MESSAGE_AGE_TIMER',
|
|
IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER',
|
|
IFLA_BRPORT_HOLD_TIMER : 'IFLA_BRPORT_HOLD_TIMER',
|
|
IFLA_BRPORT_FLUSH : 'IFLA_BRPORT_FLUSH',
|
|
IFLA_BRPORT_MULTICAST_ROUTER : 'IFLA_BRPORT_MULTICAST_ROUTER',
|
|
IFLA_BRPORT_PEER_LINK : 'IFLA_BRPORT_PEER_LINK',
|
|
IFLA_BRPORT_DUAL_LINK : 'IFLA_BRPORT_DUAL_LINK'
|
|
}
|
|
|
|
# BRIDGE IFLA_AF_SPEC attributes
|
|
IFLA_BRIDGE_FLAGS = 0
|
|
IFLA_BRIDGE_MODE = 1
|
|
IFLA_BRIDGE_VLAN_INFO = 2
|
|
|
|
ifla_bridge_af_spec_to_string = {
|
|
IFLA_BRIDGE_FLAGS : 'IFLA_BRIDGE_FLAGS',
|
|
IFLA_BRIDGE_MODE : 'IFLA_BRIDGE_MODE',
|
|
IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO'
|
|
}
|
|
|
|
# BRIDGE_VLAN_INFO flags
|
|
BRIDGE_VLAN_INFO_MASTER = 1
|
|
BRIDGE_VLAN_INFO_PVID = 2
|
|
BRIDGE_VLAN_INFO_UNTAGGED = 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 flags
|
|
BRIDGE_FLAGS_MASTER = 1
|
|
BRIDGE_FLAGS_SELF = 2
|
|
|
|
bridge_flags_to_string = {
|
|
BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER',
|
|
BRIDGE_FLAGS_SELF : 'BRIDGE_FLAGS_SELF'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug=False, logger=None):
|
|
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
|
self.PACK = 'BxHiII'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def get_link_type_string(self, index):
|
|
return self.get_string(self.link_type_to_string, index)
|
|
|
|
def get_ifla_bridge_af_spec_to_string(self, index):
|
|
return self.get_string(self.ifla_bridge_af_spec_to_string, index)
|
|
|
|
def get_ifla_info_string(self, index):
|
|
return self.get_string(self.ifla_info_to_string, index)
|
|
|
|
def get_ifla_vlan_string(self, index):
|
|
return self.get_string(self.ifla_vlan_to_string, index)
|
|
|
|
def get_ifla_vxlan_string(self, index):
|
|
return self.get_string(self.ifla_vxlan_to_string, index)
|
|
|
|
def get_ifla_macvlan_string(self, index):
|
|
return self.get_string(self.ifla_macvlan_to_string, index)
|
|
|
|
def get_macvlan_mode_string(self, index):
|
|
return self.get_string(self.macvlan_mode_to_string, index)
|
|
|
|
def get_ifla_bond_string(self, index):
|
|
return self.get_string(self.ifla_bond_to_string, index)
|
|
|
|
def get_ifla_bridge_string(self, index):
|
|
return self.get_string(self.ifla_bridge_to_string, index)
|
|
|
|
def get_bridge_vlan_string(self, index):
|
|
return self.get_string(self.bridge_vlan_to_string, index)
|
|
|
|
def get_bridge_flags_string(self, index):
|
|
return self.get_string(self.bridge_flags_to_string, index)
|
|
|
|
def decode_service_header(self):
|
|
|
|
# Nothing to do if the message did not contain a service header
|
|
if self.length == self.header_LEN:
|
|
return
|
|
|
|
(self.family, self.device_type,
|
|
self.ifindex,
|
|
self.flags,
|
|
self.change_mask) = \
|
|
unpack(self.PACK, self.msg_data[:self.LEN])
|
|
|
|
if self.debug:
|
|
color = yellow
|
|
self.dump_buffer.append(" \033[%dmService Header\033[0m" % color)
|
|
for x in range(0, self.LEN/4):
|
|
if self.line_number == 5:
|
|
extra = "Family %s (%d), Device Type %s (%d - %s)" % \
|
|
(zfilled_hex(self.family, 2), self.family,
|
|
zfilled_hex(self.device_type, 4), self.device_type, self.get_link_type_string(self.device_type))
|
|
elif self.line_number == 6:
|
|
extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
|
|
elif self.line_number == 7:
|
|
extra = "Device Flags %s (%s)" % (zfilled_hex(self.flags, 8), self.get_flags_string())
|
|
elif self.line_number == 8:
|
|
extra = "Change Mask %s" % zfilled_hex(self.change_mask, 8)
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
start = x * 4
|
|
end = start + 4
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
|
|
self.line_number += 1
|
|
|
|
def is_up(self):
|
|
if self.flags & Link.IFF_UP:
|
|
return True
|
|
return False
|
|
|
|
|
|
class Neighbor(NetlinkPacket):
|
|
"""
|
|
Service Header
|
|
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Family | Reserved1 | Reserved2 |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Interface Index |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| State | Flags | Type |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
"""
|
|
|
|
# Neighbor attributes
|
|
# /usr/include/linux/neighbour.h
|
|
NDA_UNSPEC = 0x00 # Unknown type
|
|
NDA_DST = 0x01 # A neighbour cache network. layer destination address
|
|
NDA_LLADDR = 0x02 # A neighbor cache link layer address.
|
|
NDA_CACHEINFO = 0x03 # Cache statistics
|
|
NDA_PROBES = 0x04
|
|
NDA_VLAN = 0x05
|
|
NDA_PORT = 0x06
|
|
NDA_VNI = 0x07
|
|
NDA_IFINDEX = 0x08
|
|
NDA_MASTER = 0x09
|
|
NDA_LINK_NETNSID = 0x0A
|
|
|
|
attribute_to_class = {
|
|
NDA_UNSPEC : ('NDA_UNSPEC', AttributeGeneric),
|
|
NDA_DST : ('NDA_DST', AttributeIPAddress),
|
|
NDA_LLADDR : ('NDA_LLADDR', AttributeMACAddress),
|
|
NDA_CACHEINFO : ('NDA_CACHEINFO', AttributeGeneric),
|
|
NDA_PROBES : ('NDA_PROBES', AttributeFourByteValue),
|
|
NDA_VLAN : ('NDA_VLAN', AttributeGeneric),
|
|
NDA_PORT : ('NDA_PORT', AttributeGeneric),
|
|
NDA_VNI : ('NDA_VNI', AttributeGeneric),
|
|
NDA_IFINDEX : ('NDA_IFINDEX', AttributeGeneric),
|
|
NDA_MASTER : ('NDA_MASTER', AttributeGeneric),
|
|
NDA_LINK_NETNSID : ('NDA_LINK_NETNSID', AttributeGeneric)
|
|
}
|
|
|
|
# Neighbor flags
|
|
# /usr/include/linux/neighbour.h
|
|
NTF_USE = 0x01
|
|
NTF_PROXY = 0x08 # A proxy ARP entry
|
|
NTF_ROUTER = 0x80 # An IPv6 router
|
|
|
|
flag_to_string = {
|
|
NTF_USE : 'NTF_USE',
|
|
NTF_PROXY : 'NTF_PROXY',
|
|
NTF_ROUTER : 'NTF_ROUTER'
|
|
}
|
|
|
|
# Neighbor states
|
|
# /usr/include/linux/neighbour.h
|
|
NUD_NONE = 0x00
|
|
NUD_INCOMPLETE = 0x01 # Still attempting to resolve
|
|
NUD_REACHABLE = 0x02 # A confirmed working cache entry
|
|
NUD_STALE = 0x04 # an expired cache entry
|
|
NUD_DELAY = 0x08 # Neighbor no longer reachable. Traffic sent, waiting for confirmatio.
|
|
NUD_PROBE = 0x10 # A cache entry that is currently being re-solicited
|
|
NUD_FAILED = 0x20 # An invalid cache entry
|
|
NUD_NOARP = 0x40 # A device which does not do neighbor discovery(ARP)
|
|
NUD_PERMANENT = 0x80 # A static entry
|
|
|
|
state_to_string = {
|
|
NUD_NONE : 'NUD_NONE',
|
|
NUD_INCOMPLETE : 'NUD_INCOMPLETE',
|
|
NUD_REACHABLE : 'NUD_REACHABLE',
|
|
NUD_STALE : 'NUD_STALE',
|
|
NUD_DELAY : 'NUD_DELAY',
|
|
NUD_PROBE : 'NUD_PROBE',
|
|
NUD_FAILED : 'NUD_FAILED',
|
|
NUD_NOARP : 'NUD_NOARP',
|
|
NUD_PERMANENT : 'NUD_PERMANENT'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug=False, logger=None):
|
|
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
|
self.PACK = 'BxxxiHBB'
|
|
self.LEN = calcsize(self.PACK)
|
|
|
|
def get_state_string(self, index):
|
|
return self.get_string(self.state_to_string, index)
|
|
|
|
def decode_service_header(self):
|
|
|
|
# Nothing to do if the message did not contain a service header
|
|
if self.length == self.header_LEN:
|
|
return
|
|
|
|
(self.family,
|
|
self.ifindex,
|
|
self.state, self.flags, self.neighbor_type) = \
|
|
unpack(self.PACK, self.msg_data[:self.LEN])
|
|
|
|
if self.debug:
|
|
color = yellow
|
|
self.dump_buffer.append(" \033[%dmService Header\033[0m" % color)
|
|
|
|
for x in range(0, self.LEN/4):
|
|
if self.line_number == 5:
|
|
extra = "Family %s (%d)" % (zfilled_hex(self.family, 2), self.family)
|
|
elif self.line_number == 6:
|
|
extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
|
|
elif self.line_number == 7:
|
|
extra = "State %s (%d), Flags %s, Type %s (%d)" % \
|
|
(zfilled_hex(self.state, 4), self.state,
|
|
zfilled_hex(self.flags, 2),
|
|
zfilled_hex(self.neighbor_type, 4), self.neighbor_type)
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
start = x * 4
|
|
end = start + 4
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
|
|
self.line_number += 1
|
|
|
|
|
|
class Route(NetlinkPacket):
|
|
"""
|
|
Service Header
|
|
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Family | Dest length | Src length | TOS |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Table ID | Protocol | Scope | Type |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Flags |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
"""
|
|
|
|
# Route attributes
|
|
# /usr/include/linux/rtnetlink.h
|
|
RTA_UNSPEC = 0x00 # Ignored.
|
|
RTA_DST = 0x01 # Protocol address for route destination address.
|
|
RTA_SRC = 0x02 # Protocol address for route source address.
|
|
RTA_IIF = 0x03 # Input interface index.
|
|
RTA_OIF = 0x04 # Output interface index.
|
|
RTA_GATEWAY = 0x05 # Protocol address for the gateway of the route
|
|
RTA_PRIORITY = 0x06 # Priority of broker.
|
|
RTA_PREFSRC = 0x07 # Preferred source address in cases where more than one source address could be used.
|
|
RTA_METRICS = 0x08 # Route metrics attributed to route and associated protocols(e.g., RTT, initial TCP window, etc.).
|
|
RTA_MULTIPATH = 0x09 # Multipath route next hop's attributes.
|
|
RTA_PROTOINFO = 0x0A # Firewall based policy routing attribute.
|
|
RTA_FLOW = 0x0B # Route realm.
|
|
RTA_CACHEINFO = 0x0C # Cached route information.
|
|
RTA_SESSION = 0x0D
|
|
RTA_MP_ALGO = 0x0E
|
|
RTA_TABLE = 0x0F
|
|
RTA_MARK = 0x10
|
|
RTA_MFC_STATS = 0x11
|
|
RTA_VIA = 0x12
|
|
RTA_NEWDST = 0x13
|
|
RTA_PREF = 0x14
|
|
RTA_ENCAP_TYPE= 0x15
|
|
RTA_ENCAP = 0x16
|
|
|
|
attribute_to_class = {
|
|
RTA_UNSPEC : ('RTA_UNSPEC', AttributeGeneric),
|
|
RTA_DST : ('RTA_DST', AttributeIPAddress),
|
|
RTA_SRC : ('RTA_SRC', AttributeIPAddress),
|
|
RTA_IIF : ('RTA_IIF', AttributeFourByteValue),
|
|
RTA_OIF : ('RTA_OIF', AttributeFourByteValue),
|
|
RTA_GATEWAY : ('RTA_GATEWAY', AttributeIPAddress),
|
|
RTA_PRIORITY : ('RTA_PRIORITY', AttributeFourByteValue),
|
|
RTA_PREFSRC : ('RTA_PREFSRC', AttributeIPAddress),
|
|
RTA_METRICS : ('RTA_METRICS', AttributeGeneric),
|
|
RTA_MULTIPATH : ('RTA_MULTIPATH', AttributeRTA_MULTIPATH),
|
|
RTA_PROTOINFO : ('RTA_PROTOINFO', AttributeGeneric),
|
|
RTA_FLOW : ('RTA_FLOW', AttributeGeneric),
|
|
RTA_CACHEINFO : ('RTA_CACHEINFO', AttributeGeneric),
|
|
RTA_SESSION : ('RTA_SESSION', AttributeGeneric),
|
|
RTA_MP_ALGO : ('RTA_MP_ALGO', AttributeGeneric),
|
|
RTA_TABLE : ('RTA_TABLE', AttributeFourByteValue),
|
|
RTA_MARK : ('RTA_MARK', AttributeGeneric),
|
|
RTA_MFC_STATS : ('RTA_MFC_STATS', AttributeGeneric),
|
|
RTA_VIA : ('RTA_VIA', AttributeGeneric),
|
|
RTA_NEWDST : ('RTA_NEWDST', AttributeGeneric),
|
|
RTA_PREF : ('RTA_PREF', AttributeGeneric),
|
|
RTA_ENCAP_TYPE: ('RTA_ENCAP_TYPE', AttributeGeneric),
|
|
RTA_ENCAP : ('RTA_ENCAP', AttributeGeneric)
|
|
}
|
|
|
|
# Route tables
|
|
# /usr/include/linux/rtnetlink.h
|
|
RT_TABLE_UNSPEC = 0x00 # An unspecified routing table
|
|
RT_TABLE_COMPAT = 0xFC
|
|
RT_TABLE_DEFAULT = 0xFD # The default table
|
|
RT_TABLE_MAIN = 0xFE # The main table
|
|
RT_TABLE_LOCAL = 0xFF # The local table
|
|
|
|
table_to_string = {
|
|
RT_TABLE_UNSPEC : 'RT_TABLE_UNSPEC',
|
|
RT_TABLE_COMPAT : 'RT_TABLE_COMPAT',
|
|
RT_TABLE_DEFAULT : 'RT_TABLE_DEFAULT',
|
|
RT_TABLE_MAIN : 'RT_TABLE_MAIN',
|
|
RT_TABLE_LOCAL : 'RT_TABLE_LOCAL'
|
|
}
|
|
|
|
# Route scope
|
|
# /usr/include/linux/rtnetlink.h
|
|
RT_SCOPE_UNIVERSE = 0x00 # Global route
|
|
RT_SCOPE_SITE = 0xC8 # Interior route in the local autonomous system
|
|
RT_SCOPE_LINK = 0xFD # Route on this link
|
|
RT_SCOPE_HOST = 0xFE # Route on the local host
|
|
RT_SCOPE_NOWHERE = 0xFF # Destination does not exist
|
|
|
|
scope_to_string = {
|
|
RT_SCOPE_UNIVERSE : 'RT_SCOPE_UNIVERSE',
|
|
RT_SCOPE_SITE : 'RT_SCOPE_SITE',
|
|
RT_SCOPE_LINK : 'RT_SCOPE_LINK',
|
|
RT_SCOPE_HOST : 'RT_SCOPE_HOST',
|
|
RT_SCOPE_NOWHERE : 'RT_SCOPE_NOWHERE'
|
|
}
|
|
|
|
# Routing stack
|
|
# /usr/include/linux/rtnetlink.h
|
|
RT_PROT_UNSPEC = 0x00 # Identifies what/who added the route
|
|
RT_PROT_REDIRECT = 0x01 # By an ICMP redirect
|
|
RT_PROT_KERNEL = 0x02 # By the kernel
|
|
RT_PROT_BOOT = 0x03 # During bootup
|
|
RT_PROT_STATIC = 0x04 # By the administrator
|
|
RT_PROT_GATED = 0x08 # GateD
|
|
RT_PROT_RA = 0x09 # RDISC/ND router advertissements
|
|
RT_PROT_MRT = 0x0A # Merit MRT
|
|
RT_PROT_ZEBRA = 0x0B # ZEBRA
|
|
RT_PROT_BIRD = 0x0C # BIRD
|
|
RT_PROT_DNROUTED = 0x0D # DECnet routing daemon
|
|
RT_PROT_XORP = 0x0E # XORP
|
|
RT_PROT_NTK = 0x0F # Netsukuku
|
|
RT_PROT_DHCP = 0x10 # DHCP client
|
|
RT_PROT_EXABGP = 0x11 # Exa Networks ExaBGP
|
|
|
|
prot_to_string = {
|
|
RT_PROT_UNSPEC : 'RT_PROT_UNSPEC',
|
|
RT_PROT_REDIRECT : 'RT_PROT_REDIRECT',
|
|
RT_PROT_KERNEL : 'RT_PROT_KERNEL',
|
|
RT_PROT_BOOT : 'RT_PROT_BOOT',
|
|
RT_PROT_STATIC : 'RT_PROT_STATIC',
|
|
RT_PROT_GATED : 'RT_PROT_GATED',
|
|
RT_PROT_RA : 'RT_PROT_RA',
|
|
RT_PROT_MRT : 'RT_PROT_MRT',
|
|
RT_PROT_ZEBRA : 'RT_PROT_ZEBRA',
|
|
RT_PROT_BIRD : 'RT_PROT_BIRD',
|
|
RT_PROT_DNROUTED : 'RT_PROT_DNROUTED',
|
|
RT_PROT_XORP : 'RT_PROT_XORP',
|
|
RT_PROT_NTK : 'RT_PROT_NTK',
|
|
RT_PROT_DHCP : 'RT_PROT_DHCP',
|
|
RT_PROT_EXABGP : 'RT_PROT_EXABGP'
|
|
}
|
|
|
|
# Route types
|
|
# /usr/include/linux/rtnetlink.h
|
|
RTN_UNSPEC = 0x00 # Unknown broker.
|
|
RTN_UNICAST = 0x01 # A gateway or direct broker.
|
|
RTN_LOCAL = 0x02 # A local interface broker.
|
|
RTN_BROADCAST = 0x03 # A local broadcast route(sent as a broadcast).
|
|
RTN_ANYCAST = 0x04 # An anycast broker.
|
|
RTN_MULTICAST = 0x05 # A multicast broker.
|
|
RTN_BLACKHOLE = 0x06 # A silent packet dropping broker.
|
|
RTN_UNREACHABLE = 0x07 # An unreachable destination. Packets dropped and
|
|
# host unreachable ICMPs are sent to the originator.
|
|
RTN_PROHIBIT = 0x08 # A packet rejection broker. Packets are dropped and
|
|
# communication prohibited ICMPs are sent to the originator.
|
|
RTN_THROW = 0x09 # When used with policy routing, continue routing lookup
|
|
# in another table. Under normal routing, packets are
|
|
# dropped and net unreachable ICMPs are sent to the originator.
|
|
RTN_NAT = 0x0A # A network address translation rule.
|
|
RTN_XRESOLVE = 0x0B # Refer to an external resolver(not implemented).
|
|
|
|
rt_type_to_string = {
|
|
RTN_UNSPEC : 'RTN_UNSPEC',
|
|
RTN_UNICAST : 'RTN_UNICAST',
|
|
RTN_LOCAL : 'RTN_LOCAL',
|
|
RTN_BROADCAST : 'RTN_BROADCAST',
|
|
RTN_ANYCAST : 'RTN_ANYCAST',
|
|
RTN_MULTICAST : 'RTN_MULTICAST',
|
|
RTN_BLACKHOLE : 'RTN_BLACKHOLE',
|
|
RTN_UNREACHABLE : 'RTN_UNREACHABLE',
|
|
RTN_PROHIBIT : 'RTN_PROHIBIT',
|
|
RTN_THROW : 'RTN_THROW',
|
|
RTN_NAT : 'RTN_NAT',
|
|
RTN_XRESOLVE : 'RTN_XRESOLVE'
|
|
}
|
|
|
|
# Route flags
|
|
# /usr/include/linux/rtnetlink.h
|
|
RTM_F_NOTIFY = 0x100 # If the route changes, notify the user
|
|
RTM_F_CLONED = 0x200 # Route is cloned from another route
|
|
RTM_F_EQUALIZE = 0x400 # Allow randomization of next hop path in multi-path routing(currently not implemented)
|
|
RTM_F_PREFIX = 0x800 # Prefix Address
|
|
|
|
flag_to_string = {
|
|
RTM_F_NOTIFY : 'RTM_F_NOTIFY',
|
|
RTM_F_CLONED : 'RTM_F_CLONED',
|
|
RTM_F_EQUALIZE : 'RTM_F_EQUALIZE',
|
|
RTM_F_PREFIX : 'RTM_F_PREFIX'
|
|
}
|
|
|
|
def __init__(self, msgtype, debug=False, logger=None):
|
|
NetlinkPacket.__init__(self, msgtype, debug, logger)
|
|
self.PACK = '=8BI' # or is it 8Bi ?
|
|
self.LEN = calcsize(self.PACK)
|
|
self.family = None
|
|
|
|
def get_prefix_string(self):
|
|
dst = self.get_attribute_value(self.RTA_DST)
|
|
|
|
if dst:
|
|
return "%s/%d" % (dst, self.src_len)
|
|
else:
|
|
if self.family == AF_INET:
|
|
return "0.0.0.0/0"
|
|
elif self.family == AF_INET6:
|
|
return "::/0"
|
|
|
|
def get_protocol_string(self, index=None):
|
|
if index is None:
|
|
index = self.protocol
|
|
return self.get_string(self.prot_to_string, index)
|
|
|
|
def get_rt_type_string(self, index=None):
|
|
if index is None:
|
|
index = self.route_type
|
|
return self.get_string(self.rt_type_to_string, index)
|
|
|
|
def get_scope_string(self, index=None):
|
|
if index is None:
|
|
index = self.scope
|
|
return self.get_string(self.scope_to_string, index)
|
|
|
|
def get_table_id_string(self, index=None):
|
|
if index is None:
|
|
index = self.table_id
|
|
return self.get_string(self.table_to_string, index)
|
|
|
|
def _get_ifname_from_index(self, ifindex, ifname_by_index):
|
|
if ifindex:
|
|
ifname = ifname_by_index.get(ifindex)
|
|
|
|
if ifname is None:
|
|
ifname = str(ifindex)
|
|
else:
|
|
ifname = None
|
|
|
|
return ifname
|
|
|
|
def get_nexthops(self, ifname_by_index={}):
|
|
nexthop = self.get_attribute_value(self.RTA_GATEWAY)
|
|
multipath = self.get_attribute_value(self.RTA_MULTIPATH)
|
|
nexthops = []
|
|
|
|
if nexthop:
|
|
rta_oif = self.get_attribute_value(self.RTA_OIF)
|
|
ifname = self._get_ifname_from_index(rta_oif, ifname_by_index)
|
|
nexthops.append((nexthop, ifname))
|
|
|
|
elif multipath:
|
|
for (nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops) in multipath:
|
|
ifname = self._get_ifname_from_index(rtnh_ifindex, ifname_by_index)
|
|
nexthops.append((nexthop, ifname))
|
|
|
|
return nexthops
|
|
|
|
def get_nexthops_string(self, ifname_by_index={}):
|
|
output = []
|
|
|
|
for (nexthop, ifname) in self.get_nexthops(ifname_by_index):
|
|
output.append(" via %s on %s" % (nexthop, ifname))
|
|
|
|
return ",".join(output)
|
|
|
|
def decode_service_header(self):
|
|
|
|
# Nothing to do if the message did not contain a service header
|
|
if self.length == self.header_LEN:
|
|
return
|
|
|
|
(self.family, self.src_len, self.dst_len, self.tos,
|
|
self.table_id, self.protocol, self.scope, self.route_type,
|
|
self.flags) = \
|
|
unpack(self.PACK, self.msg_data[:self.LEN])
|
|
|
|
if self.debug:
|
|
color = yellow
|
|
self.dump_buffer.append(" \033[%dmService Header\033[0m" % color)
|
|
|
|
for x in range(0, self.LEN/4):
|
|
if self.line_number == 5:
|
|
extra = "Family %s (%d), Source Length %s (%d), Destination Length %s (%d), TOS %s (%d)" % \
|
|
(zfilled_hex(self.family, 2), self.family,
|
|
zfilled_hex(self.src_len, 2), self.src_len,
|
|
zfilled_hex(self.dst_len, 2), self.dst_len,
|
|
zfilled_hex(self.tos, 2), self.tos)
|
|
elif self.line_number == 6:
|
|
extra = "Table ID %s (%d - %s), Protocol %s (%d - %s), Scope %s (%d - %s), Type %s (%d - %s)" % \
|
|
(zfilled_hex(self.table_id, 2), self.table_id, self.get_table_id_string(),
|
|
zfilled_hex(self.protocol, 2), self.protocol, self.get_protocol_string(),
|
|
zfilled_hex(self.scope, 2), self.scope, self.get_scope_string(),
|
|
zfilled_hex(self.route_type, 2), self.route_type, self.get_rt_type_string())
|
|
elif self.line_number == 7:
|
|
extra = "Flags %s" % zfilled_hex(self.flags, 8)
|
|
else:
|
|
extra = "Unexpected line number %d" % self.line_number
|
|
|
|
start = x * 4
|
|
end = start + 4
|
|
self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
|
|
self.line_number += 1
|