packages/net/sqm-scripts/files/usr/lib/sqm/functions.sh
Sebastian Moeller 2ba6a3f9ab [SQM/luci-app-sqm] Wire up link layer dandling for cake
The cake traffic-shaper qdisc omne stop solution knows how to handle
link layer adjustments for ATM and can account for per packet overhead.
This commit adds cake as link layer adjustment mechanism in the GUI and
passes numerically specified overhead as well as the ATM linklayer
keywords on to cake. This change also passes the "advanced option strings"
from the Queue Discipline tab to cake. But as before no error checking.
This needs testing, as I have no working cake qdisc available so
caveat emptor...

Signed-off-by: Sebastian Moeller <moeller0@gmx.de>
2015-07-12 22:24:21 +02:00

503 lines
15 KiB
Bash

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
#improve the logread output
sqm_logger() {
logger -t SQM -s ${1}
}
insmod() {
lsmod | grep -q ^$1 || $INSMOD $1
}
ipt() {
d=`echo $* | sed s/-A/-D/g`
[ "$d" != "$*" ] && {
iptables $d > /dev/null 2>&1
ip6tables $d > /dev/null 2>&1
}
d=`echo $* | sed s/-I/-D/g`
[ "$d" != "$*" ] && {
iptables $d > /dev/null 2>&1
ip6tables $d > /dev/null 2>&1
}
iptables $* > /dev/null 2>&1
ip6tables $* > /dev/null 2>&1
}
do_modules() {
#sm TODO: check first whether the modules exist and only load then
insmod act_ipt
insmod sch_$QDISC
insmod sch_ingress
insmod act_mirred
insmod cls_fw
insmod sch_htb
}
# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
[ -z "$UPLINK" ] && UPLINK=2302
[ -z "$DOWNLINK" ] && DOWNLINK=14698
[ -z "$IFACE" ] && IFACE=ge00
[ -z "$QDISC" ] && QDISC=fq_codel
[ -z "$LLAM" ] && LLAM="tc_stab"
[ -z "$LINKLAYER" ] && LINKLAYER="none"
[ -z "$OVERHEAD" ] && OVERHEAD=0
[ -z "$STAB_MTU" ] && STAB_MTU=2047
[ -z "$STAB_MPU" ] && STAB_MPU=0
[ -z "$STAB_TSIZE" ] && STAB_TSIZE=512
[ -z "$AUTOFLOW" ] && AUTOFLOW=0
[ -z "$LIMIT" ] && LIMIT=1001 # sane global default for *LIMIT for fq_codel on a small memory device
[ -z "$ILIMIT" ] && ILIMIT=
[ -z "$ELIMIT" ] && ELIMIT=
[ -z "$ITARGET" ] && ITARGET=
[ -z "$ETARGET" ] && ETARGET=
[ -z "$IECN" ] && IECN="ECN"
[ -z "$EECN" ] && EECN="NOECN"
[ -z "$SQUASH_DSCP" ] && SQUASH_DSCP="1"
[ -z "$SQUASH_INGRESS" ] && SQUASH_INGRESS="1"
[ -z "$IQDISC_OPTS" ] && IQDISC_OPTS=""
[ -z "$EQDISC_OPTS" ] && EQDISC_OPTS=""
[ -z "$TC" ] && TC=`which tc`
#[ -z "$TC" ] && TC="sqm_logger tc"# this redirects all tc calls into the log
[ -z "$IP" ] && IP=$( which ip )
[ -z "$INSMOD" ] && INSMOD=`which insmod`
[ -z "$TARGET" ] && TARGET="5ms"
[ -z "$IPT_MASK" ] && IPT_MASK="0xff"
[ -z "$IPT_MASK_STRING" ] && IPT_MASK_STRING="/${IPT_MASK}" # for set-mark
#sqm_logger "${0} IPT_MASK: ${IPT_MASK_STRING}"
# find the ifb device associated with a specific interface, return nothing of no ifb is associated with IF
get_ifb_associated_with_if() {
CUR_IF=$1
# CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[[:digit:]]\+' )
CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[^)]\+' ) # my editor's syntax coloration is limitied so I need a single quote in this line (between eiditor and s)
sqm_logger "ifb associated with interface ${CUR_IF}: ${CUR_IFB}"
echo ${CUR_IFB}
}
# ATTENTION, IFB names can only be 15 chararcters, so we chop of excessive characters at the start of the interface name
# if required
create_new_ifb_for_if() {
CUR_IF=$1
MAX_IF_NAME_LENGTH=15
IFB_PREFIX="ifb4"
NEW_IFB="${IFB_PREFIX}${CUR_IF}"
IFB_NAME_LENGTH=${#NEW_IFB}
if [ ${IFB_NAME_LENGTH} -gt ${MAX_IF_NAME_LENGTH} ];
then
sqm_logger "The requsted IFB name ${NEW_IFB} is longer than the allowed 15 characters, trying to make it shorter"
OVERLIMIT=$(( ${#NEW_IFB} - ${MAX_IF_NAME_LENGTH} ))
NEW_IFB=${IFB_PREFIX}${CUR_IF:${OVERLIMIT}:$(( ${MAX_IF_NAME_LENGTH} - ${#IFB_PREFIX} ))}
fi
sqm_logger "trying to create new IFB: ${NEW_IFB}"
$IP link add name ${NEW_IFB} type ifb #>/dev/null 2>&1 # better be verbose
echo ${NEW_IFB}
}
# the best match is either the IFB already associated with the current interface or a new named IFB
get_ifb_for_if() {
CUR_IF=$1
# if an ifb is already associated return that
CUR_IFB=$( get_ifb_associated_with_if ${CUR_IF} )
[ -z "$CUR_IFB" ] && CUR_IFB=$( create_new_ifb_for_if ${CUR_IF} )
[ -z "$CUR_IFB" ] && sqm_logger "Could not find existing IFB for ${CUR_IF}, nor create a new IFB instead..."
echo ${CUR_IFB}
}
#sm: we need the functions above before trying to set the ingress IFB device
[ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} ) # automagically get the right IFB device for the IFACE"
get_htb_adsll_string() {
ADSLL=""
if [ "$LLAM" = "htb_private" -a "$LINKLAYER" != "none" ];
then
# HTB defaults to MTU 1600 and an implicit fixed TSIZE of 256, but HTB as of around 3.10.0
# does not actually use a table in the kernel
ADSLL="mpu ${STAB_MPU} linklayer ${LINKLAYER} overhead ${OVERHEAD} mtu ${STAB_MTU}"
sqm_logger "ADSLL: ${ADSLL}"
fi
echo ${ADSLL}
}
get_stab_string() {
STABSTRING=""
if [ "${LLAM}" = "tc_stab" -a "$LINKLAYER" != "none" ];
then
STABSTRING="stab mtu ${STAB_MTU} tsize ${STAB_TSIZE} mpu ${STAB_MPU} overhead ${OVERHEAD} linklayer ${LINKLAYER}"
sqm_logger "STAB: ${STABSTRING}"
fi
echo ${STABSTRING}
}
#sm: cake knows how to handle ATM and per packet overhead, so expose and use this...
get_cake_lla_string() {
STABSTRING=""
if [ "${LLAM}" = "cake" -a "${LINKLAYER}" != "none" ];
then
if [ "${LINKLAYER}" = "atm" ];
then
STABSTRING="atm"
fi
STABSTRING="${STABSTRING} overhead ${OVERHEAD}"
sqm_logger "cake link layer adjustments: ${STABSTRING}"
fi
echo ${STABSTRING}
}
sqm_stop() {
$TC qdisc del dev $IFACE ingress
$TC qdisc del dev $IFACE root
$TC qdisc del dev $DEV root
}
# Note this has side effects on the prio variable
# and depends on the interface global too
fc() {
$TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
prio=$(($prio + 1))
$TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
prio=$(($prio + 1))
}
fc_pppoe() {
PPPOE_SESSION_ETHERTYPE="0x8864"
PPPOE_DISCOVERY_ETHERTYPE="0x8863"
PPP_PROTO_IP4="0x0021"
PPP_PROTO_IP6="0x0057"
ARP_PROTO_IP4="0x0806"
$TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
$TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 400 + ${prio} )) u32 \
match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
match u8 $2 0xfc at 9 \
flowid $3
prio=$(($prio + 1))
$TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
$TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 600 + ${prio} )) u32 \
match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
match u16 0x0${2:2:2}0 0x0fc0 at 8 \
flowid $3
prio=$(($prio + 1))
}
# FIXME: actually you need to get the underlying MTU on PPOE thing
get_mtu() {
BW=$2
F=`cat /sys/class/net/$1/mtu`
if [ -z "$F" ]
then
F=1500
fi
if [ $BW -gt 20000 ]
then
F=$(($F * 2))
fi
if [ $BW -gt 30000 ]
then
F=$(($F * 2))
fi
if [ $BW -gt 40000 ]
then
F=$(($F * 2))
fi
if [ $BW -gt 50000 ]
then
F=$(($F * 2))
fi
if [ $BW -gt 60000 ]
then
F=$(($F * 2))
fi
if [ $BW -gt 80000 ]
then
F=$(($F * 2))
fi
echo $F
}
# FIXME should also calculate the limit
# Frankly I think Xfq_codel can pretty much always run with high numbers of flows
# now that it does fate sharing
# But right now I'm trying to match the ns2 model behavior better
# So SET the autoflow variable to 1 if you want the cablelabs behavior
get_flows() {
if [ "$AUTOFLOW" -eq "1" ]
then
FLOWS=8
[ $1 -gt 999 ] && FLOWS=16
[ $1 -gt 2999 ] && FLOWS=32
[ $1 -gt 7999 ] && FLOWS=48
[ $1 -gt 9999 ] && FLOWS=64
[ $1 -gt 19999 ] && FLOWS=128
[ $1 -gt 39999 ] && FLOWS=256
[ $1 -gt 69999 ] && FLOWS=512
[ $1 -gt 99999 ] && FLOWS=1024
case $QDISC in
codel|ns2_codel|pie|*fifo|pfifo_fast) ;;
fq_codel|*fq_codel|sfq) echo flows $FLOWS ;;
esac
fi
}
# set the target parameter, also try to only take well formed inputs
# Note, the link bandwidth in the current direction (ingress or egress)
# is required to adjust the target for slow links
get_target() {
local CUR_TARGET=${1}
local CUR_LINK_KBPS=${2}
[ ! -z "$CUR_TARGET" ] && sqm_logger "cur_target: ${CUR_TARGET} cur_bandwidth: ${CUR_LINK_KBPS}"
CUR_TARGET_STRING=
# either e.g. 100ms or auto
CUR_TARGET_VALUE=$( echo ${CUR_TARGET} | grep -o -e \^'[[:digit:]]\+' )
CUR_TARGET_UNIT=$( echo ${CUR_TARGET} | grep -o -e '[[:alpha:]]\+'\$ )
#[ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_VALUE: $CUR_TARGET_VALUE"
#[ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_UNIT: $CUR_TARGET_UNIT"
AUTO_TARGET=
UNIT_VALID=
case $QDISC in
*codel|*pie)
if [ ! -z "${CUR_TARGET_VALUE}" -a ! -z "${CUR_TARGET_UNIT}" ];
then
case ${CUR_TARGET_UNIT} in
# permissible units taken from: tc_util.c get_time()
s|sec|secs|ms|msec|msecs|us|usec|usecs)
CUR_TARGET_STRING="target ${CUR_TARGET_VALUE}${CUR_TARGET_UNIT}"
UNIT_VALID="1"
;;
esac
fi
# empty field in GUI or undefined GUI variable now defaults to auto
if [ -z "${CUR_TARGET_VALUE}" -a -z "${CUR_TARGET_UNIT}" ];
then
if [ ! -z "${CUR_LINK_KBPS}" ];
then
TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS )
TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US )
CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}"
AUTO_TARGET="1"
sqm_logger "get_target defaulting to auto."
else
sqm_logger "required link bandwidth in kbps not passed to get_target()."
fi
fi
# but still allow explicit use of the keyword auto for backward compatibility
case ${CUR_TARGET_UNIT} in
auto|Auto|AUTO)
if [ ! -z "${CUR_LINK_KBPS}" ];
then
TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS )
TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US )
CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}"
AUTO_TARGET="1"
else
sqm_logger "required link bandwidth in kbps not passed to get_target()."
fi
;;
esac
case ${CUR_TARGET_UNIT} in
default|Default|DEFAULT)
if [ ! -z "${CUR_LINK_KBPS}" ];
then
CUR_TARGET_STRING="" # return nothing so the default target is not over-ridden...
AUTO_TARGET="1"
#sqm_logger "get_target using qdisc default, no explicit target string passed."
else
sqm_logger "required link bandwidth in kbps not passed to get_target()."
fi
;;
esac
if [ ! -z "${CUR_TARGET}" ];
then
if [ -z "${CUR_TARGET_VALUE}" -o -z "${UNIT_VALID}" ];
then
[ -z "$AUTO_TARGET" ] && sqm_logger "${CUR_TARGET} is not a well formed tc target specifier; e.g.: 5ms (or s, us), or one of the strings auto or default."
fi
fi
;;
esac
# sqm_logger "target: ${CUR_TARGET_STRING}"
echo $CUR_TARGET_STRING
}
# for low bandwidth links fq_codels default target of 5ms does not work too well
# so increase target for slow links (note below roughly 2500kbps a single packet will \
# take more than 5 ms to be tansfered over the wire)
adapt_target_to_slow_link() {
CUR_LINK_KBPS=$1
CUR_EXTENDED_TARGET_US=
MAX_PAKET_DELAY_IN_US_AT_1KBPS=$(( 1000 * 1000 *1540 * 8 / 1000 ))
CUR_EXTENDED_TARGET_US=$(( ${MAX_PAKET_DELAY_IN_US_AT_1KBPS} / ${CUR_LINK_KBPS} )) # note this truncates the decimals
# do not change anything for fast links
[ "$CUR_EXTENDED_TARGET_US" -lt 5000 ] && CUR_EXTENDED_TARGET_US=5000
case ${QDISC} in
*codel|pie)
echo "${CUR_EXTENDED_TARGET_US}"
;;
esac
}
# codel looks at a whole interval to figure out wether observed latency stayed below target
# if target >= interval that will not work well, so increase interval by the same amonut that target got increased
adapt_interval_to_slow_link() {
CUR_TARGET_US=$1
case ${QDISC} in
*codel)
CUR_EXTENDED_INTERVAL_US=$(( (100 - 5) * 1000 + ${CUR_TARGET_US} ))
echo "interval ${CUR_EXTENDED_INTERVAL_US}us"
;;
pie)
## not sure if pie needs this, probably not
#CUR_EXTENDED_TUPDATE_US=$(( (30 - 20) * 1000 + ${CUR_TARGET_US} ))
#echo "tupdate ${CUR_EXTENDED_TUPDATE_US}us"
;;
esac
}
# set quantum parameter if available for this qdisc
get_quantum() {
case $QDISC in
*fq_codel|fq_pie|drr) echo quantum $1 ;;
*) ;;
esac
}
# only show limits to qdiscs that can handle them...
# Note that $LIMIT contains the default limit
get_limit() {
CURLIMIT=$1
case $QDISC in
*codel|*pie|pfifo_fast|sfq|pfifo) [ -z ${CURLIMIT} ] && CURLIMIT=${LIMIT} # use the global default limit
;;
bfifo) [ -z "$CURLIMIT" ] && [ ! -z "$LIMIT" ] && CURLIMIT=$(( ${LIMIT} * $( cat /sys/class/net/${IFACE}/mtu ) )) # bfifo defaults to txquelength * MTU,
;;
*) sqm_logger "${QDISC} does not support a limit"
;;
esac
sqm_logger "get_limit: $1 CURLIMIT: ${CURLIMIT}"
if [ ! -z "$CURLIMIT" ]
then
echo "limit ${CURLIMIT}"
fi
}
get_ecn() {
CURECN=$1
#sqm_logger CURECN: $CURECN
case ${CURECN} in
ECN)
case $QDISC in
*codel|*pie|*red)
CURECN=ecn
;;
*)
CURECN=""
;;
esac
;;
NOECN)
case $QDISC in
*codel|*pie|*red)
CURECN=noecn
;;
*)
CURECN=""
;;
esac
;;
*)
sqm_logger "ecn value $1 not handled"
;;
esac
#sqm_logger "get_ECN: $1 CURECN: ${CURECN} IECN: ${IECN} EECN: ${EECN}"
echo ${CURECN}
}
# 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_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
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 protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
prio=$(($prio + 1))
}
diffserv_pppoe() {
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_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
fc_pppoe 1:0 0x00 1:12 # BE
fc_pppoe 1:0 0x20 1:13 # CS1
fc_pppoe 1:0 0x10 1:11 # IMM
fc_pppoe 1:0 0xb8 1:11 # EF
fc_pppoe 1:0 0xc0 1:11 # CS3
fc_pppoe 1:0 0xe0 1:11 # CS6
fc_pppoe 1:0 0x90 1:11 # AF42 (mosh)
# Arp traffic
$TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
prio=$(($prio + 1))
}