You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1230 lines
33 KiB

mwan3: fix interface-bound traffic when interface is offline This commit fixed what 6d99b602 was supposed to fix without affecting interface-bound traffic. Before 6d99b602 interface-bound traffic was working normally as long as at least one interface was online. However when the last interface went offline, it was impossible to ping and such state was unrecoverable. Commit 6d99b602 fixed unrecoverable offline state problem (it was possible to ping -I iface) but messed inteface-bound traffic. Traffic with interface source address was not working if the interface was in "offline" state, even if another interface was online. The problem was caused by an inconsistent "offline" interface state: iptables-related rules were kept while routing table and policy were deleted. The idea behind this commit is to: 1. Keep all the rules for each interface (iptables, routing table, policy) regardless of its state. This ensures consistency, 2. Make interface state hotplug events affect only iptables' mwan3_policy_* rules. Interface-related iptables, routing table and policy is removed only when mwan3 is manually stopped. To make such changes possible, it's necessary to change the way mwan3_policy_* rule generator keeps track of interface state hotplug events. Until now, it checked for the existence of custom interface-related routing table (table id 1, 2, 3, ...). Clearly we can no longer rely on that so each interface state is stored explicitly in file. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
7 years ago
mwan3: fix interface-bound traffic when interface is offline This commit fixed what 6d99b602 was supposed to fix without affecting interface-bound traffic. Before 6d99b602 interface-bound traffic was working normally as long as at least one interface was online. However when the last interface went offline, it was impossible to ping and such state was unrecoverable. Commit 6d99b602 fixed unrecoverable offline state problem (it was possible to ping -I iface) but messed inteface-bound traffic. Traffic with interface source address was not working if the interface was in "offline" state, even if another interface was online. The problem was caused by an inconsistent "offline" interface state: iptables-related rules were kept while routing table and policy were deleted. The idea behind this commit is to: 1. Keep all the rules for each interface (iptables, routing table, policy) regardless of its state. This ensures consistency, 2. Make interface state hotplug events affect only iptables' mwan3_policy_* rules. Interface-related iptables, routing table and policy is removed only when mwan3 is manually stopped. To make such changes possible, it's necessary to change the way mwan3_policy_* rule generator keeps track of interface state hotplug events. Until now, it checked for the existence of custom interface-related routing table (table id 1, 2, 3, ...). Clearly we can no longer rely on that so each interface state is stored explicitly in file. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
7 years ago
mwan3: fix interface-bound traffic when interface is offline This commit fixed what 6d99b602 was supposed to fix without affecting interface-bound traffic. Before 6d99b602 interface-bound traffic was working normally as long as at least one interface was online. However when the last interface went offline, it was impossible to ping and such state was unrecoverable. Commit 6d99b602 fixed unrecoverable offline state problem (it was possible to ping -I iface) but messed inteface-bound traffic. Traffic with interface source address was not working if the interface was in "offline" state, even if another interface was online. The problem was caused by an inconsistent "offline" interface state: iptables-related rules were kept while routing table and policy were deleted. The idea behind this commit is to: 1. Keep all the rules for each interface (iptables, routing table, policy) regardless of its state. This ensures consistency, 2. Make interface state hotplug events affect only iptables' mwan3_policy_* rules. Interface-related iptables, routing table and policy is removed only when mwan3 is manually stopped. To make such changes possible, it's necessary to change the way mwan3_policy_* rule generator keeps track of interface state hotplug events. Until now, it checked for the existence of custom interface-related routing table (table id 1, 2, 3, ...). Clearly we can no longer rely on that so each interface state is stored explicitly in file. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
7 years ago
mwan3: fix interface-bound traffic when interface is offline This commit fixed what 6d99b602 was supposed to fix without affecting interface-bound traffic. Before 6d99b602 interface-bound traffic was working normally as long as at least one interface was online. However when the last interface went offline, it was impossible to ping and such state was unrecoverable. Commit 6d99b602 fixed unrecoverable offline state problem (it was possible to ping -I iface) but messed inteface-bound traffic. Traffic with interface source address was not working if the interface was in "offline" state, even if another interface was online. The problem was caused by an inconsistent "offline" interface state: iptables-related rules were kept while routing table and policy were deleted. The idea behind this commit is to: 1. Keep all the rules for each interface (iptables, routing table, policy) regardless of its state. This ensures consistency, 2. Make interface state hotplug events affect only iptables' mwan3_policy_* rules. Interface-related iptables, routing table and policy is removed only when mwan3 is manually stopped. To make such changes possible, it's necessary to change the way mwan3_policy_* rule generator keeps track of interface state hotplug events. Until now, it checked for the existence of custom interface-related routing table (table id 1, 2, 3, ...). Clearly we can no longer rely on that so each interface state is stored explicitly in file. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
7 years ago
mwan3: fix interface-bound traffic when interface is offline This commit fixed what 6d99b602 was supposed to fix without affecting interface-bound traffic. Before 6d99b602 interface-bound traffic was working normally as long as at least one interface was online. However when the last interface went offline, it was impossible to ping and such state was unrecoverable. Commit 6d99b602 fixed unrecoverable offline state problem (it was possible to ping -I iface) but messed inteface-bound traffic. Traffic with interface source address was not working if the interface was in "offline" state, even if another interface was online. The problem was caused by an inconsistent "offline" interface state: iptables-related rules were kept while routing table and policy were deleted. The idea behind this commit is to: 1. Keep all the rules for each interface (iptables, routing table, policy) regardless of its state. This ensures consistency, 2. Make interface state hotplug events affect only iptables' mwan3_policy_* rules. Interface-related iptables, routing table and policy is removed only when mwan3 is manually stopped. To make such changes possible, it's necessary to change the way mwan3_policy_* rule generator keeps track of interface state hotplug events. Until now, it checked for the existence of custom interface-related routing table (table id 1, 2, 3, ...). Clearly we can no longer rely on that so each interface state is stored explicitly in file. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
7 years ago
  1. #!/bin/sh
  2. . "${IPKG_INSTROOT}/usr/share/libubox/jshn.sh"
  3. . "${IPKG_INSTROOT}/lib/mwan3/common.sh"
  4. CONNTRACK_FILE="/proc/net/nf_conntrack"
  5. IPv6_REGEX="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
  6. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|"
  7. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
  8. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
  9. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
  10. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
  11. IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
  12. IPv6_REGEX="${IPv6_REGEX}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
  13. IPv6_REGEX="${IPv6_REGEX}:((:[0-9a-fA-F]{1,4}){1,7}|:)|"
  14. IPv6_REGEX="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
  15. 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])|"
  16. 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])"
  17. 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]?)"
  18. DEFAULT_LOWEST_METRIC=256
  19. mwan3_push_update()
  20. {
  21. # helper function to build an update string to pass on to
  22. # IPTR or IPS RESTORE. Modifies the 'update' variable in
  23. # the local scope.
  24. update="$update"$'\n'"$*";
  25. }
  26. mwan3_update_dev_to_table()
  27. {
  28. local _tid
  29. # shellcheck disable=SC2034
  30. mwan3_dev_tbl_ipv4=" "
  31. # shellcheck disable=SC2034
  32. mwan3_dev_tbl_ipv6=" "
  33. update_table()
  34. {
  35. local family curr_table device enabled
  36. let _tid++
  37. config_get family "$1" family ipv4
  38. network_get_device device "$1"
  39. [ -z "$device" ] && return
  40. config_get enabled "$1" enabled
  41. [ "$enabled" -eq 0 ] && return
  42. curr_table=$(eval "echo \"\$mwan3_dev_tbl_${family}\"")
  43. export "mwan3_dev_tbl_$family=${curr_table}${device}=$_tid "
  44. }
  45. network_flush_cache
  46. config_foreach update_table interface
  47. }
  48. mwan3_update_iface_to_table()
  49. {
  50. local _tid
  51. mwan3_iface_tbl=" "
  52. update_table()
  53. {
  54. let _tid++
  55. export mwan3_iface_tbl="${mwan3_iface_tbl}${1}=$_tid "
  56. }
  57. config_foreach update_table interface
  58. }
  59. mwan3_route_line_dev()
  60. {
  61. # must have mwan3 config already loaded
  62. # arg 1 is route device
  63. local _tid route_line route_device route_family entry curr_table
  64. route_line=$2
  65. route_family=$3
  66. route_device=$(echo "$route_line" | sed -ne "s/.*dev \([^ ]*\).*/\1/p")
  67. unset "$1"
  68. [ -z "$route_device" ] && return
  69. curr_table=$(eval "echo \"\$mwan3_dev_tbl_${route_family}\"")
  70. for entry in $curr_table; do
  71. if [ "${entry%%=*}" = "$route_device" ]; then
  72. _tid=${entry##*=}
  73. export "$1=$_tid"
  74. return
  75. fi
  76. done
  77. }
  78. # counts how many bits are set to 1
  79. # n&(n-1) clears the lowest bit set to 1
  80. mwan3_count_one_bits()
  81. {
  82. local count n
  83. count=0
  84. n=$(($1))
  85. while [ "$n" -gt "0" ]; do
  86. n=$((n&(n-1)))
  87. count=$((count+1))
  88. done
  89. echo $count
  90. }
  91. mwan3_get_iface_id()
  92. {
  93. local _tmp
  94. [ -z "$mwan3_iface_tbl" ] && mwan3_update_iface_to_table
  95. _tmp="${mwan3_iface_tbl##* ${2}=}"
  96. _tmp=${_tmp%% *}
  97. export "$1=$_tmp"
  98. }
  99. mwan3_set_custom_ipset_v4()
  100. {
  101. local custom_network_v4
  102. for custom_network_v4 in $($IP4 route list table "$1" | awk '{print $1}' | grep -E "$IPv4_REGEX"); do
  103. LOG notice "Adding network $custom_network_v4 from table $1 to mwan3_custom_v4 ipset"
  104. mwan3_push_update -! add mwan3_custom_ipv4 "$custom_network_v4"
  105. done
  106. }
  107. mwan3_set_custom_ipset_v6()
  108. {
  109. local custom_network_v6
  110. for custom_network_v6 in $($IP6 route list table "$1" | awk '{print $1}' | grep -E "$IPv6_REGEX"); do
  111. LOG notice "Adding network $custom_network_v6 from table $1 to mwan3_custom_v6 ipset"
  112. mwan3_push_update -! add mwan3_custom_ipv6 "$custom_network_v6"
  113. done
  114. }
  115. mwan3_set_custom_ipset()
  116. {
  117. local update=""
  118. mwan3_push_update -! create mwan3_custom_ipv4 hash:net
  119. mwan3_push_update flush mwan3_custom_ipv4
  120. config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v4
  121. if [ $NO_IPV6 -eq 0 ]; then
  122. mwan3_push_update -! create mwan3_custom_ipv6 hash:net family inet6
  123. mwan3_push_update flush mwan3_custom_ipv6
  124. config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v6
  125. fi
  126. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_custom_ipset.dump"
  127. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_custom_ipset: $error"
  128. }
  129. mwan3_set_connected_ipv4()
  130. {
  131. local connected_network_v4 error
  132. local candidate_list cidr_list
  133. local update=""
  134. mwan3_push_update -! create mwan3_connected_ipv4 hash:net
  135. mwan3_push_update flush mwan3_connected_ipv4
  136. candidate_list=""
  137. cidr_list=""
  138. route_lists()
  139. {
  140. $IP4 route | awk '{print $1}'
  141. $IP4 route list table 0 | awk '{print $2}'
  142. }
  143. for connected_network_v4 in $(route_lists | grep -E "$IPv4_REGEX"); do
  144. if [ -z "${connected_network_v4##*/*}" ]; then
  145. cidr_list="$cidr_list $connected_network_v4"
  146. else
  147. candidate_list="$candidate_list $connected_network_v4"
  148. fi
  149. done
  150. for connected_network_v4 in $cidr_list; do
  151. mwan3_push_update -! add mwan3_connected_ipv4 "$connected_network_v4"
  152. done
  153. for connected_network_v4 in $candidate_list; do
  154. mwan3_push_update -! add mwan3_connected_ipv4 "$connected_network_v4"
  155. done
  156. mwan3_push_update add mwan3_connected_ipv4 224.0.0.0/3
  157. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipv4.dump"
  158. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipv4: $error"
  159. }
  160. mwan3_set_connected_ipv6()
  161. {
  162. local connected_network_v6 error
  163. local update=""
  164. [ $NO_IPV6 -eq 0 ] || return
  165. mwan3_push_update -! create mwan3_connected_ipv6 hash:net family inet6
  166. mwan3_push_update flush mwan3_connected_ipv6
  167. for connected_network_v6 in $($IP6 route | awk '{print $1}' | grep -E "$IPv6_REGEX"); do
  168. mwan3_push_update -! add mwan3_connected_ipv6 "$connected_network_v6"
  169. done
  170. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipv6.dump"
  171. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipv6: $error"
  172. }
  173. mwan3_set_connected_ipset()
  174. {
  175. local error
  176. local update=""
  177. mwan3_push_update -! create mwan3_connected_ipv4 hash:net
  178. mwan3_push_update flush mwan3_connected_ipv4
  179. if [ $NO_IPV6 -eq 0 ]; then
  180. mwan3_push_update -! create mwan3_connected_ipv6 hash:net family inet6
  181. mwan3_push_update flush mwan3_connected_ipv6
  182. fi
  183. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_connected_ipset.dump"
  184. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipset: $error"
  185. }
  186. mwan3_set_dynamic_ipset()
  187. {
  188. local error
  189. local update=""
  190. mwan3_push_update -! create mwan3_dynamic_ipv4 list:set
  191. mwan3_push_update flush mwan3_dynamic_ipv4
  192. if [ $NO_IPV6 -eq 0 ]; then
  193. mwan3_push_update -! create mwan3_dynamic_ipv6 hash:net family inet6
  194. mwan3_push_update flush mwan3_dynamic_ipv6
  195. fi
  196. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_dynamic_ipset.dump"
  197. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_dynamic_ipset: $error"
  198. }
  199. mwan3_set_general_rules()
  200. {
  201. local IP
  202. for IP in "$IP4" "$IP6"; do
  203. [ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue
  204. RULE_NO=$((MM_BLACKHOLE+2000))
  205. if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
  206. $IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole
  207. fi
  208. RULE_NO=$((MM_UNREACHABLE+2000))
  209. if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
  210. $IP rule add pref $RULE_NO fwmark $MMX_UNREACHABLE/$MMX_MASK unreachable
  211. fi
  212. done
  213. }
  214. mwan3_set_general_iptables()
  215. {
  216. local IPT current update error family
  217. for IPT in "$IPT4" "$IPT6"; do
  218. [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
  219. current="$($IPT -S)"$'\n'
  220. update="*mangle"
  221. if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
  222. mwan3_push_update -N mwan3_ifaces_in
  223. fi
  224. if [ "$IPT" = "$IPT6" ]; then
  225. family="ipv6"
  226. else
  227. family="ipv4"
  228. fi
  229. for chain in custom connected dynamic; do
  230. echo "${current}" | grep -q "\-N mwan3_${chain}_${family}$"
  231. local ret="$?"
  232. if [ "$ret" = 1 ]; then
  233. mwan3_push_update -N mwan3_${chain}_${family}
  234. mwan3_push_update -A mwan3_${chain}_${family} \
  235. -m set --match-set mwan3_${chain}_${family} dst \
  236. -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
  237. fi
  238. done
  239. if [ -n "${current##*-N mwan3_rules*}" ]; then
  240. mwan3_push_update -N mwan3_rules
  241. fi
  242. if [ -n "${current##*-N mwan3_hook*}" ]; then
  243. mwan3_push_update -N mwan3_hook
  244. # do not mangle ipv6 ra service
  245. if [ "$IPT" = "$IPT6" ]; then
  246. mwan3_push_update -A mwan3_hook \
  247. -p ipv6-icmp \
  248. -m icmp6 --icmpv6-type 133 \
  249. -j RETURN
  250. mwan3_push_update -A mwan3_hook \
  251. -p ipv6-icmp \
  252. -m icmp6 --icmpv6-type 134 \
  253. -j RETURN
  254. mwan3_push_update -A mwan3_hook \
  255. -p ipv6-icmp \
  256. -m icmp6 --icmpv6-type 135 \
  257. -j RETURN
  258. mwan3_push_update -A mwan3_hook \
  259. -p ipv6-icmp \
  260. -m icmp6 --icmpv6-type 136 \
  261. -j RETURN
  262. mwan3_push_update -A mwan3_hook \
  263. -p ipv6-icmp \
  264. -m icmp6 --icmpv6-type 137 \
  265. -j RETURN
  266. fi
  267. mwan3_push_update -A mwan3_hook \
  268. -m mark --mark 0x0/$MMX_MASK \
  269. -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
  270. mwan3_push_update -A mwan3_hook \
  271. -m mark --mark 0x0/$MMX_MASK \
  272. -j mwan3_ifaces_in
  273. for chain in custom connected dynamic; do
  274. mwan3_push_update -A mwan3_hook \
  275. -m mark --mark 0x0/$MMX_MASK \
  276. -j mwan3_${chain}_${family}
  277. done
  278. mwan3_push_update -A mwan3_hook \
  279. -m mark --mark 0x0/$MMX_MASK \
  280. -j mwan3_rules
  281. mwan3_push_update -A mwan3_hook \
  282. -j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
  283. for chain in custom connected dynamic; do
  284. mwan3_push_update -A mwan3_hook \
  285. -m mark ! --mark $MMX_DEFAULT/$MMX_MASK \
  286. -j mwan3_${chain}_${family}
  287. done
  288. fi
  289. if [ -n "${current##*-A PREROUTING -j mwan3_hook*}" ]; then
  290. mwan3_push_update -A PREROUTING -j mwan3_hook
  291. fi
  292. if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then
  293. mwan3_push_update -A OUTPUT -j mwan3_hook
  294. fi
  295. mwan3_push_update COMMIT
  296. mwan3_push_update ""
  297. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_general_iptables-${family}.dump"
  298. if [ "$IPT" = "$IPT4" ]; then
  299. error=$(echo "$update" | $IPT4R 2>&1) || LOG error "set_general_iptables (${family}): $error"
  300. else
  301. error=$(echo "$update" | $IPT6R 2>&1) || LOG error "set_general_iptables (${family}): $error"
  302. fi
  303. done
  304. }
  305. mwan3_create_iface_iptables()
  306. {
  307. local id family IPT IPTR current update error
  308. config_get family "$1" family ipv4
  309. mwan3_get_iface_id id "$1"
  310. [ -n "$id" ] || return 0
  311. if [ "$family" = "ipv4" ]; then
  312. IPT="$IPT4"
  313. IPTR="$IPT4R"
  314. elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
  315. IPT="$IPT6"
  316. IPTR="$IPT6R"
  317. else
  318. return
  319. fi
  320. current="$($IPT -S)"$'\n'
  321. update="*mangle"
  322. if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
  323. mwan3_push_update -N mwan3_ifaces_in
  324. fi
  325. if [ -n "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then
  326. mwan3_push_update -N "mwan3_iface_in_$1"
  327. else
  328. mwan3_push_update -F "mwan3_iface_in_$1"
  329. fi
  330. for chain in custom connected dynamic; do
  331. mwan3_push_update -A "mwan3_iface_in_$1" \
  332. -i "$2" \
  333. -m set --match-set mwan3_${chain}_${family} src \
  334. -m mark --mark "0x0/$MMX_MASK" \
  335. -m comment --comment "default" \
  336. -j MARK --set-xmark "$MMX_DEFAULT/$MMX_MASK"
  337. done
  338. mwan3_push_update -A "mwan3_iface_in_$1" \
  339. -i "$2" \
  340. -m mark --mark "0x0/$MMX_MASK" \
  341. -m comment --comment "$1" \
  342. -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
  343. if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}$'\n'*}" ]; then
  344. mwan3_push_update -A mwan3_ifaces_in \
  345. -m mark --mark 0x0/$MMX_MASK \
  346. -j "mwan3_iface_in_$1"
  347. LOG debug "create_iface_iptables: mwan3_iface_in_$1 not in iptables, adding"
  348. else
  349. LOG debug "create_iface_iptables: mwan3_iface_in_$1 already in iptables, skip"
  350. fi
  351. mwan3_push_update COMMIT
  352. mwan3_push_update ""
  353. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-create_iface_iptables-${1}.dump"
  354. error=$(echo "$update" | $IPTR 2>&1) || LOG error "create_iface_iptables (${1}): $error"
  355. }
  356. mwan3_delete_iface_iptables()
  357. {
  358. local IPT update
  359. config_get family "$1" family ipv4
  360. if [ "$family" = "ipv4" ]; then
  361. IPT="$IPT4"
  362. fi
  363. if [ "$family" = "ipv6" ]; then
  364. [ $NO_IPV6 -ne 0 ] && return
  365. IPT="$IPT6"
  366. fi
  367. update="*mangle"
  368. mwan3_push_update -D mwan3_ifaces_in \
  369. -m mark --mark 0x0/$MMX_MASK \
  370. -j "mwan3_iface_in_$1" &> /dev/null
  371. mwan3_push_update -F "mwan3_iface_in_$1" &> /dev/null
  372. mwan3_push_update -X "mwan3_iface_in_$1" &> /dev/null
  373. mwan3_push_update COMMIT
  374. mwan3_push_update ""
  375. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-delete_iface_iptables-${1}.dump"
  376. error=$(echo "$update" | $IPTR 2>&1) || LOG error "delete_iface_iptables (${1}): $error"
  377. }
  378. mwan3_extra_tables_routes()
  379. {
  380. $IP route list table "$1"
  381. }
  382. mwan3_get_routes()
  383. {
  384. {
  385. $IP route list table main
  386. config_list_foreach "globals" "rt_table_lookup" mwan3_extra_tables_routes
  387. } | sed -ne "$MWAN3_ROUTE_LINE_EXP" | sort -u
  388. }
  389. mwan3_create_iface_route()
  390. {
  391. local tid route_line family IP id tbl
  392. config_get family "$1" family ipv4
  393. mwan3_get_iface_id id "$1"
  394. [ -n "$id" ] || return 0
  395. if [ "$family" = "ipv4" ]; then
  396. IP="$IP4"
  397. elif [ "$family" = "ipv6" ]; then
  398. IP="$IP6"
  399. fi
  400. tbl=$($IP route list table $id 2>/dev/null)$'\n'
  401. mwan3_update_dev_to_table
  402. mwan3_get_routes | while read -r route_line; do
  403. mwan3_route_line_dev "tid" "$route_line" "$family"
  404. { [ -z "${route_line##default*}" ] || [ -z "${route_line##fe80::/64*}" ]; } && [ "$tid" != "$id" ] && continue
  405. if [ -z "$tid" ] || [ "$tid" = "$id" ]; then
  406. # possible that routes are already in the table
  407. # if 'connected' was called after 'ifup'
  408. [ -n "$tbl" ] && [ -z "${tbl##*$route_line$'\n'*}" ] && continue
  409. $IP route add table $id $route_line ||
  410. LOG warn "failed to add $route_line to table $id"
  411. fi
  412. done
  413. }
  414. mwan3_delete_iface_route()
  415. {
  416. local id family
  417. config_get family "$1" family ipv4
  418. mwan3_get_iface_id id "$1"
  419. if [ -z "$id" ]; then
  420. LOG warn "delete_iface_route: could not find table id for interface $1"
  421. return 0
  422. fi
  423. if [ "$family" = "ipv4" ]; then
  424. $IP4 route flush table "$id"
  425. elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
  426. $IP6 route flush table "$id"
  427. fi
  428. }
  429. mwan3_create_iface_rules()
  430. {
  431. local id family IP
  432. config_get family "$1" family ipv4
  433. mwan3_get_iface_id id "$1"
  434. [ -n "$id" ] || return 0
  435. if [ "$family" = "ipv4" ]; then
  436. IP="$IP4"
  437. elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
  438. IP="$IP6"
  439. else
  440. return
  441. fi
  442. mwan3_delete_iface_rules "$1"
  443. $IP rule add pref $((id+1000)) iif "$2" lookup "$id"
  444. $IP rule add pref $((id+2000)) fwmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" lookup "$id"
  445. $IP rule add pref $((id+3000)) fwmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" unreachable
  446. }
  447. mwan3_delete_iface_rules()
  448. {
  449. local id family IP rule_id
  450. config_get family "$1" family ipv4
  451. mwan3_get_iface_id id "$1"
  452. [ -n "$id" ] || return 0
  453. if [ "$family" = "ipv4" ]; then
  454. IP="$IP4"
  455. elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
  456. IP="$IP6"
  457. else
  458. return
  459. fi
  460. for rule_id in $(ip rule list | awk '$1 % 1000 == '$id' && $1 > 1000 && $1 < 4000 {print substr($1,0,4)}'); do
  461. $IP rule del pref $rule_id
  462. done
  463. }
  464. mwan3_delete_iface_ipset_entries()
  465. {
  466. local id setname entry
  467. mwan3_get_iface_id id "$1"
  468. [ -n "$id" ] || return 0
  469. for setname in $(ipset -n list | grep ^mwan3_rule_); do
  470. for entry in $(ipset list "$setname" | grep "$(mwan3_id2mask id MMX_MASK | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
  471. $IPS del "$setname" $entry ||
  472. LOG notice "failed to delete $entry from $setname"
  473. done
  474. done
  475. }
  476. mwan3_set_policy()
  477. {
  478. local id iface family metric probability weight device is_lowest is_offline IPT IPTR total_weight current update error
  479. is_lowest=0
  480. config_get iface "$1" interface
  481. config_get metric "$1" metric 1
  482. config_get weight "$1" weight 1
  483. [ -n "$iface" ] || return 0
  484. network_get_device device "$iface"
  485. [ "$metric" -gt $DEFAULT_LOWEST_METRIC ] && LOG warn "Member interface $iface has >$DEFAULT_LOWEST_METRIC metric. Not appending to policy" && return 0
  486. mwan3_get_iface_id id "$iface"
  487. [ -n "$id" ] || return 0
  488. [ "$(mwan3_get_iface_hotplug_state "$iface")" = "online" ]
  489. is_offline=$?
  490. config_get family "$iface" family ipv4
  491. if [ "$family" = "ipv4" ]; then
  492. IPT="$IPT4"
  493. IPTR="$IPT4R"
  494. elif [ "$family" = "ipv6" ]; then
  495. IPT="$IPT6"
  496. IPTR="$IPT6R"
  497. fi
  498. current="$($IPT -S)"$'\n'
  499. update="*mangle"
  500. if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then
  501. if [ "$metric" -lt "$lowest_metric_v4" ]; then
  502. is_lowest=1
  503. total_weight_v4=$weight
  504. lowest_metric_v4=$metric
  505. elif [ "$metric" -eq "$lowest_metric_v4" ]; then
  506. total_weight_v4=$((total_weight_v4+weight))
  507. total_weight=$total_weight_v4
  508. else
  509. return
  510. fi
  511. elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ] && [ $is_offline -eq 0 ]; then
  512. if [ "$metric" -lt "$lowest_metric_v6" ]; then
  513. is_lowest=1
  514. total_weight_v6=$weight
  515. lowest_metric_v6=$metric
  516. elif [ "$metric" -eq "$lowest_metric_v6" ]; then
  517. total_weight_v6=$((total_weight_v6+weight))
  518. total_weight=$total_weight_v6
  519. else
  520. return
  521. fi
  522. fi
  523. if [ $is_lowest -eq 1 ]; then
  524. mwan3_push_update -F "mwan3_policy_$policy"
  525. mwan3_push_update -A "mwan3_policy_$policy" \
  526. -m mark --mark 0x0/$MMX_MASK \
  527. -m comment --comment \"$iface $weight $weight\" \
  528. -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
  529. elif [ $is_offline -eq 0 ]; then
  530. probability=$((weight*1000/total_weight))
  531. if [ "$probability" -lt 10 ]; then
  532. probability="0.00$probability"
  533. elif [ $probability -lt 100 ]; then
  534. probability="0.0$probability"
  535. elif [ $probability -lt 1000 ]; then
  536. probability="0.$probability"
  537. else
  538. probability="1"
  539. fi
  540. mwan3_push_update -I "mwan3_policy_$policy" \
  541. -m mark --mark 0x0/$MMX_MASK \
  542. -m statistic \
  543. --mode random \
  544. --probability "$probability" \
  545. -m comment --comment \"$iface $weight $total_weight\" \
  546. -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
  547. elif [ -n "$device" ]; then
  548. echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" ||
  549. mwan3_push_update -I "mwan3_policy_$policy" \
  550. -o "$device" \
  551. -m mark --mark 0x0/$MMX_MASK \
  552. -m comment --comment \"out $iface $device\" \
  553. -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
  554. fi
  555. mwan3_push_update COMMIT
  556. mwan3_push_update ""
  557. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_policy-${1}.dump"
  558. error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_policy ($1): $error"
  559. }
  560. mwan3_create_policies_iptables()
  561. {
  562. local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT current update error
  563. policy="$1"
  564. config_get last_resort "$1" last_resort unreachable
  565. if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
  566. LOG warn "Policy $1 exceeds max of 15 chars. Not setting policy" && return 0
  567. fi
  568. for IPT in "$IPT4" "$IPT6"; do
  569. [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
  570. current="$($IPT -S)"$'\n'
  571. update="*mangle"
  572. if [ -n "${current##*-N mwan3_policy_$1$'\n'*}" ]; then
  573. mwan3_push_update -N "mwan3_policy_$1"
  574. fi
  575. mwan3_push_update -F "mwan3_policy_$1"
  576. case "$last_resort" in
  577. blackhole)
  578. mwan3_push_update -A "mwan3_policy_$1" \
  579. -m mark --mark 0x0/$MMX_MASK \
  580. -m comment --comment "blackhole" \
  581. -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK
  582. ;;
  583. default)
  584. mwan3_push_update -A "mwan3_policy_$1" \
  585. -m mark --mark 0x0/$MMX_MASK \
  586. -m comment --comment "default" \
  587. -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
  588. ;;
  589. *)
  590. mwan3_push_update -A "mwan3_policy_$1" \
  591. -m mark --mark 0x0/$MMX_MASK \
  592. -m comment --comment "unreachable" \
  593. -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK
  594. ;;
  595. esac
  596. mwan3_push_update COMMIT
  597. mwan3_push_update ""
  598. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-create_policies_iptables-${1}.dump"
  599. if [ "$IPT" = "$IPT4" ]; then
  600. error=$(echo "$update" | $IPT4R 2>&1) || LOG error "create_policies_iptables ($1): $error"
  601. else
  602. error=$(echo "$update" | $IPT6R 2>&1) || LOG error "create_policies_iptables ($1): $error"
  603. fi
  604. done
  605. lowest_metric_v4=$DEFAULT_LOWEST_METRIC
  606. total_weight_v4=0
  607. lowest_metric_v6=$DEFAULT_LOWEST_METRIC
  608. total_weight_v6=0
  609. config_list_foreach "$1" use_member mwan3_set_policy
  610. }
  611. mwan3_set_policies_iptables()
  612. {
  613. config_foreach mwan3_create_policies_iptables policy
  614. }
  615. mwan3_set_sticky_iptables()
  616. {
  617. local rule="${1}"
  618. local interface="${2}"
  619. local ipv="${3}"
  620. local policy="${4}"
  621. local id iface
  622. for iface in $(echo "$current" | grep "^-A $policy" | cut -s -d'"' -f2 | awk '{print $1}'); do
  623. if [ "$iface" = "$interface" ]; then
  624. mwan3_get_iface_id id "$iface"
  625. [ -n "$id" ] || return 0
  626. if [ -z "${current##*-N mwan3_iface_in_${iface}$'\n'*}" ]; then
  627. mwan3_push_update -I "mwan3_rule_$rule" \
  628. -m mark --mark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" \
  629. -m set ! --match-set "mwan3_rule_${ipv}_${rule}" src,src \
  630. -j MARK --set-xmark "0x0/$MMX_MASK"
  631. mwan3_push_update -I "mwan3_rule_$rule" \
  632. -m mark --mark "0/$MMX_MASK" \
  633. -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK"
  634. fi
  635. fi
  636. done
  637. }
  638. mwan3_set_sticky_ipset()
  639. {
  640. local rule="$1"
  641. local mmx="$2"
  642. local timeout="$3"
  643. local error
  644. local update=""
  645. mwan3_push_update -! create "mwan3_rule_ipv4_$rule" \
  646. hash:ip,mark markmask "$mmx" \
  647. timeout "$timeout"
  648. [ $NO_IPV6 -eq 0 ] &&
  649. mwan3_push_update -! create "mwan3_rule_ipv6_$rule" \
  650. hash:ip,mark markmask "$mmx" \
  651. timeout "$timeout" family inet6
  652. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/ipset-set_sticky_ipset-${rule}.dump"
  653. error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_sticky_ipset (${rule}): $error"
  654. }
  655. mwan3_set_user_iptables_rule()
  656. {
  657. local ipset family proto policy src_ip src_port src_iface src_dev
  658. local sticky dest_ip dest_port use_policy timeout policy
  659. local global_logging rule_logging loglevel rule_policy rule ipv
  660. rule="$1"
  661. ipv="$2"
  662. rule_policy=0
  663. config_get sticky "$1" sticky 0
  664. config_get timeout "$1" timeout 600
  665. config_get ipset "$1" ipset
  666. config_get proto "$1" proto all
  667. config_get src_ip "$1" src_ip
  668. config_get src_iface "$1" src_iface
  669. config_get src_port "$1" src_port
  670. config_get dest_ip "$1" dest_ip
  671. config_get dest_port "$1" dest_port
  672. config_get use_policy "$1" use_policy
  673. config_get family "$1" family any
  674. config_get rule_logging "$1" logging 0
  675. config_get global_logging globals logging 0
  676. config_get loglevel globals loglevel notice
  677. [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
  678. [ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
  679. [ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
  680. for ipaddr in "$src_ip" "$dest_ip"; do
  681. if [ -n "$ipaddr" ] && { { [ "$ipv" = "ipv4" ] && echo "$ipaddr" | grep -qE "$IPv6_REGEX"; } ||
  682. { [ "$ipv" = "ipv6" ] && echo "$ipaddr" | grep -qE $IPv4_REGEX; } }; then
  683. LOG warn "invalid $ipv address $ipaddr specified for rule $rule"
  684. return
  685. fi
  686. done
  687. if [ -n "$src_iface" ]; then
  688. network_get_device src_dev "$src_iface"
  689. if [ -z "$src_dev" ]; then
  690. LOG notice "could not find device corresponding to src_iface $src_iface for rule $1"
  691. return
  692. fi
  693. fi
  694. [ -z "$dest_ip" ] && unset dest_ip
  695. [ -z "$src_ip" ] && unset src_ip
  696. [ -z "$ipset" ] && unset ipset
  697. [ -z "$src_port" ] && unset src_port
  698. [ -z "$dest_port" ] && unset dest_port
  699. if [ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ]; then
  700. [ -n "$src_port" ] && {
  701. LOG warn "src_port set to '$src_port' but proto set to '$proto' not tcp or udp. src_port will be ignored"
  702. }
  703. [ -n "$dest_port" ] && {
  704. LOG warn "dest_port set to '$dest_port' but proto set to '$proto' not tcp or udp. dest_port will be ignored"
  705. }
  706. unset src_port
  707. unset dest_port
  708. fi
  709. if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
  710. LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0
  711. fi
  712. if [ -n "$ipset" ]; then
  713. ipset="-m set --match-set $ipset dst"
  714. fi
  715. if [ -z "$use_policy" ]; then
  716. return
  717. fi
  718. if [ "$use_policy" = "default" ]; then
  719. policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK"
  720. elif [ "$use_policy" = "unreachable" ]; then
  721. policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK"
  722. elif [ "$use_policy" = "blackhole" ]; then
  723. policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK"
  724. else
  725. rule_policy=1
  726. policy="mwan3_policy_$use_policy"
  727. if [ "$sticky" -eq 1 ]; then
  728. mwan3_set_sticky_ipset "$rule" "$MMX_MASK" "$timeout"
  729. fi
  730. fi
  731. if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy$'\n'*}" ]; then
  732. mwan3_push_update -N "$policy"
  733. fi
  734. if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then
  735. if [ -n "${current##*-N mwan3_rule_$1$'\n'*}" ]; then
  736. mwan3_push_update -N "mwan3_rule_$1"
  737. fi
  738. mwan3_push_update -F "mwan3_rule_$1"
  739. config_foreach mwan3_set_sticky_iptables interface $ipv "$policy"
  740. mwan3_push_update -A "mwan3_rule_$1" \
  741. -m mark --mark 0/$MMX_MASK \
  742. -j "$policy"
  743. mwan3_push_update -A "mwan3_rule_$1" \
  744. -m mark ! --mark 0xfc00/0xfc00 \
  745. -j SET --del-set "mwan3_rule_${ipv}_${rule}" src,src
  746. mwan3_push_update -A "mwan3_rule_$1" \
  747. -m mark ! --mark 0xfc00/0xfc00 \
  748. -j SET --add-set "mwan3_rule_${ipv}_${rule}" src,src
  749. policy="mwan3_rule_$1"
  750. fi
  751. if [ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ]; then
  752. mwan3_push_update -A mwan3_rules \
  753. -p "$proto" \
  754. ${src_ip:+-s} $src_ip \
  755. ${src_dev:+-i} $src_dev \
  756. ${dest_ip:+-d} $dest_ip \
  757. $ipset \
  758. ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
  759. ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
  760. -m mark --mark 0/$MMX_MASK \
  761. -m comment --comment "$1" \
  762. -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)"
  763. fi
  764. mwan3_push_update -A mwan3_rules \
  765. -p "$proto" \
  766. ${src_ip:+-s} $src_ip \
  767. ${src_dev:+-i} $src_dev \
  768. ${dest_ip:+-d} $dest_ip \
  769. $ipset \
  770. ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \
  771. ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
  772. -m mark --mark 0/$MMX_MASK \
  773. -j $policy
  774. }
  775. mwan3_set_user_iface_rules()
  776. {
  777. local current iface update family error device is_src_iface
  778. iface=$1
  779. device=$2
  780. if [ -z "$device" ]; then
  781. LOG notice "set_user_iface_rules: could not find device corresponding to iface $iface"
  782. return
  783. fi
  784. config_get family "$iface" family ipv4
  785. if [ "$family" = "ipv4" ]; then
  786. IPT="$IPT4"
  787. IPTR="$IPT4R"
  788. elif [ "$family" = "ipv6" ]; then
  789. IPT="$IPT6"
  790. IPTR="$IPT6R"
  791. fi
  792. $IPT -S | grep -q "^-A mwan3_rules.*-i $device" && return
  793. is_src_iface=0
  794. iface_rule()
  795. {
  796. local src_iface
  797. config_get src_iface "$1" src_iface
  798. [ "$src_iface" = "$iface" ] && is_src_iface=1
  799. }
  800. config_foreach iface_rule rule
  801. [ $is_src_iface -eq 1 ] && mwan3_set_user_rules
  802. }
  803. mwan3_set_user_rules()
  804. {
  805. local IPT IPTR ipv
  806. local current update error
  807. for ipv in ipv4 ipv6; do
  808. if [ "$ipv" = "ipv4" ]; then
  809. IPT="$IPT4"
  810. IPTR="$IPT4R"
  811. elif [ "$ipv" = "ipv6" ]; then
  812. IPT="$IPT6"
  813. IPTR="$IPT6R"
  814. fi
  815. [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
  816. update="*mangle"
  817. current="$($IPT -S)"$'\n'
  818. if [ -n "${current##*-N mwan3_rules*}" ]; then
  819. mwan3_push_update -N "mwan3_rules"
  820. fi
  821. mwan3_push_update -F mwan3_rules
  822. config_foreach mwan3_set_user_iptables_rule rule "$ipv"
  823. mwan3_push_update COMMIT
  824. mwan3_push_update ""
  825. echo "$update" > "${MWAN3_STATUS_IPTABLES_LOG_DIR}/iptables-set_user_rules-${ipv}.dump"
  826. error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_user_rules (${ipv}): $error"
  827. done
  828. }
  829. mwan3_interface_hotplug_shutdown()
  830. {
  831. local interface status device ifdown
  832. interface="$1"
  833. ifdown="$2"
  834. [ -f $MWAN3TRACK_STATUS_DIR/$interface/STATUS ] && {
  835. status=$(cat $MWAN3TRACK_STATUS_DIR/$interface/STATUS)
  836. }
  837. [ "$status" != "online" ] && [ "$ifdown" != 1 ] && return
  838. if [ "$ifdown" = 1 ]; then
  839. env -i ACTION=ifdown \
  840. INTERFACE=$interface \
  841. DEVICE=$device \
  842. sh /etc/hotplug.d/iface/15-mwan3
  843. else
  844. [ "$status" = "online" ] && {
  845. env -i MWAN3_SHUTDOWN="1" \
  846. ACTION="disconnected" \
  847. INTERFACE="$interface" \
  848. DEVICE="$device" /sbin/hotplug-call iface
  849. }
  850. fi
  851. }
  852. mwan3_interface_shutdown()
  853. {
  854. mwan3_interface_hotplug_shutdown $1
  855. mwan3_track_clean $1
  856. }
  857. mwan3_ifup()
  858. {
  859. local interface=$1
  860. local caller=$2
  861. local up l3_device status true_iface
  862. if [ "${caller}" = "cmd" ]; then
  863. # It is not necessary to obtain a lock here, because it is obtained in the hotplug
  864. # script, but we still want to do the check to print a useful error message
  865. /etc/init.d/mwan3 running || {
  866. echo 'The service mwan3 is global disabled.'
  867. echo 'Please execute "/etc/init.d/mwan3 start" first.'
  868. exit 1
  869. }
  870. config_load mwan3
  871. fi
  872. mwan3_get_true_iface true_iface $interface
  873. status=$(ubus -S call network.interface.$true_iface status)
  874. [ -n "$status" ] && {
  875. json_load "$status"
  876. json_get_vars up l3_device
  877. }
  878. hotplug_startup()
  879. {
  880. env -i MWAN3_STARTUP=$caller ACTION=ifup \
  881. INTERFACE=$interface DEVICE=$l3_device \
  882. sh /etc/hotplug.d/iface/15-mwan3
  883. }
  884. if [ "$up" != "1" ] || [ -z "$l3_device" ]; then
  885. return
  886. fi
  887. if [ "${caller}" = "init" ]; then
  888. hotplug_startup &
  889. hotplug_pids="$hotplug_pids $!"
  890. else
  891. hotplug_startup
  892. fi
  893. }
  894. mwan3_set_iface_hotplug_state() {
  895. local iface=$1
  896. local state=$2
  897. echo "$state" > "$MWAN3_STATUS_DIR/iface_state/$iface"
  898. }
  899. mwan3_get_iface_hotplug_state() {
  900. local iface=$1
  901. cat "$MWAN3_STATUS_DIR/iface_state/$iface" 2>/dev/null || echo "offline"
  902. }
  903. mwan3_report_iface_status()
  904. {
  905. local device result tracking IP IPT error
  906. mwan3_get_iface_id id "$1"
  907. network_get_device device "$1"
  908. config_get enabled "$1" enabled 0
  909. config_get family "$1" family ipv4
  910. if [ "$family" = "ipv4" ]; then
  911. IP="$IP4"
  912. IPT="$IPT4"
  913. fi
  914. if [ "$family" = "ipv6" ]; then
  915. IP="$IP6"
  916. IPT="$IPT6"
  917. fi
  918. if [ -z "$id" ] || [ -z "$device" ]; then
  919. result="offline"
  920. else
  921. error=0
  922. [ -n "$($IP rule | awk '$1 == "'$((id+1000)):'"')" ] ||
  923. error=$((error+1))
  924. [ -n "$($IP rule | awk '$1 == "'$((id+2000)):'"')" ] ||
  925. error=$((error+2))
  926. [ -n "$($IP rule | awk '$1 == "'$((id+3000)):'"')" ] ||
  927. error=$((error+4))
  928. [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] ||
  929. error=$((error+8))
  930. [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ] ||
  931. error=$((error+16))
  932. fi
  933. if [ "$result" = "offline" ]; then
  934. :
  935. elif [ $error -eq 0 ]; then
  936. online=$(get_online_time "$1")
  937. network_get_uptime uptime "$1"
  938. online="$(printf '%02dh:%02dm:%02ds\n' $((online/3600)) $((online%3600/60)) $((online%60)))"
  939. uptime="$(printf '%02dh:%02dm:%02ds\n' $((uptime/3600)) $((uptime%3600/60)) $((uptime%60)))"
  940. result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime"
  941. elif [ $error -gt 0 ] && [ $error -ne 31 ]; then
  942. result="error (${error})"
  943. elif [ "$enabled" = "1" ]; then
  944. result="offline"
  945. else
  946. result="disabled"
  947. fi
  948. tracking="$(mwan3_get_mwan3track_status $1)"
  949. echo " interface $1 is $result and tracking is $tracking"
  950. }
  951. mwan3_report_policies()
  952. {
  953. local ipt="$1"
  954. local policy="$2"
  955. local percent total_weight weight iface
  956. total_weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
  957. if [ -n "${total_weight##*[!0-9]*}" ]; then
  958. for iface in $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do
  959. weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
  960. percent=$((weight*100/total_weight))
  961. echo " $iface ($percent%)"
  962. done
  963. else
  964. echo " $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')"
  965. fi
  966. }
  967. mwan3_report_policies_v4()
  968. {
  969. local policy
  970. for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
  971. echo "$policy:" | sed 's/mwan3_policy_//'
  972. mwan3_report_policies "$IPT4" "$policy"
  973. done
  974. }
  975. mwan3_report_policies_v6()
  976. {
  977. local policy
  978. for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
  979. echo "$policy:" | sed 's/mwan3_policy_//'
  980. mwan3_report_policies "$IPT6" "$policy"
  981. done
  982. }
  983. mwan3_report_connected_v4()
  984. {
  985. if [ -n "$($IPT4 -S mwan3_connected_ipv4 2> /dev/null)" ]; then
  986. $IPS -o save list mwan3_connected_ipv4 | grep add | cut -d " " -f 3
  987. fi
  988. }
  989. mwan3_report_connected_v6()
  990. {
  991. if [ -n "$($IPT6 -S mwan3_connected_ipv6 2> /dev/null)" ]; then
  992. $IPS -o save list mwan3_connected_ipv6 | grep add | cut -d " " -f 3
  993. fi
  994. }
  995. mwan3_report_rules_v4()
  996. {
  997. if [ -n "$($IPT4 -S mwan3_rules 2> /dev/null)" ]; then
  998. $IPT4 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
  999. fi
  1000. }
  1001. mwan3_report_rules_v6()
  1002. {
  1003. if [ -n "$($IPT6 -S mwan3_rules 2> /dev/null)" ]; then
  1004. $IPT6 -L mwan3_rules -n -v 2> /dev/null | tail -n+3 | sed 's/mark.*//' | sed 's/mwan3_policy_/- /' | sed 's/mwan3_rule_/S /'
  1005. fi
  1006. }
  1007. mwan3_flush_conntrack()
  1008. {
  1009. local interface="$1"
  1010. local action="$2"
  1011. handle_flush() {
  1012. local flush_conntrack="$1"
  1013. local action="$2"
  1014. if [ "$action" = "$flush_conntrack" ]; then
  1015. echo f > ${CONNTRACK_FILE}
  1016. LOG info "Connection tracking flushed for interface '$interface' on action '$action'"
  1017. fi
  1018. }
  1019. if [ -e "$CONNTRACK_FILE" ]; then
  1020. config_list_foreach "$interface" flush_conntrack handle_flush "$action"
  1021. fi
  1022. }
  1023. mwan3_track_clean()
  1024. {
  1025. rm -rf "${MWAN3_STATUS_DIR:?}/${1}" &> /dev/null
  1026. rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR"
  1027. }