VXLAN Explained: An Overlay You Can Read on the Wire
This post is part of Protocol Lab, a free, hands-on series for learning networking protocols by building and breaking them in a container lab. All the lab material — topologies, configs, and scripts — lives in the repo: github.com/pathvector-studio/protocol-lab.
In Lab #16, WireGuard built an encrypted tunnel: the underlay showed only ciphertext. This lab builds a VXLAN overlay, which encapsulates the same way — inner packets inside outer UDP — but does not encrypt. So the underlay reveals both the VXLAN header and the inner frame in the clear.
VXLAN also differs in what it carries: it is a Layer-2 overlay (it tunnels Ethernet frames), the technology data centers use to stretch one virtual L2 network across many physical hosts.
Reading guide: rfc-notes/vxlan-overlay.md
Prerequisites: Lab 16: WireGuard, TCP Lab 07 (reading captures)
Expected time: 45–60 minutes.
The Goal
You will:
- build a point-to-point VXLAN overlay (VNI 100) between two nodes,
- ping across the overlay (10.200.0.0/24),
- capture the underlay and see
VXLAN, vni 100over UDP 4789 — with the innerICMP echovisible.
By the end, you should be able to compare Lab 16 and Lab 18:
| WireGuard (Lab 16) | VXLAN (Lab 18) | |
|---|---|---|
| Carries | any IP packet (L3) | Ethernet frames (L2) |
| Encrypted? | yes | no |
| Underlay shows | ciphertext only (UDP 51820) | VXLAN header + inner frame (UDP 4789) |
What You Will Learn
- What VXLAN is: an L2-over-L3 overlay, identified by a 24-bit VNI, carried in UDP 4789.
- The difference between an overlay address (10.200.0.x) and the underlay address (10.0.0.x).
- That encapsulation and encryption are separate: VXLAN encapsulates but does not encrypt.
- Why you can read the inner packet on the underlay here, but not in Lab 16.
- Where VXLAN is used (data-center network virtualization, EVPN).
This lab does not cover:
- Multicast VXLAN, VTEP learning, or EVPN control planes (we use a static point-to-point remote).
- VXLAN security (it usually rides on a trusted or separately encrypted underlay).
- Bridging real Ethernet segments; we just put IP on the VXLAN interface.
Where to Read in the RFCs
| Reference | What to focus on |
|---|---|
| RFC 7348 §4 | The VXLAN frame format (outer UDP + VXLAN header + inner Ethernet) |
| RFC 7348 §5 | The role of the VTEP (VXLAN Tunnel Endpoint) |
| RFC 7348 §3 | Segmentation by the 24-bit VNI, and UDP port 4789 |
| RFC 5737 §3 | Confirming the addresses used here are documentation-only |
The Big Picture
Two nodes. On top of the underlay (eth1, 10.0.0.0/24), we stretch a VXLAN overlay (vxlan0, 10.200.0.0/24).
overlay: 10.200.0.1 <== vxlan0 (VNI 100) ==> 10.200.0.2
| |
node-a --------------- eth1 (underlay 10.0.0.0/24) --------------- node-b
10.0.0.1 10.0.0.2
When you ping the overlay address 10.200.0.2, VXLAN wraps the inner frame in UDP 4789 and sends it to the peer across the underlay. Because VXLAN does not encrypt, peeking at the underlay shows both the VXLAN header and the inner ICMP.
flowchart LR
subgraph node-a
p1["ping 10.200.0.2<br/>(inner Ethernet/IP/ICMP)"] --> v1["vxlan0<br/>encapsulate"]
end
subgraph node-b
v2["vxlan0<br/>decapsulate"] --> p2["ICMP delivered"]
end
v1 -- "UDP 4789 · VNI 100<br/>(inner frame in the clear)" --> v2
note["on the underlay, both the VXLAN header<br/>and the inner frame are readable"]
Note: Everything here uses local/documentation address space (RFC 5737), so nothing in this lab touches the real internet.
What You Need
Recommended environment:
- Linux / WSL2 / a Linux VM — the host needs the vxlan kernel module. It's standard on recent Linux and is auto-loaded the first time a vxlan interface is created.
- Docker
- containerlab
Images used:
nicolaka/netshoot:latest— bundlesipandtcpdump(with VXLAN decoding).
No additional images are required. The VXLAN data path runs in the host kernel.
Running the Lab
The quick path, which deploys, verifies, and tears down for you:
./scripts/labctl.sh run vxlan-18
Or step through it manually:
1. Move into the working directory
cd protocol-lab/examples/vxlan-18
2. Deploy
sudo containerlab deploy -t vxlan-18.clab.yml
3. Bring up the VXLAN overlay
# node-a: peer (10.0.0.2) as remote, VNI 100, UDP 4789
docker exec clab-vxlan-18-node-a sh -c \
"ip link add vxlan0 type vxlan id 100 remote 10.0.0.2 dstport 4789 dev eth1; \
ip addr add 10.200.0.1/24 dev vxlan0; ip link set vxlan0 up"
# node-b: the mirror image
docker exec clab-vxlan-18-node-b sh -c \
"ip link add vxlan0 type vxlan id 100 remote 10.0.0.1 dstport 4789 dev eth1; \
ip addr add 10.200.0.2/24 dev vxlan0; ip link set vxlan0 up"
4. Ping across the overlay
docker exec clab-vxlan-18-node-a ping -c3 10.200.0.2
docker exec clab-vxlan-18-node-a ip -d link show vxlan0
5. Capture the underlay and confirm you can read the payload
docker exec -d clab-vxlan-18-node-a tcpdump -i eth1 -n -w /tmp/vx.pcap "udp port 4789"
docker exec clab-vxlan-18-node-a ping -c3 10.200.0.2
docker exec clab-vxlan-18-node-a pkill -INT tcpdump
docker exec clab-vxlan-18-node-a tcpdump -n -e -vv -r /tmp/vx.pcap
Right after the VXLAN, ... vni 100 line, the inner 10.200.0.1 > 10.200.0.2: ICMP echo request is visible as-is.
Expected Output
ping 10.200.0.2succeeds.ip -d link show vxlan0showsvxlan id 100 ... dstport 4789.- The underlay capture shows
VXLAN, ... vni 100(UDP 4789), and inside it, theICMP echoin plain text.
Why It Works
VXLAN (Virtual eXtensible LAN) is an overlay that wraps L2 Ethernet frames in L3 UDP packets. It exists to let one virtual LAN be shared across physically separate hosts.
- Encapsulation. The inner Ethernet frame (containing IP, containing ICMP) is wrapped in
UDP 4789 + a VXLAN header. The VXLAN header carries the VNI, a 24-bit identifier that says which overlay the frame belongs to — up to about 16 million distinct overlays. - VTEP. The endpoint that encapsulates and decapsulates is called a VTEP. In this lab, each node's
vxlan0is the VTEP; specifying the peer VTEP's underlay address withremotegives us a point-to-point setup. - No encryption. VXLAN's job is only "wrap and carry." It does not encrypt. That's why capturing the underlay shows both the VXLAN header and the inner frame in plain text — and tcpdump decodes VXLAN and shows you the ICMP inside.
- Contrast with Lab 16. WireGuard also "wraps in UDP and carries," but it encrypts the contents, so the underlay showed nothing but ciphertext. Encapsulation (wrapping) and encryption are separate functions. VXLAN does only the former; WireGuard does both. To run VXLAN safely, use it over a trusted underlay or encrypt the underlay separately, e.g. with IPsec.
The key insight: wrapping and encrypting are independent. An overlay is not automatically confidential.
Common Pitfalls
- Assuming the overlay is "encrypted." VXLAN does not encrypt. The contents are readable on the underlay.
- Mixing up overlay and underlay addresses. Underlay = 10.0.0.0/24 (
eth1); overlay = 10.200.0.0/24 (vxlan0). You ping the overlay. - Forgetting the VNI. Both ends must use the same VNI (here, 100) or nothing gets through.
- Forgetting it's an L2 overlay. VXLAN carries Ethernet frames. This lab puts IP directly on
vxlan0, but the real-world use is attaching it to a bridge to stretch an L2 segment. - Kernel module. The VXLAN data path is in the host kernel. Environments without it won't work (recent Linux ships it by default).
- MTU. The VXLAN header shrinks the overlay's effective MTU (by roughly 50 bytes). Watch out for fragmentation with large packets.
Cleanup
sudo containerlab destroy -t vxlan-18.clab.yml --cleanup
If you used labctl.sh run vxlan-18, the script runs destroy for you at the end.
Check Your Understanding
- What does VXLAN wrap in what (inner and outer)? Which port does it use?
- What is a VNI? How many bits, and what does it distinguish?
- In this lab, which addresses are the overlay and which are the underlay?
- Why is the inner ICMP visible when you capture the underlay? How is this different from Lab 16 (WireGuard)?
- How do encapsulation and encryption differ? Which does VXLAN do, and which does WireGuard do?
- If you want to use VXLAN securely, what should you combine it with?
References
- RFC 7348: Virtual eXtensible Local Area Network (VXLAN)
- RFC 5737: IPv4 Address Blocks Reserved for Documentation
- ip-link(8) manual page (vxlan)
Verified Run Log (2026-07-07)
This lab has been confirmed reproducible on real hardware.
Environment:
- Ubuntu 26.04 LTS (kernel 7.0.0-27-generic, x86_64). The vxlan kernel module auto-loaded when
vxlan0was first created. - Docker 29.1.3
- containerlab 0.77.0
- node-a / node-b:
nicolaka/netshoot:latest(tcpdump 4.99.6, with VXLAN decoding)
Running PATH="/tmp/pl-shim:$PATH" ./scripts/labctl.sh run vxlan-18 performed deploy → verify → destroy, and verification.json returned "status": "verified".
Ping across the overlay, and the VXLAN interface
$ docker exec clab-vxlan-18-node-a ping -c3 10.200.0.2
3 packets transmitted, 3 received, 0% packet loss
$ docker exec clab-vxlan-18-node-a ip -d link show vxlan0
... vxlan id 100 remote 10.0.0.2 dev eth1 srcport 0 0 dstport 4789 ...
The underlay shows both the VXLAN header and the inner frame
$ docker exec clab-vxlan-18-node-a tcpdump -n -e -vv -r underlay.pcap
10.0.0.1.46777 > 10.0.0.2.4789: [udp sum ok] VXLAN, flags [I] (0x08), vni 100
10.200.0.1 > 10.200.0.2: ICMP echo request, id ..., seq 1, length 64
10.0.0.2.33408 > 10.0.0.1.4789: [udp sum ok] VXLAN, flags [I] (0x08), vni 100
10.200.0.2 > 10.200.0.1: ICMP echo reply, id ..., seq 1, length 64
The outer layer is VXLAN ... vni 100 (UDP 4789), and inside it the ICMP echo request/reply is visible in plain text, as-is. VXLAN encapsulates but does not encrypt — the decisive difference from Lab 16's WireGuard, where the underlay showed ciphertext only. Wrapping and encrypting really are separate functions.
Cleanup
containerlab destroy -t vxlan-18.clab.yml --cleanup
That's VXLAN: an L2 overlay that wraps Ethernet frames in UDP and carries them anywhere the underlay reaches — with everything still readable on the wire, because encapsulation is not encryption.
Explore the full Protocol Lab series here: github.com/pathvector-studio/protocol-lab. If these labs are useful to you, please ⭐ star the repo on GitHub — it genuinely helps others find the project.
Next up, we'll keep pulling tunnels apart — looking at how overlays like this get a real control plane with EVPN, so VTEPs can find each other without static configuration.