]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: correctly handle bridges with VLAN filtering enabled
authorVincent Bernat <vincent@bernat.ch>
Sat, 1 Feb 2020 16:17:48 +0000 (17:17 +0100)
committerVincent Bernat <vincent@bernat.ch>
Sat, 1 Feb 2020 16:17:48 +0000 (17:17 +0100)
With VLAN-aware bridges, bridges are usually configured to filter
VLAN. In this case, we should not propagate VLAN from upper
interfaces.

Fix #377

src/daemon/interfaces.c
src/daemon/lldpd.h
src/daemon/netlink.c
tests/integration/fixtures/network.py
tests/integration/test_interfaces.py

index 80324566ab880199c1e303fbde39f1b4a5310516..9f74eeeeca275da595998693e921703bc2f2a4b3 100644 (file)
@@ -285,6 +285,13 @@ iface_append_vlan_to_lower(struct lldpd *cfg,
            "looking to apply VLAN %s to physical interface behind %s",
            vlan->name, upper->name);
 
+       /* Some bridges managed VLAN internally, skip them. */
+       if (upper->type & IFACE_BRIDGE_VLAN_T) {
+               log_debug("interfaces", "VLAN %s ignored for VLAN-aware bridge interface %s",
+                   vlan->name, upper->name);
+               return;
+       }
+
        /* Easy: check if we have a lower interface. */
        if (upper->lower) {
                log_debug("interfaces", "VLAN %s on lower interface %s",
index 862ad08a7fd9d1b1d9c9f613f3c10456bd7008bb..d5bef420bb25955a64d29370d1c70055dedc4a61 100644 (file)
@@ -298,11 +298,12 @@ void     interfaces_update(struct lldpd *);
 
 /* interfaces.c */
 /* An interface cannot be both physical and (bridge or bond or vlan) */
-#define IFACE_PHYSICAL_T (1 << 0) /* Physical interface */
-#define IFACE_BRIDGE_T   (1 << 1) /* Bridge interface */
-#define IFACE_BOND_T     (1 << 2) /* Bond interface */
-#define IFACE_VLAN_T     (1 << 3) /* VLAN interface */
-#define IFACE_WIRELESS_T (1 << 4) /* Wireless interface */
+#define IFACE_PHYSICAL_T    (1 << 0) /* Physical interface */
+#define IFACE_BRIDGE_T      (1 << 1) /* Bridge interface */
+#define IFACE_BOND_T        (1 << 2) /* Bond interface */
+#define IFACE_VLAN_T        (1 << 3) /* VLAN interface */
+#define IFACE_WIRELESS_T    (1 << 4) /* Wireless interface */
+#define IFACE_BRIDGE_VLAN_T (1 << 5) /* Bridge-aware VLAN interface */
 
 #define MAX_VLAN 4096
 #define VLAN_BITMAP_LEN (MAX_VLAN / 32)
index d0164c8d88be647ac56feb83de94302fad95d08e..02cb5926d76312bcf8cf3535b1831239ff4dc0fd 100644 (file)
@@ -292,6 +292,18 @@ netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int le
                }
        }
 
+       if (kind && !strcmp(kind, "bridge") && link_info_attrs[IFLA_INFO_DATA]) {
+               struct rtattr *bridge_link_info_data_attrs[IFLA_BR_MAX+1] = {};
+               netlink_parse_rtattr(bridge_link_info_data_attrs, IFLA_BR_MAX,
+                   RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
+                   RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
+
+               if (bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING] &&
+                   *(uint8_t *)RTA_DATA(bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING]) > 0) {
+                       iff->type |= IFACE_BRIDGE_VLAN_T;
+               }
+       }
+
        free(kind);
 }
 
index c4be48027ac44cf0dcb7d0cdd3dc1655864c155b..8796e36044580ea86c6c0bd4f78c0057f86954ae 100644 (file)
@@ -69,13 +69,14 @@ class LinksFactory(object):
 
             self.count += 2
 
-    def bridge(self, name, *ifaces):
+    def bridge(self, name, *ifaces, filtering=False):
         """Create a bridge."""
         ipr = pyroute2.IPRoute()
         # Create the bridge
         ipr.link('add',
                  ifname=name,
-                 kind='bridge')
+                 kind='bridge',
+                 br_vlan_filtering=filtering)
         idx = ipr.link_lookup(ifname=name)[0]
         # Attach interfaces
         for iface in ifaces:
index 1e34d08f3f8f37eb97ff5949308170b15e47f8ec..bb71458c2cd4c2e9c9c324a07dea48753a9b1a6a 100644 (file)
@@ -73,7 +73,7 @@ def test_vlan_aware_bridge_with_vlan(lldpd1, lldpd, lldpcli, namespaces, links,
     with namespaces(2):
         if when == 'after':
             lldpd()
-        links.bridge('br42', 'eth1', 'eth3')
+        links.bridge('br42', 'eth1', 'eth3', filtering=True)
         links.bridge_vlan('eth1', 100, pvid=True)
         links.bridge_vlan('eth1', 200)
         links.bridge_vlan('eth1', 300)
@@ -102,6 +102,39 @@ def test_vlan_aware_bridge_with_vlan(lldpd1, lldpd, lldpcli, namespaces, links,
             'no'
 
 
+@pytest.mark.skipif("'Dot1' not in config.lldpd.features",
+                    reason="Dot1 not supported")
+@pytest.mark.parametrize('filtering', [False, True])
+def test_vlan_aware_bridge_filtering(lldpd1, lldpd, lldpcli,
+                                     namespaces, links, filtering):
+    links(namespaces(3), namespaces(2))  # Another link to setup a bridge
+    with namespaces(2):
+        links.bridge('br42', 'eth1', 'eth3', filtering=filtering)
+        links.bridge_vlan('eth1', 100, pvid=True)
+        links.bridge_vlan('eth1', 200)
+        links.bridge_vlan('eth1', 300)
+        links.bridge_vlan('eth3', 400)
+        links.vlan('vlan400', 400, 'br42')
+        lldpd()
+    with namespaces(1):
+        out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
+        assert out['lldp.eth0.port.descr'] == 'eth1'
+        if filtering:
+            assert out['lldp.eth0.vlan'] == \
+                ['vlan100', 'vlan200', 'vlan300']
+            assert out['lldp.eth0.vlan.vlan-id'] == \
+                ['100', '200', '300']
+            assert out['lldp.eth0.vlan.pvid'] == \
+                ['yes', 'no', 'no']
+        else:
+            assert out['lldp.eth0.vlan'] == \
+                ['vlan100', 'vlan200', 'vlan300', 'vlan400']
+            assert out['lldp.eth0.vlan.vlan-id'] == \
+                ['100', '200', '300', '400']
+            assert out['lldp.eth0.vlan.pvid'] == \
+                ['yes', 'no', 'no', 'no']
+
+
 @pytest.mark.skipif("'Dot3' not in config.lldpd.features",
                     reason="Dot3 not supported")
 @pytest.mark.parametrize('when', ['before', 'after'])