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
  frag= 0
  ttl= 64
  proto= ip
  chksum= None

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="", src="", tos=184)
>>> l3
<IP  tos=0xb8 src= dst= |>

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.

lalufu (guest)
August 1, 2011 at 6:23 a.m. UTC

You could also let python do the DSCP calculation:

l3=IP(dst="", src="", 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

Ron Arrendale (guest)
September 12, 2011 at 5:48 p.m. UTC

Hey Stretch,
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.....


Subunit (guest)
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!

Comments have closed for this article due to its age.