23

I am new to using tc and netem. I want to delay packets being sent to a specific IP address. However, the commands below cause all packets on the system to be delayed, instead of just to the IP address 1.2.3.4:

tc qdisc del dev eth0 root
tc qdisc add dev eth0 root handle 1: prio
tc qdisc add dev eth0 parent 1:1 handle 2: netem delay 500ms
tc filter add dev eth0 parent 1:0 protocol ip pref 55 handle ::55 u32 match ip dst 1.2.3.4 flowid 2:1

My guess is that I need some kind of catch-all filter at the end to specify that all remaining traffic should not go through netem. But I can't get anything to work. How would I get this to work?

Matt White
  • 707
  • 1
  • 5
  • 17

4 Answers4

21

The chosen answer is incorrect/incomplete. I faced a similar issue, the chosen answer gave some help, but not enough.

First, the following command is not really needed.

tc qdisc del dev eth0 root

It will 'delete' the root qdisc, but inmediately gets substituted by a pfifo_fast one (so you don't lose connectivity).

The second command:

tc qdisc add dev eth0 root handle 1: prio

Will substitute the pfifo_fast qdisc with the prio one. By default, the prio queue has 3 bands (0, 1, 2) each managed by one class (1:1, 1:2 and 1:3).

The packets will be sent to one of those bands using the TOS field of the IP package. This configuration is shown when you execute:

tc qdisc ls

looking at the 'priomap' values.

Then, you add a netem qdisc:

tc qdisc add dev eth0 parent 1:1 handle 2: netem delay 500ms

With this command you delay all traffic going to the 1:1 band (until the filter is in place).

But there are two caveats:

  • Your traffic can have a different TOS value and then being sent to another band.
  • The prio qdisc can be configured so the traffic goes to another band.

The following solved my issue to not be affected by the netem while the filter is not applied. Instead of the above steps, I did:

tc qdisc add dev eth0 root handle 1: prio priomap 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

This will send all traffic by default to the band 1:3.

Then, I added the rule to delay traffic:

tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 100ms 10ms

This creates the qdisc in the band 0, but since all traffic goes to band 3, it didn't affect me.

Afterwards, I added the filter:

tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 10.0.0.1/32 match ip dport 80 0xffff flowid 1:1

Now with the filter, only the chosen IP/port will be affected, since we redirect the chosen traffic to the band 0.

All the other traffic continues unaffected since it continues to flow to band 3.

Telegrapher
  • 311
  • 2
  • 9
  • what is the "ip dst 10.0.0.1/32" ? Is that the destination ip? does that mean there is a "ip src xxx.yyy.zz.www/aa" ? – Zach Folwick May 20 '17 at 03:36
  • 1
    Yes, it is the destination IP in my example. And yes, there is an 'ip src' option. – Telegrapher May 23 '17 at 09:35
  • The reason for the first command (tc qdisc del) is to clear out any previous state - like you might have if you are experimenting trying to make this work. FWIW the accepted answer Worked For Me. – Dan Pritts Aug 02 '17 at 02:09
  • Thanks, this answer was REALLY helpful. – PepeHands Oct 05 '19 at 14:18
  • I tried the command and got the following file exists error: `root@OpenWrt:~# tc qdisc add dev eth1.2 root handle 1: prio priomap 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2` `RTNETLINK answers: File exists` – user55570 Aug 23 '20 at 00:42
  • @user55570 See the final paragraph of this: https://stackoverflow.com/a/615757/2854555 . It basically means previous rules exist, and you need to delete or replace that rather than adding to that. – renyuneyun Sep 20 '22 at 10:09
15

Ok, I solved my own problem. It turns out that if you execute the first 3 lines above (the "tc qdisc" ones), it will delay all packets because there are no filters yet. The 4th line changes it to only delay packets from that single IP address. Additional filter lines can be added to add additional IP addresses to the "delayed" list. So: don't create a "netem delay" line without a filter pointing to it.

Matt White
  • 707
  • 1
  • 5
  • 17
  • Thankyou for coming back and posting the answer. Oddly I found that it worked just fine both ways, but anyway. I wrote a wrapper script around those three commands to help with testing, just thought i'd [give back a little](https://gist.github.com/arr2036/6598137) :) – Arran Cudbard-Bell Sep 17 '13 at 18:04
2

Simple example from https://wiki.linuxfoundation.org/networking/netem that lets you delay packets to a given IP without affecting any other traffic, even during configuration:

tc qdisc del dev eth0 root # Ensure you start from a clean slate
tc qdisc add dev eth0 root handle 1: prio
tc qdisc add dev eth0 parent 1:3 handle 30: netem delay 500ms
tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 \
   match ip dst 192.168.1.2 flowid 1:3
NeilenMarais
  • 343
  • 1
  • 2
  • 8
  • I must add a caveat, it later seemed like the delay applied more broadly that I expected and I could not get to the bottom of it. Not all traffic seemed delayed though. – NeilenMarais Jan 18 '19 at 08:18
1

I haven't manage to delay traffic to one IP while keeping traffic normal to others IP normal with the method described in this thread.

However, I manage to do it using the following commands.

tc qdisc add dev eth0 root handle 1: prio priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
tc qdisc add dev eth0 parent 1:2 handle 20: netem delay 0ms
tc filter add dev eth0 parent 1:0 protocol ip u32 match ip src `hostname -I` flowid 1:2
tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 15001ms
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dst 1.2.3.4 flowid 1:1

To delay 15001ms traffic to IP 1.2.3.4 from the host where the command is executed. The command hostname -I is used to get the main IP of the host but the value can be replaced directly inside the command.

I had to add another filter with 0ms delay to match the traffic coming from the host. For sure it is not elegant but I haven't managed to have something nicer working.

The last command can be replaced to match a single port.

tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dport 18583 0xffff flowid 1:1

To delay traffic to port 18583 instead of IP 1.2.3.4.


I have also found a second method on this answer to delay traffic to 1.2.3.4:18583 without impact on other traffic.

tc qdisc add dev eth0 root handle 1: prio
tc filter add dev eth0 protocol ip  parent 1: prio 1 u32 match ip dst 1.2.3.4 match ip dport 18583 0xffff flowid 1:1
tc filter add dev eth0 protocol all parent 1: prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:2
tc filter add dev eth0 protocol all parent 1: prio 2 u32 match ip protocol 1 0xff flowid 1:2
tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 10ms
tc qdisc add dev eth0 parent 1:2 handle 20: sfq