last updated: 2009-07-21
This is the home of my linux router shaper script which allows something like fair bandwidth sharing among clients in the local network. The script is not great or anything - please don't expect the holy grail here - I just thought I'd publish it because many people helped me write it and maybe someone has some use for it. I bet there are still lots of things that can be improved. Sorry about the crappy design of this page, I don't have time to put more effort in better looks.
Here's a very basic ASCII-art which shows my network situation:
+-----+
+--------+ | S |
| User A |---+ W |
+--------+ | I |
+--------+ | T | +--------+ +----------+
| User B |---+ C +-----| Router |--------| Internet |
+--------+ | H | +--------+ +----------+
.... ... / ...
+--------+ | H |
| User N |---+ U |
+--------+ | B |
+-----+
In words, it's a common setup. You have one internet connection, one router and several people in your LAN (family, roommates, neighbours, ...) who all want to use the internet.
You have a certain number of Clients (User A - User N) in your LAN which are connected by a Switch (or a Hub or BNC) to the Linux Router which is supposed to act as a gateway to the internet. The trouble now is, User B has a lot of downloads running and User C uploads stuff day and night, which leaves User A who only wants to use an interactive SSH shell in the rain, since B and C already use up all bandwidth the internet connection offers.
What we need to do is to share available bandwidth fairly among clients. In order to achieve this, I first tried several searches at Google and Freshmeat. This turned up quite a lot of results, like the Linux Advanced Routing & Traffic Control HOWTO which is a must-read and also contains great scripts, like the Wondershaper for single users. Another great general purpose script I found was HTB.init, which doesn't do anything by default, but gives you an easy way to setup HTB queues. In case you prefer CBQ, there's a CBQ.init too. If you don't know what I'm talking about, read the HOWTO above or continue reading here.
Since I never found a script that did exactly what I wanted, I decided to write my own. It's designed to be an all-I-need script, therefore it does not just setup Traffic Shaping, but Masquerading and Port Forwarding too. In short, it does everything that has to do with IPTables and Traffic Control. I use HTB (Hierarchical Token Bucket) to share bandwidth among clients (one class per client). On top of that I added a PRIO queue to prioritize interactive traffic on a per-user basis. On top of PRIO I set SFQ to treat connections fairly. In version 0.72, experimental support for IPP2P to recognize peer-to-peer traffic was added.
This is the simplified class setup for per user Fair NAT uses per default:
HTB class (for bandwidth sharing)
|
\-- PRIO (for prioritizing interactive traffic)
|
\--- Interactive: SFQ (to treat concurrent connections fairly)
\--- Normal: SFQ
\--- High-Traffic: SFQ
[ \--- P2P: SFQ (if IPP2P support is enabled only) ]
I bet this can still be improved and I'm always interested in ways to do so. In case you want another class structure, this can be done by replacing the parent_class and user_class functions in the script. See CLASS_MODE in Configuration section and the function documentation in the script for details. Feel free to send me your own functions with a short explanation, if you want me to make them available for everybody.
Here's a "real" graphic, which shows the complete qdisc/class structure on $DEV_LAN if you use the unmodified example configuration file. This graphic was created using a hacked version of Stef Coene's show.pl script and GraphViz. Click here to see it, but I warn you: it's quite big. Here's a similar picture, which includes IPP2P support. Note that there are more filter rules (the blue arrows) now which put the filesharing traffic into the user's prio band 4.
Without traffic shaping, users with low-traffic, interactive connections experience ping times between 2-5 seconds, when other users have up- and downloads running. This is of course deadly for SSH connections. You can't work on remote machines like that. With my script, I get much lower pings, at about 100-200ms. Compared to the 2000-5000ms before, this is a huge improvement. However, considering that the ping on a free line would be at around 50ms, the connection still feels laggy. It's nearly impossible to make perfect interactive connections if the line is maxed out in both directions.
For this script, you need iptables, tc and a QoS-enabled kernel. All these binaries must support HTB (usually the case unless you got a really old installation, in which case you ought to update anyway). I also use several kernel patches, none of which are actually required (unless if you want P2P shaping and some other Hacks). See README.patches in the tarball.
Wheee, configuration. At first, my script didn't even have a configuration file. Now that it has one, a whole load of options were added to it. An example configuration file with lots of comments is included in the package. The stuff you most likely will have to change is the devices, the rates and the user settings. Otherwise the script won't work. The other stuff is mainly for people who know what they're doing.
Your internet upload connection speed in kbit/s. Since ISPs tend to overestimate their rates, you should use a realistic value here (measure it on a completely free line).
You can also specify a unit for a rate. Supported units are kbps, mbps, mbit, kbit, and bps (same as 'tc'). If no unit is specified, kbit will be used per default.
Your internet download connection speed in kbit/s. Just like RATE_UP, you should use a realistic value here.
You can also specify a unit for a rate. Supported units are kbps, mbps, mbit, kbit, and bps (same as 'tc'). If no unit is specified, kbit will be used per default.
Specify who to do NAT for (who is allowed to use this machine as a gateway to the internet). Users are specified per IP and must be in the same subnet as your LAN device. Therefore only the last number of the client's machine is needed. For example, if your gateway is 192.168.100.42, the number 183 would expand to 192.168.100.183. It is also possible to group IPs (for users with more than one machine on the LAN) using ':'. So for example "3 4 7:9:12 42 128" stands for 5 users, one of which has 3 IPs.
You can also specify a custom ceil download / upload rate per user. For example 17@300 limits IP 192.168.100.17 to 300kbit/s download rate. 17@300|50 adds a 50kbit/s upload limit, and 17@|50 limits upload only.
Currently, the following arguments are understood:
Let me add some notes about the IPP2P support introduced in Fair NAT v0.72. IPP2P provides a (more or less) reliable means to detect traffic of peer-to-peer (filesharing) applications. To use it, you first have to patch the kernel and the iptables program. You can get the patches at the Official IPP2P Homepage along with plenty documentation. When that's done, you have to enable IPP2P in the Fair NAT configuration file, too. See the example configuration file for details.
Please note that IPP2P also requires the CONNMARK patch, unless you intend to drop all P2P packets in general. Applying this patch can be a pain in the ..., it took me several days to do it properly. If you use the newest version of the CONNMARK patch, you also need a new version of iptables - at least version 1.2.10 (at the moment, you need iptables-cvs). Otherwise CONNMARK won't work - you'll get an 'Invalid Argument' error. Another solution would be using an OLD version of the CONNMARK patch, but I haven't found any that worked together with kernel 2.4.26 so far. The patch is part of patch-o-matic-ng which you can get on the Official Netfilter Homepage .
If IPP2P is enabled, Fair NAT uses it like this. If P2P traffic is forbidden in general, all packets recognized by IPP2P are dropped. End of story. Bye bye. However, if P2P traffic is allowed, Fair NAT does the following:
With Fair NAT 0.80, I've started taking the first steps towards Firewall support (which is one of the most requested features). In order not to collide with Firewall iptables rules, Fair NAT now creates it's very own private iptables chains. These can be flushed without risking to lose something the Firewall might have set up.
However, there are at least three things about Firewall Support I need to point out:
For Firewall Support, it has to put packets into the Fair NAT chains at some point. Ideally this is after the Firewall dropped unwanted packets, but that's just a guess. The best spot to put the rules in depends on the Firewall. Here's the rules:
iptables -t nat -A PREROUTING -j FAIRNAT_PREROUTING iptables -t mangle -A PREROUTING -j FAIRNAT_PREROUTING iptables -A FORWARD -j FAIRNAT_FORWARD iptables -t mangle -A FORWARD -j FAIRNAT_FORWARD iptables -t nat -A POSTROUTING -j FAIRNAT_POSTROUTING iptables -t mangle -A POSTROUTING -j FAIRNAT_POSTROUTING |
Of course, that's just a suggestion. You can modify these rules any way you like. However, don't forget that packets that don't go into the Fair NAT chains, will not be seen by Fair NAT rules, which could have side effects like packets not being shaped properly and so on.
Please send me some feedback if this solution works for you.
WARNING: Use this script at your own risk only. It may remove all existing firewall rules, it may have critical bugs that are lethal to your system, it will set your duck on fire and many more bad things may happen to you. I'm not responsible for any damage caused by this script. Handle with care. Thanks.
Due to lack of free time and a proper Linux box, this script will no longer be updated. Hopefully the script can still be useful as an example that helps you creating your own shaping setup. Other / contributed versions of the script available down below.
The script can be found here: (use 0.79 if you have issues with 0.80)
Please take the time to read the comments directly in the script, especially in the sample configuration file, too. I tried to add loads of comments in case you want to modify some parts of the script.
I have an OpenWRT router now. Since some different rules apply to this box than the stand alone Linux PC I had as a router at my old place, I wrote a simplified version of the FairNAT script for this router. It still uses subroutines to do its work, but it does not have a config file. So every user has to write his own shaping rules in his own subroutine. Other than that, the concept is still the same. Since the OpenWRT box has only one ethernet device, downstream shaping is done using a virtual IMQ interface. It uses HTB and one class per user, and it works great. It does not use HSFC because I can not make this scheduler behave like I want it to.
Albeit simplified, this is the latest version of Fair NAT (2008-01-10), and by the looks of it also the last for now. This OpenWRT router is serving a 16Mbit DSL line now, and I'm stuck with that solution for at least two years. I won't add any features to it because I don't need them, and I don't have time for adding them.
As some of you may already have noticed, I don't have much time to work on this project, as I'm very busy with my studies and such. I also moved, and my new home does not have a Linux router and no people to share a line with, so I'm not even using Fair NAT myself for the time being. So instead of letting all the contributions go to waste, I'll put user modified versions of Fair NAT here. These are all untested, so use at your own risk only.
If you find bugs, please report them. If you have ideas for improvements, please tell me. If you need more features, ask me to implement them. If you decide to (not) use the script, drop me a note. If the script works particularly well (or bad) for you, I want to hear about it. If you can't get it to work, I'll see what I can do to help you. No, really. Give me some feedback. I'm tired of having nothing but spam in my mailbox. Thanks.
My mail address is: Andreas.Klauer@metamorpher.de
Thanks to all those people who helped me write this script. Special thanks to the authors of the LARTC Howto and the people on the LARTC mailing list. And of course to Stef Coene for his great Docum page. Thanks to everyone who sent me bug reports, suggestions, feature requests and so on.
This is more like a will-never-be-done list than a TODO list really.