mirror of
				https://github.com/CumulusNetworks/ifupdown2.git
				synced 2024-05-06 15:54:50 +00:00 
			
		
		
		
	Ticket: CM-9078 Reviewed By: CCR-4110 Testing Done: clag bond add/del and clag slave add/del This change basically does the following - 1. Proto-down swpX pre-clag-bond-enslave 2. Proto-up swpX post-clag-bond-release Setting/clearing of clag-id will result in similar proto-state changes and those are handled by clagd. Note: I really wanted to keep these changes out of ifupdown2 but the order of setting is critical i.e. protodown has to happen enslave to prevent additional flaps/STP TCNs. Theoretically #2 can be done by clagd but there is no easy way to do #1.
		
			
				
	
	
		
			898 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			898 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| #
 | |
| # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
 | |
| #
 | |
| # Author: Scott Feldman, sfeldma@cumulusnetworks.com
 | |
| # Author: Roopa Prabhu, roopa@cumulusnetworks.com
 | |
| #
 | |
| #
 | |
| from socket import NETLINK_ROUTE, AF_INET, AF_INET6
 | |
| from string import printable
 | |
| from ipaddr import *
 | |
| from ctypes import *
 | |
| from netlink import *
 | |
| import logging
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| #
 | |
| # from /usr/include/linux/rtnetlink.h
 | |
| #
 | |
| 
 | |
| RTMGRP_LINK = 0x1
 | |
| RTMGRP_IPV4_IFADDR = 0x10
 | |
| RTMGRP_IPV4_ROUTE = 0x40
 | |
| RTMGRP_IPV6_IFADDR = 0x100
 | |
| RTMGRP_IPV6_ROUTE = 0x400
 | |
| 
 | |
| RTM_NEWLINK = 16
 | |
| RTM_DELLINK = 17
 | |
| RTM_GETLINK = 18
 | |
| RTM_SETLINK = 19
 | |
| RTM_NEWADDR = 20
 | |
| RTM_DELADDR = 21
 | |
| RTM_GETADDR = 22
 | |
| RTM_NEWROUTE = 24
 | |
| RTM_DELROUTE = 25
 | |
| RTM_GETROUTE = 26
 | |
| 
 | |
| # Definitions used in routing table administration.
 | |
| 
 | |
| class Nlmsg(Structure):
 | |
| 
 | |
|     def _stringify(self):
 | |
|         return string_at(addressof(self), sizeof(self))
 | |
| 
 | |
|     def __eq__(self, other):
 | |
|         return self._stringify() == other._stringify() and \
 | |
|             self.__dict__ == other.__dict__
 | |
| 
 | |
|     def to_rta(self):
 | |
|         return Rtattr.from_address(addressof(self) + NLMSG_ALIGN(sizeof(self)))
 | |
| 
 | |
|     def pack_extra(self, extra, addr):
 | |
|         memmove(addr, addressof(extra), sizeof(extra))
 | |
|         return NLMSG_ALIGN(sizeof(extra))
 | |
| 
 | |
|     def pack_rtas(self, rtas, addr):
 | |
|         total_len = 0
 | |
|         for rta_type, value in rtas.iteritems():
 | |
|             rta = Rtattr.from_address(addr)
 | |
|             rta.rta_type = rta_type
 | |
|             pack_fn = self.rta_fn(rta_type)
 | |
|             rta_len = NLMSG_ALIGN(pack_fn(rta, value))
 | |
|             total_len += rta_len
 | |
|             addr += rta_len
 | |
|         return total_len
 | |
| 
 | |
|     def pack_rtas_new(self, rtas, addr, policy):
 | |
|         total_len = 0
 | |
|                 
 | |
|         for rta_type, value in rtas.iteritems():
 | |
|             if type(value) == dict:
 | |
|             	rta = Rtattr.from_address(addr)
 | |
|             	rta.rta_type = rta_type
 | |
|                 rta.rta_len = RTA_LENGTH(0)
 | |
|                 rta_len = NLMSG_ALIGN(rta.rta_len)
 | |
|                 total_len += rta_len
 | |
|                 addr += rta_len
 | |
|             	pack_fn = policy.get(rta_type)
 | |
|                 rta_len = NLMSG_ALIGN(pack_fn(addr, value))
 | |
| 
 | |
|                 rta.rta_len += rta_len
 | |
|             else:
 | |
|                 rta = Rtattr.from_address(addr)
 | |
|                 rta.rta_type = rta_type
 | |
|                 pack_fn = policy.get(rta_type)
 | |
|                 rta_len = NLMSG_ALIGN(pack_fn(rta, value))
 | |
|             total_len += rta_len
 | |
|             addr += rta_len
 | |
|         return total_len
 | |
| 
 | |
|     def rta_linkinfo(self, addr, rtas):
 | |
|         total_len = 0
 | |
| 
 | |
|         # Check interface kind 
 | |
|         kind = rtas.get(IFLA_INFO_KIND)
 | |
|         if kind == 'vlan':
 | |
|             data_policy = self.rta_linkinfo_data_vlan_policy()
 | |
|         else:
 | |
|             data_policy = self.rta_linkinfo_data_macvlan_policy()
 | |
| 
 | |
|         # Pack info kind
 | |
|         rta = Rtattr.from_address(addr)
 | |
|         rta.rta_type = IFLA_INFO_KIND
 | |
|         rta_len = NLMSG_ALIGN(self.rta_string(rta, kind))
 | |
|         total_len += rta_len
 | |
|         addr += rta_len
 | |
| 
 | |
|         # nest start link info data
 | |
|         rta = Rtattr.from_address(addr)
 | |
|         rta.rta_type = IFLA_INFO_DATA
 | |
|         rta.rta_len = RTA_LENGTH(0)
 | |
|         rta_len = NLMSG_ALIGN(rta.rta_len)
 | |
|         total_len += rta_len
 | |
|         addr += rta_len
 | |
|         rta_len = self.pack_rtas_new(rtas.get(IFLA_INFO_DATA), addr,
 | |
|                                      data_policy)
 | |
|         rta.rta_len += rta_len
 | |
| 
 | |
|         total_len += rta_len
 | |
|         addr += rta_len
 | |
| 
 | |
|         return total_len
 | |
| 
 | |
|     def rta_bridge_vlan_info(self, rta, value):
 | |
|         if value:
 | |
|            data = RTA_DATA(rta)
 | |
|            memmove(data, addressof(value), sizeof(value))
 | |
|            rta.rta_len = RTA_LENGTH(sizeof(value))
 | |
|            return rta.rta_len
 | |
| 
 | |
|     def rta_af_spec(self, addr, rtas):
 | |
|         total_len = 0
 | |
| 
 | |
|         # XXX: Check family (Assumes bridge family for now)
 | |
|         rta_len = self.pack_rtas_new(rtas, addr,
 | |
|                                      self.rta_bridge_af_spec_policy())
 | |
|         total_len += rta_len
 | |
|         return total_len
 | |
| 
 | |
|     def unpack_rtas(self, which_ones=[]):
 | |
|         len = self.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(self))
 | |
|         rta = self.to_rta()
 | |
|         rtas = {}
 | |
|         while RTA_OK(rta, len):
 | |
|             rta_type = rta.rta_type
 | |
|             if not which_ones or rta_type in which_ones:
 | |
|                 unpack_fn = self.rta_fn(rta_type)
 | |
|                 rtas[rta_type] = unpack_fn(rta)
 | |
|             len, rta = RTA_NEXT(rta, len)
 | |
|         return rtas
 | |
| 
 | |
|     def dump_rtas(self):
 | |
|         rtas = self.unpack_rtas()
 | |
|         for type, value in rtas.iteritems():
 | |
|             print "rta", type, ":", value
 | |
| 
 | |
|     class _IPv6Addr(BigEndianStructure):
 | |
|         _fields_ = [
 | |
|             ('upper', c_uint64),
 | |
|             ('lower', c_uint64),
 | |
|         ]
 | |
| 
 | |
|     class _IPv4Addr(BigEndianStructure):
 | |
|         _fields_ = [
 | |
|             ('addr', c_uint32),
 | |
|         ]
 | |
| 
 | |
|     def rta_uint8(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value is not None:
 | |
|             c_uint8.from_address(data).value = value
 | |
|             rta.rta_len = RTA_LENGTH(sizeof(c_uint8))
 | |
|             return rta.rta_len
 | |
| 	else:
 | |
|             return c_uint8.from_address(data).value
 | |
| 
 | |
|     def rta_uint16(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value:
 | |
|             c_uint16.from_address(data).value = value
 | |
|             rta.rta_len = RTA_LENGTH(sizeof(c_uint16))
 | |
|             return rta.rta_len
 | |
| 	else:
 | |
|             return c_uint16.from_address(data).value
 | |
| 
 | |
|     def rta_sint32(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value is not None:
 | |
|             c_int32.from_address(data).value = value
 | |
|             rta.rta_len = RTA_LENGTH(sizeof(c_int32))
 | |
|             return rta.rta_len
 | |
|         else:
 | |
|             return c_int32.from_address(data).value
 | |
| 
 | |
|     def rta_uint32(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value:
 | |
|             c_uint32.from_address(data).value = value
 | |
|             rta.rta_len = RTA_LENGTH(sizeof(c_uint32))
 | |
|             return rta.rta_len
 | |
|         else:
 | |
|             return c_uint32.from_address(data).value
 | |
| 
 | |
|     def rta_string(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value:
 | |
|             s = create_string_buffer(value)
 | |
|             memmove(data, addressof(s), len(value))
 | |
|             rta.rta_len = RTA_LENGTH(len(value))
 | |
|             return rta.rta_len
 | |
|         else:
 | |
|             return c_char_p(data).value
 | |
| 
 | |
|     def rta_addr(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value:
 | |
|             if isinstance(value, IPv4Address):
 | |
|                 self._IPv4Addr.from_address(data).addr = value._ip
 | |
|                 rta.rta_len = RTA_LENGTH(sizeof(self._IPv4Addr))
 | |
|             elif isinstance(value, IPv6Address):
 | |
|                 addr = self._IPv6Addr.from_address(data)
 | |
|                 addr.upper = value._ip >> 64
 | |
|                 addr.lower = value._ip & 0xffffffffffffffff
 | |
|                 rta.rta_len = RTA_LENGTH(sizeof(self._IPv6Addr))
 | |
|             else:
 | |
|                 assert(False)
 | |
|             return rta.rta_len
 | |
|         else:
 | |
|             if RTA_PAYLOAD(rta) == 4:
 | |
|                 addr = c_uint32.__ctype_be__.from_address(data).value
 | |
|                 addr = IPv4Address(addr)
 | |
|             else:
 | |
|                 addr = self._IPv6Addr.from_address(data)
 | |
|                 addr = IPv6Address((addr.upper << 64) + addr.lower)
 | |
|             return addr
 | |
| 
 | |
|     def rta_uint8_array(self, rta, value=None):
 | |
|         data = RTA_DATA(rta)
 | |
|         if value:
 | |
|             s = (c_uint8 * len(value)).from_buffer_copy(value)
 | |
|             memmove(data, addressof(s), len(value))
 | |
|             rta.rta_len = RTA_LENGTH(len(value))
 | |
|             return rta.rta_len
 | |
|         else:
 | |
|             array = (c_uint8 * RTA_PAYLOAD(rta))()
 | |
|             memmove(array, data, RTA_PAYLOAD(rta))
 | |
|             return array
 | |
| 
 | |
|     def rta_uint32_array(self, rta, value=None):
 | |
|         if value:
 | |
|             assert(False)
 | |
|         else:
 | |
|             data = RTA_DATA(rta)
 | |
|             size = RTA_PAYLOAD(rta) / sizeof(c_uint32)
 | |
|             array = (c_uint32 * size)()
 | |
|             memmove(array, data, RTA_PAYLOAD(rta))
 | |
|             return array
 | |
| 
 | |
|     def rta_multipath(self, rta, value=None):
 | |
|         # XXX implement this
 | |
|         return None
 | |
| 
 | |
|     def rta_wtf(self, rta, value=None):
 | |
|         return None
 | |
| 
 | |
|     def rta_none(self, rta, value=None):
 | |
|         return None
 | |
| 
 | |
|     def rta_fn(self, rta_type):
 | |
|         return None
 | |
| 
 | |
| 
 | |
| # rtm_type
 | |
| 
 | |
| RTN_UNSPEC = 0
 | |
| RTN_UNICAST = 1            # Gateway or direct route
 | |
| RTN_LOCAL = 2              # Accept locally
 | |
| RTN_BROADCAST = 3          # Accept locally as broadcast,
 | |
|                            # send as broadcast
 | |
| RTN_ANYCAST = 4            # Accept locally as broadcast,
 | |
|                            # but send as unicast
 | |
| RTN_MULTICAST = 5          # Multicast route
 | |
| RTN_BLACKHOLE = 6          # Drop
 | |
| RTN_UNREACHABLE = 7        # Destination is unreachable
 | |
| RTN_PROHIBIT = 8           # Administratively prohibited
 | |
| RTN_THROW = 9              # Not in this table
 | |
| RTN_NAT = 10               # Translate this address
 | |
| RTN_XRESOLVE = 11          # Use external resolver
 | |
| RTN_MAX = 11
 | |
| 
 | |
| # rtm_protocol
 | |
| 
 | |
| RTPROT_UNSPEC = 0
 | |
| RTPROT_REDIRECT = 1     # Route installed by ICMP redirects;
 | |
|                         # not used by current IPv4
 | |
| RTPROT_KERNEL = 2       # Route installed by kernel
 | |
| RTPROT_BOOT = 3         # Route installed during boot
 | |
| RTPROT_STATIC = 4       # Route installed by administrator
 | |
| 
 | |
| # Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
 | |
| # they are just passed from user and back as is.
 | |
| # It will be used by hypothetical multiple routing daemons.
 | |
| # Note that protocol values should be standardized in order to
 | |
| # avoid conflicts.
 | |
| 
 | |
| RTPROT_GATED = 8       # Apparently, GateD
 | |
| RTPROT_RA = 9          # RDISC/ND router advertisements
 | |
| RTPROT_MRT = 10        # Merit MRT
 | |
| RTPROT_ZEBRA = 11      # Zebra
 | |
| RTPROT_BIRD = 12       # BIRD
 | |
| RTPROT_DNROUTED = 13   # DECnet routing daemon
 | |
| RTPROT_XORP = 14       # XORP
 | |
| RTPROT_NTK = 15        # Netsukuku
 | |
| RTPROT_DHCP = 16       # DHCP client
 | |
| 
 | |
| # rtm_scope
 | |
| 
 | |
| # Really it is not scope, but sort of distance to the destination.
 | |
| # NOWHERE are reserved for not existing destinations, HOST is our
 | |
| # local addresses, LINK are destinations, located on directly attached
 | |
| # link and UNIVERSE is everywhere in the Universe.
 | |
| 
 | |
| # Intermediate values are also possible f.e. interior routes
 | |
| # could be assigned a value between UNIVERSE and LINK.
 | |
| 
 | |
| RT_SCOPE_UNIVERSE = 0
 | |
| # User defined values
 | |
| RT_SCOPE_SITE = 200
 | |
| RT_SCOPE_LINK = 253
 | |
| RT_SCOPE_HOST = 254
 | |
| RT_SCOPE_NOWHERE=255
 | |
| 
 | |
| # rtm_flags
 | |
| 
 | |
| RTM_F_NOTIFY = 0x100   # Notify user of route change
 | |
| RTM_F_CLONED = 0x200   # This route is cloned
 | |
| RTM_F_EQUALIZE = 0x400 # Multipath equalizer: NI
 | |
| RTM_F_PREFIX = 0x800   # Prefix addresses
 | |
| 
 | |
| # Reserved table identifiers
 | |
| 
 | |
| RT_TABLE_UNSPEC = 0
 | |
| # User defined values
 | |
| RT_TABLE_COMPAT = 252
 | |
| RT_TABLE_DEFAULT = 253
 | |
| RT_TABLE_MAIN = 254
 | |
| RT_TABLE_LOCAL = 255
 | |
| RT_TABLE_MAX = 0xFFFFFFFF
 | |
| 
 | |
| # Generic structure for encapsulation of optional route information.
 | |
| # It is reminiscent of sockaddr, but with sa_family replaced
 | |
| # with attribute type.
 | |
| 
 | |
| class Rtattr(Structure):
 | |
| 
 | |
|     _fields_ = [
 | |
|         ('rta_len', c_uint16),
 | |
|         ('rta_type', c_uint16),
 | |
|     ]
 | |
| 
 | |
| # Routing message attributes
 | |
| 
 | |
| RTA_UNSPEC = 0
 | |
| RTA_DST = 1
 | |
| RTA_SRC = 2
 | |
| RTA_IIF = 3
 | |
| RTA_OIF = 4
 | |
| RTA_GATEWAY = 5
 | |
| RTA_PRIORITY = 6
 | |
| RTA_PREFSRC = 7
 | |
| RTA_METRICS = 8
 | |
| RTA_MULTIPATH = 9
 | |
| RTA_PROTOINFO = 10        # no longer used
 | |
| RTA_FLOW = 11
 | |
| RTA_CACHEINFO = 12
 | |
| RTA_SESSION = 13          # no longer used
 | |
| RTA_MP_ALGO = 14          # no longer used 
 | |
| RTA_TABLE = 15
 | |
| RTA_MAX = 15
 | |
| 
 | |
| # Macros to handle rtattributes
 | |
| 
 | |
| RTA_ALIGNTO = 4
 | |
| def RTA_ALIGN(len):
 | |
|     return (len + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)
 | |
| def RTA_OK(rta, len):
 | |
|     return len >= sizeof(Rtattr) and \
 | |
|         rta.rta_len >= sizeof(Rtattr) and \
 | |
|         rta.rta_len <= len
 | |
| def RTA_NEXT(rta, len):
 | |
|     cur = RTA_ALIGN(rta.rta_len)
 | |
|     rta = Rtattr.from_address(addressof(rta) + cur)
 | |
|     return len - cur, rta
 | |
| def RTA_LENGTH(len):
 | |
|     return len + RTA_ALIGN(sizeof(Rtattr))
 | |
| def RTA_SPACE(len):
 | |
|     return RTA_ALIGN(RTA_LENGTH(len))
 | |
| def RTA_DATA(rta):
 | |
|     return addressof(rta) + RTA_LENGTH(0)
 | |
| def RTA_PAYLOAD(rta):
 | |
|     return rta.rta_len - RTA_LENGTH(0)
 | |
| 
 | |
| RTNH_F_DEAD = 1         # Nexthop is dead (used by multipath)
 | |
| RTNH_F_PERVASIVE = 2    # Do recursive gateway lookup
 | |
| RTNH_F_ONLINK = 4       # Gateway is forced on link
 | |
| 
 | |
| # Reserved table identifiers
 | |
| 
 | |
| RT_TABLE_UNSPEC = 0
 | |
| # User defined values
 | |
| RT_TABLE_COMPAT = 252
 | |
| RT_TABLE_DEFAULT = 253
 | |
| RT_TABLE_MAIN = 254
 | |
| RT_TABLE_LOCAL = 255
 | |
| RT_TABLE_MAX = 0xFFFFFFFF
 | |
| 
 | |
| class Rtmsg(Nlmsg):
 | |
| 
 | |
|     _fields_ = [
 | |
|         ('rtm_family', c_uint8),
 | |
|         ('rtm_dst_len', c_uint8),
 | |
|         ('rtm_src_len', c_uint8),
 | |
|         ('rtm_tos', c_uint8),
 | |
|         ('rtm_table', c_uint8),
 | |
|         ('rtm_protocol', c_uint8),
 | |
|         ('rtm_scope', c_uint8),
 | |
|         ('rtm_type', c_uint8),
 | |
|         ('rtm_flags', c_uint32),
 | |
|     ]
 | |
| 
 | |
|     _table_str = {
 | |
|         RT_TABLE_UNSPEC: "unspecified",
 | |
|         RT_TABLE_COMPAT: "compat",
 | |
|         RT_TABLE_DEFAULT: "default",
 | |
|         RT_TABLE_MAIN: "main",
 | |
|         RT_TABLE_LOCAL: "local",
 | |
|     }
 | |
| 
 | |
|     _proto_str = {
 | |
|         RTPROT_UNSPEC: "none",
 | |
|         RTPROT_REDIRECT: "redirect",
 | |
|         RTPROT_KERNEL: "kernel",
 | |
|         RTPROT_BOOT: "boot",
 | |
|         RTPROT_STATIC: "static",
 | |
|         RTPROT_GATED: "gated",
 | |
|         RTPROT_RA: "ra",
 | |
|         RTPROT_MRT: "mrtmrt",
 | |
|         RTPROT_ZEBRA: "zebra",
 | |
|         RTPROT_BIRD: "bird",
 | |
|         RTPROT_DNROUTED: "dnrouted",
 | |
|         RTPROT_XORP: "xorp",
 | |
|         RTPROT_NTK: "ntk",
 | |
|         RTPROT_DHCP: "dhcp",
 | |
|     }
 | |
| 
 | |
|     _scope_str = {
 | |
|         RT_SCOPE_UNIVERSE: "universe",
 | |
|         RT_SCOPE_SITE: "site",
 | |
|         RT_SCOPE_LINK: "link",
 | |
|         RT_SCOPE_HOST: "host",
 | |
|         RT_SCOPE_NOWHERE: "nowhere",
 | |
|     }
 | |
| 
 | |
|     _type_str = {
 | |
|         RTN_UNSPEC: "unspecified",
 | |
|         RTN_UNICAST: "unicast",
 | |
|         RTN_LOCAL: "local",
 | |
|         RTN_BROADCAST: "broadcast",
 | |
|         RTN_ANYCAST: "anycast",
 | |
|         RTN_MULTICAST: "multicast",
 | |
|         RTN_BLACKHOLE: "blackhole",
 | |
|         RTN_UNREACHABLE: "unreachable",
 | |
|         RTN_PROHIBIT: "prohibit",
 | |
|         RTN_THROW: "throw",
 | |
|         RTN_NAT: "nat",
 | |
|         RTN_XRESOLVE: "xresolve",
 | |
|     }
 | |
| 
 | |
|     def dump(self):
 | |
|         print 'rtm_family', self.rtm_family
 | |
|         print 'rtm_dst_len', self.rtm_dst_len
 | |
|         print 'rtm_src_len', self.rtm_src_len
 | |
|         print 'rtm_tos', self.rtm_tos
 | |
|         print 'rtm_table', self._table_str.get(self.rtm_table, self.rtm_table)
 | |
|         print 'rtm_protocol', self._proto_str.get(self.rtm_protocol)
 | |
|         print 'rtm_scope', self._scope_str.get(self.rtm_scope)
 | |
|         print 'rtm_type', self._type_str.get(self.rtm_type)
 | |
|         print 'rtm_flags 0x%08x' % self.rtm_flags
 | |
| 
 | |
|     def rta_fn(self, rta_type):
 | |
|         fns = {
 | |
|             RTA_DST: self.rta_addr,
 | |
|             RTA_SRC: self.rta_addr,
 | |
|             RTA_IIF: self.rta_uint32,
 | |
|             RTA_OIF: self.rta_uint32,
 | |
|             RTA_GATEWAY: self.rta_addr,
 | |
|             RTA_PRIORITY: self.rta_uint32,
 | |
|             RTA_PREFSRC: self.rta_addr,
 | |
|             RTA_METRICS: self.rta_uint32_array,
 | |
|             RTA_MULTIPATH: self.rta_multipath,
 | |
|             RTA_PROTOINFO: self.rta_none,
 | |
|             RTA_FLOW: self.rta_uint32,
 | |
|             RTA_CACHEINFO: self.rta_none,
 | |
|             RTA_SESSION: self.rta_none,
 | |
|             RTA_MP_ALGO: self.rta_none,
 | |
|             RTA_TABLE: self.rta_uint32,
 | |
|         }
 | |
| 
 | |
|         return fns.get(rta_type)
 | |
| 
 | |
| class Rtgenmsg(Nlmsg):
 | |
| 
 | |
|     _fields_ = [
 | |
|         ('rtgen_family', c_uint8),
 | |
|     ]
 | |
| 
 | |
|     def dump(self):
 | |
|         print 'rtgen_family', self.rtgen_family
 | |
| 
 | |
| # New extended info filters for IFLA_EXT_MASK
 | |
| RTEXT_FILTER_VF = (1 << 0)
 | |
| 
 | |
| # passes link level specific information, not dependent
 | |
| # on network protocol.
 | |
| 
 | |
| 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          # Wireless Extension event - see wireless.h
 | |
| IFLA_PROTINFO = 12          # Protocol specific information for a link
 | |
| 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            # Number of VFs if device is SR-IOV PF
 | |
| IFLA_VFINFO_LIST = 22
 | |
| IFLA_STATS64 = 23
 | |
| IFLA_VF_PORTS = 24
 | |
| IFLA_PORT_SELF = 25
 | |
| IFLA_AF_SPEC = 26
 | |
| IFLA_GROUP = 27             # Group the device belongs to
 | |
| IFLA_NET_NS_FD = 28
 | |
| IFLA_EXT_MASK = 29          # Extended info mask, VFs, etc
 | |
| 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_MAX = 40
 | |
| 
 | |
| # IFLA_LINKINFO attributes
 | |
| IFLA_INFO_UNSPEC = 0
 | |
| IFLA_INFO_KIND = 1
 | |
| IFLA_INFO_DATA = 2
 | |
| IFLA_INFO_XSTATS = 3
 | |
| IFLA_INFO_MAX = 4
 | |
| 
 | |
| # IFLA_LINKINFO_DATA attributes for vlan
 | |
| IFLA_VLAN_UNSPEC = 0
 | |
| IFLA_VLAN_ID = 1
 | |
| 
 | |
| # IFLA_LINKINFO_DATA attributes for macvlan
 | |
| IFLA_MACVLAN_UNSPEC = 0
 | |
| IFLA_MACVLAN_MODE = 1
 | |
| 
 | |
| # macvlan modes
 | |
| MACVLAN_MODE_PRIVATE = 1
 | |
| MACVLAN_MODE_VEPA = 2
 | |
| MACVLAN_MODE_BRIDGE = 3
 | |
| MACVLAN_MODE_PASSTHRU = 4
 | |
| 
 | |
| # BRIDGE IFLA_AF_SPEC attributes
 | |
| IFLA_BRIDGE_FLAGS = 0
 | |
| IFLA_BRIDGE_MODE = 1
 | |
| IFLA_BRIDGE_VLAN_INFO = 2
 | |
| 
 | |
| # BRIDGE_VLAN_INFO flags
 | |
| BRIDGE_VLAN_INFO_MASTER = 1
 | |
| BRIDGE_VLAN_INFO_PVID = 2
 | |
| BRIDGE_VLAN_INFO_UNTAGGED = 4
 | |
| 
 | |
| # Bridge flags
 | |
| BRIDGE_FLAGS_MASTER = 1
 | |
| BRIDGE_FLAGS_SELF = 2
 | |
| 
 | |
| class BridgeVlanInfo(Structure):
 | |
|     _fields_ = [
 | |
|         ('flags', c_uint16),
 | |
|         ('vid', c_uint16),
 | |
|     ]
 | |
| 
 | |
| class Ifinfomsg(Nlmsg):
 | |
| 
 | |
|     _fields_ = [
 | |
|         ('ifi_family', c_uint8),
 | |
|         ('__ifi_pad', c_uint8),
 | |
|         ('ifi_type', c_uint16),      # ARPHRD_*
 | |
|         ('ifi_index', c_int32),      # Link index
 | |
|         ('ifi_flags', c_uint32),     # IFF_* flags
 | |
|         ('ifi_change', c_uint32),    # IFF_* change mask
 | |
|     ]
 | |
| 
 | |
|     def dump(self):
 | |
|         print 'ifi_family', self.ifi_family
 | |
|         print 'ifi_type', self.ifi_type
 | |
|         print 'ifi_index', self.ifi_index
 | |
|         print 'ifi_flags 0x%08x' % self.ifi_flags
 | |
|         print 'ifi_change 0x%08x' % self.ifi_change
 | |
| 
 | |
|     def rta_linkinfo_data_vlan_policy(self):
 | |
|         fns = {
 | |
|             IFLA_VLAN_ID : self.rta_uint16,
 | |
|         }
 | |
|         return fns
 | |
| 
 | |
|     def rta_linkinfo_data_macvlan_policy(self):
 | |
|         fns = {
 | |
|             IFLA_MACVLAN_MODE : self.rta_uint32,
 | |
|         }
 | |
|         return fns
 | |
| 
 | |
|     def rta_linkinfo_policy(self):
 | |
|         fns = {
 | |
|             IFLA_INFO_KIND : self.rta_string,
 | |
|             IFLA_INFO_DATA : self.rta_linkinfo_data,
 | |
|         }
 | |
|         return fns
 | |
| 
 | |
|     def rta_bridge_af_spec_policy(self):
 | |
|         # Assume bridge family for now
 | |
|         fns = {
 | |
|             IFLA_BRIDGE_FLAGS : self.rta_uint16,
 | |
|             IFLA_BRIDGE_VLAN_INFO : self.rta_bridge_vlan_info,
 | |
|         }
 | |
|         return fns
 | |
| 
 | |
|     def rta_policy(self):
 | |
|         fns = {
 | |
|             IFLA_UNSPEC: self.rta_wtf,
 | |
|             IFLA_ADDRESS: self.rta_uint8_array,
 | |
|             IFLA_BROADCAST: self.rta_uint8_array,
 | |
|             IFLA_IFNAME: self.rta_string,
 | |
|             IFLA_MTU: self.rta_uint32,
 | |
|             IFLA_LINK: self.rta_uint32,
 | |
|             IFLA_QDISC: self.rta_string,
 | |
|             IFLA_STATS: self.rta_none,
 | |
|             IFLA_COST: self.rta_none,
 | |
|             IFLA_PRIORITY: self.rta_none,
 | |
|             IFLA_MASTER: self.rta_uint32,
 | |
|             IFLA_WIRELESS: self.rta_none,
 | |
|             IFLA_PROTINFO: self.rta_none,
 | |
|             IFLA_TXQLEN: self.rta_uint32,
 | |
|             IFLA_MAP: self.rta_none,
 | |
|             IFLA_WEIGHT: self.rta_uint32,
 | |
|             IFLA_OPERSTATE: self.rta_uint8,
 | |
|             IFLA_LINKMODE: self.rta_uint8,
 | |
|             IFLA_LINKINFO: self.rta_linkinfo,
 | |
|             IFLA_NET_NS_PID: self.rta_uint32,
 | |
|             IFLA_IFALIAS: self.rta_string,
 | |
|             IFLA_NUM_VF: self.rta_uint32,
 | |
|             IFLA_VFINFO_LIST: self.rta_none,
 | |
|             IFLA_STATS64: self.rta_none,
 | |
|             IFLA_VF_PORTS: self.rta_none,
 | |
|             IFLA_PORT_SELF: self.rta_none,
 | |
|             IFLA_AF_SPEC: self.rta_af_spec,
 | |
|             IFLA_GROUP: self.rta_none,
 | |
|             IFLA_NET_NS_FD: self.rta_none,
 | |
|             IFLA_EXT_MASK: self.rta_none,
 | |
|             IFLA_PROMISCUITY: self.rta_uint32,
 | |
|             IFLA_NUM_TX_QUEUES: self.rta_uint32,
 | |
|             IFLA_NUM_RX_QUEUES: self.rta_uint32,
 | |
|             IFLA_CARRIER: self.rta_uint8,
 | |
|             IFLA_PHYS_PORT_ID: self.rta_uint8_array,
 | |
|             IFLA_CARRIER_CHANGES: self.rta_uint32,
 | |
|             IFLA_PHYS_SWITCH_ID: self.rta_uint8_array,
 | |
|             IFLA_LINK_NETNSID: self.rta_sint32,
 | |
|             IFLA_PHYS_PORT_NAME: self.rta_string,
 | |
|             IFLA_PROTO_DOWN: self.rta_uint8,
 | |
|         }
 | |
|         return fns;
 | |
| 
 | |
|     def rta_fn(self, rta_type):
 | |
|         fns = {
 | |
|             IFLA_UNSPEC: self.rta_wtf,
 | |
|             IFLA_ADDRESS: self.rta_uint8_array,
 | |
|             IFLA_BROADCAST: self.rta_uint8_array,
 | |
|             IFLA_IFNAME: self.rta_string,
 | |
|             IFLA_MTU: self.rta_uint32,
 | |
|             IFLA_LINK: self.rta_uint32,
 | |
|             IFLA_QDISC: self.rta_string,
 | |
|             IFLA_STATS: self.rta_none,
 | |
|             IFLA_COST: self.rta_none,
 | |
|             IFLA_PRIORITY: self.rta_none,
 | |
|             IFLA_MASTER: self.rta_uint32,
 | |
|             IFLA_WIRELESS: self.rta_none,
 | |
|             IFLA_PROTINFO: self.rta_none,
 | |
|             IFLA_TXQLEN: self.rta_uint32,
 | |
|             IFLA_MAP: self.rta_none,
 | |
|             IFLA_WEIGHT: self.rta_uint32,
 | |
|             IFLA_OPERSTATE: self.rta_uint8,
 | |
|             IFLA_LINKMODE: self.rta_uint8,
 | |
|             IFLA_LINKINFO: self.rta_linkinfo,
 | |
|             IFLA_NET_NS_PID: self.rta_uint32,
 | |
|             IFLA_IFALIAS: self.rta_string,
 | |
|             IFLA_NUM_VF: self.rta_uint32,
 | |
|             IFLA_VFINFO_LIST: self.rta_none,
 | |
|             IFLA_STATS64: self.rta_none,
 | |
|             IFLA_VF_PORTS: self.rta_none,
 | |
|             IFLA_PORT_SELF: self.rta_none,
 | |
|             IFLA_AF_SPEC: self.rta_af_spec,
 | |
|             IFLA_GROUP: self.rta_none,
 | |
|             IFLA_NET_NS_FD: self.rta_none,
 | |
|             IFLA_EXT_MASK: self.rta_none,
 | |
|             IFLA_PROMISCUITY: self.rta_uint32,
 | |
|             IFLA_NUM_TX_QUEUES: self.rta_uint32,
 | |
|             IFLA_NUM_RX_QUEUES: self.rta_uint32,
 | |
|             IFLA_CARRIER: self.rta_uint8,
 | |
|             IFLA_PHYS_PORT_ID: self.rta_uint8_array,
 | |
|             IFLA_CARRIER_CHANGES: self.rta_uint32,
 | |
|             IFLA_PHYS_SWITCH_ID: self.rta_uint8_array,
 | |
|             IFLA_LINK_NETNSID: self.rta_sint32,
 | |
|             IFLA_PHYS_PORT_NAME: self.rta_string,
 | |
|             IFLA_PROTO_DOWN: self.rta_uint8,
 | |
|         }
 | |
|         return fns.get(rta_type)
 | |
| 
 | |
| # passes address specific information
 | |
| 
 | |
| # Important comment:
 | |
| # IFA_ADDRESS is prefix address, rather than local interface address.
 | |
| # It makes no difference for normally configured broadcast interfaces,
 | |
| # but for point-to-point IFA_ADDRESS is DESTINATION address,
 | |
| # local address is supplied in IFA_LOCAL attribute.
 | |
| 
 | |
| IFA_UNSPEC = 0
 | |
| IFA_ADDRESS = 1
 | |
| IFA_LOCAL = 2
 | |
| IFA_LABEL = 3
 | |
| IFA_BROADCAST = 4
 | |
| IFA_ANYCAST = 5
 | |
| IFA_CACHEINFO = 6
 | |
| IFA_MULTICAST = 7
 | |
| IFA_MAX = 7
 | |
| 
 | |
| class Ifaddrmsg(Nlmsg):
 | |
| 
 | |
|     _fields_ = [
 | |
|         ('ifa_family', c_uint8),
 | |
|         ('ifa_prefixlen', c_uint8), # The prefix length
 | |
|         ('ifa_flags', c_uint8),     # Flags
 | |
|         ('ifa_scope', c_uint8),     # Address scope
 | |
|         ('ifa_index', c_uint32),    # Link index
 | |
|     ]
 | |
| 
 | |
|     _family_str = {
 | |
|         AF_INET: "inet",
 | |
|         AF_INET6: "inet6",
 | |
|     }
 | |
| 
 | |
|     def dump(self):
 | |
|         print 'ifa_family', self.ifa_family
 | |
|         print 'ifa_prefixlen', self.ifa_prefixlen
 | |
|         print 'ifa_flags 0x%02x' % self.ifa_flags
 | |
|         print 'ifa_scope', self.ifa_scope
 | |
|         print 'ifa_index', self.ifa_index
 | |
| 
 | |
|     def rta_fn(self, rta_type):
 | |
|         fns = {
 | |
|             IFA_ADDRESS: self.rta_addr,
 | |
|             IFA_LOCAL: self.rta_addr,
 | |
|             IFA_LABEL: self.rta_string,
 | |
|             IFA_BROADCAST: self.rta_addr,
 | |
|             IFA_ANYCAST: self.rta_addr,
 | |
|             IFA_CACHEINFO: self.rta_none,
 | |
|             IFA_MULTICAST: self.rta_addr,
 | |
|         }
 | |
|         return fns.get(rta_type)
 | |
| 
 | |
| class RtNetlinkError(Exception):
 | |
| 
 | |
|     def __init__(self, message):
 | |
|         Exception.__init__(self, message)
 | |
|         logger.error(message)
 | |
| 
 | |
| class RtNetlink(Netlink):
 | |
| 
 | |
|     def __init__(self, pid):
 | |
|         Netlink.__init__(self, pid, NETLINK_ROUTE)
 | |
| 
 | |
|     _rt_nlmsg_type_str = {
 | |
|         RTM_NEWROUTE: "RTM_NEWROUTE",
 | |
|         RTM_DELROUTE: "RTM_DELROUTE",
 | |
|         RTM_NEWLINK: "RTM_NEWLINK",
 | |
|         RTM_SETLINK: "RTM_SETLINK",
 | |
|         RTM_DELLINK: "RTM_DELLINK",
 | |
|         RTM_GETLINK: "RTM_GETLINK",
 | |
|         RTM_NEWADDR: "RTM_NEWADDR",
 | |
|         RTM_DELADDR: "RTM_DELADDR",
 | |
|     }
 | |
| 
 | |
|     def _hexdump(self, buf):
 | |
|         while buf:
 | |
|             chunk = buf[:16]
 | |
|             buf = buf[16:]
 | |
|             nums = ["%02x" % c for c in chunk]
 | |
|             txt = [chr(c) if chr(c) in printable[:-5] else '.' for c in chunk]
 | |
|             print " ".join(nums).ljust(48), "".join(txt)
 | |
| 
 | |
|     def dump(self, nlh):
 | |
|         nlmsg = self.nlmsg(nlh)
 | |
|         print
 | |
|         self._hexdump(self.sendbuf[:nlh.nlmsg_len])
 | |
|         print
 | |
|         nlh.dump()
 | |
|         print
 | |
|         nlmsg.dump()
 | |
|         print
 | |
|         nlmsg.dump_rtas()
 | |
| 
 | |
|     def nlmsg(self, nlh):
 | |
|         nlmsg_struct = {
 | |
|             RTM_NEWROUTE: Rtmsg,
 | |
|             RTM_DELROUTE: Rtmsg,
 | |
|             RTM_GETROUTE: Rtmsg,
 | |
|             RTM_NEWLINK: Ifinfomsg,
 | |
|             RTM_SETLINK: Ifinfomsg,
 | |
|             RTM_DELLINK: Ifinfomsg,
 | |
|             RTM_GETLINK: Rtgenmsg,
 | |
|             RTM_NEWADDR: Ifaddrmsg,
 | |
|             RTM_DELADDR: Ifaddrmsg,
 | |
|             RTM_GETADDR: Rtgenmsg,
 | |
|         }
 | |
|         nldata = NLMSG_DATA(nlh)
 | |
|         nlmsg = nlmsg_struct[nlh.nlmsg_type].from_address(nldata)
 | |
|         nlmsg.nlh = nlh
 | |
|         return nlmsg
 | |
| 
 | |
|     def _nl_cb(self, nlh):
 | |
| #        print "nl cb", self._rt_nlmsg_type_str[nlh.nlmsg_type]
 | |
| 
 | |
|         if nlh.nlmsg_type in self._cbs:
 | |
| 
 | |
|             nlmsg = self.nlmsg(nlh)
 | |
| 
 | |
|             # validate nl length
 | |
|             if nlh.nlmsg_len - NLMSG_LENGTH(sizeof(nlmsg)) < 0:
 | |
|                 raise RtNetlinkError("invalid nl length")
 | |
| 
 | |
|             self._cbs[nlh.nlmsg_type](nlh, nlmsg)
 | |
| 
 | |
|     def bind(self, groups, cbs):
 | |
|         self._cbs = cbs
 | |
|         Netlink.bind(self, groups, self._nl_cb)
 | |
| 
 | |
|     def request(self, nlmsg_type, flags, extra, rtas={}):
 | |
| 
 | |
|         nlh = Nlmsghdr.from_buffer(self.sendbuf)
 | |
|         nlh_p = addressof(nlh)
 | |
| 
 | |
|         seq = self.seq
 | |
|         pid = self.pid
 | |
| 
 | |
|         nlh.nlmsg_len = NLMSG_HDRLEN()
 | |
|         nlh.nlmsg_type = nlmsg_type
 | |
|         nlh.nlmsg_flags = flags
 | |
|         nlh.nlmsg_pid = pid
 | |
|         nlh.nlmsg_seq = seq
 | |
| 
 | |
|         nlmsg = self.nlmsg(nlh)
 | |
| 
 | |
|         nlh.nlmsg_len += nlmsg.pack_extra(extra, nlh_p + nlh.nlmsg_len)
 | |
|         nlh.nlmsg_len += nlmsg.pack_rtas_new(rtas, nlh_p + nlh.nlmsg_len,
 | |
|                                              nlmsg.rta_policy())
 | |
|         #self.dump(nlh)
 | |
|         self.sendall(string_at(nlh_p, nlh.nlmsg_len))
 | |
|         self.seq += 1
 | |
| 
 | |
|         token = (pid, seq)
 | |
|         return token
 |