Showing posts with label shaping. Show all posts
Showing posts with label shaping. Show all posts

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/

more netroute2 hacks – new traffic shaper

On my previous post, more netroute2 hacks – high availability, one of the changed files was the dial_conn file. At the end of the diff there was a line with a # in front:
+ sleep 5
+ #/etc/bin/wshaper ppp0 192 1024

Inside netroute2 one can find the /etc-ro/ppp/wshaper file which is the traffic shaping script of the modem/router. Unfortunately it resides in the read-only section of the router so you can’t make changes directly to it. What I did was to make a copy of it on the writable /etc/bin/ and change a line in my /etc/bin/dial_conn to call it from there, right after (5 seconds later) the connection with the ISP has been established.
If you have followed the previous post about high availability the only thing you need to change is to edit your /etc/bin/dial_conn file and remove the # from the live above. Else…read the previous post :)
The first argument of the script is the device the rules will apply to, the second argument is the upload speed and the third is the download speed. Netroute2’s own traffic shaping script gets the 3 arguments while syncing with the dslam. The problem with adsl lines here in Greece, and I guess in many other countries as well, is that the speed the modem syncs with the dslam has nothing to do with the real speed you actually get. So shaping for 256kbit upload while never reaching more than 200 is a bit foolish imho. What I did was lower the upload so that I am always (or mostly always) sure that this is my max upload speed at the time. I can now create rules based on the assumption that my upload speed is 192kbit. If the upload speed your modem syncs is 192kbit I would advise you not to put more than 128kbit as the first argument. It’s a trial and error situation.
While lowering my shaped upload speed and keeping the rest of the script intact already made a difference I knew that I could do some more tweaking.
The first thing one has to know before creating any traffic shaping script is to learn what the TOS field is:
#TOS FIELD
# 0×10 – (minimize delay)
# 0×08 (maximize throughput),
# 0×04 (maximize reliability),
# 0×02 (minimize cost)
# 0×00 (best effort)
You can then create rules with iptables to change the TOS field of certain packets, for example:
$IPTABLES -t mangle -A POSTROUTING -o $DEV -p tcp --syn -m length --length 40:68 -j TOS --set-tos 0x10
$IPTABLES -t mangle -A POSTROUTING -o $DEV -p tcp --tcp-flags ALL ACK,FIN -j TOS --set-tos 0x10

A great rule to add to any of your scripts is to speed up ACK packets,(2) by adding them to the highest priority class (on netroute2 that’s 1:10):
$TC filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip protocol 6 0xff \
match u8 0x05 0x0f at 0 \
match u16 0x0000 0xffc0 at 2 \
match u8 0x10 0xff at 33 \
flowid 1:10

What is also very very helpfull is to specify the port your torrent client uses (eg 17777) and add it to the lowest priority class (on netroute2 that 1:30):
$TC filter add dev $DEV parent 1:0 protocol ip prio 3 u32 match ip sport 17777 0xffff flowid 1:30
$TC filter add dev $DEV parent 1:0 protocol ip prio 3 u32 match ip dport 17777 0xffff flowid 1:30

Of course you can create your own classes inside /etc/bin/wshaper. If you are carefull enough with the rules you add you will be more than happy with the result :)
To monitor how your traffic shaping is going you can download a great perl script from here: http://qos.kallenberg.dk/ called qos.pl. This script reads a machine’s qos classes and priorities and creates graphs like the ones on the site. The problem with netroute2 is that it doesn’t have perl included, so one has to modify qos.pl to make it read netroute2’s qos performance while running from another machine. This is done by making the script run its commands through ssh-ing to netroute2 using public key auth. If you don’t know how to enable this on netroute2 please read part F of my older post: Intracom netroute2 hacks/.
What you need to change on the qos.pl script is:
a) change the $tc line with something like this:
$tc = "ssh root\@NETROUTE2.IP.GOES.HERE /usr/sbin/tc";
b) Find any occurances of “eth2″ and replace with “ppp0″ (there must be 2 occurances only).
now run the qos.pl script and it will start creating some graphs (png files) and an index.html on the directory from which you executed it. qos.pl depends on gnuplot, so you must install it before you run it.
The graphs are a great visual aid to to tweak your new traffic shaping script more and more.

Source : http://www.void.gr/kargig/blog/2007/07/12/more-netroute2-hacks-new-traffic-shaper/

traffic shaping a dsl line with linux

The case is like this:
[code]
Internet < --> [dsl modem] < --> [linux box] < --> [Lan]
[/code]
DSL modem is connected on eth2 on linux box and the rest of the Lan on eth0. I had a serious problem with people leaving edonkey clients opens all night..limiting the download speed to 20kb/sec but forgetting to limit the upload. The current dsl line is 384/128 so having the uploads unlimited…is like killing the line.
The solution was to setup a QOS script. And here it is:
[code]
#!/bin/bash
DEV="eth2"
LOCALIF="eth2"
# Reset everything to a known state (cleared)
tc qdisc del dev $DEV root 2> /dev/null > /dev/null
tc qdisc del dev imq0 root 2> /dev/null > /dev/null
iptables -t mangle -F POSTROUTING 2> /dev/null > /dev/null
iptables -t mangle -Z POSTROUTING 2> /dev/null > /dev/null
iptables -t mangle -X POSTROUTING 2> /dev/null > /dev/null
iptables -t mangle -F tosfix
iptables -t mangle -F ack
ip link set imq0 down 2> /dev/null > /dev/null
rmmod imq 2> /dev/null > /dev/null
if [ "$1" = "stop" ]
then
echo "Shaping removed on $DEV."
exit
fi
tc qdisc add dev $DEV root handle 1: tbf rate 85kbit burst 1600 limit 1
tc qdisc add dev $DEV parent 1:1 handle 2: prio bands 4
tc qdisc add dev $DEV parent 2:1 handle 10: sfq perturb 10
tc qdisc add dev $DEV parent 2:2 handle 20: sfq perturb 10
tc qdisc add dev $DEV parent 2:3 handle 30: sfq perturb 10
tc qdisc add dev $DEV parent 2:4 handle 40: tbf rate 40kbit burst 1600 limit 3000
tc qdisc add dev $DEV parent 40:1 handle 41: pfifo limit 10
iptables -t mangle -N tosfix
iptables -t mangle -A tosfix -p tcp -m length --length 0:64 -j RETURN
iptables -t mangle -A tosfix -m limit --limit 2/s --limit-burst 10 -j RETURN
iptables -t mangle -A tosfix -j TOS --set-tos Maximize-Throughput
iptables -t mangle -A tosfix -j RETURN
iptables -t mangle -N ack
iptables -t mangle -A ack -m tos ! --tos Normal-Service -j RETURN
iptables -t mangle -A ack -p tcp -m length --length 0:64 \
-j TOS --set-tos Minimize-Delay
iptables -t mangle -A ack -p tcp -m length --length 64: \
-j TOS --set-tos Maximize-Throughput
iptables -t mangle -A ack -j RETURN
# Is our TOS broken? Fix it for TCP ACK and OpenSSH.
iptables -t mangle -A POSTROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK ACK -j ack
iptables -t mangle -A POSTROUTING -p tcp -m tos --tos Minimize-Delay -j tosfix
# Here we deal with ACK, SYN, and RST packets
# Match SYN and RST packets
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp -m tcp --tcp-flags ! SYN,RST,ACK ACK \
-j CLASSIFY --set-class 2:1
# Match ACK packets
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp -m tcp --tcp-flags SYN,RST,ACK ACK \
-m length --length :128 -m tos --tos Minimize-Delay \
-j CLASSIFY --set-class 2:1
# Match packets with TOS Minimize-Delay
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp -m tos --tos Minimize-Delay \
-j CLASSIFY --set-class 2:1
### Actual traffic shaping classifications with CLASSIFY
# ICMP (ping)
iptables -t mangle -A POSTROUTING -o $LOCALIF -p icmp -j CLASSIFY --set-class 2:1
# Outbound client requests for HTTP, IRC and AIM (dport matches)
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp --dport 80 -j CLASSIFY --set-class 2:2
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp --dport 6667 -j CLASSIFY --set-class 2:2
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp --dport 5190 -j CLASSIFY --set-class 2:2
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp --sport 80 -j CLASSIFY --set-class 2:3
iptables -t mangle -A POSTROUTING -o $LOCALIF -p tcp --dport 1024: -j CLASSIFY --set-class 2:4
[/code]

It WORKS for me…I don’t know whether it will work for you though. I take no responsibility. I will explain it no further because comments do exists and it’s really easy to understand what’s going on if you read a couple of tc tutorials from the net. Many ideas about this script were “stolen” from other scripts I studied while trying to make mine.
Have fun with it…

Source : http://www.void.gr/kargig/blog/2005/07/27/traffic-shaping-a-dsl-line-with-linux/