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.

381 lines
15 KiB

  1. #!/bin/sh
  2. # travelmate, a wlan connection manager for travel router
  3. # written by Dirk Brenken (dev@brenken.org)
  4. # This is free software, licensed under the GNU General Public License v3.
  5. # You should have received a copy of the GNU General Public License
  6. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  7. # set initial defaults
  8. #
  9. LC_ALL=C
  10. PATH="/usr/sbin:/usr/bin:/sbin:/bin"
  11. trm_ver="1.1.0"
  12. trm_sysver="unknown"
  13. trm_enabled=0
  14. trm_debug=0
  15. trm_automatic=1
  16. trm_captive=1
  17. trm_captiveurl="http://captive.apple.com"
  18. trm_minquality=30
  19. trm_maxretry=3
  20. trm_maxwait=30
  21. trm_timeout=60
  22. trm_radio=""
  23. trm_connection=""
  24. trm_rtfile="/tmp/trm_runtime.json"
  25. trm_fetch="$(command -v uclient-fetch)"
  26. trm_iwinfo="$(command -v iwinfo)"
  27. trm_wpa="$(command -v wpa_supplicant)"
  28. # load travelmate environment
  29. #
  30. f_envload()
  31. {
  32. local sys_call sys_desc sys_model sys_ver
  33. # get system information
  34. #
  35. sys_call="$(ubus -S call system board 2>/dev/null)"
  36. if [ -n "${sys_call}" ]
  37. then
  38. sys_desc="$(printf '%s' "${sys_call}" | jsonfilter -e '@.release.description')"
  39. sys_model="$(printf '%s' "${sys_call}" | jsonfilter -e '@.model')"
  40. sys_ver="$(cat /etc/turris-version 2>/dev/null)"
  41. if [ -n "${sys_ver}" ]
  42. then
  43. sys_desc="${sys_desc}/${sys_ver}"
  44. fi
  45. trm_sysver="${sys_model}, ${sys_desc}"
  46. fi
  47. # initialize variables
  48. #
  49. trm_devlist=""
  50. trm_stalist=""
  51. trm_radiolist=""
  52. # load uci config and check 'enabled' option
  53. #
  54. option_cb()
  55. {
  56. local option="${1}"
  57. local value="${2}"
  58. eval "${option}=\"${value}\""
  59. }
  60. config_load travelmate
  61. if [ ${trm_enabled} -ne 1 ]
  62. then
  63. f_log "info" "travelmate is currently disabled, please set 'trm_enabled' to '1' to use this service"
  64. exit 0
  65. fi
  66. # check eap capabilities
  67. #
  68. trm_eap="$("${trm_wpa}" -veap >/dev/null 2>&1; printf "%u" ${?})"
  69. }
  70. # gather radio information & bring down all STA interfaces
  71. #
  72. f_prep()
  73. {
  74. local config="${1}"
  75. local mode="$(uci_get wireless "${config}" mode)"
  76. local network="$(uci_get wireless "${config}" network)"
  77. local radio="$(uci_get wireless "${config}" device)"
  78. local disabled="$(uci_get wireless "${config}" disabled)"
  79. local eaptype="$(uci_get wireless "${config}" eap_type)"
  80. if ([ -z "${trm_radio}" ] || [ "${trm_radio}" = "${radio}" ]) && \
  81. [ -z "$(printf "%s" "${trm_radiolist}" | grep -Fo " ${radio}")" ]
  82. then
  83. trm_radiolist="${trm_radiolist} ${radio}"
  84. fi
  85. if [ "${mode}" = "sta" ] && [ "${network}" = "${trm_iface}" ]
  86. then
  87. if [ -z "${disabled}" ] || [ "${disabled}" = "0" ]
  88. then
  89. uci_set wireless "${config}" disabled 1
  90. fi
  91. if [ -z "${eaptype}" ] || [ ${trm_eap} -eq 0 ]
  92. then
  93. trm_stalist="${trm_stalist} ${config}_${radio}"
  94. fi
  95. fi
  96. f_log "debug" "f_prep ::: config: ${config}, mode: ${mode}, network: ${network}, radio: ${radio}, disabled: ${disabled}"
  97. }
  98. # check interface status
  99. #
  100. f_check()
  101. {
  102. local ifname radio dev_status config sta_iface sta_radio sta_essid sta_bssid heartbeat cnt=1 mode="${1}" status="${2:-"false"}"
  103. trm_ifquality=0
  104. trm_ifstatus="false"
  105. if [ "${mode}" != "initial" ]
  106. then
  107. ubus call network reload
  108. fi
  109. while [ ${cnt} -le ${trm_maxwait} ]
  110. do
  111. dev_status="$(ubus -S call network.wireless status 2>/dev/null)"
  112. if [ -n "${dev_status}" ]
  113. then
  114. if [ "${mode}" = "dev" ]
  115. then
  116. if [ "${trm_ifstatus}" != "${status}" ]
  117. then
  118. trm_ifstatus="${status}"
  119. f_jsnup
  120. fi
  121. for radio in ${trm_radiolist}
  122. do
  123. trm_ifstatus="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e "@.${radio}.up")"
  124. if [ "${trm_ifstatus}" = "true" ] && [ -z "$(printf "%s" "${trm_devlist}" | grep -Fo " ${radio}")" ]
  125. then
  126. trm_devlist="${trm_devlist} ${radio}"
  127. fi
  128. done
  129. if [ "${trm_radiolist}" = "${trm_devlist}" ] || [ ${cnt} -eq ${trm_maxwait} ] || [ "${status}" = "false" ]
  130. then
  131. ifname="${trm_devlist}"
  132. break
  133. fi
  134. else
  135. ifname="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].ifname')"
  136. config="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')"
  137. if [ -n "${ifname}" ] && [ -n "${config}" ]
  138. then
  139. sta_iface="$(uci_get wireless "${config}" network)"
  140. sta_radio="$(uci_get wireless "${config}" device)"
  141. sta_essid="$(uci_get wireless "${config}" ssid)"
  142. sta_bssid="$(uci_get wireless "${config}" bssid)"
  143. trm_ifquality="$(${trm_iwinfo} ${ifname} info 2>/dev/null | awk -F "[\/| ]" '/Link Quality:/{printf "%i\n", (100 / $NF * $(NF-1)) }')"
  144. if [ ${trm_ifquality} -ge ${trm_minquality} ]
  145. then
  146. trm_ifstatus="$(ubus -S call network.interface dump 2>/dev/null | jsonfilter -l1 -e "@.interface[@.device=\"${ifname}\"].up")"
  147. elif [ "${mode}" = "initial" ] && [ ${trm_ifquality} -lt ${trm_minquality} ]
  148. then
  149. trm_connection=""
  150. f_log "info" "uplink '${sta_essid:-"-"}/${sta_bssid:-"-"}' is out of range (${trm_ifquality}/${trm_minquality}), uplink disconnected (${trm_sysver})"
  151. fi
  152. fi
  153. fi
  154. if [ "${mode}" = "initial" ] || [ "${trm_ifstatus}" = "true" ]
  155. then
  156. if ([ "${trm_ifstatus}" != "true" ] && [ "${trm_ifstatus}" != "${status}" ]) || [ ${trm_ifquality} -lt ${trm_minquality} ]
  157. then
  158. f_jsnup
  159. fi
  160. if [ "${mode}" = "initial" ] && [ "${trm_captive}" -eq 1 ] && [ "${trm_ifstatus}" = "true" ]
  161. then
  162. heartbeat="$(${trm_fetch} --timeout=1 --spider "${trm_captiveurl}" 2>&1 | awk '/^Redirected/{printf "%s" "cp \047"$NF"\047";exit}/^Download completed/{printf "%s" "net ok";exit}/^Failed/{printf "%s" "net nok";exit}')"
  163. if ([ -n "${heartbeat}" ] && [ -z "${trm_connection}" ]) || [ "${trm_connection%/*}" != "${heartbeat}" ]
  164. then
  165. trm_connection="${heartbeat}/${trm_ifquality}"
  166. f_jsnup "${sta_iface}" "${sta_radio}" "${sta_essid}" "${sta_bssid}"
  167. fi
  168. fi
  169. break
  170. fi
  171. fi
  172. cnt=$((cnt+1))
  173. sleep 1
  174. done
  175. f_log "debug" "f_check::: mode: ${mode}, name: ${ifname:-"-"}, status: ${trm_ifstatus}, quality: ${trm_ifquality}, connection: ${trm_connection:-"-"}, cnt: ${cnt}, max-wait: ${trm_maxwait}, min-quality: ${trm_minquality}, captive: ${trm_captive}, automatic: ${trm_automatic}"
  176. }
  177. # update runtime information
  178. #
  179. f_jsnup()
  180. {
  181. local status="${trm_ifstatus}" iface="${1}" radio="${2}" essid="${3}" bssid="${4}"
  182. if [ "${status}" = "true" ]
  183. then
  184. status="connected (${trm_connection:-"-"})"
  185. elif [ "${status}" = "false" ]
  186. then
  187. status="not connected"
  188. fi
  189. json_init
  190. json_add_object "data"
  191. json_add_string "travelmate_status" "${status}"
  192. json_add_string "travelmate_version" "${trm_ver}"
  193. json_add_string "station_id" "${essid:-"-"}/${bssid:-"-"}"
  194. json_add_string "station_interface" "${iface:-"-"}"
  195. json_add_string "station_radio" "${radio:-"-"}"
  196. json_add_string "last_rundate" "$(/bin/date "+%d.%m.%Y %H:%M:%S")"
  197. json_add_string "system" "${trm_sysver}"
  198. json_close_object
  199. json_dump > "${trm_rtfile}"
  200. }
  201. # write to syslog
  202. #
  203. f_log()
  204. {
  205. local class="${1}"
  206. local log_msg="${2}"
  207. if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || [ ${trm_debug} -eq 1 ])
  208. then
  209. logger -p "${class}" -t "travelmate-[${trm_ver}]" "${log_msg}"
  210. if [ "${class}" = "err" ]
  211. then
  212. trm_ifstatus="error"
  213. f_jsnup
  214. logger -p "${class}" -t "travelmate-[${trm_ver}]" "Please check 'https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md' (${trm_sysver})"
  215. exit 1
  216. fi
  217. fi
  218. }
  219. # main function for connection handling
  220. #
  221. f_main()
  222. {
  223. local dev config combo_list scan scan_essid scan_bssid scan_quality sta sta_essid sta_bssid sta_radio sta_iface cnt=1 IFS=" "
  224. f_check "initial"
  225. if [ "${trm_ifstatus}" != "true" ]
  226. then
  227. config_load wireless
  228. config_foreach f_prep wifi-iface
  229. if [ -n "$(uci -q changes wireless)" ]
  230. then
  231. uci_commit wireless
  232. fi
  233. f_check "dev" "running"
  234. f_log "debug" "f_main ::: iwinfo: ${trm_iwinfo}, eap_rc: ${trm_eap}, dev_list: ${trm_devlist}, sta_list: ${trm_stalist}"
  235. for dev in ${trm_devlist}
  236. do
  237. cnt=1
  238. if [ -z "$(printf "%s" "${trm_stalist}" | grep -Fo "_${dev}")" ]
  239. then
  240. continue
  241. fi
  242. while [ ${trm_maxretry} -eq 0 ] || [ ${cnt} -le ${trm_maxretry} ]
  243. do
  244. combo_list="$(${trm_iwinfo} "${dev}" scan 2>/dev/null | awk 'BEGIN{FS="[/ ]"}/Address:/{var1=$NF}/ESSID:/{var2="";for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i}}/Quality:/{printf "%i,%s,%s\n",(100/$NF*$(NF-1)),var1,var2}' | sort -rn | awk '{ORS=",";print $0}')"
  245. f_log "debug" "f_main ::: dev: ${dev}, combo_list: ${combo_list}"
  246. if [ -n "${combo_list}" ]
  247. then
  248. for sta in ${trm_stalist}
  249. do
  250. config="${sta%%_*}"
  251. sta_radio="${sta##*_}"
  252. sta_essid="$(uci_get wireless "${config}" ssid)"
  253. sta_bssid="$(uci_get wireless "${config}" bssid)"
  254. sta_iface="$(uci_get wireless "${config}" network)"
  255. IFS=","
  256. for scan in ${combo_list}
  257. do
  258. if [ -z "${scan_quality}" ]
  259. then
  260. scan_quality="${scan}"
  261. elif [ -z "${scan_bssid}" ]
  262. then
  263. scan_bssid="${scan}"
  264. elif [ -z "${scan_essid}" ]
  265. then
  266. scan_essid="${scan}"
  267. fi
  268. if [ -n "${scan_quality}" ] && [ -n "${scan_bssid}" ] && [ -n "${scan_essid}" ]
  269. then
  270. if [ ${scan_quality} -ge ${trm_minquality} ]
  271. then
  272. if (([ "${scan_essid}" = "\"${sta_essid}\"" ] && [ -z "${sta_bssid}" ]) || \
  273. [ "${scan_bssid}" = "${sta_bssid}" ]) && [ "${dev}" = "${sta_radio}" ]
  274. then
  275. uci_set wireless "${config}" disabled 0
  276. f_check "sta"
  277. if [ ${trm_ifquality} -ge ${trm_minquality} ]
  278. then
  279. if [ "${trm_ifstatus}" = "true" ]
  280. then
  281. uci_commit wireless
  282. f_log "info" "interface '${sta_iface}' on '${sta_radio}' connected to uplink '${sta_essid:-"-"}/${sta_bssid:-"-"}' (${trm_sysver})"
  283. f_check "initial"
  284. return 0
  285. elif [ ${trm_maxretry} -ne 0 ] && [ ${cnt} -eq ${trm_maxretry} ]
  286. then
  287. uci_set wireless "${config}" disabled 1
  288. if [ -n "${sta_essid}" ]
  289. then
  290. uci_set wireless "${config}" ssid "${sta_essid}_err"
  291. fi
  292. if [ -n "${sta_bssid}" ]
  293. then
  294. uci_set wireless "${config}" bssid "${sta_bssid}_err"
  295. fi
  296. uci_commit wireless
  297. f_check "dev"
  298. f_log "info" "can't connect to uplink '${sta_essid:-"-"}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}), uplink disabled (${trm_sysver})"
  299. else
  300. if [ ${trm_maxretry} -eq 0 ]
  301. then
  302. cnt=0
  303. fi
  304. uci -q revert wireless
  305. f_check "dev"
  306. f_log "info" "can't connect to uplink '${sta_essid:-"-"}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}) (${trm_sysver})"
  307. fi
  308. f_jsnup
  309. fi
  310. fi
  311. fi
  312. scan_quality=""
  313. scan_bssid=""
  314. scan_essid=""
  315. fi
  316. done
  317. IFS=" "
  318. done
  319. fi
  320. cnt=$((cnt+1))
  321. sleep 5
  322. done
  323. done
  324. if [ ! -s "${trm_rtfile}" ]
  325. then
  326. trm_ifstatus="false"
  327. f_jsnup
  328. fi
  329. else
  330. if [ ! -s "${trm_rtfile}" ]
  331. then
  332. config="$(ubus -S call network.wireless status | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')"
  333. sta_radio="$(uci_get wireless "${config}" device)"
  334. sta_essid="$(uci_get wireless "${config}" ssid)"
  335. sta_bssid="$(uci_get wireless "${config}" bssid)"
  336. sta_iface="$(uci_get wireless "${config}" network)"
  337. f_jsnup "${sta_iface}" "${sta_radio}" "${sta_essid}" "${sta_bssid}"
  338. fi
  339. fi
  340. }
  341. # source required system libraries
  342. #
  343. if [ -r "/lib/functions.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]
  344. then
  345. . "/lib/functions.sh"
  346. . "/usr/share/libubox/jshn.sh"
  347. else
  348. f_log "err" "system libraries not found"
  349. fi
  350. # control travelmate actions
  351. #
  352. f_envload
  353. f_main
  354. while [ ${trm_automatic} -eq 1 ]
  355. do
  356. sleep ${trm_timeout}
  357. f_envload
  358. f_main
  359. done
  360. exit 0