You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
13 KiB

  1. # This program is free software; you can redistribute it and/or modify
  2. # it under the terms of the GNU General Public License version 2 as
  3. # published by the Free Software Foundation.
  4. #
  5. # Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
  6. #improve the logread output
  7. sqm_logger() {
  8. logger -t SQM -s ${1}
  9. }
  10. insmod() {
  11. lsmod | grep -q ^$1 || $INSMOD $1
  12. }
  13. ipt() {
  14. d=`echo $* | sed s/-A/-D/g`
  15. [ "$d" != "$*" ] && {
  16. iptables $d > /dev/null 2>&1
  17. ip6tables $d > /dev/null 2>&1
  18. }
  19. d=`echo $* | sed s/-I/-D/g`
  20. [ "$d" != "$*" ] && {
  21. iptables $d > /dev/null 2>&1
  22. ip6tables $d > /dev/null 2>&1
  23. }
  24. iptables $* > /dev/null 2>&1
  25. ip6tables $* > /dev/null 2>&1
  26. }
  27. do_modules() {
  28. insmod act_ipt
  29. insmod sch_$QDISC
  30. insmod sch_ingress
  31. insmod act_mirred
  32. insmod cls_fw
  33. insmod sch_htb
  34. }
  35. # You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
  36. [ -z "$UPLINK" ] && UPLINK=2302
  37. [ -z "$DOWNLINK" ] && DOWNLINK=14698
  38. [ -z "$IFACE" ] && IFACE=ge00
  39. [ -z "$QDISC" ] && QDISC=fq_codel
  40. [ -z "$LLAM" ] && LLAM="tc_stab"
  41. [ -z "$LINKLAYER" ] && LINKLAYER="none"
  42. [ -z "$OVERHEAD" ] && OVERHEAD=0
  43. [ -z "$STAB_MTU" ] && STAB_MTU=2047
  44. [ -z "$STAB_MPU" ] && STAB_MPU=0
  45. [ -z "$STAB_TSIZE" ] && STAB_TSIZE=512
  46. [ -z "$AUTOFLOW" ] && AUTOFLOW=0
  47. [ -z "$LIMIT" ] && LIMIT=1001 # sane global default for *LIMIT for fq_codel on a small memory device
  48. [ -z "$ILIMIT" ] && ILIMIT=
  49. [ -z "$ELIMIT" ] && ELIMIT=
  50. [ -z "$ITARGET" ] && ITARGET=
  51. [ -z "$ETARGET" ] && ETARGET=
  52. [ -z "$IECN" ] && IECN="ECN"
  53. [ -z "$EECN" ] && EECN="NOECN"
  54. [ -z "$SQUASH_DSCP" ] && SQUASH_DSCP="1"
  55. [ -z "SQUASH_INGRESS" ] && SQUASH_INGRESS="1"
  56. [ -z "$IQDISC_OPTS" ] && IQDISC_OPTS=""
  57. [ -z "$EQDISC_OPTS" ] && EQDISC_OPTS=""
  58. [ -z "$TC" ] && TC=`which tc`
  59. #[ -z "$TC" ] && TC="sqm_logger tc"# this redirects all tc calls into the log
  60. [ -z "$IP" ] && IP=$( which ip )
  61. [ -z "$INSMOD" ] && INSMOD=`which insmod`
  62. [ -z "TARGET" ] && TARGET="5ms"
  63. # find the ifb device associated with a specific interface, return nothing of no ifb is associated with IF
  64. get_ifb_associated_with_if() {
  65. CUR_IF=$1
  66. # CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[[:digit:]]\+' )
  67. 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)
  68. sqm_logger "ifb associated with interface ${CUR_IF}: ${CUR_IFB}"
  69. echo ${CUR_IFB}
  70. }
  71. # ATTENTION, IFB names can only be 15 chararcters, so we chop of excessive characters at the start of the interface name
  72. # if required
  73. create_new_ifb_for_if() {
  74. CUR_IF=$1
  75. MAX_IF_NAME_LENGTH=15
  76. IFB_PREFIX="ifb4"
  77. NEW_IFB="${IFB_PREFIX}${CUR_IF}"
  78. IFB_NAME_LENGTH=${#NEW_IFB}
  79. if [ ${IFB_NAME_LENGTH} -gt ${MAX_IF_NAME_LENGTH} ];
  80. then
  81. sqm_logger "The requsted IFB name ${NEW_IFB} is longer than the allowed 15 characters, trying to make it shorter"
  82. OVERLIMIT=$(( ${#NEW_IFB} - ${MAX_IF_NAME_LENGTH} ))
  83. NEW_IFB=${IFB_PREFIX}${CUR_IF:${OVERLIMIT}:$(( ${MAX_IF_NAME_LENGTH} - ${#IFB_PREFIX} ))}
  84. fi
  85. sqm_logger "trying to create new IFB: ${NEW_IFB}"
  86. $IP link add name ${NEW_IFB} type ifb #>/dev/null 2>&1 # better be verbose
  87. echo ${NEW_IFB}
  88. }
  89. # the best match is either the IFB already associated with the current interface or a new named IFB
  90. get_ifb_for_if() {
  91. CUR_IF=$1
  92. # if an ifb is already associated return that
  93. CUR_IFB=$( get_ifb_associated_with_if ${CUR_IF} )
  94. [ -z "$CUR_IFB" ] && CUR_IFB=$( create_new_ifb_for_if ${CUR_IF} )
  95. [ -z "$CUR_IFB" ] && sqm_logger "Could not find existing IFB for ${CUR_IF}, nor create a new IFB instead..."
  96. echo ${CUR_IFB}
  97. }
  98. #sm: we need the functions above before trying to set the ingress IFB device
  99. [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} ) # automagically get the right IFB device for the IFACE"
  100. get_htb_adsll_string() {
  101. ADSLL=""
  102. if [ "$LLAM" = "htb_private" -a "$LINKLAYER" != "none" ];
  103. then
  104. # HTB defaults to MTU 1600 and an implicit fixed TSIZE of 256, but HTB as of around 3.10.0
  105. # does not actually use a table in the kernel
  106. ADSLL="mpu ${STAB_MPU} linklayer ${LINKLAYER} overhead ${OVERHEAD} mtu ${STAB_MTU}"
  107. sqm_logger "ADSLL: ${ADSLL}"
  108. fi
  109. echo ${ADSLL}
  110. }
  111. get_stab_string() {
  112. STABSTRING=""
  113. if [ "${LLAM}" = "tc_stab" -a "$LINKLAYER" != "none" ];
  114. then
  115. STABSTRING="stab mtu ${STAB_MTU} tsize ${STAB_TSIZE} mpu ${STAB_MPU} overhead ${OVERHEAD} linklayer ${LINKLAYER}"
  116. sqm_logger "STAB: ${STABSTRING}"
  117. fi
  118. echo ${STABSTRING}
  119. }
  120. sqm_stop() {
  121. $TC qdisc del dev $IFACE ingress
  122. $TC qdisc del dev $IFACE root
  123. $TC qdisc del dev $DEV root
  124. }
  125. # Note this has side effects on the prio variable
  126. # and depends on the interface global too
  127. fc() {
  128. $TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
  129. prio=$(($prio + 1))
  130. $TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
  131. prio=$(($prio + 1))
  132. }
  133. fc_pppoe() {
  134. PPPOE_SESSION_ETHERTYPE="0x8864"
  135. PPPOE_DISCOVERY_ETHERTYPE="0x8863"
  136. PPP_PROTO_IP4="0x0021"
  137. PPP_PROTO_IP6="0x0057"
  138. ARP_PROTO_IP4="0x0806"
  139. $TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
  140. $TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 400 + ${prio} )) u32 \
  141. match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
  142. match u8 $2 0xfc at 9 \
  143. flowid $3
  144. prio=$(($prio + 1))
  145. $TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
  146. $TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 600 + ${prio} )) u32 \
  147. match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
  148. match u16 0x0${2:2:2}0 0x0fc0 at 8 \
  149. flowid $3
  150. prio=$(($prio + 1))
  151. }
  152. # FIXME: actually you need to get the underlying MTU on PPOE thing
  153. get_mtu() {
  154. BW=$2
  155. F=`cat /sys/class/net/$1/mtu`
  156. if [ -z "$F" ]
  157. then
  158. F=1500
  159. fi
  160. if [ $BW -gt 20000 ]
  161. then
  162. F=$(($F * 2))
  163. fi
  164. if [ $BW -gt 30000 ]
  165. then
  166. F=$(($F * 2))
  167. fi
  168. if [ $BW -gt 40000 ]
  169. then
  170. F=$(($F * 2))
  171. fi
  172. if [ $BW -gt 50000 ]
  173. then
  174. F=$(($F * 2))
  175. fi
  176. if [ $BW -gt 60000 ]
  177. then
  178. F=$(($F * 2))
  179. fi
  180. if [ $BW -gt 80000 ]
  181. then
  182. F=$(($F * 2))
  183. fi
  184. echo $F
  185. }
  186. # FIXME should also calculate the limit
  187. # Frankly I think Xfq_codel can pretty much always run with high numbers of flows
  188. # now that it does fate sharing
  189. # But right now I'm trying to match the ns2 model behavior better
  190. # So SET the autoflow variable to 1 if you want the cablelabs behavior
  191. get_flows() {
  192. if [ "$AUTOFLOW" -eq "1" ]
  193. then
  194. FLOWS=8
  195. [ $1 -gt 999 ] && FLOWS=16
  196. [ $1 -gt 2999 ] && FLOWS=32
  197. [ $1 -gt 7999 ] && FLOWS=48
  198. [ $1 -gt 9999 ] && FLOWS=64
  199. [ $1 -gt 19999 ] && FLOWS=128
  200. [ $1 -gt 39999 ] && FLOWS=256
  201. [ $1 -gt 69999 ] && FLOWS=512
  202. [ $1 -gt 99999 ] && FLOWS=1024
  203. case $QDISC in
  204. codel|ns2_codel|pie|*fifo|pfifo_fast) ;;
  205. fq_codel|*fq_codel|sfq) echo flows $FLOWS ;;
  206. esac
  207. fi
  208. }
  209. # set the target parameter, also try to only take well formed inputs
  210. # Note, the link bandwidth in the current direction (ingress or egress)
  211. # is required to adjust the target for slow links
  212. get_target() {
  213. local CUR_TARGET=${1}
  214. local CUR_LINK_KBPS=${2}
  215. [ ! -z "$CUR_TARGET" ] && sqm_logger "cur_target: ${CUR_TARGET} cur_bandwidth: ${CUR_LINK_KBPS}"
  216. CUR_TARGET_STRING=
  217. # either e.g. 100ms or auto
  218. CUR_TARGET_VALUE=$( echo ${CUR_TARGET} | grep -o -e \^'[[:digit:]]\+' )
  219. CUR_TARGET_UNIT=$( echo ${CUR_TARGET} | grep -o -e '[[:alpha:]]\+'\$ )
  220. # [ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_VALUE: $CUR_TARGET_VALUE"
  221. # [ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_UNIT: $CUR_TARGET_UNIT"
  222. AUTO_TARGET=
  223. UNIT_VALID=
  224. case $QDISC in
  225. *codel|*pie)
  226. if [ ! -z "${CUR_TARGET_VALUE}" -a ! -z "${CUR_TARGET_UNIT}" ];
  227. then
  228. case ${CUR_TARGET_UNIT} in
  229. # permissible units taken from: tc_util.c get_time()
  230. s|sec|secs|ms|msec|msecs|us|usec|usecs)
  231. CUR_TARGET_STRING="target ${CUR_TARGET_VALUE}${CUR_TARGET_UNIT}"
  232. UNIT_VALID="1"
  233. ;;
  234. esac
  235. fi
  236. case ${CUR_TARGET_UNIT} in
  237. auto|Auto|AUTO)
  238. if [ ! -z "${CUR_LINK_KBPS}" ];
  239. then
  240. TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS )
  241. TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US )
  242. CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}"
  243. AUTO_TARGET="1"
  244. else
  245. sqm_logger "required link bandwidth in kbps not passed to get_target()."
  246. fi
  247. ;;
  248. esac
  249. if [ ! -z "${CUR_TARGET}" ];
  250. then
  251. if [ -z "${CUR_TARGET_VALUE}" -o -z "${UNIT_VALID}" ];
  252. then
  253. [ -z "$AUTO_TARGET" ] && sqm_logger "${CUR_TARGET} is not a well formed tc target specifier; e.g.: 5ms (or s, us), or the string auto."
  254. fi
  255. fi
  256. ;;
  257. esac
  258. # sqm_logger "target: ${CUR_TARGET_STRING}"
  259. echo $CUR_TARGET_STRING
  260. }
  261. # for low bandwidth links fq_codels default target of 5ms does not work too well
  262. # so increase target for slow links (note below roughly 2500kbps a single packet will \
  263. # take more than 5 ms to be tansfered over the wire)
  264. adapt_target_to_slow_link() {
  265. CUR_LINK_KBPS=$1
  266. CUR_EXTENDED_TARGET_US=
  267. MAX_PAKET_DELAY_IN_US_AT_1KBPS=$(( 1000 * 1000 *1540 * 8 / 1000 ))
  268. CUR_EXTENDED_TARGET_US=$(( ${MAX_PAKET_DELAY_IN_US_AT_1KBPS} / ${CUR_LINK_KBPS} )) # note this truncates the decimals
  269. # do not change anything for fast links
  270. [ "$CUR_EXTENDED_TARGET_US" -lt 5000 ] && CUR_EXTENDED_TARGET_US=5000
  271. case ${QDISC} in
  272. *codel|pie)
  273. echo "${CUR_EXTENDED_TARGET_US}"
  274. ;;
  275. esac
  276. }
  277. # codel looks at a whole interval to figure out wether observed latency stayed below target
  278. # if target >= interval that will not work well, so increase interval by the same amonut that target got increased
  279. adapt_interval_to_slow_link() {
  280. CUR_TARGET_US=$1
  281. case ${QDISC} in
  282. *codel)
  283. CUR_EXTENDED_INTERVAL_US=$(( (100 - 5) * 1000 + ${CUR_TARGET_US} ))
  284. echo "interval ${CUR_EXTENDED_INTERVAL_US}us"
  285. ;;
  286. pie)
  287. ## not sure if pie needs this, probably not
  288. #CUR_EXTENDED_TUPDATE_US=$(( (30 - 20) * 1000 + ${CUR_TARGET_US} ))
  289. #echo "tupdate ${CUR_EXTENDED_TUPDATE_US}us"
  290. ;;
  291. esac
  292. }
  293. # set quantum parameter if available for this qdisc
  294. get_quantum() {
  295. case $QDISC in
  296. *fq_codel|fq_pie|drr) echo quantum $1 ;;
  297. *) ;;
  298. esac
  299. }
  300. # only show limits to qdiscs that can handle them...
  301. # Note that $LIMIT contains the default limit
  302. get_limit() {
  303. CURLIMIT=$1
  304. case $QDISC in
  305. *codel|*pie|pfifo_fast|sfq|pfifo) [ -z ${CURLIMIT} ] && CURLIMIT=${LIMIT} # use the global default limit
  306. ;;
  307. bfifo) [ -z "$CURLIMIT" ] && [ ! -z "$LIMIT" ] && CURLIMIT=$(( ${LIMIT} * $( cat /sys/class/net/${IFACE}/mtu ) )) # bfifo defaults to txquelength * MTU,
  308. ;;
  309. *) sqm_logger "${QDISC} does not support a limit"
  310. ;;
  311. esac
  312. sqm_logger "get_limit: $1 CURLIMIT: ${CURLIMIT}"
  313. if [ ! -z "$CURLIMIT" ]
  314. then
  315. echo "limit ${CURLIMIT}"
  316. fi
  317. }
  318. get_ecn() {
  319. CURECN=$1
  320. #sqm_logger CURECN: $CURECN
  321. case ${CURECN} in
  322. ECN)
  323. case $QDISC in
  324. *codel|*pie|*red)
  325. CURECN=ecn
  326. ;;
  327. *)
  328. CURECN=""
  329. ;;
  330. esac
  331. ;;
  332. NOECN)
  333. case $QDISC in
  334. *codel|*pie|*red)
  335. CURECN=noecn
  336. ;;
  337. *)
  338. CURECN=""
  339. ;;
  340. esac
  341. ;;
  342. *)
  343. sqm_logger "ecn value $1 not handled"
  344. ;;
  345. esac
  346. #sqm_logger "get_ECN: $1 CURECN: ${CURECN} IECN: ${IECN} EECN: ${EECN}"
  347. echo ${CURECN}
  348. }
  349. # This could be a complete diffserv implementation
  350. diffserv() {
  351. interface=$1
  352. prio=1
  353. # Catchall
  354. $TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \
  355. match ip protocol 0 0x00 flowid 1:12
  356. # Find the most common matches fast
  357. #fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
  358. fc 1:0 0x00 1:12 # BE
  359. fc 1:0 0x20 1:13 # CS1
  360. fc 1:0 0x10 1:11 # IMM
  361. fc 1:0 0xb8 1:11 # EF
  362. fc 1:0 0xc0 1:11 # CS3
  363. fc 1:0 0xe0 1:11 # CS6
  364. fc 1:0 0x90 1:11 # AF42 (mosh)
  365. # Arp traffic
  366. $TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
  367. prio=$(($prio + 1))
  368. }
  369. diffserv_pppoe() {
  370. interface=$1
  371. prio=1
  372. # Catchall
  373. $TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \
  374. match ip protocol 0 0x00 flowid 1:12
  375. # Find the most common matches fast
  376. #fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
  377. fc_pppoe 1:0 0x00 1:12 # BE
  378. fc_pppoe 1:0 0x20 1:13 # CS1
  379. fc_pppoe 1:0 0x10 1:11 # IMM
  380. fc_pppoe 1:0 0xb8 1:11 # EF
  381. fc_pppoe 1:0 0xc0 1:11 # CS3
  382. fc_pppoe 1:0 0xe0 1:11 # CS6
  383. fc_pppoe 1:0 0x90 1:11 # AF42 (mosh)
  384. # Arp traffic
  385. $TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
  386. prio=$(($prio + 1))
  387. }