I read a lot of people trying to divide up bandwidth like its a pie but that kind of paradigm probably won't give you any results you want. Let me put a better metaphor on you. Bandwidth is like the line at the bank and your data packets are the people in line and it is important that some of these people get done at the bank ASAP, while others don't mind to wait a few minutes longer. Most bank tellers (the metaphorical dequeing agents) are not going to make any distinction between the different people in the line and take them one at a time. If that bank wanted to, it could tell when there was a VIP in a hurry standing in line and they wouldn't make him wait in line, but instead bring him to the front.
That is what shaping will allow you to do, but this metaphor can only be applied to your upstream. You cannot shape your downstream at all, it can not be done. Say it with me: "there is no such thing as downstream shaping". I don't mean that theres sometimes some things you can do to shape downstream. It is simply not possible with the way things are so don't get it in your head that you can control what data is sent to you over your internet connection first because you will just drive yourself bonkers.
I'm not saying that it is impossible to manipulate the system in your favor a little, however. Consider the fact that most ISP's will queue your downstream packets to speed up the download on your end. Back to the bank line metaphor, that is like a bank having a really long roped off line in front of the teller desk. If people are just pouring into this bank lobby, the VIP's are going to be waiting all day and he's got to be out of there on time! Also, if the bank's lobby were full of people, then everyone who would come to the door would just leave and come back later when there was space. That is exactly what happens to a full network link, packets are just ignored and if those packets are TCP, then they are just retransmitted later. In properly functioning TCP/IP stacks, senders of tcp packets will adjust their rate of transmission when there is packet loss. If you're still with me on the bank metaphor then the solution here is pretty obvious, you need to make your line as short as possible and make these people come back later, then any VIP coming in won't have to wait very long. In a nutshell, this means making your router accept data at a significantly slower rate than it is actually capable of--this rate limiting is called "policing".
Assuming all the remote ends of your connections have proper TCP/IP software, they will reduce their rate of transmission and that will eliminate or significantly reduce the amount of queueing at your ISP and thus lead to less latency for your very important packets.
I've been using the wondershaper script for a long time now but I've been concerned with the possibility that the ingress policer will drop ICMP, UDP, or interactive TCP traffic unnecessarily. If you know the traffic is of high importance, why make the sender resend it? ICMP and UDP packets would not get retransmitted anyway, so why drop them? It never sat right with me.
So I have this script for you. It uses HTB for upstream shaping. The TXRATE variable is set to 340 kbits/second, only 44kbits/second less than what my cable modem is rated for. The RXRATE, however, is set to 3 mbits/second and considering that my modem is rated for 4 mbits/second downstream bandwidth, this is a significant reduction in bandwidth--and it still might not be enough of a reduction, it depends on how much queueing my ISP does. I just tweak it when I need to.
For upstream shaping, I have 3 classes of traffic: high priority, low priority, and default. TCP ACK, and ICMP are put in the high priority class by default. Any packets that have been tagged by my firewall as "Minimum-delay" in the ToS section of the IP header is also put into this class. Packets marked by my firewall as "Maximum-thoughput" are put in the low priority field. Everything else is in the default class. For downstream, ICMP, UDP, and TCP traffic from certain ports are dequeued normally but all other TCP traffic is strictly limited to 3mbits/second, dropping the excess packets.
Code: Select all
#!/bin/bash
# Heavy Metal Traffic Control (HMTC)
# Erik Elmore
# Based on concepts from WonderShaper
### Configuration ###
# Set this to your WAN interface
INTERFACE=eth0
# Upstream Parameters #
# True maximal upstream rate in kbits/second
MAXTXRATE=384
# Limit upstream rate to this percentage of the max rate
TXFACTOR=90
# Guaranteed percentage for high priority class
HIGHBWFACTOR=60
# Guaranteed percentage for default class
NORMBWFACTOR=30
# Upstream burst in kbits for default class
#TXBURST=8k
# Guaranteed bandwidth for low priority class in kbits/second
LOWBWRATE=1
# Downstream Parameters #
# True maximal downstream rate in kbits/second
MAXRXRATE=4000
# Police ingress at this percentage of the max rate
RXFACTOR=85
# Set downstream burst in kbits
RXBURST=15k
# Comment this line out after the script has been configured
# echo "Traffic control script requires configuration" && exit
### End of configuration ###
TXRATE=$[$MAXTXRATE*$TXFACTOR/100]
HIGHBWRATE=$[$TXRATE*$HIGHBWFACTOR/100]
NORMBWRATE=$[$TXRATE*$NORMBWFACTOR/100]
RXRATE=$[$MAXRXRATE*$RXFACTOR/100]
if [ "$1" = "status" ]; then
echo "Configuration for $INTERFACE:"
echo "Upstream bandwidth limited to $TXRATE kbps (${TXFACTOR}% of $MAXTXRATE kbps)"
echo "$HIGHBWRATE kbps (${HIGHBWFACTOR}% of upstream bandwidth limit) guaranteed for high priority"
echo "$NORMBWRATE kbit/s (${NORMBWFACTOR}% of upstream bandwidth limit) guaranteed for normal priority"
echo "$LOWBWRATE kbit/s is guaranteed for low priority"
echo "$[$TXRATE-$HIGHBWRATE-$NORMBWRATE] kbit/s not committed ($[100-$HIGHBWFACTOR-$NORMBWFACTOR]% of $TXRATE kbit/s)"
echo
echo "Downstream bandwidth policed at $RXRATE kbit/s (${RXFACTOR}% of $MAXRXRATE)"
echo
echo "Qdiscs for $INTERFACE:"
tc -s qdisc ls dev $INTERFACE
echo
echo "Classes for $INTERFACE:"
tc -s class ls dev $INTERFACE
exit
fi
# Erase all existing TC settings, suppress output.
tc qdisc del dev $INTERFACE root > /dev/null 2>&1
tc qdisc del dev $INTERFACE ingress > /dev/null 2>&1
if [ "$1" = "stop" ]; then exit; fi
### Egress shaping ###
# Create root HTB qdisc, default to 1:20
tc qdisc add dev $INTERFACE root handle 1: htb \
default 30
# Limit egress speed to TXRATE
tc class add dev $INTERFACE parent 1: classid 1:1 htb \
rate ${TXRATE}kbit
# Create high priority class 1:10
tc class add dev $INTERFACE parent 1:1 classid 1:10 htb \
rate ${HIGHBWRATE}kbit \
ceil ${TXRATE}kbit \
prio 1
# Create normal class 1:20
tc class add dev $INTERFACE parent 1:1 classid 1:20 htb \
rate ${NORMBWRATE}kbit \
ceil ${TXRATE}kbit \
prio 2
# Create low priority class 1:30
tc class add dev $INTERFACE parent 1:1 classid 1:30 htb \
rate ${LOWBWRATE}kbit \
ceil ${TXRATE}kbit \
prio 3
# Packets with ToS "Minimum Delay" (0x10) flag get high priority
tc filter add dev $INTERFACE parent 1:0 protocol ip prio 10 u32 \
match ip tos 0x10 0x10 \
flowid 1:10
# ICMP packets get high priority
tc filter add dev $INTERFACE parent 1:0 protocol ip prio 10 u32 \
match ip protocol 0x01 0xff \
flowid 1:10
# ACK packets get high priority
tc filter add dev $INTERFACE parent 1: protocol ip prio 10 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
# Packets with ToS "Maximum Throughput" (0x08) flag get low priority
tc filter add dev $INTERFACE parent 1:0 protocol ip prio 14 u32 \
match ip tos 0x08 0x08 \
flowid 1:20
# All other traffic gets normal priority
#tc filter add dev $INTERFACE parent 1: protocol ip prio 18 u32 \
# match ip dst 0.0.0.0/0 \
# flowid 1:20
# Enable stochastic fairness for each class
tc qdisc add dev $INTERFACE parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev $INTERFACE parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev $INTERFACE parent 1:30 handle 30: sfq perturb 10
### Ingress policing ###
# Enable ingress qdisc
tc qdisc add dev $INTERFACE handle ffff: ingress
# ICMP packets are never dropped
tc filter add dev $INTERFACE parent ffff: protocol ip prio 40 u32 \
match ip protocol 0x01 0xff \
flowid :1
# UDP packets are never dropped
tc filter add dev $INTERFACE parent ffff: protocol ip prio 40 u32 \
match ip protocol 0x11 0xff \
flowid :1
# Ventrilo packets are never dropped
tc filter add dev $INTERFACE parent ffff: protocol ip prio 40 u32 \
match ip sport 3784 0xffff \
flowid :1
# Guild Wars packets are never dropped
tc filter add dev $INTERFACE parent ffff: protocol ip prio 40 u32 \
match ip sport 6112 0xffff \
flowid :1
# Add other rules based on port, change XXXX to the appropriate value
#tc filter add dev $INTERFACE parent ffff: protocol ip prio 40 u32 \
# match ip sport XXXX 0xffff \
# flowid :1
# Drop all other packets coming in faster than RXRATE
tc filter add dev $INTERFACE parent ffff: protocol ip prio 50 u32 \
match ip src 0.0.0.0/0 \
police rate ${RXRATE}kbit \
burst $RXBURST \
drop \
flowid :1Code: Select all
#SOURCE DEST PROTOCOL SOURCE PORTS DEST PORTS TOS
# Teamspeak
all all udp - 8767 16
# Ventrilo
all all tcp - 3784 16
# GuildWars
all all tcp - 6112 16
# City of Heroes
all all udp - 7000:7100 16
# WWW
all all tcp 80 - 8
all all tcp - 80 8
#LAST LINE -- Add your entries above -- DO NOT REMOVE
Notice that port 22 (ssh) is NOT listed here. Having the firwall flag ALL port 22 traffic as high priority is a bad idea because you will find that SCP file transfers (which use port 22) will also be tagged for high priority and thus introduce latency for the real high priority traffic. Don't worry about suffering latency on your ssh sessions though! The ssh client and server software will automatically set the ToS "Minimum-delay" flag on appropriate packets and not set them for SCP packets!
Likewise, it is very easy to add preferred kinds of traffic to the ingress policer. It is more or less just adding lines like the one indicated in the script as long as you're identifying it by the source port of the incoming packet.
Important notes about ingress policing:
- It is very important to understand that this is not shaping. It is still just dropping packets coming in faster than the set rate, just without dropping the important stuff.
- It is mportant not to protect too much TCP traffic from the policer because that can lead to the ISP queueing up packets. Protect only the super time-sensitive packets from policing.
- It is probably NOT a good idea protect TCP port 22 (ssh) from ingress policing. The policing alone will be very adequate for minimizing latency in ssh sessions. Along the same lines as above, SCP file transfers will EASILY dominate your downstream and actually CAUSING latency if port 22 were protected from policing.
- TeamSpeak2 and City of Heroes (as well as some other stuff) both use UDP, so don't worry about adding UDP ports to this list at all because they're already protected from being dropped.
[Edit]
Required software considerations
Your kernel will need to have the QoS features enabled. They are found under "QoS and/or fair queueing":
Code: Select all
(this is 2.6 kernel, but the 2.4 kernel has these features also)
+ Device Drivers
|--+ Networking support
|--+ Networking support
|--+ Networking options
|--> QoS and/or fair queueing
You will also need iproute2 installed, this is available in Portage.
Code: Select all
router ~ # emerge iproute2 -a


