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.

386 lines
10 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. disabled() {
  117. STATUS='disabled'
  118. echo "disabled" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  119. STARTED=0
  120. }
  121. firstconnect() {
  122. local true_iface
  123. network_flush_cache
  124. mwan3_get_true_iface true_iface $INTERFACE
  125. network_get_device DEVICE $true_iface
  126. if [ "$STATUS" != "online" ]; then
  127. config_get STATUS $INTERFACE initial_state "online"
  128. fi
  129. if ! network_is_up $true_iface || [ -z "$DEVICE" ]; then
  130. disabled
  131. return
  132. fi
  133. mwan3_get_src_ip SRC_IP $INTERFACE
  134. LOG debug "firstconnect: called on $INTERFACE/$true_iface ($DEVICE). Status is $STATUS. SRC_IP is $SRC_IP"
  135. STARTED=1
  136. if [ "$STATUS" = "offline" ]; then
  137. disconnected 1
  138. else
  139. connected 1
  140. fi
  141. }
  142. update_status() {
  143. local track_ip=$1
  144. echo "$2" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  145. [ -z "$3" ] && return
  146. echo "$3" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LATENCY_${track_ip}
  147. echo "$4" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOSS_${track_ip}
  148. }
  149. main() {
  150. local reliability count timeout interval failure_interval
  151. local recovery_interval down up size
  152. local keep_failure_interval check_quality failure_latency
  153. local recovery_latency failure_loss recovery_loss
  154. local max_ttl httping_ssl track_ips do_log
  155. INTERFACE=$1
  156. STATUS=""
  157. STARTED=0
  158. TRACK_OUTPUT=$MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_OUTPUT
  159. mwan3_init
  160. mkdir -p $MWAN3TRACK_STATUS_DIR/$INTERFACE
  161. trap clean_up TERM
  162. trap if_down USR1
  163. trap if_up USR2
  164. config_load mwan3
  165. config_get FAMILY $INTERFACE family ipv4
  166. config_get track_method $INTERFACE track_method ping
  167. config_get_bool httping_ssl $INTERFACE httping_ssl 0
  168. validate_track_method $track_method || {
  169. track_method=ping
  170. if validate_track_method $track_method; then
  171. LOG warn "Using ping to track interface $INTERFACE avaliability"
  172. else
  173. LOG err "No track method avaliable"
  174. exit 1
  175. fi
  176. }
  177. config_get reliability $INTERFACE reliability 1
  178. config_get count $INTERFACE count 1
  179. config_get timeout $INTERFACE timeout 4
  180. config_get interval $INTERFACE interval 10
  181. config_get down $INTERFACE down 5
  182. config_get up $INTERFACE up 5
  183. config_get size $INTERFACE size 56
  184. config_get max_ttl $INTERFACE max_ttl 60
  185. config_get failure_interval $INTERFACE failure_interval $interval
  186. config_get_bool keep_failure_interval $INTERFACE keep_failure_interval 0
  187. config_get recovery_interval $INTERFACE recovery_interval $interval
  188. config_get_bool check_quality $INTERFACE check_quality 0
  189. config_get failure_latency $INTERFACE failure_latency 1000
  190. config_get recovery_latency $INTERFACE recovery_latency 500
  191. config_get failure_loss $INTERFACE failure_loss 40
  192. config_get recovery_loss $INTERFACE recovery_loss 10
  193. local sleep_time result ping_status loss latency
  194. mwan3_list_track_ips()
  195. {
  196. track_ips="$track_ips $1"
  197. }
  198. config_list_foreach "$1" track_ip mwan3_list_track_ips
  199. local score=$((down+up))
  200. local host_up_count=0
  201. local lost=0
  202. local turn=0
  203. firstconnect
  204. while true; do
  205. [ $STARTED -eq 0 ] && { sleep $MAX_SLEEP & SLEEP_PID=$!; wait; }
  206. unset SLEEP_PID
  207. sleep_time=$interval
  208. for track_ip in $track_ips; do
  209. if [ $host_up_count -lt $reliability ]; then
  210. case "$track_method" in
  211. ping)
  212. if [ $check_quality -eq 0 ]; then
  213. WRAP $PING -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null &
  214. TRACK_PID=$!
  215. wait $TRACK_PID
  216. result=$?
  217. else
  218. WRAP $PING -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null > $TRACK_OUTPUT &
  219. TRACK_PID=$!
  220. wait $TRACK_PID
  221. ping_status=$?
  222. loss="$(sed $TRACK_OUTPUT -ne 's/.*\([0-9]\+\)% packet loss.*/\1/p')"
  223. if [ "$ping_status" -ne 0 ] || [ "$loss" -eq 100 ]; then
  224. latency=999999
  225. loss=100
  226. else
  227. latency="$(sed $TRACK_OUTPUT -ne 's%\(rtt\|round-trip\).* = [^/]*/\([0-9]\+\).*%\2%p')"
  228. fi
  229. fi
  230. ;;
  231. arping)
  232. WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null &
  233. TRACK_PID=$!
  234. wait $TRACK_PID
  235. result=$?
  236. ;;
  237. httping)
  238. if [ "$httping_ssl" -eq 1 ]; then
  239. WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null &
  240. else
  241. WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null &
  242. fi
  243. TRACK_PID=$!
  244. wait $TRACK_PID
  245. result=$?
  246. ;;
  247. nping-*)
  248. WRAP nping -c $count $track_ip --${FAMILY#nping-} > $TRACK_OUTPUT &
  249. TRACK_PID=$!
  250. wait $TRACK_PID
  251. result=$(grep $TRACK_OUTPUT Lost | awk '{print $12}')
  252. ;;
  253. esac
  254. do_log=""
  255. if [ $check_quality -eq 0 ]; then
  256. if [ $result -eq 0 ]; then
  257. let host_up_count++
  258. update_status "$track_ip" "up"
  259. [ $score -le $up ] && do_log="success"
  260. else
  261. let lost++
  262. update_status "$track_ip" "down"
  263. [ $score -gt $up ] && do_log="failed"
  264. fi
  265. [ -n "$do_log" ] && LOG info "Check ($track_method) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
  266. else
  267. if [ "$loss" -ge "$failure_loss" ] || [ "$latency" -ge "$failure_latency" ]; then
  268. let lost++
  269. update_status "$track_ip" "down" $latency $loss
  270. [ $score -gt $up ] && do_log="failed"
  271. elif [ "$loss" -le "$recovery_loss" ] && [ "$latency" -le "$recovery_latency" ]; then
  272. let host_up_count++
  273. update_status "$track_ip" "up" $latency $loss
  274. [ $score -le $up ] && do_log="success"
  275. else
  276. echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  277. fi
  278. [ -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"
  279. fi
  280. else
  281. echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
  282. fi
  283. done
  284. if [ $host_up_count -lt $reliability ]; then
  285. let score--
  286. if [ $score -lt $up ]; then
  287. score=0
  288. [ ${keep_failure_interval} -eq 1 ] && sleep_time=$failure_interval
  289. else
  290. sleep_time=$failure_interval
  291. fi
  292. if [ $score -eq $up ]; then
  293. disconnected
  294. score=0
  295. fi
  296. else
  297. if [ $score -lt $((down+up)) ] && [ $lost -gt 0 ]; then
  298. LOG info "Lost $((lost*count)) ping(s) on interface $INTERFACE ($DEVICE). Current score: $score"
  299. fi
  300. let score++
  301. lost=0
  302. if [ $score -gt $up ]; then
  303. echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
  304. score=$((down+up))
  305. elif [ $score -le $up ]; then
  306. sleep_time=$recovery_interval
  307. fi
  308. if [ $score -eq $up ]; then
  309. connected
  310. fi
  311. fi
  312. let turn++
  313. mkdir -p "$MWAN3TRACK_STATUS_DIR/${1}"
  314. echo "${lost}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOST
  315. echo "${score}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/SCORE
  316. echo "${turn}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TURN
  317. get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TIME
  318. host_up_count=0
  319. if [ "${IFDOWN_EVENT}" -eq 0 ] && [ "${IFUP_EVENT}" -eq 0 ]; then
  320. sleep "${sleep_time}" &
  321. SLEEP_PID=$!
  322. wait
  323. unset SLEEP_PID
  324. fi
  325. if [ "${IFDOWN_EVENT}" -eq 1 ]; then
  326. LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})"
  327. disabled
  328. disconnected
  329. IFDOWN_EVENT=0
  330. fi
  331. if [ "${IFUP_EVENT}" -eq 1 ]; then
  332. LOG debug "Register ifup event on interface ${INTERFACE} (${DEVICE})"
  333. firstconnect
  334. IFUP_EVENT=0
  335. fi
  336. done
  337. }
  338. main "$@"