Virtual LANs (VLANs)
A university deploys one physical switch fabric across a building. Yet the Computer Science department, the Registrar's office, and the guest Wi-Fi network must never see each other's broadcast traffic. Buying separate switches, separate cables, and separate patch panels for every department is expensive and inflexible. Adding one new office means running new wire. The problem is that a plain Link-Layer Switches and Bridging fabric treats every port as belonging to one giant broadcast domain. An ARP request or a DHCP broadcast from any host reaches every other host on every port. Virtual LANs solve this by letting a single physical switch, and even a single physical link between switches, carry multiple logically isolated Ethernet networks. A VLAN is a broadcast domain enforced in software rather than by physical wiring. Frames are tagged with a VLAN identifier as they cross switch-to-switch links. This lets one fiber trunk multiplex dozens of independent LANs. Switch ports can be reassigned to different departments by typing a configuration command instead of pulling cable. This matters because nearly every enterprise, data-center, and campus network in production today is carved into VLANs. Isolating broadcast domains bounds the blast radius of ARP storms. It keeps a compromised guest device from sniffing HR traffic. It lets network operators mirror organizational structure onto the network without matching it to building topology. It also gives routers a clean set of subnets to route between. Data centers use VLANs (and their descendants like VXLAN) to give thousands of tenants isolated virtual networks on shared switch hardware. This article explains precisely what a VLAN is. It shows how the IEEE 802.1Q tagging standard multiplexes VLANs over trunk links. It covers how switches learn and forward per-VLAN. It also shows how this interacts with the Address Resolution Protocol (ARP) and with inter-VLAN routing. We build the mechanism from first principles, and trace a worked example frame-by-frame across a tagged trunk. We also catalog the pitfalls that trip up real deployments.
- A VLAN is a logically separate broadcast domain built on top of shared physical switch hardware.
- 802.1Q inserts a 4-byte tag into an Ethernet frame carrying a 12-bit VLAN ID (4094 usable VLANs).
- Access ports belong to exactly one VLAN and carry untagged frames; trunk ports carry tagged frames for many VLANs.
- Switches maintain a separate MAC address table per VLAN, so forwarding and flooding never cross VLAN boundaries.
- Communication between VLANs requires a layer-3 device: a router-on-a-stick or a layer-3 switch with an SVI per VLAN.
- VLANs improve isolation and scalability but add configuration state that must be kept consistent across every switch in the fabric.
A VLAN is a broadcast domain defined by configuration, not by wiring, and 802.1Q tags let one physical link carry many such domains at once.
The idea
A broadcast domain is the set of hosts that receive a frame sent to the Ethernet broadcast address `FF:FF:FF:FF:FF:FF`. In an untagged switched LAN, the broadcast domain equals the entire physical Link-Layer Switches and Bridging fabric: every port on every interconnected switch. A Virtual LAN (VLAN) redefines the broadcast domain to be a configured subset of ports, independent of physical topology, identified by a numeric VLAN ID (VID), an integer from 1 to 4094 (0 and 4095 are reserved). Two hosts in the same VLAN behave, from a layer-2 perspective, as if they sat on their own isolated switch; two hosts in different VLANs cannot exchange an Ethernet frame at all without passing through a layer-3 device. Ports on a VLAN-capable switch are one of two types. An access port belongs to exactly one VLAN and is unaware that VLANs exist — it sends and receives ordinary untagged Ethernet frames, and the switch internally associates every frame on that port with the port's configured VLAN. A trunk port carries frames belonging to multiple VLANs over a single physical link, which requires each frame to be marked with its VLAN ID so the receiving switch knows which broadcast domain it belongs to. That marking is done by the IEEE 802.1Q standard, which inserts a 4-byte tag between the source MAC address and the EtherType/length field of the Ethernet header. The tag carries a 12-bit VLAN ID field plus a 3-bit priority field (used for Quality of Service-style traffic prioritization, formally 802.1p) and a 1-bit flag historically used for token ring compatibility. Within a switch, every forwarding decision — flooding an unknown-destination frame, looking up a known destination in the MAC table, and learning a source MAC from an incoming frame — is scoped per VLAN. Concretely, the switch does not maintain one MAC address table; it maintains one table per VLAN (or equivalently, one table keyed by (VLAN, MAC) pairs). This is what makes VLANs a true isolation mechanism rather than a labeling convenience: a switch will never forward a frame from a port in VLAN 10 out to a port in VLAN 20, and a broadcast in VLAN 10 is flooded only to VLAN 10 ports.
How it works
Step 1 — Port configuration. Each switch port is administratively set to access mode with a single VLAN, or to trunk mode carrying a set of allowed VLANs (typically all VLANs, or a restricted list). Trunk mode also designates one VLAN as the native VLAN, whose frames are sent untagged on that trunk for backward compatibility with non-802.1Q equipment (a frequent source of misconfiguration, discussed below).
Step 2 — Tag insertion (ingress at access port, or already tagged from a trunk). When an untagged frame arrives on an access port, the switch does not modify the frame on the wire, but internally binds it to the port's VLAN ID for the duration of its processing. When a frame must leave the switch on a trunk port, the switch inserts an 802.1Q tag carrying that VLAN ID (unless the VLAN is the trunk's native VLAN, in which case the frame is sent untagged).
Step 3 — Per-VLAN learning. On receiving a frame, the switch reads (VLAN ID, source MAC) and records the mapping to the ingress port in the MAC table for that VLAN specifically. This is standard transparent bridge learning, but scoped by VLAN.
Step 4 — Per-VLAN forwarding. The switch looks up (VLAN ID, destination MAC) in that VLAN's table:
- If found, forward out the recorded port only (unicast, no flooding).
- If not found (unknown unicast), flood out every port that belongs to that VLAN, except the ingress port.
- If the destination is broadcast or an unknown multicast, flood out every port belonging to that VLAN, except the ingress port.
Step 5 — Tag removal (egress at access port). When a tagged (or internally VLAN-bound) frame is sent out an access port, the switch strips the 802.1Q tag (or, if it was never tagged, simply sends it as-is) so the receiving end host sees ordinary untagged Ethernet.
Step 6 — Inter-VLAN communication. Because a switch never bridges across VLANs, two hosts in different VLANs need a router or layer-3 switch. Either a single router interface is subdivided into per-VLAN sub-interfaces over one trunk link (router-on-a-stick), or a layer-3 switch instantiates a switch virtual interface (SVI) per VLAN, each with its own IP subnet, and routes between them internally. ``` // Simplified per-frame switch forwarding logic on a VLAN-aware switch // Each switch maintains: mac_table[vlan_id] -> { mac_address -> port } // port_config[port] -> {mode: ACCESS/TRUNK, // vlan (if ACCESS), // allowed_vlans (if TRUNK), // native_vlan (if TRUNK)} function RECEIVE_FRAME(frame, in_port): cfg = port_config[in_port] if cfg.mode == ACCESS: vlan = cfg.vlan // implicit VLAN, frame arrives untagged payload = frame // no tag to strip else: // TRUNK if frame.has_8021q_tag: vlan = frame.tag.vlan_id payload = strip_tag(frame) else: vlan = cfg.native_vlan // untagged frame on trunk = native VLAN payload = frame // --- learning --- mac_table[vlan][payload.src_mac] = in_port // --- forwarding --- dst = payload.dst_mac if is_broadcast(dst) or is_unknown_multicast(dst): FLOOD(payload, vlan, exclude = in_port) else if mac_table[vlan].has(dst): out_port = mac_table[vlan][dst] SEND(payload, out_port, vlan) else: FLOOD(payload, vlan, exclude = in_port) // unknown unicast function FLOOD(payload, vlan, exclude): for port in all_ports: if port == exclude: continue cfg = port_config[port] if cfg.mode == ACCESS and cfg.vlan == vlan: transmit_untagged(payload, port) else if cfg.mode == TRUNK and vlan in cfg.allowed_vlans: if vlan == cfg.native_vlan: transmit_untagged(payload, port) else: transmit_tagged(payload, vlan, port) function SEND(payload, out_port, vlan): cfg = port_config[out_port] if cfg.mode == ACCESS: transmit_untagged(payload, out_port) else: // TRUNK if vlan == cfg.native_vlan: transmit_untagged(payload, out_port) else: transmit_tagged(payload, vlan, out_port) ``` The tag itself sits directly after the source MAC address: ``` Ethernet frame WITHOUT 802.1Q tag: [ Dest MAC(6) | Src MAC(6) | EtherType/Len(2) | Payload | FCS(4) ] Ethernet frame WITH 802.1Q tag: [ Dest MAC(6) | Src MAC(6) | TPID=0x8100(2) | TCI(2) | EtherType/Len(2) | Payload | FCS(4) ] \_______ 802.1Q tag (4 bytes) _______/ TCI = 3-bit priority (802.1p) | 1-bit CFI/DEI | 12-bit VLAN ID ```
Complexity & correctness
Space. Instead of one MAC table of size at most (total learned hosts), a VLAN-aware switch maintains tables, one per active VLAN, with combined size still since each learned (VLAN, MAC) pair is stored exactly once regardless of how many VLANs exist. Splitting by VLAN costs a small constant factor of bookkeeping overhead (a VLAN index alongside each MAC entry) but does not change the asymptotic space, which remains where is the number of distinct active hosts across all VLANs. The tag itself costs 4 bytes per frame on trunk links, negligible relative to typical frame sizes and irrelevant on access links where no tag is transmitted at all.
Time. Per-frame processing is amortized with a hash-table implementation of `mac_table[vlan][mac]`, exactly as in ordinary bridge learning, because the VLAN ID is just an extra key component (or equivalently, the tables are physically or logically partitioned so lookup remains a single hash computation over the concatenated key). Flooding costs where is the number of ports currently assigned to VLAN , strictly less than or equal to the flooding cost of an unpartitioned switch with total ports — VLANs never make flooding more expensive and typically make it cheaper by shrinking the flood domain.
Correctness of isolation. The key invariant maintained by the algorithm above is: > Invariant: for any two ports with disjoint VLAN membership, no frame received on is ever transmitted on . This holds because both branches of `FORWARD` — the direct unicast branch (`SEND`) and the flood branch (`FLOOD`) — only ever select an output port whose VLAN set intersects the frame's associated VLAN ID; the `RECEIVE_FRAME` procedure assigns exactly one VLAN ID to every frame at ingress (from port configuration if access, or from the tag/native VLAN if trunk), and every learned entry in `mac_table[vlan]` is written only under that same VLAN key. By induction on the sequence of frames processed, a port never appears as a value under a VLAN key different from its own configured (or currently tagged) VLAN, so lookups and floods restricted to that key can never select a port outside the VLAN. This is precisely what distinguishes VLANs from mechanisms like port-based mirroring or ACLs bolted onto a flat broadcast domain: isolation is structural, enforced by the data structure itself, not by a filter that could be bypassed.
Correctness of trunking (tag transparency). A trunk must deliver frames to the correct VLAN on the far switch even though physically only one wire exists. This works because the VLAN ID is encoded in the frame itself (the tag), so the invariant above composes across multiple switches: each switch independently applies the same per-VLAN forwarding rule, using the tag as the source of truth for VLAN membership instead of port configuration. The native VLAN's untagged special case is the one deliberate violation of "every frame explicitly carries its VLAN" and is exactly the source of the double-tagging vulnerability discussed below.
Tradeoffs. VLANs add configuration surface: every switch in the path must agree on the same VLAN ID for the same logical network (a global naming problem, historically eased by VLAN Trunking Protocol variants, themselves a source of outages when misused). VLANs also do not scale past 4094 IDs, a real limit motivating overlay techniques such as VXLAN in large multi-tenant data centers. Finally, VLANs give isolation, not security by encryption or authentication — a device with a trunk-capable NIC or a misconfigured port can potentially defeat isolation (see double-tagging below), so VLANs complement but do not replace access control and encryption.
Worked example
Consider the topology in the figure: Switch A and Switch B are connected by one 802.1Q trunk carrying VLAN 10 and VLAN 20, with a native VLAN configured as VLAN 99 (a convention many real deployments use — an unused "parking" VLAN as native, precisely to avoid the double-tagging pitfall). Host H1 (VLAN 10, on Switch A) sends an ARP request that eventually gets an answer from Host H3 (VLAN 10, on Switch B).
| Step | Event | VLAN context | What crosses the wire |
|---|---|---|---|
| 1 | H1 broadcasts ARP request for H3's IP | Access port on Switch A, VLAN 10 (implicit) | Untagged Ethernet frame, dst = broadcast |
| 2 | Switch A learns (VLAN10, H1_MAC) → port p1; looks up dst = broadcast | VLAN 10 flood domain only | Internally tagged with VLAN 10 for processing |
| 3 | Switch A floods to all VLAN-10 members: other VLAN-10 access ports and the trunk (since VLAN10 ≠ native VLAN 99) | VLAN 10 | 802.1Q-tagged frame, VID=10, sent on trunk |
| 4 | Switch B receives tagged frame on trunk, reads VID=10 | VLAN 10 | Learns (VLAN10, H1_MAC) → trunk port |
| 5 | Switch B floods within VLAN 10 only (strips tag for access ports) | VLAN 10 | Untagged frame delivered to H3; VLAN-20 ports on Switch B never see it |
| 6 | H3 replies unicast to H1's MAC | Access port on Switch B, VLAN 10 | Untagged frame |
| 7 | Switch B learns (VLAN10, H3_MAC) → its access port; looks up H1_MAC → found → trunk port | VLAN 10 | Tagged VID=10 frame sent on trunk |
| 8 | Switch A receives tagged frame, learns (VLAN10, H3_MAC) → trunk port, looks up H1_MAC → found → forwards to p1 only | VLAN 10 | Untagged frame delivered only to H1 |
Notice VLAN 20's hosts H2 and H4 never see any of these eight frames — the broadcast in step 1 was flooded only within VLAN 10's port set, both on Switch A and, after crossing the tagged trunk, on Switch B. If H2 (VLAN 20) later sends its own broadcast, it independently floods only within VLAN 20's ports, using the same trunk wire but tagged VID=20, and Switch A's and Switch B's VLAN 20 tables are populated completely independently of the VLAN 10 tables shown above — exactly the isolation invariant proved earlier, now traced concretely. If H1 (VLAN 10) instead wants to reach a host in VLAN 20, no amount of switch flooding helps: the frame must be handed to a router or an SVI configured with an IP address in VLAN 10's subnet and another in VLAN 20's subnet, which performs an ordinary IP routing decision and re-encapsulates the packet in a new Ethernet frame tagged (or untagged) for the destination VLAN — this is the router-on-a-stick / SVI mechanism from Step 6 of "How it works," and it is the only path across the VLAN boundary.
Common pitfalls & traps
| Pitfall | Why it happens | Consequence |
|---|---|---|
| Native VLAN mismatch between trunk ends | One switch's trunk configured with native VLAN 1, the other's with native VLAN 99 | Untagged frames land in the wrong VLAN silently; hard to diagnose since no error is raised |
| VLAN hopping via double tagging | Attacker sends a frame with two 802.1Q tags; the outer tag matches the (shared) native VLAN and gets stripped by the first switch, exposing the inner tag to the second switch | Frame escapes its intended VLAN, reaching a VLAN the attacker's port was never authorized for |
| Forgetting to prune trunk VLANs | Trunk configured to allow "all VLANs" by default | Broadcast storms and unnecessary flooding traverse links that don't need every VLAN, wasting bandwidth |
| Assuming VLAN = subnet automatically | VLAN is a layer-2 broadcast domain; IP subnet assignment is a separate, independent configuration step | Hosts in the same VLAN but different subnets can't ARP each other; easy to misconfigure DHCP scopes |
| Overlapping or inconsistent VLAN IDs across a large fabric | No centralized VLAN database, manual per-switch configuration drifts over time | Same VLAN ID used for two logically different networks on different switches, silently merging traffic that should be isolated |
| Spanning-tree per-VLAN assumptions ignored | Some spanning tree variants (e.g., per-VLAN spanning tree) compute a different tree per VLAN; assuming one shared tree covers all VLANs | Suboptimal or broken loop prevention on some VLANs while others work fine, since it isn't tested per-VLAN |
Pioneers & history
The VLAN concept grew directly out of the transparent bridging and spanning-tree work that made scalable Ethernet switching practical, and was standardized as the IEEE moved to make VLAN tagging vendor-neutral rather than locked to proprietary inter-switch link protocols.
Origins · 1 minRadia Perlman and how this was discovered
Radia Perlman designed the Spanning Tree Protocol in 1985 while at Digital Equipment Corporation, solving the problem of building loop-free forwarding topologies over arbitrarily meshed bridged Ethernet — the same substrate on which VLAN-aware switching and per-VLAN spanning trees were later built. Her broader body of work on network robustness, self-stabilizing algorithms, and secure routing has made her one of the most cited architects of the modern link-layer.
Where this matters
Enterprise campus networks use VLANs to separate departments, voice traffic (VoIP phones typically get their own VLAN via LLDP-MED/CDP auto-configuration), and guest Wi-Fi from the corporate network, all on shared switch hardware and shared trunk links between wiring closets. Data centers use VLANs to give each tenant or application tier (web, application, database) an isolated broadcast domain on shared top-of-rack switches, often combined with layer-3 SVIs on core switches for inter-tier routing. Service providers use VLAN tagging (sometimes stacked, "QinQ," two tags deep) to multiplex customer traffic over shared metro Ethernet links. Virtualization platforms expose VLAN tagging to virtual switches inside hypervisors, letting virtual machines on the same physical host belong to entirely different, isolated networks — the direct ancestor of software-defined overlay networking in cloud platforms. Any network engineer configuring a managed switch, any cloud architect designing tenant isolation, and any security team segmenting a network to contain breaches relies on the mechanisms described here.
Ask the tutor: How does the Spanning Tree Protocol interact with VLANs when a fabric runs one spanning tree instance per VLAN instead of one shared tree?
Is a VLAN the same thing as an IP subnet?
No, though they are almost always paired one-to-one in practice. A VLAN is a layer-2 construct: a broadcast domain defined by switch configuration and, on trunks, by 802.1Q tags. An IP subnet is a layer-3 construct: a range of IP addresses assigned by an administrator, independent of any switch's knowledge of VLANs. The common convention — one subnet per VLAN — exists because it keeps ARP and broadcast traffic naturally scoped: broadcasting a DHCP request or ARP query only reaches hosts that could plausibly be on that subnet. Nothing prevents multiple subnets from cohabiting one VLAN or a subnet from spanning several VLANs via routing, but both configurations are unusual and typically indicate either a legacy migration or a misconfiguration, since they defeat the natural alignment between broadcast scope and address scope that makes network design tractable.
Why can't a normal Ethernet switch just filter broadcasts by port group without VLAN tags?
It could, for hosts directly attached to that one switch, using port-based grouping without any tagging at all — this is exactly what "access port VLAN assignment" achieves on a single switch. The tag becomes necessary the moment two switches must cooperate over a shared trunk link: without a tag, a frame arriving on the trunk carries no information about which VLAN it belongs to, so the receiving switch cannot know whether to treat it as VLAN 10 or VLAN 20 traffic. The 802.1Q tag is the mechanism that lets VLAN membership travel with the frame across switch boundaries, rather than being purely a local, per-switch, per-port property that dies at the edge of a single switch's port table.
What happens if a host directly connects to a trunk port instead of an access port?
Most operating systems' network stacks do not understand 802.1Q tags by default and will either drop tagged frames as malformed or, if VLAN-aware drivers are configured, correctly parse them and bind specific sub-interfaces to specific VLAN IDs. Server virtualization hosts frequently do exactly this deliberately: the hypervisor's physical NIC is set to trunk mode, and each virtual machine's virtual NIC is bound to a specific VLAN ID entirely in software, letting one physical NIC serve many isolated virtual networks. Doing this accidentally with an ordinary end-user device, however, typically just breaks connectivity, since the device sends and expects untagged native-VLAN traffic and silently drops or ignores tagged frames for other VLANs.
Do VLANs provide security equivalent to physically separate networks?
Not fully. VLANs provide strong logical isolation of broadcast domains and, correctly configured, prevent one VLAN's hosts from directly exchanging layer-2 frames with another's. However, VLAN enforcement lives in switch configuration, which can be misconfigured (native VLAN mismatches), attacked (double-tagging VLAN hopping), or bypassed if an attacker gains control of a trunk-capable port. Physically separate networks remove an entire class of these software-configuration risks at the cost of far higher capital expense and inflexibility. In practice, VLANs are treated as a strong but not absolute isolation boundary, typically layered with access control lists, 802.1X port authentication, and encryption for genuinely sensitive traffic rather than relied upon as the sole security mechanism.
How many VLANs can a network actually have, and why is that number 4094?
The 802.1Q VLAN ID field is 12 bits wide, giving 4096 possible values, of which VID 0 is reserved to mean "no VLAN, priority tag only" and VID 4095 is reserved for implementation use, leaving 4094 usable VLAN IDs (1–4094). This is more than sufficient for a single campus or enterprise but becomes a real constraint for cloud providers hosting tens of thousands of mutually isolated tenants on shared infrastructure. That scarcity is precisely what motivated overlay technologies like VXLAN, which use a 24-bit identifier (roughly 16 million values) encapsulated over an IP/UDP header, letting isolated "virtual VLANs" span across IP-routed data-center fabrics rather than being confined to a single 802.1Q-tagged layer-2 trunk.
What is a "router-on-a-stick" and why is the name used?
It describes a router with a single physical link to a switch, configured with multiple logical sub-interfaces, each tagged for a different VLAN and each holding the default-gateway IP address for that VLAN's subnet. All inter-VLAN traffic physically travels down and back up that one link ("on a stick"), tagged appropriately for each direction, rather than the router having one dedicated physical port per VLAN. It was historically popular because router ports were expensive relative to switch ports, but it concentrates all inter-VLAN traffic onto one link's bandwidth, which is why modern designs more often use layer-3 switches with per-VLAN SVIs and hardware-accelerated routing internal to the switch chassis instead.
Can two switches disagree about what a given VLAN ID "means" and still function?
Yes, and this is a genuine operational hazard rather than a hypothetical edge case. Nothing in 802.1Q enforces a global namespace; VLAN ID 10 is just an integer that each switch's administrator chooses to associate with some logical network. If two switches in the same fabric are configured by different people at different times, VLAN ID 10 could silently mean "Finance" on one switch and "Guest Wi-Fi" on another, merging two networks that were never meant to share a broadcast domain the moment a trunk connects them. Centralized VLAN databases, naming conventions, and configuration-management discipline exist specifically to prevent this kind of silent, hard-to-detect misconfiguration in larger fabrics.