DSCP & TOS

Note: I'm perfectly happy for this table and associated information to be used anywhere by anyone, that's why it's been published here, I couldn't find an easy reference, so I created one and published it for everyone, but, if you do republish the information, please attribute the source and don't try to pass it off as original work. Thanks.

You're trying to get QoS working smoothly on your network and you have a DSCP tag on your packets, but, you can only see ToS when capturing packets? How do you work out which ToS value equates to which DSCP value? Or... You are tagging using DSCP/PHB classes but only seeing DSCP hex or decimal tags on your packets? What does it all mean? 

The following table shows common decimal, hex and binary values for TOS, broken down into the meaning of the parts of that byte including DSCP values when interpretting that byte as DSCP.

TOS (Dec) TOS (Hex) TOS (Bin) TOS Precedence (Bin) TOS Precedence (Dec) TOS Precedence Name TOS Delay flag TOS Throughput flag TOS Reliability flag DSCP (Bin) DSCP (Hex) DSCP (Dec) DSCP/PHB Class
0 0x00 00000000 000 0 Routine 0 0 0 000000 0x00 0 none
4 0x04 00000100 000 0 Routine 0 0 1 000001 0x01 1 none
8 0x08 00001000 000 0 Routine 0 1 0 000010 0x02 2 none
12 0x0C 00001100 000 0 Routine 0 1 1 000011 0x03 3 none
16 0x10 00010000 000 0 Routine 1 0 0 000100 0x04 4 none
32 0x20 00100000 001 1 Priority 0 0 0 001000 0x08 8 cs1
40 0x28 00101000 001 1 Priority 0 1 0 001010 0x0A 10 af11
48 0x30 00110000 001 1 Priority 1 0 0 001100 0x0C 12 af12
56 0x38 00111000 001 1 Priority 1 1 0 001110 0x0E 14 af13
64 0x40 01000000 010 2 Immediate 0 0 0 010000 0x10 16 cs2
72 0x48 01001000 010 2 Immediate 0 1 0 010010 0x12 18 af21
80 0x50 01010000 010 2 Immediate 1 0 0 010100 0x14 20 af22
88 0x58 01011000 010 2 Immediate 1 1 0 010110 0x16 22 af23
96 0x60 01100000 011 3 Flash 0 0 0 011000 0x18 24 cs3
104 0x68 01101000 011 3 Flash 0 1 0 011010 0x1A 26 af31
112 0x70 01110000 011 3 Flash 1 0 0 011100 0x1C 28 af32
120 0x78 01111000 011 3 Flash 1 1 0 011110 0x1E 30 af33
128 0x80 10000000 100 4 FlashOverride 0 0 0 100000 0x20 32 cs4
136 0x88 10001000 100 4 FlashOverride 0 1 0 100010 0x22 34 af41
144 0x90 10010000 100 4 FlashOverride 1 0 0 100100 0x24 36 af42
152 0x98 10011000 100 4 FlashOverride 1 1 0 100110 0x26 38 af43
160 0xA0 10100000 101 5 Critical 0 0 0 101000 0x28 40 cs5
176 0xB0 10110000 101 5 Critical 1 0 0 101100 0x2C 44 voice-admit
184 0xB8 10111000 101 5 Critical 1 1 0 101110 0x2E 46 ef
192 0xC0 11000000 110 6 InterNetwork Control 0 0 0 110000 0x30 48 cs6
224 0xE0 11100000 111 7 Network Control 0 0 0 111000 0x38 56 cs7

So, there you have it, one byte in a packet header, two ways to look at it...

If dealing with TOS (Type of Service), the first 3 bits indicate the precedence, the 4th bit indicates the whether or not low delay is preferred, the 5th bit indicates whether or not high throughput is preferred, the 6th bit indicates whether or not high reliability is preferred and the 7th and 8th bits are reserved... More info can be found in RFC 791, written in 1981, which defines IP.

If dealing with DSCP (Differentiated Services (Diffserv) Codepoint) only the first 6 bits are used and the last 2 are ignored, these can be used for ECN (Explicit Congestion Notification) RFC 3168 ... More info can be found in RFC 2474, written in 1998, which defines the Differentiated Services Field (DS Field) which is what the TOS byte is referred to when talking about differentiated services and specifically DSCP. Also, RFC 2597 and RFC 3246 which define some of the PHB (Per-hop Behaviour) classes may be useful reading...

Update 2013-04-21: Added voice-admit as defined in RFC 5865 and listed at the IANA DSCP Registry. Added various TOS flag only options as used in certain software, e.g. openssh and old versions of asterisk.

Examples

tcpdump

I want to capture IPv4 packets using tcpdump that have had the DSCP class 'af21' set, but, tcpdump doesn't have a filter for DSCP and doesn't decode values to DSCP classes, what can I do?

	$ tcpdump -v -n -i ppp0 'ip and ip[1] & 0xfc == 72'

What this does run tcpdump with verbose output (-v), no name lookups (-n) on the interface ppp0 (-i ppp0), the filter, specified in quotes, says to only include packets that are ip (ip) and (and) where the second byte in the ip header (ip[1]) has a decimal value of 72 which we took from the table above as being the TOS decimal value equivalent to the DSCP class 'af21' (== 72) ignoring the last 2 bits in that byte as they may contain ECN flags (& 0xfc).

The result:

	17:50:20.207827 IP (tos 0x48, ttl 108, id 12190, offset 0, flags [none], proto UDP (17), length 116)
	    124.123.123.123.22238 > 180.123.123.123.51420: UDP, length 88

tcpdump shows packets that match our filter, it prefers to use a hex TOS value in it's display, so, showing 'tos 0x48'.

If we instead want to capture IPv6 traffic with the same class set, we'd do:

	$ tcpdump -v -n -i he-ipv6 'ip6 and (ip6[0:2] & 0xfc0) >> 4  == 72'

Here, it's a little more complex, with IPv6 the traffic class byte straddles the first and second bytes of the header, so, we look at the first two bytes of the header (ip6[0:2]), ignore the first 4 bits and the last 6 bits (& 0xfc0) then shift the value 4 bits to the right (>> 4) to remove the 4 right hand ignored bits that are outside the traffic class byte from the value and leave us with the value we want.

In both these examples, you can use TOS hex values instead of TOS decimal values, e.g. == 0x48. Alternatively, if you want to use DSCP hex or DSCP decimal values, you can shift the result, for the first example, this would give, as exact equivalents of those above:

	$ tcpdump -v -n -i ppp0 'ip and (ip[1] & 0xfc) >> 2 == 0x12'

and for the second example:

	$ tcpdump -v -n -i he-ipv6 'ip6 and (ip6[0:2] & 0xfc0) >> 4  == 0x12'

In both cases, using the DSCP hex value of 0x12 which as you can see from the table above is equivalent to the TOS decimal value 72.

Note the quotes around the filter string above, while you don't need quotes when specifying simple filters with tcpdump, without them in this case the shell would likely interpret '&' and '>>' executing a partial command as a background task and trying to execute the rest with output redirected, something that you likely don't want to do...

ping

Ping can be used to generate some outbound packets to test your QoS configuration or tcpdump filters. Ping has a '-Q' option to specify the value you want to set on your packets, for IPv4, this takes either a TOS hex or TOS decimal value, for IPv6, it only takes a TOS hex value. 

To generate packets that our tcpdump filters above would capture, for IPv4 these two are equivalent:

	$ ping -Q 72 8.8.8.8
	$ ping -Q 0x48 8.8.8.8

For IPv6, these two are equivalent:

	$ ping6 -Q 48 2404:6800:4003:801::1014
	$ ping6 -Q 0x48 2404:6800:4003:801::1014

As mentioned, ping6 only takes a hex value, while 48 in this case may appear to be decimal as we didn't explicitly specify it was hex with '0x', it is interpreted as hex.