Suppose your ISP is traffic shaping your connection when you exceed a certain upload quota, and some rogue user in your network is constantly uploading data.
To avoid getting hit by traffic management, you want to limit the amount of data the user is able to upload per day, and limit their upload speed significantly when the quota has been reached.
We can accomplish both with Linux traffic shaping (
This setup assumes and requires:
- OpenWRT Barrier Breaker 14.07.
- sqm-scripts enabled and configured.
- Your outgoing interface is
I’ve been playing with different traffic shaping scripts and none gave satisfactory results as SQM. One could write the
tc rules manually and spend a lot of time studying the documentation but the most recent algorithms, such as
hfsc, are notoriously complicated and underdocumented.
SQM gives us a modern QoS infrastructure for people that don’t have time to read papers and navigate through the source code. Please refer to the OpenWRT wiki page for SQM for installation instructions. This guide assumes the default SQM configuration is in place and running.
Configuring the bandwidth limiter
First, we need to modify the default sqm configuration (
/usr/lib/sqm/simple.qos) and add a new
tc class to limit upload speed to
our rogue user.
You’ll find the patch at https://gist.github.com/1player/fba5775fe0780af310a3
Also, make sure to create a help file for it, or the
luci module for SQM won’t be able to find it. Assuming you’ve called the new script
cp /usr/lib/sqm/simple.qos.help /usr/lib/sqm/simple-bwlimit.qos.help # edit as needed
This new QoS script creates a new class, with mark 4, with a maximum bandwidth of a 100th of the total upload bandwidth, as defined in the
$BL_RATE variable, which in my case resolves to 50kbps.
We need to make sure SQM is using this new configuration: open the SQM luci module, and in the
Queue Discipline tab select the new script, then load in the new rules.
To make sure the changes have been applied,
tc class show should list your new class.
tc -s class show dev eth1 | grep 1:14 # class htb 1:14 parent 1:1 leaf 140: prio 4 rate 50Kbit ceil 50Kbit burst 1600b cburst 1600b
Setting up the iptables rules and quota
The traffic shaping class is set up, but no MAC address is being filtered yet, nor we’re not doing any kind of quota management. We’ll be using iptables and specifically the
quota module to do that.
quota module isn’t loaded or installed by default, install it with:
opkg update opkg install iptables-mod-extra modprobe xt_quota
Now, we just need to set up the filtering logic:
iptables -t mangle -A FORWARD -o eth1 -m mac --mac-source ro:gu:e0:00:us:er -m quota --quota 157286400 -j RETURN iptables -t mangle -A FORWARD -o eth1 -m mac --mac-source ro:gu:e0:00:us:er -j MARK --set-mark 4
These rules, in the
mangle FORWARD chain, are processed when forwarding the packets from an interface to the other. With the
-o eth1 option we want to match the packets going out to the internet. Also, the rule are applied only to the MAC address
ro:gu:e0:00:us:er: you can specify an IP address instead of a MAC, but these rules won’t work anymore if the DHCP client decides to give the rogue user a different address.
The first line lets the outgoing packets pass unscathed if the quota, expressed in bytes (here 150 MB) has not been reached.
The second line will take effect when the quota is reached, marking the packets which will then be shaped by the
tc class we’ve defined previously. The bandwidth limited class from my QoS script has been given the mark number 4.
This is what your iptables configuration should now look like:
iptables -t mangle -vL FORWARD # Chain FORWARD (policy ACCEPT 40050 packets, 28M bytes) # pkts bytes target prot opt in out source destination # 67883 44M mssfix all -- any any anywhere anywhere # 23356 26M RETURN all -- any eth1 anywhere anywhere MAC ro:gu:e0:00:us:er quota: 157286400 bytes # 0 0 MARK all -- any eth1 anywhere anywhere MAC ro:gu:e0:00:us:er MARK set 0x4
We’re almost done, there’s one last problem: the quota, represented by the “bytes” column, doesn’t reset by itself. The easiest way to reset it is to add a new crontab rule:
0 0 * * * iptables -t mangle -Z FORWARD 2
iptables -t mangle -Z FORWARD 2 command will be executed every day at midnight, and reset the stats of the second rule, which in my case corresponds to the quota rule as listed from
iptables -t mangle -vL FORWARD
To test if it works, just set the mac-source to your PC’s MAC and try to upload some data somewhere: after reaching the limit you should see your speed decreasing significantly.