Skip to the content.

QoS troubleshooting has a major visibility problem.

Reading QoS Markings Off the Wire with dscpdump

Your firewall or SD-WAN policy says traffic should be marked AF41. Your application team says the WAN is dropping their video calls. The network team says QoS is configured correctly. Nobody is actually looking at what’s on the wire.

dscpdump fixes that. It’s a GNU awk script that wraps tcpdump and decodes DSCP, ECN, and legacy ToS markings in real time, one line per packet. No Wireshark, no capture files, no post-processing. Best of all: No Math For You!

Extra bonus: No Python interpreter-of-the-day and zero dependendencies. Who doesn’t have GNU awk these days? (ok, my Mac didn’t, but Homebrew hooked me up).

Sample output:

    DF       TCP      192.0.2.210 44948         10.27.0.192 22   [S]
  AF22       TCP         10.27.0.192 22      192.0.2.210 44948  [S.]
    DF       TCP      192.0.2.210 44948         10.27.0.192 22   [.]
  AF22       TCP         10.27.0.192 22      192.0.2.210 44948  [P.]
    DF       TCP      192.0.2.210 44948         10.27.0.192 22   [.]
    DF       TCP      192.0.2.210 44948         10.27.0.192 22  [P.]
    DF       TCP      192.0.2.210 44948         10.27.0.192 22  [F.]
  AF22       TCP         10.27.0.192 22      192.0.2.210 44948   [.]
  AF22       TCP         10.27.0.192 22      192.0.2.210 44948  [P.]
  AF22       TCP      192.0.2.210 44948         10.27.0.192 22   [R]
    DF       UDP      192.0.2.210 54389        192.0.2.162 161
   CS2       UDP        192.0.2.162 161      192.0.2.210 54389
    DF       UDP      192.0.2.210 54389        192.0.2.162 161
   CS2       UDP        192.0.2.162 161      192.0.2.210 54389
    DF      ICMP            192.0.2.210             10.27.0.42
   CS2      ICMP             10.27.0.42            192.0.2.210
    DF       UDP      192.0.2.210 33195         192.0.2.92 161
   CS2       UDP         192.0.2.92 161      192.0.2.210 33195
    DF       UDP      192.0.2.210 33195         192.0.2.92 161
   CS2       UDP         192.0.2.92 161      192.0.2.210 33195
    DF       TCP      192.0.2.214 54474        192.0.2.210 443   [S]
    DF       TCP        192.0.2.210 443      192.0.2.214 54474  [S.]
    DF       TCP      192.0.2.214 54474        192.0.2.210 443   [.]
    DF       TCP      192.0.2.214 54474        192.0.2.210 443  [P.]

How To Get It

The script is available in my network-tools repo.

git clone https://github.com/duanetoler/network-tools

Copy it to somewhere usable when you run sudo for tcpdump. Your home directory may or may not work, such as if your home directory is NFS-mounted.

sudo cp bin/dscpdump /usr/local/bin

Background

The IPv4 ToS byte has had two lives. Originally, it carried IP Precedence in the top 3 bits and four ToS flags (Delay, Throughput, Reliability, and a reserved bit) in the next four. RFC 2474 repurposed the top 6 bits as the Differentiated Services Code Point (DSCP), leaving the bottom 2 bits for ECN per RFC 3168.

The byte layout:

 7   6   5   4   3   2   1   0
[     DSCP (6 bits)    ][ ECN ]

DSCP values you’ll see in practice:

ECN values:

For the recently-initiated: ECN is Explicit Congestion Notification. This was a sort of adaptation of congestion notification already present it older WAN protocols such as ATM and Frame-Relay. ECN is congestion notification for TCP layer 4 and IP layer 3 protocols.

ECT is “ECN Capable Transport”, a device that “speaks” ECN.

How The Script Works

tcpdump with -v splits each IP packet across two lines — the IP header on the first, the payload summary on the second.

The script expects the string tos to be present in Field 3 of the output to identify header lines, extracts the ToS byte from Field 4, then calls getline to advance to the payload line for the address and protocol fields.

The ToS byte extraction is straightforward bitwise math:

ecn  = and(tos, 3);               # mask bottom 2 bits
dscp = rshift(and(tos, 252), 2);  # mask top 6, shift to 0-63
cs   = rshift(dscp, 3);           # class selector (top 3 of DSCP)
dp   = and(dscp, 3);              # drop precedence (bottom 2 of DSCP... wait

One subtlety: for AF codepoints, drop precedence lives in bits 1-2 of the DSCP field (the bottom 2 of the top 6), not the bottom 2 of the full byte. and(dscp, 3) after the right-shift gets you there cleanly.

The legacy ToS branch (dscp < 8) handles pre-RFC 2474 markings by inspecting the original flag bits directly in the raw ToS byte. These show up occasionally on older equipment or misconfigured hosts and are worth identifying rather than silently misclassifying.

“It’s just that easy!”

Usage

sudo tcpdump -lvnni eth0 ip | ./dscpdump

Filter to a specific host:

sudo tcpdump -lvnni eth0 ip and host 192.0.2.1 | ./dscpdump

Pass -v DEBUG=1 on the command line to see the raw decoded values alongside each packet. Here you can see how tos/dscp/cs interrelate. See? Two lives of ToS!

tos: 0; dscp: 0; cs: 0; dp: 0; ecn: 0
    DF      ICMP            192.0.2.210            192.0.2.178
tos: 64; dscp: 16; cs: 2; dp: 0; ecn: 0
   CS2      ICMP            192.0.2.178            192.0.2.210
tos: 0; dscp: 0; cs: 0; dp: 0; ecn: 0
    DF      ICMP            192.0.2.210            192.0.2.216
tos: 64; dscp: 16; cs: 2; dp: 0; ecn: 0
   CS2      ICMP            192.0.2.216            192.0.2.210

Requirements