Ugly Stool Rotating Header Image

April, 2009:

Prioritizing ACKs on DD-WRT

I used to used to run the Tomato firmware on my home router, but its hardware support is limited.  If you have the correct hardware, Tomato is great and I recommend it.  A nice feature of Tomato is its simplicity, especially around traffic shaping.  Tomato has a single checkbox to enable ACK prioritization.  Prioritizing ACKs allows you to better utilize your upload and download bandwidth.  It prevents a big download from dramatically slowing down web browsing.

I bought a new router that was not supported by Tomato, so I used the next best solution DD-WRT.  Unfortunately, DD-WRT does not support ACK prioritization out of the box.  Support can easily be added by customizing and uploading the following script to DD-WRT.

I am familiar with pf, but not with iptables.  I stole this script from someone on the Internet, and I modified it for my needs.  (I cannot find the original author otherwise I would have linked to him or her.)

IPT=/usr/sbin/iptables
TC=/usr/sbin/tc
PRIORITY_ACK=1
PRIORITY_PREMIUM=2
PRIORITY_EXPRESS=3
PRIORITY_STANDARD=4
PRIORITY_BULK=5
UPLOAD=2400
L7_PREM=""
L7_EXPR=""
L7_STND=""
L7_BULK=""
IPP2P_BITTORRENT="ipp2p"
TCP_PREM="22 3389"
UDP_PREM=""
TCP_EXPR="53"
UDP_EXPR="53"
TCP_STND="21 80 443 8080"
UDP_STND=""
TCP_BULK="20 25 6881:6889"
UDP_BULK=""
QOS_IF=$(nvram get wan_ifname)
DBURST_D=10
DBURST_U=10
MTU=1600
MIN_RATE=10
$IPT -t mangle -F
$IPT -t mangle -X
sed -n 's/ *\(.*\):.*/\1/p' /proc/net/dev | while read INTERFACE; do
    $TC qdisc del dev $INTERFACE root >&- 2>&-
done
BURST_U=$(($DBURST_U*$UPLOAD/8))
[ $BURST_U -lt $((1*$MTU)) ] && BURST_U=$((1*$MTU))
RTOQ_U=$(($MIN_RATE*$UPLOAD*10/(8*$MTU)))
[ $RTOQ_U -gt 20 ] && RTOQ_U=20
[ $RTOQ_U -eq 0 ] && RTOQ_U=1
$TC qdisc add dev $QOS_IF root handle 1: htb default 40 r2q $RTOQ_U
$TC class add dev $QOS_IF parent 1: classid 1:1 htb rate ${UPLOAD}kbit ceil ${UPLOAD}kbit burst $(($BURST_U*3)) cburst $(($BURST_U*3)) mtu $MTU
$TC class add dev $QOS_IF parent 1:1 classid 1:10 htb rate $(($UPLOAD*5/10))kbit ceil ${UPLOAD}kbit burst $(($BURST_U*1)) cburst $(($BURST_U*1)) prio 1 mtu
 $MTU
$TC class add dev $QOS_IF parent 1:1 classid 1:20 htb rate $(($UPLOAD*2/10))kbit ceil ${UPLOAD}kbit burst $(($BURST_U*1)) cburst $(($BURST_U*1)) prio 2 mtu
 $MTU
$TC class add dev $QOS_IF parent 1:1 classid 1:30 htb rate $(($UPLOAD*2/10))kbit ceil ${UPLOAD}kbit burst $(($BURST_U*1)) cburst $(($BURST_U*1)) prio 3 mtu
 $MTU
$TC class add dev $QOS_IF parent 1:1 classid 1:40 htb rate $(($UPLOAD*1/10))kbit ceil ${UPLOAD}kbit burst $(($BURST_U*1)) cburst $(($BURST_U*1)) prio 4 mtu
 $MTU
$TC class add dev $QOS_IF parent 1:1 classid 1:50 htb rate $(($UPLOAD*1/10))kbit ceil ${UPLOAD}kbit burst $(($BURST_U*1)) cburst $(($BURST_U*1)) prio 5 mtu
 $MTU
$TC qdisc add dev $QOS_IF parent 1:10 sfq quantum $MTU perturb 10
$TC qdisc add dev $QOS_IF parent 1:20 sfq quantum $MTU perturb 10
$TC qdisc add dev $QOS_IF parent 1:30 sfq quantum $MTU perturb 10
$TC qdisc add dev $QOS_IF parent 1:40 sfq quantum $MTU perturb 10
$TC qdisc add dev $QOS_IF parent 1:50 sfq quantum $MTU perturb 10
$TC filter add dev $QOS_IF parent 1: prio 1 protocol ip handle 1 fw flowid 1:10
$TC filter add dev $QOS_IF parent 1: prio 2 protocol ip handle 2 fw flowid 1:20
$TC filter add dev $QOS_IF parent 1: prio 3 protocol ip handle 3 fw flowid 1:30
$TC filter add dev $QOS_IF parent 1: prio 4 protocol ip handle 4 fw flowid 1:40
$TC filter add dev $QOS_IF parent 1: prio 5 protocol ip handle 5 fw flowid 1:50
$IPT -t mangle -N mark_chain
$IPT -t mangle -N egress_chain
$IPT -t mangle -A POSTROUTING -o $QOS_IF -j egress_chain
$IPT -t mangle -A mark_chain -m mark --mark 0 -j CONNMARK --restore-mark
for PORT in $UDP_PREM; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p udp --dport $PORT -j MARK --set-mark $PRIORITY_PREMIUM
done
for PORT in $TCP_PREM; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p tcp --dport $PORT -j MARK --set-mark $PRIORITY_PREMIUM
done
for PORT in $UDP_EXPR; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p udp --dport $PORT -j MARK --set-mark $PRIORITY_EXPRESS
done
for PORT in $TCP_EXPR; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p tcp --dport $PORT -j MARK --set-mark $PRIORITY_EXPRESS
done
for PORT in $UDP_STND; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p udp --dport $PORT -j MARK --set-mark $PRIORITY_STANDARD
done
for PORT in $TCP_STND; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p tcp --dport $PORT -j MARK --set-mark $PRIORITY_STANDARD
done
for PORT in $UDP_BULK; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p udp --dport $PORT -j MARK --set-mark $PRIORITY_BULK
done
for PORT in $TCP_BULK; do
    $IPT -t mangle -A mark_chain -m mark --mark 0 -p tcp --dport $PORT -j MARK --set-mark $PRIORITY_BULK
done
ALL_PROTOS=""
for PROTO in $IPP2P_BULK; do
    ALL_PROTOS="${ALL_PROTOS}--$PROTO "
done
[ "$ALL_PROTOS" ] && $IPT -t mangle -A mark_chain -m mark --mark 0 -m ipp2p $ALL_PROTOS -j MARK --set-mark $PRIORITY_BULK
$IPT -t mangle -A mark_chain -m mark --mark 0 -j MARK --set-mark $PRIORITY_BULK
$IPT -t mangle -A mark_chain -j CONNMARK --save-mark
[ "$UDP_LENGTH" -gt 0 ] && $IPT -t mangle -A mark_chain -p udp -m length --length :$UDP_LENGTH -j MARK --set-mark $PRIORITY_PREMIUM
$IPT -t mangle -A egress_chain -j mark_chain
$IPT -t mangle -A egress_chain -p tcp -m length --length :128 --tcp-flags SYN,RST,ACK ACK -j MARK --set-mark $PRIORITY_ACK

The script defines five priority bands.  The bands are listed in order of priority from lowest priority to highest priority.  Lowest priority being the traffic with the least desired responsiveness, such as bittorrent.

  1. Bulk
  2. Standard
  3. Express
  4. Premium
  5. ACK

Modify the TCP_PREM, UDP_PREM, TCP_EXPR, UDP_EXPR, TCP_STND, UDP_STND, TCP_BULK, and UDP_BULK variables to suit your needs.  Add the port number of the service you want to be in the priority band.  The following services are in the following priority bands.

  1. Bulk: ftp-data, smtp, bittorrent
  2. Standard: ftp-control, http, https, http-proxy
  3. Express: DNS
  4. Premium: ssh, RDP
  5. ACK: TCP ACKs

Specify the upload bandwidth of your internet connection by setting the variable UPLOAD.  The value is specified in Kbps.

After the script has been customized logon to DD-WRT, select the top-level Administration tab, select the bottom-level Commands tab, and paste the contents of the script in Firewall dialog box.  Click Save Firewall, and restart your router.

Page optimized by WP Minify WordPress Plugin