From: Vincent Bernat Date: Sat, 1 Feb 2020 16:17:48 +0000 (+0100) Subject: interfaces: correctly handle bridges with VLAN filtering enabled X-Git-Tag: 1.0.5~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=385e5e4cf3a74e1d56947418db49911640cfc4a9;p=thirdparty%2Flldpd.git interfaces: correctly handle bridges with VLAN filtering enabled With VLAN-aware bridges, bridges are usually configured to filter VLAN. In this case, we should not propagate VLAN from upper interfaces. Fix #377 --- diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c index 80324566..9f74eeee 100644 --- a/src/daemon/interfaces.c +++ b/src/daemon/interfaces.c @@ -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", diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 862ad08a..d5bef420 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -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) diff --git a/src/daemon/netlink.c b/src/daemon/netlink.c index d0164c8d..02cb5926 100644 --- a/src/daemon/netlink.c +++ b/src/daemon/netlink.c @@ -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); } diff --git a/tests/integration/fixtures/network.py b/tests/integration/fixtures/network.py index c4be4802..8796e360 100644 --- a/tests/integration/fixtures/network.py +++ b/tests/integration/fixtures/network.py @@ -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: diff --git a/tests/integration/test_interfaces.py b/tests/integration/test_interfaces.py index 1e34d08f..bb71458c 100644 --- a/tests/integration/test_interfaces.py +++ b/tests/integration/test_interfaces.py @@ -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'])