Showing posts with label iptables. Show all posts
Showing posts with label iptables. Show all posts

Thursday, September 8, 2011

Traffic Control

Introduction

Linux's traffic control functionality offers a lot of capabilities related to influencing the rate of flow, as well as latency, of primarily outgoing but also in some cases incoming network traffic. It is designed to be a "construction kit" rather than a turn-key system, where complex network traffic policing and shaping decisions can be made using a variety of algorithms. The Linux traffic control code is also often used by academia for research purposes, where is it can be a useful mechanism to simulate and explore the impact of a variety of different network behaviors. See netem for an example of a simulation framework that can be used for this purpose.
Of course, Linux traffic control can also be extremely useful in an IT context, and this document is intended to focus on the practical, useful applications of Linux traffic control, where these capabilities can be applied to solve problems that are often experienced on modern networks.

Monday, September 5, 2011

NETMAP target



11.11. NETMAP target

NETMAP is a new implementation of the SNAT and DNAT targets where the host part of the IP address isn't changed. It provides a 1:1 NAT function for whole networks which isn't available in the standard SNAT and DNAT functions. For example, lets say we have a network containing 254 hosts using private IP addresses (a /24 network), and we just got a new /24 network of public IP's. Instead of walking around and changing the IP of each and every one of the hosts, we would be able to simply use the NETMAP target like -j NETMAP -to 10.5.6.0/24 and voila, all the hosts are seen as 10.5.6.x when they leave the firewall. For example, 192.168.0.26 would become 10.5.6.26.

Dirty NAT tricks to get a VPN to work with clients also numbered in the private address space

The Problem

You have a corporate LAN. You want to set up a VPN (in this case OpenVPN) into the LAN for your road-warriors. However, your LAN is numbered with one of the very common private subnets, such as 192.168/16. Your road-warriors often get addresses in the same private subnet from their coffee-shops, and this breaks things horribly. This is illustrated in the diagram below.
I'm going to use specific IP addresses and interface names on this page, since too often I find when people try to describe the general solution it actually makes it less clear and harder to apply to your own network.

Thursday, August 25, 2011

Iptables Command

You are here: DD-WRT wiki mainpage / Scripting / SSH/Telnet & The CLI / iptables
Iptables is a powerful administration tool for IPv4 packet filtering and NAT. It is used to set up, maintain, and inspect the tables of IP packet filter rules in the Linux kernel.
Iptables commands can be entered by command line interface, and/or saved as a Firewall script in the dd-wrt Administration panel. I tend to recommend testing and confirming your rules at the command line first. This way, if you happen to make a big mistake (like blocking access to the router), simply rebooting the router should repair it rather than having to do a hard reset. To get your rules to survive a reboot of the router, save them in a Firewall script as mentioned earlier.
I think we should have something about Firewall Builder on this page, since they're kind of related...

Monday, August 8, 2011

Advanced Policy Firewall


Description:
Advanced Policy Firewall (APF) is an iptables(netfilter) based firewall system designed around the essential needs of today’s Linux servers. The configuration is designed to be very informative and easy to follow. The management on a day-to-day basis is conducted from the command line with the ‘apf’ command, which includes detailed usage information on all the features.

Wednesday, August 3, 2011

iptables: MIRROR target


The MIRROR target is an experimental and demonstration target only, and you are warned against using it, since it may result in really bad loops hence, among other things, resulting in serious Denial of Service. The MIRROR target is used to invert the source and destination fields in the IP header, and then to retransmit the packet. This can cause some really funny effects, and I'll bet that thanks to this target not just one red faced cracker has cracked his own box by now. The effect of using this target is stark, to say the least. Let's say we set up a MIRROR target for port 80 at computer A. If host B were to come from yahoo.com, and try to access the HTTP server at host A, the MIRROR target would return the yahoo host's own web page (since this is where it came from).
Note that the MIRROR target is only valid within the INPUT, FORWARD and PREROUTING chains, and any user-defined chains which are called from those chains. Also note that outgoing packets resulting from the MIRROR target are not seen by any of the normal chains in the filter, nat or mangle tables, which could give rise to loops and other problems. This could make the target the cause of unforeseen headaches. For example, a host might send a spoofed packet to another host that uses the MIRROR command with a TTL of 255, at the same time spoofing its own packet, so as to seem as if it comes from a third host that uses the MIRROR command. The packet will then bounce back and forth incessantly, for the number of hops there are to be completed. If there is only 1 hop, the packet will jump back and forth 240-255 times. Not bad for a cracker, in other words, to send 1500 bytes of data and eat up 380 kbyte of your connection. Note that this is a best case scenario for the cracker or script kiddie, whatever we want to call them.

http://www.faqs.org/docs/iptables/targets.html

Tuesday, July 19, 2011

Reject IP packets with an ICMP error, or just drop them?

Consider an internet-facing host (the outer firewall). What should be done with undesired traffic: just drop it, or send back an ICMP error such as port unreachable? (In Linux terms: iptables -P DROP or iptables -j REJECT?)

This isn't about the security of our network, the packets aren't getting in either way. It's about being a good citizen. REJECT could make our firewall participate in a DDoS, if the incoming packets have a spoofed origin. But is it always the right choice? Are there circumstances where it's better to send back a proper ICMP error?

network firewalls ddos
linkimprove this question
asked Apr 23 at 14:27
Gilles
597112
1
as I mentioned on Unix SE, I would suggest putting a limit on any reject, that way if someone is sitting there sending packets you aren't sending a ton the other direction. I can't suggest how many to allow. – xenoterracide Apr 23 at 18:01
@xenoterracide the way DDoS reflection attacks work (the attack being initiated from thousands of bots that could theoretically use any "reflector), it's still enough for an interconnected host to produce just a little bit of unintended traffic in order to contribute to a major attack. Still such limits on rejects should be considered "Good Internet Citizen" practices – Georgios Apr 24 at 1:42
feedback
1 Answeractiveoldestvotes
up vote
5
down vote
accepted
I usually vote for sending back an ICMP error for UDP and a RST packet for TCP. It does make debugging issues so much easier. And it prevents annoying timeouts: Mail and IRC servers often attempt to do an ident query or check that the client is not an open proxy.

If it is done at the out-most firewall there will be no relevant information disclosed. Depending on the setup it may even conceal that there is a firewall. If there were no answer, it would be obvious that there is a black hole.

It's important that servers send no answer for packets sent to a broadcast address in order to prevent an amplifying effect in a DoS situation. It's okay for the firewall to send the error message in this situation, resulting in only one answer.

ICMP-errors and TCP-RST packets are not larger than the smallest original packet. So these are not interesting for DDoS attacks.

Edit: DNS authoritative servers (and missconfigured DNS resolvers) are a lot more interesting for reflective DDoS attacks because the DNS answers are larger than the queries and therefore grant the attacker an amplification free of charge.

linkimprove this answer
edited Apr 23 at 20:14

answered Apr 23 at 14:41
Hendrik Brummermann
3,3272525
1
Regarding your last paragraph: what about the case when a DDoS wants to hide which systems are infected? The bot, B, sends a packet to server S that claims to be from source address V, the victim. Then S sends an error/reset packet to V. There are many B, and many S. – Graham Lee♦ Apr 23 at 18:59
2
While this is theoretically possible, this is an extremely inefficient way for two reasons: a) ICMP unreachable and unsolicited TCP-RST are very easily filterable at or before the victim. And b) more important: The TCP-RST und ICMP unreachable packets are very small, so the botnet needs to be extremely large. As such large botnets are expensive, an attacker will likely try to get the amplification behind the botnet for free. That's why reflection attacks without amplification are not seen in the wild. You should worry about your DNS server more than about error responses. – Hendrik Brummermann Apr 23 at 20:06
really useful reply, thanks :)

http://security.stackexchange.com/questions/3290/reject-ip-packets-with-an-icmp-error-or-just-drop-them

Thursday, May 26, 2011

hfsc script

tc qdisc add dev $DEV root handle 1: hfsc default 10
tc class add dev $DEV parent 1: classid 1:1 hfsc sc rate 3072kbit ul rate 3072kbit
tc class add dev $DEV parent 1:1 classid 1:10 hfsc sc umax 1500b dmax 53ms rate 614kbit ul rate 3072kbit
tc class add dev $DEV parent 1:1 classid 1:11 hfsc sc umax 1500b dmax 30ms rate 2457kbit ul rate 3072kbit

iptables -t mangle --new-chain voip
iptables -t mangle --insert voip -o $DEV -j voip

tc filter add dev $DEV parent 1: prio 1 protocol ip handle 1 fw flowid 1:11

iptables -t mangle -A voip -p udp --sport 4569 -j MARK --set-mark 1
iptables -t mangle -A voip -p udp --dport 4569 -j MARK --set-mark 1

iptables -t mangle -A voip -p udp --sport 5060 -j MARK --set-mark 1
iptables -t mangle -A voip -p udp --dport 5060 -j MARK --set-mark 1


iptables -t mangle -A voip -p udp --sport 10000:20000 -j MARK --set-mark 1
iptables -t mangle -A voip -p udp --dport 10000:20000 -j MARK --set-mark 1




tc -s qdisc show eth0
qdisc hfsc 1: root refcnt 2 default 10
Sent 83509933 bytes 1029276 pkt (dropped 3, overlimits 6238 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0

tc -s class show dev
class hfsc 1: root
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
period 0 level 2

class hfsc 1:11 parent 1:1 sc m1 0bit d 25.1ms m2 2457Kbit ul m1 0bit d 0us m2 3072Kbit
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
period 0 level 0

class hfsc 1:1 parent 1: sc m1 0bit d 0us m2 3072Kbit ul m1 0bit d 0us m2 3072Kbit
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
period 1029159 work 83510091 bytes level 1

class hfsc 1:10 parent 1:1 sc m1 0bit d 33.5ms m2 614000bit ul m1 0bit d 0us m2 3072Kbit
Sent 83510091 bytes 1029278 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
period 1029159 work 83510091 bytes rtwork 82189624 bytes level 0


tc -s filter show dev
filter parent 1: protocol ip pref 1 fw
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:11

iptables -t mangle -L voip -v -x
Chain voip (1 references)
pkts bytes target prot opt in out source destination
0 0 MARK udp -- any any anywhere anywhere udp spt:sip MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp dpt:sip MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp spt:iax MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp dpt:iax MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp spts:10000:20000 MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp dpts:10000:20000 MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp spt:sip-tls MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere anywhere udp dpt:sip-tls MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any x.x.x.18 anywhere MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere x.x.x.18 MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any x.x.x.24 anywhere MARK xset 0x1/0xffffffff
0 0 MARK udp -- any any anywhere x.x.x.24 MARK xset 0x1/0xffffffff

Saturday, March 26, 2011

IPTables Linux firewall with packet string-matching support

by Anton Chuvakin
IPTables Linux firewall with packet string-matching support
by Anton Chuvakin, Ph.D.
last updated December 31, 2001

Linux firewalling code has come a long way since the time ipfwadm was introduced in kernel version 1.2.1 in 1995. Ipfwadm enabled standard TCP/IP packet filtering features such as filtering by source/target addresses and port numbers. Then, in early 1999, when the first stable 2.2.0 kernel was released, firewalling code was replaced with new ipchains-controlled code. New features included support for chains of rules, fragmentation handling, better network address translation (NAT) support and several usability improvements. Readers should be reminded that Linux firewalling includes kernel-level code (usually in form of loadable module or kernel source patch) and user-level code (a control utility such as /usr/bin/ipchains, that is used to insert packet rules into kernel-space). Thus whenever new Linux firewalling code was introduced it involved both kernel and userspace code rewrite.
With the release of 2.4 in 2001, iptables code came out. It introduced many important improvements such as stateful firewalling, filtering packets based on any combination of TCP flags and on MAC address, more configurable and flexible logging, powerful and easy to use support for network address translation (NAT) and transparent proxies, DoS blocking support by rate-limiting and others. (For details see, for example A Comparison of iptables Automation Tools.)
However, the most important architectural addition was the introduction of the modular architecture. For example, ipchains and ipfwadm compatibility mode is implemented as a set of kernel modules that can be inserted into the running kernel to provide the corresponding functionality. In addition, user-coded modules are now possible. For example, filtering by port range, TTL value and time of packet arrival, stateful inspection for custom protocols, and random packet inspection are not part of the iptables suite, but were implemented later. Many new and interesting modules were coded. To program a module one has to create a kernel-level part that will be compiled into a loadable module and user-level part that will be used to control the filtering behavior. For more details see Rusty Russell's Linux iptables HOWTO.
This article will cover string pattern matching functionality (evidently, implemented as a module), which allows limited investigation of the packet payload. This is a significant breakthrough for iptables technology since it goes beyond inspecting TCP/IP flags, which is standard for packet filter firewalls. It is well-known that firewalls can be loosely categorized into proxies and packet filters. The latter "know" the application-level protocols such as telnet, HTTP or SMTP and can inspect the protocol payloads and verify the commands. This comes at a significant performance penalty since packets have to be processed higher in the network protocol stack in application layer. For each inspected protocol a new proxy should be written. The packet filters, on the other hand, can usually only inspect source and target addresses and ports, TCP/IP flags and have to totally ignore higher-layer protocol payloads. Due to that reason, they are usually much faster than proxy firewalls (3-10 times). Thus proxies are used for more granular security while packet filters are used on higher bandwidth lines for higher throughput.
In light of the above, adding content inspection capabilities in iptables presents an attempt to cross the bridge between two firewall groups without getting stuck in disadvantages of either method. It also clearly demonstrated an advantage of a new modular architecture over old ipchains code. It should be noted that packet filter such as iptables does not become higher-layer-protocol-aware since it still operates at network level (layer 3 in OSI structure), but is only allowed to peek at payloads, rather than analyze the application-level communication structure.
Before the string matching module was started in May 2001, there were attempts to add content inspection to iptables-based firewalls. One such project is Hogwash, which couples the Snort IDS rule-matching engine with iptables in order to respond to packets with attack signatures in them.
Now we will provide a step-by-step direction for enabling string matching packet inspection for RedHat Linux. Standard RH 7.2 comes with iptables 1.2.3 and 1.2.4 is available as an RPM update. However, string-matching functionality is not included in either since it is marked as "experimental" by developers. Thus some kernel recompilation is required.
If you are using RH 7.1-7.2 you already have kernel version 2.4 installed. You need at least 2.4.4 for the latest iptables 1.2.4 to function. It is always recommended to download the latest kernel version available from you system distributor. At present, there is one exception from this rule: iptables-1.2.4 string matching patch does not seem to work with 2.4.9 kernel version. You should have kernel source RPM package installed (that usually puts the full source tree in /usr/src/linux-2.4.x) or just have the kernel source downloaded from elsewhere (such as www.kernel.org or one of its mirrors).
For this article, the latest 2.4.16 will be used as an example. Test were also run with 2.4.7 kernel shipped with RedHat 7.2, but the 2.4.7 kernel is not recommended due to several minor bugs in security mechanisms such as SYN-cookie protection and iptables save/restore functionality.
Surely it is redundant to remind the reader to backup the entire kernel source tree before starting the experiment or keep a source RPM handy. Keep in mind, that the latter will only help if you have not compiled the kernel before.
Now, iptables code should be downloaded from http://netfilter.samba.org/iptables-1.2.4.tar.bz2 . After archive is unpacked, the iptables should be configured and the appropriate source code merged with the kernel source tree. The semi-automated program is available for that purpose. First, one might want to run the program to include the iptables patches that are already considered stable, but have not been included in the kernel release. From the directory where iptables are unpacked (in this example iptables are in /home/anton/iptables-1.2.4 and kernel source is in /usr/src/linux-2.4.16) run:
make pending-patches KERNEL_DIR=/usr/src/linux-2.4.16 
That will start the process of interactive patch application. It appears, that, while you can safely apply all of those patches, none are required for iptables string support. Say y (yes) to whichever patches you think will be needed for your installation. If you want to play it safe choose none.
Now we are ready to apply experimental patches such as string matching support. Run:
make patch-o-matic KERNEL_DIR=/usr/src/linux-2.4.16 
In the interactive session that follows answer y (yes) to application of string.patch. The program will go though all available patches, including the stable ones.
Testing... string.patch NOT APPLIED ( 2 missing files) The string patch: Author: Emmanuel Roger  Status: Working, not with kernel 2.4.9 This patch adds CONFIG_IP_NF_MATCH_STRING which allows you to match a string in a whole packet. THIS PATCH DOES NOT WORK WITH KERNEL 2.4.9 ! Do you want to apply this patch [N/y/t/f/q/?] y 
While the rest of the patches may sound like fun as well, they are not relevant to this article. If you choose to install any other patches, heed the warnings given by developers about what patches break functionality (such as dropped tables patch). Make sure you do not install the MAC filtering patch since it was recently found to contain a bug.
Now we are ready to compile the user-space code and the libraries:
make KERNEL_DIR=/usr/src/linux-2.4.16 
and then install them (iptables program goes in /usr/local/user/sbin and libraries go into /usr/local/lib/iptables). As root:
make install KERNEL_DIR=/usr/src/linux-2.4.16 
Now we are ready to configure and compile the kernel:
cd /usr/src/linux-2.4.16 
Any of the kernel configuration methods can be used. Detailed discussion of this is provided in a huge number of Internet sources such as kernel HOWTO. In brief:
make xconfig 
In the GUI that appears got to Netfilter configuration and choose m (for modular support) in String match support (EXPERIMENTAL). See picture

Then follow with a standard
make dep ; make bzImage ; make modules ; make modules_install 
Now install the kernel itself using you favorite method and reboot. Upon reboot, test whether the iptables with string support is enabled. As root:
/usr/local/sbin/iptables -m string -help 
It should produce the iptables standard help message appended by:
STRING match v1.2.4 options: --string [!] string Match a string in a packet 
The resulting functionality will allow you to match packets by the content. Some real life tests can be performed using netcat or telnet to make sure we can really grab packets by the content.
Run:
iptables -A INPUT -m string --string "test" -j LOG --log-level
 info --log-prefix "TEST" 
Then start a netcat server by:
nc -l -p 3456 
Connect to it via:
telnet localhost 3456 
Now type:
test whatevertestdoes 
It should produce the message similar to the following in your log files (providing you log messages with severity level ‘info’ somewhere)
Nov 27 23:16:53 pua kernel:
 TEST IN=lo OUT=MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00  
SRC=127.0.0.1 DST=127.0.0.1 LEN=2154 TOS=0x00 PREC=0x00 TTL=64 
ID=42880 DF PROTO=TCP SPT=3128 DPT=33018 WINDOW=32767 RES=0x00 ACK PSH URGP=0 Nov 27 23:16:53 pua kernel:
 TEST IN=lo OUT=MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00  
SRC=127.0.0.1 DST=127.0.0.1LEN=1830 TOS=0x00 PREC=0x00 TTL=64 
ID=17451 DF PROTO=TCP SPT=8000 DPT=33017 WINDOW=32767 RES=0x00 ACK PSH URGP=0 
Every time the string test is present in TCP/IP packets, the above log message is produced. What is it good for? Many things. As was suggested in the excellent article, Filtering packets based on string matching, by sysctl at Linuxguru.net, it can be used to block all those pesky IIS worms from filling your UNIX web server log files with requests to cmd.exe etc. Those worms cannot hurt you, but if your /var partition (where logs are usually stored) is not too big this measure can be pretty helpful. Just silently drop all those port 80 requests or log them using the message limiting facility:
To silently drop them:
iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --string "cmd.exe" 
To only log no more then 1 message per hour:
iptables -I INPUT -j LOG -p tcp -s 0.0.0.0/0 -m string --string "cmd.exe" -m limit
 --limit 1/hour 
Replace the string with whatever the new Windows "worm-of-the-moment" is requesting and just add another rule.
You can also stop requests to things you do not want to have retrieved from your Web server. Apparently, test.cgi should be removed, rather than put into iptables rules. However, in some cases the ability to stop requests for specific documents at the TCP/IP level before they reach the Web server access control facility might provide the needed "defense in-depth" for your Web infrastructure.
Another great suggestion from Bill Stearns (author of Mason firewall building script) is to convert your Snort network IDS rules into iptables rules with string support. Snort IDS attack signature database contains about 1200 signatures and appears to be the biggest publicly available attack database suitable for instant deployment. The ability to use the ready-made signatures for iptables is of immense value. The page that describes his experimental software is at http://www.stearns.org/snort2iptables/. There, you can find the shell script to convert a standard Snort ruleset into iptables rules. Here are a couple of examples for well-known Linux attacks against mountd and bind network daemons:
Snort rules:
1. alert udp $EXTERNAL_NET any -> $HOME_NET 518
  (msg:"EXPLOIT ntalkd x86 linux overflow";
  content:"|0103 0000 000 0 0001 0002 02e8|";
 reference:bugtraq,210;  classtype:attempted-admin; sid:313; rev:2;)   2. alert tcp $EXTERNAL_NET any -> $HOME_NET 53
  (msg:"EXPLOIT named tsig infoleak";
 content: "|AB CD 09 80 00 00 00 01 00 00 00 00 00 00 01 00 01 20 20 20 20 02 61|";
 reference:cve,CAN-2000-0010; reference:bugtraq,2302; reference:arachnids,482;
  classtype:attempted-admin; sid:303; rev:3;) 3. alert udp $EXTERNAL_NET any -> $HOME_NET 635
 (msg:"EXPLOIT x86 linux mountd overflow";
 content:"|5eb0 0289 06fe c889 4604 b006 8946|";
reference:cve,CVE-1999-0002; classtype:attempted-admin; sid:315; rev:1;) 
Converted iptables rules:
1. iptables -A SnortRules -p udp -s $EXTERNAL_NET -d $HOME_NET --dport 518 -m 
string --string "   è" -j LOG --log-prefix "SID313 " # "EXPLOIT ntalkd x86
 linux overflow" bugtraq,210 classtype: attempted-admin sid:313 
2. iptables -A SnortRules -p tcp -s $EXTERNAL_NET -d $HOME_NET --dport 53 -m 
string --string "«Í .a" -j LOG --log-prefix " 
SID303 " # "EXPLOIT named tsig infoleak" cve,CAN-2000-0010 bugtraq,2302
 arachnids,482 classtype:attempted-admin sid:303 
3. iptables -A SnortRules -p udp -s $EXTERNAL_NET -d $HOME_NET --dport 635 -m 
string --string "^° ‰ þȉF ° ‰F" -j LOG --log-prefix " cve-CVE-1999-0002
 " # "EXPLOIT x86 linux mountd overflow" classtype:attempted-admin sid:315 
It is easy to see that the above conversion uses the buffer overflow string used for the above exploits to catch the attack. Some rules are not converted mostly due to the fact that Snort is still "smarter" than iptables in fragmentation handling.
Overall, iptables with string support can be used to protect networks (if deployed on the organization gateway) and individual hosts (deployed as a part of host hardening) from many attacks on the network services that have to be open to the world (WWW, mail, DNS, ftp) and have not been protected by ordinary packet filters. In addition, iptables string matching can also help with policy enforcement - just implement the rules that stop inappropriate content by keyword. There might be better solutions of implementing the content scanning, but another layer of protection never hurts.
Anton Chuvakin, Ph.D. is in the process of looking for another infosec job after having his fun dot-bomb experience. He spends his plentiful pastime running Linux, building a small infosec portal and preparing for CISSP.
This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.

Fair traffic shaping an ADSL line for a local network using Linux

I am currently writing this up! In the meantime, full details are located here
Traffic shaping a standard ADSL link in order to share it with a couple of hundred users is a common problem. There are dozens of bits of software and firewall scripts out there already to do this. This particular script is one that I have written. It aims to be as simple as possible, is easily customised, and uses connlimit to identify P2P users. Although the latter is not 100% reliable, it seems to work pretty well and does not fall foul of any new/changed P2P software that happens to some of the other scripts.


Contents

[hide]

[edit] Software required

Any recent Linux distribution should be able to be used for these scripts. The example shown here uses Debian.
The following packages are required:
  • Kernel 2.6.26
  • IPset
IPset is not yet in the stable kernel, so the easiest way to install it (in Debian) is:
apt-get install ipset
apt-get install module-assistant xtables-addons-source
module-assistant prepare
module-assistant auto-install xtables-addons-source
(Note: at the time of writing, the above packages are not in stable, so will have to be installed from testing repositories. See here for an example).
A better way is possibly:
apt-get install ipset
apt-get install module-assistant
apt-get install netfilter-extensions-source
module-assistant build netfilter-extensions-source
module-assistant install netfilter-extensions-source
or from recent feedback:
aptitude install ipset ipset-source module-assistant
module-assistant auto-install ipset-source
But I need to check this on a clean install.

[edit] Principle of operation

  • All traffic is given an iptables MARK depending on its type:
    • 10 for low latency traffic such as SSH shells
    • 30 for normal web browsing
    • 40 for any normal traffic including large web downloads
    • 60 for P2P traffic
  • HTB is used to create classes for all this traffic
  • All traffic shaping is hashed by client, not by connection. This means that one user with several large download connections will get the same amount of total bandwidth as one user with one connection. The default within Linux and routers is normally to do it per connection.
  • ipset is used to generate a list of client IP addresses that are using P2P software.
  • connlimit is used to detect P2P software. The firewall rules below look for multiple connections to high port numbers from a client.


[edit] Assumptions

  • ppp0 is the internet connection
  • eth0 is the local network connection


[edit] Firewall rules required

I will first describe some of the rules that form the basis of the traffic shaping. Later in this page the full script is shown.
The first task is to create an ipset for storing the IP addresses of all our naughty users slurping up the bandwidth with constantly downloading P2P software. A timeout of 60 seconds is used, so that as soon as they turn off their software their IP address is removed. The current IP addresses in the IPset can be monitored with the 'ipset -L' comand from the bash prompt
# Create a set called p2p with 60 second timeout
ipset -N p2p iptree --timeout 60
Next we need to mark traffic as required, using the principles set out earlier. The following code contains some examples.
# First mark all traffic coming in on the ppp link with the default of 40
$IPTABLES -t mangle -A PREROUTING -i ppp0 -j MARK --set-mark 40

# Mark http and https traffic as 30, both in and out
$IPTABLES -t mangle -A FORWARD -p tcp --sport 80 -i ppp0 -j MARK --set-mark 30
$IPTABLES -t mangle -A FORWARD -p tcp --dport 80 -o ppp0 -j MARK --set-mark 30
$IPTABLES -t mangle -A FORWARD -p tcp --sport 443 -i ppp0 -j MARK --set-mark 30
$IPTABLES -t mangle -A FORWARD -p tcp --dport 443 -i eth0 -j MARK --set-mark 30

# Mark in and out SSH traffic as high priority
$IPTABLES -t mangle -A FORWARD -p tcp --sport 22 -i ppp0 -j MARK --set-mark 10
$IPTABLES -t mangle -A FORWARD -p tcp --dport 22 -o ppp0 -j MARK --set-mark 10

# Mark any traffic destined for the local machine with 1
# This traffic does not need shaping and we will ignore it later
$IPTABLES -t mangle -A POSTROUTING --source 10.0.0.1 -j MARK --set-mark 1
$IPTABLES -t mangle -A POSTROUTING --destination 10.0.0.1 -j MARK --set-mark 1

# Mark any large downloads as 40 (they may have been marked 30 or 10 earlier)
$IPTABLES -t mangle -A FORWARD -m connbytes --connbytes 504857: --connbytes-dir both \
  --connbytes-mode bytes -j MARK --set-mark 40

Now we need to look out for all those P2P connections. We're going to find these out by looking for a client on the network making lots of connections to high port numbers, which is generally what P2P software does. This isn't foolproof of course: I have seen P2P software start to use port 80, and there could be false negatives, but on the whole it seems to work better than any other solutions out there that I have tried.
# TCP traffic from clients going to high port numbers
# Add the client IP address to the ipset
$IPTABLES -t mangle -A FORWARD -o ppp0 -p tcp --dport 1024: \
 -m connlimit --connlimit-above 8 -j SET --add-set p2p src 

# UDP traffic from clients going to high port numbers
$IPTABLES -t mangle -A FORWARD -o ppp0 -p udp --dport 1024: \
 -m connlimit --connlimit-above 4 -j SET --add-set p2p src 

# TCP traffic in to clients from high port numbers
$IPTABLES -t mangle -A FORWARD -i ppp0 -p tcp --sport 1024: \
 -m connlimit --connlimit-above 8 -j SET --add-set p2p dst

# UDP traffic in to clients from high port numbers
$IPTABLES -t mangle -A FORWARD -i ppp0 -p udp --sport 1024: \
 -m connlimit --connlimit-above 4 -j SET --add-set p2p dst
The above rules just add the client IP address to the ipset. We now need to mark the traffic, which the following rules do. On one network, traffic became so slow that I marked ALL traffic to and from those clients as '60'. This certainly sped the network up, but of course the entire internet connection for that client became really slow. If you want to do that just remove the destination port parameters.
# Once a user is in the p2p IPSET, these rules mark their traffic
# that is above 1024 to the lowest priority

# TCP traffic from the client
$IPTABLES -t mangle -A FORWARD -o ppp0 -p tcp --dport 1024: \
 -m set --set p2p src -j MARK --set-mark 60

# TCP traffic to the client
$IPTABLES -t mangle -A FORWARD -i ppp0 -p tcp --sport 1024: \
 -m set --set p2p dst -j MARK --set-mark 60

# UDP traffic from the client
$IPTABLES -t mangle -A FORWARD -o ppp0 -p udp --dport 1024: \
 -m set --set p2p src -j MARK --set-mark 60

# UDP traffic to the client
$IPTABLES -t mangle -A FORWARD -i ppp0 -p udp --sport 1024: \
 -m set --set p2p dst -j MARK --set-mark 60

Finally enable internet connection sharing:
$IPTABLES -A FORWARD -i ppp0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -i eth0 -o ppp0 -j ACCEPT
$IPTABLES -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

[edit] Shaping the traffic using HTB

Now that we have got to this stage, we have all our traffic nicely marked. All we need to do now is shape it. Unfortunately ingress shaping on Linux is somewhat limited, so instead we only do egress shaping. To limit traffic coming in from the internet, we shape the traffic as it leaves the server. Not ideal, but it seems to work okay.
In these scripts I limit the downlink at 2200 kbps and the uplink at 330 kpbs. These figures *must* be less that your connection is capable of, otherwise no shaping will take place as the connection will do the shaping instead. The generally accepted approach is to set the values to 90% the theoretical maximum, but I advise you to experiment.
First, the downlink. Shaping is done on eth0 as the traffic leaves for the network.
We use HTB to do the shaping. It can be difficult to understand HTB and tc in general, but if you look hard enough there is some good documentation out there, it's just hard to find. I will place some links at the end of this document.
First, some basics. A qdisc is a whole set of shaping rules that we apply, normally to a whole interface. Here we add a HTB qdisc to the interface eth0. This is known as the root for the interface.
We don't set a default class; this is so that local eth0 traffic is not shaped. As already stated, we have to shape at eth0 and not ppp0 as we can only do egress shaping decently.
# Create the root qdisc on the interface
tc qdisc add dev eth0 root handle 1: htb

# Add some overall rate limits as defined above. Parent number 1:
# says to apply it to the root above. Call this child 1:1
tc class add dev eth0 parent 1: classid 1:1 htb rate 2200kbit
We now have one root HTB qdisc with a rate limit of 2200kbps. To this we add 4 further children. Note the numbers. 1: (or 1:0) is the root. 1:1 is the first child with the overall rate limit. Each child of this is 1:10, 1:20 and so on. To make things simpler, I have numbered the children below to align with the MARK numbers. Note that all the rates of the children should add up to the single rate limit of the parent.
# Add a number of classes to the root qdisc.
# With some qdiscs the classes are automatically
# created. With HTB they are not so we add 4 in total
# with different rate limits for each

# interactive traffic
tc class add dev eth0 parent 1:1  classid 1:10 htb \
 rate 100kbit ceil 100kbit prio 0

# web browsing
tc class add dev eth0 parent 1:1  classid 1:30 htb \
 rate 1000kbit ceil 1000kbit prio 1

# default traffic
tc class add dev eth0 parent 1:1  classid 1:40 htb \
 rate 1000kbit ceil 1000kbit prio 2

# bad boys
tc class add dev eth0 parent 1:1  classid 1:60 htb \
 rate 100kbit ceil 100kbit prio 3

We now have the HTB qdisc fully set up. However, no traffic will be sent to it yet, and the traffic will not be shaped within each class.
The next set of rules shape traffic in each class. If we don't do this, then all the traffic for a particular class (such as all the webbrowsing traffic - 30) will be piled into the class on a fifo basis. We want to be more intelligent than this. SFQ does some nice fair shaping.
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
tc qdisc add dev eth0 parent 1:40 handle 40: sfq perturb 10
tc qdisc add dev eth0 parent 1:60 handle 60: sfq perturb 10
So, everything is set up and ready to go. We just need to divert some traffic into each of the classes. We do this by attaching a filter to the class. A filter looks for traffic of a particular type and sucks it into the class. In this example, we use the MARK of the traffic (called flowid here).
tc filter add dev eth0 parent 1:0 protocol ip handle 10 fw flowid 1:10
tc filter add dev eth0 parent 1:0 protocol ip handle 30 fw flowid 1:30
tc filter add dev eth0 parent 1:0 protocol ip handle 40 fw flowid 1:40
tc filter add dev eth0 parent 1:0 protocol ip handle 60 fw flowid 1:60
Everything will be working nicely at this point. However, we have one more tweak to do. We want to share traffic between clients (by IP address) not by connection. This means that if one client has 4 downloads on the go, and another has only one, that traffic will be split 50/50, as opposed to the first client getting 80%. We do this by applying more filters to the existing ones.
tc filter add dev eth0 parent 10: protocol ip handle 10 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 30: protocol ip handle 30 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 40: protocol ip handle 40 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 60: protocol ip handle 60 flow hash keys nfct-dst divisor 1024
That completes the downlink. The uplink (out on ppp0) is exactly the same. I have not repeated it here, but it is shown in the full example later.

[edit] The full script

The full script is the same script as used for the complete web portal / traffic shaping solution (detailed here). It can be downloaded from here.

[edit] References

A really well written overview of traffic shaping (chapter 2)
http://www.opalsoft.net/qos/DS.htm

HTB info and manual:
http://www.mikrotik.com/testdocs/ros/2.9/root/queue.php
http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm

Kernel traffic routing diagram - really handy
http://www.docum.org/docum.org/kptd/

Courtesy : http://www.andybev.com/index.php/Fair_traffic_shaping_an_ADSL_line_for_a_local_network_using_Linux 

Kernel Packet Traveling Diagram

Intro

On the LARTC mailing list, there was a long discussion about how a packet is handled by the kernel. Finally, there was a post by Leonardo Balliache that I copied onto this page. I hope this helps people to better understand how it all works. I added extra info aboute the IMQ device, I hope I didn't make any mistakes. All info/updates/corrections are welcome.

Kernel Packet Traveling Diagram

Network
                    -----------+-----------
                               |
                  +--------------------------+
          +-------+-------+        +---------+---------+
          |    IPCHAINS   |        |      IPTABLES     |
          |     INPUT     |        |     PREROUTING    |
          +-------+-------+        | +-------+-------+ |
                  |                | |   conntrack   | |
                  |                | +-------+-------+ |
                  |                | |    mangle     | | <- MARK WRITE  
                  |                | +-------+-------+ |
                  |                | |      IMQ      | |
                  |                | +-------+-------+ |
                  |                | |      nat      | | <- DEST REWRITE
                  |                | +-------+-------+ |     DNAT or REDIRECT or DE-MASQUERADE
                  |                +---------+---------+
                  +------------+-------------+
                               |
                       +-------+-------+
                       |      QOS      |
                       |    INGRESS    |
                       +-------+-------+
                               |
         packet is for +-------+-------+ packet is for
          this machine |     INPUT     | another address
        +--------------+    ROUTING    +--------------+
        |              |    + PDBB     |              |
        |              +---------------+              |
+-------+-------+                                     |
|   IPTABLES    |                                     |
|     INPUT     |                                     |
| +-----+-----+ |                                     |
| |   mangle  | |                                     |
| +-----+-----+ |                                     |
| |   filter  | |                                     |
| +-----+-----+ |                                     |
+-------+-------+                                     |
        |                               +---------------------------+
+-------+-------+                       |                           |
|     Local     |               +-------+-------+           +-------+-------+
|    Process    |               |    IPCHAINS   |           |    IPTABLES   |
+-------+-------+               |    FORWARD    |           |    FORWARD    |
        |                       +-------+-------+           | +-----+-----+ |
+-------+-------+                       |                   | |  mangle   | | <- MARK WRITE
|    OUTPUT     |                       |                   | +-----+-----+ |
|    ROUTING    |                       |                   | |  filter   | |
+-------+-------+                       |                   | +-----+-----+ |
        |                               |                   +-------+-------+
+-------+-------+                       |                           |
|    IPTABLES   |                       +---------------------------+
|     OUTPUT    |                                     |
| +-----------+ |                                     |
| | conntrack | |                                     |
| +-----+-----+ |                                     |
| |   mangle  | | <- MARK WRITE                       |
| +-----+-----+ |                                     |
| |    nat    | | <-DEST REWRITE                      |
| +-----+-----+ |     DNAT or REDIRECT                |
| |   filter  | |                                     |
| +-----+-----+ |                                     |
+-------+-------+                                     |
        |                                             |
        +----------------------+----------------------+
                               |
                  +------------+------------+
                  |                         |
          +-------+-------+       +---------+---------+
          |    IPCHAINS   |       |      IPTABLES     |
          |     OUTPUT    |       |    POSTROUTING    |
          +-------+-------        | +-------+-------+ |
                  |               | |    mangle     | | <- MARK WRITE  
                  |               | +-------+-------+ |
                  |               | |      nat      | | <- SOURCE REWRITE
                  |               | +-------+-------+ |      SNAT or MASQUERADE
                  |               | |      IMQ      | |
                  |               | +-------+-------+ |
                  |               +---------+---------+
                  +------------+------------+
                               |
                        +------+------+
                        |     QOS     |
                        |    EGRESS   |
                        +------+------+
                               |
                    -----------+-----------
                            Network
  • Name of firewall chain (in bold)
  • Controlled by iptables/ipchains (in blue)
  • Controlled by ip/tc (in red)

My remarks on the diagram

  • Output routing : the local process selects a source address and a route. This route is attached to the packet and used later.
  • Postrouting : there is also rerouting possible if netfilter changes some parts of the packets like address, tos, ... .
  • RPDB : routing policy database, controlled by ip. That's also the place where the kernel does source validation and nexthop decision.
  • IMQ : Packets put in the imq device travel also thru the "EGRESS" part of the diagram so you can use htb/cbq to control the packets in the imq device.
  • ipchains : Yes, there is some ipchains code in kernel 2.4. If you load the ipchains module, you can't use iptables anymore. You can even load the ipfwadm module if you want ipfwadm support. So it's iptables, or ipchains, or ipfwadm, but no combination is possible.
  • mangle : since kernel 2.4.18, you have a mangle table in all 5 netfilter hooks.
  • IMQ in input comes before nat so IMQ does not know the real ip address. Ingress comes after nat, so ingress knows the real ip address.

Leonardo notes

  • The input routing determines local/forward.
  • ip rule (routing policy database RPDB) is input routing, more correctly, part of the input routing.
  • The output routing is performed from "higher layer".
  • nexthop and output device are determined both from the input and the output routing.
  • The forwarding process is called at input routing by functions from specific places in the code. It executes after input routing and does not perform nexthop/outdev selection. It's the process of receiving and sending the same packet, but in the context of all these hooks the code that sends ICMP redirects (demanded from input routing), decrements the IP TTL, performs dumb NAT and calls the filter chain. This code is used only for forwarded packets.
  • Sometimes the word "Forwarding" with "big F", is used for referencing both, the routing and forwarding process.

Updates

I remove conntrack from POSTROUTING. More info on http://iptables-tutorial.frozentux.net/iptables-tutorial.html#STATEMACHINE. See last part of section 4 : "All connection tracking is handled in the PREROUTING chain, except locally generated packets which are handled in the OUTPUT chain. What this means is that iptables will do all recalculation of states and so on within the PREROUTING chain. If we send the initial packet in a stream, the state gets set to NEW within the OUTPUT chain, and when we receive a return packet, the state gets changed in the PREROUTING chain to ESTABLISHED, and so on. If the first packet is not originated by ourself, the NEW state is set within the PREROUTING chain of course. So, all state changes and calculations are done within the PREROUTING and OUTPUT chains of the nat table."

I received this email :
Since I've recently failed in a few iproute2 experiments, I have the following comment on the packet travelling guide: It is incomplete in respect to locally generated packets.
The traversal guide states that routing actually happens before the packet enters the netfilter OUTPUT queue. However, this is not all that happens in current kernels. If the packet is somehow modified while traversing the output queue (for example, by putting a fwmark on it), netfilter recognizes that the packet needs to be routed again and does so. So, there is possibly another 'OUTPUT ROUTING' rectangle after the netfilter OUTPUT chain.
However, if I'm not mistaken, there are some problems with that (as per my recent posting to the lartc- and netdev-list, it seems that the source address is chosen before the netfilter OUTPUT is traversed, and subsequent the subsequently chosen other route's src attribute no longer affects the source address of the socket/packet).
Now, I also might be totally wrong, but so far nobody has been able to point out what exactly I'm misunderstanding...

More info

http://www.gnumonks.org/ftp/pub/doc/packet-journey-2.4.html
http://www.linuxvirtualserver.org/Joseph.Mack/HOWTO/LVS-HOWTO-19.html#ss19.21

Courtesy : http://www.docum.org/docum.org/kptd/ 

tc iptables marking (htb shape)

# Script to shape up and downlink with traffic that is already MARKed

# Delete all old stuff first
tc qdisc del dev ppp0 root    2> /dev/null > /dev/null
tc qdisc del dev ifb0 root   2> /dev/null > /dev/null
tc qdisc del dev ifb0 ingress   2> /dev/null > /dev/null
tc qdisc del dev ppp0 root 2> /dev/null > /dev/null
tc qdisc del dev ifb0 root 2> /dev/null > /dev/null
tc qdisc del dev ppp0 ingress 2> /dev/null > /dev/null
tc qdisc del dev eth0 root 2> /dev/null > /dev/null
tc qdisc del dev eth0 ingress 2> /dev/null > /dev/null

# Set variables
# The downlink should be slightly less than the ADSL line
# All the classes below should add up to this
DOWNLINK=2200
# Same for the uplink
UPLINK=330

##############################################################################
## downlink via eth0 ##

# A qdisc is a whole set of shaping rules that we apply
# Here we add a HTB qdisc to the interface eth0
# This is known as the root for the interface
# We don't set a default class to stop us shaping local eth0 traffic
# We have to shape at eth0 and not ppp0 as we can only do egress shaping
tc qdisc add dev eth0 root handle 1: htb

# Then we add to the root some overall rate limits
tc class add dev eth0 parent 1: classid 1:1 htb rate ${DOWNLINK}kbit

# Then we go through and add a number of classes to the
# root qdisc. With some qdiscs the classes are automatically
# created. With HTB they are not so we add 4 in total
# with different rate limits for each

# interactive traffic
tc class add dev eth0 parent 1:1  classid 1:10 htb \
 rate 100kbit ceil 100kbit prio 0

# web browsing
tc class add dev eth0 parent 1:1  classid 1:30 htb \
 rate 1000kbit ceil 1000kbit prio 1

# default traffic
tc class add dev eth0 parent 1:1  classid 1:40 htb \
 rate 1000kbit ceil 1000kbit prio 2

# bad boys
tc class add dev eth0 parent 1:1  classid 1:60 htb \
 rate 100kbit ceil 100kbit prio 3

# Next we add some more qdiscs. These are different priority
# qdiscs but they each sit on top of the classes already created.
# So the root qdisc dumps traffic in each class, and the qdisc
# here prioritises within that class
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10 #flow hash keys dst divisor 1024 #hash ctorigsrc
tc qdisc add dev eth0 parent 1:40 handle 40: sfq perturb 10 #hash ctorigsrc
tc qdisc add dev eth0 parent 1:60 handle 60: sfq perturb 10 #hash ctorigsrc

# Finally we attach some filters to each class
# A filter is what sends a particular packet to a particular class
# This filter uses the iptables marks to allocate them
tc filter add dev eth0 parent 1:0 protocol ip handle 10 fw flowid 1:10
tc filter add dev eth0 parent 1:0 protocol ip handle 30 fw flowid 1:30
tc filter add dev eth0 parent 1:0 protocol ip handle 40 fw flowid 1:40
tc filter add dev eth0 parent 1:0 protocol ip handle 60 fw flowid 1:60

# These are additional filters that hash based on the client IP
# This means that rather than bandwidth being shared between
# several connections (so that one client could get several times
# the bandwidth), each client gets an equal slice of the bandwidth
# regardless of the number of connections they have
tc filter add dev eth0 parent 10: protocol ip handle 10 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 30: protocol ip handle 30 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 40: protocol ip handle 40 flow hash keys nfct-dst divisor 1024
tc filter add dev eth0 parent 60: protocol ip handle 60 flow hash keys nfct-dst divisor 1024

########################################## End of downlink configuration ##########################################



###################################################################################################################
## uplink via ppp0 ##

# A qdisc is a whole set of shaping rules that we apply
# Here we add a HTB qdisc to the interface ppp0
# This is known as the root for the interface
# Default class is 40
tc qdisc add dev ppp0 root handle 1: htb default 40

# Then we add to the root some overall rate limits
tc class add dev ppp0 parent 1: classid 1:1 htb rate ${UPLINK}kbit

# Then we go through and add a number of classes to the
# root qdisc. With some qdiscs the classes are automatically
# created. With HTB they are not so we add 4 in total
# with different rate limits each

# interactive traffic
tc class add dev ppp0 parent 1:1  classid 1:10 htb \
 rate 50kbit ceil 50kbit prio 0

# web browsing
tc class add dev ppp0 parent 1:1  classid 1:30 htb \
 rate 120kbit ceil 200kbit prio 1

# default traffic
tc class add dev ppp0 parent 1:1  classid 1:40 htb \
 rate 120kbit ceil 200kbit prio 2

# bad boys
tc class add dev ppp0 parent 1:1  classid 1:60 htb \
 rate 40kbit ceil 40kbit prio 3

# Next we add some more qdiscs. These are different priority
# qdiscs but they each sit on top of the classes already created.
# So the root qdisc dumps traffic in each class, and the qdisc
# here prioritises within that class
tc qdisc add dev ppp0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev ppp0 parent 1:30 handle 30: sfq perturb 10
tc qdisc add dev ppp0 parent 1:40 handle 40: sfq perturb 10 
tc qdisc add dev ppp0 parent 1:60 handle 60: sfq perturb 10

# Finally we attach some filters to each class
# A filter is what sends a particular packet to a particular class
# This filter uses the iptables marks to allocate them
tc filter add dev ppp0 parent 1:0 prio 0 protocol ip handle 10 fw flowid 1:10
tc filter add dev ppp0 parent 1:0 prio 0 protocol ip handle 30 fw flowid 1:30
tc filter add dev ppp0 parent 1:0 prio 0 protocol ip handle 40 fw flowid 1:40
tc filter add dev ppp0 parent 1:0 prio 0 protocol ip handle 60 fw flowid 1:60

# see download comment
tc filter add dev ppp0 parent 10: protocol ip handle 10 flow hash keys nfct-src divisor 1024
tc filter add dev ppp0 parent 30: protocol ip handle 30 flow hash keys nfct-src divisor 1024
tc filter add dev ppp0 parent 40: protocol ip handle 40 flow hash keys nfct-src divisor 1024
tc filter add dev ppp0 parent 60: protocol ip handle 60 flow hash keys nfct-src divisor 1024

################################# End of uplink configuration #####################################################
 
Courtesy : http://files.andybev.com/web-portal/shape-htb

IPTables: Fun with MARK

One thing that’s always bugged me about IPTables is the lack of a way to use groups when writing rules, which can complicate things if you’ve got a potentially large rulebase. One way round this is to use something like fwbuilder, which gives you a graphical interface not unlike Checkpoint‘s SmartDashboard GUI for their Firewall-1 devices. The downside to this, though, is that the resulting IPTables ruleset is far from legible – which, to be fair, isn’t the goal of fwbuilder – and this makes hacking about with the rules nearly impossible.
So what options are there? One way is to repeat the same rule for different sources or destinations, but this can quickly get messy, especially if there’s multiple ports involved. If there was a way we could group things together and keep them tidy, maintaining the rulebase would be a lot easier. This is where MARK comes in.
The MARK target lets us set a 32-bit value (or 0xFFFFFFFF) on a packet, which we can then look for later with the mark match. This in itself can be useful, but where it gets really handy is adding the values together.

Starting out

To start off with, here’s an example
# Create a new chain, which will in effect be our source 'group'
iptables -N S-TRUSTED
# Add our sources
iptables -A S-TRUSTED -s 192.168.1.1/32 -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s 192.168.2.0/24 -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s 192.168.3.3/29 -j MARK --set-xmark 0x8/0x0

# Create a chain for the destination group
iptables -N D-DMZ
# Add our DMZ machines
iptables -A D-DMZ -d 10.1.1.1/32 -j MARK --set-xmark 0x4/0x0
iptables -A D-DMZ -d 10.1.2.0/24 -j MARK --set-xmark 0x4/0x0

# Create a chain for the services
iptables -N D-SRV-MGMT
# Add our service
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 22 -j MARK --set-xmark 0x2/0x0
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x2/0x0
So what we have now are three chains, which do the following
  • If the source matches, add 0×8 to the packet mark
  • If the destination matches, add 0×4 to the packet mark
  • If the service matches, add 0×2 to the packet mark
If you know your hexadecimal, you’ll already know that if all of these are true, we’ll come out with 0xE (or 14, in decimal).

Making the rule

Hopefully you’ll see where we’re going with this with the next example, which is our actual rule
# Create a new chain for our rule and add it to our FORWARD chain
iptables -N R-ALLOW-DMZ-MGMT
iptables -A FORWARD -j R-ALLOW-DMZ-MGMT
# Zero out the packet mark to make sure no previous rules interfere
iptables -A R-ALLOW-DMZ-MGMT -j MARK --set-xmark 0x0/0x0
# Jump to our 'source group' chain
iptables -A R-ALLOW-DMZ-MGMT -j S-TRUSTED
# Jump to our 'destination group' chain
iptables -A R-ALLOW-DMZ-MGMT -j D-DMZ
# Jump to our 'service group' chain
iptables -A R-ALLOW-DMZ-MGMT -j D-SRV-MGMT
# If the packet mark matches 0xE, then ACCEPT
iptables -A R-ALLOW-DMZ-MGMT -m mark --mark 0xE -j ACCEPT
And there we have it – if the packet matches the source, destination and service, the packet mark will be 0xE. If, say, it matches everything except the destination, it’ll come out as 0xC, which won’t match and so netfilter will carry on along the rest of the rules. If you want processing to stop here, you could always add a LOG and REJECT/DROP target at the end of the R-ALLOW-DMZ-MGMT chain.

Negation

Sometimes we want to be able to say ‘everything but that particular network’, whether it be for accepting or dropping packets. We can do that with this, too
# Add a new chain for negating the source
iptables -N S-NEGATE
# XOR the current packet mark with 0x8 - our 'source match' identifier
iptables -A S-NEGATE -j MARK --xor-mark 0x8
To use it, simply drop it in the R-ALLOW-DMZ-MGMT rule above after the jump to the S-TRUSTED chain, and if S-TRUSTED matched, it won’t any more, and vice versa. To negate the destination and service matches, you’ll need to create similar chains for (for example) D-NEGATE and D-SRV-NEGATE, replacing the 0×8 with 0×4 and 0×2 respectively.

Things to note

One downside of this method is that because of the way IPTables works, if you want to use the same set of networks and hosts as a source and a destination, you’ll need to duplicate them, but match on the source or destination as appropriate. Using the example given above, if we wanted a group with the same entries as S-TRUSTED, but matching on traffic going to them, we’d need to create another group (for example, D-TRUSTED), which will be identical save for the IP matches (which will need changing to -d) and the mask (which will need setting to 0×4 instead of 0×8).
Also, be careful if you’re using packet marks to do something outside of netfilter (say, for traffic control – which I’ll cover in a future post). One way round this is the facility to save the current packet mark to the current connection mark, or vice versa – if you go down this path then having a look at the iptables manpage for the MARK and CONNMARK targets will be useful.

Conclusions

This is an effective way of grouping hosts, networks and services within IPTables. It can be quite a bit of work to start with to add all the groups, but once in place it makes writing rules a lot more logical.

Taking things further

One way which you could take this further would be to group interfaces together in a similar fashion, say by adding 0×10 to the packet, and then matching on 0x1E rather than 0xE.
Usefully, if you send the packet out to syslog with the LOG target, netfilter will print out the current packet mark at the end of the log message as MARK=0xN, which can be useful when debugging.

Courtesy : http://andys.org.uk/bits/2010/01/27/iptables-fun-with-mark/

Using IP sets for best performance

IP sets are the best method for blocking specific countries using our IP prefix files for specific countries.
From the IPset homepage:
IP sets are a framework inside the Linux 2.4.x and 2.6.x kernel, which can be administered by the ipset utility. Depending on the type, currently an IP set may store IP addresses, (TCP/UDP) port numbers or IP addresses with MAC addresses in a way, which ensures lightning speed when matching an entry against a set.
If you want to
  • store multiple IP addresses or port numbers and match against the collection by iptables at one swoop;
  • dynamically update iptables rules against IP addresses or ports without performance penalty;
  • express complex IP address and ports based rulesets with one single iptables rule and benefit from the speed of IP sets

  • If you have a network or server with high traffic and using standard iptables for traffic blocking it may affect your server/network performance. With IPsets you can “group” multiple IP sets in groups and then match these in one swoop. This will provide you top performance.


    IP sets allows you to bind an entry in a set to another set, which forms a relationship between the set element and the set it is bound to. The sets may have a default binding, which is valid for every set element for which there is no binding defined at all.

    Example:

    ipset -N servers ipmap –network 10.10.10.0/16
    ipset -A servers 10.10.10.1
    ipset -A servers 10.10.10.2
    ipset -N ports portmap –from 1 –to 1024
    ipset -A ports 21
    ipset -A ports 22
    ipset -A ports 25
    ipset -B servers 10.10.10.2 -b ports
    iptables -A FORWARD -m set –set servers dst,dst -j ACCEPT
    iptables -A FORWARD -j DROP
    Courtesy :  http://www.ipdeny.com/blog/using-ip-sets-for-best-performance/

    Monday, September 6, 2010

    Traffic shaping TorrentFlux

    TorrentFlux is a great program/interface to download your torrents remotely on a linux machine. It is based on php and it uses a modified bittornado client to download the torrents.
    The problem: The bittornado client is able to put specific limits on a per torrent basis. That means that you can put a 100kb/sec download and 50kb/sec upload limit per torrent through torrentflux’s web interface. If you have 20 torrents though, this easily becomes 20*50=1Mb/sec upload “limit”. There are cases that you don’t want this to happen and you want both a per torrent limit (eg 50kb/sec) and a global limit (eg 300kb/sec).
    The solution: My solution is based on iptables, layer7 filter and tc (iproute2). I am using layer7 filter to pick out the bittorrent packets, iptables to mark those packets with specific values and tc to shape those marked packets into categories. Beware that the method I am using works mostly on the “uploading” part (outgoing traffic). It is not that hard to make it work for the incoming traffic as well, but it is my personal view that downloading with a few Mb/sec is not as harmfull as uploading with a few Mb/sec. I usually have my downloads seeded over many weeks…so it’s good for my ratio to have the torrent downloaded as fast as possible and then seed it endlessly. I usually like to seed until i get a ratio over 1000% per torrent (that means 10 times as much uploaded traffic than downloaded). The following example configs are created for use on a 100mbit line and keeping in mind that outgoing torrent traffic should not exceed 2-2.5Mbits (~250-300kb/sec).
    The procedure:
    0) Before you begin make sure you have the kernel sources on /usr/src/linux.
    1) Then, you need to patch your kernel for layer7 filtering and enable marking. On gentoo linux you only need to:
    #emerge -avt net-misc/l7-filter net-misc/l7-protocols
    and then configure your kernel for marking.
    Here’s how my netfilter configuration looks like:
    CONFIG_NETFILTER=y
    # CONFIG_NETFILTER_DEBUG is not set
    # CONFIG_BRIDGE_NETFILTER is not set
    #
    # Core Netfilter Configuration
    #
    # CONFIG_NETFILTER_NETLINK is not set
    CONFIG_NETFILTER_XTABLES=y
    CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
    # CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
    CONFIG_NETFILTER_XT_TARGET_MARK=m
    CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
    CONFIG_NETFILTER_XT_MATCH_COMMENT=m
    CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
    CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
    CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
    CONFIG_NETFILTER_XT_MATCH_DCCP=m
    CONFIG_NETFILTER_XT_MATCH_ESP=m
    CONFIG_NETFILTER_XT_MATCH_HELPER=m
    CONFIG_NETFILTER_XT_MATCH_LENGTH=m
    CONFIG_NETFILTER_XT_MATCH_LIMIT=m
    CONFIG_NETFILTER_XT_MATCH_MAC=m
    CONFIG_NETFILTER_XT_MATCH_MARK=m
    # CONFIG_NETFILTER_XT_MATCH_POLICY is not set
    CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
    CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
    # CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
    CONFIG_NETFILTER_XT_MATCH_REALM=m
    CONFIG_NETFILTER_XT_MATCH_SCTP=m
    CONFIG_NETFILTER_XT_MATCH_STATE=m
    # CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
    CONFIG_NETFILTER_XT_MATCH_STRING=m
    CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
    #
    # IP: Netfilter Configuration
    #
    CONFIG_IP_NF_CONNTRACK=m
    CONFIG_IP_NF_CT_ACCT=y
    CONFIG_IP_NF_CONNTRACK_MARK=y
    # CONFIG_IP_NF_CONNTRACK_EVENTS is not set
    CONFIG_IP_NF_CT_PROTO_SCTP=m
    CONFIG_IP_NF_FTP=m
    CONFIG_IP_NF_IRC=m
    CONFIG_IP_NF_NETBIOS_NS=m
    CONFIG_IP_NF_TFTP=m
    CONFIG_IP_NF_AMANDA=m
    CONFIG_IP_NF_PPTP=m
    CONFIG_IP_NF_H323=m
    CONFIG_IP_NF_SIP=m
    CONFIG_IP_NF_QUEUE=m
    CONFIG_IP_NF_IPTABLES=y
    CONFIG_IP_NF_MATCH_IPRANGE=y
    CONFIG_IP_NF_MATCH_LAYER7=m
    # CONFIG_IP_NF_MATCH_LAYER7_DEBUG is not set
    CONFIG_IP_NF_MATCH_TOS=y
    CONFIG_IP_NF_MATCH_RECENT=m
    CONFIG_IP_NF_MATCH_ECN=m
    CONFIG_IP_NF_MATCH_DSCP=m
    CONFIG_IP_NF_MATCH_AH=m
    CONFIG_IP_NF_MATCH_TTL=m
    CONFIG_IP_NF_MATCH_OWNER=m
    CONFIG_IP_NF_MATCH_ADDRTYPE=m
    CONFIG_IP_NF_MATCH_HASHLIMIT=m
    CONFIG_IP_NF_FILTER=y
    CONFIG_IP_NF_TARGET_REJECT=y
    CONFIG_IP_NF_TARGET_LOG=y
    CONFIG_IP_NF_TARGET_ULOG=m
    CONFIG_IP_NF_TARGET_TCPMSS=y
    CONFIG_IP_NF_NAT=m
    CONFIG_IP_NF_NAT_NEEDED=y
    CONFIG_IP_NF_TARGET_MASQUERADE=m
    CONFIG_IP_NF_TARGET_REDIRECT=m
    CONFIG_IP_NF_TARGET_NETMAP=m
    CONFIG_IP_NF_TARGET_SAME=m
    CONFIG_IP_NF_NAT_SNMP_BASIC=m
    CONFIG_IP_NF_NAT_IRC=m
    CONFIG_IP_NF_NAT_FTP=m
    CONFIG_IP_NF_NAT_TFTP=m
    CONFIG_IP_NF_NAT_AMANDA=m
    CONFIG_IP_NF_NAT_PPTP=m
    CONFIG_IP_NF_NAT_H323=m
    CONFIG_IP_NF_NAT_SIP=m
    CONFIG_IP_NF_MANGLE=m
    CONFIG_IP_NF_TARGET_TOS=m
    CONFIG_IP_NF_TARGET_ECN=m
    CONFIG_IP_NF_TARGET_DSCP=m
    CONFIG_IP_NF_TARGET_TTL=m
    CONFIG_IP_NF_TARGET_CLUSTERIP=m
    # CONFIG_IP_NF_RAW is not set
    CONFIG_IP_NF_ARPTABLES=m
    CONFIG_IP_NF_ARPFILTER=m
    CONFIG_IP_NF_ARP_MANGLE=m
    You can clearly see layer7 being enabled as a module: CONFIG_IP_NF_MATCH_LAYER7=m
    Rebuild your kernel and install the proper modules. If you need to reboot your machine to apply the new kernel do it now.
    2) Now it’s time to install iptables and iproute2 if you don’t have them already. On gentoo linux:

    #echo "net-firewall/iptables extensions l7filter" >> /etc/portage/package.use
    #emerge -avt net-firewall/iptables sys-apps/iproute2

    3) Now it’s the iptables marking time. I am going to show you (some of) the output of my iptables-save command. Change it to fit your neeeds:

    # Generated by iptables-save v1.3.5 on Fri Jan 12 20:50:52 2007
    *mangle
    :P REROUTING ACCEPT [1102387:193393325]
    :INPUT ACCEPT [1102372:193390208]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [2100485:2922693566]
    :P OSTROUTING ACCEPT [2100483:2922690566]
    -A PREROUTING -s IP.OF.MACHINE -p tcp -m multiport --sports 22,80 -j MARK --set-mark 1001
    -A PREROUTING -d IP.OF.MACHINE -p tcp -m multiport --dports 22,80 -j MARK --set-mark 1001
    -A PREROUTING -m layer7 --l7proto ssh -j MARK --set-mark 1001
    #-A PREROUTING -m layer7 --l7proto bittorrent -j MARK --set-mark 11090
    -A PREROUTING -m mark --mark 1001 -j RETURN
    -A POSTROUTING -s IP.OF.MACHINE -p tcp -m multiport --sports 22,80 -j MARK --set-mark 1001
    -A POSTROUTING -d IP.OF.MACHINE -p tcp -m multiport --dports 22,80 -j MARK --set-mark 1001
    -A POSTROUTING -m mark --mark 1001 -j RETURN
    -A POSTROUTING -m connmark --mark 0x0 -j MARK --set-mark 11030
    -A POSTROUTING -m layer7 --l7proto dns -j MARK --set-mark 11010
    -A POSTROUTING -m layer7 --l7proto ssh -j MARK --set-mark 11010
    -A POSTROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 11010
    -A POSTROUTING -p icmp -j MARK --set-mark 11010
    -A POSTROUTING -m layer7 --l7proto bittorrent -j MARK --set-mark 11090
    COMMIT
    # Completed on Fri Jan 12 20:50:52 2007
    # Generated by iptables-save v1.3.5 on Fri Jan 12 20:50:52 2007
    *nat
    :P REROUTING ACCEPT [407:30699]
    :P OSTROUTING ACCEPT [111:6662]
    :OUTPUT ACCEPT [111:6662]
    COMMIT
    # Completed on Fri Jan 12 20:50:52 2007
    # Generated by iptables-save v1.3.5 on Fri Jan 12 20:50:52 2007
    *filter
    :INPUT ACCEPT [266369:32040284]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [479227:676859047]
    COMMIT
    # Completed on Fri Jan 12 20:50:52 2007

    You need to change IP.OF.MACHINE with the IP of your linux box.
    4) And now the traffic shaping part:

    # Main Link
    LINK=100000
    SHAPEDLINK=50000
    # High Priority
    HIGHPRIO=10000
    HIGHPRIO_MAX=$SHAPEDLINK
    # Normal
    NORMAL=512
    NORMAL_MAX=$SHAPEDLINK
    # Downloads
    TOR=512
    TOR_MAX=2048
    # del old
    tc qdisc del dev $DEV root 2> /dev/null > /dev/null
    # add root
    tc qdisc add dev $DEV root handle 100: htb default 1
    tc class add dev $DEV parent 100: classid 100:1 htb rate ${LINK}kbit
    tc qdisc add dev $DEV parent 100:1 handle 1: htb
    tc class add dev $DEV parent 1: classid 1:1 htb rate ${SHAPEDLINK}kbit
    # some more rules
    tc class add dev $DEV parent 100: classid 100:1 htb rate ${LINK}kbit
    tc qdisc add dev $DEV parent 100:1 sfq perturb 10
    tc filter add dev $DEV parent 100:0 protocol ip prio 1 handle 1001 fw flowid 100:1
    tc class add dev $DEV parent 1:1 classid 1:10 htb rate ${SHAPEDLINK}kbit ceil ${SHAPEDLINK}kbit prio 5
    tc qdisc add dev $DEV parent 1:10 sfq perturb 10
    # High priority
    tc class add dev $DEV parent 1:10 classid 1:1010 htb rate ${HIGHPRIO}kbit ceil ${HIGHPRIO_MAX}kbit prio 0
    tc qdisc add dev $DEV parent 1:1010 sfq perturb 10
    tc filter add dev $DEV parent 1:0 protocol ip prio 0 handle 11010 fw flowid 1:1010
    # normal
    tc class add dev $DEV parent 1:10 classid 1:1030 htb rate ${NORMAL}kbit ceil ${NORMAL_MAX}kbit prio 5
    tc qdisc add dev $DEV parent 1:1030 sfq perturb 10
    tc filter add dev $DEV parent 1:0 protocol ip prio 5 handle 11030 fw flowid 1:1030
    # bittorent
    tc class add dev $DEV parent 1:10 classid 1:1090 htb rate ${TOR}kbit ceil ${TOR_MAX}kbit prio 10
    tc qdisc add dev $DEV parent 1:1090 sfq perturb 10
    tc filter add dev $DEV parent 1:0 protocol ip prio 10 handle 11090 fw flowid 1:1090

    The rules are pretty straightforward…so I am not going to fully explain them. The basic concept is that you create a “shaped” partition of your bandwith and you add classes (high priority, normal , bittorrent) there. The trick is that you can skip anything you don’t want shaped by marking it with iptables 1001 mark.
    In my iptables example above, I mark as 1001 the outgoing ssh and http traffic. This way I can shape the seeding of my torrents using TorrentFlux but I can download via http without any traffic shaping the torrents to my PC at home. I can also ssh to the machine without any latency caused by the shaping because the sshd port (22) is marked with 1001.
    The only problem I faced with those scripts was that sometimes the layer7 filter for bittorrent let’s some torrent traffic pass by. My solution to that was to change NORMAL_MAX=$SHAPEDLINK to NORMAL_MAX=2048 for example. Then, even “normal traffic” was shaped. Remember that anything I didn’t want shaped, was marked as 1001 on the iptables script…so the machine was still very responsive even after shaping the “normal traffic”.
    To check how your scripts are doing in terms of shaping you can download this excellent perl script: tc-viewer. Click here for a screenshot: tc-viewer htb screenshot
    The above example configs are very very generic. If you have a server that serves many other duties apart from ssh, http and bittorrent, then this script might not work out of the box for you.
    *Update*
    It looks like the problem I had with layer7 bittorrent filter missing packets was not actually a layer7’s “problem”, but rather a new feature of the latest version of bittornado. I was using bittornado version 0.3.18 (experimental) which is the first bittornado version that comes with Message_Stream_Encryption. What this means: whenever bittornado finds another peer with encryption capabilities, it encrypts all traffic between you and the other peer, so the layer7 filter cannot understand that these flows are torrent traffic anymore, and categorizes them as “normal” traffic. That’s why I needed to “shape” normal traffic as well.
    There are three ways to cope with encrypted bittorrent traffic. The first one is the one I described above without even knowing about it (shaping normal traffic). The second way is to go back to a version without encryption (0.3.17), which I think is a _really_ bad idea. Encryption came to help us hide our traffic from ISP filters, and is a step we can all take to protect ourselves. The third way is to mark the port range that torrentflux uses (check the admin panel of torrentflux for it) as torrent traffic by our iptables script. If the port range is high enough it can be almost certain that no other service will use those ports, so no priority traffic will be mis-matched as “torrent traffic”. If, for example, you have defined your port range to be from port 61000 to port 63000, then inject a command like:
    -A POSTROUTING -p tcp --sport 61000:63000 -j MARK --set-mark 11090
    just below the
    -A POSTROUTING -m layer7 --l7proto bittorrent -j MARK --set-mark 11090
    command of the iptables script above.
    Enjoy shaped encrypted bittorent uploads! Keep seeding…


    Source : http://www.void.gr/kargig/blog/2007/01/14/traffic-shaping-torrentflux/