What it is
tc is the command-line utility for configuring the Linux kernel’s network traffic control (networking) subsystem, allowing you to shape, police, and prioritize network traffic.
Installation
tc is part of the iproute2 package, which is typically installed by default on most Linux distributions.
Linux:
sudo apt update && sudo apt install iproute2 # Debian/Ubuntu
sudo yum install iproute2 # CentOS/RHEL
sudo dnf install iproute2 # Fedora
Mac: tc is a Linux-specific tool and is not available on macOS. For macOS traffic shaping, you would typically use pfctl or third-party tools.
Windows: tc is a Linux-specific tool and is not available on Windows. For Windows, you might explore NetLimiter or similar third-party solutions.
Core Concepts
- Queuing Disciplines (qdiscs): Algorithms that manage how packets are queued and transmitted on a network interface. Examples include
fq_codel,htb,tbf. - Classes: Used by some qdiscs (like
htb) to categorize traffic and assign different priorities or bandwidth limits. - Filters: Rules that classify packets and direct them to specific qdiscs or classes based on criteria like IP address, port, or protocol.
- Network Device: The network interface (e.g.,
eth0,wlan0) to which the traffic control rules are applied. - Root Qdisc: The main queuing discipline attached to a network device. All traffic from the device passes through it.
- Child Qdiscs/Classes: Qdiscs or classes attached to other qdiscs or classes, forming a hierarchy.
Commands / Usage
Basic Interface Configuration
Show existing traffic control configuration on an interface:
sudo tc qdisc show dev eth0
Displays the queuing disciplines and their configuration for the eth0 interface.
Delete all traffic control configuration from an interface:
sudo tc qdisc del dev eth0 root
Removes all qdiscs and filters attached to the root of the eth0 interface.
Simple Rate Limiting (Token Bucket Filter - TBF)
Limit download speed on eth0 to 1 Mbps:
sudo tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 50ms
Adds a Token Bucket Filter (TBF) to the root of eth0, limiting outgoing traffic to 1 Megabit per second, with a burst capacity of 32 Kilobits, and a maximum latency of 50 milliseconds.
Limit upload speed on eth0 to 500 Kbps:
sudo tc qdisc add dev eth0 root tbf rate 500kbit burst 16kbit latency 20ms
Adds a TBF to the root of eth0, limiting outgoing traffic to 500 Kilobits per second, with a burst capacity of 16 Kilobits, and a maximum latency of 20 milliseconds.
Change the rate of an existing TBF:
sudo tc qdisc change dev eth0 root tbf rate 2mbit burst 64kbit latency 10ms
Modifies the existing root TBF on eth0 to a new rate of 2 Mbps, burst of 64 Kbps, and latency of 10 ms.
Delete an existing TBF:
sudo tc qdisc del dev eth0 root tbf
Removes the TBF qdisc from the root of eth0.
Fair Queuing (FQ-CoDel)
Apply FQ-CoDel to eth0 for fair queuing and improved latency:
sudo tc qdisc add dev eth0 root fq_codel
Adds the FQ-CoDel queuing discipline to the root of eth0. This automatically handles fair queuing and employs CoDel for buffer management to reduce latency.
Apply FQ-CoDel with specific CE threshold:
sudo tc qdisc add dev eth0 root fq_codel limit 10000 ce_threshold 100ms
Adds FQ-CoDel with a packet limit of 10000 and a CE (Congestion Experienced) threshold of 100ms.
Hierarchical Token Bucket (HTB) - For Bandwidth Allocation
Set up a root HTB qdisc with a total bandwidth of 10 Mbps:
sudo tc qdisc add dev eth0 root handle 1: htb default 12
Adds a root HTB qdisc with handle 1:. default 12 means traffic not matching any class will go to class 1:12.
Add a parent class for general traffic with 80% of the total bandwidth (8 Mbps):
sudo tc class add dev eth0 parent 1: classid 1:10 htb rate 8mbit ceil 8mbit
Creates a class 1:10 under the root 1: with a guaranteed rate of 8 Mbps and a ceiling of 8 Mbps.
Add a child class for SSH traffic with 2 Mbps guaranteed and 10 Mbps ceiling:
sudo tc class add dev eth0 parent 1:10 classid 1:11 htb rate 2mbit ceil 10mbit
Creates a class 1:11 under 1:10 with a guaranteed rate of 2 Mbps and a ceiling of 10 Mbps. This class has priority over other traffic within 1:10 up to its ceiling.
Add a default class for other traffic with 1 Mbps guaranteed and 8 Mbps ceiling:
sudo tc class add dev eth0 parent 1:10 classid 1:12 htb rate 1mbit ceil 8mbit
Creates the default class 1:12 under 1:10 with a guaranteed rate of 1 Mbps and a ceiling of 8 Mbps.
Filter SSH traffic (port 22) to class 1:11:
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip protocol 6 47 and ip dst :22 0xffff flowid 1:11
Adds a filter to the root HTB (1:) that matches TCP traffic destined for port 22 and directs it to class 1:11.
Filter all other traffic to the default class 1:12:
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:12
This is often implicitly handled by default 12 on the root qdisc, but can be explicitly added. It matches any IP destination and directs it to class 1:12.
Show HTB classes:
sudo tc class show dev eth0
Displays the configured classes within the HTB hierarchy.
Show HTB filters:
sudo tc filter show dev eth0
Displays the configured filters directing traffic to classes.
Rate Limiting Specific Traffic (using filters)
Limit UDP traffic on eth0 to 1 Mbps:
sudo tc qdisc add dev eth0 root handle 1: prio
sudo tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate 1mbit burst 32kbit latency 50ms
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip protocol 17 0xff flowid 10:
Creates a priority qdisc (prio) at the root, then a TBF qdisc (10:) for UDP traffic, and finally a filter to direct UDP (protocol 17) to that TBF.
Limit traffic to/from a specific IP address (e.g., 192.168.1.100) to 500 Kbps:
sudo tc qdisc add dev eth0 root handle 1: prio
sudo tc qdisc add dev eth0 parent 1:1 handle 20: tbf rate 500kbit burst 16kbit latency 20ms
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip src 192.168.1.100/32 flowid 20:
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip dst 192.168.1.100/32 flowid 20:
Creates a priority qdisc, a TBF (20:) for the specific IP, and two filters to match traffic originating from or destined to 192.168.1.100.
Shaping Outgoing Traffic (Egress)
Limit outgoing traffic on eth0 to 2 Mbps:
sudo tc qdisc add dev eth0 root tbf rate 2mbit burst 64kbit latency 50ms
This is the standard way to limit egress bandwidth. tc primarily controls egress traffic.
Shaping Incoming Traffic (Ingress)
Limit incoming traffic on eth0 to 5 Mbps:
sudo tc qdisc add dev eth0 ingress
sudo tc qdisc add dev eth0 parent ffff: police rate 5mbit burst 100kbit conform-action drop
Creates an ingress qdisc on eth0. Then, a police qdisc (often used for ingress) is attached to the virtual ffff: handle, limiting incoming traffic. conform-action drop means packets exceeding the limit are dropped.
Limit incoming traffic on eth0 to 5 Mbps, but pass conforming traffic:
sudo tc qdisc add dev eth0 ingress
sudo tc qdisc add dev eth0 parent ffff: police rate 5mbit burst 100kbit conform-action pass
Similar to the above, but conform-action pass ensures packets within the limit are allowed.
Controlling Latency (using Netem)
Introduce 100ms of additional latency to all traffic on eth0:
sudo tc qdisc add dev eth0 root netem delay 100ms
Adds the netem qdisc with a fixed delay of 100 milliseconds.
Introduce 50-150ms of jitter (random delay) to traffic on eth0:
sudo tc qdisc add dev eth0 root netem delay 100ms 50ms
Adds netem with a base delay of 100ms and a variation (jitter) of +/- 50ms.
Introduce 50ms delay and 10% packet loss:
sudo tc qdisc add dev eth0 root netem delay 50ms loss 10%
Adds netem with a 50ms delay and a 10% probability of packet loss.
Introduce 20ms delay and duplicate 1% of packets:
sudo tc qdisc add dev eth0 root netem delay 20ms duplicate 1%
Adds netem with a 20ms delay and a 1% probability of packet duplication.
Introduce 30ms delay and corrupt 5% of packets:
sudo tc qdisc add dev eth0 root netem delay 30ms corrupt 5%
Adds netem with a 30ms delay and a 5% probability of packet corruption.
Combine netem effects (delay, loss, duplicate):
sudo tc qdisc add dev eth0 root netem delay 10ms 5ms distribution normal loss 1% duplicate 0.5% corrupt 0.1%
Applies a complex netem configuration with specified delay, loss, duplication, and corruption probabilities.
Common Patterns
Limit download speed for a specific application (e.g., torrent client on port 6881-6999):
# Assuming eth0 is your internet-facing interface
sudo tc qdisc add dev eth0 root handle 1: prio
sudo tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate 100kbit burst 16kbit latency 50ms
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 6881:6999 0xffff flowid 10:
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip sport 6881:6999 0xffff flowid 10:
This sets up a priority qdisc, then a TBF for the torrent traffic, and filters both incoming and outgoing traffic on the specified ports to that TBF.
Prioritize SSH traffic (port 22) while limiting general web traffic:
# Total bandwidth 10 Mbps
sudo tc qdisc add dev eth0 root handle 1: htb default 12
# High priority class for SSH (guaranteed 2 Mbps, can use up to 10 Mbps)
sudo tc class add dev eth0 parent 1: classid 1:10 htb rate 2mbit ceil 10mbit
# Default class for other traffic (guaranteed 1 Mbps, can use up to 8 Mbps)
sudo tc class add dev eth0 parent 1: classid 1:12 htb rate 1mbit ceil 8mbit
# Filter SSH traffic to the high priority class
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip protocol 6 47 and ip dst :22 0xffff flowid 1:10
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip protocol 6 47 and ip src :22 0xffff flowid 1:10
This HTB setup gives SSH traffic preferential treatment and guarantees it a minimum bandwidth.
Limit total bandwidth to 5 Mbps for all egress traffic:
sudo tc qdisc add dev eth0 root tbf rate 5mbit burst 128kbit latency 50ms
A straightforward TBF configuration for overall egress shaping.
Simulate a slow mobile connection (e.g., 3G):
# Limit bandwidth to 1 Mbps down, 500 Kbps up
sudo tc qdisc add dev eth0 root handle 1: htb default 12
sudo tc class add dev eth0 parent 1: classid 1:10 htb rate 1mbit ceil 1mbit
sudo tc class add dev eth0 parent 1: classid 1:12 htb rate 500kbit ceil 500kbit
# Add latency and packet loss
sudo tc qdisc add dev eth0 parent 1:10 netem delay 150ms 50ms loss 2%
sudo tc qdisc add dev eth0 parent 1:12 netem delay 150ms 50ms loss 2%
# Filter traffic to appropriate classes (example: all traffic to class 1:10, then split further if needed)
# For simplicity, let's assume all traffic goes to 1:10 and then to 1:12 if not specific
# A more robust setup would involve filters based on IP/port
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dst 0.0.0.0/0 flowid 1:10
# If you wanted to separate ingress/egress more clearly, you'd use ingress qdisc for incoming
This example combines HTB for bandwidth allocation with Netem for latency and loss simulation.
Apply traffic control rules only to a specific IP address:
sudo tc qdisc add dev eth0 root handle 1: prio
sudo tc qdisc add dev eth0 parent 1:1 handle 20: tbf rate 1mbit burst 32kbit latency 50ms
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip src 192.168.1.50/32 flowid 20:
This setup targets traffic originating from 192.168.1.50 and applies a 1 Mbps TBF to it.
Gotchas
- Egress vs. Ingress:
tcprimarily controls egress (outgoing) traffic. Shaping ingress (incoming) traffic is more complex and often requires specific configurations like theingressqdisc or usingiptablesto mark packets fortcto handle. - Root Qdisc: You must have a root qdisc on the interface before you can add classes or filters. Use
tc qdisc add dev <iface> root ...as the first step. - Handles: Qdiscs and classes are identified by "handles" (e.g.,
1:,1:10). The first part (e.g.,1) identifies the qdisc, and the second part (e.g.,10) identifies the class within that qdisc.1:is the root. tcCommands are Additive: If you runtc qdisc add ...and a qdisc already exists, it might fail or create a duplicate depending on the qdisc type and options. Usetc qdisc replace ...ortc qdisc change ...to modify existing qdiscs, andtc qdisc del ...to remove them.handleUniqueness: When adding multiple qdiscs or classes, ensure their handles are unique within their parent.u32Filter Complexity: Theu32classifier is very powerful but can be tricky to get right. Ensure your bitmasks (0xffff) and offsets are correct for the fields you are matching.netemLimitations:netemsimulates network conditions but doesn’t perfectly replicate real-world network behavior. It’s best for testing and debugging.- Permissions: Most
tccommands require root privileges (sudo). - Persistence:
tcconfigurations are not persistent across reboots by default. You’ll need to use scripts or systemd services to reapply them. - Interface Names: Always double-check the network interface name (
dev eth0) you are targeting. Applying rules to the wrong interface can have unintended consequences. defaultKeyword: In HTB, thedefault <classid>option on a parent qdisc is crucial. It specifies which child class traffic goes to if no specific filter matches.ceilvs.ratein HTB:rateis the guaranteed bandwidth, whileceilis the maximum bandwidth the class can consume (often limited by the parent’sceilor the interface speed).