]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
add support for single vxlan device (bridge-vlan-vni-map)
authorJulien Fortin <julien@cumulusnetworks.com>
Thu, 14 May 2020 00:16:47 +0000 (02:16 +0200)
committerJulien Fortin <julien@cumulusnetworks.com>
Thu, 14 May 2020 00:18:39 +0000 (02:18 +0200)
new attribute:

"bridge-vlan-vni-map": {
    "help": "Single vxlan support",
    "example": "bridge-vlan-vni-map 1000-1001=1000-1001",
}

example of config:

auto bridge
iface bridge
      bridge-vlan-aware yes
      bridge-ports vxlan0 swp1
      bridge-stp on
      bridge-vids 1000-1001
      bridge-pvid 1

auto vxlan0
iface vxlan0
      vxlan-local-tunnelip 27.0.0.9
      bridge-learning off
      # vlan 1000-1001 maps to vni 1000-1001
      bridge-vlan-vni-map 1000-1001=1000-1001

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
debian/changelog
ifupdown2/addons/bridge.py
ifupdown2/addons/vxlan.py
ifupdown2/ifupdown/iface.py
ifupdown2/lib/iproute2.py
ifupdown2/lib/nlcache.py
ifupdown2/nlmanager/nlpacket.py

index 19aa60bc6bc088b1201997666809452d7a7e2f0d..f902af2da6a1fae29e92c6b28bc4d1dfddfd5d1b 100644 (file)
@@ -3,6 +3,7 @@ ifupdown2 (3.0.1-1) unstable; urgency=medium
    * New. Enabled: bridge-always-up attribute
    * New. Enabled: ES bond with "es-sys-mac" attribute
    * New. Enabled: dhcp policy: dhclient_retry_on_failure
+   * New. Enabled: bridge-vlan-vni-map attribute (single vxlan device)
    * Fix: start-networking script is back to handle mgmt & hotplug cases
 
  -- Julien Fortin <julien@cumulusnetworks.com>  Tue, 14 Apr 2020 23:42:42 +0200
index 1f8016215f8f7914a8d773d359d841db9be4cbd4..ff902c22cc73072af95ea45d774b189dc7f36daa 100644 (file)
@@ -441,6 +441,10 @@ class bridge(Addon, moduleBase):
                     "required": False,
                     "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"]
             },
+            "bridge-vlan-vni-map": {
+                "help": "Single vxlan support",
+                "example": "bridge-vlan-vni-map 1000-1001=1000-1001",
+            },
             "bridge-always-up": {
                 "help": "Enabling this attribute on a bridge will enslave a dummy interface to the bridge",
                 "required": False,
@@ -917,8 +921,7 @@ class bridge(Addon, moduleBase):
     def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
         if not ifaceobj_getfunc:
             return True
-        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN
-                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
             if ifaceobj.get_attr_value('bridge-access'):
                 return True
             for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
@@ -1555,7 +1558,7 @@ class bridge(Addon, moduleBase):
             because kernel does honor vid info flags during deletes.
 
         """
-        if not isbridge and bportifaceobj.link_kind & ifaceLinkKind.VXLAN:
+        if not isbridge and (bportifaceobj.link_kind & ifaceLinkKind.VXLAN and not bportifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
             if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
                 self._error_vxlan_in_vlan_aware_br(bportifaceobj,
                                                    bportifaceobj.upperifaces[0])
@@ -1628,6 +1631,9 @@ class bridge(Addon, moduleBase):
             if vids_to_del:
                if pvid_to_add in vids_to_del:
                    vids_to_del.remove(pvid_to_add)
+
+               vids_to_del = self.remove_bridge_vlans_mapped_to_vnis_from_vids_list(None, bportifaceobj, vids_to_del)
+
                self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name,
                                           self._compress_into_ranges(
                                           vids_to_del), isbridge)
@@ -1662,6 +1668,47 @@ class bridge(Addon, moduleBase):
                                %(bportifaceobj.name, pvid_to_add, str(e)),
                                bportifaceobj)
 
+    def get_bridge_vlans_mapped_to_vnis_as_integer_list(self, ifaceobj):
+        """
+            Get all vlans that the user wants to configured in vlan-vni maps
+        """
+        try:
+            vids = []
+
+            for vlans_vnis_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
+                vids.extend(self._ranges_to_ints([vlans_vnis_map.split("=")[0]]))
+
+            return vids
+        except Exception as e:
+            self.logger.debug("get_bridge_vlans_mapped_to_vnis_as_integer_list: %s" % str(e))
+            return []
+
+    def remove_bridge_vlans_mapped_to_vnis_from_vids_list(self, bridge_ifaceobj, vxlan_ifaceobj, vids_list):
+        """
+            For single vxlan we need to remove the vlans mapped to vnis
+            from the vids list otherwise they will get removed from the brport
+        """
+        if not (vxlan_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
+            return vids_list
+
+        user_config_vids = []
+
+        if bridge_ifaceobj:
+            for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(bridge_ifaceobj):
+                user_config_vids.append(vid)
+
+        if vxlan_ifaceobj:
+            for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(vxlan_ifaceobj):
+                user_config_vids.append(vid)
+
+        for vlan in user_config_vids:
+            try:
+                vids_list.remove(vlan)
+            except:
+                pass
+
+        return vids_list
+
     def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
                                                    bridge_vids=None,
                                                    bridge_pvid=None):
@@ -1916,6 +1963,7 @@ class bridge(Addon, moduleBase):
 
     def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
         ifname = ifaceobj.name
+        single_vxlan_device_ifaceobj = None
 
         try:
             brports_ifla_info_slave_data    = dict()
@@ -2221,9 +2269,20 @@ class bridge(Addon, moduleBase):
                             pass
 
                     #
-                    #
+                    # SINGLE VXLAN - enable IFLA_BRPORT_VLAN_TUNNEL
                     #
 
+                    if brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
+                        single_vxlan_device_ifaceobj = brport_ifaceobj
+                        brport_vlan_tunnel_cached_value = self.cache.get_link_info_slave_data_attribute(
+                            brport_name,
+                            Link.IFLA_BRPORT_VLAN_TUNNEL
+                        )
+
+                        if not brport_vlan_tunnel_cached_value:
+                            self.logger.info("%s: %s: enabling vlan_tunnel on single vxlan device" % (ifname, brport_name))
+                            brport_ifla_info_slave_data[Link.IFLA_BRPORT_VLAN_TUNNEL] = 1
+
                 else:
                     kind = None
                     ifla_info_data = {}
@@ -2244,6 +2303,41 @@ class bridge(Addon, moduleBase):
         except Exception as e:
             self.log_error(str(e), ifaceobj)
 
+        if single_vxlan_device_ifaceobj:
+            self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj)
+
+    def apply_bridge_port_vlan_vni_map(self, ifaceobj):
+        """
+        bridge vlan add vid <vlan-id> dev vxlan0
+        bridge vlan add dev vxlan0 vid <vlan-id> tunnel_info id <vni>
+        """
+        vxlan_name = ifaceobj.name
+        try:
+            self.iproute2.batch_start()
+            for vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
+
+                try:
+                    vlans_str, vni_str = vlan_vni_map.split("=")
+                except:
+                    return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
+
+                vlans = self._ranges_to_ints([vlans_str])
+                vnis = self._ranges_to_ints([vni_str])
+
+                if len(vlans) != len(vnis):
+                    return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
+
+                # TODO: query the cache prio to executing those commands
+                self.iproute2.bridge_vlan_add_vid_list_self(vxlan_name, vlans, False)
+                self.iproute2.bridge_vlan_add_vlan_tunnel_info(vxlan_name, vlans, vnis)
+
+            self.iproute2.batch_commit()
+        except Exception as e:
+            self.log_error("%s: error while processing bridge-vlan-vni-map attribute: %s" % (vxlan_name, str(e)))
+
+    def __warn_bridge_vlan_vni_map_syntax_error(self, user_config_vlan_vni_map):
+        self.logger.warning("%s: syntax error: bridge-vlan-vni-map %s" % user_config_vlan_vni_map)
+
     def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
         """ Detect QinQ bridge
         Potential improvement: We could add a ifaceobj.link_privflags called
@@ -3259,6 +3353,21 @@ class bridge(Addon, moduleBase):
         attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
         if vids:
            vids = re.split(r'[\s\t]\s*', vids)
+
+           # Special treatment to make sure that the vlans mapped with vnis
+           # (in single-vxlan context) are not mistaken for regular vlans.
+           # We need to proactively remove them from the "running_vids"
+           vlans_mapped_with_vnis = self.get_bridge_vlans_mapped_to_vnis_as_integer_list(ifaceobj)
+           new_running_vids = []
+           user_config_vids = self._ranges_to_ints(vids)
+           for v in running_vids:
+               if v in user_config_vids:
+                   new_running_vids.append(v)
+               elif v not in vlans_mapped_with_vnis:
+                   new_running_vids.append(v)
+           running_vids = new_running_vids
+           #####################################################################
+
            if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False):
                running_vids = [str(o) for o in running_vids]
                ifaceobjcurr.update_config_with_status(attr_name,
@@ -3401,6 +3510,85 @@ class bridge(Addon, moduleBase):
 
         self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
 
+        #
+        # bridge-vlan-vni-map
+        #
+        fail = False
+        cached_vlans, cached_vnis = self.get_vlan_vni_ranges(self.cache.get_vlan_vni(ifaceobj.name))
+
+        for bridge_vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
+
+            if fail:
+                ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
+                continue
+
+            try:
+                vlans_str, vni_str = bridge_vlan_vni_map.split("=")
+            except:
+                ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
+                return self.__warn_bridge_vlan_vni_map_syntax_error(bridge_vlan_vni_map)
+
+            vlans_list = self._ranges_to_ints([vlans_str])   # self.bridge_vlan_vni_map_convert_user_config_to_set(vlans_str)
+            vnis_list = self._ranges_to_ints([vni_str]) #self.bridge_vlan_vni_map_convert_user_config_to_set(vni_str)
+
+            # since there can be multiple entry of bridge-vlan-vni-map
+            # we could simply check that all vlans and vnis are correctly
+            # set on the vxlan but we would probably miss the case where extra
+            # vlans and vnis were added. So we ned to keep a copy of the cache
+            # entry and pop vlans and svis from the cache copy as we iterate
+            # through the user config. After processing only extra vlans and
+            # vnis should be left.
+            try:
+                for vlan in vlans_list:
+                    cached_vlans.remove(vlan)
+            except:
+                ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
+                fail = True
+                continue
+
+            try:
+                for vni in vnis_list:
+                    cached_vnis.remove(vni)
+            except:
+                ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
+                fail = True
+                continue
+
+            ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 0)
+
+        if not fail and (cached_vlans or cached_vnis):
+            # cached_vlans and cached_vnis are not empty, it means more
+            # vlans-vni maps were configured on the bridge port
+            ifaceobjcurr.update_config_with_status(
+                "bridge-vlan-vni-map",
+                "%s=%s" % (cached_vlans, cached_vnis),
+                1
+            )
+
+    @staticmethod
+    def get_vlan_vni_ranges(bridge_vlan_tunnel):
+        vlans = []
+        vnis = []
+
+        tunnel_vlan_range = None
+        tunnel_vni_range = None
+
+        for tunnel_vlan, tunnel_vni, tunnel_flags in bridge_vlan_tunnel:
+
+            if tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
+                tunnel_vlan_range = tunnel_vlan
+                tunnel_vni_range = tunnel_vni
+
+            elif tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_END:
+                vlans.extend(range(tunnel_vlan_range, tunnel_vlan + 1))
+                vnis.extend(range(tunnel_vni_range, tunnel_vni + 1))
+
+            else:
+                vlans.append(tunnel_vlan)
+                vnis.append(tunnel_vni)
+
+        return vlans, vnis
+
     def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
         user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
 
index 30f44ffa31a0076cc2ee5886b4cb64e48d611d59..9b080d6cc1d54783f8275c7cd217af7f8674e65d 100644 (file)
@@ -156,6 +156,9 @@ class vxlan(Addon, moduleBase):
         return True
 
     def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+        if ifaceobj.get_attr_value_first("bridge-vlan-vni-map"):
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.SINGLE_VXLAN
+
         if self._is_vxlan_device(ifaceobj):
             ifaceobj.link_kind |= ifaceLinkKind.VXLAN
             self._set_global_local_ip(ifaceobj)
@@ -191,7 +194,10 @@ class vxlan(Addon, moduleBase):
 
     @staticmethod
     def _is_vxlan_device(ifaceobj):
-        return ifaceobj.link_kind & ifaceLinkKind.VXLAN or ifaceobj.get_attr_value_first('vxlan-id')
+        return ifaceobj.link_kind & ifaceLinkKind.VXLAN \
+               or ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN \
+               or ifaceobj.get_attr_value_first("vxlan-id") \
+               or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")
 
     def __get_vlxan_purge_remotes(self, ifaceobj):
         if not ifaceobj:
@@ -609,7 +615,8 @@ class vxlan(Addon, moduleBase):
     def _up(self, ifaceobj):
         vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id")
 
-        if not vxlan_id_str:
+        if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not vxlan_id_str:
+            self.logger.warning("%s: missing vxlan-id attribute on vxlan device" % ifaceobj.name)
             return
 
         ifname = ifaceobj.name
@@ -634,7 +641,10 @@ class vxlan(Addon, moduleBase):
 
         user_request_vxlan_info_data = {}
 
-        self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+        if vxlan_id_str:
+            # for single vxlan device we don't have a vxlan-id
+            self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+
         self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
         self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
         self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
@@ -674,20 +684,30 @@ class vxlan(Addon, moduleBase):
                 # element: vxlan-id
                 self.logger.info('%s: vxlan already exists - no change detected' % ifname)
             else:
-                try:
-                    if flap_vxlan_device:
-                        self.netlink.link_down_force(ifname)
-
-                    self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data)
-
-                    if flap_vxlan_device:
-                        self.netlink.link_up_force(ifname)
-                except Exception as e:
+                if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
                     if link_exists:
-                        self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj)
+                        self.logger.warning("%s: updating existing single vxlan device is not support yet" % ifname)
                     else:
-                        self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj)
-                    return
+                        self.iproute2.link_add_single_vxlan(
+                            ifname,
+                            local,
+                            user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT)
+                        )
+                else:
+                    try:
+                        if flap_vxlan_device:
+                            self.netlink.link_down_force(ifname)
+
+                        self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data)
+
+                        if flap_vxlan_device:
+                            self.netlink.link_up_force(ifname)
+                    except Exception as e:
+                        if link_exists:
+                            self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj)
+                        else:
+                            self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj)
+                        return
 
         vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
 
index ab1b8373d2c537ef732fe92017b8b2d3efe9b8a7..3c699b5ba063f06aa5ffcd5bd273eccd854b7a8f 100644 (file)
@@ -79,6 +79,7 @@ class ifaceLinkKind():
 class ifaceLinkPrivFlags():
     """ This corresponds to kernel netdev->priv_flags
         and can be BRIDGE_PORT, BOND_SLAVE etc """
+
     UNKNOWN =           0x00000
     BRIDGE_PORT =       0x00001
     BOND_SLAVE =        0x00010
@@ -89,7 +90,8 @@ class ifaceLinkPrivFlags():
     LOOPBACK = 0x1000000
     KEEP_LINK_DOWN = 0x10000000
     MGMT_INTF = 0x100000000
-    ES_BOND = 0x1000000000
+    SINGLE_VXLAN = 0x1000000000
+    ES_BOND = 0x10000000000
 
     @classmethod
     def get_str(cls, flag):
index 7872d09c85956311916026546e4a3c4a04199543..2b960a6c1391a34be55401acb9e142737f77c4e1 100644 (file)
@@ -269,6 +269,20 @@ class IPRoute2(Cache, Requirements):
 
     ###
 
+    def link_add_single_vxlan(self, ifname, ip, port):
+        self.logger.info("creating single vxlan device: %s" % ifname)
+
+        cmd = ["link add dev %s type vxlan external" % ifname]
+
+        if ip:
+            cmd.append("local %s" % ip)
+
+        if port:
+            cmd.append("dstport %s" % port)
+
+        self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
+        self.__update_cache_after_link_creation(ifname, "vxlan")
+
     def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
                           remoteips=None, learning='on', ageing=None, ttl=None, physdev=None):
         if svcnodeip and remoteips:
@@ -583,12 +597,18 @@ class IPRoute2(Cache, Requirements):
                 "vlan del vid %s dev %s %s" % (v, ifname, target)
             )
 
-    @staticmethod
-    def bridge_vlan_add_vid_list(ifname, vids):
-        for v in vids:
-            utils.exec_command(
-                "%s vlan add vid %s dev %s" % (utils.bridge_cmd, v, ifname)
-            )
+    def bridge_vlan_add_vlan_tunnel_info(self, ifname, vids, vnis):
+        for i in range(0, len(vids)):
+            try:
+                self.__execute_or_batch(
+                    utils.bridge_cmd,
+                    "vlan add dev %s vid %s tunnel_info id %s" % (
+                        ifname, vids[i], vnis[i]
+                    )
+                )
+            except Exception as e:
+                if "exists" not in str(e).lower():
+                    self.logger.error(e)
 
     def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True):
         target = "self" if is_bridge else ""
index 5de84b95d7e23d1471d23d9f958f2f67231e793d..5f6433f345ff56690a33613d1cd6ad9a7036b0c9 100644 (file)
@@ -161,6 +161,7 @@ class _NetlinkCache:
         self._link_cache = {}
         self._addr_cache = {}
         self._bridge_vlan_cache = {}
+        self._bridge_vlan_vni_cache = {}
 
         # helper dictionaries
         # ifindex: ifname
@@ -306,6 +307,11 @@ class _NetlinkCache:
         except KeyError:
             pass
 
+        try:
+            del self._bridge_vlan_vni_cache[slave]
+        except KeyError:
+            pass
+
     def append_to_ignore_rtm_newlinkq(self, ifname):
         """
         Register device 'ifname' to the ignore_rtm_newlinkq list pending
@@ -1022,6 +1028,10 @@ class _NetlinkCache:
         except (KeyError, AttributeError):
             return 0
 
+    def get_vlan_vni(self, ifname):
+        with self._cache_lock:
+            return self._bridge_vlan_vni_cache.get(ifname)
+
     def get_pvid_and_vids(self, ifname):
         """
         vlan-identifiers are stored in:
@@ -1352,6 +1362,7 @@ class _NetlinkCache:
         """
         vlans_list = []
 
+        # Todo: acquire the lock only when really needed
         with self._cache_lock:
             ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC)
             ifname = msg.get_attribute_value(Link.IFLA_IFNAME)
@@ -1385,6 +1396,9 @@ class _NetlinkCache:
                         # (flag, vlan) so that we can sort the list of tuples
                         vlans_list.append((vlan_id, vlan_flag))
 
+                elif x_type == Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO:
+                    self._bridge_vlan_vni_cache.update({ifname: x_value})
+
             self._bridge_vlan_cache.update({ifname: vlans_list})
 
     def force_add_slave(self, master, slave):
@@ -1516,6 +1530,11 @@ class _NetlinkCache:
             except:
                 pass
 
+            try:
+                del self._bridge_vlan_vni_cache[ifname]
+            except:
+                pass
+
             try:
                 del self._ifname_by_ifindex[ifindex]
             except KeyError:
index c855e5e957f77081c9492b408bf846291b093042..f02ce099a91b57f6cd81786d1af1859e1ef41dd1 100644 (file)
@@ -1661,6 +1661,11 @@ class AttributeIFLA_AF_SPEC(Attribute):
         {
             Link.IFLA_BRIDGE_FLAGS: flags,
             Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
+            Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO: [
+                    __u32 tunnel_id;
+                    __u16 tunnel_vid;
+                    __u16 tunnel_flags;
+            ]
         }
 
         FROM: David Ahern
@@ -1722,6 +1727,13 @@ class AttributeIFLA_AF_SPEC(Attribute):
             sub_attr_data = data[4:sub_attr_end]
 
             if self.family == AF_BRIDGE:
+                # /* Bridge management nested attributes
+                #  * [IFLA_AF_SPEC] = {
+                #  *     [IFLA_BRIDGE_FLAGS]
+                #  *     [IFLA_BRIDGE_MODE]
+                #  *     [IFLA_BRIDGE_VLAN_INFO]
+                #  * }
+                #  */
                 if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
                     self.value[Link.IFLA_BRIDGE_FLAGS] = unpack("=H", sub_attr_data[0:2])[0]
 
@@ -1730,6 +1742,38 @@ class AttributeIFLA_AF_SPEC(Attribute):
                         self.value[Link.IFLA_BRIDGE_VLAN_INFO] = []
                     self.value[Link.IFLA_BRIDGE_VLAN_INFO].append(tuple(unpack("=HH", sub_attr_data[0:4])))
 
+                elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO:
+                    # Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO: {
+                    #     __u32 tunnel_id;
+                    #     __u16 tunnel_vid;
+                    #     __u16 tunnel_flags;
+                    # }
+                    # all the nested attributes are padded on 8 bytes
+
+                    tunnel_id = 0
+                    tunnel_vid = 0
+                    tunnel_flags = 0
+
+                    while sub_attr_data:
+                        (s_sub_attr_length, s_sub_attr_type) = unpack("=HH", sub_attr_data[:4])
+                        s_sub_attr_end = padded_length(s_sub_attr_length)
+                        d = sub_attr_data[4:s_sub_attr_end]
+
+                        if s_sub_attr_type == Link.IFLA_BRIDGE_VLAN_TUNNEL_ID:
+                            tunnel_id = unpack("=L", d)[0]
+
+                        elif s_sub_attr_type == Link.IFLA_BRIDGE_VLAN_TUNNEL_VID:
+                            tunnel_vid = unpack("=L", d)[0]
+
+                        elif s_sub_attr_type == Link.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS:
+                            tunnel_flags = unpack("=L", d)[0]
+
+                        sub_attr_data = sub_attr_data[s_sub_attr_end:]
+
+                    if Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO not in self.value:
+                        self.value[Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO] = []
+
+                    self.value[Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO].append((tunnel_id, tunnel_vid, tunnel_flags))
                 else:
                     self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_AF_SPEC sub-attribute '
                                                      'type %s (%d), length %d, padded to %d' %
@@ -4291,15 +4335,38 @@ class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes):
         IFLA_INET_CONF      : 'IFLA_INET_CONF',
     }
 
+    # /* Bridge Flags */
+    BRIDGE_FLAGS_MASTER = 1  # /* Bridge command to/from master */
+    BRIDGE_FLAGS_SELF = 2  # /* Bridge command to/from lowerdev */
+
+    bridge_flags_to_string = {
+        BRIDGE_FLAGS_MASTER : "BRIDGE_FLAGS_MASTER",
+        BRIDGE_FLAGS_SELF   : "BRIDGE_FLAGS_SELF"
+    }
+
+    BRIDGE_MODE_VEB = 0  # /* Default loopback mode */
+    BRIDGE_MODE_VEPA = 1  # /* 802.1Qbg defined VEPA mode */
+    BRIDGE_MODE_UNDEF = 0xFFFF  # /* mode undefined */
+
+    # /* Bridge management nested attributes
+    #  * [IFLA_AF_SPEC] = {
+    #  *     [IFLA_BRIDGE_FLAGS]
+    #  *     [IFLA_BRIDGE_MODE]
+    #  *     [IFLA_BRIDGE_VLAN_INFO]
+    #  * }
+    #  */
+
     # BRIDGE IFLA_AF_SPEC attributes
     IFLA_BRIDGE_FLAGS     = 0
     IFLA_BRIDGE_MODE      = 1
     IFLA_BRIDGE_VLAN_INFO = 2
+    IFLA_BRIDGE_VLAN_TUNNEL_INFO = 3
 
     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'
+        IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO',
+        IFLA_BRIDGE_VLAN_TUNNEL_INFO : "IFLA_BRIDGE_VLAN_TUNNEL_INFO"
     }
 
     # BRIDGE_VLAN_INFO flags
@@ -4319,15 +4386,33 @@ class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes):
         BRIDGE_VLAN_INFO_BRENTRY     : 'BRIDGE_VLAN_INFO_BRENTRY'
     }
 
-    # 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'
+    # struct bridge_vlan_info {
+    #  __u16 flags;
+    #  __u16 vid;
+    # };
+
+    IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC = 0
+    IFLA_BRIDGE_VLAN_TUNNEL_ID = 1
+    IFLA_BRIDGE_VLAN_TUNNEL_VID = 2
+    IFLA_BRIDGE_VLAN_TUNNEL_FLAGS = 3
+
+    bridge_vlan_tunnel_to_string = {
+        IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC: "IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC",
+        IFLA_BRIDGE_VLAN_TUNNEL_ID: "IFLA_BRIDGE_VLAN_TUNNEL_ID",
+        IFLA_BRIDGE_VLAN_TUNNEL_VID: "IFLA_BRIDGE_VLAN_TUNNEL_VID",
+        IFLA_BRIDGE_VLAN_TUNNEL_FLAGS: "IFLA_BRIDGE_VLAN_TUNNEL_FLAGS",
     }
 
+    # struct bridge_vlan_xstats {
+    #  __u64 rx_bytes;
+    #  __u64 rx_packets;
+    #  __u64 tx_bytes;
+    #  __u64 tx_packets;
+    #  __u16 vid;
+    #  __u16 flags;
+    #  __u32 pad2;
+    # };
+
     # filters for IFLA_EXT_MASK
     RTEXT_FILTER_VF                = 1 << 0
     RTEXT_FILTER_BRVLAN            = 1 << 1