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.

406 lines
11 KiB

  1. #!/bin/sh
  2. . /lib/functions.sh
  3. . /lib/functions/network.sh
  4. . /lib/mwan3/common.sh
  5. INTERFACE=""
  6. DEVICE=""
  7. IFDOWN_EVENT=0
  8. IFUP_EVENT=0
  9. stop_subprocs() {
  10. [ -n "$SLEEP_PID" ] && kill "$SLEEP_PID" && unset SLEEP_PID
  11. [ -n "$TRACK_PID" ] && kill "$TRACK_PID" && unset TRACK_PID
  12. }
  13. WRAP() {
  14. # shellcheck disable=SC2048
  15. FAMILY=$FAMILY DEVICE=$DEVICE SRCIP=$SRC_IP FWMARK=$MMX_DEFAULT LD_PRELOAD=/lib/mwan3/libwrap_mwan3_sockopt.so.1.0 $*
  16. }
  17. clean_up() {
  18. LOG notice "Stopping mwan3track for interface \"${INTERFACE}\". Status was \"${STATUS}\""
  19. stop_subprocs
  20. exit 0
  21. }
  22. if_down() {
  23. LOG info "Detect ifdown event on interface ${INTERFACE} (${DEVICE})"
  24. IFDOWN_EVENT=1
  25. stop_subprocs
  26. }
  27. if_up() {
  28. LOG info "Detect ifup event on interface ${INTERFACE} (${DEVICE})"
  29. IFDOWN_EVENT=0
  30. IFUP_EVENT=1
  31. STARTED=1
  32. stop_subprocs
  33. }
  34. ping_test_host() {
  35. if [ "$FAMILY" = "ipv6" ]; then
  36. echo "::1"
  37. else
  38. echo "127.0.0.1"
  39. fi
  40. }
  41. get_ping_command() {
  42. if [ -x "/usr/bin/ping" ] && /usr/bin/ping -${FAMILY#ipv} -c1 -q $(ping_test_host) &>/dev/null; then
  43. # -4 option added in iputils c3e68ac6 so need to check if we can use it
  44. # or if we must use ping and ping6
  45. echo "/usr/bin/ping -${FAMILY#ipv}"
  46. elif [ "$FAMILY" = "ipv6" ] && [ -x "/usr/bin/ping6" ]; then
  47. echo "/usr/bin/ping6"
  48. elif [ "$FAMILY" = "ipv4" ] && [ -x "/usr/bin/ping" ]; then
  49. echo "/usr/bin/ping"
  50. elif [ -x "/bin/ping" ]; then
  51. echo "/bin/ping -${FAMILY#ipv}"
  52. else
  53. return 1
  54. fi
  55. }
  56. validate_track_method() {
  57. case "$1" in
  58. ping)
  59. PING=$(get_ping_command)
  60. if [ $? -ne 0 ]; then
  61. LOG warn "Missing ping. Please enable BUSYBOX_DEFAULT_PING and recompile busybox or install iputils-ping package."
  62. return 1
  63. fi
  64. ;;
  65. arping)
  66. command -v arping 1>/dev/null 2>&1 || {
  67. LOG warn "Missing arping. Please install iputils-arping package."
  68. return 1
  69. }
  70. ;;
  71. httping)
  72. command -v httping 1>/dev/null 2>&1 || {
  73. LOG warn "Missing httping. Please install httping package."
  74. return 1
  75. }
  76. ;;
  77. nping-*)
  78. command -v nping 1>/dev/null 2>&1 || {
  79. LOG warn "Missing nping. Please install nping package."
  80. return 1
  81. }
  82. ;;
  83. *)
  84. LOG warn "Unsupported tracking method: $track_method"
  85. return 2
  86. ;;
  87. esac
  88. }
  89. validate_wrap() {
  90. [ -x /lib/mwan3/libwrap_mwan3_sockopt.so.1.0 ] && return
  91. LOG error "Missing libwrap_mwan3_sockopt. Please reinstall mwan3." &&
  92. exit 1
  93. }
  94. disconnected() {
  95. STATUS='offline'
  96. echo "offline" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  97. get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE
  98. echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE
  99. score=0
  100. [ "$1" = 1 ] && return
  101. LOG notice "Interface $INTERFACE ($DEVICE) is offline"
  102. env -i ACTION="disconnected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
  103. }
  104. connected() {
  105. STATUS='online'
  106. echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  107. echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE
  108. get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE
  109. host_up_count=0
  110. lost=0
  111. turn=0
  112. loss=0
  113. LOG notice "Interface $INTERFACE ($DEVICE) is online"
  114. env -i FIRSTCONNECT=$1 ACTION="connected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
  115. }
  116. disconnecting() {
  117. if [ "$STATUS" != "disconnecting" ] ; then
  118. STATUS="disconnecting"
  119. echo "disconnecting" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  120. LOG notice "Interface $INTERFACE ($DEVICE) is disconnecting"
  121. env -i ACTION="disconnecting" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
  122. fi
  123. }
  124. connecting() {
  125. if [ "$STATUS" != "connecting" ] ; then
  126. STATUS="connecting"
  127. echo "connecting" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  128. LOG notice "Interface $INTERFACE ($DEVICE) is connecting"
  129. env -i ACTION="connecting" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
  130. fi
  131. }
  132. disabled() {
  133. STATUS='disabled'
  134. echo "disabled" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  135. STARTED=0
  136. }
  137. firstconnect() {
  138. local true_iface
  139. network_flush_cache
  140. mwan3_get_true_iface true_iface $INTERFACE
  141. network_get_device DEVICE $true_iface
  142. if [ "$STATUS" != "online" ]; then
  143. config_get STATUS $INTERFACE initial_state "online"
  144. fi
  145. if ! network_is_up $true_iface || [ -z "$DEVICE" ]; then
  146. disabled
  147. return
  148. fi
  149. mwan3_get_src_ip SRC_IP $INTERFACE
  150. LOG debug "firstconnect: called on $INTERFACE/$true_iface ($DEVICE). Status is $STATUS. SRC_IP is $SRC_IP"
  151. STARTED=1
  152. if [ "$STATUS" = "offline" ]; then
  153. disconnected 1
  154. else
  155. connected 1
  156. fi
  157. }
  158. update_status() {
  159. local track_ip=$1
  160. echo "$2" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  161. [ -z "$3" ] && return
  162. echo "$3" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LATENCY_${track_ip}
  163. echo "$4" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOSS_${track_ip}
  164. }
  165. main() {
  166. local reliability count timeout interval failure_interval
  167. local recovery_interval down up size
  168. local keep_failure_interval check_quality failure_latency
  169. local recovery_latency failure_loss recovery_loss
  170. local max_ttl httping_ssl track_ips do_log
  171. INTERFACE=$1
  172. STATUS=""
  173. STARTED=0
  174. TRACK_OUTPUT=$MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_OUTPUT
  175. mwan3_init
  176. mkdir -p $MWAN3TRACK_STATUS_DIR/$INTERFACE
  177. trap clean_up TERM
  178. trap if_down USR1
  179. trap if_up USR2
  180. config_get FAMILY $INTERFACE family ipv4
  181. config_get track_method $INTERFACE track_method ping
  182. config_get_bool httping_ssl $INTERFACE httping_ssl 0
  183. validate_track_method $track_method || {
  184. track_method=ping
  185. if validate_track_method $track_method; then
  186. LOG warn "Using ping to track interface $INTERFACE avaliability"
  187. else
  188. LOG err "No track method avaliable"
  189. exit 1
  190. fi
  191. }
  192. config_get reliability $INTERFACE reliability 1
  193. config_get count $INTERFACE count 1
  194. config_get timeout $INTERFACE timeout 4
  195. config_get interval $INTERFACE interval 10
  196. config_get down $INTERFACE down 5
  197. config_get up $INTERFACE up 5
  198. config_get size $INTERFACE size 56
  199. config_get max_ttl $INTERFACE max_ttl 60
  200. config_get failure_interval $INTERFACE failure_interval $interval
  201. config_get_bool keep_failure_interval $INTERFACE keep_failure_interval 0
  202. config_get recovery_interval $INTERFACE recovery_interval $interval
  203. config_get_bool check_quality $INTERFACE check_quality 0
  204. config_get failure_latency $INTERFACE failure_latency 1000
  205. config_get recovery_latency $INTERFACE recovery_latency 500
  206. config_get failure_loss $INTERFACE failure_loss 40
  207. config_get recovery_loss $INTERFACE recovery_loss 10
  208. local sleep_time result ping_status loss latency
  209. mwan3_list_track_ips()
  210. {
  211. track_ips="$track_ips $1"
  212. }
  213. config_list_foreach "$1" track_ip mwan3_list_track_ips
  214. local score=$((down+up))
  215. local host_up_count=0
  216. local lost=0
  217. local turn=0
  218. firstconnect
  219. while true; do
  220. [ $STARTED -eq 0 ] && { sleep $MAX_SLEEP & SLEEP_PID=$!; wait; }
  221. unset SLEEP_PID
  222. sleep_time=$interval
  223. for track_ip in $track_ips; do
  224. if [ $host_up_count -lt $reliability ]; then
  225. case "$track_method" in
  226. ping)
  227. if [ $check_quality -eq 0 ]; then
  228. WRAP $PING -n -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null &
  229. TRACK_PID=$!
  230. wait $TRACK_PID
  231. result=$?
  232. else
  233. WRAP $PING -n -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null > $TRACK_OUTPUT &
  234. TRACK_PID=$!
  235. wait $TRACK_PID
  236. ping_status=$?
  237. loss="$(sed $TRACK_OUTPUT -ne 's/.*\([0-9]\+\)% packet loss.*/\1/p')"
  238. if [ "$ping_status" -ne 0 ] || [ "$loss" -eq 100 ]; then
  239. latency=999999
  240. loss=100
  241. else
  242. latency="$(sed $TRACK_OUTPUT -ne 's%\(rtt\|round-trip\).* = [^/]*/\([0-9]\+\).*%\2%p')"
  243. fi
  244. fi
  245. ;;
  246. arping)
  247. WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null &
  248. TRACK_PID=$!
  249. wait $TRACK_PID
  250. result=$?
  251. ;;
  252. httping)
  253. if [ "$httping_ssl" -eq 1 ]; then
  254. WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null &
  255. else
  256. WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null &
  257. fi
  258. TRACK_PID=$!
  259. wait $TRACK_PID
  260. result=$?
  261. ;;
  262. nping-*)
  263. WRAP nping -c $count $track_ip --${FAMILY#nping-} > $TRACK_OUTPUT &
  264. TRACK_PID=$!
  265. wait $TRACK_PID
  266. result=$(grep $TRACK_OUTPUT Lost | awk '{print $12}')
  267. ;;
  268. esac
  269. do_log=""
  270. if [ $check_quality -eq 0 ]; then
  271. if [ $result -eq 0 ]; then
  272. let host_up_count++
  273. update_status "$track_ip" "up"
  274. [ $score -le $up ] && do_log="success"
  275. else
  276. let lost++
  277. update_status "$track_ip" "down"
  278. [ $score -gt $up ] && do_log="failed"
  279. fi
  280. [ -n "$do_log" ] && LOG info "Check ($track_method) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
  281. else
  282. if [ "$loss" -ge "$failure_loss" ] || [ "$latency" -ge "$failure_latency" ]; then
  283. let lost++
  284. update_status "$track_ip" "down" $latency $loss
  285. [ $score -gt $up ] && do_log="failed"
  286. elif [ "$loss" -le "$recovery_loss" ] && [ "$latency" -le "$recovery_latency" ]; then
  287. let host_up_count++
  288. update_status "$track_ip" "up" $latency $loss
  289. [ $score -le $up ] && do_log="success"
  290. else
  291. echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  292. fi
  293. [ -n "$do_log" ] && LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
  294. fi
  295. else
  296. echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  297. fi
  298. done
  299. if [ $host_up_count -lt $reliability ]; then
  300. let score--
  301. if [ $score -lt $up ]; then
  302. score=0
  303. [ ${keep_failure_interval} -eq 1 ] && sleep_time=$failure_interval
  304. else
  305. disconnecting
  306. sleep_time=$failure_interval
  307. fi
  308. if [ $score -eq $up ]; then
  309. disconnected
  310. score=0
  311. fi
  312. else
  313. if [ $score -lt $((down+up)) ] && [ $lost -gt 0 ]; then
  314. connecting
  315. LOG info "Lost $((lost*count)) ping(s) on interface $INTERFACE ($DEVICE). Current score: $score"
  316. fi
  317. let score++
  318. lost=0
  319. if [ $score -gt $up ]; then
  320. echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  321. score=$((down+up))
  322. elif [ $score -le $up ]; then
  323. connecting
  324. sleep_time=$recovery_interval
  325. fi
  326. if [ $score -eq $up ]; then
  327. connected
  328. fi
  329. fi
  330. let turn++
  331. mkdir -p "$MWAN3TRACK_STATUS_DIR/${1}"
  332. echo "${lost}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOST
  333. echo "${score}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/SCORE
  334. echo "${turn}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TURN
  335. get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TIME
  336. host_up_count=0
  337. if [ "${IFDOWN_EVENT}" -eq 0 ] && [ "${IFUP_EVENT}" -eq 0 ]; then
  338. sleep "${sleep_time}" &
  339. SLEEP_PID=$!
  340. wait
  341. unset SLEEP_PID
  342. fi
  343. if [ "${IFDOWN_EVENT}" -eq 1 ]; then
  344. LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})"
  345. disabled
  346. disconnected
  347. IFDOWN_EVENT=0
  348. fi
  349. if [ "${IFUP_EVENT}" -eq 1 ]; then
  350. LOG debug "Register ifup event on interface ${INTERFACE} (${DEVICE})"
  351. firstconnect
  352. IFUP_EVENT=0
  353. fi
  354. done
  355. }
  356. main "$@"