VLAN filtering bridge(4): why and how ===================================== (last updated 2025-08-05, ivy@freebsd.org.) VLAN filtering bridge(4), available since FreeBSD 15.0, is a new way to handle vlans and bridges. the purpose of the feature is: * to make bridge(4) work more like a traditional switch or router; * to simplify complex setups involving multiple interfaces and vlans; * to enable future work such as: * standards-based RSTP (as opposed to PVST); * MSTP; * MVRP; * scalable VXLAN bridging (EVPN); * bridge hardware offloading (in concert with etherswitch(4)). how things work today --------------------- in a non-VLAN-filtering world, vlan(4) interfaces are always created on physical interfaces[0] and those vlan interfaces may be used for host traffic or placed into a bridge. bridging multiple vlans requires creating multiple vlan and bridge interfaces. [0] or pseudo-interfaces which behave like Ethernet, such as lagg or epair. for example, a simple configuration with two bridged vlans looks like this: ifconfig ix0.100 create ifconfig ix0.101 create ifconfig ix1.100 create ifconfig ix1.101 create ifconfig bridge100 create addm ix0.100 addm ix1.100 ifconfig bridge101 create addm ix0.101 addm ix1.101 now bridge100 carries traffic for vlan 100, and bridge101 carries traffic for vlan 101. if the host needs to communicate in either of these vlans, an IP address can be configured on those bridge interfaces. to add an untagged interface to vlan 101, we could do this: ifconfig bridge101 addm em0 since vlan(4) removes the incoming vlan tags, and the bridge only processes untagged frames, em0 will only see untagged frames. as far as it goes, this configuration works fine: people have been using this type of setup for years, it's relatively easy to understand, and it works. however, it can end up being quite verbose. for example, if we want to bridge 10 vlans between two interfaces, we need to do something like this: ifconfig ix0.1 create ifconfig ix0.2 create ifconfig ix0.3 create ifconfig ix0.4 create ifconfig ix0.5 create ifconfig ix0.6 create ifconfig ix0.7 create ifconfig ix0.8 create ifconfig ix0.9 create ifconfig ix0.10 create ifconfig ix1.1 create ifconfig ix1.2 create ifconfig ix1.3 create ifconfig ix1.4 create ifconfig ix1.5 create ifconfig ix1.6 create ifconfig ix1.7 create ifconfig ix1.8 create ifconfig ix1.9 create ifconfig ix1.10 create ifconfig bridge1 create addm ix0.1 addm ix1.1 ifconfig bridge2 create addm ix0.2 addm ix1.2 ifconfig bridge3 create addm ix0.3 addm ix1.3 ifconfig bridge4 create addm ix0.4 addm ix1.4 ifconfig bridge5 create addm ix0.5 addm ix1.5 ifconfig bridge6 create addm ix0.6 addm ix1.6 ifconfig bridge7 create addm ix0.7 addm ix1.7 ifconfig bridge8 create addm ix0.8 addm ix1.8 ifconfig bridge9 create addm ix0.9 addm ix1.9 ifconfig bridge10 create addm ix0.10 addm ix1.10 now imagine how much configuration would be required if we want to bridge 50 vlans across 5 interfaces. even if you script the configuration, the output of "netstat -i" or "ifconfig" would be rather excessive. inverting the bridge/vlan relationship -------------------------------------- the fundamental change with the vlan-filtering bridge is that the relationship between bridge and vlan is inverted: instead of interface->vlan->bridge, we put the bridge directly on top of the interface, then teach bridge to handle vlans itself: interface->bridge->vlan. as it happens, bridge(4) already understands vlans on a basic level: it does IVL[1] for its host table and can forward tagged frames between interfaces. vlan filtering adds the missing bits required to make this more generally useful, e.g. the ability to control which interfaces can communicate on which vlans, and to add or remove tags to support traffic between tagged and untagged interfaces. [1] Independent VLAN Learning, meaning that it learns Ethernet addresses separately for each vlan, so that the same address can be on a different port in different vlans. this relatively simple change allows a completely new approach to configuring bridging: we can create a single bridge, put all our interfaces into it, then configure the behaviour of those ports inside the bridge. essentially, we've turned the bridge into a vlan-aware switch. this makes certain configurations much easier. for example, the 10-vlan example from before can now be written like this: ifconfig bridge0 create vlanfilter ifconfig bridge0 addm ix0 tagged 1-10 ifconfig bridge0 addm ix1 tagged 1-10 if you want to bridge 100 vlans, simply change 1-10 into 1-100, and if we want to add an untagged interface on vlan 10: ifconfig bridge0 addm em0 untagged 10 if we want to make two more interfaces able to communicate only with themselves, simply put them on another vlan: ifconfig bridge0 addm em1 untagged 20 ifconfig bridge0 addm em2 untagged 20 and if the host needs to communicate on that vlan: ifconfig bridge0.20 create inet6 ... in fact, nearly every configuration[2] can be done using a single bridge and the appropriate filtering configuration, no matter how many VLANs are in use. instead of the number of interfaces scaling as nvlans*ninterfaces, it is simply 1+ninterfaces (plus 1 interface for each vlan the host has access to). [2] certain configurations aren't yet supported, most notably "tag mapping" where the same Ethernet segment has a different tag on two different interfaces. hopefully, this can be added in the future. what if you want to bridge untagged frames between ix0 and ix1, and also let the host communicate with vlan 10 on ix0? with the old style configuration, you might try this: ifconfig bridge0 create addm0 ix0 addm ix1 ifconfig ix0.10 create inet6 ... unfortunately, this doesn't actually work properly: because the bridge and the vlan interface both want to handle tagged frames, bridge uses a heuristic to shunt some frames to vlan. this heuristic is not very reliable, and causes some things to not work in surprising ways. this setup also doesn't work in the way most people expect. for example, what should the bridge do if it receives a frame on ix1 in vlan 10? since we didn't configure vlan 10 on ix1, we might expect it to simply drop the frame. what it will actually do is forward the frame to ix0 on vlan 10, but since we created the ix0.10 vlan interface, the return traffic may or may or may not arrive, depending on the bridge heuristic. instead, you might do something like this: ifconfig epair0 create ifconfig bridge0 create addm ix0 addm ix1 addm epair0a ifconfig epair0b.10 create inet6 ... this works fine and is completely supported, but it doesn't achieve our aim, because now ix1 can communicate on vlan 10 as well, and indeed on *any* vlan, since the bridge doesn't have any vlan filtering. with bridge vlan filtering, we can use this configuration: ifconfig bridge0 create vlanfilter ifconfig bridge0 addm ix0 untagged 1 tagged 10 ifconfig bridge0 addm ix1 untagged 1 ifconfig bridge0.10 create inet6 ... now we've expressed precisely what we want: ix0 and ix1 can communicate with untagged frames (on vlan 1), and the host can communicate with ix0 on vlan 10. configuring the vlan-filtering bridge ------------------------------------- vlan filtering is enabled by setting the vlanfilter flag on the bridge: ifconfig bridge0 create vlanfilter at this point, interfaces can be added to the bridge as normal, but they won't be able to send or receive any traffic since we haven't given them access to any vlans. we do this with the 'untagged' and 'tagged' options... but first, an aside: when converting to vlan filtering, there is one conceptual change which can cause confusion: with the old bridge, untagged traffic was placed in "VLAN 0", a fake VLAN which never exists on the wire. VLAN 0 is also where traffic on the bridge interface itself goes, e.g. when an IP address is assigned to the bridge. with vlan filtering: * all traffic has a VLAN, which is assigned when the traffic enters the bridge. there is no such thing as "untagged traffic", and IP addresses should not be assigned to the bridge interface itself, because they won't be able to reach anything. * instead of untagged traffic, you now have untagged *interfaces*, i.e., interfaces which send and receive traffic for a particular VLAN without using a tag on the wire. to demonstrate this with an example, consider a simple bridge setup: ifconfig bridge0 create ifconfig bridge0 addm ix0 addm ix1 ifconfig bridge0 inet6 2001:db8::1/64 untagged traffic from ix0 and ix1 is placed into "VLAN 0", and traffic from the host (using the IP address 2001:db8::1) is also in VLAN 0, meaning all the ports can communicate with each other and with the host. with vlan filtering, this is more or less the same, except we explicitly place the untagged traffic into vlan 1: ifconfig bridge0 create vlanfilter ifconfig bridge0 addm ix0 untagged 1 ifconfig bridge0 addm ix1 untagged 1 ifconfig bridge0.1 create inet6 2001:db8::1/64 "untagged ix0 1" says that untagged traffic received on ix0 should be assigned to VLAN 1, and because we want the host to communicate in VLAN 1 as well, we create the bridge0.1 interface to assign the IP address to. the behaviour is the same: both interfaces can communicate with each other and with the host, using untagged frames. VLAN 1 only exists on the host, and is not visible to the member interfaces. (note that "ifconfig bridge0.1 create" is exactly equivalent to "ifconfig vlan1 create vlan 1 vlandev bridge0", but using the bridgeX.Y name is generally more convenient and makes it more clear what's going on. there is absolutely no functional difference whichever way you prefer to do it.) to make this a little easier, you can use the "defuntagged" option, which sets the untagged option on every interface added to the bridge: ifconfig bridge0 create vlanfilter defuntagged 1 ifconfig bridge0 addm ix0 ifconfig bridge0 addm ix1 ifconfig bridge0.1 create inet6 2001:db8::1/64 this is also useful if you have other software (like a VM or jail manager) which wants to add interfaces to the bridge but isn't aware of vlan filtering. to put this another way (because sometimes people find the terminology confusing), the "untagged" option is describing the behaviour of the *interface*, not the traffic: the bridge traffic is never untagged, but the interface is untagged, because the frames on the wire do not have a tag. for tagged interface traffic, we use the "tagged" option instead. this says that if traffic on a particular interface is received with a (non-zero) tag, the traffic will be assigned to a vlan based on the tag. for example: ifconfig bridge0 addm ix0 tagged ix0 1,2,100-199 this means that ix0 will accept tagged frames where the tag is 1, 2, or anything between 100 and 199 (inclusive). if the tag has any other value, the frame will be dropped. the same vlan can be tagged on one interface and untagged on another: ifconfig bridge0 addm ix0 tagged 10 ifconfig bridge0 addm ix1 untagged 10 traffic received on ix0 with a vlan tag of 10 will be sent to ix1, but before the frame is forwarded, the bridge will strip the vlan tag, because on ix1 vlan 10 is untagged. similarly, untagged traffic received on ix1 will be assigned to vlan 10, and before it's sent to ix0, the bridge will add a tag, because on ix0, vlan 10 is tagged. when converting to vlan filtering, it might help to start by enumerating all the Ethernet segments currently on the host (e.g., each existing bridge) and assigning each one a numeric VLAN ID. when using untagged interfaces, you might choose to match the vlan id configured on the switch at the other end of the link, but that's not a requirement. future work ----------- now that we've taught bridge about vlans, what else can we do? * MSTP: since the bridge has a full understanding of what vlans exist and are reachable on which ports, we can now implement MSTP to be compatible with modern networks and avoid the problems that come up with RSTP in a VLAN network. * MVRP: as the bridge manages the vlan configuration itself, adding or removing vlans from an interface can be done automatically using MVRP. * VXLAN: you can already put a vxlan(4) interface in a bridge, so it's possible to bridge a vxlan with a vlan, e.g. to carry access traffic over a vxlan core. but this suffers from a similar problem to the old bridge: because a separate vxlan interface is required for every vlan, it quickly becomes difficult to scale. with the vlan-aware bridge, we could imagine this working in a different way: a single vxlan interface could be added to a bridge, and configured with a tagged access list to let the access vlan traffic reach the appropriate vxlan transparently. * hardware offloading: the vlan-filtering bridge configuration is fairly close to the type of configuration required to program a hardware switch chip, making it a simple matter of programming to pass this to etherswitch and have it program the hardware.