Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I do not wish to limit the rate of a specific service. My goals is to limit rate based solely on the incoming IP address. For example using a pseudo-rule:

john.domain.local (192.168.1.100) can only download from our httpd/ftp servers at "10KB/s" (instead of 1MB/s)

How could I rate limit using IPTables based on incoming IP addresses?

share|improve this question
add comment

2 Answers

up vote 137 down vote accepted

IPTables isnt made for this kind of work, where lots and lots of packets need to be analyzed to make these decisions. IPTables is partly the answer though!

The real answer to this is the awesome and underused traffic control facilities in linux. Note that mucking around with this without knowing what is going on may lead to you losing network connectivity to the machine! You have been warned!

Assuming eth0 is the outgoing device you will need to create a class-based traffic control queue which will by default output most traffic throught the 'fast' queue and put a specific list of people into the 'slow' queue.

The beauty of this is you can create a situation whereby you allow lots of outbound traffic for the slow user unless an overriding class wants the bandwidth, but this example does not do this (will always provide 10kbps to the slow users). The queing system will look something like this:

                         Inbound traffic
                              +
                              |
                              |
                              v
                     +------------------+
                     |   Class 1:1      |
                     |------------------|
                     |  Root (all flows)|
                     |       100mbit    |
                     +-----+-----+------+
                           |     |
                           |     |
                           |     |
                           |     |
                           |     |
          +----------+     |     |     +----------+
          |    1:11  +-----+     +-----+    1:12  |
          |----------|                 |----------|
          | Default  |                 | Slow     |
          |100mb-80kb|                 |   80kb   |
          +----------+                 +----------+

To do this, first you'll need to setup the queuing discipline in the kernel. The following will do this for you.. you must run this as one whole script

#!/bin/bash
tc qdisc add dev eth0 parent root handle 1: hfsc default 11
tc class add dev eth0 parent 1: classid 1:1 hfsc sc rate 100mbit ul rate 100mbit
tc class add dev eth0 parent 1:1 classid 1:11 hfsc sc rate 99920kbit ul rate 100000kbit
tc class add dev eth0 parent 1:1 classid 1:12 hfsc sc rate 80kbit ul rate 80kbit

tc qdisc add dev eth0 parent 1:11 handle 11:1 pfifo
tc qdisc add dev eth0 parent 1:12 handle 12:1 pfifo

The "default 11" is important as it tells the kernel what to do with traffic not classified.

Once this is done, you can then setup an iptables rule to classify packets that match a certain criteria. If you plan on putting lots and lots of people into this slow rule an ipset rule is more appropriate (which should be available on rhel6 I believe).

So, create an ipset database to do the matching against...

ipset create slowips hash:ip,port

Then create the iptables rule to do the match..

iptables -t mangle -I OUTPUT -m set --match-set slowips dst,src -j CLASSIFY --set-class 1:12

This instructs the kernel that if you match the destination IP with the source port from the set, classify it into the slow queue you setup with traffic control.

Now, finally whenever you want to slow an IP down you can use the ipset command to add the ip to the set such as this:

ipset add slowips 192.168.1.1,80
ipset add slowips 192.168.1.1,21
...

You can test it works using the command "tc -s class show dev eth0" and you will see stats in there indicating packets being redirected to the slow queue.

Note the only real downside to this is making it survive reboots. I dont think there are any init scripts available to create the ipsets from dumps on reboot (and they also must be created before iptables rules) and im certain theres no init scripts to resetup traffic control rules on reboot. If your not bothered, you can just recreate the whole thing from invoking a script in rc.local.

share|improve this answer
3  
Well, I can't thank you enough. This is very descriptive and very informative. I later realized that knowledge of TC would be required and I have since started looking into this. Thanks Again! –  James Apr 28 '12 at 23:41
    
Oh and as for losing connection. I'm making sure I have the configuration down before moving from my VPS to the host machine. Also, I have VPN access to the private network on ETH0. I'll only be working on ETH1 so in theory I wont have the problem. But warning heard! –  James Apr 28 '12 at 23:43
2  
I cant tell you how many times I have read similar tutorials , this is the first one that made sense –  RC1140 May 5 '12 at 20:16
5  
As a sidenote, its typically more appropriate to do resource limitation like this in control groups (again, also possible and also underused and awesome) as you can define per-application limits on cpu, memory, IO and network in a centralized 'policy store'. But have yet to see such a question being posited to offer an answer. –  Matthew Ife May 5 '12 at 20:44
2  
If you don't like the tc syntax you could give tcng a try which adds a bit more user friendly language that generates tc commands. I have used to like this in shell scripts: echo '... multi line tcng configuration ...' | tcng | sh. –  Mattias Wadman May 6 '12 at 15:08
show 1 more comment

It's as simple as taking a rate limiting rule and adding the -s switch. The -s switch matches incoming IPs. For example iptables -A INPUT -s 1.1.1.1 and then finishing up with your preferred method of rate limiting for that rule.

share|improve this answer
    
Thank you for your prompt answer. Unfortunately my main problem is the second half it. I've looked into --limit and I haven't seen anything that allows me to limit based on KB/s - any direction you can point me? –  James Apr 28 '12 at 21:28
1  
@James I would have gotten back to you but I had to step out to a friend's house. Just got back and I see that MIfe has done quite a job. =) –  Wesley Apr 29 '12 at 3:30
    
It's all good. Your answer led me to "tc". So I would have got their eventually. Mlfe's response was a very welcomed bonus! Thanks to you both and for making my first servervault participation a pleasant experience. –  James Apr 29 '12 at 4:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.