Renovate the rahunas-bandwidth wrapper script
[rahunas] / tools / rahunas-bandwidth.in
1 #!/bin/sh
2 # File: rahunas-bandwidth
3 # Description: The bandwidth shaper wrapper script for RahuNAS
4
5 prefix=@prefix@
6 PATH=/sbin:/bin:/usr/sbin:/usr/bin:${prefix}/sbin:${prefix}/bin
7 exec_prefix=${prefix}
8
9 TC=/sbin/tc
10 IP=/sbin/ip
11 BC=/usr/bin/bc
12 IFCONFIG=/sbin/ifconfig
13
14 INIT=/etc/default/rahunas
15 RUN=/var/run/rahunas-bandwidth
16
17 test -f $INIT || exit 0
18 . $INIT
19
20 # Interface Speed (kbit)
21 INTERFACE_SPEED=${INTERFACE_SPEED:-51200}
22 QDISC=htb
23
24 MAX_DOWN_SPEED=${INTERFACE_SPEED}
25 MAX_UP_SPEED=${INTERFACE_SPEED}
26
27 R2Q_DOWN=${R2Q_DOWN:-10}
28 R2Q_UP=${R2Q_UP:-10}
29 HZ=${HZ:-1000}
30 MAX_BURST_PCT=${MAX_BURST_PCT:-70}
31
32 get_iface_burst () {
33   speed=$1
34   echo "${speed}/8/${HZ}" | $BC
35 }
36
37 get_max_burst_kb () {
38   direction=$1
39   if [ "x$direction" = "xdown" ]; then
40     echo "${MAX_DOWN_SPEED}/8/${HZ}*${MAX_BURST_PCT}/100" | $BC
41   else
42     echo "${MAX_UP_SPEED}/8/${HZ}*${MAX_BURST_PCT}/100" | $BC
43   fi
44 }
45
46 get_max_burst () {
47   direction=$1
48   max_burst_kb=`get_max_burst_kb $direction`
49   echo "$max_burst_kb * 1024" | $BC
50 }
51
52 get_burst () {
53   speed=$1
54   direction=$2
55   max_burst=`get_max_burst $direction`
56
57   b=`echo "${speed}/8/${HZ}" | $BC`
58
59   if [ $b -lt 2048 ]; then
60     echo 2048
61   elif [ $b -gt $max_burst ]; then
62     echo $max_burst
63   else
64     echo $b
65   fi
66 }
67
68 INTERFACE_ID="0xffff"
69 DEFAULT_ID="0xfffe"
70
71 MESSAGE=""
72
73
74 test "$RUN_DAEMON" = "yes" || exit 0
75 test -f $RAHUNAS_CONFIG || exit 1
76
77 SHAPING_DOWN_INF=""
78 SHAPING_UP_INF=""
79
80 MSG_NOT_COMPLETED="NOT COMPLETED"
81
82 get_config_value () {
83   section=$1
84   key=$2
85   file=$3
86
87   test -n "$file" || return
88   test -e "$file" && cat "$file" | sed -e "0,/$section = {/ ! { /}/,/$section = {/ ! s/^/>>/ }" | grep "^>>" | sed -e "s/^>>//g" | grep -w "$key" | cut -d= -f2 | sed "s/^ *\(.*[^ ]\) *$/\1/" | sed 's/"//g'
89 }
90
91 BANDWIDTH_SHAPE=`get_config_value main bandwidth_shape $RAHUNAS_CONFIG`
92
93 test "$BANDWIDTH_SHAPE" = "yes" || exit 0
94
95 interface_setting () {
96   if [ "x$QDISC" = "xhfsc" ]; then
97     qdisc_down_opt="hfsc default ${DEFAULT_ID}"
98     qdisc_up_opt="hfsc default ${DEFAULT_ID}"
99     class_down_opt="hfsc sc rate ${MAX_DOWN_SPEED}kbit ul rate ${INTERFACE_SPEED}kbit"
100     class_up_opt="hfsc sc rate ${MAX_UP_SPEED}kbit ul rate ${INTERFACE_SPEED}kbit"
101     class_down_iface_opt=$class_down_opt
102     class_down_default_opt=$class_down_opt
103     class_up_iface_opt=$class_up_opt
104     class_up_default_opt=$class_up_opt
105   else
106     qdisc_down_opt="htb default ${DEFAULT_ID} r2q ${R2Q_DOWN}"
107     qdisc_up_opt="htb default ${DEFAULT_ID} r2q ${R2Q_UP}"
108     class_down_opt="htb rate ${MAX_DOWN_SPEED}kbit ceil ${INTERFACE_SPEED}kbit"
109     class_up_opt="htb rate ${MAX_UP_SPEED}kbit ceil ${INTERFACE_SPEED}kbit"
110     class_down_iface_opt="$class_down_opt burst `get_iface_burst ${MAX_DOWN_SPEED}`Kb"
111     class_down_default_opt="$class_down_opt burst `get_max_burst_kb down`Kb"
112     class_up_iface_opt="$class_up_opt burst `get_iface_burst ${MAX_UP_SPEED}`Kb"
113     class_up_default_opt="$class_up_opt burst `get_max_burst_kb up`Kb"
114   fi
115
116   case "$1" in
117     start)
118       # Interface Downlink
119       $TC qdisc add dev $SHAPING_DOWN_INF root handle 1: $qdisc_down_opt
120
121       $TC class add dev $SHAPING_DOWN_INF parent 1: \
122         classid 1:${INTERFACE_ID} $class_down_iface_opt
123       $TC class add dev $SHAPING_DOWN_INF parent 1:${INTERFACE_ID} \
124         classid 1:${DEFAULT_ID} $class_down_default_opt
125
126       # Interface Uplink
127       $IFCONFIG $SHAPING_UP_INF up
128       $TC qdisc add dev $SHAPING_UP_INF root handle 1: $qdisc_up_opt
129
130       $TC class add dev $SHAPING_UP_INF parent 1: \
131         classid 1:${INTERFACE_ID} $class_up_iface_opt
132       $TC class add dev $SHAPING_UP_INF parent 1:${INTERFACE_ID} \
133         classid 1:${DEFAULT_ID} $class_up_default_opt
134
135       # Redirect incoming traffic to IFB
136       $TC qdisc add dev $SHAPING_DOWN_INF ingress
137       $TC filter add dev $SHAPING_DOWN_INF parent ffff: protocol ip prio 50 u32 \
138         match u32 0 0 flowid 1:${INTERFACE_ID} \
139         action mirred egress redirect dev $SHAPING_UP_INF
140
141       # Hash filter
142       # Interface Downlink
143       $TC filter add dev $SHAPING_DOWN_INF parent 1: prio 5 protocol ip u32
144       $TC filter add dev $SHAPING_DOWN_INF parent 1: prio 5 handle 2: \
145         protocol ip u32 divisor 256
146       $TC filter add dev $SHAPING_DOWN_INF parent 1: prio 5 protocol ip u32 \
147         ht 800:: match ip dst 0.0.0.0/0 hashkey mask 0xff000000 at 16 link 2:
148
149       # Interface Uplink
150       $TC filter add dev $SHAPING_UP_INF parent 1: prio 50 protocol ip u32
151       $TC filter add dev $SHAPING_UP_INF parent 1: prio 50 handle 2: \
152         protocol ip u32 divisor 256
153       $TC filter add dev $SHAPING_UP_INF parent 1: prio 50 protocol ip u32 \
154         ht 800:: match ip src 0.0.0.0/0 hashkey mask 0xff000000 at 12 link 2:
155       ;;
156   stop)
157       # Redirect incoming traffic to IFB
158       $TC filter del dev $SHAPING_DOWN_INF parent ffff: protocol ip prio 50
159       $TC qdisc del dev $SHAPING_DOWN_INF ingress
160
161       # Interface Downlink
162       $TC qdisc del dev $SHAPING_DOWN_INF root
163
164       # Interface Uplink
165       $TC qdisc del dev $SHAPING_UP_INF root
166
167       $IFCONFIG $SHAPING_UP_INF down
168       ;;
169   esac
170 }
171
172 start () {
173   if [ ! -e $RUN ]; then
174     interface_setting start
175     MESSAGE="Start OK"
176     touch $RUN
177   fi
178 }
179
180 stop () {
181   if [ -e $RUN ]; then
182     interface_setting stop
183     MESSAGE="Stop OK"
184     rm -f $RUN
185   fi
186 }
187
188 usage_add() {
189   echo "Usage: $1 add ID IP DOWNSPEED UPSPEED DOWN_IF UP_IF"
190   echo "         ID - ID number from 1 to 9900"
191   echo "         IP - IPv4 Address"
192   echo "  DOWNSPEED - Download speed (bits/s)"
193   echo "    UPSPEED - Upload speed (bits/s)"
194   echo "    DOWN_IF - Downstream interface (eth0, eth1, vlan1, ...)"
195   echo "      UP_IF - Upstream interface (ifb0, ifb1, ...)"
196 }
197
198 usage_del() {
199   echo "Usage: $1 del ID DOWN_IF UP_IF"
200   echo "         ID - ID number from 1 to 9900"
201   echo "    DOWN_IF - Downstream interface (eth0, eth1, vlan1, ...)"
202   echo "      UP_IF - Upstream interface (ifb0, ifb1, ...)"
203 }
204
205 usage() {
206   echo "Usage: $1 {start|stop|restart|add|del}"
207   echo "       $1 add --help"
208   echo "       $1 del --help"
209 }
210
211 filter_add() {
212   ID=$1
213   IP=$2
214   TYPE=$3
215
216   if [ "x$TYPE" = "xdownlink" ]; then
217     iface=${SHAPING_DOWN_INF}
218     filter_opt="dst"
219     offset=16
220     prio=5
221   else
222     iface=${SHAPING_UP_INF}
223     filter_opt=src
224     offset=12
225     prio=50
226   fi
227
228   ip1=`echo $IP | cut -d. -f1`
229   ip2=`echo $IP | cut -d. -f2`
230   ip3=`echo $IP | cut -d. -f3`
231   ip4=`echo $IP | cut -d. -f4`
232
233   ht1=`echo "obase=16;$ip1" | $BC | awk '{print tolower($0)}'`
234   ht2=`echo "obase=16;$ip2" | $BC | awk '{print tolower($0)}'`
235   ht3=`echo "obase=16;$ip3" | $BC | awk '{print tolower($0)}'`
236   ht4=`echo "obase=16;$ip4" | $BC | awk '{print tolower($0)}'`
237
238   $TC filter show dev ${iface} | grep -w "fh a${ht1}:" >/dev/null 2>&1
239   if [ $? != 0 ]; then
240     $TC filter add dev ${iface} parent 1: prio $prio handle a${ht1}: \
241       protocol ip u32 divisor 256
242     $TC filter add dev ${iface} parent 1: prio $prio protocol ip u32 \
243       ht 2:${ht1}: match ip $filter_opt 0.0.0.0/0 \
244       hashkey mask 0xff0000 at $offset link a${ht1}:
245   fi
246
247   $TC filter show dev ${iface} | grep -w "fh b${ht2}:" >/dev/null 2>&1
248   if [ $? != 0 ]; then
249     $TC filter add dev ${iface} parent 1: prio $prio handle b${ht2}: \
250       protocol ip u32 divisor 256
251     $TC filter add dev ${iface} parent 1: prio $prio protocol ip u32 \
252       ht a${ht1}:${ht2}: match ip $filter_opt 0.0.0.0/0 \
253       hashkey mask 0xff00 at $offset link b${ht2}:
254   fi
255
256   $TC filter show dev ${iface} | grep -w "fh c${ht3}:" >/dev/null 2>&1
257   if [ $? != 0 ]; then
258     $TC filter add dev ${iface} parent 1: prio $prio handle c${ht3}: \
259       protocol ip u32 divisor 256
260     $TC filter add dev ${iface} parent 1: prio $prio protocol ip u32 \
261       ht b${ht2}:${ht3}: match ip $filter_opt 0.0.0.0/0 \
262       hashkey mask 0xff at $offset link c${ht3}:
263   fi
264
265   $TC filter show dev ${iface} | grep -w "fh c${ht3}:${ht4}:800" >/dev/null 2>&1
266   if [ $? != 0 ]; then
267     $TC filter add dev ${iface} parent 1: prio $prio protocol ip u32 \
268       ht c${ht3}:${ht4}: match ip $filter_opt ${IP}/32 flowid 1:${ID}
269   else
270     $TC filter del dev ${iface} parent 1: prio $prio protocol ip \
271       handle c${ht3}:${ht4}:800 u32
272     $TC filter add dev ${iface} parent 1: prio $prio protocol ip u32 \
273       ht c${ht3}:${ht4}: match ip $filter_opt ${IP}/32 flowid 1:${ID}
274   fi
275 }
276
277 filter_del() {
278   ID=$1
279   TYPE=$2
280
281   if [ "x$TYPE" = "xdownlink" ]; then
282     iface=$SHAPING_DOWN_INF
283     prio=5
284   else
285     iface=$SHAPING_UP_INF
286     prio=50
287   fi
288
289   handle=`$TC -p filter show dev ${iface} | grep -w "flowid 1:${ID}" | grep "fh " | awk -F"fh " '{print $2}' | awk '{print $1}'`
290   for h in $handle; do
291     $TC filter del dev $iface parent 1: prio $prio protocol ip handle $h u32
292   done
293 }
294
295 bw_add() {
296   ID=$1
297
298   if [ -z "$ID" ]; then
299     MESSAGE=$MSG_NOT_COMPLETED
300     return
301   fi
302
303   IP=$2
304   DOWNSPEED=$3
305   UPSPEED=$4
306   MAX_DOWN=`echo "$MAX_DOWN_SPEED*1024" | $BC`
307   MAX_UP=`echo "$MAX_UP_SPEED*1024" | $BC`
308
309   if [ $DOWNSPEED -gt $MAX_DOWN ]; then
310     DOWNSPEED=$MAX_DOWN
311   fi
312
313   if [ $UPSPEED -gt $MAX_UP ]; then
314     UPSPEED=$MAX_UP
315   fi
316
317   DOWN_MBPS=`echo "scale=2; $DOWNSPEED/1048576" | $BC | awk '{printf "%.2f", $0}'`
318   UP_MBPS=`echo "scale=2; $UPSPEED/1048576" | $BC | awk '{printf "%.2f", $0}'`
319   MESSAGE="Add shaping slot id: $ID for $IP with D/U=${DOWN_MBPS}/${UP_MBPS} Mbits/s"
320
321   if [ "x$QDISC" = "xhfsc" ]; then
322     class_down_opt="hfsc sc rate ${DOWNSPEED}bit ul rate ${DOWNSPEED}"
323     class_up_opt="hfsc sc rate ${UPSPEED}bit ul rate ${UPSPEED}"
324   else
325     class_down_opt="htb rate ${DOWNSPEED}bit ceil ${DOWNSPEED}bit burst `get_burst ${DOWNSPEED} down`"
326     class_up_opt="htb rate ${UPSPEED}bit ceil ${UPSPEED}bit burst `get_burst ${UPSPEED} up`"
327   fi
328
329   # Downlink
330   $TC class add dev $SHAPING_DOWN_INF parent 1:${INTERFACE_ID} \
331     classid 1:${ID} $class_down_opt
332   if [ $? != 0 ]; then
333     $TC class replace dev $SHAPING_DOWN_INF parent 1:${INTERFACE_ID} \
334       classid 1:${ID} $class_down_opt
335   fi
336
337   filter_add $ID $IP downlink
338
339   # Uplink
340   $TC class add dev $SHAPING_UP_INF parent 1:${INTERFACE_ID} \
341     classid 1:${ID} $class_up_opt
342   if [ $? != 0 ]; then
343     $TC class replace dev $SHAPING_UP_INF parent 1:${INTERFACE_ID} \
344       classid 1:${ID} $class_up_opt
345   fi
346
347   filter_add $ID $IP uplink
348 }
349
350 bw_del() {
351   ID=$1
352
353   if [ -z "$ID" ]; then
354     MESSAGE=$MSG_NOT_COMPLETED
355     return
356   fi
357
358   MESSAGE="Delete shaping slot id: $ID"
359
360   # Filter Removal
361   # Downlink/Uplink
362   filter_del $ID downlink
363   filter_del $ID uplink
364
365   # Class Removal
366   # Downlink/Uplink
367   $TC class del dev $SHAPING_DOWN_INF parent 1:${INTERFACE_ID} classid 1:${ID}
368   $TC class del dev $SHAPING_UP_INF parent 1:${INTERFACE_ID} classid 1:${ID}
369 }
370
371 chk_interface () {
372   SHAPING_DOWN_INF=$1
373   SHAPING_UP_INF=$2
374
375   iface_list=`$IFCONFIG -a | grep "Link encap" | awk '{print $1}'`
376
377   echo $iface_list | grep -w "$SHAPING_DOWN_INF" > /dev/null 2>&1
378   if [ $? -ne 0 ]; then
379     return 1
380   fi
381
382   echo $iface_list | grep -w "$SHAPING_UP_INF" > /dev/null 2>&1
383   if [ $? -ne 0 ]; then
384     return 1
385   fi
386
387   return 0
388 }
389
390 N=${prefix}/sbin/rahunas-bandwidth
391
392 case "$1" in
393   start)
394     if [ -z "$2" ] || [ -z "$3" ]; then
395       MESSAGE="$MSG_NOT_COMPLETED"
396     else
397       chk_interface $2 $3
398       if [ $? -eq 0 ]; then
399         RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
400         start
401       else
402         MESSAGE="$MSG_NOT_COMPLETED"
403       fi
404     fi
405
406     test -n "$MESSAGE" || MESSAGE="$MSG_NOT_COMPLETED"
407     echo $MESSAGE
408     ;;
409   stop)
410     if [ -z "$2" ] || [ -z "$3" ]; then
411       MESSAGE="$MSG_NOT_COMPLETED"
412     else
413       chk_interface $2 $3
414       if [ $? -eq 0 ]; then
415         RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
416         stop
417       else
418         MESSAGE="$MSG_NOT_COMPLETED"
419       fi
420     fi
421
422     test -n "$MESSAGE" || MESSAGE="$MSG_NOT_COMPLETED"
423     echo $MESSAGE
424
425     ;;
426   restart)
427     if [ -z "$2" ] || [ -z "$3" ]; then
428       MESSAGE="$MSG_NOT_COMPLETED"
429     else
430       chk_interface $2 $3
431       if [ $? -eq 0 ]; then
432         RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
433         stop
434         start
435       else
436         MESSAGE="$MSG_NOT_COMPLETED"
437       fi
438     fi
439
440     test -n "$MESSAGE" || MESSAGE="$MSG_NOT_COMPLETED"
441     echo $MESSAGE
442
443     ;;
444   add)
445     if [ "$2" = "--help" ]; then
446       usage_add $N
447       exit 3
448     fi
449
450     if [ $# != 7 ]; then
451       echo "Error: too few arguments"
452       usage_add $N
453       exit 1
454     fi
455
456     if [ -z "$6" ] || [ -z "$7" ]; then
457       MESSAGE="$MSG_NOT_COMPLETED"
458     else
459       chk_interface $6 $7
460       if [ $? -eq 0 ]; then
461         RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
462         bw_add $2 $3 $4 $5
463       else
464         MESSAGE="$MSG_NOT_COMPLETED"
465       fi
466     fi
467     echo $MESSAGE
468     ;;
469   del)
470     if [ "$2" = "--help" ]; then
471       usage_del $N
472       exit 3
473     fi
474
475     if [ $# != 4 ]; then
476       echo "Error: too few arguments"
477       usage_del $N
478       exit 1
479     fi
480     if [ -z "$3" ] || [ -z "$4" ]; then
481       MESSAGE="$MSG_NOT_COMPLETED"
482     else
483       chk_interface $3 $4
484       if [ $? -eq 0 ]; then
485         RUN=${RUN}-${SHAPING_DOWN_INF}-${SHAPING_UP_INF}
486         bw_del $2
487       else
488         MESSAGE="$MSG_NOT_COMPLETED"
489       fi
490     fi
491     echo $MESSAGE
492     ;;
493   *)
494     usage $N
495     exit 3
496     ;;
497 esac
498
499 exit 0