3 .
"${IPKG_INSTROOT}/usr/share/libubox/jshn.sh"
4 .
"${IPKG_INSTROOT}/lib/mwan3/common.sh"
6 CONNTRACK_FILE
="/proc/net/nf_conntrack"
7 IPv6_REGEX
="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
8 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|"
9 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
10 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
11 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
12 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
13 IPv6_REGEX
="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
14 IPv6_REGEX
="${IPv6_REGEX}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
15 IPv6_REGEX
="${IPv6_REGEX}:((:[0-9a-fA-F]{1,4}){1,7}|:)|"
16 IPv6_REGEX
="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
17 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])|"
18 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])"
19 IPv4_REGEX
="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
21 DEFAULT_LOWEST_METRIC
=256
25 # helper function to build an update string to pass on to
26 # IPTR or IPS RESTORE. Modifies the 'update' variable in
28 update
="$update"$
'\n'"$*";
31 mwan3_update_dev_to_table
()
34 # shellcheck disable=SC2034
35 mwan3_dev_tbl_ipv4
=" "
36 # shellcheck disable=SC2034
37 mwan3_dev_tbl_ipv6
=" "
41 local family curr_table device enabled
43 config_get family
"$1" family ipv4
44 network_get_device device
"$1"
45 [ -z "$device" ] && return
46 config_get enabled
"$1" enabled
47 [ "$enabled" -eq 0 ] && return
48 curr_table
=$
(eval "echo \"\$mwan3_dev_tbl_${family}\"")
49 export "mwan3_dev_tbl_$family=${curr_table}${device}=$_tid "
52 config_foreach update_table interface
55 mwan3_update_iface_to_table
()
62 export mwan3_iface_tbl
="${mwan3_iface_tbl}${1}=$_tid "
64 config_foreach update_table interface
67 mwan3_route_line_dev
()
69 # must have mwan3 config already loaded
70 # arg 1 is route device
71 local _tid route_line route_device route_family entry curr_table
74 route_device
=$
(echo "$route_line" |
sed -ne "s/.*dev \([^ ]*\).*/\1/p")
76 [ -z "$route_device" ] && return
78 curr_table
=$
(eval "echo \"\$mwan3_dev_tbl_${route_family}\"")
79 for entry
in $curr_table; do
80 if [ "${entry%%=*}" = "$route_device" ]; then
88 # counts how many bits are set to 1
89 # n&(n-1) clears the lowest bit set to 1
90 mwan3_count_one_bits
()
95 while [ "$n" -gt "0" ]; do
105 [ -z "$mwan3_iface_tbl" ] && mwan3_update_iface_to_table
106 _tmp
="${mwan3_iface_tbl##* ${2}=}"
111 mwan3_set_custom_ipset_v4
()
113 local custom_network_v4
115 for custom_network_v4
in $
($IP4 route list table
"$1" |
awk '{print $1}' |
grep -E "$IPv4_REGEX"); do
116 LOG notice
"Adding network $custom_network_v4 from table $1 to mwan3_custom_v4 ipset"
117 mwan3_push_update
-! add mwan3_custom_ipv4
"$custom_network_v4"
121 mwan3_set_custom_ipset_v6
()
123 local custom_network_v6
125 for custom_network_v6
in $
($IP6 route list table
"$1" |
awk '{print $1}' |
grep -E "$IPv6_REGEX"); do
126 LOG notice
"Adding network $custom_network_v6 from table $1 to mwan3_custom_v6 ipset"
127 mwan3_push_update
-! add mwan3_custom_ipv6
"$custom_network_v6"
131 mwan3_set_custom_ipset
()
135 mwan3_push_update
-! create mwan3_custom_ipv4
hash:net
136 mwan3_push_update flush mwan3_custom_ipv4
137 config_list_foreach
"globals" "rt_table_lookup" mwan3_set_custom_ipset_v4
139 if [ $NO_IPV6 -eq 0 ]; then
140 mwan3_push_update
-! create mwan3_custom_ipv6
hash:net family inet6
141 mwan3_push_update flush mwan3_custom_ipv6
142 config_list_foreach
"globals" "rt_table_lookup" mwan3_set_custom_ipset_v6
145 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_custom_ipset.dump"
146 error
=$
(echo "$update" |
$IPS restore
2>&1) || LOG error
"set_custom_ipset: $error"
150 mwan3_set_connected_ipv4
()
152 local connected_network_v4 error
153 local candidate_list cidr_list
156 mwan3_push_update
-! create mwan3_connected_ipv4
hash:net
157 mwan3_push_update flush mwan3_connected_ipv4
163 $IP4 route |
awk '{print $1}'
164 $IP4 route list table
0 |
awk '{print $2}'
166 for connected_network_v4
in $
(route_lists |
grep -E "$IPv4_REGEX"); do
167 if [ -z "${connected_network_v4##*/*}" ]; then
168 cidr_list
="$cidr_list $connected_network_v4"
170 candidate_list
="$candidate_list $connected_network_v4"
174 for connected_network_v4
in $cidr_list; do
175 mwan3_push_update
-! add mwan3_connected_ipv4
"$connected_network_v4"
177 for connected_network_v4
in $candidate_list; do
178 mwan3_push_update
-! add mwan3_connected_ipv4
"$connected_network_v4"
181 mwan3_push_update add mwan3_connected_ipv4
224.0.0.0/3
183 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipv4.dump"
184 error
=$
(echo "$update" |
$IPS restore
2>&1) || LOG error
"set_connected_ipv4: $error"
187 mwan3_set_connected_ipv6
()
189 local connected_network_v6 error
191 [ $NO_IPV6 -eq 0 ] ||
return
193 mwan3_push_update
-! create mwan3_connected_ipv6
hash:net family inet6
194 mwan3_push_update flush mwan3_connected_ipv6
196 for connected_network_v6
in $
($IP6 route |
awk '{print $1}' |
grep -E "$IPv6_REGEX"); do
197 mwan3_push_update
-! add mwan3_connected_ipv6
"$connected_network_v6"
200 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipv6.dump"
201 error
=$
(echo "$update" |
$IPS restore
2>&1) || LOG error
"set_connected_ipv6: $error"
204 mwan3_set_connected_ipset
()
209 mwan3_push_update
-! create mwan3_connected_ipv4
hash:net
210 mwan3_push_update flush mwan3_connected_ipv4
212 if [ $NO_IPV6 -eq 0 ]; then
213 mwan3_push_update
-! create mwan3_connected_ipv6
hash:net family inet6
214 mwan3_push_update flush mwan3_connected_ipv6
217 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipset.dump"
218 error
=$
(echo "$update" |
$IPS restore
2>&1) || LOG error
"set_connected_ipset: $error"
221 mwan3_set_dynamic_ipset
()
226 mwan3_push_update
-! create mwan3_dynamic_ipv4 list
:set
227 mwan3_push_update flush mwan3_dynamic_ipv4
229 if [ $NO_IPV6 -eq 0 ]; then
230 mwan3_push_update
-! create mwan3_dynamic_ipv6
hash:net family inet6
231 mwan3_push_update flush mwan3_dynamic_ipv6
234 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_dynamic_ipset.dump"
235 error
=$
(echo "$update" |
$IPS restore
2>&1) || LOG error
"set_dynamic_ipset: $error"
238 mwan3_set_general_rules
()
242 for IP
in "$IP4" "$IP6"; do
243 [ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue
244 RULE_NO
=$
((MM_BLACKHOLE
+2000))
245 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
246 $IP rule add pref
$RULE_NO fwmark
$MMX_BLACKHOLE/$MMX_MASK blackhole
249 RULE_NO
=$
((MM_UNREACHABLE
+2000))
250 if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
251 $IP rule add pref
$RULE_NO fwmark
$MMX_UNREACHABLE/$MMX_MASK unreachable
256 mwan3_set_general_iptables
()
258 local IPT current update error family
260 for IPT
in "$IPT4" "$IPT6"; do
261 [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
262 current
="$($IPT -S)"$
'\n'
264 if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
265 mwan3_push_update
-N mwan3_ifaces_in
268 if [ "$IPT" = "$IPT6" ]; then
274 for chain
in custom connected dynamic
; do
275 echo "${current}" | grep -q "\-N mwan3_${chain}_${family}$"
277 if [ "$ret" = 1 ]; then
278 mwan3_push_update
-N mwan3_
${chain}_
${family}
279 mwan3_push_update
-A mwan3_
${chain}_
${family} \
280 -m set --match-set mwan3_
${chain}_
${family} dst \
281 -j MARK
--set-xmark $MMX_DEFAULT/$MMX_MASK
285 if [ -n "${current##*-N mwan3_rules*}" ]; then
286 mwan3_push_update
-N mwan3_rules
289 if [ -n "${current##*-N mwan3_hook*}" ]; then
290 mwan3_push_update
-N mwan3_hook
291 # do not mangle ipv6 ra service
292 if [ "$IPT" = "$IPT6" ]; then
293 mwan3_push_update
-A mwan3_hook \
295 -m icmp6
--icmpv6-type 133 \
297 mwan3_push_update
-A mwan3_hook \
299 -m icmp6
--icmpv6-type 134 \
301 mwan3_push_update
-A mwan3_hook \
303 -m icmp6
--icmpv6-type 135 \
305 mwan3_push_update
-A mwan3_hook \
307 -m icmp6
--icmpv6-type 136 \
309 mwan3_push_update
-A mwan3_hook \
311 -m icmp6
--icmpv6-type 137 \
315 mwan3_push_update
-A mwan3_hook \
316 -m mark
--mark 0x0/$MMX_MASK \
317 -j CONNMARK
--restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
318 mwan3_push_update
-A mwan3_hook \
319 -m mark
--mark 0x0/$MMX_MASK \
322 for chain
in custom connected dynamic
; do
323 mwan3_push_update
-A mwan3_hook \
324 -m mark
--mark 0x0/$MMX_MASK \
325 -j mwan3_
${chain}_
${family}
328 mwan3_push_update
-A mwan3_hook \
329 -m mark
--mark 0x0/$MMX_MASK \
331 mwan3_push_update
-A mwan3_hook \
332 -j CONNMARK
--save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
334 for chain
in custom connected dynamic
; do
335 mwan3_push_update
-A mwan3_hook \
336 -m mark
! --mark $MMX_DEFAULT/$MMX_MASK \
337 -j mwan3_
${chain}_
${family}
341 if [ -n "${current##*-A PREROUTING -j mwan3_hook*}" ]; then
342 mwan3_push_update
-A PREROUTING
-j mwan3_hook
344 if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then
345 mwan3_push_update
-A OUTPUT
-j mwan3_hook
347 mwan3_push_update COMMIT
350 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_general_iptables-${family}.dump"
351 if [ "$IPT" = "$IPT4" ]; then
352 error
=$
(echo "$update" |
$IPT4R 2>&1) || LOG error
"set_general_iptables (${family}): $error"
354 error
=$
(echo "$update" |
$IPT6R 2>&1) || LOG error
"set_general_iptables (${family}): $error"
359 mwan3_create_iface_iptables
()
361 local id family IPT IPTR current update error
363 config_get family
"$1" family ipv4
364 mwan3_get_iface_id id
"$1"
366 [ -n "$id" ] ||
return 0
368 if [ "$family" = "ipv4" ]; then
371 elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
378 current
="$($IPT -S)"$
'\n'
380 if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
381 mwan3_push_update
-N mwan3_ifaces_in
384 if [ -n "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then
385 mwan3_push_update
-N "mwan3_iface_in_$1"
387 mwan3_push_update
-F "mwan3_iface_in_$1"
390 for chain
in custom connected dynamic
; do
391 mwan3_push_update
-A "mwan3_iface_in_$1" \
393 -m set --match-set mwan3_
${chain}_
${family} src \
394 -m mark
--mark "0x0/$MMX_MASK" \
395 -m comment
--comment "default" \
396 -j MARK
--set-xmark "$MMX_DEFAULT/$MMX_MASK"
398 mwan3_push_update
-A "mwan3_iface_in_$1" \
400 -m mark
--mark "0x0/$MMX_MASK" \
401 -m comment
--comment "$1" \
402 -j MARK
--set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
404 if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}$'\n'*}" ]; then
405 mwan3_push_update
-A mwan3_ifaces_in \
406 -m mark
--mark 0x0/$MMX_MASK \
407 -j "mwan3_iface_in_$1"
408 LOG debug
"create_iface_iptables: mwan3_iface_in_$1 not in iptables, adding"
410 LOG debug
"create_iface_iptables: mwan3_iface_in_$1 already in iptables, skip"
413 mwan3_push_update COMMIT
416 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-create_iface_iptables-${1}.dump"
417 error
=$
(echo "$update" |
$IPTR 2>&1) || LOG error
"create_iface_iptables (${1}): $error"
420 mwan3_delete_iface_iptables
()
423 config_get family
"$1" family ipv4
425 if [ "$family" = "ipv4" ]; then
429 if [ "$family" = "ipv6" ]; then
430 [ $NO_IPV6 -ne 0 ] && return
436 mwan3_push_update
-D mwan3_ifaces_in \
437 -m mark
--mark 0x0/$MMX_MASK \
438 -j "mwan3_iface_in_$1" &> /dev
/null
439 mwan3_push_update
-F "mwan3_iface_in_$1" &> /dev
/null
440 mwan3_push_update
-X "mwan3_iface_in_$1" &> /dev
/null
442 mwan3_push_update COMMIT
445 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-delete_iface_iptables-${1}.dump"
446 error
=$
(echo "$update" |
$IPTR 2>&1) || LOG error
"delete_iface_iptables (${1}): $error"
449 mwan3_extra_tables_routes
()
451 $IP route list table
"$1"
457 $IP route list table main
458 config_list_foreach
"globals" "rt_table_lookup" mwan3_extra_tables_routes
459 } |
sed -ne "$MWAN3_ROUTE_LINE_EXP" |
sort -u
462 mwan3_create_iface_route
()
464 local tid route_line family IP id tbl
465 config_get family
"$1" family ipv4
466 mwan3_get_iface_id id
"$1"
468 [ -n "$id" ] ||
return 0
470 if [ "$family" = "ipv4" ]; then
472 elif [ "$family" = "ipv6" ]; then
476 tbl
=$
($IP route list table
$id 2>/dev
/null
)$
'\n'
477 mwan3_update_dev_to_table
478 mwan3_get_routes |
while read -r route_line
; do
479 mwan3_route_line_dev
"tid" "$route_line" "$family"
480 { [ -z "${route_line##default*}" ] || [ -z "${route_line##fe80::/64*}" ]; } && [ "$tid" != "$id" ] && continue
481 if [ -z "$tid" ] || [ "$tid" = "$id" ]; then
482 # possible that routes are already in the table
483 # if 'connected' was called after 'ifup'
484 [ -n "$tbl" ] && [ -z "${tbl##*$route_line$'\n'*}" ] && continue
485 $IP route add table $id $route_line ||
486 LOG debug "Route
'$route_line' already added to table
$id"
492 mwan3_delete_iface_route()
496 config_get family "$1" family ipv4
497 mwan3_get_iface_id id "$1"
499 if [ -z "$id" ]; then
500 LOG warn "delete_iface_route
: could not
find table id
for interface
$1"
504 if [ "$family" = "ipv4
" ]; then
505 $IP4 route flush table "$id"
506 elif [ "$family" = "ipv6
" ] && [ $NO_IPV6 -eq 0 ]; then
507 $IP6 route flush table "$id"
511 mwan3_create_iface_rules()
515 config_get family "$1" family ipv4
516 mwan3_get_iface_id id "$1"
518 [ -n "$id" ] || return 0
520 if [ "$family" = "ipv4
" ]; then
522 elif [ "$family" = "ipv6
" ] && [ $NO_IPV6 -eq 0 ]; then
528 mwan3_delete_iface_rules "$1"
530 $IP rule add pref $((id+1000)) iif "$2" lookup "$id"
531 $IP rule add pref $((id+2000)) fwmark "$
(mwan3_id2mask id MMX_MASK
)/$MMX_MASK" lookup "$id"
532 $IP rule add pref $((id+3000)) fwmark "$
(mwan3_id2mask id MMX_MASK
)/$MMX_MASK" unreachable
535 mwan3_delete_iface_rules()
537 local id family IP rule_id
539 config_get family "$1" family ipv4
540 mwan3_get_iface_id id "$1"
542 [ -n "$id" ] || return 0
544 if [ "$family" = "ipv4
" ]; then
546 elif [ "$family" = "ipv6
" ] && [ $NO_IPV6 -eq 0 ]; then
552 for rule_id in $(ip rule list | awk '$1 % 1000 == '$id' && $1 > 1000 && $1 < 4000 {print substr($1,0,4)}'); do
553 $IP rule del pref $rule_id
557 mwan3_delete_iface_ipset_entries()
559 local id setname entry
561 mwan3_get_iface_id id "$1"
563 [ -n "$id" ] || return 0
565 for setname in $(ipset -n list | grep ^mwan3_rule_); do
566 for entry in $(ipset list "$setname" | grep "$
(mwan3_id2mask id MMX_MASK |
awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
567 $IPS del "$setname" $entry ||
568 LOG notice "failed to delete
$entry from
$setname"
576 local id iface family metric probability weight device is_lowest is_offline IPT IPTR total_weight current update error
579 config_get iface "$1" interface
580 config_get metric "$1" metric 1
581 config_get weight "$1" weight 1
583 [ -n "$iface" ] || return 0
584 network_get_device device "$iface"
585 [ "$metric" -gt $DEFAULT_LOWEST_METRIC ] && LOG warn "Member interface
$iface has
>$DEFAULT_LOWEST_METRIC metric. Not appending to policy
" && return 0
587 mwan3_get_iface_id id "$iface"
589 [ -n "$id" ] || return 0
591 [ "$
(mwan3_get_iface_hotplug_state
"$iface")" = "online
" ]
594 config_get family "$iface" family ipv4
596 if [ "$family" = "ipv4
" ]; then
599 elif [ "$family" = "ipv6
" ]; then
603 current="$
($IPT -S)"$'\n'
606 if [ "$family" = "ipv4
" ] && [ $is_offline -eq 0 ]; then
607 if [ "$metric" -lt "$lowest_metric_v4" ]; then
609 total_weight_v4=$weight
610 lowest_metric_v4=$metric
611 elif [ "$metric" -eq "$lowest_metric_v4" ]; then
612 total_weight_v4=$((total_weight_v4+weight))
613 total_weight=$total_weight_v4
617 elif [ "$family" = "ipv6
" ] && [ $NO_IPV6 -eq 0 ] && [ $is_offline -eq 0 ]; then
618 if [ "$metric" -lt "$lowest_metric_v6" ]; then
620 total_weight_v6=$weight
621 lowest_metric_v6=$metric
622 elif [ "$metric" -eq "$lowest_metric_v6" ]; then
623 total_weight_v6=$((total_weight_v6+weight))
624 total_weight=$total_weight_v6
629 if [ $is_lowest -eq 1 ]; then
630 mwan3_push_update -F "mwan3_policy_
$policy"
631 mwan3_push_update -A "mwan3_policy_
$policy" \
632 -m mark --mark 0x0/$MMX_MASK \
633 -m comment --comment \"$iface $weight $weight\" \
634 -j MARK --set-xmark "$
(mwan3_id2mask id MMX_MASK
)/$MMX_MASK"
635 elif [ $is_offline -eq 0 ]; then
636 probability=$((weight*1000/total_weight))
637 if [ "$probability" -lt 10 ]; then
638 probability="0.00$probability"
639 elif [ $probability -lt 100 ]; then
640 probability="0.0$probability"
641 elif [ $probability -lt 1000 ]; then
642 probability="0.
$probability"
647 mwan3_push_update -I "mwan3_policy_
$policy" \
648 -m mark --mark 0x0/$MMX_MASK \
651 --probability "$probability" \
652 -m comment --comment \"$iface $weight $total_weight\" \
653 -j MARK --set-xmark "$
(mwan3_id2mask id MMX_MASK
)/$MMX_MASK"
654 elif [ -n "$device" ]; then
655 echo "$current" | grep -q "^
-A mwan3_policy_
$policy.
*--comment .
* [0-9]* [0-9]*" ||
656 mwan3_push_update -I "mwan3_policy_
$policy" \
658 -m mark --mark 0x0/$MMX_MASK \
659 -m comment --comment \"out $iface $device\" \
660 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
662 mwan3_push_update COMMIT
665 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_policy-
${1}.dump
"
666 error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_policy
($1): $error"
669 mwan3_create_policies_iptables()
671 local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT current update error
675 config_get last_resort "$1" last_resort unreachable
677 if [ "$1" != "$
(echo "$1" | cut
-c1-15)" ]; then
678 LOG warn "Policy
$1 exceeds max of
15 chars. Not setting policy
" && return 0
681 for IPT in "$IPT4" "$IPT6"; do
682 [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
683 current="$
($IPT -S)"$'\n'
685 if [ -n "${current##*-N mwan3_policy_$1$'\n'*}" ]; then
686 mwan3_push_update -N "mwan3_policy_
$1"
689 mwan3_push_update -F "mwan3_policy_
$1"
691 case "$last_resort" in
693 mwan3_push_update -A "mwan3_policy_
$1" \
694 -m mark --mark 0x0/$MMX_MASK \
695 -m comment --comment "blackhole
" \
696 -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK
699 mwan3_push_update -A "mwan3_policy_
$1" \
700 -m mark --mark 0x0/$MMX_MASK \
701 -m comment --comment "default
" \
702 -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
705 mwan3_push_update -A "mwan3_policy_
$1" \
706 -m mark --mark 0x0/$MMX_MASK \
707 -m comment --comment "unreachable
" \
708 -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK
711 mwan3_push_update COMMIT
714 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-create_policies_iptables-
${1}.dump
"
715 if [ "$IPT" = "$IPT4" ]; then
716 error=$(echo "$update" | $IPT4R 2>&1) || LOG error "create_policies_iptables
($1): $error"
718 error=$(echo "$update" | $IPT6R 2>&1) || LOG error "create_policies_iptables
($1): $error"
722 lowest_metric_v4=$DEFAULT_LOWEST_METRIC
725 lowest_metric_v6=$DEFAULT_LOWEST_METRIC
728 config_list_foreach "$1" use_member mwan3_set_policy
731 mwan3_set_policies_iptables()
733 config_foreach mwan3_create_policies_iptables policy
736 mwan3_set_sticky_iptables()
738 local interface="${1}"
744 for iface in $(echo "$current" | grep "^
-A $policy" | cut -s -d'"' -f2 | awk '{print
$1}'); do
745 if [ "$iface" = "$interface" ]; then
747 mwan3_get_iface_id id "$iface"
749 [ -n "$id" ] || return 0
750 if [ -z "${current##*-N mwan3_iface_in_${iface}$'\n'*}" ]; then
751 mwan3_push_update -I "mwan3_rule_$rule" \
752 -m mark --mark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" \
753 -m set ! --match-set "mwan3_rule_${ipv}_${rule}" src,src \
754 -j MARK --set-xmark "0x0/$MMX_MASK"
755 mwan3_push_update -I "mwan3_rule_$rule" \
756 -m mark --mark "0/$MMX_MASK" \
757 -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
763 mwan3_set_sticky_ipset()
772 mwan3_push_update -! create "mwan3_rule_ipv4_$rule" \
773 hash:ip,mark markmask "$mmx" \
776 [ $NO_IPV6 -eq 0 ] &&
777 mwan3_push_update -! create "mwan3_rule_ipv6_$rule" \
778 hash:ip,mark markmask "$mmx" \
779 timeout "$timeout" family inet6
781 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_sticky_ipset-${rule}.dump"
782 error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_sticky_ipset (${rule}): $error"
785 mwan3_set_user_iptables_rule()
787 local ipset family proto policy src_ip src_port src_iface src_dev
788 local sticky dest_ip dest_port use_policy timeout policy
789 local global_logging rule_logging loglevel rule_policy rule ipv
794 config_get sticky "$1" sticky 0
795 config_get timeout "$1" timeout 600
796 config_get ipset "$1" ipset
797 config_get proto "$1" proto all
798 config_get src_ip "$1" src_ip
799 config_get src_iface "$1" src_iface
800 config_get src_port "$1" src_port
801 config_get dest_ip "$1" dest_ip
802 config_get dest_port "$1" dest_port
803 config_get use_policy "$1" use_policy
804 config_get family "$1" family any
805 config_get rule_logging "$1" logging 0
806 config_get global_logging globals logging 0
807 config_get loglevel globals loglevel notice
809 [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
810 [ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
811 [ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
813 for ipaddr in "$src_ip" "$dest_ip"; do
814 if [ -n "$ipaddr" ] && { { [ "$ipv" = "ipv4" ] && echo "$ipaddr" | grep -qE "$IPv6_REGEX"; } ||
815 { [ "$ipv" = "ipv6" ] && echo "$ipaddr" | grep -qE $IPv4_REGEX; } }; then
816 LOG warn "invalid $ipv address $ipaddr specified for rule $rule"
821 if [ -n "$src_iface" ]; then
822 network_get_device src_dev "$src_iface"
823 if [ -z "$src_dev" ]; then
824 LOG notice "could not find device corresponding to src_iface $src_iface for rule $1"
829 [ -z "$dest_ip" ] && unset dest_ip
830 [ -z "$src_ip" ] && unset src_ip
831 [ -z "$ipset" ] && unset ipset
832 [ -z "$src_port" ] && unset src_port
833 [ -z "$dest_port" ] && unset dest_port
834 if [ "$proto" != 'tcp
' ] && [ "$proto" != 'udp
' ]; then
835 [ -n "$src_port" ] && {
836 LOG warn "src_port set to '$src_port' but proto set to '$proto' not tcp or udp. src_port will be ignored"
839 [ -n "$dest_port" ] && {
840 LOG warn "dest_port set to '$dest_port' but proto set to '$proto' not tcp or udp. dest_port will be ignored"
846 if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
847 LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0
850 if [ -n "$ipset" ]; then
851 ipset="-m set --match-set $ipset dst"
854 if [ -z "$use_policy" ]; then
858 if [ "$use_policy" = "default" ]; then
859 policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK"
860 elif [ "$use_policy" = "unreachable" ]; then
861 policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK"
862 elif [ "$use_policy" = "blackhole" ]; then
863 policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK"
866 policy="mwan3_policy_$use_policy"
867 if [ "$sticky" -eq 1 ]; then
868 mwan3_set_sticky_ipset "$rule" "$MMX_MASK" "$timeout"
872 if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy$'\n'*}" ]; then
873 mwan3_push_update -N "$policy"
876 if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then
877 if [ -n "${current##*-N mwan3_rule_$1$'\n'*}" ]; then
878 mwan3_push_update -N "mwan3_rule_$1"
881 mwan3_push_update -F "mwan3_rule_$1"
882 config_foreach mwan3_set_sticky_iptables interface "$rule" "$ipv" "$policy"
885 mwan3_push_update -A "mwan3_rule_$1" \
886 -m mark --mark 0/$MMX_MASK \
888 mwan3_push_update -A "mwan3_rule_$1" \
889 -m mark ! --mark 0xfc00/0xfc00 \
890 -j SET --del-set "mwan3_rule_${ipv}_${rule}" src,src
891 mwan3_push_update -A "mwan3_rule_$1" \
892 -m mark ! --mark 0xfc00/0xfc00 \
893 -j SET --add-set "mwan3_rule_${ipv}_${rule}" src,src
894 policy="mwan3_rule_$1"
896 if [ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ]; then
897 mwan3_push_update -A mwan3_rules \
899 ${src_ip:+-s} $src_ip \
900 ${src_dev:+-i} $src_dev \
901 ${dest_ip:+-d} $dest_ip \
903 ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
904 ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
905 -m mark --mark 0/$MMX_MASK \
906 -m comment --comment "$1" \
907 -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)"
910 mwan3_push_update -A mwan3_rules \
912 ${src_ip:+-s} $src_ip \
913 ${src_dev:+-i} $src_dev \
914 ${dest_ip:+-d} $dest_ip \
916 ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
917 ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
918 -m mark --mark 0/$MMX_MASK \
923 mwan3_set_user_iface_rules()
925 local current iface update family error device is_src_iface
929 if [ -z "$device" ]; then
930 LOG notice "set_user_iface_rules: could not find device corresponding to iface $iface"
934 config_get family "$iface" family ipv4
936 if [ "$family" = "ipv4" ]; then
939 elif [ "$family" = "ipv6" ]; then
943 $IPT -S | grep -q "^-A mwan3_rules.*-i $device" && return
950 config_get src_iface "$1" src_iface
951 [ "$src_iface" = "$iface" ] && is_src_iface=1
953 config_foreach iface_rule rule
954 [ $is_src_iface -eq 1 ] && mwan3_set_user_rules
957 mwan3_set_user_rules()
960 local current update error
962 for ipv in ipv4 ipv6; do
963 if [ "$ipv" = "ipv4" ]; then
966 elif [ "$ipv" = "ipv6" ]; then
970 [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
972 current="$($IPT -S)"$'\n'
975 if [ -n "${current##*-N mwan3_rules*}" ]; then
976 mwan3_push_update -N "mwan3_rules"
979 mwan3_push_update -F mwan3_rules
981 config_foreach mwan3_set_user_iptables_rule rule "$ipv"
983 mwan3_push_update COMMIT
986 echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_user_rules-${ipv}.dump"
987 error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_user_rules (${ipv}): $error"
993 mwan3_interface_hotplug_shutdown()
995 local interface status device ifdown
998 [ -f $MWAN3TRACK_STATUS_DIR/$interface/STATUS ] && {
999 status=$(cat $MWAN3TRACK_STATUS_DIR/$interface/STATUS)
1002 [ "$status" != "online" ] && [ "$ifdown" != 1 ] && return
1004 if [ "$ifdown" = 1 ]; then
1005 env -i ACTION=ifdown \
1006 INTERFACE=$interface \
1008 sh /etc/hotplug.d/iface/15-mwan3
1010 [ "$status" = "online" ] && {
1011 env -i MWAN3_SHUTDOWN="1" \
1012 ACTION="disconnected" \
1013 INTERFACE="$interface" \
1014 DEVICE="$device" /sbin/hotplug-call iface
1020 mwan3_interface_shutdown()
1022 mwan3_interface_hotplug_shutdown $1
1023 mwan3_track_clean $1
1031 local up l3_device status true_iface
1033 if [ "${caller}" = "cmd" ]; then
1034 # It is not necessary to obtain a lock here, because it is obtained in the hotplug
1035 # script, but we still want to do the check to print a useful error message
1036 /etc/init.d/mwan3 running || {
1037 echo 'The service mwan3 is global disabled.
'
1038 echo 'Please execute
"/etc/init.d/mwan3 start" first.
'
1043 mwan3_get_true_iface true_iface $interface
1044 status=$(ubus -S call network.interface.$true_iface status)
1046 [ -n "$status" ] && {
1048 json_get_vars up l3_device
1052 env -i MWAN3_STARTUP=$caller ACTION=ifup \
1053 INTERFACE=$interface DEVICE=$l3_device \
1054 sh /etc/hotplug.d/iface/15-mwan3
1057 if [ "$up" != "1" ] || [ -z "$l3_device" ]; then
1061 if [ "${caller}" = "init" ]; then
1063 hotplug_pids="$hotplug_pids $!"
1070 mwan3_set_iface_hotplug_state() {
1074 echo "$state" > "$MWAN3_STATUS_DIR/iface_state/$iface"
1077 mwan3_get_iface_hotplug_state() {
1080 cat "$MWAN3_STATUS_DIR/iface_state/$iface" 2>/dev/null || echo "offline"
1083 mwan3_report_iface_status()
1085 local device result tracking IP IPT error
1087 mwan3_get_iface_id id "$1"
1088 network_get_device device "$1"
1089 config_get enabled "$1" enabled 0
1090 config_get family "$1" family ipv4
1092 if [ "$family" = "ipv4" ]; then
1097 if [ "$family" = "ipv6" ]; then
1102 if [ -z "$id" ] || [ -z "$device" ]; then
1106 [ -n "$($IP rule | awk '$1 == "'$((id+1000)):'"')" ] ||
1108 [ -n "$($IP rule | awk '$1 == "'$((id+2000)):'"')" ] ||
1110 [ -n "$($IP rule | awk '$1 == "'$((id+3000)):'"')" ] ||
1112 [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] ||
1114 [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ] ||
1118 if [ "$result" = "offline" ]; then
1120 elif [ $error -eq 0 ]; then
1121 online=$(get_online_time "$1")
1122 network_get_uptime uptime "$1"
1123 online="$(printf '%02dh
:%02dm
:%02ds
\n' $((online/3600)) $((online%3600/60)) $((online%60)))"
1124 uptime="$(printf '%02dh
:%02dm
:%02ds
\n' $((uptime/3600)) $((uptime%3600/60)) $((uptime%60)))"
1125 result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime"
1126 elif [ $error -gt 0 ] && [ $error -ne 31 ]; then
1127 result="error (${error})"
1128 elif [ "$enabled" = "1" ]; then
1134 tracking="$(mwan3_get_mwan3track_status $1)"
1135 echo " interface $1 is $result and tracking is $tracking"
1138 mwan3_report_policies()
1143 local percent total_weight weight iface
1145 total_weight=$($ipt -S "$policy" | grep -v '.
*--comment "out .*" .
*$
' | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
1147 if [ -n "${total_weight##*[!0-9]*}" ]; then
1148 for iface in $($ipt -S "$policy" | grep -v '.*--comment "out .
*" .*$' | cut -s -d'"' -f2 | awk '{print
$1}'); do
1149 weight=$($ipt -S "$policy" | grep -v '.
*--comment "out .*" .
*$
' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
1150 percent=$((weight*100/total_weight))
1151 echo " $iface ($percent%)"
1154 echo " $
($ipt -S "$policy" |
grep -v '.*--comment "out .*" .*$' |
sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')"
1158 mwan3_report_policies_v4()
1162 for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
1163 echo "$policy:" | sed 's/mwan3_policy_//'
1164 mwan3_report_policies "$IPT4" "$policy"
1168 mwan3_report_policies_v6()
1172 for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
1173 echo "$policy:" | sed 's/mwan3_policy_//'
1174 mwan3_report_policies "$IPT6" "$policy"
1178 mwan3_report_connected_v4()
1180 if [ -n "$
($IPT4 -S mwan3_connected_ipv4
2> /dev
/null
)" ]; then
1181 $IPS -o save list mwan3_connected_ipv4 | grep add | cut -d " " -f 3
1185 mwan3_report_connected_v6()
1187 if [ -n "$
($IPT6 -S mwan3_connected_ipv6
2> /dev
/null
)" ]; then
1188 $IPS -o save list mwan3_connected_ipv6 | grep add | cut -d " " -f 3
1192 mwan3_report_rules_v4()
1194 if [ -n "$
($IPT4 -S mwan3_rules
2> /dev
/null
)" ]; then
1195 $IPT4 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
1199 mwan3_report_rules_v6()
1201 if [ -n "$
($IPT6 -S mwan3_rules
2> /dev
/null
)" ]; then
1202 $IPT6 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
1206 mwan3_flush_conntrack()
1208 local interface="$1"
1212 local flush_conntrack="$1"
1215 if [ "$action" = "$flush_conntrack" ]; then
1216 echo f > ${CONNTRACK_FILE}
1217 LOG info "Connection tracking flushed
for interface
'$interface' on action
'$action'"
1221 if [ -e "$CONNTRACK_FILE" ]; then
1222 config_list_foreach "$interface" flush_conntrack handle_flush "$action"
1228 rm -rf "${MWAN3_STATUS_DIR:?}/${1}" &> /dev/null
1229 rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR"