Date created: 03/05/19 18:24:56. Last modified: 12/10/21 07:59:48

'tc' Notes

Add Delay for Specific Traffic with Filter

In the below example the root qdisc on the interface is replaced with a basic priority scheduler with the handle "1:". It has three queues, "1:1" is the high priority queue, by default no traffic enters this queue. "1:2" is the medium priority queue which all traffic falls into by default. "1:3" is the low priority queue which no traffic falls into by default. Below a netem delay queue with handle "30:" is added to the low priority queue on the root scheduler ("1:3"). A filter is then applied to the root schedule which matches specific traffic and places it into queue three ("1:3") of the root scheduler, which in turns feeds into the netem delay queue, creating a delay only for specific traffic that matches the filter.

Note that in this example, the netem delay is applied to the egress direction. Traffic is being matched and queue on egress.

# Replace the default queuing discipline with a basic priority scheduler
sudo tc qdisc add dev eth0 root handle 1: prio
# Attach a netem delay to queue 3
sudo tc qdisc add dev eth0 parent 1:3 handle 30: netem delay 1000ms
tc qdisc show dev eth0

# Example filter to match IPv6 traffic to any destination address
sudo tc filter add dev eth0 protocol ipv6 parent 1:0 prio 1 u32 match ip6 dst any flowid 1:3
# Example filter to match any ICMPv6 traffic
sudo tc filter add dev eth0 protocol ipv6 parent 1:0 prio 1 u32 match ip6 protocol 58 0xff flowid 1:3
# Example filter to match a single byte value of 0x3f exactly (0xff) at offset 7 from the start of the IPv6 header (the Hop Limit field in the IPv6 header)
sudo tc filter add dev eth0 protocol ipv6 parent 1:0 prio 1 u32 match u8 0x3f 0xff at 7 flowid 1:3
# Example filer IPv6 hop limit field as above, but using a 4 byte value, mask, and offset
sudo tc filter add dev eth0 protocol ipv6 parent 1:0 prio 1 u32 match u32 0x0000003f 0x000000ff at 4 flowid 1:3

tc filter show dev eth0

# See the number of packets/bytes falling into each queue
tc -s qdisc show dev eth0

# To remove the queues (which will be immediately replaced with the OS default queue) and all filters:
sudo tc qdisc del dev eth0 root
# To remove only the filters:
sudo tc filter del dev eth0

 

Prioritise By IP

An example of prioritising traffic to a specific IP, by placing it into the highest priority queue of a three priority scheduler, and placing all other traffic into the next highest priority queue. This is a strict priority schedule, as long as there are packets in queue 1, they will always be dequeued first:

# Show existing queue disc, this is Ubnutu 18, it uses fq_codel by default:
$ tc qdisc show dev wlp3s0
qdisc mq 0: root
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn

# Replace the default fq_codel queuing discipline with a basic priority scheduler,
# which has 3 queues by default. It's handle is 1:
$ sudo tc qdisc replace dev wlp3s0 root handle 1: prio

# Attach three basic FIFO queues/classs (called 10:, 20:, and 30:) to each of the root priority scheduler's three priority queues,
# 10: is attached to the root priority scheduler's priority 1 queue (the highest),
# 30: is attached to the root priority scheduler's priority 3 queue (the lowest):
$ sudo tc qdisc add dev wlp3s0 parent 1:1 handle 10: pfifo
$ sudo tc qdisc add dev wlp3s0 parent 1:2 handle 20: pfifo
$ sudo tc qdisc add dev wlp3s0 parent 1:3 handle 30: pfifo
$ sudo tc qdisc show dev wlp3s0

# Delete any existing filters,
# then create a new destination IP filter and map it to the 10: FIFO queue
# (which is in turn mapped the highest priority queue on root priority scheduler):
$ sudo tc filter del dev wlp3s0
$ sudo tc filter add dev wlp3s0 protocol ip parent 1:0 prio 1 u32 match ip dst 89.21.235.194/32 flowid 10:
# Map all other traffic the next highest priority FIFO queue:
$ sudo tc filter add dev wlp3s0 protocol ip parent 1:0 prio 2 u32 match ip dst 0.0.0.0/0 flowid 20:
$ sudo tc filter show dev wlp3s0

# To reset to default simply delete the priority schedule at the root of the interface, it will be replaced with the default fq_codel mechanism:
sudo tc qdisc del dev wlp3s0 root

 

Simulate Packet Loss

When simulating packet loss it is more effecting to apply the netem qdsic to a bridge interface. If it is applied to the same interface the application is bound to, when the qdisc drops a packet the higher layer protocols are notified which means that TCP for example, with retransmit strait away. Creating a bridge interface, binding the application to the bridge interface, adding the Ethernet interface to the bridge, and then finally placing the netem qdisc on the Ethernet interface is more effective.

It might also be helpful to disable the NetworkManager:

$ sudo /etc/init.d/network-manager stop

Ubuntu 18.04 using Linux 4.15.0-42-generic.

Remove the root queuing discipline if it isn't the default (fq_codel is default on this test machine):

$ tc qdisc show dev enp0s8
qdisc netem 8001: root refcnt 2 limit 1000 loss 0.1%

$ sudo tc -s qdisc del dev enp0s8 root

$ tc qdisc show dev enp0s8
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn

Add a new root qdisc using the netem qdisc and specific a packet loss percentage:

$ sudo tc qdisc add dev enp0s8 root handle 0: netem loss 0.01%

$ tc qdisc show dev enp0s8
qdisc netem 8002: root refcnt 2 limit 1000 loss 0.01%

$ tc -s qdisc show dev enp0s8
qdisc netem 8002: root refcnt 2 limit 1000 loss 0.01%
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0