functions.sh 15 KB

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