(Vlan-aware) Bridges on Linux

Lately I’ve had some conversations about how Linux sucks at bridging tagged VLANs into VMs, which just isn’t true anymore.

With recent Kernels Linux bridges have become vlan-aware and now allow configuring any bridge port like a port of any decent network switch with respect to 802.1q VLANS. A port can present a VLAN as untagged traffic as well as a number of VLANs in tagged mode. As can be expected, SVIs can be configured as vlan interfaces of a bridge, too.

The old brctl utility has been integrated within the iproute suite as part of of ip link. The commands map as follows:

brctl addbr br0
ip link add br0 type bridge
    [ forward_delay FORWARD_DELAY ]
    [ vlan_filtering VLAN_FILTERING ]
    [ vlan_default_pvid VLAN_D_PVID ]
    [ nf_call_iptables NF_CALL_IPT ]
    [ nf_call_ip6tables NF_CALL_IP6TABLES ]
    [ nf_call_arptables NF_CALL_ARPTABLES ]

brctl addif br0 eth0
ip link set eth0 master br0

The full list of bridge related parameters of ip link contains all the glory:

$ ip link add br0 type bridge help
Usage: ... bridge [ forward_delay FORWARD_DELAY ]
 [ hello_time HELLO_TIME ]
 [ max_age MAX_AGE ]
 [ ageing_time AGEING_TIME ]
 [ stp_state STP_STATE ]
 [ priority PRIORITY ]
 [ group_fwd_mask MASK ]
 [ group_address ADDRESS ]
 [ vlan_filtering VLAN_FILTERING ]
 [ vlan_protocol VLAN_PROTOCOL ]
 [ vlan_default_pvid VLAN_DEFAULT_PVID ]
 [ mcast_snooping MULTICAST_SNOOPING ]
 [ mcast_router MULTICAST_ROUTER ]
 [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]
 [ mcast_querier MULTICAST_QUERIER ]
 [ mcast_hash_elasticity HASH_ELASTICITY ]
 [ mcast_hash_max HASH_MAX ]
 [ mcast_last_member_count LAST_MEMBER_COUNT ]
 [ mcast_startup_query_count STARTUP_QUERY_COUNT ]
 [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]
 [ mcast_membership_interval MEMBERSHIP_INTERVAL ]
 [ mcast_querier_interval QUERIER_INTERVAL ]
 [ mcast_query_interval QUERY_INTERVAL ]
 [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]
 [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]
 [ nf_call_iptables NF_CALL_IPTABLES ]
 [ nf_call_ip6tables NF_CALL_IP6TABLES ]
 [ nf_call_arptables NF_CALL_ARPTABLES ]

Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }

The vlan_filtering option of the iproute command maps to a not very well hidden magic switch in sysfs, which might be relevant for checking support for vlan-awareness:

echo 1 > /sys/class/net/br0/bridge/vlan_filtering

On Debian based systems I’d recommend using ifupdown2 for the configuration network interfaces. It’s nearly a drop-in replacement for classic ifupdown network configuration and is developed by the folks at CumulusNetworks, who among other things developed vlan-aware-ness for bridges. It’s no surprise that it comes with config options to configure vlan-aware bridges from within /etc/network/interfaces

auto br0
iface br0
    bridge-ports bond0
    bridge-vlan-aware yes
    bridge-vids 1013 4002

iface bond0
    bridge-vids 100 101 200 201 1013 2000 [..]

iface cr02_eth1
    bridge-vids 1013 2000 2004 2006 3002

iface br0.1013

Adding/removing ports to/from the bridge  manually could be done with the new bridge utility also shipped by the iproute suite:

bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]
                                              [ self ] [ master ]

bridge vlan show [ dev DEV ] [ vid VLAN_ID ]

Firewalling / netfilter

Previously there were global options to enable/disable netfilter interaction with Linux bridges (for iptables, ip6tables, arptables). These have been moved to per-bridge options as indicated by the highlighted nf_call_* parameters shown in the ip link help output above. This might come in handy when handling multiple bridges with different firewalling requirements.


Vlan-aware bridges come in really handy, when running Linux-based virtualization hosts, for example using KVM. With a simple hook for KVM/libvirt installed as /etc/libvirt/hooks/qemu which uses the interface names from the libvirt VM configuration to configure untagged VLANs on VM interfaces. Using the following interface configuration and the simple hook above, the interface will be configured as a member of br0 as an untagged port serving VLAN 1013:

<interface type='bridge'>
 <mac address='52:54:00:0b:10:13'/>
 <source bridge='br0'/>
 <target dev='ticket_v1013'/>
 <model type='virtio'/>

My talk on Contemporay Linux Networking from DENOG9 contains some more hands-on glimpses into current Linux networking features.

Leave a Reply