summaryrefslogtreecommitdiffstats
path: root/node-admin/scripts/pyroute2/netlink/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'node-admin/scripts/pyroute2/netlink/__init__.py')
-rw-r--r--node-admin/scripts/pyroute2/netlink/__init__.py1349
1 files changed, 0 insertions, 1349 deletions
diff --git a/node-admin/scripts/pyroute2/netlink/__init__.py b/node-admin/scripts/pyroute2/netlink/__init__.py
deleted file mode 100644
index 499c7ab79d3..00000000000
--- a/node-admin/scripts/pyroute2/netlink/__init__.py
+++ /dev/null
@@ -1,1349 +0,0 @@
-# By Peter V. Saveliev https://pypi.python.org/pypi/pyroute2. Dual licensed under the Apache 2 and GPLv2+ see https://github.com/svinota/pyroute2 for License details.
-'''
-Netlink basics
-==============
-
-General netlink packet structure::
-
- nlmsg packet:
- + header
- + data
-
-Generic netlink message header::
-
- nlmsg header:
- + uint32 length
- + uint16 type
- + uint16 flags
- + uint32 sequence number
- + uint32 pid
-
-The `length` field is the length of all the packet, including
-data and header. The `type` field is used to distinguish different
-message types, commands etc. Please note, that there is no
-explicit protocol field -- you choose a netlink protocol, when
-you create a socket.
-
-The `sequence number` is very important. Netlink is an asynchronous
-protocol -- it means, that the packet order doesn't matter and is
-not guaranteed. But responses to a request are always marked with
-the same sequence number, so you can treat it as a cookie.
-
-Please keep in mind, that a netlink request can initiate a
-cascade of events, and netlink messages from these events can
-carry sequence number == 0. E.g., it is so when you remove a
-primary IP addr from an interface, when `promote_secondaries`
-sysctl is set.
-
-Beside of incapsulated headers and other protocol-specific data,
-netlink messages can carry NLA (netlink attributes). NLA
-structure is as follows::
-
- NLA header:
- + uint16 length
- + uint16 type
- NLA data:
- + data-specific struct
- # optional:
- + NLA
- + NLA
- + ...
-
-So, NLA structures can be nested, forming a tree.
-
-Complete structure of a netlink packet::
-
- nlmsg header:
- + uint32 length
- + uint16 type
- + uint16 flags
- + uint32 sequence number
- + uint32 pid
- [ optional protocol-specific data ]
- [ optional NLA tree ]
-
-More information about netlink protocol you can find in
-the man pages.
-
-Pyroute2 and netlink
-====================
-
-packets
--------
-
-To simplify the development, pyroute2 provides an easy way to
-describe packet structure. As an example, you can take the
-ifaddrmsg description -- `pyroute2/netlink/rtnl/ifaddrmsg.py`.
-
-To describe a packet, you need to inherit from `nlmsg` class::
-
- from pyroute2.netlink import nlmsg
-
- class foo_msg(nlmsg):
- fields = ( ... )
- nla_map = ( ... )
-
-NLA are described in the same way, but the parent class should be
-`nla`, instead of `nlmsg`. And yes, it is important to use the
-proper parent class -- it affects the header structure.
-
-fields attribute
-----------------
-
-The `fields` attribute describes the structure of the
-protocol-specific data. It is a tuple of tuples, where each
-member contains a field name and its data format.
-
-Field data format should be specified as for Python `struct`
-module. E.g., ifaddrmsg structure::
-
- ifaddrmsg structure:
- + unsigned char ifa_family
- + unsigned char ifa_prefixlen
- + unsigned char ifa_flags
- + unsigned char ifa_scope
- + int ifa_index
-
-should be described as follows::
-
- class ifaddrmsg(nlmsg):
- fields = (('family', 'B'),
- ('prefixlen', 'B'),
- ('flags', 'B'),
- ('scope', 'B'),
- ('index', 'I'))
-
-Format strings are passed directly to the `struct` module,
-so you can use all the notations like `>I`, `16s` etc. All
-fields are parsed from the stream separately, so if you
-want to explicitly fix alignemt, as if it were C struct,
-use the `pack` attribute::
-
- class tstats(nla):
- pack = 'struct'
- fields = (('version', 'H'),
- ('ac_exitcode', 'I'),
- ('ac_flag', 'B'),
- ...)
-
-Explicit padding bytes also can be used, when struct
-packing doesn't work well::
-
- class ipq_mode_msg(nlmsg):
- pack = 'struct'
- fields = (('value', 'B'),
- ('__pad', '7x'),
- ('range', 'I'),
- ('__pad', '12x'))
-
-
-nla_map attribute
------------------
-
-The `nla_map` attribute is a tuple of NLA descriptions. Each
-description is also a tuple in two different forms: either
-two fields, name and format, or three fields: type, name and
-format.
-
-Please notice, that the format field is a string name of
-corresponding NLA class::
-
- class ifaddrmsg(nlmsg):
- ...
- nla_map = (('IFA_UNSPEC', 'hex'),
- ('IFA_ADDRESS', 'ipaddr'),
- ('IFA_LOCAL', 'ipaddr'),
- ...)
-
-This code will create mapping, where IFA_ADDRESS NLA will be of
-type 1 and IFA_LOCAL -- of type 2, etc. Both NLA will be decoded
-as IP addresses (class `ipaddr`). IFA_UNSPEC will be of type 0,
-and if it will be in the NLA tree, it will be just dumped in hex.
-
-NLA class names are should be specified as strings, since they
-are resolved in runtime.
-
-There are several pre-defined NLA types, that you will get with
-`nla` class:
-
- - none # forces pyroute2 just to skip this NLA
- - uint8
- - uint16
- - uint32 # there are dedicated NLA of these types as well
- - ipaddr # IP address, IPv4 or IPv6, depending on the socket
- - l2addr # MAC address
- - hex # hex dump as a string -- useful for debugging
- - cdata # just a binary string
- - asciiz # zero-terminated ASCII string
-
-Please refer to `pyroute2/netlink/__init__.py` for details.
-
-You can also make your own NLA descriptions::
-
- class ifaddrmsg(nlmsg):
- ...
- nla_map = (...
- ('IFA_CACHEINFO', 'cacheinfo'),
- ...)
-
- class cacheinfo(nla):
- fields = (('ifa_prefered', 'I'),
- ('ifa_valid', 'I'),
- ('cstamp', 'I'),
- ('tstamp', 'I'))
-
-Custom NLA descriptions should be defined in the same class,
-where they are used.
-
-Also, it is possible to use not autogenerated type numbers, as
-for ifaddrmsg, but specify them explicitly::
-
- class iw_event(nla):
- ...
- nla_map = ((0x8B00, 'SIOCSIWCOMMIT', 'hex'),
- (0x8B01, 'SIOCGIWNAME', 'hex'),
- (0x8B02, 'SIOCSIWNWID', 'hex'),
- (0x8B03, 'SIOCGIWNWID', 'hex'),
- ...)
-
-Here you can see custom NLA type numbers -- 0x8B00, 0x8B01 etc.
-It is not permitted to mix these two forms in one class: you should
-use ether autogenerated type numbers (two fields tuples), or
-explicit numbers (three fields typles).
-
-parsed netlink message
-----------------------
-
-Netlink messages are represented by pyroute2 as dictionaries
-as follows::
-
- {'header': {'pid': ...,
- 'length: ...,
- 'flags': ...,
- 'error': None, # if you are lucky
- 'type': ...,
- 'sequence_number': ...},
-
- # fields attributes
- 'field_name1': value,
- ...
- 'field_nameX': value,
-
- # nla tree
- 'attrs': [['NLA_NAME1', value],
- ...
- ['NLA_NAMEX', value],
- ['NLA_NAMEY', {'field_name1': value,
- ...
- 'field_nameX': value,
- 'attrs': [['NLA_NAME.... ]]}]]}
-
-As an example, a message from the wireless subsystem about new
-scan event::
-
- {'index': 4,
- 'family': 0,
- '__align': 0,
- 'header': {'pid': 0,
- 'length': 64,
- 'flags': 0,
- 'error': None,
- 'type': 16,
- 'sequence_number': 0},
- 'flags': 69699,
- 'ifi_type': 1,
- 'event': 'RTM_NEWLINK',
- 'change': 0,
- 'attrs': [['IFLA_IFNAME', 'wlp3s0'],
- ['IFLA_WIRELESS',
- {'attrs': [['SIOCGIWSCAN',
- '00:00:00:00:00:00:00:00:00:00:00:00']]}]]}
-
-create and send messages
-------------------------
-
-Using high-level interfaces like `IPRoute` or `IPDB`, you will never
-need to manually construct and send netlink messages. But in the case
-you really need it, it is simple as well.
-
-Having a description class, like `ifaddrmsg` from above, you need to:
-
- - instantiate it
- - fill the fields
- - encode the packet
- - send the encoded data
-
-The code::
-
- from pyroute2.netlink import NLM_F_REQUEST
- from pyroute2.netlink import NLM_F_ACK
- from pyroute2.netlink import NLM_F_CREATE
- from pyroute2.netlink import NLM_F_EXCL
- from pyroute2.iproute import RTM_NEWADDR
- from pyroute2.netlink.rtnl.ifaddrmsg import ifaddrmsg
-
- ##
- # add an addr to an interface
- #
-
- # create the message
- msg = ifaddrmsg()
-
- # fill the protocol-specific fields
- msg['index'] = index # index of the interface
- msg['family'] = AF_INET # address family
- msg['prefixlen'] = 24 # the address mask
- msg['scope'] = scope # see /etc/iproute2/rt_scopes
-
- # attach NLA -- it MUST be a list / mutable
- msg['attrs'] = [['IFA_LOCAL', '192.168.0.1'],
- ['IFA_ADDRESS', '192.162.0.1']]
-
- # fill generic netlink fields
- msg['header']['sequence_number'] = nonce # an unique seq number
- msg['header']['pid'] = os.getpid()
- msg['header']['type'] = RTM_NEWADDR
- msg['header']['flags'] = NLM_F_REQUEST |\\
- NLM_F_ACK |\\
- NLM_F_CREATE |\\
- NLM_F_EXCL
-
- # encode the packet
- msg.encode()
-
- # send the buffer
- nlsock.sendto(msg.buf.getvalue(), (0, 0))
-
-Please notice, that NLA list *MUST* be mutable.
-
-------------------
-
-Module contents:
-'''
-
-import traceback
-import logging
-import socket
-import struct
-import types
-import sys
-import io
-import re
-import os
-
-from pyroute2.common import hexdump
-from pyroute2.common import basestring
-
-_letters = re.compile('[A-Za-z]')
-_fmt_letters = re.compile('[^!><@=][!><@=]')
-
-##
-# That's a hack for the code linter, which works under
-# Python3, see unicode reference in the code below
-if sys.version[0] == '3':
- unicode = str
-
-NLMSG_MIN_TYPE = 0x10
-
-GENL_NAMSIZ = 16 # length of family name
-GENL_MIN_ID = NLMSG_MIN_TYPE
-GENL_MAX_ID = 1023
-
-GENL_ADMIN_PERM = 0x01
-GENL_CMD_CAP_DO = 0x02
-GENL_CMD_CAP_DUMP = 0x04
-GENL_CMD_CAP_HASPOL = 0x08
-
-#
-# List of reserved static generic netlink identifiers:
-#
-GENL_ID_GENERATE = 0
-GENL_ID_CTRL = NLMSG_MIN_TYPE
-
-#
-# Controller
-#
-
-CTRL_CMD_UNSPEC = 0x0
-CTRL_CMD_NEWFAMILY = 0x1
-CTRL_CMD_DELFAMILY = 0x2
-CTRL_CMD_GETFAMILY = 0x3
-CTRL_CMD_NEWOPS = 0x4
-CTRL_CMD_DELOPS = 0x5
-CTRL_CMD_GETOPS = 0x6
-CTRL_CMD_NEWMCAST_GRP = 0x7
-CTRL_CMD_DELMCAST_GRP = 0x8
-CTRL_CMD_GETMCAST_GRP = 0x9 # unused
-
-
-CTRL_ATTR_UNSPEC = 0x0
-CTRL_ATTR_FAMILY_ID = 0x1
-CTRL_ATTR_FAMILY_NAME = 0x2
-CTRL_ATTR_VERSION = 0x3
-CTRL_ATTR_HDRSIZE = 0x4
-CTRL_ATTR_MAXATTR = 0x5
-CTRL_ATTR_OPS = 0x6
-CTRL_ATTR_MCAST_GROUPS = 0x7
-
-CTRL_ATTR_OP_UNSPEC = 0x0
-CTRL_ATTR_OP_ID = 0x1
-CTRL_ATTR_OP_FLAGS = 0x2
-
-CTRL_ATTR_MCAST_GRP_UNSPEC = 0x0
-CTRL_ATTR_MCAST_GRP_NAME = 0x1
-CTRL_ATTR_MCAST_GRP_ID = 0x2
-
-
-# Different Netlink families
-#
-NETLINK_ROUTE = 0 # Routing/device hook
-NETLINK_UNUSED = 1 # Unused number
-NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols
-NETLINK_FIREWALL = 3 # Firewalling hook
-NETLINK_INET_DIAG = 4 # INET socket monitoring
-NETLINK_NFLOG = 5 # netfilter/iptables ULOG
-NETLINK_XFRM = 6 # ipsec
-NETLINK_SELINUX = 7 # SELinux event notifications
-NETLINK_ISCSI = 8 # Open-iSCSI
-NETLINK_AUDIT = 9 # auditing
-NETLINK_FIB_LOOKUP = 10
-NETLINK_CONNECTOR = 11
-NETLINK_NETFILTER = 12 # netfilter subsystem
-NETLINK_IP6_FW = 13
-NETLINK_DNRTMSG = 14 # DECnet routing messages
-NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace
-NETLINK_GENERIC = 16
-# leave room for NETLINK_DM (DM Events)
-NETLINK_SCSITRANSPORT = 18 # SCSI Transports
-
-# NLA flags
-NLA_F_NESTED = 1 << 15
-NLA_F_NET_BYTEORDER = 1 << 14
-
-NLMSG_ALIGNTO = 4
-
-
-class NetlinkError(Exception):
- '''
- Base netlink error
- '''
- def __init__(self, code, msg=None):
- msg = msg or os.strerror(code)
- super(NetlinkError, self).__init__(code, msg)
- self.code = code
-
-
-class NetlinkDecodeError(Exception):
- '''
- Base decoding error class.
-
- Incapsulates underlying error for the following analysis
- '''
- def __init__(self, exception):
- self.exception = exception
-
-
-class NetlinkHeaderDecodeError(NetlinkDecodeError):
- '''
- The error occured while decoding a header
- '''
- pass
-
-
-class NetlinkDataDecodeError(NetlinkDecodeError):
- '''
- The error occured while decoding the message fields
- '''
- pass
-
-
-class NetlinkNLADecodeError(NetlinkDecodeError):
- '''
- The error occured while decoding NLA chain
- '''
- pass
-
-
-def NLMSG_ALIGN(l):
- return (l + NLMSG_ALIGNTO - 1) & ~ (NLMSG_ALIGNTO - 1)
-
-
-class NotInitialized(Exception):
- pass
-
-
-# Netlink message flags values (nlmsghdr.flags)
-#
-NLM_F_REQUEST = 1 # It is request message.
-NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE
-NLM_F_ACK = 4 # Reply with ack, with zero or error code
-NLM_F_ECHO = 8 # Echo this request
-# Modifiers to GET request
-NLM_F_ROOT = 0x100 # specify tree root
-NLM_F_MATCH = 0x200 # return all matching
-NLM_F_ATOMIC = 0x400 # atomic GET
-NLM_F_DUMP = (NLM_F_ROOT | NLM_F_MATCH)
-# Modifiers to NEW request
-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
-
-NLMSG_NOOP = 0x1 # Nothing
-NLMSG_ERROR = 0x2 # Error
-NLMSG_DONE = 0x3 # End of a dump
-NLMSG_OVERRUN = 0x4 # Data lost
-NLMSG_CONTROL = 0xe # Custom message type for messaging control
-NLMSG_TRANSPORT = 0xf # Custom message type for NL as a transport
-NLMSG_MIN_TYPE = 0x10 # < 0x10: reserved control messages
-NLMSG_MAX_LEN = 0xffff # Max message length
-
-mtypes = {1: 'NLMSG_NOOP',
- 2: 'NLMSG_ERROR',
- 3: 'NLMSG_DONE',
- 4: 'NLMSG_OVERRUN'}
-
-IPRCMD_NOOP = 0
-IPRCMD_STOP = 1
-IPRCMD_ACK = 2
-IPRCMD_ERR = 3
-IPRCMD_REGISTER = 4
-IPRCMD_RELOAD = 5
-IPRCMD_ROUTE = 6
-IPRCMD_CONNECT = 7
-IPRCMD_DISCONNECT = 8
-IPRCMD_SERVE = 9
-IPRCMD_SHUTDOWN = 10
-IPRCMD_SUBSCRIBE = 11
-IPRCMD_UNSUBSCRIBE = 12
-IPRCMD_PROVIDE = 13
-IPRCMD_REMOVE = 14
-IPRCMD_DISCOVER = 15
-IPRCMD_UNREGISTER = 16
-
-SOL_NETLINK = 270
-
-NETLINK_ADD_MEMBERSHIP = 1
-NETLINK_DROP_MEMBERSHIP = 2
-NETLINK_PKTINFO = 3
-NETLINK_BROADCAST_ERROR = 4
-NETLINK_NO_ENOBUFS = 5
-NETLINK_RX_RING = 6
-NETLINK_TX_RING = 7
-
-
-class nlmsg_base(dict):
- '''
- Netlink base class. You do not need to inherit it directly, unless
- you're inventing completely new protocol structure.
-
- Use nlmsg or nla classes.
-
- The class provides several methods, but often one need to customize
- only `decode()` and `encode()`.
- '''
-
- fields = [] # data field names, to build a dictionary
- header = None # optional header class
- pack = None # pack pragma
- array = False
- nla_map = {} # NLA mapping
- nla_flags = 0 # NLA flags
- value_map = {}
-
- def __init__(self, buf=None, length=None, parent=None, debug=False):
- dict.__init__(self)
- for i in self.fields:
- self[i[0]] = 0 # FIXME: only for number values
- self.raw = None
- self.debug = debug
- self.length = length or 0
- self.parent = parent
- self.offset = 0
- self.prefix = None
- self['attrs'] = []
- self['value'] = NotInitialized
- self.value = NotInitialized
- self.register_nlas()
- self.r_value_map = dict([(x[1], x[0]) for x in self.value_map.items()])
- self.reset(buf)
- self.clean_cbs = []
- if self.header is not None:
- self['header'] = self.header(self.buf)
-
- def copy(self):
- '''
- Return a decoded copy of the netlink message. Works
- correctly only if the message was encoded, or is
- received from the socket.
- '''
- ret = type(self)(self.buf.getvalue())
- ret.decode()
- return ret
-
- def reset(self, buf=None):
- '''
- Reset the message buffer. Optionally, set the message
- from the `buf` parameter. This parameter can be either
- string, or io.BytesIO, or dict instance.
- '''
- if isinstance(buf, basestring):
- b = io.BytesIO()
- b.write(buf)
- b.seek(0)
- buf = b
- if isinstance(buf, dict):
- self.setvalue(buf)
- buf = None
- self.buf = buf or io.BytesIO()
- if 'header' in self:
- self['header'].buf = self.buf
-
- def register_clean_cb(self, cb):
- if self.parent is not None:
- return self.parent.register_clean_cb(cb)
- else:
- self.clean_cbs.append(cb)
-
- def _strip_one(self, name):
- for i in tuple(self['attrs']):
- if i[0] == name:
- self['attrs'].remove(i)
- return self
-
- def strip(self, attrs):
- '''
- Remove an NLA from the attrs chain. The `attrs`
- parameter can be either string, or iterable. In
- the latter case, will be stripped NLAs, specified
- in the provided list.
- '''
- if isinstance(attrs, basestring):
- self._strip_one(attrs)
- else:
- for name in attrs:
- self._strip_one(name)
- return self
-
- def __ops(self, rvalue, op0, op1):
- lvalue = self.getvalue()
- res = self.__class__()
- for key in lvalue:
- if key not in ('header', 'attrs'):
- if op0 == '__sub__':
- # operator -, complement
- if (key not in rvalue) or (lvalue[key] != rvalue[key]):
- res[key] = lvalue[key]
- elif op0 == '__and__':
- # operator &, intersection
- if (key in rvalue) and (lvalue[key] == rvalue[key]):
- res[key] = lvalue[key]
- if 'attrs' in lvalue:
- res['attrs'] = []
- for attr in lvalue['attrs']:
- if isinstance(attr[1], nla):
- diff = getattr(attr[1], op0)(rvalue.get_attr(attr[0]))
- if diff is not None:
- res['attrs'].append([attr[0], diff])
- else:
- if op0 == '__sub__':
- # operator -, complement
- if rvalue.get_attr(attr[0]) != attr[1]:
- res['attrs'].append(attr)
- elif op0 == '__and__':
- # operator &, intersection
- if rvalue.get_attr(attr[0]) == attr[1]:
- res['attrs'].append(attr)
- if not len(res):
- return None
- else:
- if 'header' in res:
- del res['header']
- if 'value' in res:
- del res['value']
- if 'attrs' in res and not len(res['attrs']):
- del res['attrs']
- return res
-
- def __sub__(self, rvalue):
- '''
- Subjunction operation.
- '''
- return self.__ops(rvalue, '__sub__', '__ne__')
-
- def __and__(self, rvalue):
- '''
- Conjunction operation.
- '''
- return self.__ops(rvalue, '__and__', '__eq__')
-
- def __eq__(self, rvalue):
- '''
- Having nla, we are able to use it in operations like::
-
- if nla == 'some value':
- ...
- '''
- lvalue = self.getvalue()
- if lvalue is self:
- for key in self:
- try:
- assert self.get(key) == rvalue.get(key)
- except Exception:
- # on any error -- is not equal
- return False
- return True
- else:
- return lvalue == rvalue
-
- @classmethod
- def get_size(self):
- size = 0
- for field in self.fields:
- size += struct.calcsize(field[1])
- return size
-
- @classmethod
- def nla2name(self, name):
- '''
- Convert NLA name into human-friendly name
-
- Example: IFLA_ADDRESS -> address
-
- Requires self.prefix to be set
- '''
- return name[(name.find(self.prefix) + 1) * len(self.prefix):].lower()
-
- @classmethod
- def name2nla(self, name):
- '''
- Convert human-friendly name into NLA name
-
- Example: address -> IFLA_ADDRESS
-
- Requires self.prefix to be set
- '''
- name = name.upper()
- if name.find(self.prefix) == -1:
- name = "%s%s" % (self.prefix, name)
- return name
-
- def reserve(self):
- '''
- Reserve space in the buffer for data. This can be used
- to skip encoding of the header until some fields will
- be known.
- '''
- size = 0
- for i in self.fields:
- size += struct.calcsize(i[1])
- self.buf.seek(size, 1)
-
- def decode(self):
- '''
- Decode the message. The message should have the `buf`
- attribute initialized. e.g.::
-
- data = sock.recv(16384)
- msg = ifinfmsg(data)
-
- If you want to customize the decoding process, override
- the method, but don't forget to call parent's `decode()`::
-
- class CustomMessage(nlmsg):
-
- def decode(self):
- nlmsg.decode(self)
- ... # do some custom data tuning
- '''
- self.offset = self.buf.tell()
- # decode the header
- if self.header is not None:
- try:
- self['header'].decode()
- # update length from header
- # it can not be less than 4
- self.length = max(self['header']['length'], 4)
- save = self.buf.tell()
- self.buf.seek(self.offset)
- self.raw = self.buf.read(self.length)
- self.buf.seek(save)
- except Exception as e:
- raise NetlinkHeaderDecodeError(e)
- # handle the array case
- if self.array:
- self.setvalue([])
- while self.buf.tell() < self.offset + self.length:
- cell = type(self)(self.buf, parent=self, debug=self.debug)
- cell.array = False
- cell.decode()
- self.value.append(cell)
- # decode the data
- try:
- if self.pack == 'struct':
- names = []
- formats = []
- for field in self.fields:
- names.append(field[0])
- formats.append(field[1])
- fields = ((','.join(names), ''.join(formats)), )
- else:
- fields = self.fields
-
- for field in fields:
- name = field[0]
- fmt = field[1]
-
- # 's' and 'z' can be used only in connection with
- # length, encoded in the header
- if field[1] in ('s', 'z'):
- fmt = '%is' % (self.length - 4)
-
- size = struct.calcsize(fmt)
- raw = self.buf.read(size)
- actual_size = len(raw)
-
- # FIXME: adjust string size again
- if field[1] in ('s', 'z'):
- size = actual_size
- fmt = '%is' % (actual_size)
- if size == actual_size:
- value = struct.unpack(fmt, raw)
- if len(value) == 1:
- self[name] = value[0]
- # cut zero-byte from z-strings
- # 0x00 -- python3; '\0' -- python2
- if field[1] == 'z' and self[name][-1] in (0x00, '\0'):
- self[name] = self[name][:-1]
- else:
- if self.pack == 'struct':
- names = name.split(',')
- values = list(value)
- for name in names:
- if name[0] != '_':
- self[name] = values.pop(0)
- else:
- self[name] = value
-
- else:
- # FIXME: log an error
- pass
-
- except Exception as e:
- raise NetlinkDataDecodeError(e)
- # decode NLA
- try:
- # align NLA chain start
- self.buf.seek(NLMSG_ALIGN(self.buf.tell()))
- # read NLA chain
- if self.nla_map:
- self.decode_nlas()
- except Exception as e:
- logging.warning(traceback.format_exc())
- raise NetlinkNLADecodeError(e)
- if len(self['attrs']) == 0:
- del self['attrs']
- if self['value'] is NotInitialized:
- del self['value']
-
- def encode(self):
- '''
- Encode the message into the binary buffer::
-
- msg.encode()
- sock.send(msg.buf.getvalue())
-
- If you want to customize the encoding process, override
- the method::
-
- class CustomMessage(nlmsg):
-
- def encode(self):
- ... # do some custom data tuning
- nlmsg.encode(self)
- '''
- init = self.buf.tell()
- diff = 0
- # reserve space for the header
- if self.header is not None:
- self['header'].reserve()
-
- if self.getvalue() is not None:
-
- payload = b''
- for i in self.fields:
- name = i[0]
- fmt = i[1]
- value = self[name]
-
- if fmt == 's':
- length = len(value)
- fmt = '%is' % (length)
- elif fmt == 'z':
- length = len(value) + 1
- fmt = '%is' % (length)
-
- # in python3 we should force it
- if sys.version[0] == '3':
- if isinstance(value, str):
- value = bytes(value, 'utf-8')
- elif isinstance(value, float):
- value = int(value)
- elif sys.version[0] == '2':
- if isinstance(value, unicode):
- value = value.encode('utf-8')
-
- try:
- if fmt[-1] == 'x':
- payload += struct.pack(fmt)
- elif type(value) in (list, tuple, set):
- payload += struct.pack(fmt, *value)
- else:
- payload += struct.pack(fmt, value)
- except struct.error:
- logging.error(traceback.format_exc())
- logging.error("error pack: %s %s %s" %
- (fmt, value, type(value)))
- raise
-
- diff = NLMSG_ALIGN(len(payload)) - len(payload)
- self.buf.write(payload)
- self.buf.write(b'\0' * diff)
- # write NLA chain
- if self.nla_map:
- diff = 0
- self.encode_nlas()
- # calculate the size and write it
- if self.header is not None:
- self.update_length(init, diff)
-
- def update_length(self, start, diff=0):
- save = self.buf.tell()
- self['header']['length'] = save - start - diff
- self.buf.seek(start)
- self['header'].encode()
- self.buf.seek(save)
-
- def setvalue(self, value):
- if isinstance(value, dict):
- self.update(value)
- else:
- try:
- value = self.r_value_map.get(value, value)
- except TypeError:
- pass
- self['value'] = value
- self.value = value
-
- def get_encoded(self, attr, default=None):
- '''
- Return the first encoded NLA by name
- '''
- return self.get_attr(attr, default, 'encoded')
-
- def get_attr(self, attr, default=None, fmt='raw'):
- '''
- Return the first attr by name or None
- '''
- try:
- attrs = self.get_attrs(attr, fmt)
- except KeyError:
- return default
- if attrs:
- return attrs[0]
- else:
- return default
-
- def get_attrs(self, attr, fmt='raw'):
- '''
- Return attrs by name
- '''
- fmt_map = {'raw': 1,
- 'encoded': 2}
- return [i[fmt_map[fmt]] for i in self['attrs'] if i[0] == attr]
-
- def getvalue(self):
- '''
- Atomic NLAs return their value in the 'value' field,
- not as a dictionary. Complex NLAs return whole dictionary.
- '''
- if self.value != NotInitialized:
- # value decoded by custom decoder
- return self.value
-
- if 'value' in self and self['value'] != NotInitialized:
- # raw value got by generic decoder
- return self.value_map.get(self['value'], self['value'])
-
- return self
-
- def register_nlas(self):
- '''
- Convert 'nla_map' tuple into two dictionaries for mapping
- and reverse mapping of NLA types.
-
- ex: given::
-
- nla_map = (('TCA_HTB_UNSPEC', 'none'),
- ('TCA_HTB_PARMS', 'htb_parms'),
- ('TCA_HTB_INIT', 'htb_glob'))
-
- creates::
-
- t_nla_map = {0: (<class 'pyroute2...none'>, 'TCA_HTB_UNSPEC'),
- 1: (<class 'pyroute2...htb_parms'>, 'TCA_HTB_PARMS'),
- 2: (<class 'pyroute2...htb_glob'>, 'TCA_HTB_INIT')}
- r_nla_map = {'TCA_HTB_UNSPEC': (<class 'pyroute2...none'>, 0),
- 'TCA_HTB_PARMS': (<class 'pyroute2...htb_parms'>, 1),
- 'TCA_HTB_INIT': (<class 'pyroute2...htb_glob'>, 2)}
-
- nla_map format::
-
- nla_map = (([ID, ] NAME, TYPE[, FLAGS]), ...)
-
- Items in `[...]` are optional. If ID is not given, then the map will
- be autonumerated from 0. If flags are not given, they are 0 by default.
-
- '''
- # clean up NLA mappings
- self.t_nla_map = {}
- self.r_nla_map = {}
-
- # work only on non-empty mappings
- if not self.nla_map:
- return
-
- # fix nla flags
- nla_map = []
- for item in self.nla_map:
- if not isinstance(item[-1], int):
- item = list(item)
- item.append(0)
- nla_map.append(item)
-
- # detect, whether we have pre-defined keys
- if not isinstance(nla_map[0][0], int):
- # create enumeration
- nla_types = enumerate((i[0] for i in nla_map))
- # that's a little bit tricky, but to reduce
- # the required amount of code in modules, we have
- # to jump over the head
- zipped = [(k[1][0], k[0][0], k[0][1], k[0][2]) for k in
- zip(nla_map, nla_types)]
- else:
- zipped = nla_map
-
- for (key, name, nla_class, nla_flags) in zipped:
- # is it an array
- if nla_class[0] == '*':
- nla_class = nla_class[1:]
- array = True
- else:
- array = False
- # lookup NLA class
- if nla_class == 'recursive':
- nla_class = type(self)
- else:
- nla_class = getattr(self, nla_class)
- # update mappings
- self.t_nla_map[key] = (nla_class, name, nla_flags, array)
- self.r_nla_map[name] = (nla_class, key, nla_flags, array)
-
- def encode_nlas(self):
- '''
- Encode the NLA chain. Should not be called manually, since
- it is called from `encode()` routine.
- '''
- for i in self['attrs']:
- if i[0] in self.r_nla_map:
- msg_class = self.r_nla_map[i[0]][0]
- msg_type = self.r_nla_map[i[0]][1]
- # is it a class or a function?
- if isinstance(msg_class, types.MethodType):
- # if it is a function -- use it to get the class
- msg_class = msg_class()
- # encode NLA
- nla = msg_class(self.buf, parent=self)
- nla.nla_flags |= self.r_nla_map[i[0]][2]
- nla['header']['type'] = msg_type | nla.nla_flags
- nla.setvalue(i[1])
- try:
- nla.encode()
- except:
- raise
- else:
- if len(i) == 2:
- i.append(nla)
- elif len(i) == 3:
- i[2] = nla
-
- def decode_nlas(self):
- '''
- Decode the NLA chain. Should not be called manually, since
- it is called from `decode()` routine.
- '''
- while self.buf.tell() < (self.offset + self.length):
- init = self.buf.tell()
- nla = None
- # pick the length and the type
- (length, msg_type) = struct.unpack('HH', self.buf.read(4))
- # first two bits of msg_type are flags:
- msg_type = msg_type & ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
- # rewind to the beginning
- self.buf.seek(init)
- length = min(max(length, 4),
- (self.length - self.buf.tell() + self.offset))
-
- # we have a mapping for this NLA
- if msg_type in self.t_nla_map:
- # get the class
- msg_class = self.t_nla_map[msg_type][0]
- # is it a class or a function?
- if isinstance(msg_class, types.MethodType):
- # if it is a function -- use it to get the class
- msg_class = msg_class(buf=self.buf, length=length)
- # and the name
- msg_name = self.t_nla_map[msg_type][1]
- # is it an array?
- msg_array = self.t_nla_map[msg_type][3]
- # decode NLA
- nla = msg_class(self.buf, length, self, debug=self.debug)
- nla.array = msg_array
- try:
- nla.decode()
- nla.nla_flags = msg_type & (NLA_F_NESTED |
- NLA_F_NET_BYTEORDER)
- except Exception:
- logging.warning("decoding %s" % (msg_name))
- logging.warning(traceback.format_exc())
- self.buf.seek(init)
- msg_name = 'UNDECODED'
- msg_value = hexdump(self.buf.read(length))
- else:
- msg_value = nla.getvalue()
- else:
- msg_name = 'UNKNOWN'
- msg_value = hexdump(self.buf.read(length))
-
- self['attrs'].append([msg_name, msg_value])
-
- # fix the offset
- self.buf.seek(init + NLMSG_ALIGN(length))
-
-
-class nla_header(nlmsg_base):
- '''
- The NLA header structure: uin16 length and uint16 type.
- '''
- fields = (('length', 'H'),
- ('type', 'H'))
-
-
-class nla_base(nlmsg_base):
- '''
- The NLA base class. Use `nla_header` class as the header.
- '''
- header = nla_header
-
-
-class nlmsg_header(nlmsg_base):
- '''
- Common netlink message header
- '''
- fields = (('length', 'I'),
- ('type', 'H'),
- ('flags', 'H'),
- ('sequence_number', 'I'),
- ('pid', 'I'))
-
-
-class nlmsg_atoms(nlmsg_base):
- '''
- A collection of base NLA types
- '''
- class none(nla_base):
- '''
- 'none' type is used to skip decoding of NLA. You can
- also use 'hex' type to dump NLA's content.
- '''
- def decode(self):
- nla_base.decode(self)
- self.value = None
-
- class uint8(nla_base):
- fields = [('value', 'B')]
-
- class uint16(nla_base):
- fields = [('value', 'H')]
-
- class uint32(nla_base):
- fields = [('value', 'I')]
-
- class uint64(nla_base):
- fields = [('value', 'Q')]
-
- class be8(nla_base):
- fields = [('value', '>B')]
-
- class be16(nla_base):
- fields = [('value', '>H')]
-
- class be32(nla_base):
- fields = [('value', '>I')]
-
- class be64(nla_base):
- fields = [('value', '>Q')]
-
- class ipXaddr(nla_base):
- fields = [('value', 's')]
- family = None
-
- def encode(self):
- self['value'] = socket.inet_pton(self.family,
- self.value)
- nla_base.encode(self)
-
- def decode(self):
- nla_base.decode(self)
- self.value = socket.inet_ntop(self.family,
- self['value'])
-
- class ip4addr(ipXaddr):
- '''
- Explicit IPv4 address type class.
- '''
- family = socket.AF_INET
-
- class ip6addr(ipXaddr):
- '''
- Explicit IPv6 address type class.
- '''
- family = socket.AF_INET6
-
- class ipaddr(nla_base):
- '''
- This class is used to decode IP addresses according to
- the family. Socket library currently supports only two
- families, AF_INET and AF_INET6.
-
- We do not specify here the string size, it will be
- calculated in runtime.
- '''
- fields = [('value', 's')]
- family_map = {socket.AF_INET: socket.AF_INET,
- socket.AF_BRIDGE: socket.AF_INET,
- socket.AF_INET6: socket.AF_INET6}
-
- def encode(self):
- family = self.family_map[self.parent['family']]
- self['value'] = socket.inet_pton(family, self.value)
- nla_base.encode(self)
-
- def decode(self):
- nla_base.decode(self)
- family = self.family_map[self.parent['family']]
- self.value = socket.inet_ntop(family, self['value'])
-
- class l2addr(nla_base):
- '''
- Decode MAC address.
- '''
- fields = [('value', '=6s')]
-
- def encode(self):
- self['value'] = struct.pack('BBBBBB',
- *[int(i, 16) for i in
- self.value.split(':')])
- nla_base.encode(self)
-
- def decode(self):
- nla_base.decode(self)
- self.value = ':'.join('%02x' % (i) for i in
- struct.unpack('BBBBBB', self['value']))
-
- class hex(nla_base):
- '''
- Represent NLA's content with header as hex string.
- '''
- fields = [('value', 's')]
-
- def decode(self):
- nla_base.decode(self)
- self.value = hexdump(self['value'])
-
- class cdata(nla_base):
- '''
- Binary data
- '''
- fields = [('value', 's')]
-
- class asciiz(nla_base):
- '''
- Zero-terminated string.
- '''
- # FIXME: move z-string hacks from general decode here?
- fields = [('value', 'z')]
-
- def encode(self):
- if isinstance(self['value'], str) and sys.version[0] == '3':
- self['value'] = bytes(self['value'], 'utf-8')
- nla_base.encode(self)
-
- def decode(self):
- nla_base.decode(self)
- try:
- assert sys.version[0] == '3'
- self.value = self['value'].decode('utf-8')
- except (AssertionError, UnicodeDecodeError):
- self.value = self['value']
-
-
-class nla(nla_base, nlmsg_atoms):
- '''
- Main NLA class
- '''
- def decode(self):
- nla_base.decode(self)
- if not self.debug:
- del self['header']
-
-
-class nlmsg(nlmsg_atoms):
- '''
- Main netlink message class
- '''
- header = nlmsg_header
-
-
-class genlmsg(nlmsg):
- '''
- Generic netlink message
- '''
- fields = (('cmd', 'B'),
- ('version', 'B'),
- ('reserved', 'H'))
-
-
-class ctrlmsg(genlmsg):
- '''
- Netlink control message
- '''
- # FIXME: to be extended
- nla_map = (('CTRL_ATTR_UNSPEC', 'none'),
- ('CTRL_ATTR_FAMILY_ID', 'uint16'),
- ('CTRL_ATTR_FAMILY_NAME', 'asciiz'),
- ('CTRL_ATTR_VERSION', 'uint32'),
- ('CTRL_ATTR_HDRSIZE', 'uint32'),
- ('CTRL_ATTR_MAXATTR', 'uint32'),
- ('CTRL_ATTR_OPS', '*ops'),
- ('CTRL_ATTR_MCAST_GROUPS', '*mcast_groups'))
-
- class ops(nla):
- nla_map = (('CTRL_ATTR_OP_UNSPEC', 'none'),
- ('CTRL_ATTR_OP_ID', 'uint32'),
- ('CTRL_ATTR_OP_FLAGS', 'uint32'))
-
- class mcast_groups(nla):
- nla_map = (('CTRL_ATTR_MCAST_GRP_UNSPEC', 'none'),
- ('CTRL_ATTR_MCAST_GRP_NAME', 'asciiz'),
- ('CTRL_ATTR_MCAST_GRP_ID', 'uint32'))