Traditional Linux software bridge implementations required creating separate bridge devices for every virtual LAN (for example, br-vlan10, br-vlan20) alongside intermediate sub-interfaces, leading to complex interface sprawl and high memory overhead in virtualized hypervisors and container hosts. The Linux kernel’s modern Bridge VLAN Filtering architecture solves this complexity by turning a single software bridge into an 802.1Q-aware switch capable of handling tagged, untagged, and native VLAN traffic across multiple trunk and access ports. While administrators often configure this imperatively using bridge vlan, Ubuntu Server provides powerful, declarative configuration through systemd-networkd. This article demonstrates how to configure declarative Bridge VLAN Filtering, define trunk and access ports, and manage VLAN filtering rules in Ubuntu using systemd-networkd.
Understanding Linux Bridge VLAN Filtering Architecture
When VLAN filtering is activated on a Linux bridge, the bridge driver inspects Ethernet frames passing through bridge ports and filters them according to an internal VLAN filtering database (FDB):
- Single Master Bridge: A single master bridge (e.g.
br0) handles all Layer 2 forwarding across dozens or hundreds of VLANs, eliminating the need to spin up multiple software bridges. - Port VLAN ID (PVID): Defines the default VLAN assigned to incoming untagged ingress frames on an access port.
- Egress Untagged: Specifies which VLAN tags must be stripped from frames as they exit the port, converting internal tagged packets into standard untagged frames for client workstations or virtual machines.
- Trunk Ports: Ports configured to accept multiple tagged 802.1Q frames without stripping headers, ideal for uplinks to physical managed switches or hypervisor virtual switches.
Configuring the Declarative VLAN Filtering Bridge (.netdev)
To create a VLAN-filtering bridge in systemd-networkd, define a .netdev file within /etc/systemd/network/. In this example, we define a bridge named br0 with hardware VLAN filtering enabled.
Create the file /etc/systemd/network/10-br0.netdev:
[NetDev]
Name=br0
Kind=bridge
[Bridge]
VLANFiltering=yes
DefaultPVID=1
The VLANFiltering=yes parameter activates 802.1Q frame inspection within the kernel bridge, while DefaultPVID=1 assigns untagged ingress packets to management VLAN 1 by default unless overridden by individual port definitions.
Configuring Bridge Port Interfaces (.network)
Next, configure the network definitions for the bridge master and member interfaces. First, configure /etc/systemd/network/15-br0.network to bring up the master bridge interface:
[Match]
Name=br0
[Network]
ConfigureWithoutCarrier=yes
Configuring a Trunk Uplink Port
Assume physical interface eth0 connects to an upstream managed switch carrying tagged traffic for VLANs 10, 20, and 30, with VLAN 1 as the untagged native/management network. Create /etc/systemd/network/20-eth0-trunk.network:
[Match]
Name=eth0
[Network]
Bridge=br0
[BridgeVLAN]
VLAN=1
PVID=1
EgressUntagged=1
[BridgeVLAN]
VLAN=10-30
The Bridge=br0 directive enslaves eth0 to the master bridge. The [BridgeVLAN] VLAN=10-30 block permits tagged frames for VLANs 10 through 30 to pass transparently through the trunk without tag stripping.
Configuring an Access Port
Now configure interface eth1 as an access port dedicated exclusively to VLAN 20 (for example, feeding an isolated internal department or virtualized workload). Create /etc/systemd/network/25-eth1-access.network:
[Match]
Name=eth1
[Network]
Bridge=br0
[BridgeVLAN]
VLAN=20
PVID=20
EgressUntagged=20
Under this configuration, untagged packets entering eth1 are automatically stamped with VLAN ID 20 (PVID=20). When packets destined for this port exit eth1, the bridge automatically strips the 802.1Q header (EgressUntagged=20), presenting standard untagged Ethernet frames to the connected device.
Applying Configuration and Verifying Bridge VLAN Rules
Set standard configuration file permissions and restart the systemd-networkd daemon to apply the declarative rules:
sudo chmod 644 /etc/systemd/network/*
sudo systemctl restart systemd-networkd
Verify that br0 is active and running using networkctl:
networkctl status br0
To inspect the active kernel Bridge VLAN filtering table across all member ports, execute the bridge vlan inspection utility:
bridge vlan show
The terminal outputs the operational VLAN assignments for each port:
port vlan ids
eth0 1 PVID Egress Untagged
10-30
eth1 20 PVID Egress Untagged
br0 1 PVID Egress Untagged
The output confirms that eth0 operates as a multi-VLAN trunk carrying VLANs 10–30 while eth1 enforces access isolation within VLAN 20.
Monitoring Forwarding Database (FDB) Tables
To observe active MAC address learning and verify that MAC addresses are compartmentalized by their respective VLAN IDs, inspect the bridge forwarding database:
bridge fdb show br br0
Every dynamic MAC entry is displayed alongside its associated VLAN ID (e.g. vlan 20), confirming that Layer 2 forwarding decisions remain completely isolated across distinct broadcast domains.
By implementing Bridge VLAN Filtering with systemd-networkd, Ubuntu Server administrators achieve robust, high-performance network segmentation that mirrors enterprise hardware switches while maintaining clean declarative configuration files.