Switches, routers and provider-edge devices are often tested with traffic that already carries an 802.1Q tag, a Q-in-Q stack, or an MPLS label stack. A capture you already have can be stripped or rewritten (an earlier article covers that). Building the tagged or labelled frame yourself is a different job: you choose the TPID, the VLAN IDs, the MPLS labels and the payload, then write a pcap or send a short burst into an isolated lab.
This article compares Scapy with a GUI workflow in Cat KARAT Packet Builder. Send only on an authorized, isolated lab network. Prefer loopback, keep bursts finite, and never aim traffic at a production or third-party network.
What a useful tagged or labelled frame needs
- The tag or label is part of the frame, not a display filter. The EtherType (or TPID) on the wire has to be the one the DUT looks for: 0x8100 for a customer 802.1Q tag, 0x88a8 for 802.1ad, 0x8847 / 0x8848 for MPLS unicast / multicast.
- Inner and outer IDs are distinct. In Q-in-Q the outer (SP-VLAN) and inner (CE-VLAN) IDs, priorities and TPIDs can all differ. In MPLS, each label has its own value, traffic class, TTL and Bottom of Stack bit.
- The payload still has to parse. After the tags or labels comes IPv4, IPv6, or, for Ethernet over MPLS, an inner Ethernet frame. Checksums of the inner IP packet still need to be right — or wrong on purpose.
- You verify the result in a decoder, not only in the builder. Open the pcap and read both the outer encapsulation and the inner packet.
Building tags is the opposite of the capture-conversion article, which is about removing 802.1Q / Q-in-Q or unwrapping GTP-U from a file you already have.
Option 1: Scapy
Scapy's Dot1Q and Dot1AD layers (in scapy.layers.l2) take vlan and prio. Stacking Dot1AD then Dot1Q produces 802.1ad Q-in-Q; the Ethernet type becomes 0x88a8. MPLS lives in scapy.contrib.mpls: each MPLS object has label, cos, s (Bottom of Stack) and ttl. Set s=0 on every label except the last.
from scapy.all import Ether, IP, ICMP, Dot1Q, Dot1AD, wrpcap
from scapy.contrib.mpls import MPLS
single = (Ether(dst="00:11:22:33:44:55", src="00:aa:bb:cc:dd:ee")
/ Dot1Q(vlan=100, prio=5)
/ IP(src="192.0.2.1", dst="198.51.100.2")
/ ICMP(type="echo-request"))
qinq = (Ether(dst="00:11:22:33:44:55", src="00:aa:bb:cc:dd:ee")
/ Dot1AD(vlan=200, prio=0)
/ Dot1Q(vlan=300, prio=0)
/ IP(src="192.0.2.1", dst="198.51.100.2")
/ ICMP(type="echo-request"))
mpls = (Ether(dst="00:11:22:33:44:55", src="00:aa:bb:cc:dd:ee")
/ MPLS(label=100, cos=0, s=0, ttl=64)
/ MPLS(label=200, cos=0, s=1, ttl=64)
/ IP(src="192.0.2.1", dst="198.51.100.2")
/ ICMP(type="echo-request"))
wrpcap("encap.pcap", [single, qinq, mpls])
Good for: a scripted, reviewable stack in a Python test.
Limits: you own the order of tags, the S-bit on every label, and the inner checksums. A typo in s or a missing Dot1AD is a frame the DUT will not treat as the case you meant.
Option 2: Cat KARAT Packet Builder
Cat KARAT Packet Builder includes Vlan/QinQ, MPLS and EoMPLS in Packet Flow, as the User Guide lists among its main features. Checksums and lengths of the IP/TCP/UDP/ICMP payload are still computed automatically unless an Override is checked.
802.1Q and Q-in-Q
- In Packet Flow, tick Vlan/QinQ in the Encapsulation column, then choose IPv4 (or IPv6) and a Layer 4 protocol such as UDP or ICMP.
- Open the VLAN tab. The type list is Single Vlan or Stack Vlan (Q in Q).
- For a single tag, set VLAN ID (0–4095), User Priority (0–7), CFI, and Tag Protocol ID. ID modes are the same as for MAC addresses: Fixed, Incr, Decr or Rand. The same Tag Protocol ID list is offered for the outer and the inner tag:
0x8100,0x88a8,0x9100,0x9200and0x9300. Both default to 0x8100. For a standard customer tag, leave that default. - For Q-in-Q, choose Stack Vlan (Q in Q). The outer fields are labelled SP-VLAN (Outer) and the inner fields CE-VLAN (Inner). Each side has its own ID, priority, CFI and TPID. For 802.1ad, set the outer Tag Protocol ID to 0x88a8 and typically keep the inner TPID at 0x8100. If you leave both at the default
0x8100, you still get two tags, but they are not 802.1ad on the wire. - Auto packet size grows by four bytes per tag. Confirm the stack in Packet View, then Save Packet or send a short burst.
From a script, the same setup is a set of fields plus compile and a finite send. A single VLAN 100 with PCP 5 looks like "VLANS::VlanType": "Single Vlan", "VLANS::SVLANID": "100", "VLANS::SVLANPRI": "5", "VLANUSE": "1". Q-in-Q adds "VLANS::VlanType": "Stack Vlan (Q in Q)" with SVLANID (outer) and CVLANID (inner).
Karat --run vlan-qinq.karat.json --output qinq.pcap
Karat --run vlan-qinq.karat.json --safety-loopback
MPLS and EoMPLS
- In Packet Flow, select MPLS in the Layer 2.5 column (and IPv4 or IPv6 above it), or choose EoMPLS as the encapsulation when the payload should be an Ethernet frame inside MPLS.
- On the MPLS tab, pick MPLS Unicast or MPLS Multicast (EtherTypes 0x8847 and 0x8848). The current User Guide describes an eight-label stack: tick Use Label 1 through Use Label 8 for each label you need (labels 7 and 8 are included and encoded). Each slot has Label (0–1048575), Exper. Use (0–7), Bottom of Stack and Time to Live.
- Leave Automatically Set "Bottom of Stack" Bit checked unless you are testing a deliberately wrong S-bit; the last enabled label then gets S=1 and the others S=0.
- For EoMPLS, fill EXTERNAL DESTINATION MAC, EXTERNAL SOURCE MAC and PseudoWire Control Word (Hex) (eight hexadecimal digits).
A two-label unicast stack over IPv4/UDP, as in KARAT's golden example, enables labels 100 and 200 and leaves VLAN off.
PCAP validation in KARAT flags VLAN ID 4095 as a warning (reserved), along with runt or oversize frames. Use that report on the file you wrote before you send it to a DUT.
A practical lab workflow
- Decide the encapsulation the DUT expects: one 802.1Q tag, 802.1ad Q-in-Q, MPLS unicast, or Ethernet over MPLS.
- Build the frame in Packet Flow, or in Scapy if you already have a script. Use documentation addresses unless the lab requires real ones.
- Set TPIDs and S-bits explicitly. Do not assume "Q-in-Q" means 0x88a8, or that a second MPLS label has S=1, unless you set it.
- Write a pcap and open it. Check the Ethernet type, the VLAN IDs or label stack, and the inner IP packet.
- Send a short burst on loopback, then on a dedicated lab port if you are authorized.
--safety-loopbackand a finite Packets per Burst are the safe defaults.
If you already have a tagged capture and need to remove tags rather than add them, use a capture editor or tcprewrite --enet-vlan=del as described in the article on stripping VLAN tags.
Which tool when?
| Need | Good fit |
|---|---|
| Scripted 802.1Q / 802.1ad / MPLS in Python | Scapy Dot1Q, Dot1AD, scapy.contrib.mpls |
| Visual IDs, PCP, TPID and label fields, Packet View | KARAT Vlan/QinQ and MPLS / EoMPLS tabs |
| Repeatable pcap or loopback burst | KARAT --run + --output or --safety-loopback |
| Remove tags from an existing capture | Capture editor / tcprewrite (see the strip-VLAN article) |
| Inner IP checksum left wrong on purpose | KARAT Override on the IPv4/IPv6 or L4 tab, after the tags are set |
Editions
Cat KARAT is a free single-edition product for personal, educational and commercial use. The current release runs on Windows 10/11 64-bit. Live sending and capture need Npcap; writing a pcap with --output does not. A Linux build is available on request.
Explore Cat KARAT Packet Builder →
Limitations
- Q-in-Q with both TPIDs left at
0x8100is two 802.1Q tags, not 802.1ad. Set the outer Tag Protocol ID to0x88a8when that is what the DUT expects. - VLAN ID 4095 is reserved; KARAT's validation report warns about it.
- Structure changes (adding a VLAN, switching MPLS on) are applied on the next Start Transmit, not to packets already being sent.
- You remain responsible for every frame you transmit.
FAQ
Does a VLAN tag change the IPv4 checksum?
No. The tag sits between Ethernet and the EtherType of the payload. The IPv4/TCP/UDP/ICMP checksums are computed from those headers, and KARAT still fills them unless Override is on.
Can I increment the VLAN ID across a burst?
Yes. The VLAN tab has the same Fixed / Incr / Decr / Rand modes as MAC addresses, independently for the outer and, in Q-in-Q, the inner ID.
How do I keep the Bottom of Stack bit correct on a multi-label frame?
Leave Automatically Set "Bottom of Stack" Bit enabled, and enable only the labels you need (up to eight). Turn the option off only when a wrong S-bit is the test.
Is EoMPLS the same as IP over MPLS?
No. MPLS in the Layer 2.5 column sits in front of IPv4 or IPv6. EoMPLS encapsulates an Ethernet frame (external MACs and a pseudowire control word) in the MPLS stack.