3 .
/usr
/share
/libubox
/jshn.sh
8 IPT4
="iptables -t mangle -w"
9 IPT6
="ip6tables -t mangle -w"
10 IPT4R
="iptables-restore -T mangle -w -n"
11 IPT6R
="ip6tables-restore -T mangle -w -n"
12 CONNTRACK_FILE
="/proc/net/nf_conntrack"
13 IPv6_REGEX
="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
14 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|"
15 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
16 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
17 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
18 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
19 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
20 IPv6_REGEX
="${IPv6_REGEX}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
21 IPv6_REGEX
="${IPv6_REGEX}:((:[0-9a-fA-F]{1,4}){1,7}|:)|"
22 IPv6_REGEX
="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
23 IPv6_REGEX
="${IPv6_REGEX}::(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])|"
24 IPv6_REGEX
="${IPv6_REGEX}([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])"
26 MWAN3_STATUS_DIR
="/var/run/mwan3"
27 MWAN3TRACK_STATUS_DIR
="/var/run/mwan3track"
28 MWAN3_INTERFACE_MAX
=""
29 DEFAULT_LOWEST_METRIC
=256
38 command -v ip6tables
> /dev
/null
43 # helper function to build an update string to pass on to
44 # IPTR or IPS RESTORE. Modifies the 'update' variable in
50 mwan3_update_dev_to_table
()
53 mwan3_dev_tbl_ipv4
=" "
54 mwan3_dev_tbl_ipv6
=" "
58 local family curr_table device enabled
60 config_get family
"$1" family ipv4
61 network_get_device device
"$1"
62 [ -z "$device" ] && return
63 config_get enabled
"$1" enabled
64 [ "$enabled" -eq 0 ] && return
65 curr_table
=$
(eval "echo \"\$mwan3_dev_tbl_${family}\"")
66 export "mwan3_dev_tbl_$family=${curr_table}${device}=$_tid "
69 config_foreach update_table interface
72 mwan3_update_iface_to_table
()
79 export mwan3_iface_tbl
="${mwan3_iface_tbl}${1}=$_tid "
81 config_foreach update_table interface
84 mwan3_get_true_iface
()
88 config_get family
"$iface" family ipv4
89 if [ "$family" = "ipv4" ]; then
91 elif [ "$family" = "ipv6" ]; then
94 ubus call
"network.interface.${iface}_${V}" status &>/dev/null && _true_iface="${iface}_${V}"
95 export "$1=$_true_iface"
98 mwan3_route_line_dev
()
100 # must have mwan3 config already loaded
101 # arg 1 is route device
102 local _tid route_line route_device route_family entry curr_table
105 route_device
=$
(echo "$route_line" |
sed -ne "s/.*dev \([^ ]*\).*/\1/p")
107 [ -z "$route_device" ] && return
109 curr_table
=$
(eval "echo \"\$mwan3_dev_tbl_${route_family}\"")
110 for entry
in $curr_table; do
111 if [ "${entry%%=*}" = "$route_device" ]; then
119 # counts how many bits are set to 1
120 # n&(n-1) clears the lowest bit set to 1
121 mwan3_count_one_bits
()
126 while [ "$n" -gt "0" ]; do
133 # maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
134 # which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
135 # 0 0 0 0 0 1 0 1 (0x05) 1st parameter
136 # 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
140 local bit_msk bit_val result
143 for bit_msk
in $
(seq 0 31); do
144 if [ $
((($2>>bit_msk
)&1)) = "1" ]; then
145 if [ $
((($1>>bit_val
)&1)) = "1" ]; then
146 result
=$
((result|
(1<<bit_msk)))
148 bit_val=$((bit_val+1))
151 printf "0x%x" $result
159 [ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state
161 # mwan3's MARKing mask (at least 3 bits should be set)
162 if [ -e "${MWAN3_STATUS_DIR}/mmx_mask" ]; then
163 MMX_MASK=$(cat "${MWAN3_STATUS_DIR}/mmx_mask")
164 MWAN3_INTERFACE_MAX=$(uci_get_state mwan3 globals iface_max)
167 config_get MMX_MASK globals mmx_mask '0x3F00'
168 echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
169 LOG debug "Using firewall mask ${MMX_MASK}"
171 bitcnt=$(mwan3_count_one_bits MMX_MASK)
172 mmdefault=$(((1<<bitcnt)-1))
173 MWAN3_INTERFACE_MAX=$(($mmdefault-3))
174 uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
175 LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
178 # mark mask constants
179 bitcnt=$(mwan3_count_one_bits MMX_MASK)
180 mmdefault=$(((1<<bitcnt)-1))
181 MM_BLACKHOLE=$(($mmdefault-2))
182 MM_UNREACHABLE=$(($mmdefault-1))
184 # MMX_DEFAULT should equal MMX_MASK
185 MMX_DEFAULT=$(mwan3_id2mask mmdefault MMX_MASK)
186 MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
187 MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
191 lock /var/run/mwan3.lock
192 #LOG debug "$1 $2 (lock)"
196 #LOG debug "$1 $2 (unlock)"
197 lock -u /var/run/mwan3.lock
202 local family _src_ip true_iface
205 config_get family "$true_iface" family ipv4
206 if [ "$family" = "ipv4" ]; then
207 network_get_ipaddr _src_ip "$true_iface"
208 [ -n "$_src_ip" ] || _src_ip="0.0.0.0"
209 elif [ "$family" = "ipv6" ]; then
210 network_get_ipaddr6 _src_ip "$true_iface"
211 [ -n "$_src_ip" ] || _src_ip="::"
219 [ -z "$mwan3_iface_tbl" ] && mwan3_update_iface_to_table
220 _tmp="${mwan3_iface_tbl##* ${2}=}"
226 mwan3_set_custom_ipset_v4()
228 local custom_network_v4
230 for custom_network_v4 in $($IP4 route list table "$1" | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
231 LOG notice "Adding network $custom_network_v4 from table $1 to mwan3_custom_v4 ipset"
232 mwan3_push_update -! add mwan3_custom_v4 "$custom_network_v4"
236 mwan3_set_custom_ipset_v6()
238 local custom_network_v6
240 for custom_network_v6 in $($IP6 route list table "$1" | awk '{print $1}' | egrep "$IPv6_REGEX"); do
241 LOG notice "Adding network $custom_network_v6 from table $1 to mwan3_custom_v6 ipset"
242 mwan3_push_update -! add mwan3_custom_v6 "$custom_network_v6"
246 mwan3_set_custom_ipset()
250 mwan3_push_update -! create mwan3_custom_v4 hash:net
251 config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v4
253 mwan3_push_update -! create mwan3_custom_v6 hash:net family inet6
254 config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v6
256 mwan3_push_update -! create mwan3_connected list:set
257 mwan3_push_update -! add mwan3_connected mwan3_custom_v4
258 mwan3_push_update -! add mwan3_connected mwan3_custom_v6
259 error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_custom_ipset: $error"
263 mwan3_set_connected_ipv4()
265 local connected_network_v4 candidate_list cidr_list
266 local ipv4regex='[0-9]{1,3}(\.[0-9]{1,3}){3}'
267 $IPS -! create mwan3_connected_v4 hash:net
268 $IPS create mwan3_connected_v4_temp hash:net
274 $IP4 route | awk '{print $1}'
275 $IP4 route list table 0 | awk '{print $2}'
277 for connected_network_v4 in $(route_lists | egrep "$ipv4regex"); do
278 if [ -z "${connected_network_v4##*/*}" ]; then
279 cidr_list="$cidr_list $connected_network_v4"
281 candidate_list="$candidate_list $connected_network_v4"
285 for connected_network_v4 in $cidr_list; do
286 $IPS -! add mwan3_connected_v4_temp "$connected_network_v4"
288 for connected_network_v4 in $candidate_list; do
289 ipset -q test mwan3_connected_v4_temp "$connected_network_v4" ||
290 $IPS -! add mwan3_connected_v4_temp "$connected_network_v4"
293 $IPS add mwan3_connected_v4_temp 224.0.0.0/3
295 $IPS swap mwan3_connected_v4_temp mwan3_connected_v4
296 $IPS destroy mwan3_connected_v4_temp
300 mwan3_set_connected_iptables()
302 local connected_network_v6 source_network_v6 error
304 mwan3_set_connected_ipv4
306 [ $NO_IPV6 -eq 0 ] && {
307 mwan3_push_update -! create mwan3_connected_v6 hash:net family inet6
308 mwan3_push_update flush mwan3_connected_v6
310 for connected_network_v6 in $($IP6 route | awk '{print $1}' | egrep "$IPv6_REGEX"); do
311 mwan3_push_update -! add mwan3_connected_v6 "$connected_network_v6"
314 mwan3_push_update -! create mwan3_source_v6 hash:net family inet6
315 for source_network_v6 in $($IP6 addr ls | sed -ne 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p'); do
316 mwan3_push_update -! add mwan3_source_v6 "$source_network_v6"
320 mwan3_push_update -! create mwan3_connected list:set
321 mwan3_push_update flush mwan3_connected
322 mwan3_push_update -! add mwan3_connected mwan3_connected_v4
323 [ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_connected_v6
325 mwan3_push_update -! create mwan3_dynamic_v4 hash:net
326 mwan3_push_update -! add mwan3_connected mwan3_dynamic_v4
328 [ $NO_IPV6 -eq 0 ] && mwan3_push_update -! create mwan3_dynamic_v6 hash:net family inet6
329 [ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_dynamic_v6
330 error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_iptables: $error"
333 mwan3_set_general_rules()
337 for IP in "$IP4" "$IP6"; do
338 [ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue
339 RULE_NO=$(($MM_BLACKHOLE+2000))
340 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
341 $IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole
344 RULE_NO=$(($MM_UNREACHABLE+2000))
345 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
346 $IP rule add pref $RULE_NO fwmark $MMX_UNREACHABLE/$MMX_MASK unreachable
351 mwan3_set_general_iptables()
353 local IPT current update error
354 for IPT in "$IPT4" "$IPT6"; do
355 [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
358 if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
359 mwan3_push_update -N mwan3_ifaces_in
362 if [ -n "${current##*-N mwan3_connected*}" ]; then
363 mwan3_push_update -N mwan3_connected
364 $IPS -! create mwan3_connected list:set
365 mwan3_push_update -A mwan3_connected \
366 -m set --match-set mwan3_connected dst \
367 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
370 if [ -n "${current##*-N mwan3_rules*}" ]; then
371 mwan3_push_update -N mwan3_rules
374 if [ -n "${current##*-N mwan3_hook*}" ]; then
375 mwan3_push_update -N mwan3_hook
376 # do not mangle ipv6 ra service
377 if [ "$IPT" = "$IPT6" ]; then
378 mwan3_push_update -A mwan3_hook \
380 -m icmp6 --icmpv6-type 133 \
382 mwan3_push_update -A mwan3_hook \
384 -m icmp6 --icmpv6-type 134 \
386 mwan3_push_update -A mwan3_hook \
388 -m icmp6 --icmpv6-type 135 \
390 mwan3_push_update -A mwan3_hook \
392 -m icmp6 --icmpv6-type 136 \
394 mwan3_push_update -A mwan3_hook \
396 -m icmp6 --icmpv6-type 137 \
398 # do not mangle outgoing echo request
399 mwan3_push_update -A mwan3_hook \
400 -m set --match-set mwan3_source_v6 src \
402 -m icmp6 --icmpv6-type 128 \
406 mwan3_push_update -A mwan3_hook \
407 -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
408 mwan3_push_update -A mwan3_hook \
409 -m mark --mark 0x0/$MMX_MASK \
411 mwan3_push_update -A mwan3_hook \
412 -m mark --mark 0x0/$MMX_MASK \
414 mwan3_push_update -A mwan3_hook \
415 -m mark --mark 0x0/$MMX_MASK \
417 mwan3_push_update -A mwan3_hook \
418 -j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
419 mwan3_push_update -A mwan3_hook \
420 -m mark ! --mark $MMX_DEFAULT/$MMX_MASK \
424 if [ -n "${current##*-A PREROUTING -j mwan3_hook*}" ]; then
425 mwan3_push_update -A PREROUTING -j mwan3_hook
427 if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then
428 mwan3_push_update -A OUTPUT -j mwan3_hook
430 mwan3_push_update COMMIT
432 if [ "$IPT" = "$IPT4" ]; then
433 error=$(echo "$update" | $IPT4R 2>&1) || LOG error "set_general_iptables: $error"
435 error=$(echo "$update" | $IPT6R 2>&1) || LOG error "set_general_iptables: $error"
440 mwan3_create_iface_iptables()
442 local id family connected_name IPT IPTR current update error
444 config_get family "$1" family ipv4
445 mwan3_get_iface_id id "$1"
447 [ -n "$id" ] || return 0
449 if [ "$family" = "ipv4" ]; then
450 connected_name=mwan3_connected
453 $IPS -! create $connected_name list:set
455 elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
456 connected_name=mwan3_connected_v6
459 $IPS -! create $connected_name hash:net family inet6
465 if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
466 mwan3_push_update -N mwan3_ifaces_in
469 if [ -n "${current##*-N mwan3_iface_in_$1*}" ]; then
470 mwan3_push_update -N "mwan3_iface_in_$1"
472 mwan3_push_update -F "mwan3_iface_in_$1"
475 mwan3_push_update -A "mwan3_iface_in_$1" \
477 -m set --match-set $connected_name src \
478 -m mark --mark 0x0/$MMX_MASK \
479 -m comment --comment "default" \
480 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
481 mwan3_push_update -A "mwan3_iface_in_$1" \
483 -m mark --mark 0x0/$MMX_MASK \
484 -m comment --comment "$1" \
485 -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
487 if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}*}" ]; then
488 mwan3_push_update -A mwan3_ifaces_in \
489 -m mark --mark 0x0/$MMX_MASK \
490 -j "mwan3_iface_in_$1"
491 LOG debug "create_iface_iptables: mwan3_iface_in_$1 not in iptables, adding"
493 LOG debug "create_iface_iptables: mwan3_iface_in_$1 already in iptables, skip"
496 mwan3_push_update COMMIT
498 error=$(echo "$update" | $IPTR 2>&1) || LOG error "create_iface_iptables: $error"
502 mwan3_delete_iface_iptables()
505 config_get family "$1" family ipv4
507 if [ "$family" = "ipv4" ]; then
511 if [ "$family" = "ipv6" ]; then
512 [ $NO_IPV6 -ne 0 ] && return
516 $IPT -D mwan3_ifaces_in \
517 -m mark --mark 0x0/$MMX_MASK \
518 -j "mwan3_iface_in_$1" &> /dev/null
519 $IPT -F "mwan3_iface_in_$1" &> /dev/null
520 $IPT -X "mwan3_iface_in_$1" &> /dev/null
524 mwan3_create_iface_route()
526 local id via metric V V_ IP family
527 local iface device cmd true_iface
531 config_get family "$iface" family ipv4
532 mwan3_get_iface_id id "$iface"
534 [ -n "$id" ] || return 0
536 mwan3_get_true_iface true_iface $iface
537 if [ "$family" = "ipv4" ]; then
540 elif [ "$family" = "ipv6" ]; then
545 network_get_gateway${V_} via "$true_iface"
547 { [ -z "$via" ] || [ "$via" = "0.0.0.0" ] || [ "$via" = "::" ] ; } && unset via
549 network_get_metric metric "$true_iface"
551 $IP route flush table "$id"
552 cmd="$IP route add table $id default \
554 ${metric:+metric} $metric \
556 $cmd || LOG warn "ip cmd failed $cmd"
560 mwan3_add_non_default_iface_route()
562 local tid route_line family IP id
563 config_get family "$1" family ipv4
564 mwan3_get_iface_id id "$1"
566 [ -n "$id" ] || return 0
568 if [ "$family" = "ipv4" ]; then
570 elif [ "$family" = "ipv6" ]; then
574 mwan3_update_dev_to_table
575 $IP route list table main | grep -v "^default\|linkdown\|^::/0\|^fe80::/64\|^unreachable" | while read route_line; do
576 mwan3_route_line_dev "tid" "$route_line" "$family"
577 if [ -z "$tid" ] || [ "$tid" = "$id" ]; then
578 $IP route add table $id $route_line ||
579 LOG warn "failed to add $route_line to table $id"
585 mwan3_add_all_nondefault_routes()
587 local tid IP route_line ipv family active_tbls tid
592 config_get family "$1" family ipv4
593 [ "$family" != "$ipv" ] && return
594 $IP route list table $tid 2>/dev/null | grep -q "^default\|^::/0" && {
595 active_tbls="$active_tbls${tid} "
602 [ -n "${active_tbls##* $tid *}" ] && return
603 $IP route add table $tid $route_line ||
604 LOG warn "failed to add $route_line to table $tid"
607 mwan3_update_dev_to_table
608 for ipv in ipv4 ipv6; do
609 [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
610 if [ "$ipv" = "ipv4" ]; then
612 elif [ "$ipv" = "ipv6" ]; then
617 config_foreach add_active_tbls interface
618 $IP route list table main | grep -v "^default\|linkdown\|^::/0\|^fe80::/64\|^unreachable" | while read route_line; do
619 mwan3_route_line_dev "tid" "$route_line" "$ipv"
620 if [ -n "$tid" ]; then
621 $IP route add table $tid $route_line
623 config_foreach add_route interface
628 mwan3_delete_iface_route()
632 config_get family "$1" family ipv4
633 mwan3_get_iface_id id "$1"
635 [ -n "$id" ] || return 0
637 if [ "$family" = "ipv4" ]; then
638 $IP4 route flush table "$id"
641 if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
642 $IP6 route flush table "$id"
646 mwan3_create_iface_rules()
650 config_get family "$1" family ipv4
651 mwan3_get_iface_id id "$1"
653 [ -n "$id" ] || return 0
655 if [ "$family" = "ipv4" ]; then
657 elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
663 while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
664 $IP rule del pref $(($id+1000))
667 while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
668 $IP rule del pref $(($id+2000))
671 $IP rule add pref $(($id+1000)) iif "$2" lookup "$id"
672 $IP rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup "$id"
675 mwan3_delete_iface_rules()
679 config_get family "$1" family ipv4
680 mwan3_get_iface_id id "$1"
682 [ -n "$id" ] || return 0
684 if [ "$family" = "ipv4" ]; then
686 elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
692 while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
693 $IP rule del pref $(($id+1000))
696 while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
697 $IP rule del pref $(($id+2000))
701 mwan3_delete_iface_ipset_entries()
703 local id setname entry
705 mwan3_get_iface_id id "$1"
707 [ -n "$id" ] || return 0
709 for setname in $(ipset -n list | grep ^mwan3_sticky_); do
710 for entry in $(ipset list "$setname" | grep "$(mwan3_id2mask id MMX_MASK | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
711 $IPS del "$setname" $entry
719 for protocol in "ipv4" "ipv6"; do
720 pid="$(pgrep -f "mwan3rtmon $protocol")"
721 [ "$protocol" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
722 if [ "${pid}" = "" ]; then
723 [ -x /usr/sbin/mwan3rtmon ] && /usr/sbin/mwan3rtmon $protocol &
732 mwan3_list_track_ips()
734 track_ips="$track_ips $1"
736 config_list_foreach "$1" track_ip mwan3_list_track_ips
738 # don't match device in case it changed from last launch
739 if pids=$(pgrep -f "mwan3track $1 "); then
740 kill -TERM $pids > /dev/null 2>&1
742 kill -KILL $(pgrep -f "mwan3track $1 ") > /dev/null 2>&1
745 if [ -n "$track_ips" ]; then
746 [ -x /usr/sbin/mwan3track ] && MWAN3_STARTUP=0 /usr/sbin/mwan3track "$1" "$2" "$3" "$4" $track_ips &
752 local id iface family metric probability weight device is_lowest is_offline IPT IPTR total_weight current update error
755 config_get iface "$1" interface
756 config_get metric "$1" metric 1
757 config_get weight "$1" weight 1
759 [ -n "$iface" ] || return 0
760 network_get_device device "$iface"
761 [ "$metric" -gt $DEFAULT_LOWEST_METRIC ] && LOG warn "Member interface $iface has >$DEFAULT_LOWEST_METRIC metric. Not appending to policy" && return 0
763 mwan3_get_iface_id id "$iface"
765 [ -n "$id" ] || return 0
767 [ "$(mwan3_get_iface_hotplug_state "$iface")" = "online" ]
770 config_get family "$iface" family ipv4
772 if [ "$family" = "ipv4" ]; then
775 elif [ "$family" = "ipv6" ]; then
782 if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then
783 if [ "$metric" -lt "$lowest_metric_v4" ]; then
785 total_weight_v4=$weight
786 lowest_metric_v4=$metric
787 elif [ "$metric" -eq "$lowest_metric_v4" ]; then
788 total_weight_v4=$(($total_weight_v4+$weight))
789 total_weight=$total_weight_v4
793 elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ] && [ $is_offline -eq 0 ]; then
794 if [ "$metric" -lt "$lowest_metric_v6" ]; then
796 total_weight_v6=$weight
797 lowest_metric_v6=$metric
798 elif [ "$metric" -eq "$lowest_metric_v6" ]; then
799 total_weight_v6=$(($total_weight_v6+$weight))
800 total_weight=$total_weight_v6
805 if [ $is_lowest -eq 1 ]; then
806 mwan3_push_update -F "mwan3_policy_$policy"
807 mwan3_push_update -A "mwan3_policy_$policy" \
808 -m mark --mark 0x0/$MMX_MASK \
809 -m comment --comment \"$iface $weight $weight\" \
810 -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
811 elif [ $is_offline -eq 0 ]; then
812 probability=$(($weight*1000/$total_weight))
813 if [ "$probability" -lt 10 ]; then
814 probability="0.00$probability"
815 elif [ $probability -lt 100 ]; then
816 probability="0.0$probability"
817 elif [ $probability -lt 1000 ]; then
818 probability="0.$probability"
823 mwan3_push_update -I "mwan3_policy_$policy" \
824 -m mark --mark 0x0/$MMX_MASK \
827 --probability "$probability" \
828 -m comment --comment \"$iface $weight $total_weight\" \
829 -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
830 elif [ -n "$device" ]; then
831 echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" ||
832 mwan3_push_update -I "mwan3_policy_$policy" \
834 -m mark --mark 0x0/$MMX_MASK \
835 -m comment --comment \"out $iface $device\" \
836 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
838 mwan3_push_update COMMIT
840 error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_policy ($1): $error"
844 mwan3_create_policies_iptables()
846 local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT current update error
850 config_get last_resort "$1" last_resort unreachable
852 if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
853 LOG warn "Policy $1 exceeds max of 15 chars. Not setting policy" && return 0
856 for IPT in "$IPT4" "$IPT6"; do
857 [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
860 if [ -n "${current##*-N mwan3_policy_$1*}" ]; then
861 mwan3_push_update -N "mwan3_policy_$1"
864 mwan3_push_update -F "mwan3_policy_$1"
866 case "$last_resort" in
868 mwan3_push_update -A "mwan3_policy_$1" \
869 -m mark --mark 0x0/$MMX_MASK \
870 -m comment --comment "blackhole" \
871 -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK
874 mwan3_push_update -A "mwan3_policy_$1" \
875 -m mark --mark 0x0/$MMX_MASK \
876 -m comment --comment "default" \
877 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
880 mwan3_push_update -A "mwan3_policy_$1" \
881 -m mark --mark 0x0/$MMX_MASK \
882 -m comment --comment "unreachable" \
883 -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK
886 mwan3_push_update COMMIT
888 if [ "$IPT" = "$IPT4" ]; then
889 error=$(echo "$update" | $IPT4R 2>&1) || LOG error "create_policies_iptables ($1): $error"
891 error=$(echo "$update" | $IPT6R 2>&1) || LOG error "create_policies_iptables ($1): $error"
895 lowest_metric_v4=$DEFAULT_LOWEST_METRIC
898 lowest_metric_v6=$DEFAULT_LOWEST_METRIC
901 config_list_foreach "$1" use_member mwan3_set_policy
904 mwan3_set_policies_iptables()
906 config_foreach mwan3_create_policies_iptables policy
909 mwan3_set_sticky_iptables()
912 for iface in $(echo "$current" | grep "^-A $policy" | cut -s -d'"' -f2 | awk '{print $1}'); do
913 if [ "$iface" = "$1" ]; then
915 mwan3_get_iface_id id "$1"
917 [ -n "$id" ] || return 0
918 if [ -z "${current##*-N mwan3_iface_in_$1*}" ]; then
919 mwan3_push_update -I "mwan3_rule_$rule" \
920 -m mark --mark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK \
921 -m set ! --match-set "mwan3_sticky_$rule" src,src \
922 -j MARK --set-xmark 0x0/$MMX_MASK
923 mwan3_push_update -I "mwan3_rule_$rule" \
924 -m mark --mark 0/$MMX_MASK \
925 -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
931 mwan3_set_user_iptables_rule()
933 local ipset family proto policy src_ip src_port src_iface src_dev
934 local sticky dest_ip dest_port use_policy timeout policy
935 local global_logging rule_logging loglevel rule_policy rule ipv
940 config_get sticky "$1" sticky 0
941 config_get timeout "$1" timeout 600
942 config_get ipset "$1" ipset
943 config_get proto "$1" proto all
944 config_get src_ip "$1" src_ip
945 config_get src_iface "$1" src_iface
946 config_get src_port "$1" src_port
947 config_get dest_ip "$1" dest_ip
948 config_get dest_port "$1" dest_port
949 config_get use_policy "$1" use_policy
950 config_get family "$1" family any
951 config_get rule_logging "$1" logging 0
952 config_get global_logging globals logging 0
953 config_get loglevel globals loglevel notice
955 if [ -n "$src_iface" ]; then
956 network_get_device src_dev "$src_iface"
957 if [ -z "$src_dev" ]; then
958 LOG notice "could not find device corresponding to src_iface $src_iface for rule $1"
963 [ -z "$dest_ip" ] && unset dest_ip
964 [ -z "$src_ip" ] && unset src_ip
965 [ -z "$ipset" ] && unset ipset
966 [ -z "$src_port" ] && unset src_port
967 [ -z "$dest_port" ] && unset dest_port
968 if [ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ]; then
969 [ -n "$src_port" ] && {
970 LOG warn "src_port set to '$src_port' but proto set to '$proto' not tcp or udp. src_port will be ignored"
973 [ -n "$dest_port" ] && {
974 LOG warn "dest_port set to '$dest_port' but proto set to '$proto' not tcp or udp. dest_port will be ignored"
980 if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
981 LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0
984 if [ -n "$ipset" ]; then
985 ipset="-m set --match-set $ipset dst"
988 if [ -z "$use_policy" ]; then
992 if [ "$use_policy" = "default" ]; then
993 policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK"
994 elif [ "$use_policy" = "unreachable" ]; then
995 policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK"
996 elif [ "$use_policy" = "blackhole" ]; then
997 policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK"
1000 policy="mwan3_policy_$use_policy"
1001 if [ "$sticky" -eq 1 ]; then
1002 $IPS -! create "mwan3_sticky_v4_$rule" \
1003 hash:ip,mark markmask "$MMX_MASK" \
1005 [ $NO_IPV6 -eq 0 ] &&
1006 $IPS -! create "mwan3_sticky_v6_$rule" \
1007 hash:ip,mark markmask "$MMX_MASK" \
1008 timeout "$timeout" family inet6
1009 $IPS -! create "mwan3_sticky_$rule" list:set
1010 $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v4_$rule"
1011 [ $NO_IPV6 -eq 0 ] &&
1012 $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v6_$rule"
1016 [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
1017 [ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
1018 [ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
1020 if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy*}" ]; then
1021 mwan3_push_update -N "$policy"
1024 if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then
1025 if [ -n "${current##*-N mwan3_rule_$1*}" ]; then
1026 mwan3_push_update -N "mwan3_rule_$1"
1029 mwan3_push_update -F "mwan3_rule_$1"
1030 config_foreach mwan3_set_sticky_iptables interface $ipv
1033 mwan3_push_update -A "mwan3_rule_$1" \
1034 -m mark --mark 0/$MMX_MASK \
1036 mwan3_push_update -A "mwan3_rule_$1" \
1037 -m mark ! --mark 0xfc00/0xfc00 \
1038 -j SET --del-set "mwan3_sticky_$rule" src,src
1039 mwan3_push_update -A "mwan3_rule_$1" \
1040 -m mark ! --mark 0xfc00/0xfc00 \
1041 -j SET --add-set "mwan3_sticky_$rule" src,src
1042 policy="mwan3_rule_$1"
1044 if [ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ]; then
1045 mwan3_push_update -A mwan3_rules \
1047 ${src_ip:+-s} $src_ip \
1048 ${src_dev:+-i} $src_dev \
1049 ${dest_ip:+-d} $dest_ip \
1051 ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
1052 ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
1053 -m mark --mark 0/$MMX_MASK \
1054 -m comment --comment "$1" \
1055 -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)"
1058 mwan3_push_update -A mwan3_rules \
1060 ${src_ip:+-s} $src_ip \
1061 ${src_dev:+-i} $src_dev \
1062 ${dest_ip:+-d} $dest_ip \
1064 ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
1065 ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
1066 -m mark --mark 0/$MMX_MASK \
1071 mwan3_set_user_iface_rules()
1073 local current iface update family error device is_src_iface
1077 if [ -z "$device" ]; then
1078 LOG notice "set_user_iface_rules: could not find device corresponding to iface $iface"
1082 config_get family "$iface" family ipv4
1084 if [ "$family" = "ipv4" ]; then
1087 elif [ "$family" = "ipv6" ]; then
1091 $IPT -S | grep -q "^-A mwan3_rules.*-i $device" && return
1098 config_get src_iface "$1" src_iface
1099 [ "$src_iface" = "$iface" ] && is_src_iface=1
1101 config_foreach iface_rule rule
1102 [ $is_src_iface -eq 1 ] && mwan3_set_user_rules
1105 mwan3_set_user_rules()
1108 local current update error
1110 for ipv in ipv4 ipv6; do
1111 if [ "$ipv" = "ipv4" ]; then
1114 elif [ "$ipv" = "ipv6" ]; then
1118 [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
1120 current="$($IPT -S)"
1123 if [ -n "${current##*-N mwan3_rules*}" ]; then
1124 mwan3_push_update -N "mwan3_rules"
1127 mwan3_push_update -F mwan3_rules
1129 config_foreach mwan3_set_user_iptables_rule rule "$ipv"
1131 mwan3_push_update COMMIT
1132 mwan3_push_update ""
1133 error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_user_rules: $error"
1139 mwan3_set_iface_hotplug_state() {
1143 echo "$state" > "$MWAN3_STATUS_DIR/iface_state/$iface"
1146 mwan3_get_iface_hotplug_state() {
1149 cat "$MWAN3_STATUS_DIR/iface_state/$iface" 2>/dev/null || echo "offline"
1152 mwan3_report_iface_status()
1154 local device result track_ips tracking IP IPT
1156 mwan3_get_iface_id id "$1"
1157 network_get_device device "$1"
1158 config_get enabled "$1" enabled 0
1159 config_get family "$1" family ipv4
1161 if [ "$family" = "ipv4" ]; then
1166 if [ "$family" = "ipv6" ]; then
1171 if [ -z "$id" ] || [ -z "$device" ]; then
1173 elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] && \
1174 [ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] && \
1175 [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] && \
1176 [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
1178 json_add_string section interfaces
1179 json_add_string interface "$1"
1180 json_load "$(ubus call mwan3 status "$(json_dump)")"
1181 json_select "interfaces"
1183 json_get_vars online uptime
1186 online="$(printf '%02dh:%02dm:%02ds\n' $(($online/3600)) $(($online%3600/60)) $(($online%60)))"
1187 uptime="$(printf '%02dh:%02dm:%02ds\n' $(($uptime/3600)) $(($uptime%3600/60)) $(($uptime%60)))"
1188 result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime"
1189 elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] || \
1190 [ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] || \
1191 [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] || \
1192 [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
1194 elif [ "$enabled" = "1" ]; then
1200 mwan3_list_track_ips()
1202 track_ips="$1 $track_ips"
1204 config_list_foreach "$1" track_ip mwan3_list_track_ips
1206 if [ -n "$track_ips" ]; then
1207 if [ -n "$(pgrep -f "mwan3track $1 $device")" ]; then
1213 tracking="not enabled"
1216 echo " interface $1 is $result and tracking is $tracking"
1219 mwan3_report_policies()
1224 local percent total_weight weight iface
1226 total_weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
1228 if [ ! -z "${total_weight##*[!0-9]*}" ]; then
1229 for iface in $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do
1230 weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
1231 percent=$(($weight*100/$total_weight))
1232 echo " $iface ($percent%)"
1235 echo " $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')"
1239 mwan3_report_policies_v4()
1243 for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
1244 echo "$policy:" | sed 's/mwan3_policy_//'
1245 mwan3_report_policies "$IPT4" "$policy"
1249 mwan3_report_policies_v6()
1253 for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
1254 echo "$policy:" | sed 's/mwan3_policy_//'
1255 mwan3_report_policies "$IPT6" "$policy"
1259 mwan3_report_connected_v4()
1261 if [ -n "$($IPT4 -S mwan3_connected 2> /dev/null)" ]; then
1262 $IPS -o save list mwan3_connected_v4 | grep add | cut -d " " -f 3
1266 mwan3_report_connected_v6()
1268 if [ -n "$($IPT6 -S mwan3_connected 2> /dev/null)" ]; then
1269 $IPS -o save list mwan3_connected_v6 | grep add | cut -d " " -f 3
1273 mwan3_report_rules_v4()
1275 if [ -n "$($IPT4 -S mwan3_rules 2> /dev/null)" ]; then
1276 $IPT4 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
1280 mwan3_report_rules_v6()
1282 if [ -n "$($IPT6 -S mwan3_rules 2> /dev/null)" ]; then
1283 $IPT6 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
1287 mwan3_flush_conntrack()
1289 local interface="$1"
1293 local flush_conntrack="$1"
1296 if [ "$action" = "$flush_conntrack" ]; then
1297 echo f > ${CONNTRACK_FILE}
1298 LOG info "Connection tracking flushed for interface '$interface' on action '$action'"
1302 if [ -e "$CONNTRACK_FILE" ]; then
1303 config_list_foreach "$interface" flush_conntrack handle_flush "$action"
1309 rm -rf "$MWAN3_STATUS_DIR/${1}" &> /dev/null
1310 rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR"