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.

427 lines
12 KiB

  1. #!/bin/sh
  2. mwan3_get_iface_id()
  3. {
  4. let iface_count++
  5. [ "$1" == "$INTERFACE" ] && iface_id=$iface_count
  6. }
  7. mwan3_set_general_iptables()
  8. {
  9. if ! $IPT -S mwan3_ifaces &> /dev/null; then
  10. $IPT -N mwan3_ifaces
  11. fi
  12. if ! $IPT -S mwan3_connected &> /dev/null; then
  13. $IPT -N mwan3_connected
  14. $IPS create mwan3_connected hash:net
  15. $IPT -A mwan3_connected -m set --match-set mwan3_connected dst -j MARK --set-xmark 0xff00/0xff00
  16. fi
  17. if ! $IPT -S mwan3_track &> /dev/null; then
  18. $IPT -N mwan3_track
  19. fi
  20. if ! $IPT -S mwan3_rules &> /dev/null; then
  21. $IPT -N mwan3_rules
  22. fi
  23. if ! $IPT -S mwan3_hook &> /dev/null; then
  24. $IPT -N mwan3_hook
  25. $IPT -A mwan3_hook -j CONNMARK --restore-mark --nfmask 0xff00 --ctmask 0xff00
  26. $IPT -A mwan3_hook -m mark --mark 0x0/0xff00 -j mwan3_ifaces
  27. $IPT -A mwan3_hook -m mark --mark 0x0/0xff00 -j mwan3_connected
  28. $IPT -A mwan3_hook -m mark --mark 0x0/0xff00 -j mwan3_track
  29. $IPT -A mwan3_hook -m mark --mark 0x0/0xff00 -j mwan3_rules
  30. $IPT -A mwan3_hook -j CONNMARK --save-mark --nfmask 0xff00 --ctmask 0xff00
  31. $IPT -A mwan3_hook -m mark ! --mark 0xff00/0xff00 -j mwan3_connected
  32. fi
  33. if ! $IPT -S PREROUTING | grep mwan3_hook &> /dev/null; then
  34. $IPT -A PREROUTING -j mwan3_hook
  35. fi
  36. if ! $IPT -S OUTPUT | grep mwan3_hook &> /dev/null; then
  37. $IPT -A OUTPUT -j mwan3_hook
  38. fi
  39. $IPT -F mwan3_rules
  40. }
  41. mwan3_set_general_rules()
  42. {
  43. if [ -z "$($IP rule list | awk '$1 == "2253:"')" ]; then
  44. $IP rule add pref 2253 fwmark 0xfd00/0xff00 blackhole
  45. fi
  46. if [ -z "$($IP rule list | awk '$1 == "2254:"')" ]; then
  47. $IP rule add pref 2254 fwmark 0xfe00/0xff00 unreachable
  48. fi
  49. }
  50. mwan3_set_connected_iptables()
  51. {
  52. local connected_network
  53. if $IPT -S mwan3_connected &> /dev/null; then
  54. $IPS create mwan3_connected_temp hash:net
  55. for connected_network in $($IP route | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
  56. $IPS -! add mwan3_connected_temp $connected_network
  57. done
  58. for connected_network in $($IP route list table 0 | awk '{print $2}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
  59. $IPS -! add mwan3_connected_temp $connected_network
  60. done
  61. $IPS add mwan3_connected_temp 224.0.0.0/3
  62. $IPS swap mwan3_connected_temp mwan3_connected
  63. $IPS destroy mwan3_connected_temp
  64. fi
  65. }
  66. mwan3_set_iface_iptables()
  67. {
  68. if ! $IPT -S mwan3_iface_$INTERFACE &> /dev/null; then
  69. $IPT -N mwan3_iface_$INTERFACE
  70. fi
  71. $IPT -F mwan3_iface_$INTERFACE
  72. $IPT -D mwan3_ifaces -m mark --mark 0x0/0xff00 -j mwan3_iface_$INTERFACE &> /dev/null
  73. if [ $ACTION == "ifup" ]; then
  74. $IPT -I mwan3_iface_$INTERFACE -i $DEVICE -m set --match-set mwan3_connected src -m mark --mark 0x0/0xff00 -m comment --comment "default" -j MARK --set-xmark 0xff00/0xff00
  75. $IPT -A mwan3_iface_$INTERFACE -i $DEVICE -m mark --mark 0x0/0xff00 -m comment --comment "$INTERFACE" -j MARK --set-xmark $(($iface_id*256))/0xff00
  76. $IPT -A mwan3_ifaces -m mark --mark 0x0/0xff00 -j mwan3_iface_$INTERFACE
  77. fi
  78. if [ $ACTION == "ifdown" ]; then
  79. $IPT -X mwan3_iface_$INTERFACE
  80. fi
  81. }
  82. mwan3_set_iface_route()
  83. {
  84. $IP route flush table $iface_id
  85. [ $ACTION == "ifup" ] && $IP route add table $iface_id default $route_args
  86. }
  87. mwan3_set_iface_rules()
  88. {
  89. while [ -n "$($IP rule list | awk '$1 == "'$(($iface_id+1000)):'"')" ]; do
  90. $IP rule del pref $(($iface_id+1000))
  91. done
  92. while [ -n "$($IP rule list | awk '$1 == "'$(($iface_id+2000)):'"')" ]; do
  93. $IP rule del pref $(($iface_id+2000))
  94. done
  95. [ $ACTION == "ifup" ] && $IP rule add pref $(($iface_id+1000)) iif $DEVICE lookup main
  96. [ $ACTION == "ifup" ] && $IP rule add pref $(($iface_id+2000)) fwmark $(($iface_id*256))/0xff00 lookup $iface_id
  97. }
  98. mwan3_set_iface_ipset()
  99. {
  100. local setname entry
  101. for setname in $(ipset -n list | grep ^mwan3_sticky_); do
  102. for entry in $(ipset list $setname | grep "$(echo $(($iface_id*256)) | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
  103. $IPS del $setname $entry
  104. done
  105. done
  106. }
  107. mwan3_track()
  108. {
  109. local track_ip track_ips reliability count timeout interval down up
  110. mwan3_list_track_ips()
  111. {
  112. track_ips="$1 $track_ips"
  113. }
  114. config_list_foreach $INTERFACE track_ip mwan3_list_track_ips
  115. if [ -e /var/run/mwan3track-$INTERFACE.pid ] ; then
  116. kill $(cat /var/run/mwan3track-$INTERFACE.pid) &> /dev/null
  117. rm /var/run/mwan3track-$INTERFACE.pid &> /dev/null
  118. fi
  119. if [ -n "$track_ips" ]; then
  120. config_get reliability $INTERFACE reliability 1
  121. config_get count $INTERFACE count 1
  122. config_get timeout $INTERFACE timeout 4
  123. config_get interval $INTERFACE interval 10
  124. config_get down $INTERFACE down 5
  125. config_get up $INTERFACE up 5
  126. $IPS -! create mwan3_track_$INTERFACE hash:ip
  127. $IPS create mwan3_track_temp_$INTERFACE hash:ip
  128. for track_ip in $track_ips; do
  129. $IPS -! add mwan3_track_temp_$INTERFACE $track_ip
  130. done
  131. $IPS swap mwan3_track_temp_$INTERFACE mwan3_track_$INTERFACE
  132. $IPS destroy mwan3_track_temp_$INTERFACE
  133. $IPT -D mwan3_track -p icmp -m set --match-set mwan3_track_$INTERFACE dst -m icmp --icmp-type 8 -m length --length 32 -j MARK --set-xmark 0xff00/0xff00 &> /dev/null
  134. $IPT -A mwan3_track -p icmp -m set --match-set mwan3_track_$INTERFACE dst -m icmp --icmp-type 8 -m length --length 32 -j MARK --set-xmark 0xff00/0xff00
  135. [ -x /usr/sbin/mwan3track ] && /usr/sbin/mwan3track $INTERFACE $DEVICE $reliability $count $timeout $interval $down $up $track_ips &
  136. else
  137. $IPT -D mwan3_track -p icmp -m set --match-set mwan3_track_$INTERFACE dst -m icmp --icmp-type 8 -m length --length 32 -j MARK --set-xmark 0xff00/0xff00 &> /dev/null
  138. $IPS destroy mwan3_track_$INTERFACE
  139. fi
  140. }
  141. mwan3_set_policy()
  142. {
  143. local iface_count iface_id INTERFACE metric probability weight
  144. config_get INTERFACE $1 interface
  145. config_get metric $1 metric 1
  146. config_get weight $1 weight 1
  147. [ -n "$INTERFACE" ] || return 0
  148. config_foreach mwan3_get_iface_id interface
  149. [ -n "$iface_id" ] || return 0
  150. if $IPT -S mwan3_iface_$INTERFACE &> /dev/null; then
  151. if [ "$metric" -lt "$lowest_metric" ]; then
  152. total_weight=$weight
  153. $IPT -F mwan3_policy_$policy
  154. $IPT -A mwan3_policy_$policy -m mark --mark 0x0/0xff00 -m comment --comment "$INTERFACE $weight $weight" -j MARK --set-xmark $(($iface_id*256))/0xff00
  155. lowest_metric=$metric
  156. elif [ "$metric" -eq "$lowest_metric" ]; then
  157. total_weight=$(($total_weight+$weight))
  158. probability=$(($weight*1000/$total_weight))
  159. if [ "$probability" -lt 10 ]; then
  160. probability="0.00$probability"
  161. elif [ $probability -lt 100 ]; then
  162. probability="0.0$probability"
  163. elif [ $probability -lt 1000 ]; then
  164. probability="0.$probability"
  165. else
  166. probability="1"
  167. fi
  168. probability="-m statistic --mode random --probability $probability"
  169. $IPT -I mwan3_policy_$policy -m mark --mark 0x0/0xff00 $probability -m comment --comment "$INTERFACE $weight $total_weight" -j MARK --set-xmark $(($iface_id*256))/0xff00
  170. fi
  171. fi
  172. }
  173. mwan3_set_policies_iptables()
  174. {
  175. local last_resort lowest_metric policy total_weight
  176. policy=$1
  177. config_get last_resort $1 last_resort unreachable
  178. if [ "$policy" != $(echo "$policy" | cut -c1-15) ]; then
  179. $LOG warn "Policy $policy exceeds max of 15 chars. Not setting policy" && return 0
  180. fi
  181. if ! $IPT -S mwan3_policy_$policy &> /dev/null; then
  182. $IPT -N mwan3_policy_$policy
  183. fi
  184. $IPT -F mwan3_policy_$policy
  185. case "$last_resort" in
  186. blackhole)
  187. $IPT -A mwan3_policy_$policy -m mark --mark 0x0/0xff00 -m comment --comment "blackhole" -j MARK --set-xmark 0xfd00/0xff00
  188. ;;
  189. default)
  190. $IPT -A mwan3_policy_$policy -m mark --mark 0x0/0xff00 -m comment --comment "default" -j MARK --set-xmark 0xff00/0xff00
  191. ;;
  192. *)
  193. $IPT -A mwan3_policy_$policy -m mark --mark 0x0/0xff00 -m comment --comment "unreachable" -j MARK --set-xmark 0xfe00/0xff00
  194. ;;
  195. esac
  196. lowest_metric=256
  197. total_weight=0
  198. config_list_foreach $policy use_member mwan3_set_policy
  199. }
  200. mwan3_set_sticky_iptables()
  201. {
  202. local INTERFACE iface_count iface_id
  203. INTERFACE="$1"
  204. config_foreach mwan3_get_iface_id interface
  205. unset iface_count
  206. $IPS -! create mwan3_sticky_$rule hash:ip,mark markmask 0xff00 timeout $timeout
  207. if [ -n "$iface_id" ]; then
  208. if [ -n "$($IPT -S mwan3_iface_$1 2> /dev/null)" ]; then
  209. $IPT -I mwan3_rule_$rule -m set ! --match-set mwan3_sticky_$rule src,src -j MARK --set-xmark 0x0/0xff00
  210. $IPT -I mwan3_rule_$rule -m mark --mark 0/0xff00 -j MARK --set-xmark $(($iface_id*256))/0xff00
  211. fi
  212. fi
  213. unset iface_id
  214. }
  215. mwan3_set_user_rules_iptables()
  216. {
  217. local ipset proto src_ip src_port sticky dest_ip dest_port use_policy rule timeout
  218. config_get sticky $1 sticky 0
  219. config_get timeout $1 timeout 600
  220. config_get ipset $1 ipset
  221. config_get proto $1 proto all
  222. config_get src_ip $1 src_ip 0.0.0.0/0
  223. config_get src_port $1 src_port 0:65535
  224. config_get dest_ip $1 dest_ip 0.0.0.0/0
  225. config_get dest_port $1 dest_port 0:65535
  226. config_get use_policy $1 use_policy
  227. rule="$1"
  228. if [ "$rule" != $(echo "$rule" | cut -c1-15) ]; then
  229. $LOG warn "Rule $rule exceeds max of 15 chars. Not setting rule" && return 0
  230. fi
  231. if [ -n "$ipset" ]; then
  232. if [ -z "$($IPS -n list $ipset)" ]; then
  233. $IPS create $ipset hash:ip timeout 3600
  234. fi
  235. ipset="-m set --match-set $ipset dst"
  236. fi
  237. if [ -n "$use_policy" ]; then
  238. if [ "$use_policy" == "default" ]; then
  239. use_policy="MARK --set-xmark 0xff00/0xff00"
  240. elif [ "$use_policy" == "unreachable" ]; then
  241. use_policy="MARK --set-xmark 0xfe00/0xff00"
  242. elif [ "$use_policy" == "blackhole" ]; then
  243. use_policy="MARK --set-xmark 0xfd00/0xff00"
  244. else
  245. if [ "$sticky" -eq 1 ]; then
  246. if ! $IPT -S mwan3_rule_$rule &> /dev/null; then
  247. $IPT -N mwan3_rule_$rule
  248. fi
  249. $IPT -F mwan3_rule_$rule
  250. config_foreach mwan3_set_sticky_iptables interface
  251. $IPT -A mwan3_rule_$rule -m mark --mark 0/0xff00 -j mwan3_policy_$use_policy
  252. $IPT -A mwan3_rule_$rule -m mark ! --mark 0xfc00/0xfc00 -j SET --del-set mwan3_sticky_$rule src,src
  253. $IPT -A mwan3_rule_$rule -m mark ! --mark 0xfc00/0xfc00 -j SET --add-set mwan3_sticky_$rule src,src
  254. use_policy="mwan3_rule_$rule"
  255. else
  256. use_policy="mwan3_policy_$use_policy"
  257. fi
  258. fi
  259. case $proto in
  260. tcp|udp)
  261. $IPT -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m multiport --sports $src_port -m multiport --dports $dest_port -m mark --mark 0/0xff00 -m comment --comment "$1" -j $use_policy &> /dev/null
  262. ;;
  263. *)
  264. $IPT -A mwan3_rules -p $proto -s $src_ip -d $dest_ip $ipset -m mark --mark 0/0xff00 -m comment --comment "$1" -j $use_policy &> /dev/null
  265. ;;
  266. esac
  267. fi
  268. }
  269. mwan3_ifupdown()
  270. {
  271. local counter enabled iface_count iface_id route_args wan_metric
  272. config_load mwan3
  273. config_foreach mwan3_get_iface_id interface
  274. [ -n "$iface_id" ] || return 0
  275. [ "$iface_count" -le 250 ] || return 0
  276. unset iface_count
  277. config_get enabled $INTERFACE enabled 0
  278. counter=0
  279. if [ $ACTION == "ifup" ]; then
  280. [ "$enabled" -eq 1 ] || return 0
  281. while [ -z "$($IP route list dev $DEVICE default | head -1)" -a "$counter" -lt 10 ]; do
  282. sleep 1
  283. let counter++
  284. if [ "$counter" -ge 10 ]; then
  285. $LOG warn "Could not find gateway for interface $INTERFACE ($DEVICE)" && return 0
  286. fi
  287. done
  288. route_args=$($IP route list dev $DEVICE default | head -1 | sed '/.*via \([^ ]*\) .*$/!d;s//via \1/;q' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}')
  289. route_args="$route_args dev $DEVICE"
  290. fi
  291. while [ "$(pgrep -f -o hotplug-call)" -ne $$ -a "$counter" -lt 60 ]; do
  292. sleep 1
  293. let counter++
  294. if [ "$counter" -ge 60 ]; then
  295. $LOG warn "Timeout waiting for older hotplug processes to finish. $ACTION interface $INTERFACE (${DEVICE:-unknown}) aborted" && return 0
  296. fi
  297. done
  298. $LOG notice "$ACTION interface $INTERFACE (${DEVICE:-unknown})"
  299. mwan3_set_general_iptables
  300. mwan3_set_general_rules
  301. mwan3_set_iface_iptables
  302. mwan3_set_iface_route
  303. mwan3_set_iface_rules
  304. [ $ACTION == "ifdown" ] && mwan3_set_iface_ipset
  305. [ $ACTION == "ifup" ] && mwan3_track
  306. config_foreach mwan3_set_policies_iptables policy
  307. config_foreach mwan3_set_user_rules_iptables rule
  308. }
  309. [ -n "$ACTION" ] || exit 0
  310. [ -n "$INTERFACE" ] || exit 0
  311. if [ $ACTION == "ifup" ]; then
  312. [ -n "$DEVICE" ] || exit 0
  313. fi
  314. [ -x /usr/sbin/ip ] || exit 1
  315. [ -x /usr/sbin/ipset ] || exit 1
  316. [ -x /usr/sbin/iptables ] || exit 1
  317. [ -x /usr/bin/logger ] || exit 1
  318. local IP IPS IPT LOG
  319. IP="/usr/sbin/ip -4"
  320. IPS="/usr/sbin/ipset"
  321. IPT="/usr/sbin/iptables -t mangle -w"
  322. LOG="/usr/bin/logger -t mwan3 -p"
  323. case "$ACTION" in
  324. ifup|ifdown)
  325. mwan3_ifupdown
  326. mwan3_set_connected_iptables
  327. ;;
  328. esac
  329. exit 0