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.

477 lines
13 KiB

  1. #!/bin/sh /etc/rc.common
  2. # Internal uci firewall chains are flushed and recreated on reload, so
  3. # put custom rules into the root chains e.g. INPUT or FORWARD or into the
  4. # special user chains, e.g. input_wan_rule or postrouting_lan_rule.
  5. START=25
  6. USE_PROCD=1
  7. echo_err() {
  8. echo "$@" >&2
  9. }
  10. msg() {
  11. local level=$1; shift
  12. echo_err "$APPNAME[$level]: $*"
  13. }
  14. LOGLEVEL=${LOGLEVEL:-2}
  15. die() {
  16. local err=$1; shift
  17. e "$*"
  18. exit $err
  19. }
  20. APPNAME="trafficshaper"
  21. IPT_CHAIN=$APPNAME
  22. debug_exec(){
  23. local err
  24. d "exec: $*"
  25. if "$@"; then
  26. return 0
  27. else
  28. err="$?"
  29. fi
  30. e "exec[err=$err]: $*"
  31. return "$err"
  32. }
  33. IP="debug_exec ip"
  34. TC="debug_exec tc"
  35. IP4T="debug_exec iptables -w 5"
  36. IP6T="debug_exec ip6tables -w 5"
  37. #QDISC="cake autorate_ingress internet ethernet diffserv4 triple-isolate"
  38. QDISC="cake"
  39. REQ_MODULES="sch_htb sch_cake act_connmark act_mirred em_u32"
  40. REQ_CMDS="ip tc iptables"
  41. preinit(){
  42. [ "$LOGLEVEL" -ge 1 ] && e() { msg ERROR "$@"; } || e() { true; }
  43. [ "$LOGLEVEL" -ge 2 ] && v() { msg INFO "$@"; } || v() { true; }
  44. [ "$LOGLEVEL" -ge 3 ] && d() { msg DEBUG "$@"; } || d() { true; }
  45. [ "$LOGLEVEL" -ge 4 ] && set -x
  46. set -e
  47. }
  48. requires() {
  49. for module in $REQ_MODULES; do
  50. [ -d /sys/module/$module ] || insert_modules "$module" ||
  51. die 2 "cannot load $module. Please install kmod-$module"
  52. done
  53. for cmd in $REQ_CMDS; do
  54. command -v $cmd &>/dev/null ||
  55. die 2 "cannot find command $cmd. Please install $cmd"
  56. done
  57. if ! command -v ip6tables &>/dev/null; then
  58. v "Disabling IPv6 as ip6tables was not found"
  59. IP6T=true
  60. fi
  61. . /lib/functions/network.sh
  62. config_load $APPNAME
  63. }
  64. do_stop() {
  65. local only_int=$1
  66. preinit
  67. requires
  68. v "Stopping $APPNAME${only_int:+ for interface $only_int}"
  69. if [ -z "$only_int" ]; then
  70. d "Cleaning iptables"
  71. # Cleaning iptables
  72. for IPT in "$IP4T" "$IP6T"; do
  73. $IPT -t mangle -D FORWARD -j $IPT_CHAIN &>/dev/null || :
  74. $IPT -t mangle -F $IPT_CHAIN &>/dev/null || :
  75. $IPT -t mangle -X $IPT_CHAIN &>/dev/null || :
  76. $IPT -t mangle -F $IPT_CHAIN-classify &>/dev/null || :
  77. $IPT -t mangle -X $IPT_CHAIN-classify &>/dev/null || :
  78. done
  79. fi
  80. d "Cleaning tc"
  81. local dev_done int dev ifb interfaces
  82. if [ "$only_int" ]; then
  83. config_get type $only_int TYPE
  84. if [ "$type" != "wan" ]; then
  85. d "interface $only_int not found in trafficshaper config. Ignoring"
  86. return 0
  87. fi
  88. interfaces="$only_int"
  89. else
  90. interfaces="$(config_foreach echo wan)"
  91. fi
  92. for int in $interfaces; do
  93. d "Cleaning tc for interface $int"
  94. network_get_physdev dev "$int" ||
  95. die 1 "failed to get physical dev of interface $int"
  96. if echo "$dev_done" | grep -x -F -q "$dev"; then
  97. continue
  98. fi
  99. ifb="ifb_$dev"
  100. if [ ${#ifb} -gt 15 ]; then
  101. die 1 "ifb name too long: ${ifb}"
  102. fi
  103. $TC qdisc del dev ${ifb} root 2> /dev/null || :
  104. $TC qdisc del dev ${dev} root 2> /dev/null || :
  105. $TC qdisc del dev ${dev} ingress 2> /dev/null || :
  106. d "Removing ${ifb}..."
  107. $IP link set dev ${ifb} down 2>/dev/null || :
  108. $IP link delete dev ${ifb} 2>/dev/null || :
  109. intdev_done="$(echo "$dev_done"; echo -n $dev)"
  110. done
  111. }
  112. calc_bw() {
  113. local value=$1 reference=$2
  114. case "${value}" in
  115. *%) echo "$((${value%\%} * reference / 100 ))";;
  116. *) echo ${value};;
  117. esac
  118. }
  119. mask_range() {
  120. local mask=$(($1)) n=0 fsb
  121. if [ $mask -le 0 ]; then
  122. e "mask '$1' must be greater than 0 (have a sequence of set bit)"
  123. return 2
  124. fi
  125. while [ "$((mask & 0x1))" -eq 0 ]; do
  126. mask=$((mask >> 1))
  127. : $((n++))
  128. done
  129. fsb="$n"
  130. while [ "$((mask & 0x1))" -eq 1 ]; do
  131. mask=$((mask >> 1))
  132. : $((n++))
  133. done
  134. if [ $mask -ne 0 ]; then
  135. e "mask '$1' must be a continuos sequence of set bit"
  136. return 2
  137. fi
  138. echo $fsb $((n-1))
  139. return 0
  140. }
  141. start_iptables(){
  142. d "Creating iptables mangle rules"
  143. config_get mark_mask globals mark_mask 0xFF
  144. mark_mask=$(printf '0x%X\n' $(($mark_mask)))
  145. local fsb_lst class_id_max class_id_shift
  146. fsb_lst=$(mask_range $mark_mask)
  147. class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))+1))
  148. class_id_shift=$((${fsb_lst% *}))
  149. d "General iptables rules:"
  150. for IPT in "$IP4T" "$IP6T"; do
  151. $IPT -t mangle -N $IPT_CHAIN
  152. $IPT -t mangle -N $IPT_CHAIN-classify
  153. $IPT -t mangle -A FORWARD -j $IPT_CHAIN
  154. $IPT -t mangle -A $IPT_CHAIN -j CONNMARK --restore-mark --nfmask $mark_mask --ctmask $mark_mask \
  155. -m comment --comment "Get previous class"
  156. $IPT -t mangle -A $IPT_CHAIN -m mark --mark 0x0/$mark_mask -j $IPT_CHAIN-classify \
  157. -m comment --comment "If no class, try to classify"
  158. done
  159. d "Classes iptables rules:"
  160. local class_reserved_uplink class_reserved_downlink class_nets i=2 xi default_class_id
  161. for class in $(config_foreach echo class); do
  162. config_get class_reserved_uplink $class reserved_uplink
  163. config_get class_reserved_downlink $class reserved_downlink
  164. config_get class_nets $class network
  165. if [ "$class" = default ]; then
  166. default_class_id=$i
  167. if [ -z "$class_reserved_uplink" -a -z "$class_reserved_downlink" ] ; then
  168. die 2 "class default must defined either reserved uplink or downlink!"
  169. fi
  170. if [ "$class_nets" ]; then
  171. die 2 "class default must not have any network defined!"
  172. fi
  173. else
  174. if [ "$i" -ge "$class_id_max" ]; then
  175. die 1 "Max client classes reached. Please, use less classes or increase option mark_mask '$mark_mask' in globals. Current mask allows only $((class_id_max-2)) classes if default is the last one."
  176. fi
  177. fi
  178. xi=$(printf '0x%X\n' $(((i-1)<<class_id_shift)))
  179. for class_net in $class_nets; do
  180. case $class_net in
  181. *:*) IPT="$IP6T" ;;
  182. *.*) IPT="$IP4T" ;;
  183. *) die 2 "Unknown address family of network $class_net in class $class!"
  184. esac
  185. if [ "$class_reserved_uplink" ]; then
  186. $IPT -t mangle -A $IPT_CHAIN-classify -s $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
  187. -m comment --comment "$APPNAME-$class up"
  188. fi
  189. if [ "$class_reserved_downlink" ]; then
  190. $IPT -t mangle -A $IPT_CHAIN-classify -d $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
  191. -m comment --comment "$APPNAME-$class down"
  192. fi
  193. done
  194. : $((i++))
  195. done
  196. if [ -z "$default_class_id" ]; then
  197. die 2 "No default class defined!"
  198. fi
  199. $IP4T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
  200. $IP6T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
  201. }
  202. start_tc_interface() {
  203. local int=$1; shift
  204. local dev=$1; shift
  205. local default_class_id=$1; shift
  206. config_get mark_mask globals mark_mask 0xFF
  207. local fsb_lst class_id_max class_id_shift
  208. fsb_lst=$(mask_range $mark_mask)
  209. class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))))
  210. class_id_shift=$((${fsb_lst% *}))
  211. local downlink uplink type
  212. config_get downlink $int downlink
  213. config_get uplink $int uplink
  214. d "Creating tc rules for $int ($dev)"
  215. local dev_down dev_up
  216. if [ "$downlink" ]; then
  217. local ifb="ifb_$dev"
  218. if [ ${#ifb} -gt 15 ]; then
  219. die 1 "ifb name too long: ${ifb}"
  220. fi
  221. d "Creating ${ifb}..."
  222. $IP link add name ${ifb} type ifb
  223. $IP link set dev $ifb up
  224. d "Redirect ingress $dev to $ifb..."
  225. $TC qdisc add dev $dev handle ffff: ingress
  226. $TC filter add dev $dev parent ffff: protocol all u32 match u32 0 0 action connmark action mirred egress redirect dev $ifb
  227. dev_down=$ifb
  228. else
  229. dev_down=
  230. fi
  231. if [ "$uplink" ]; then
  232. dev_up="$dev"
  233. fi
  234. # Download/Upload
  235. if [ "$dev_down" ]; then
  236. tc qdisc add dev $dev_down root handle 1: htb default "$default_class_id"
  237. tc class add dev $dev_down parent 1: classid 1:1 htb rate $(calc_bw ${downlink})kbit burst 500k quantum 1500
  238. fi
  239. if [ "$dev_up" ]; then
  240. tc qdisc add dev $dev_up root handle 1: htb default "$default_class_id"
  241. tc class add dev $dev_up parent 1: classid 1:1 htb rate $(calc_bw ${uplink})kbit burst 500k quantum 1500
  242. fi
  243. v "$int($dev):" \
  244. "${downlink:+downlink of ${downlink}kbit}"\
  245. "${uplink:+uplink of ${uplink}kbit}"\
  246. local class class_reserved_downlink class_reserved_uplink class_allowed_downlink class_allowed_uplink class_nets class_net i=2
  247. for class in $(config_foreach echo class); do
  248. config_get class_reserved_downlink $class reserved_downlink
  249. if [ "$class_reserved_downlink" ]; then
  250. if [ "$dev_down" ]; then
  251. class_reserved_downlink=$(calc_bw $class_reserved_downlink $downlink)
  252. config_get class_allowed_downlink $class allowed_downlink "$class_reserved_downlink"
  253. class_allowed_downlink=$(calc_bw $class_allowed_downlink $downlink)
  254. else
  255. e "class $class defines reserved downlink but not wan $int. Downlink shapping will be ignored"
  256. class_reserved_downlink=
  257. fi
  258. elif [ "$dev_down" ]; then
  259. e "class $class does not define reserved downlink but wan $int does. Downlink shapping will use default class"
  260. fi
  261. if [ "$class_allowed_downlink" -lt "$class_reserved_downlink" ]; then
  262. die 1 "Allowed downlink bandwitdh in class $class must not be smaller than reserved downlink."
  263. fi
  264. config_get class_reserved_uplink $class reserved_uplink
  265. if [ "$class_reserved_uplink" ]; then
  266. if [ "$dev_up" ]; then
  267. class_reserved_uplink=$(calc_bw $class_reserved_uplink $uplink)
  268. config_get class_allowed_uplink $class allowed_uplink "$class_reserved_uplink"
  269. class_allowed_uplink=$(calc_bw $class_allowed_uplink $uplink)
  270. else
  271. e "class $class defines reserved uplink but not wan $int. Downlink shapping will be ignored"
  272. class_reserved_uplink=
  273. fi
  274. elif [ "$dev_up" ]; then
  275. e "class $class does not define reserved uplink but wan $int does. Downlink shapping will use default class"
  276. fi
  277. if [ -n "$class_allowed_uplink" -a -n "$class_reserved_uplink" ] && [ "$class_allowed_uplink" -lt "$class_reserved_uplink" ]; then
  278. die 1 "Allowed uplink bandwitdh in class $class must not be smaller than reserved uplink."
  279. fi
  280. v "$int($dev): $class(class 1:$i) will have" \
  281. "${class_reserved_downlink:+download of ${class_reserved_downlink}kbit (up to ${class_allowed_downlink}kbit)}"\
  282. "${class_reserved_uplink:+upload of ${class_reserved_uplink}kbit up (up to ${class_allowed_uplink}kbit)}"
  283. xi=$(printf '0x%X\n' $(((i-1)<<class_id_shift)))
  284. if [ "$class_reserved_uplink" ]; then
  285. $TC class add dev $dev_up parent 1:1 classid 1:$i htb rate ${class_reserved_uplink}kbit ceil ${class_allowed_uplink}kbit quantum 1500 burst 50k
  286. $TC qdisc add dev $dev_up parent 1:$i handle $i: $QDISC
  287. if [ "$class" != default ]; then
  288. $TC filter add dev $dev_up parent 1: protocol ip prio $i handle ${xi}/$mark_mask fw flowid 1:$i
  289. fi
  290. fi
  291. if [ "$class_reserved_downlink" ]; then
  292. $TC class add dev $dev_down parent 1:1 classid 1:$i htb rate ${class_reserved_downlink}kbit ceil ${class_allowed_downlink}kbit quantum 1500 burst 50k
  293. $TC qdisc add dev $dev_down parent 1:$i handle $i: $QDISC
  294. if [ "$class" != default ]; then
  295. $TC filter add dev $dev_down parent 1: protocol ip prio $i handle ${xi}/$mark_mask fw flowid 1:$i
  296. fi
  297. fi
  298. : $((i++))
  299. done
  300. }
  301. start_tc() {
  302. d "Creating tc rules"
  303. local dev_done int dev interfaces
  304. local default_class_id=$1; shift
  305. local only_int=$1
  306. if [ "$only_int" ]; then
  307. config_get type $only_int TYPE
  308. if [ "$type" != "wan" ]; then
  309. d "interface $only_int not found in trafficshaper config. Ignoring"
  310. return 0
  311. fi
  312. interfaces="$only_int"
  313. else
  314. interfaces="$(config_foreach echo wan)"
  315. fi
  316. for int in $interfaces; do
  317. network_get_physdev dev "$int" ||
  318. die 1 "failed to get physical dev of interface $int"
  319. if echo "$dev_done" | grep -x -F -q "$dev"; then
  320. e "$int uses $dev which was already configured. Only list each WAN once. Skipping..."
  321. continue
  322. fi
  323. start_tc_interface $int $dev $ifb "$default_class_id"
  324. intdev_done="$(echo "$dev_done"; echo -n $dev)"
  325. done
  326. }
  327. do_start() {
  328. local only_int=$1 type
  329. preinit
  330. (LOGLEVEL=0 do_stop "$only_int")
  331. requires
  332. trap "set +e; do_stop $only_int" EXIT
  333. v "Starting $APPNAME${only_int:+ for interface $only_int}"
  334. local default_class_id
  335. if ! default_class_id=$(i=2 config_foreach 'eval echo $((i++))' class '| grep " default"'); then
  336. die 2 "No default class defined!"
  337. fi
  338. default_class_id=${default_class_id% *}
  339. [ "$only_int" ] || start_iptables
  340. start_tc "$default_class_id" "$only_int"
  341. trap - EXIT
  342. }
  343. start_service() {
  344. ( do_start )
  345. }
  346. stop_service() {
  347. ( do_stop )
  348. }
  349. restart_service() {
  350. ( do_start )
  351. }
  352. is_running() {
  353. $IP4T -t mangle -L $IPT_CHAIN &>/dev/null
  354. }
  355. reload_service() {
  356. preinit
  357. if ! is_running; then
  358. d "Not running. Nothing to reload"
  359. return 0
  360. fi
  361. logger -t "$APPNAME" "Reloading $*..."
  362. ( do_start "$@" )
  363. }
  364. add_interface_trigger() {
  365. procd_add_interface_trigger "interface.update" "$1" /etc/init.d/$APPNAME reload $1
  366. }
  367. service_triggers() {
  368. preinit; set +e
  369. requires
  370. procd_add_reload_trigger "$APPNAME"
  371. config_foreach add_interface_trigger wan
  372. procd_open_validate
  373. validate_trafficshaper_global
  374. validate_trafficshaper_wan
  375. validate_trafficshaper_class
  376. procd_close_validate
  377. }
  378. validate_trafficshaper_global() {
  379. uci_validate_section $APPNAME global "${1}" \
  380. 'mark_mask:uinteger:0xFF'
  381. }
  382. validate_trafficshaper_wan() {
  383. uci_validate_section "$APPNAME" wan "${1}" \
  384. 'downlink:uinteger' \
  385. 'uplink:uinteger'
  386. }
  387. validate_trafficshaper_class() {
  388. uci_validate_section "$APPNAME" class "${1}" \
  389. 'network:cidr' \
  390. 'reserved_downlink:or(uinteger, string)' \
  391. 'reserved_uplink:or(uinteger, string)' \
  392. 'allowed_downlink:or(uinteger, string)' \
  393. 'allowed_uplink:or(uinteger, string)'
  394. }
  395. boot() {
  396. LOGLEVEL=1 start
  397. }