Home / Alt manpages / tc-police(8)

  • tc-police(8)
  • Admin command
  • linux

Rate-Limit Ingress Traffic with tc police

You will attach a policing action to an ingress filter, cap matching traffic at a byte rate, inspect the installed rule, and remove it cleanly. The example uses the tc utility from iproute2 6.1.0 on Ubuntu and drops packets above 1 Mbit/s. Allow about 10 minutes, plus time to identify the correct interface.

Before you start

You need iproute2, a shell on the Linux host, and the name of the interface whose received traffic you want to police. Commands that change traffic control state need root privileges, so the examples use sudo. Reading the current configuration does not.

Ingress policing is a limit applied as packets arrive. It does not make the sender transmit more slowly. Dropping excess packets can affect downloads, connections and monitoring, so use a test interface or a maintenance window when the traffic matters.

Set an obvious placeholder before copying the commands. Do not leave YOUR_INTERFACE in a command.

$ ip -br link
$ DEV='YOUR_INTERFACE'
$ tc -V
tc utility, iproute2-6.1.0, libbpf 1.3.0

1. Check for an existing ingress qdisc

First inspect the interface. An ingress qdisc may already have filters attached, and replacing someone else's rule is an operational change you should understand before making it.

$ tc qdisc show dev "$DEV"
qdisc noqueue 0: root refcnt 2

The exact output varies. If you see an ingress qdisc already present, inspect its filters before continuing:

$ tc filter show dev "$DEV" ingress

Stop if the output contains rules you do not recognise. The cleanup command later removes the whole ingress qdisc, including filters belonging to other work.

2. Add the ingress hook

This creates the hook that can hold an ingress filter. It changes live packet handling, although the qdisc alone does not impose a rate limit.

$ sudo tc qdisc add dev "$DEV" handle ffff: ingress

Verify that the hook exists:

$ tc qdisc show dev "$DEV"
qdisc ingress ffff: parent ffff:fff1 ----------------

If the command reports that the qdisc already exists, return to step 1. Do not blindly delete it: an existing ingress qdisc may be shared by several filters.

3. Add a 1 Mbit/s drop policy

The filter below uses u32 with a zero mask, which matches every packet. Its police action permits up to 1 Mbit/s and a 100 kB burst. Packets that exceed the byte rate are reclassified by default, so specify conform-exceed drop/ok when the desired result is an immediate drop.

$ sudo tc filter add dev "$DEV" parent ffff: protocol ip prio 10 u32 \
    match u32 0 0 \
    action police rate 1mbit burst 100k \
    conform-exceed drop/ok

ok is the action for conforming packets. The slash separates the action for packets that exceed the limit from the action for packets that conform. If you omit this option, conforming packets are accepted and exceeding packets are reclassified, which is often not the result people expect from the word "drop".

4. Inspect and test the rule

Read the filter back before generating traffic. The output format is version-dependent, but it should show a police action with the configured rate, burst and counters.

$ tc filter show dev "$DEV" ingress
filter protocol ip pref 10 u32 chain 0
filter protocol ip pref 10 u32 chain 0 fh 800: ht divisor 1
 police rate 1Mbit burst 100Kb conform-exceed drop/ok
  action order 1: police ...

Do not treat the sample lines as byte-for-byte output. Look for the rule at priority 10 and a police action. Generate a known amount of traffic from a controlled peer, then run the same show command again and check whether the action counters increase. A counter that stays at zero usually means the filter does not match the traffic you expected, the traffic uses another protocol family, or it is arriving on another interface.

For IPv6 traffic, the example's protocol ip selector is not sufficient. Add a separate rule with the protocol selector appropriate to your test, and verify that syntax on the target iproute2 build before changing a production interface. The police action itself can measure bytes or packets, but each filter still decides what reaches it.

5. Choose a different exceed action

Dropping is only one choice. reclassify sends an exceeding packet back to filter processing as non-matching, continue moves on to the next action, pipe passes to the next action in the chain, and goto chain N jumps to another filter chain. These actions are useful when policing is one part of a larger policy, but they make the final outcome depend on the filters and actions that follow.

You can police packets rather than bytes. This is a separate token bucket and requires pkt_rate and pkt_burst, for example:

$ sudo tc filter add dev "$DEV" parent ffff: protocol ip prio 20 u32 \
    match u32 0 0 \
    action police pkt_rate 1000 pkt_burst 200 \
    conform-exceed drop/ok

At least one of rate and pkt_rate is required. A byte limit and a packet limit in separate filters can interact, so keep the test narrow and inspect both rules.

6. Remove the example safely

Removal is destructive to the ingress configuration. The command below removes the ingress qdisc and every filter attached to it, not just the example rule. Use it only when you have confirmed that the qdisc contains no other required policy.

$ tc filter show dev "$DEV" ingress
$ sudo tc qdisc del dev "$DEV" ingress
$ tc qdisc show dev "$DEV"

If you need to keep other ingress filters, remove the specific filter by its priority and protocol after checking the displayed rule, then leave the qdisc in place:

$ sudo tc filter del dev "$DEV" parent ffff: protocol ip prio 10 u32
$ tc filter show dev "$DEV" ingress

If deletion reports that the qdisc or filter is missing, inspect the current state first. A network manager or another administrator may have changed it since the last check.

Common traps

  • Wrong interface: ip -br link shows names, but a bridge, bond or virtual machine may mean the physical interface is not where packets are counted.
  • Wrong unit: 1mbit is a rate in bits per second. burst 100k is a size in bytes. A burst is not an additional sustained bandwidth allowance.
  • Unexpected pass-through: without an explicit exceed action, the documented default for exceeding packets is reclassification, not dropping.
  • Too-small MTU assumption: the optional mtu value defaults to unlimited. Set it only when you understand how packets larger than that value should be treated.
  • Configuration lost on reboot: these commands alter the live kernel configuration. Arrange persistence through the network configuration system used by the host, and test its generated commands separately.

Done means

  • The intended interface and existing ingress rules were checked first.
  • The filter shows the intended rate, burst and exceed action.
  • Controlled traffic increments the expected police counters.
  • You know whether the limit is temporary, and you have either removed the example or recorded the supported persistence method.