net/mwan3: fix mwan3track kill execution
[feed/packages.git] / net / mwan3 / files / lib / mwan3 / mwan3.sh
1 #!/bin/sh
2
3 IP4="ip -4"
4 IP6="ip -6"
5 IPS="ipset"
6 IPT4="iptables -t mangle -w"
7 IPT6="ip6tables -t mangle -w"
8 LOG="logger -t mwan3[$$] -p"
9 CONNTRACK_FILE="/proc/net/nf_conntrack"
10
11 # mwan3's MARKing mask (at least 3 bits should be set)
12 MMX_MASK=0xff00
13
14 # counts how many bits are set to 1
15 # n&(n-1) clears the lowest bit set to 1
16 mwan3_count_one_bits()
17 {
18 local count n
19 count=0
20 n=$(($1))
21 while [ "$n" -gt "0" ]; do
22 n=$((n&(n-1)))
23 count=$((count+1))
24 done
25 echo $count
26 }
27
28 # maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
29 # which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
30 # 0 0 0 0 0 1 0 1 (0x05) 1st parameter
31 # 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
32 # 1 0 1 result
33 mwan3_id2mask()
34 {
35 local bit_msk bit_val result
36 bit_val=0
37 result=0
38 for bit_msk in $(seq 0 31); do
39 if [ $((($2>>bit_msk)&1)) = "1" ]; then
40 if [ $((($1>>bit_val)&1)) = "1" ]; then
41 result=$((result|(1<<bit_msk)))
42 fi
43 bit_val=$((bit_val+1))
44 fi
45 done
46 printf "0x%x" $result
47 }
48
49 # mark mask constants
50 MM_BIT_CNT=$(mwan3_count_one_bits MMX_MASK)
51 MM_DEFAULT=$(((1<<MM_BIT_CNT)-1))
52 MM_BLACKHOLE=$(($MM_DEFAULT-2))
53 MM_UNREACHABLE=$(($MM_DEFAULT-1))
54
55 # MMX_DEFAULT should equal MMX_MASK
56 MMX_DEFAULT=$(mwan3_id2mask MM_DEFAULT MMX_MASK)
57 MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
58 MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
59
60 mwan3_lock() {
61 lock /var/run/mwan3.lock
62 }
63
64 mwan3_unlock() {
65 lock -u /var/run/mwan3.lock
66 }
67
68 mwan3_get_iface_id()
69 {
70 local _tmp _iface _iface_count
71
72 _iface="$2"
73
74 mwan3_get_id()
75 {
76 let _iface_count++
77 [ "$1" == "$_iface" ] && _tmp=$_iface_count
78 }
79 config_foreach mwan3_get_id interface
80 export "$1=$_tmp"
81 }
82
83 mwan3_set_connected_iptables()
84 {
85 local connected_network_v4 connected_network_v6
86
87 $IPS -! create mwan3_connected_v4 hash:net
88 $IPS create mwan3_connected_v4_temp hash:net
89
90 for connected_network_v4 in $($IP4 route | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
91 $IPS -! add mwan3_connected_v4_temp $connected_network_v4
92 done
93
94 for connected_network_v4 in $($IP4 route list table 0 | awk '{print $2}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
95 $IPS -! add mwan3_connected_v4_temp $connected_network_v4
96 done
97
98 $IPS add mwan3_connected_v4_temp 224.0.0.0/3
99
100 $IPS swap mwan3_connected_v4_temp mwan3_connected_v4
101 $IPS destroy mwan3_connected_v4_temp
102
103 $IPS -! create mwan3_connected_v6 hash:net family inet6
104 $IPS create mwan3_connected_v6_temp hash:net family inet6
105
106 for connected_network_v6 in $($IP6 route | awk '{print $1}' | egrep '([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])'); do
107 $IPS -! add mwan3_connected_v6_temp $connected_network_v6
108 done
109
110 $IPS swap mwan3_connected_v6_temp mwan3_connected_v6
111 $IPS destroy mwan3_connected_v6_temp
112
113 $IPS -! create mwan3_connected list:set
114 $IPS -! add mwan3_connected mwan3_connected_v4
115 $IPS -! add mwan3_connected mwan3_connected_v6
116 }
117
118 mwan3_set_general_rules()
119 {
120 local IP
121
122 for IP in "$IP4" "$IP6"; do
123
124 RULE_NO=$(($MM_BLACKHOLE+2000))
125 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
126 $IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole
127 fi
128
129 RULE_NO=$(($MM_UNREACHABLE+2000))
130 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
131 $IP rule add pref $RULE_NO fwmark $MMX_UNREACHABLE/$MMX_MASK unreachable
132 fi
133 done
134 }
135
136 mwan3_set_general_iptables()
137 {
138 local IPT
139
140 for IPT in "$IPT4" "$IPT6"; do
141
142 if ! $IPT -S mwan3_ifaces_in &> /dev/null; then
143 $IPT -N mwan3_ifaces_in
144 fi
145
146 if ! $IPT -S mwan3_connected &> /dev/null; then
147 $IPT -N mwan3_connected
148 $IPS -! create mwan3_connected list:set
149 $IPT -A mwan3_connected -m set --match-set mwan3_connected dst -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
150 fi
151
152 if ! $IPT -S mwan3_ifaces_out &> /dev/null; then
153 $IPT -N mwan3_ifaces_out
154 fi
155
156 if ! $IPT -S mwan3_rules &> /dev/null; then
157 $IPT -N mwan3_rules
158 fi
159
160 if ! $IPT -S mwan3_hook &> /dev/null; then
161 $IPT -N mwan3_hook
162 $IPT -A mwan3_hook -j CONNMARK --restore-mark --nfmask $MMX_MASK --ctmask $MMX_MASK
163 $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_ifaces_in
164 $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_connected
165 $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_ifaces_out
166 $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_rules
167 $IPT -A mwan3_hook -j CONNMARK --save-mark --nfmask $MMX_MASK --ctmask $MMX_MASK
168 $IPT -A mwan3_hook -m mark ! --mark $MMX_DEFAULT/$MMX_MASK -j mwan3_connected
169 fi
170
171 if ! $IPT -S PREROUTING | grep mwan3_hook &> /dev/null; then
172 $IPT -A PREROUTING -j mwan3_hook
173 fi
174
175 if ! $IPT -S OUTPUT | grep mwan3_hook &> /dev/null; then
176 $IPT -A OUTPUT -j mwan3_hook
177 fi
178 done
179 }
180
181 mwan3_create_iface_iptables()
182 {
183 local id family src_ip src_ipv6
184
185 config_get family $1 family ipv4
186 mwan3_get_iface_id id $1
187
188 [ -n "$id" ] || return 0
189
190 if [ "$family" == "ipv4" ]; then
191
192 ubus call network.interface.${1}_4 status &>/dev/null
193 if [ "$?" -eq "0" ]; then
194 network_get_ipaddr src_ip ${1}_4
195 else
196 network_get_ipaddr src_ip $1
197 fi
198
199 $IPS -! create mwan3_connected list:set
200
201 if ! $IPT4 -S mwan3_ifaces_in &> /dev/null; then
202 $IPT4 -N mwan3_ifaces_in
203 fi
204
205 if ! $IPT4 -S mwan3_ifaces_out &> /dev/null; then
206 $IPT4 -N mwan3_ifaces_out
207 fi
208
209 if ! $IPT4 -S mwan3_iface_in_$1 &> /dev/null; then
210 $IPT4 -N mwan3_iface_in_$1
211 fi
212
213 if ! $IPT4 -S mwan3_iface_out_$1 &> /dev/null; then
214 $IPT4 -N mwan3_iface_out_$1
215 fi
216
217 $IPT4 -F mwan3_iface_in_$1
218 $IPT4 -A mwan3_iface_in_$1 -i $2 -m set --match-set mwan3_connected src -m mark --mark 0x0/$MMX_MASK -m comment --comment "default" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
219 $IPT4 -A mwan3_iface_in_$1 -i $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
220
221 $IPT4 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null
222 $IPT4 -A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1
223
224 $IPT4 -F mwan3_iface_out_$1
225 $IPT4 -A mwan3_iface_out_$1 -s $src_ip -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
226
227 $IPT4 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null
228 $IPT4 -A mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1
229 fi
230
231 if [ "$family" == "ipv6" ]; then
232
233 ubus call network.interface.${1}_6 status &>/dev/null
234 if [ "$?" -eq "0" ]; then
235 network_get_ipaddr6 src_ipv6 ${1}_6
236 else
237 network_get_ipaddr6 src_ipv6 $1
238 fi
239
240 $IPS -! create mwan3_connected_v6 hash:net family inet6
241
242 if ! $IPT6 -S mwan3_ifaces_in &> /dev/null; then
243 $IPT6 -N mwan3_ifaces_in
244 fi
245
246 if ! $IPT6 -S mwan3_ifaces_out &> /dev/null; then
247 $IPT6 -N mwan3_ifaces_out
248 fi
249
250 if ! $IPT6 -S mwan3_iface_in_$1 &> /dev/null; then
251 $IPT6 -N mwan3_iface_in_$1
252 fi
253
254 if ! $IPT6 -S mwan3_iface_out_$1 &> /dev/null; then
255 $IPT6 -N mwan3_iface_out_$1
256 fi
257
258 $IPT6 -F mwan3_iface_in_$1
259 $IPT6 -A mwan3_iface_in_$1 -i $2 -m set --match-set mwan3_connected_v6 src -m mark --mark 0x0/$MMX_MASK -m comment --comment "default" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
260 $IPT6 -A mwan3_iface_in_$1 -i $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
261
262 $IPT6 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null
263 $IPT6 -A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1
264
265 $IPT6 -F mwan3_iface_out_$1
266 $IPT6 -A mwan3_iface_out_$1 -s $src_ipv6 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
267
268 $IPT6 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null
269 $IPT6 -A mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1
270 fi
271 }
272
273 mwan3_delete_iface_iptables()
274 {
275 config_get family $1 family ipv4
276
277 if [ "$family" == "ipv4" ]; then
278
279 $IPT4 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null
280 $IPT4 -F mwan3_iface_in_$1 &> /dev/null
281 $IPT4 -X mwan3_iface_in_$1 &> /dev/null
282
283 $IPT4 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null
284 $IPT4 -F mwan3_iface_out_$1 &> /dev/null
285 $IPT4 -X mwan3_iface_out_$1 &> /dev/null
286 fi
287
288 if [ "$family" == "ipv6" ]; then
289
290 $IPT6 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null
291 $IPT6 -F mwan3_iface_in_$1 &> /dev/null
292 $IPT6 -X mwan3_iface_in_$1 &> /dev/null
293
294 $IPT6 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null
295 $IPT6 -F mwan3_iface_out_$1 &> /dev/null
296 $IPT6 -X mwan3_iface_out_$1 &> /dev/null
297 fi
298 }
299
300 mwan3_create_iface_route()
301 {
302 local id route_args
303
304 config_get family $1 family ipv4
305 mwan3_get_iface_id id $1
306
307 [ -n "$id" ] || return 0
308
309 if [ "$family" == "ipv4" ]; then
310 ubus call network.interface.${1}_4 status &>/dev/null
311 if [ "$?" -eq "0" ]; then
312 network_get_gateway route_args ${1}_4
313 else
314 network_get_gateway route_args $1
315 fi
316
317 route_args="via $route_args dev $2"
318
319 $IP4 route flush table $id
320 $IP4 route add table $id default $route_args
321 fi
322
323 if [ "$family" == "ipv6" ]; then
324
325 ubus call network.interface.${1}_6 status &>/dev/null
326 if [ "$?" -eq "0" ]; then
327 network_get_gateway6 route_args ${1}_6
328 else
329 network_get_gateway6 route_args $1
330 fi
331
332 route_args="via $route_args dev $2"
333
334 $IP6 route flush table $id
335 $IP6 route add table $id default $route_args
336 fi
337 }
338
339 mwan3_delete_iface_route()
340 {
341 local id
342
343 config_get family $1 family ipv4
344 mwan3_get_iface_id id $1
345
346 [ -n "$id" ] || return 0
347
348 if [ "$family" == "ipv4" ]; then
349 $IP4 route flush table $id
350 fi
351
352 if [ "$family" == "ipv6" ]; then
353 $IP6 route flush table $id
354 fi
355 }
356
357 mwan3_create_iface_rules()
358 {
359 local id family
360
361 config_get family $1 family ipv4
362 mwan3_get_iface_id id $1
363
364 [ -n "$id" ] || return 0
365
366 if [ "$family" == "ipv4" ]; then
367
368 while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
369 $IP4 rule del pref $(($id+1000))
370 done
371
372 while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
373 $IP4 rule del pref $(($id+2000))
374 done
375
376 $IP4 rule add pref $(($id+1000)) iif $2 lookup main
377 $IP4 rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup $id
378 fi
379
380 if [ "$family" == "ipv6" ]; then
381
382 while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
383 $IP6 rule del pref $(($id+1000))
384 done
385
386 while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
387 $IP6 rule del pref $(($id+2000))
388 done
389
390 $IP6 rule add pref $(($id+1000)) iif $2 lookup main
391 $IP6 rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup $id
392 fi
393 }
394
395 mwan3_delete_iface_rules()
396 {
397 local id family
398
399 config_get family $1 family ipv4
400 mwan3_get_iface_id id $1
401
402 [ -n "$id" ] || return 0
403
404 if [ "$family" == "ipv4" ]; then
405
406 while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
407 $IP4 rule del pref $(($id+1000))
408 done
409
410 while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
411 $IP4 rule del pref $(($id+2000))
412 done
413 fi
414
415 if [ "$family" == "ipv6" ]; then
416
417 while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
418 $IP6 rule del pref $(($id+1000))
419 done
420
421 while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
422 $IP6 rule del pref $(($id+2000))
423 done
424 fi
425 }
426
427 mwan3_delete_iface_ipset_entries()
428 {
429 local id setname entry
430
431 mwan3_get_iface_id id $1
432
433 [ -n "$id" ] || return 0
434
435 for setname in $(ipset -n list | grep ^mwan3_sticky_); do
436 for entry in $(ipset list $setname | grep "$(echo $(mwan3_id2mask id MMX_MASK) | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
437 $IPS del $setname $entry
438 done
439 done
440 }
441
442 mwan3_track()
443 {
444 local track_ip track_ips
445
446 mwan3_list_track_ips()
447 {
448 track_ips="$1 $track_ips"
449 }
450 config_list_foreach $1 track_ip mwan3_list_track_ips
451
452 kill $(pgrep -f "mwan3track $1 $2") &> /dev/null
453 if [ -n "$track_ips" ]; then
454 [ -x /usr/sbin/mwan3track ] && /usr/sbin/mwan3track "$1" "$2" "$3" $track_ips &
455 fi
456 }
457
458 mwan3_track_signal()
459 {
460 local pid
461
462 pid="$(pgrep -f "mwan3track $1 $2")"
463 if [ "${pid}" != "" ]; then
464 kill -USR1 "${pid}"
465 else
466 $LOG warn "Unable to send signal USR1 to mwan3track on interface $1 with pid ${pid}"
467 fi
468 }
469
470 mwan3_set_policy()
471 {
472 local iface_count id iface family metric probability weight
473
474 config_get iface $1 interface
475 config_get metric $1 metric 1
476 config_get weight $1 weight 1
477
478 [ -n "$iface" ] || return 0
479
480 mwan3_get_iface_id id $iface
481
482 [ -n "$id" ] || return 0
483
484 config_get family $iface family ipv4
485
486 if [ "$family" == "ipv4" ]; then
487
488 if [ -n "$($IP4 route list table $id)" ]; then
489 if [ "$metric" -lt "$lowest_metric_v4" ]; then
490
491 total_weight_v4=$weight
492 $IPT4 -F mwan3_policy_$policy
493 $IPT4 -A mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK -m comment --comment "$iface $weight $weight" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
494
495 lowest_metric_v4=$metric
496
497 elif [ "$metric" -eq "$lowest_metric_v4" ]; then
498
499 total_weight_v4=$(($total_weight_v4+$weight))
500 probability=$(($weight*1000/$total_weight_v4))
501
502 if [ "$probability" -lt 10 ]; then
503 probability="0.00$probability"
504 elif [ $probability -lt 100 ]; then
505 probability="0.0$probability"
506 elif [ $probability -lt 1000 ]; then
507 probability="0.$probability"
508 else
509 probability="1"
510 fi
511
512 probability="-m statistic --mode random --probability $probability"
513
514 $IPT4 -I mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK $probability -m comment --comment "$iface $weight $total_weight_v4" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
515 fi
516 fi
517 fi
518
519 if [ "$family" == "ipv6" ]; then
520
521 if [ -n "$($IP6 route list table $id)" ]; then
522 if [ "$metric" -lt "$lowest_metric_v6" ]; then
523
524 total_weight_v6=$weight
525 $IPT6 -F mwan3_policy_$policy
526 $IPT6 -A mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK -m comment --comment "$iface $weight $weight" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
527
528 lowest_metric_v6=$metric
529
530 elif [ "$metric" -eq "$lowest_metric_v6" ]; then
531
532 total_weight_v6=$(($total_weight_v6+$weight))
533 probability=$(($weight*1000/$total_weight_v6))
534
535 if [ "$probability" -lt 10 ]; then
536 probability="0.00$probability"
537 elif [ $probability -lt 100 ]; then
538 probability="0.0$probability"
539 elif [ $probability -lt 1000 ]; then
540 probability="0.$probability"
541 else
542 probability="1"
543 fi
544
545 probability="-m statistic --mode random --probability $probability"
546
547 $IPT6 -I mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK $probability -m comment --comment "$iface $weight $total_weight_v6" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
548 fi
549 fi
550 fi
551 }
552
553 mwan3_create_policies_iptables()
554 {
555 local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT
556
557 policy="$1"
558
559 config_get last_resort $1 last_resort unreachable
560
561 if [ "$1" != $(echo "$1" | cut -c1-15) ]; then
562 $LOG warn "Policy $1 exceeds max of 15 chars. Not setting policy" && return 0
563 fi
564
565 for IPT in "$IPT4" "$IPT6"; do
566
567 if ! $IPT -S mwan3_policy_$1 &> /dev/null; then
568 $IPT -N mwan3_policy_$1
569 fi
570
571 $IPT -F mwan3_policy_$1
572
573 case "$last_resort" in
574 blackhole)
575 $IPT -A mwan3_policy_$1 -m mark --mark 0x0/$MMX_MASK -m comment --comment "blackhole" -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK
576 ;;
577 default)
578 $IPT -A mwan3_policy_$1 -m mark --mark 0x0/$MMX_MASK -m comment --comment "default" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
579 ;;
580 *)
581 $IPT -A mwan3_policy_$1 -m mark --mark 0x0/$MMX_MASK -m comment --comment "unreachable" -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK
582 ;;
583 esac
584 done
585
586 lowest_metric_v4=256
587 total_weight_v4=0
588
589 lowest_metric_v6=256
590 total_weight_v6=0
591
592 config_list_foreach $1 use_member mwan3_set_policy
593 }
594
595 mwan3_set_policies_iptables()
596 {
597 config_foreach mwan3_create_policies_iptables policy
598 }
599
600 mwan3_set_sticky_iptables()
601 {
602 local id iface
603
604 for iface in $($IPT4 -S $policy | cut -s -d'"' -f2 | awk '{print $1}'); do
605
606 if [ "$iface" == "$1" ]; then
607
608 mwan3_get_iface_id id $1
609
610 [ -n "$id" ] || return 0
611
612 for IPT in "$IPT4" "$IPT6"; do
613 if [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -a -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" ]; then
614 $IPT -I mwan3_rule_$rule -m mark --mark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -m set ! --match-set mwan3_sticky_$rule src,src -j MARK --set-xmark 0x0/$MMX_MASK
615 $IPT -I mwan3_rule_$rule -m mark --mark 0/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
616 fi
617 done
618 fi
619 done
620 }
621
622 mwan3_set_user_iptables_rule()
623 {
624 local ipset family proto policy src_ip src_port sticky dest_ip dest_port use_policy timeout rule policy IPT
625
626 rule="$1"
627
628 config_get sticky $1 sticky 0
629 config_get timeout $1 timeout 600
630 config_get ipset $1 ipset
631 config_get proto $1 proto all
632 config_get src_ip $1 src_ip 0.0.0.0/0
633 config_get src_port $1 src_port 0:65535
634 config_get dest_ip $1 dest_ip 0.0.0.0/0
635 config_get dest_port $1 dest_port 0:65535
636 config_get use_policy $1 use_policy
637 config_get family $1 family any
638
639 if [ "$1" != $(echo "$1" | cut -c1-15) ]; then
640 $LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0
641 fi
642
643 if [ -n "$ipset" ]; then
644 ipset="-m set --match-set $ipset dst"
645 fi
646
647 if [ -n "$use_policy" ]; then
648 if [ "$use_policy" == "default" ]; then
649 policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK"
650 elif [ "$use_policy" == "unreachable" ]; then
651 policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK"
652 elif [ "$use_policy" == "blackhole" ]; then
653 policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK"
654 else
655 if [ "$sticky" -eq 1 ]; then
656
657 policy="mwan3_policy_$use_policy"
658
659 for IPT in "$IPT4" "$IPT6"; do
660 if ! $IPT -S $policy &> /dev/null; then
661 $IPT -N $policy
662 fi
663
664 if ! $IPT -S mwan3_rule_$1 &> /dev/null; then
665 $IPT -N mwan3_rule_$1
666 fi
667
668 $IPT -F mwan3_rule_$1
669 done
670
671 $IPS -! create mwan3_sticky_v4_$rule hash:ip,mark markmask $MMX_MASK timeout $timeout
672 $IPS -! create mwan3_sticky_v6_$rule hash:ip,mark markmask $MMX_MASK timeout $timeout family inet6
673 $IPS -! create mwan3_sticky_$rule list:set
674 $IPS -! add mwan3_sticky_$rule mwan3_sticky_v4_$rule
675 $IPS -! add mwan3_sticky_$rule mwan3_sticky_v6_$rule
676
677 config_foreach mwan3_set_sticky_iptables interface
678
679 for IPT in "$IPT4" "$IPT6"; do
680 $IPT -A mwan3_rule_$1 -m mark --mark 0/$MMX_MASK -j $policy
681 $IPT -A mwan3_rule_$1 -m mark ! --mark 0xfc00/0xfc00 -j SET --del-set mwan3_sticky_$rule src,src
682 $IPT -A mwan3_rule_$1 -m mark ! --mark 0xfc00/0xfc00 -j SET --add-set mwan3_sticky_$rule src,src
683 done
684
685 policy="mwan3_rule_$1"
686 else
687 policy="mwan3_policy_$use_policy"
688
689 for IPT in "$IPT4" "$IPT6"; do
690 if ! $IPT -S $policy &> /dev/null; then
691 $IPT -N $policy
692 fi
693 done
694
695 fi
696 fi
697
698 if [ "$family" == "any" ]; then
699
700 for IPT in "$IPT4" "$IPT6"; do
701 case $proto in
702 tcp|udp)
703 $IPT -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m multiport --sports $src_port -m multiport --dports $dest_port -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
704 ;;
705 *)
706 $IPT -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
707 ;;
708 esac
709 done
710
711 elif [ "$family" == "ipv4" ]; then
712
713 case $proto in
714 tcp|udp)
715 $IPT4 -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m multiport --sports $src_port -m multiport --dports $dest_port -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
716 ;;
717 *)
718 $IPT4 -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
719 ;;
720 esac
721
722 elif [ "$family" == "ipv6" ]; then
723
724 case $proto in
725 tcp|udp)
726 $IPT6 -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m multiport --sports $src_port -m multiport --dports $dest_port -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
727 ;;
728 *)
729 $IPT6 -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m mark --mark 0/$MMX_MASK -m comment --comment "$1" -j $policy &> /dev/null
730 ;;
731 esac
732 fi
733 fi
734 }
735
736 mwan3_set_user_rules()
737 {
738 local IPT
739
740 for IPT in "$IPT4" "$IPT6"; do
741
742 if ! $IPT -S mwan3_rules &> /dev/null; then
743 $IPT -N mwan3_rules
744 fi
745
746 $IPT -F mwan3_rules
747 done
748
749 config_foreach mwan3_set_user_iptables_rule rule
750 }
751
752 mwan3_report_iface_status()
753 {
754 local device result track_ips tracking IP IPT
755
756 mwan3_get_iface_id id $1
757 network_get_device device $1
758 config_get enabled "$1" enabled 0
759 config_get family "$1" family ipv4
760
761 if [ "$family" == "ipv4" ]; then
762 IP="$IP4"
763 IPT="$IPT4"
764 fi
765
766 if [ "$family" == "ipv6" ]; then
767 IP="$IP6"
768 IPT="$IPT6"
769 fi
770
771 if [ -z "$id" -o -z "$device" ]; then
772 result="unknown"
773 elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')"i -a -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -a -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -a -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" -a -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
774 result="online"
775 elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" -o -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -o -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -o -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" -o -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
776 result="error"
777 else
778 if [ "$enabled" == "1" ]; then
779 result="offline"
780 else
781 result="disabled"
782 fi
783 fi
784
785 mwan3_list_track_ips()
786 {
787 track_ips="$1 $track_ips"
788 }
789 config_list_foreach $1 track_ip mwan3_list_track_ips
790
791 if [ -n "$track_ips" ]; then
792 if [ -n "$(pgrep -f "mwan3track $1 $device")" ]; then
793 tracking="active"
794 else
795 tracking="down"
796 fi
797 else
798 tracking="not enabled"
799 fi
800
801 echo " interface $1 is $result and tracking is $tracking"
802 }
803
804 mwan3_report_policies_v4()
805 {
806 local percent policy share total_weight weight iface
807
808 for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
809 echo "$policy:" | sed 's/mwan3_policy_//'
810
811 [ -n "$total_weight" ] || total_weight=$($IPT4 -S $policy | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
812
813 if [ ! -z "${total_weight##*[!0-9]*}" ]; then
814 for iface in $($IPT4 -S $policy | cut -s -d'"' -f2 | awk '{print $1}'); do
815 weight=$($IPT4 -S $policy | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
816 percent=$(($weight*100/$total_weight))
817 echo " $iface ($percent%)"
818 done
819 else
820 echo " $($IPT4 -S $policy | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')"
821 fi
822
823 unset total_weight
824
825 echo -e
826 done
827 }
828
829 mwan3_report_policies_v6()
830 {
831 local percent policy share total_weight weight iface
832
833 for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
834 echo "$policy:" | sed 's/mwan3_policy_//'
835
836 [ -n "$total_weight" ] || total_weight=$($IPT6 -S $policy | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
837
838 if [ ! -z "${total_weight##*[!0-9]*}" ]; then
839 for iface in $($IPT6 -S $policy | cut -s -d'"' -f2 | awk '{print $1}'); do
840 weight=$($IPT6 -S $policy | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
841 percent=$(($weight*100/$total_weight))
842 echo " $iface ($percent%)"
843 done
844 else
845 echo " $($IPT6 -S $policy | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')"
846 fi
847
848 unset total_weight
849
850 echo -e
851 done
852 }
853
854 mwan3_report_connected_v4()
855 {
856 local address
857
858 if [ -n "$($IPT4 -S mwan3_connected 2> /dev/null)" ]; then
859 for address in $($IPS list mwan3_connected_v4 | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
860 echo " $address"
861 done
862 fi
863 }
864
865 mwan3_report_connected_v6()
866 {
867 local address
868
869 if [ -n "$($IPT6 -S mwan3_connected 2> /dev/null)" ]; then
870 for address in $($IPS list mwan3_connected_v6 | egrep '([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])'); do
871 echo " $address"
872 done
873 fi
874 }
875
876 mwan3_report_rules_v4()
877 {
878 if [ -n "$($IPT4 -S mwan3_rules 2> /dev/null)" ]; then
879 $IPT4 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
880 fi
881 }
882
883 mwan3_report_rules_v6()
884 {
885 if [ -n "$($IPT6 -S mwan3_rules 2> /dev/null)" ]; then
886 $IPT6 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
887 fi
888 }
889
890 mwan3_flush_conntrack()
891 {
892 local flush_conntrack
893
894 config_get flush_conntrack $1 flush_conntrack never
895
896 if [ -e "$CONNTRACK_FILE" ]; then
897 case $flush_conntrack in
898 ifup)
899 [ "$3" = "ifup" ] && {
900 echo f > ${CONNTRACK_FILE}
901 $LOG info "connection tracking flushed on interface $1 ($2) $3"
902 }
903 ;;
904 ifdown)
905 [ "$3" = "ifdown" ] && {
906 echo f > ${CONNTRACK_FILE}
907 $LOG info "connection tracking flushed on interface $1 ($2) $3"
908 }
909 ;;
910 always)
911 echo f > ${CONNTRACK_FILE}
912 $LOG info "connection tracking flushed on interface $1 ($2) $3"
913 ;;
914 never)
915 $LOG info "connection tracking not flushed on interface $1 ($2) $3"
916 ;;
917 esac
918 else
919 $LOG warning "connection tracking not enabled"
920 fi
921 }