How VLANs, Trunks, and Tagging Actually Work

The frame-by-frame explanation of what a VLAN tag actually is, why a trunk port is different from a normal one, and the mistake that breaks isolation even when your firewall config looks correct.

The problem this solves

A single switch port carries one Ethernet cable. Without VLANs, that means one logical network per cable -- if you want your laptop and your smart plug on separate networks but they're both plugged into the same switch, you need either two switches or some way to make one switch pretend to be several. VLANs are that mechanism: one physical switch, multiple logically separate networks, using a small piece of data added to each frame to say which network it belongs to.

The mental model: a tag is just a number stamped on the frame

Every Ethernet frame VLANs affect gets a 4-byte tag inserted into its header -- defined by the IEEE 802.1Q standard. That tag mostly holds one number: the VLAN ID (1-4094). A switch reads that number and uses it to decide which other ports the frame is allowed to reach.

  Normal Ethernet frame:
[ Dest MAC | Src MAC | EtherType | Payload | ... ]
802.1Q-tagged frame:
[ Dest MAC | Src MAC | 802.1Q tag (VLAN ID) | EtherType | Payload | ... ]
^
4 extra bytes -- this is the entire
mechanism. Nothing else about the frame
changes.

Access port: a port configured for exactly one VLAN, untagged. A normal device (a laptop, a printer) plugged into an access port has no idea VLANs exist -- the switch adds the tag internally on the way in and strips it on the way out. This is how most end devices connect.

Trunk port: a port configured to carry multiple VLANs' traffic, tagged, over one cable. This is how switches connect to each other, and how a router (like OPNsense) that needs to route between several VLANs connects to the switch -- one physical cable, multiple logical networks riding on it simultaneously, each frame carrying its own tag so both ends know which VLAN each frame belongs to.

Walking through one real example

Say VLAN 10 (Trusted) and VLAN 20 (IoT) both exist on a switch, and OPNsense is connected via a trunk port carrying both.

  1. A laptop on an access port assigned to VLAN 10 sends a frame. The switch receives it untagged, internally associates it with VLAN 10 (because that's what the port is configured for), and needs to forward it.
  2. The destination is on the other side of the trunk (say, it needs to reach OPNsense for routing). The switch tags the frame with VLAN ID 10 and sends it out the trunk port.
  3. OPNsense receives the tagged frame on its trunk-connected interface, reads the VLAN 10 tag, and knows this frame belongs to the VLAN 10 sub-interface specifically -- routing/firewall decisions happen per-VLAN-interface from here, exactly as if VLAN 10 were a physically separate NIC.
  4. If a reply needs to go back to that same laptop, the reverse happens: OPNsense sends it out tagged VLAN 10, the switch receives it on the trunk, strips the tag, and forwards it untagged out the laptop's access port -- the laptop never sees a tag at any point.

The tag exists only on the wire between switch and switch, or switch and router, on trunk links. End devices on access ports never see it.

Common misconceptions

  • "My devices are on separate Wi-Fi networks, so they're already isolated." Separate SSIDs don't imply separate VLANs unless the access point is explicitly configured to map each SSID to a different VLAN ID. Many consumer APs put every SSID on the same underlying network regardless of name.
  • "I configured the firewall rules, so isolation must be working." The single most common real-world failure: a switch port left on the default/native VLAN (often VLAN 1, untagged) that never actually reaches the VLAN-aware router interface the firewall rules apply to. The firewall config can be entirely correct and isolation still fails, because the traffic in question never passed through it in the first place -- it stayed on the native VLAN the whole time. This is exactly why the Build guide's Verify it works section checks the device's actual assigned subnet, not just the firewall rule review.
  • "VLAN IDs need to match some external numbering standard." They don't -- VLAN IDs are locally significant to your own network. 10, 20, 30 are common conventions because they're easy to remember, not because anything requires them.
  • "A trunk port is inherently less secure than an access port." A trunk port carrying VLANs you've deliberately configured is no less secure than any other link -- the risk isn't the trunk itself, it's a misconfigured access port left on the wrong VLAN, or a trunk carrying VLANs it shouldn't (see the previous point).

Check your understanding

  • Can you explain, without looking back, why an end device plugged into an access port never sees an 802.1Q tag?
  • If a device on VLAN 20 unexpectedly appears able to reach VLAN 10, what's the first thing to check on the switch, not the firewall?
  • Why does a router (like OPNsense) doing inter-VLAN routing need a trunk connection, not an access port?

Go further

Resources

Primary sources:

  • IEEE 802.1Q -- the actual standard defining VLAN tagging

Official documentation:

Related DaemonPress projects: