Permalink
Fetching contributors…
Cannot retrieve contributors at this time
256 lines (189 sloc) 7.76 KB
#!/bin/sh
# Cero3 Shaper
# A 3 bin tc_codel and ipv6 enabled shaping script for
# ethernet gateways, with an eye towards working well
# with wireless with uplinks in the 2Mbit to 25Mbit
# range. It ain't done yet, and is cerowrt specific
# in that it depends on clearly identifying the
# internal interfaces via a pattern match.
# Copyright (C) 2012 Michael D Taht
# GPLv2
# Compared to the complexity that debloat had become
# this cleanly shows a means of going from diffserv marking
# to prioritization using the current tools (ip(6)tables
# and tc. I note that the complexity of debloat exists for
# a reason, and it is expected that script is run first
# to setup various other parameters such as BQL and ethtool.
# (And that the debloat script has setup the other interfaces)
# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
UPLINK=2000
DOWNLINK=20000
DEV=ifb0
QDISC=fq_codel # fq_codel is winning universally
IFACE=ge00
DEPTH=42
TC=/usr/sbin/tc
FLOWS=8000
PERTURB="perturb 0" # Permutation is costly, disable
FLOWS=16000 #
BQL_MAX=3000 # it is important to factor this into the RED calc
CEIL=$UPLINK
MTU=1500
ADSLL=""
# PPOE=yes
#config interface ge00
# option classgroup "Default"
# option enabled 0
# option upload 128
# option download 1024
# uci get aqm.enable
#
# You shouldn't need to touch anything here
if [ -s "$PPOE" ]
then
OVERHEAD=40
LINKLAYER=adsl
ADSLL="linklayer ${LINKLAYER} overhead ${OVERHEAD}"
fi
ipt() {
iptables $*
ip6tables $*
}
do_modules() {
insmod sch_$QDISC
insmod sch_ingress
insmod act_mirred
insmod cls_fw
insmod sch_htb
}
fc() {
PARENT=$1
TOS=$2
CLASSID=$3
tc filter add dev $interface protocol ip parent $PARENT prio $prio u32 match ip tos $TOS 0xfc classid $CLASSID
prio=$(($prio + 1))
tc filter add dev $interface protocol ipv6 parent $PARENT prio $prio u32 match ip6 priority $TOS 0xfc classid $CLASSID
prio=$(($prio + 1))
}
# This could be a complete diffserv implementation
diffserv() {
interface=$1
prio=1
# Catchall
tc filter add dev $interface parent 1:0 protocol all prio 999 u32 \
match ip protocol 0 0x00 flowid 1:12
# Find the most common matches fast
fc 1:0 0x00 1:12 # BE
fc 1:0 0x20 1:13 # CS1
fc 1:0 0x10 1:11 # IMM
fc 1:0 0xb8 1:11 # EF
fc 1:0 0xc0 1:11 # CS3
fc 1:0 0xe0 1:11 # CS6
fc 1:0 0x90 1:11 # AF42 (mosh)
# Arp traffic
tc filter add dev $interface parent 1:0 protocol arp prio $prio handle 1 fw classid 1:11
prio=$(($prio + 1))
}
ipt_setup() {
ipt -t mangle -F
ipt -t mangle -N QOS_MARK
ipt -t mangle -A QOS_MARK -j MARK --set-mark 0x2
# You can go further with classification but...
ipt -t mangle -A QOS_MARK -m dscp --dscp-class CS1 -j MARK --set-mark 0x3
ipt -t mangle -A QOS_MARK -m dscp --dscp-class CS3 -j MARK --set-mark 0x1
ipt -t mangle -A QOS_MARK -m dscp --dscp-class CS6 -j MARK --set-mark 0x1
ipt -t mangle -A QOS_MARK -m dscp --dscp-class EF -j MARK --set-mark 0x1
ipt -t mangle -A QOS_MARK -m dscp --dscp-class AF42 -j MARK --set-mark 0x1
ipt -t mangle -A QOS_MARK -m tos --tos Minimize-Delay -j MARK --set-mark 0x1
# and it might be a good idea to do it for udp tunnels too
# Turn it on. Preserve classification if already performed
ipt -t mangle -A POSTROUTING -o $DEV -m mark --mark 0x00 -g QOS_MARK
ipt -t mangle -A POSTROUTING -o $IFACE -m mark --mark 0x00 -g QOS_MARK
# The Syn optimization was nice but fq_codel does it for us
# ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01
# Not sure if this will work. Encapsulation is a problem period
ipt -t mangle -A PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2 # tcp tunnels need ordering
# Emanating from router, do a little more optimization
# but don't bother with it too much.
ipt -t mangle -A OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42
#Not clear if the second line is needed
#ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK
}
# TC rules
egress() {
CEIL=${UPLINK}
PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty
BE_RATE=`expr $CEIL / 6` # Min for best effort
BK_RATE=`expr $CEIL / 9` # Min for background
BE_CEIL=`expr $CEIL - 64` # A little slop at the top
R2Q=""
if [ "$CEIL" -lt 1000 ]
then
R2Q="rtq 1"
fi
tc qdisc del dev $IFACE root
tc qdisc add dev $IFACE root handle 1: htb ${RTQ} default 12
tc class add dev $IFACE parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit $ADSLL
tc class add dev $IFACE parent 1:1 classid 1:10 htb rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 $ADSLL
tc class add dev $IFACE parent 1:1 classid 1:11 htb rate 128kbit ceil ${PRIO_RATE}kbit prio 1 $ADSLL
tc class add dev $IFACE parent 1:1 classid 1:12 htb rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 $ADSLL
tc class add dev $IFACE parent 1:1 classid 1:13 htb rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 $ADSLL
tc qdisc add dev $IFACE parent 1:11 handle 110: $QDISC limit 600 quantum 300 noecn
tc qdisc add dev $IFACE parent 1:12 handle 120: $QDISC limit 600 quantum 300 noecn
tc qdisc add dev $IFACE parent 1:13 handle 130: $QDISC limit 600 noecn
tc filter add dev $IFACE parent 1:0 protocol ip prio 1 handle 1 fw classid 1:11
tc filter add dev $IFACE parent 1:0 protocol ip prio 2 handle 2 fw classid 1:12
tc filter add dev $IFACE parent 1:0 protocol ip prio 3 handle 3 fw classid 1:13
# ipv6 support. Note that the handle indicates the fw mark bucket that is looked for
tc filter add dev $IFACE parent 1:0 protocol ipv6 prio 4 handle 1 fw classid 1:11
tc filter add dev $IFACE parent 1:0 protocol ipv6 prio 5 handle 2 fw classid 1:12
tc filter add dev $IFACE parent 1:0 protocol ipv6 prio 6 handle 3 fw classid 1:13
# Arp traffic
tc filter add dev $IFACE parent 1:0 protocol arp prio 7 handle 1 fw classid 1:11
}
ingress() {
CEIL=$DOWNLINK
PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty
BE_RATE=`expr $CEIL / 3` # Min for best effort
BK_RATE=`expr $CEIL / 6` # Min for background
BE_CEIL=`expr $CEIL - 64` # A little slop at the top
R2Q=""
tc qdisc del dev $IFACE handle ffff: ingress
tc qdisc add dev $IFACE handle ffff: ingress
tc qdisc del dev $DEV root
tc qdisc add dev $DEV root handle 1: htb ${RTQ} default 12
tc class add dev $DEV parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kibit $ADSLL
tc class add dev $DEV parent 1:1 classid 1:10 htb rate ${CEIL}kbit ceil ${CEIL}kibit prio 0 $ADSLL
tc class add dev $DEV parent 1:1 classid 1:11 htb rate 32kbit ceil ${PRIO_RATE}kibit prio 1 $ADSLL
tc class add dev $DEV parent 1:1 classid 1:12 htb rate ${BE_RATE}kbit ceil ${BE_CEIL}kibit prio 2 $ADSLL
tc class add dev $DEV parent 1:1 classid 1:13 htb rate ${BK_RATE}kbit ceil ${BE_CEIL}kibit prio 3 $ADSLL
# I'd prefer to use a pre-nat filter but that causes permutation...
tc qdisc add dev $DEV parent 1:11 handle 110: $QDISC limit 1000 ecn
tc qdisc add dev $DEV parent 1:12 handle 120: $QDISC limit 1000 ecn
tc qdisc add dev $DEV parent 1:13 handle 130: $QDISC limit 1000 ecn
diffserv ifb0
ifconfig ifb0 up
# redirect all IP packets arriving in $IFACE to ifb0
$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
}
do_modules
ipt_setup
egress
ingress
# References:
# This alternate shaper attempts to go for 1/u performance in a clever way
# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
# Comments
# This does the right thing with ipv6 traffic.
# It also does not rehash with sfq skewing streams
# It also tries to leverage diffserv to some sane extent. In particular,
# the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic
# cannot starve other types. The rfc suggested 30%. 30% is probably
# a lot in today's world.
# Flaws
# Many!
# Why 42?
# Lucky number.
# the sum of the number of packets here + htb + the ar71xx device driver
# ~= 50 the core number used by theorists everywhere.