QoS Marking with Scapy
By stretch | Monday, August 1, 2011 at 2:52 a.m. UTC
Recently I need to test whether a switch was enforcing QoS marking policies on incoming frames. To simplify lab testing, rather than reconfigure a VoIP phone I decided to generate packets with various QoS markings with scapy. In this article, we'll see how to mimic the markings typical to VoIP phones and verify that the markings remain intact on the other end using Wireshark.
There are two QoS values we need to test: IEEE 802.1Q priority marking (class of service, or CoS) and the IP differentiated services control point (DSCP) field. Both of these have a default value of zero.
>>> Dot1Q().display() ###[ 802.1Q ]### prio= 0 id= 0 vlan= 1 type= 0x0 >>> IP().display() ###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= ip chksum= None src= 127.0.0.1 dst= 127.0.0.1 \options\
An Ethernet frame can only be marked at layer two if it has an 802.1Q header. Packets traveling out an access port or inside the untagged native VLAN on a trunk don't receive an 802.1Q header and thus have no priority field, and must rely on layer three QoS markings.
We'll begin building our packet with an Ethernet header with a specified destination MAC address and an 802.1Q header with a VLAN ID and priority (CoS) of five:
>>> l2=Ether(dst="00:23:7d:00:d0:a8")/Dot1Q(vlan=10, prio=5)
Setting the layer three DSCP field requires a bit more forethought. When we talk about DSCP values, we're usually referring to only the first six bits of an eight-bit field. This allows for a decimal value range of 0 through 63. For example, you'll likely recognize 46 as the expedited forwarding (EF) DSCP associated with voice and other real-time traffic. A complete list of DSCP values is shown on the QoS cheat sheet.
Scapy, however, requires us to provide an eight-bit value when setting the field in our packet. How do we convert our decimal DSCP value of 46 (EF) to an eight-bit value? Find 46 in binary, add two trailing zeros, and convert it back to decimal.
46 = 101110 10111000 = 184
Now we know to set our packet's DSCP field equal to 184 decimal. Scapy displays the DSCP field as a hexadecimal value.
>>> l3=IP(dst="192.168.0.1", src="192.168.0.2", tos=184) >>> l3 <IP tos=0xb8 src=192.168.0.2 dst=192.168.0.1 |>
Now we'll top off our packet with an empty UDP payload and see what it looks like glued together.
>>> l4=UDP() >>> l2/l3/l4 <Ether dst=00:23:7d:00:d0:a8 type=0x8100 |>>>
Looks good! Now we can send it out on the wire. Remember that since we're working at layer two, we need to use the sendp() function, not send(), to transmit our packet. Note that scapy requires root or administrator privileges to send raw packets.
>>> sendp(l2/l3/l4, iface="eth1") . Sent 1 packets.
We can use Wireshark to verify that our packet has been marked with the appropriate CoS and DSCP values at the receiving end.
One last note: If playing along at home, you may notice that Wireshark marks our packet as malformed. This is due to what it perceives as an erroneous (missing) DNS message in the UDP payload of the packet and can be safely ignored.
About the Author
Jeremy Stretch is a network engineer living in the Raleigh-Durham, North Carolina area. He is known for his blog and cheat sheets here at Packet Life. You can reach him by email or follow him on Twitter.
August 1, 2011 at 4:23 a.m. UTC
I thought you were going to do all your posts with IPv6 examples? Scapy has good IPv6 features, plus you can show Traffic Class (TC) vs DSCP.
Also, if one maybe doing this in an enterprise, they may want to use valid UDP packets as those tend to upset firewalls. Scapy has a DNSQuery() option i believe.
Anyway, good writeup again Stretch.
August 1, 2011 at 6:23 a.m. UTC
You could also let python do the DSCP calculation:
l3=IP(dst="192.168.0.1", src="192.168.0.2", tos=(46 << 2))
August 1, 2011 at 4:06 p.m. UTC
Nice writeup! I have to get playing with scapy. Any preference between the Windows and Linux versions?
August 4, 2011 at 3:53 p.m. UTC
How do we convert our decimal DSCP value of 46 (EF) to an eight-bit value? Find 46 in binary, add two trailing zeros, and convert it back to decimal.
Or you could just multiply the DSCP by 4
September 12, 2011 at 5:48 p.m. UTC
You might want to look at IxChariot. It's made by a company called Ixia. I've been using tools from them for years. Anyhoo, gotta go.....I'm glad you're still around....from one veteran to another.....
May 8, 2013 at 8:51 p.m. UTC
Same here, i didn't want to bother with connecting CoS capable devices.
It's great help when testing QoS on Cisco switches.
with Wireshark and proper vlan trunking config in linux i was able to test QoS policy over multi-hop trunk links. The tool is great for protocol fuzzing aswell.
Thanks for posting this!