diff options
author | Dirk Brenken <dev@brenken.org> | 2020-09-19 06:20:19 +0200 |
---|---|---|
committer | Dirk Brenken <dev@brenken.org> | 2020-09-19 06:20:19 +0200 |
commit | dd70304ca0b14da4223b793a199cf1f558116f4c (patch) | |
tree | 49b674d478b04bc45d8986da28e8fcbbe3033589 /net | |
parent | 50cced2475063556b777c7f9b2092bbe458f4492 (diff) |
travelmate: update to release 2.0
- new package dependency: curl (plus one of the wpad variants)
- optional package dependencies:
- 'msmtp' for email notification support
- 'wireguard' or 'openvpn' for vpn support
- removed WEP support, only WPA/WPA2/WPA3 are supported!
- new, more robust setup wizard (CLI and LuCI)
- more robust captive portal detection
- randomize mac addresses with every uplnk connect
- automatic vpn handling during uplink switch (only classic/simple
client-setups for wireguard or openvpn are supported)
- email notifications after successful uplink connections
- automatically disable uplinks after n minutes, e.g. for timed
connections
- automatically (re-)enable uplinks after n minutes, e.g. after failed
login attempts
- complete LuCI rewrite - migrated to client side JS (separate PR)
Signed-off-by: Dirk Brenken <dev@brenken.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/travelmate/Makefile | 10 | ||||
-rw-r--r-- | net/travelmate/files/README.md | 231 | ||||
-rwxr-xr-x | net/travelmate/files/chs-hotel.login | 37 | ||||
-rwxr-xr-x | net/travelmate/files/db-bahn.login | 53 | ||||
-rwxr-xr-x | net/travelmate/files/h-hotels.login | 37 | ||||
-rw-r--r-- | net/travelmate/files/travelmate.conf | 12 | ||||
-rwxr-xr-x | net/travelmate/files/travelmate.init | 137 | ||||
-rwxr-xr-x | net/travelmate/files/travelmate.mail | 72 | ||||
-rwxr-xr-x | net/travelmate/files/travelmate.sh | 948 | ||||
-rwxr-xr-x | net/travelmate/files/travelmate.vpn | 141 | ||||
-rwxr-xr-x | net/travelmate/files/travelmate_ntp.hotplug | 31 | ||||
-rwxr-xr-x | net/travelmate/files/wifionice.login | 28 |
12 files changed, 1263 insertions, 474 deletions
diff --git a/net/travelmate/Makefile b/net/travelmate/Makefile index 997ba12e3..3f721b527 100644 --- a/net/travelmate/Makefile +++ b/net/travelmate/Makefile @@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=travelmate -PKG_VERSION:=1.5.4 -PKG_RELEASE:=3 +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 PKG_LICENSE:=GPL-3.0-or-later PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org> @@ -17,7 +17,7 @@ define Package/travelmate SECTION:=net CATEGORY:=Network TITLE:=A wlan connection manager for travel router - DEPENDS:=+iwinfo +jshn +jsonfilter +uclient-fetch +dnsmasq + DEPENDS:=+iwinfo +jshn +jsonfilter +curl +ca-bundle +dnsmasq PKGARCH:=all endef @@ -47,10 +47,14 @@ define Package/travelmate/install $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./files/travelmate.init $(1)/etc/init.d/travelmate + $(INSTALL_DIR) $(1)/etc/hotplug.d/ntp + $(INSTALL_BIN) ./files/travelmate_ntp.hotplug $(1)/etc/hotplug.d/ntp/25-travelmate_ntp + $(INSTALL_DIR) $(1)/etc/config $(INSTALL_CONF) ./files/travelmate.conf $(1)/etc/config/travelmate $(INSTALL_DIR) $(1)/etc/travelmate + $(INSTALL_BIN) ./files/travelmate.mail $(1)/etc/travelmate $(INSTALL_BIN) ./files/*.login $(1)/etc/travelmate endef diff --git a/net/travelmate/files/README.md b/net/travelmate/files/README.md index 4f7eab676..06cb0e962 100644 --- a/net/travelmate/files/README.md +++ b/net/travelmate/files/README.md @@ -9,146 +9,171 @@ To avoid these kind of deadlocks, travelmate will set all station interfaces to * STA interfaces operating in an "always off" mode, to make sure that the AP is always accessible * easy setup within normal OpenWrt environment * strong LuCI-Support with builtin interface wizard and a wireless station manager +* render the QR-Code of the selected Access Point in LuCI to comfortably transfer the WLAN credentials to your mobile devices * fast uplink connections -* support all kinds of uplinks, incl. hidden and enterprise uplinks +* support all kinds of uplinks, incl. hidden and enterprise uplinks (WEP-based uplinks are no longer supported!) * continuously checks the existing uplink connection (quality), e.g. for conditional uplink (dis-) connections * automatically add open uplinks to your wireless config, e.g. hotel captive portals * captive portal detection with internet online check and a 'heartbeat' function to keep the uplink connection up & running -* captive portal auto-login hook (configured via uci/LuCI), you could reference an external script for captive portal auto-logins (see example below) +* captive portal auto-login hook (configured via uci/LuCI), you are able to reference an external script for captive portal auto-logins (see example below) +* includes a vpn hook with support for 'wireguard' or 'openvpn' client setups to handle VPN (re-) connections automatically +* includes an email hook to 'msmtp' to send notification e-mails after every succesful uplink connect * proactively scan and switch to a higher prioritized uplink, despite of an already existing connection +* connection tracking which keeps start and end date of an uplink connection +* automatically disable the uplink after n minutes, e.g. for timed connections +* automatically (re-)enable the uplink after n minutes, e.g. after failed login attempts +* option to generate a random unicast MAC address for each uplink connection +* ntp time sync before sending emails * support devices with multiple radios in any order -* procd init and hotplug support +* procd init and ntp-hotplug support * runtime information available via LuCI & via 'status' init command * status & debug logging to syslog -* optional: the LuCI frontend shows the WiFi QR codes from all configured Access Points. It allows you to connect your Android or iOS devices to your router’s WiFi using the QR code ## Prerequisites -* [OpenWrt](https://openwrt.org), tested with the stable release series (19.07.x) and with the latest OpenWrt snapshot -* iwinfo for wlan scanning, uclient-fetch for captive portal detection, dnsmasq as dns backend -* optional: qrencode 4.x for QR code support -* optional: wpad (the full version, not wpad-mini) to use Enterprise WiFi -* optional: curl to use external scripts for captive portal auto-logins +* [OpenWrt](https://openwrt.org), only compatible with the forthcoming stable 20.x or the latest OpenWrt snapshot +* 'dnsmasq' as dns backend +* 'iwinfo' for wlan scanning +* 'curl' for connection checking and all kinds of captive portal magic, e.g. cp detection and auto-logins +* a 'wpad' variant to support various WPA encrypted networks (WEP-based uplinks are no longer supported!) +* optional: 'qrencode' for AP QR code support +* optional: 'wireguard' or 'openvpn' for vpn client connections +* optional: 'msmtp' to send out travelmate related status messages via email ## Installation & Usage -* download the package [here](https://downloads.openwrt.org/snapshots/packages/x86_64/packages) -* install 'travelmate' (_opkg install travelmate_) -* configure your network: - * recommended: use the LuCI frontend with builtin interface wizard and a wireless station manager - * manual: see detailed configuration steps below - * at least you need one configured AP and one STA interface - -## LuCI travelmate companion package -* download the package [here](https://downloads.openwrt.org/snapshots/packages/x86_64/luci) -* install 'luci-app-travelmate' (_opkg install luci-app-travelmate_) -* the application is located in LuCI under 'Services' menu +* before you start with travelmate you should setup at least one Access Point, ideally on a separate radio +* download [travelmate](https://downloads.openwrt.org/snapshots/packages/x86_64/packages) +* download [luci-app-travelmate](https://downloads.openwrt.org/snapshots/packages/x86_64/luci) +* install both packages (_opkg install travelmate_, _opkg install luci-app-travelmate_) +* the LuCI application is located under the 'Services' menu +* start the travelmate 'Interface Wizard' once +* add multiple uplink stations as you like via the 'Wireless Stations' tab +* happy traveling ... ## Travelmate config options * usually the pre-configured travelmate setup works quite well and no manual config overrides are needed, all listed options apply to the 'global' section: - * trm\_enabled => main switch to enable/disable the travelmate service (bool/default: '0', disabled) - * trm\_debug => enable/disable debug logging (bool/default: '0', disabled) - * trm\_captive => enable/disable the captive portal detection (bool/default: '1', enabled) - * trm\_proactive => enable/disable the proactive uplink switch (bool/default: '1', enabled) - * trm\_autoadd => automatically add open uplinks to your wireless config (bool/default: '0', disabled) - * trm\_minquality => minimum signal quality threshold as percent for conditional uplink (dis-) connections (int/default: '35', valid range: 20-80) - * trm\_maxwait => how long (in seconds) should travelmate wait for a successful wlan interface reload action (int/default: '30', valid range: 20-40) - * trm\_maxretry => how many times should travelmate try to connect to an uplink (int/default: '3', valid range: 1-10) - * trm\_timeout => overall retry timeout in seconds (int/default: '60', valid range: 30-300) - * trm\_radio => limit travelmate to a single radio (e.g. 'radio1') or change the overall scanning priority (e.g. 'radio1 radio2 radio0') (default: not set, use all radios 0-n) - * trm\_iface => uplink / procd trigger network interface (default: trm_wwan) - * trm\_triggerdelay => additional trigger delay in seconds before travelmate processing begins (int/default: '2') -## Captive Portal auto-logins -For automated captive portal logins you could reference external shell scripts. All login scripts should be executable and located in '/etc/travelmate' with the extension '.login'. The provided 'wifionice.login' script example requires curl and automates the login to german ICE hotspots, it also explains the principle approach to extract runtime data like security tokens for a successful login. Hopefully more scripts for different captive portals will be provided by the community ... - -A typical/successful captive portal login looks like this: +| Option | Default | Description/Valid Values | +| :----------------- | :--------------------------------- | :---------------------------------------------------------------------------------------------------- | +| trm_enabled | 0, disabled | set to 1 to enable the travelmate service (this will be done by the Interface Wizard as well!) | +| trm_debug | 0, disabled | set to 1 to get the full debug output (logread -e "trm-") | +| trm_iface | -, not set | uplink- and procd trigger network interface, configured by the 'Interface Wizard' | +| trm_radio | -, not set | restrict travelmate to a single radio or change the overall scanning order ('radio1 radio0') | +| trm_captive | 1, enabled | check the internet availability and handle captive portal redirections | +| trm_netcheck | 0, disabled | treat missing internet availability as an error | +| trm_proactive | 1, enabled | proactively scan and switch to a higher prioritized uplink, despite of an already existing connection | +| trm_autoadd | 0, disabled | automatically add open uplinks like hotel captive portals to your wireless config | +| trm_randomize | 0, disabled | generate a random unicast MAC address for each uplink connection | +| trm_triggerdelay | 2 | additional trigger delay in seconds before travelmate processing begins | +| trm_maxretry | 3 | retry limit to connect to an uplink | +| trm_minquality | 35 | minimum signal quality threshold as percent for conditional uplink (dis-) connections | +| trm_maxwait | 30 | how long should travelmate wait for a successful wlan uplink connection | +| trm_timeout | 60 | overall retry timeout in seconds | +| trm_scanbuffer | 1024 | buffer size in bytes to prepare nearby scan results | +| trm_captiveurl | http://captive.apple.com | four pre-configured provider URLs that will be used for connectivity- and captive portal checks | +| trm_useragent | Mozilla/5.0 (X11; Linux x86_64... | five pre-configured user agents that will be used for connectivity- and captive portal checks | +| trm_nice | 0, normal priority | change the priority of the travelmate background processing | +| trm_vpn | 0, disabled | automatically handle VPN (re-) connections | +| trm_vpnservice | -, not set | reference the already configured 'wireguard' or 'openvpn' client instance as vpn provider | +| trm_vpniface | -, not set | the logical vpn interface, e.g. 'wg0' or 'tun0' | +| trm_laniface | -, not set | the logical lan network interface, e.g. 'br-lan' | +| trm_mail | 0, disabled | sends notification e-mails after every succesful uplink connect | +| trm_mailreceiver | -, not set | e-mail receiver address for travelmate notifications | +| trm_mailsender | no-reply@travelmate | e-mail sender address for travelmate notifications | +| trm_mailtopic | travelmate connection to '<sta>' | topic for travelmate notification E-Mails | +| trm_mailprofile | trm_notify | profile used by 'msmtp' for travelmate notification E-Mails | + +* per uplink exist an additional 'uplink' section in the travelmate config, with the following options: + +| Option | Default | Description/Valid Values | +| :----------------- | :--------------------------------- | :---------------------------------------------------------------------------------------------------- | +| enabled | 1, enabled | enable or disable the uplink in travelmate | +| device | -, not set | match the 'device' in the wireless config section | +| ssid | -, not set | match the 'ssid' in the wireless config section | +| bssid | -, not set | match the 'bssid' in the wireless config section | +| con_start | -, not set | connection start (will be automatically set after a successful ntp sync) | +| con_end | -, not set | connection end (will be automatically set after a successful ntp sync) | +| con_start_expiry | 0, disabled | automatically disable the uplink after n minutes, e.g. for timed connections | +| con_end_expiry | 0, disabled | automatically (re-)enable the uplink after n minutes, e.g. after failed login attempts | +| script | -, not set | reference to an external auto login script for captive portals | +| script_args | -, not set | optional runtime args for the auto login script | + + +## VPN client setup +Please follow one of the following guides to get a working vpn client setup on your travel router: + +* [Wireguard client setup guide](https://openwrt.org/docs/guide-user/services/vpn/wireguard/client) +* [OpenVPN client setup guide](https://openwrt.org/docs/guide-user/services/vpn/openvpn/client) + +Once your vpn client connection is running, you can reference to that setup in travelmate to handle VPN (re-) connections automatically. + +## E-Mail setup +To use E-Mail notifications you have to setup the package 'msmtp'. + +Modify the file '/etc/msmtprc', e.g. for gmail: <pre><code> [...] -Mon Aug 5 10:15:48 2019 user.info travelmate-1.4.10[1481]: travelmate instance started ::: action: start, pid: 1481 -Mon Aug 5 10:16:17 2019 user.info travelmate-1.4.10[1481]: captive portal login '/etc/travelmate/wifionice.login' for 'www.wifionice.de' has been executed with rc '0' -Mon Aug 5 10:16:23 2019 user.info travelmate-1.4.10[1481]: connected to uplink 'radio1/WIFIonICE/-' (1/5, GL.iNet GL-AR750S, OpenWrt SNAPSHOT r10644-cb49e46a8a) +defaults +auth on +tls on +tls_certcheck off +timeout 5 +syslog LOG_MAIL [...] +account trm_notify +host smtp.gmail.com +port 587 +from xxx@gmail.com +user yyy +password zzz </code></pre> -## Runtime information - -**receive travelmate runtime information:** -<pre><code> -~# /etc/init.d/travelmate status -::: travelmate runtime information - + travelmate_status : connected (net ok/100) - + travelmate_version : 1.4.10 - + station_id : radio1/blackhole/- - + station_interface : trm_wwan - + faulty_stations : - + last_rundate : 2019.08.03-20:37:19 - + system : GL.iNet GL-AR750S, OpenWrt SNAPSHOT r10644-cb49e46a8a -</code></pre> - -To debug travelmate runtime problems, please always enable the 'trm\_debug' flag, restart travelmate and scan the system log (_logread -e "travelmate"_) +Finally enable E-Mail support in travelmate and add a valid E-Mail receiver address. -## Manual Setup -**1. configure the travelmate wwan interface in /etc/config/network:** +## Captive Portal auto-logins +For automated captive portal logins you can reference an external shell script per uplink. All login scripts should be executable and located in '/etc/travelmate' with the extension '.login'. Currently the package ships five ready to run auto-login scripts: + * 'wifionice.login' for german ICE hotspots + * 'db-bahn.login' for german DB railway hotspots via portal login API (still WIP, only tested at Hannover central station) + * 'chs-hotel.login' for german chs hotels + * 'h-hotels.login' for Telekom hotspots in german h+hotels + * 'generic-user-pass.login' a template to demonstrate the optional parameter handling in login scripts + +A typical and successful captive portal login looks like this: <pre><code> [...] -config interface 'trm_wwan' - option proto 'dhcp' +Thu Sep 10 13:30:16 2020 user.info trm-2.0.0[26222]: captive portal domain 'www.wifionice.de' added to to dhcp rebind whitelist +Thu Sep 10 13:30:19 2020 user.info trm-2.0.0[26222]: captive portal login '/etc/travelmate/wifionice.login ' for 'www.wifionice.de' has been executed with rc '0' +Thu Sep 10 13:30:19 2020 user.info trm-2.0.0[26222]: connected to uplink 'radio1/WIFIonICE/-' with mac 'B2:9D:F5:96:86:A4' (1/3) [...] </code></pre> -**2. add this interface to your firewall configuration in /etc/config/firewall:** -<pre><code> -[...] -config zone - option name 'wan' - option network 'wan wan6 trm_wwan' -[...] -</code></pre> +Hopefully more scripts for different captive portals will be provided by the community! -**3. at least add one ap and (multiple) wwan stations to your wireless configuration in etc/config/wireless:** -<pre><code> -[...] -config wifi-iface - option device 'radio0' - option network 'lan' - option mode 'ap' - option ssid 'example_ap' - option encryption 'psk2+ccmp' - option key 'abc' - option disabled '0' -[...] -config wifi-iface - option device 'radio0' - option network 'trm_wwan' - option mode 'sta' - option ssid 'example_usual' - option encryption 'psk2+ccmp' - option key 'abc' - option disabled '1' -[...] -config wifi-iface - option device 'radio0' - option network 'trm_wwan' - option mode 'sta' - option ssid 'example_hidden' - option bssid '00:11:22:33:44:55' - option encryption 'psk2+ccmp' - option key 'xyz' - option disabled '1' -[...] -</code></pre> +## Runtime information -**4. start travelmate:** +**receive travelmate runtime information:** <pre><code> -edit /etc/config/travelmate and set 'trm_enabled' to '1' -/etc/init.d/travelmate restart +root@2go_ar750s:~# /etc/init.d/travelmate status +::: travelmate runtime information + + travelmate_status : connected (net ok/100) + + travelmate_version : 2.0.0 + + station_id : radio1/WIFIonICE/- + + station_mac : B2:9D:F5:96:86:A4 + + station_interface : trm_wwan + + wpa_flags : sae: ✔, owe: ✔, eap: ✔, suiteb192: ✔ + + run_flags : captive: ✔, proactive: ✔, netcheck: ✘, autoadd: ✘, randomize: ✔ + + ext_hooks : ntp: ✔, vpn: ✘, mail: ✘ + + last_run : 2020.09.10-15:21:19 + + system : GL.iNet GL-AR750S (NOR/NAND), OpenWrt SNAPSHOT r14430-2dda301d40 </code></pre> +To debug travelmate runtime problems, please always enable the 'trm\_debug' flag, restart travelmate and scan the system log (_logread -e "trm-"_) + ## Support Please join the travelmate discussion in this [forum thread](https://forum.lede-project.org/t/travelmate-support-thread/5155) or contact me by [mail](mailto:dev@brenken.org) ## Removal * stop the travelmate daemon with _/etc/init.d/travelmate stop_ -* optional: remove the travelmate package (_opkg remove travelmate_) +* optional: remove the travelmate package (_opkg remove luci-app-travelmate_, _opkg remove travelmate_) Have fun! Dirk diff --git a/net/travelmate/files/chs-hotel.login b/net/travelmate/files/chs-hotel.login new file mode 100755 index 000000000..80c1645fd --- /dev/null +++ b/net/travelmate/files/chs-hotel.login @@ -0,0 +1,37 @@ +#!/bin/sh +# captive portal auto-login script for german chs hotels +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +domain="hotspot.internet-for-guests.com" +cmd="$(command -v curl)" + +# curl check +# +if [ ! -x "${cmd}" ] +then + exit 1 +fi + +# initial get request to receive & extract valid security tokens +# +"${cmd}" "https://${domain}/logon/cgi/index.cgi" -c "/tmp/${domain}.cookie" -s -o /dev/null + +if [ -r "/tmp/${domain}.cookie" ] +then + lg_id="$(awk '/LGNSID/{print $7}' "/tmp/${domain}.cookie")" + ta_id="$(awk '/ta_id/{print $7}' "/tmp/${domain}.cookie")" + cl_id="$(awk '/cl_id/{print $7}' "/tmp/${domain}.cookie")" + rm -f "/tmp/${domain}.cookie" +else + exit 2 +fi + +# final post request/login with valid session cookie/security token +# +if [ -n "${lg_id}" ] && [ -n "${ta_id}" ] && [ -n "${cl_id}" ] +then + "${cmd}" "https://${domain}/logon/cgi/index.cgi" -H "Referer: https://${domain}/logon/cgi/index.cgi" -H "Cookie: LGNSID=${lg_id}; lang=en_US; use_mobile_interface=0; ta_id=${ta_id}; cl_id=${cl_id}" -H 'Connection: keep-alive' --data 'accept_termsofuse=&freeperperiod=1&device_infos=1125:2048:1152:2048' -s -o /dev/null +else + exit 3 +fi diff --git a/net/travelmate/files/db-bahn.login b/net/travelmate/files/db-bahn.login new file mode 100755 index 000000000..f9cb122f6 --- /dev/null +++ b/net/travelmate/files/db-bahn.login @@ -0,0 +1,53 @@ +#!/bin/sh +# captive portal auto-login script for german DB hotspots via portal login API +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +trm_fetch="$(command -v curl)" +trm_domain="wifi.bahn.de" +trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0")" +trm_maxwait="$(uci_get travelmate global trm_maxwait "30")" + +# initial get request to receive all header information +# +"${trm_fetch}" -A "${trm_useragent}" "https://${trm_domain}" -si > "/tmp/${trm_domain}.cookie" + +# extract the session cookie and the hotspot location +# +if [ -s "/tmp/${trm_domain}.cookie" ] +then + php_token="$(awk 'BEGIN{FS="[ ;]"}/^Set-Cookie:/{print $2}' "/tmp/${trm_domain}.cookie")" + location="$(awk '/^Location:/{print $2}' "/tmp/${trm_domain}.cookie")" + rm -f "/tmp/${trm_domain}.cookie" +else + exit 2 +fi + +# post request to subscribe to the portal API +# +if [ -n "${php_token}" ] && [ -n "${location}" ] +then + "${trm_fetch}" -A "${trm_useragent}" "https://${trm_domain}/portal_api.php" -H "Connection: keep-alive" -H "Referer: ${location}" -H "Cookie: ${php_token}" --data "action=subscribe&type=one&connect_policy_accept=false&user_login=&user_password=&user_password_confirm=&email_address=&prefix=&phone=&policy_accept=false&gender=&interests=" -si > "/tmp/${trm_domain}.cookie" +else + exit 3 +fi + +# extract additional login and password information from the portal API +# +if [ -s "/tmp/${trm_domain}.cookie" ] +then + login="$(awk 'BEGIN{FS="[\"]"}/^\{\"info/{print $12}' "/tmp/${trm_domain}.cookie")" + password="$(awk 'BEGIN{FS="[\"]"}/^\{\"info/{print $16}' "/tmp/${trm_domain}.cookie")" + rm -f "/tmp/${trm_domain}.cookie" +else + exit 4 +fi + +# final post request to authenticate to the portal API +# +if [ -n "${login}" ] && [ -n "${password}" ] +then + "${trm_fetch}" -A "${trm_useragent}" "https://${trm_domain}/portal_api.php" -H "Connection: keep-alive" -H "Referer: ${location}" -H "Cookie: ${php_token}" --data "action=authenticate&login=${login}&password=${password}&policy_accept=false&from_ajax=true&wispr_mode=false" +else + exit 5 +fi diff --git a/net/travelmate/files/h-hotels.login b/net/travelmate/files/h-hotels.login new file mode 100755 index 000000000..996a8e380 --- /dev/null +++ b/net/travelmate/files/h-hotels.login @@ -0,0 +1,37 @@ +#!/bin/sh +# captive portal auto-login script for Telekom hotspots in german h+hotels +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +domain="hotspot.t-mobile.net" +cmd="$(command -v curl)" + +# curl check +# +if [ ! -x "${cmd}" ] +then + exit 1 +fi + +# initial get request to receive & extract valid security tokens +# +"${cmd}" "https://${domain}/wlan/rest/freeLogin" -c "/tmp/${domain}.cookie" -s -o /dev/null + +if [ -r "/tmp/${domain}.cookie" ] +then + ses_id="$(awk '/JSESSIONID/{print $7}' "/tmp/${domain}.cookie")" + sec_id="$(awk '/DT_H/{print $7}' "/tmp/${domain}.cookie")" + dev_id="$(sha256sum /etc/config/wireless | awk '{printf "%s",substr($1,1,13)}')" + rm -f "/tmp/${domain}.cookie" +else + exit 2 +fi + +# final post request/login with valid session cookie/security token +# +if [ -n "${ses_id}" ] && [ -n "${sec_id}" ] && [ -n "${dev_id}" ] +then + "${cmd}" "https://${domain}/wlan/rest/freeLogin" -H "Referer: https://${domain}/TD/hotspot/H_Hotels/en_GB/index.html" -H "Cookie: JSESSIONID=${ses_id}; DT_DEV_ID=${dev_id}; DT_H=${sec_id}" -H 'Connection: keep-alive' --data "rememberMe=true" -s -o /dev/null +else + exit 3 +fi diff --git a/net/travelmate/files/travelmate.conf b/net/travelmate/files/travelmate.conf index ce9eb5f97..a27265b7b 100644 --- a/net/travelmate/files/travelmate.conf +++ b/net/travelmate/files/travelmate.conf @@ -1,5 +1,3 @@ -# travelmate configuration, for further information -# see 'https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md' config travelmate 'global' option trm_enabled '0' @@ -7,12 +5,6 @@ config travelmate 'global' option trm_proactive '1' option trm_netcheck '0' option trm_autoadd '0' - option trm_iface 'trm_wwan' - option trm_triggerdelay '2' + option trm_mail '0' + option trm_vpn '0' option trm_debug '0' - option trm_maxretry '5' - option trm_listexpiry '0' - option trm_minquality '35' - option trm_maxwait '30' - option trm_timeout '60' - option trm_rtfile '/tmp/trm_runtime.json' diff --git a/net/travelmate/files/travelmate.init b/net/travelmate/files/travelmate.init index 2f2eceff6..24eb23e39 100755 --- a/net/travelmate/files/travelmate.init +++ b/net/travelmate/files/travelmate.init @@ -1,9 +1,14 @@ #!/bin/sh /etc/rc.common +# set (s)hellcheck exceptions +# shellcheck disable=1091,2016,2039,2059,2086,2143,2181,2188 + START=25 USE_PROCD=1 -EXTRA_COMMANDS="status_service" +EXTRA_COMMANDS="scan setup" +EXTRA_HELP=" scan <radio> Scan for available nearby uplinks + setup [<iface>] [<zone>] [<metric>] Setup the travelmate uplink interface, by default 'trm_wwan' with firewall zone 'wan' and metric '100'" trm_init="/etc/init.d/travelmate" trm_script="/usr/bin/travelmate.sh" @@ -11,20 +16,21 @@ trm_pidfile="/var/run/travelmate.pid" boot() { - local iface="$(uci_get travelmate global trm_iface)" - - ubus -t 30 wait_for network.wireless network.interface."${iface:-"trm_wwan"}" 2>/dev/null + if [ -s "${trm_pidfile}" ] + then + > "${trm_pidfile}" + fi rc_procd start_service } start_service() { - if [ $("${trm_init}" enabled; printf "%u" ${?}) -eq 0 ] + if [ "$("${trm_init}" enabled; printf "%u" ${?})" -eq "0" ] then procd_open_instance "travelmate" procd_set_param command "${trm_script}" "${@}" procd_set_param pidfile "${trm_pidfile}" - procd_set_param nice "$(uci_get travelmate extra trm_nice "0")" + procd_set_param nice "$(uci_get travelmate global trm_nice "0")" procd_set_param stdout 1 procd_set_param stderr 1 procd_close_instance @@ -33,7 +39,9 @@ start_service() reload_service() { - local ppid pid timeout="$(uci_get travelmate global trm_timeout)" + local ppid pid timeout + + timeout="$(uci_get travelmate global trm_timeout)" if [ -s "${trm_pidfile}" ] then @@ -56,9 +64,9 @@ stop_service() status_service() { - local key keylist value rtfile="$(uci_get travelmate global trm_rtfile)" + local key keylist value rtfile - rtfile="${rtfile:-"/tmp/trm_runtime.json"}" + rtfile="$(uci_get travelmate global trm_rtfile "/tmp/trm_runtime.json")" json_load_file "${rtfile}" >/dev/null 2>&1 json_select data >/dev/null 2>&1 if [ ${?} -eq 0 ] @@ -75,18 +83,113 @@ status_service() fi } -service_triggers() +scan() { - local trigger="$(uci_get travelmate global trm_iface)" - local delay="$(uci_get travelmate global trm_triggerdelay)" + local result scan_dev radio="${1:-"radio0"}" - PROCD_RELOAD_DELAY=$((${delay:-2} * 1000)) - if [ -n "${trigger}" ] + scan_dev="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -l1 -e "@.${radio}.interfaces[0].ifname")" + result="$(iwinfo "${scan_dev:-${radio}}" scan 2>/dev/null | \ + awk 'BEGIN{FS="[[:space:]]"}/Address:/{var1=$NF}/ESSID:/{var2=""; + for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i}}/Channel:/{var3=$NF}/Quality:/{split($NF,var0,"/")}/Encryption:/{var4=""; + for(j=12;j<=NF;j++)if(var4==""){var4=$j}else{var4=var4" "$j};printf " %-11i%-10s%-35s%-20s%s\n",(var0[1]*100/var0[2]),var3,var2,var1,var4}' | \ + sort -rn)" + printf "%s\\n" "::: Available nearby uplinks on '${scan_dev:-${radio}}'" + printf "%s\\n" ":::" + if [ -n "${result}" ] then - procd_add_interface_trigger "interface.*.down" "${trigger}" "${trm_init}" reload + printf "%-15s%-10s%-35s%-20s%s\\n" " Strength" "Channel" "ESSID" "BSSID" "Encryption" + printf "%s\\n" " --------------------------------------------------------------------------------------" + printf "%s\\n" "${result}" else - procd_add_raw_trigger "interface.*.down" ${PROCD_RELOAD_DELAY} "${trm_init}" reload + printf "%s\\n" "::: No scan results" + fi +} + +setup() +{ + local iface cnt=0 input="${1:-"trm_wwan"}" zone="${2:-"wan"}" metric="${3:-"100"}" + + iface="$(uci_get travelmate global trm_iface)" + input="${input//[+*~%&\$@\"\' ]/}" + zone="${zone//[+*~%&\$@\"\' ]/}" + metric="${metric//[^0-9]/}" + + if [ -n "${iface}" ] && [ "${iface}" = "${input}" ] + then + printf "%s\n" "The uplink interface '${input}' has been already configured" + elif [ -n "${input}" ] + then + if [ -n "${iface}" ] + then + uci -q batch <<-EOC + del network."${iface}" + del network."${iface}6" + EOC + fi + uci -q batch <<-EOC + set travelmate.global.trm_enabled="1" + set travelmate.global.trm_iface="${input}" + set network."${input}"="interface" + set network."${input}".proto="dhcp" + set network."${input}".metric="${metric}" + set network."${input}6"=interface + set network."${input}6".ifname="@${input}" + set network."${input}6".proto="dhcpv6" + commit travelmate + commit network + EOC + + while [ -n "$(uci -q get firewall.@zone["${cnt}"].name)" ] + do + if [ "$(uci -q get firewall.@zone["${cnt}"].name)" = "${zone}" ] + then + if [ -n "${iface}" ] + then + uci -q batch <<-EOC + del_list firewall.@zone["${cnt}"].network="${iface}" + del_list firewall.@zone["${cnt}"].network="${iface}6" + EOC + fi + uci -q batch <<-EOC + add_list firewall.@zone["${cnt}"].network="${input}" + add_list firewall.@zone["${cnt}"].network="${input}6" + commit firewall + EOC + break + fi + cnt=$((cnt+1)) + done + + if [ -n "${iface}" ] + then + cnt=0 + while [ -n "$(uci -q get wireless.@wifi-iface["${cnt}"].network)" ] + do + if [ "$(uci -q get wireless.@wifi-iface["${cnt}"].network)" = "${iface}" ] + then + uci -q set wireless.@wifi-iface["${cnt}"].network="${input}" + fi + cnt=$((cnt+1)) + done + uci -q commit wireless + fi + /etc/init.d/network reload >/dev/null 2>&1 + /etc/init.d/firewall reload >/dev/null 2>&1 + "${trm_init}" restart + fi +} + +service_triggers() +{ + local iface delay + + iface="$(uci_get travelmate global trm_iface)" + delay="$(uci_get travelmate global trm_triggerdelay "2")" + PROCD_RELOAD_DELAY=$((delay * 1000)) + + if [ -n "${iface}" ] + then + procd_add_interface_trigger "interface.*.down" "${iface}" "${trm_init}" reload fi - procd_add_config_trigger "config.change" "wireless" "${trm_init}" reload procd_add_config_trigger "config.change" "travelmate" "${trm_init}" restart } diff --git a/net/travelmate/files/travelmate.mail b/net/travelmate/files/travelmate.mail new file mode 100755 index 000000000..859bf1f11 --- /dev/null +++ b/net/travelmate/files/travelmate.mail @@ -0,0 +1,72 @@ +#!/bin/sh +# send mail script for travelmate notifications +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +# set (s)hellcheck exceptions +# shellcheck disable=1091,2016,2039,2059,2086,2143,2181,2188 + +# Please note: you have to setup the package 'msmtp' before using this script + +LC_ALL=C +PATH="/usr/sbin:/usr/bin:/sbin:/bin" + +. "/lib/functions.sh" +trm_debug="$(uci_get travelmate global trm_debug "0")" +trm_mailsender="$(uci_get travelmate global trm_mailsender "no-reply@travelmate")" +trm_mailreceiver="$(uci_get travelmate global trm_mailreceiver)" +trm_mailprofile="$(uci_get travelmate global trm_mailprofile "trm_notify")" +trm_mail="$(command -v msmtp)" +trm_rtfile="$(uci_get travelmate global trm_rtfile "/tmp/trm_runtime.json")" +trm_logger="$(command -v logger)" +trm_rc=1 + +f_log() +{ + local class="${1}" log_msg="${2}" + + if [ -x "${trm_logger}" ] + then + "${trm_logger}" -p "${class}" -t "trm-mail [${$}]" "${log_msg}" + else + printf "%s %s %s\\n" "${class}" "trm-mail [${$}]" "${log_msg}" + fi +} + +if [ -z "${trm_mailreceiver}" ] +then + f_log "err" "please set the mail receiver with the 'trm_mailreceiver' option" + exit ${trm_rc} +fi + +if [ "${trm_debug}" -eq 1 ] +then + debug="--debug" +fi + +# info preparation +# +sys_info="$(strings /etc/banner 2>/dev/null; ubus call system board | sed -e 's/\"release\": {//' | sed -e 's/^[ \t]*//' | sed -e 's/[{}\",]//g' | sed -e 's/[ ]/ \t/' | sed '/^$/d' 2>/dev/null)" +trm_info="$(/etc/init.d/travelmate status 2>/dev/null)" +sta_info="$(jsonfilter -i "${trm_rtfile}" -l1 -e '@.data.station_id')" +trm_mailtopic="$(uci_get travelmate global trm_mailtopic "travelmate connection to '${sta_info}'")" +trm_mailhead="From: ${trm_mailsender}\\nTo: ${trm_mailreceiver}\\nSubject: ${trm_mailtopic}\\nReply-to: ${trm_mailsender}\\nMime-Version: 1.0\\nContent-Type: text/html; charset=UTF-8\\nContent-Disposition: inline\\n\\n" + +# mail body +# +trm_mailtext="<html><body><pre style='display:block;font-family:monospace;font-size:1rem;padding:20;background-color:#f3eee5;white-space:pre'>" +trm_mailtext="${trm_mailtext}\\n<strong>++\\n++ System Information ++\\n++</strong>\\n${sys_info}" +trm_mailtext="${trm_mailtext}\\n\\n<strong>++\\n++ Travelmate Information ++\\n++</strong>\\n${trm_info}" +trm_mailtext="${trm_mailtext}</pre></body></html>" + +# send mail +# +if [ -x "${trm_mail}" ] +then + printf "%b" "${trm_mailhead}${trm_mailtext}" 2>/dev/null | "${trm_mail}" ${debug} -a "${trm_mailprofile}" "${trm_mailreceiver}" >/dev/null 2>&1 + trm_rc=${?} + f_log "info" "mail sent to '${trm_mailreceiver}' with rc '${trm_rc}'" +else + f_log "err" "msmtp mail daemon not found" +fi +exit ${trm_rc} diff --git a/net/travelmate/files/travelmate.sh b/net/travelmate/files/travelmate.sh index 660b1635b..0a85cc235 100755 --- a/net/travelmate/files/travelmate.sh +++ b/net/travelmate/files/travelmate.sh @@ -8,51 +8,87 @@ # set initial defaults # -LC_ALL=C -PATH="/usr/sbin:/usr/bin:/sbin:/bin" -trm_ver="1.5.4" +export LC_ALL=C +export PATH="/usr/sbin:/usr/bin:/sbin:/bin" +set -o pipefail +trm_ver="2.0.0" trm_enabled=0 trm_debug=0 -trm_iface="trm_wwan" +trm_iface="" trm_captive=1 trm_proactive=1 trm_netcheck=0 trm_autoadd=0 -trm_captiveurl="http://captive.apple.com" +trm_randomize=0 +trm_mail=0 +trm_vpn=0 +trm_mailpgm="/etc/travelmate/travelmate.mail" +trm_vpnpgm="/etc/travelmate/travelmate.vpn" +trm_vpnservice="" trm_scanbuffer=1024 trm_minquality=35 -trm_maxretry=5 +trm_maxretry=3 trm_maxwait=30 trm_timeout=60 -trm_listexpiry=0 trm_radio="" trm_connection="" +trm_wpaflags="" trm_rtfile="/tmp/trm_runtime.json" trm_wifi="$(command -v wifi)" -trm_wificmd="reload" -trm_fetch="$(command -v uclient-fetch)" +trm_fetch="$(command -v curl)" trm_iwinfo="$(command -v iwinfo)" -trm_wpa="$(command -v wpa_supplicant)" trm_logger="$(command -v logger)" -trm_action="${1:-"start"}" +trm_wpa="$(command -v wpa_supplicant)" +trm_captiveurl="http://captive.apple.com" +trm_useragent="Mozilla/5.0 (Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0" +trm_ntpfile="/var/state/travelmate.ntp" +trm_mailfile="/var/state/travelmate.mail" trm_pidfile="/var/run/travelmate.pid" +trm_action="${1:-"start"}" # load travelmate environment # f_env() { - local IFS check wpa_checks + local IFS check wpa_checks ubus_check result + + # do nothing on stop + # + if [ "${trm_action}" = "stop" ] + then + return + fi # (re-)initialize global list variables # - unset trm_devlist trm_stalist trm_radiolist trm_active_sta + unset trm_stalist trm_radiolist trm_uplinklist trm_wpaflags trm_activesta # get system information # trm_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -e '@.model' -e '@.release.description' | \ awk 'BEGIN{ORS=", "}{print $0}' | awk '{print substr($0,1,length($0)-2)}')" - # load config and check 'enabled' option + # check travelmate config + # + if [ ! -r "/etc/config/travelmate" ] || [ -z "$(uci -q show travelmate.global.trm_vpn)" ] + then + if { [ -r "/etc/config/travelmate-opkg" ] && [ -n "$(uci -q show travelmate.global.trm_vpn)" ]; } || \ + { [ -r "/rom/etc/config/travelmate" ] && [ -n "$(uci -q show /rom/etc/config/travelmate.global.trm_vpn)" ]; } + then + if [ -r "/etc/config/travelmate-opkg" ] + then + cp -pf "/etc/config/travelmate-opkg" "/etc/config/travelmate" + elif [ -r "/rom/etc/config/travelmate" ] + then + cp -pf "/rom/etc/config/travelmate" "/etc/config/travelmate" + fi + f_log "info" "missing or old travelmate config replaced with a new one" + else + f_log "err" "unrecoverable travelmate config error, please re-install the package via opkg with the '--force-reinstall --force-maintainer' options" + fi + fi + + # load travelmate config # config_cb() { @@ -73,81 +109,80 @@ f_env() } config_load travelmate - if [ "${trm_enabled}" -ne 1 ] + # check 'enabled' option + # + if [ "${trm_enabled}" -ne "1" ] then f_log "info" "travelmate is currently disabled, please set 'trm_enabled' to '1' to use this service" - > "${trm_pidfile}" - exit 0 + /etc/init.d/travelmate stop + fi + + # check ubus network interface + # + if [ -n "${trm_iface}" ] + then + ubus_check="$(ubus -t "${trm_maxwait}" wait_for network.wireless network.interface."${trm_iface}" 2>&1)" + if [ -n "${ubus_check}" ] + then + f_log "info" "travelmate interface '${trm_iface}' does not appear on ubus, please check your network setup" + /etc/init.d/travelmate stop + fi + else + f_log "info" "travelmate is currently not configured, please use the 'Interface Setup' in LuCI or the 'setup' option in CLI" + /etc/init.d/travelmate stop fi - # get wpa_supplicant capabilities + # check wpa capabilities # - wpa_checks="eap sae owe" + wpa_checks="sae owe eap suiteb192" for check in ${wpa_checks} do if [ -x "${trm_wpa}" ] then - eval "trm_${check}check=\"$("${trm_wpa}" -v${check} >/dev/null 2>&1; printf "%u" "${?}")\"" - else - eval "trm_${check}check=\"1\"" + result="$("${trm_wpa}" -v${check} >/dev/null 2>&1; printf "%u" "${?}")" + if [ -z "${trm_wpaflags}" ] + then + if [ "${result}" -eq "0" ] + then + trm_wpaflags="${check}: $(f_char 1)" + else + trm_wpaflags="${check}: $(f_char 0)" + fi + else + if [ "${result}" -eq "0" ] + then + trm_wpaflags="$(f_trim "${trm_wpaflags}, ${check}: $(f_char 1)")" + else + trm_wpaflags="$(f_trim "${trm_wpaflags}, ${check}: $(f_char 0)")" + fi + fi fi done - # get wifi reconf capabilities - # - if [ -n "$(grep -F "reconf" "${trm_wifi}" 2>/dev/null)" ] - then - trm_wificmd="reconf" - fi - - # enable 'disabled' wifi devices + # get and enable wifi devices # config_load wireless config_foreach f_prepdev wifi-device if [ -n "$(uci -q changes "wireless")" ] then uci_commit "wireless" - "${trm_wifi}" "${trm_wificmd}" - sleep $((trm_maxwait/6)) - fi - - # validate input ranges - # - if [ "${trm_minquality}" -lt 20 ] || [ "${trm_minquality}" -gt 80 ] - then - trm_minquality=35 - fi - if [ "${trm_listexpiry}" -lt 0 ] || [ "${trm_listexpiry}" -gt 300 ] - then - trm_listexpiry=0 - fi - if [ "${trm_maxretry}" -lt 1 ] || [ "${trm_maxretry}" -gt 10 ] - then - trm_maxretry=5 - fi - if [ "${trm_maxwait}" -lt 20 ] || [ "${trm_maxwait}" -gt 40 ] || [ "${trm_maxwait}" -ge "${trm_timeout}" ] - then - trm_maxwait=30 - fi - if [ "${trm_timeout}" -lt 30 ] || [ "${trm_timeout}" -gt 300 ] || [ "${trm_timeout}" -le "${trm_maxwait}" ] - then - trm_timeout=60 + f_reconf fi # load json runtime file # json_load_file "${trm_rtfile}" >/dev/null 2>&1 json_select data >/dev/null 2>&1 - if [ "${?}" -ne 0 ] + if [ "${?}" -ne "0" ] then > "${trm_rtfile}" json_init json_add_object "data" fi - f_log "debug" "f_env ::: trm_eapcheck: ${trm_eapcheck:-"-"}, trm_saecheck: ${trm_saecheck:-"-"}, trm_owecheck: ${trm_owecheck:-"-"}, trm_wificmd: ${trm_wificmd}" + f_log "debug" "f_env ::: wpa_flags: ${trm_wpaflags}, sys_ver: ${trm_sysver}" } -# trim leading and trailing whitespace characters +# trim helper function # f_trim() { @@ -155,102 +190,403 @@ f_trim() trim="${trim#"${trim%%[![:space:]]*}"}" trim="${trim%"${trim##*[![:space:]]}"}" - printf '%s' "${trim}" + printf "%s" "${trim}" +} + +# status helper function +# +f_char() +{ + local result input="${1}" + + if [ "${input}" -eq "1" ] + then + result="✔" + else + result="✘" + fi + printf "%s" "${result}" +} + +# wifi reconf helper function +# +f_reconf() +{ + local radio cnt="0" + + "${trm_wifi}" reconf + for radio in ${trm_radiolist} + do + while [ "$(ubus -S call network.wireless status | jsonfilter -l1 -e "@.${radio}.up")" != "true" ] + do + if [ "${cnt}" -eq "$((trm_maxwait/2))" ] + then + if [ -x "/etc/init.d/wpad" ] + then + /etc/init.d/wpad restart + fi + fi + if [ "${cnt}" -ge "${trm_maxwait}" ] + then + break 2 + fi + cnt="$((cnt+1))" + sleep 1 + done + done + f_log "debug" "f_reconf ::: radio_list: ${trm_radiolist}, cnt: ${cnt}" +} + +# vpn helper function +# +f_vpn() +{ + local IFS action="${1}" + + if [ "${trm_vpn}" -eq "1" ] && [ -x "${trm_vpnpgm}" ] + then + "${trm_vpnpgm}" "${action}" >/dev/null 2>&1 + fi + f_log "debug" "f_vpn ::: vpn: ${trm_vpn}, vpnservice: ${trm_vpnservice:-"-"}, vpnpgm: ${trm_vpnpgm}, action: ${action}" +} + +# mac helper function +# +f_mac() +{ + local result ifname action="${1}" section="${2}" + + if [ "${trm_randomize}" -eq "1" ] && [ "${action}" = "set" ] + then + result="$(hexdump -n6 -ve '/1 "%.02X "' /dev/random 2>/dev/null | \ + awk -v local="2,6,A,E" -v seed="$(date +%s)" 'BEGIN{srand(seed)}NR==1{split(local,b,",");seed=int(rand()*4+1);printf "%s%s:%s:%s:%s:%s:%s",substr($1,0,1),b[seed],$2,$3,$4,$5,$6}')" + uci_set "wireless" "${section}" "macaddr" "${result}" + else + result="$(uci_get "wireless" "${section}" "macaddr")" + if [ -z "${result}" ] + then + ifname="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].ifname')" + result="$(${trm_iwinfo} "${ifname}" info 2>/dev/null | awk '/Access Point:/{printf "%s",$3}')" + fi + fi + printf "%s" "${result}" + f_log "debug" "f_mac ::: action: ${action:-"-"}, section: ${section:-"-"}, mac: ${result:-"-"}" +} + +# track/set travelmate connection information +# +f_contrack() +{ + local uplink_config radio_config essid_config bssid_config expiry action="${1}" radio="${2}" essid="${3}" bssid="${4}" cnt=0 + + while [ "$(uci_get "travelmate" "@uplink[$cnt]" >/dev/null 2>&1; echo $?)" -eq "0" ] + do + radio_config="$(uci_get "travelmate" "@uplink[$cnt]" "device")" + essid_config="$(uci_get "travelmate" "@uplink[$cnt]" "ssid")" + bssid_config="$(uci_get "travelmate" "@uplink[$cnt]" "bssid")" + if [ "${radio_config}" = "${radio}" ] && [ "${essid_config}" = "${essid}" ] && [ "${bssid_config}" = "${bssid}" ] + then + uplink_config="@uplink[$cnt]" + fi + cnt="$((cnt+1))" + done + if [ -n "${uplink_config}" ] + then + case "${action}" in + "start") + uci_remove "travelmate" "${uplink_config}" "con_start" 2>/dev/null + uci_remove "travelmate" "${uplink_config}" "con_end" 2>/dev/null + if [ -f "${trm_ntpfile}" ] + then + uci_set "travelmate" "${uplink_config}" "con_start" "$(date "+%Y.%m.%d-%H:%M:%S")" + fi + ;; + "refresh") + if [ -f "${trm_ntpfile}" ] && [ -z "$(uci_get "travelmate" "${uplink_config}" "con_start")" ] + then + uci_set "travelmate" "${uplink_config}" "con_start" "$(date "+%Y.%m.%d-%H:%M:%S")" + fi + ;; + "end") + if [ -f "${trm_ntpfile}" ] + then + uci_set "travelmate" "${uplink_config}" "con_end" "$(date "+%Y.%m.%d-%H:%M:%S")" + fi + ;; + "start_expiry") + if [ -f "${trm_ntpfile}" ] + then + expiry="$(uci_get "travelmate" "${uplink_config}" "con_start_expiry")" + uci_set "travelmate" "${uplink_config}" "enabled" "0" + uci_set "travelmate" "${uplink_config}" "con_end" "$(date "+%Y.%m.%d-%H:%M:%S")" + f_log "info" "uplink '${radio}/${essid}/${bssid:-"-"}' expired after ${expiry} minutes" + fi + ;; + "end_expiry") + if [ -f "${trm_ntpfile}" ] + then + expiry="$(uci_get "travelmate" "${uplink_config}" "con_end_expiry")" + uci_set "travelmate" "${uplink_config}" "enabled" "1" + uci_remove "travelmate" "${uplink_config}" "con_start" 2>/dev/null + uci_remove "travelmate" "${uplink_config}" "con_end" 2>/dev/null + f_log "info" "uplink '${radio}/${essid}/${bssid:-"-"}' re-enabled after ${expiry} minutes" + fi + ;; + "disabled") + uci_set "travelmate" "${uplink_config}" "enabled" "0" + if [ -f "${trm_ntpfile}" ] + then + uci_set "travelmate" "${uplink_config}" "con_end" "$(date "+%Y.%m.%d-%H:%M:%S")" + fi + ;; + esac + if [ -n "$(uci -q changes "travelmate")" ] + then + uci_commit "travelmate" + if [ ! -f "/var/run/travelmate.refresh" ] + then + printf "%s" "cfg_reload" > "/var/run/travelmate.refresh" + fi + fi + fi +} + +# get/match travelmate uplink option +# +f_uplink() +{ + local IFS result t_radio t_essid t_bssid t_option="${1}" w_radio="${2}" w_essid="${3}" w_bssid="${4}" cnt=0 + + while [ "$(uci_get "travelmate" "@uplink[$cnt]" >/dev/null 2>&1; echo $?)" -eq "0" ] + do + t_radio="$(uci_get "travelmate" "@uplink[$cnt]" "device")" + t_essid="$(uci_get "travelmate" "@uplink[$cnt]" "ssid")" + t_bssid="$(uci_get "travelmate" "@uplink[$cnt]" "bssid")" + if [ -n "${w_radio}" ] && [ -n "${w_essid}" ] && \ + [ "${t_radio}" = "${w_radio}" ] && [ "${t_essid}" = "${w_essid}" ] && [ "${t_bssid}" = "${w_bssid}" ] + then + result="$(uci_get "travelmate" "@uplink[$cnt]" "${t_option}")" + break + fi + cnt="$((cnt+1))" + done + printf "%s" "${result}" + f_log "debug" "f_uplink ::: option: ${t_option}, result: ${result}" } # prepare the 'wifi-device' sections # f_prepdev() { - local IFS disabled config="${1}" + local IFS disabled radio="${1}" - disabled="$(uci_get "wireless" "${config}" "disabled")" + disabled="$(uci_get "wireless" "${radio}" "disabled")" if [ "${disabled}" = "1" ] then - uci_set wireless "${config}" disabled 0 + uci_set wireless "${radio}" disabled 0 + fi + + if [ -z "${trm_radio}" ] && [ -z "$(printf "%s" "${trm_radiolist}" | grep -Fo "${radio}")" ] + then + trm_radiolist="$(f_trim "${trm_radiolist} ${radio}")" + elif [ -n "${trm_radio}" ] && [ -z "${trm_radiolist}" ] + then + trm_radiolist="$(f_trim "$(printf "%s" "${trm_radio}" | \ + awk '{while(match(tolower($0),/[a-z0-9_]+/)){ORS=" ";print substr(tolower($0),RSTART,RLENGTH);$0=substr($0,RSTART+RLENGTH)}}')")" + fi + f_log "debug" "f_prepdev ::: trm_radio: ${trm_radio:-"-"}, radio: ${radio}, radio_list: ${trm_radiolist:-"-"}, disabled: ${disabled:-"-"}" +} + +# add open uplink to new 'wifi-iface' section +# +f_addif() +{ + local IFS uci_cfg offset=1 radio="${1}" essid="${2}" + + config_cb() + { + local type="${1}" name="${2}" + if [ "${type}" = "wifi-iface" ] + then + if [ "$(uci -q get "wireless.${name}.ssid")" = "${essid}" ] + then + offset=0 + elif [ "${offset}" -ne "0" ] + then + offset="$((offset+1))" + fi + fi + return "${offset}" + } + config_load wireless + + if [ "${offset}" -ne "0" ] + then + uci_cfg="trm_uplink${offset}" + while [ -n "$(uci -q get "wireless.${uci_cfg}")" ] + do + offset="$((offset+1))" + uci_cfg="trm_uplink${offset}" + done + uci -q batch <<-EOC + set wireless."${uci_cfg}"="wifi-iface" + set wireless."${uci_cfg}".mode="sta" + set wireless."${uci_cfg}".network="${trm_iface}" + set wireless."${uci_cfg}".device="${radio}" + set wireless."${uci_cfg}".ssid="${essid}" + set wireless."${uci_cfg}".encryption="none" + set wireless."${uci_cfg}".disabled="1" + EOC + uci_cfg="$(uci -q add travelmate uplink)" + uci -q batch <<-EOC + set travelmate."${uci_cfg}".device="${radio}" + set travelmate."${uci_cfg}".ssid="${essid}" + set travelmate."${uci_cfg}".con_start_expiry="0" + set travelmate."${uci_cfg}".con_end_expiry="0" + set travelmate."${uci_cfg}".enabled="1" + EOC + if [ -n "$(uci -q changes "travelmate")" ] || [ -n "$(uci -q changes "wireless")" ] + then + uci_commit "travelmate" + uci_commit "wireless" + f_reconf + if [ ! -f "/var/run/travelmate.refresh" ] + then + printf "%s" "ui_reload" > "/var/run/travelmate.refresh" + fi + f_log "info" "open uplink '${radio}/${essid}' added to wireless config" + fi fi - f_log "debug" "f_prepdev ::: config: ${config}, disabled: ${disabled:-"-"}" + f_log "debug" "f_addif ::: radio: ${radio:-"-"}, essid: ${essid}, offset: ${offset:-"-"}" } # prepare the 'wifi-iface' sections # f_prepif() { - local IFS mode network radio encryption eaptype disabled config="${1}" proactive="${2}" - - mode="$(uci_get "wireless" "${config}" "mode")" - network="$(uci_get "wireless" "${config}" "network")" - radio="$(uci_get "wireless" "${config}" "device")" - encryption="$(uci_get "wireless" "${config}" "encryption")" - eaptype="$(uci_get "wireless" "${config}" "eap_type")" - disabled="$(uci_get "wireless" "${config}" "disabled")" - if [ -n "${config}" ] && [ -n "${radio}" ] && [ -n "${mode}" ] && [ -n "${network}" ] + local IFS mode radio essid bssid disabled status con_start con_end con_start_expiry con_end_expiry section="${1}" proactive="${2}" + + mode="$(uci_get "wireless" "${section}" "mode")" + radio="$(uci_get "wireless" "${section}" "device")" + essid="$(uci_get "wireless" "${section}" "ssid")" + bssid="$(uci_get "wireless" "${section}" "bssid")" + disabled="$(uci_get "wireless" "${section}" "disabled")" + status="$(f_uplink "enabled" "${radio}" "${essid}" "${bssid}")" + con_start="$(f_uplink "con_start" "${radio}" "${essid}" "${bssid}")" + con_end="$(f_uplink "con_end" "${radio}" "${essid}" "${bssid}")" + con_start_expiry="$(f_uplink "con_start_expiry" "${radio}" "${essid}" "${bssid}")" + con_end_expiry="$(f_uplink "con_end_expiry" "${radio}" "${essid}" "${bssid}")" + + if [ "${status}" = "0" ] && [ -n "${con_end}" ] && [ -n "${con_end_expiry}" ] && [ "${con_end_expiry}" != "0" ] then - if [ -z "${trm_radio}" ] && [ -z "$(printf "%s" "${trm_radiolist}" | grep -Fo "${radio}")" ] + d1="$(date -d "${con_end}" "+%s")" + d2="$(date "+%s")" + d3="$(((d2-d1)/60))" + if [ "${d3}" -ge "${con_end_expiry}" ] then - trm_radiolist="$(f_trim "${trm_radiolist} ${radio}")" - elif [ -n "${trm_radio}" ] && [ -z "${trm_radiolist}" ] + status="1" + f_contrack "end_expiry" "${radio}" "${essid}" "${bssid}" + fi + elif [ "${status}" = "1" ] && [ -n "${con_start}" ] && [ -n "${con_start_expiry}" ] && [ "${con_start_expiry}" != "0" ] + then + d1="$(date -d "${con_start}" "+%s")" + d2="$(date "+%s")" + d3="$((d1+(con_start_expiry*60)))" + if [ "${d2}" -gt "${d3}" ] then - trm_radiolist="$(f_trim "$(printf "%s" "${trm_radio}" | \ - awk '{while(match(tolower($0),/[a-z0-9]+/)){ORS=" ";print substr(tolower($0),RSTART,RLENGTH);$0=substr($0,RSTART+RLENGTH)}}')")" + status="0" + f_contrack "start_expiry" "${radio}" "${essid}" "${bssid}" fi - if [ "${mode}" = "sta" ] && [ "${network}" = "${trm_iface}" ] + fi + + if [ "${mode}" = "sta" ] + then + if [ "${status}" = "0" ] || \ + { { [ -z "${disabled}" ] || [ "${disabled}" = "0" ]; } && { [ "${proactive}" -eq "0" ] || [ "${trm_ifstatus}" != "true" ]; } } then - if { [ -z "${disabled}" ] || [ "${disabled}" = "0" ]; } && { [ "${proactive}" -eq 0 ] || [ "${trm_ifstatus}" != "true" ]; } - then - uci_set wireless "${config}" disabled 1 - elif [ "${disabled}" = "0" ] && [ "${trm_ifstatus}" = "true" ] && [ "${proactive}" -eq 1 ] - then - if [ -z "${trm_active_sta}" ] - then - trm_active_sta="${config}" - else - uci_set wireless "${config}" disabled 1 - fi - fi - if [ -z "${eaptype}" ] || { [ -n "${eaptype}" ] && [ "${trm_eapcheck}" -eq 0 ]; } + uci_set "wireless" "${section}" "disabled" "1" + elif [ "${disabled}" = "0" ] && [ "${trm_ifstatus}" = "true" ] && [ "${proactive}" -eq "1" ] + then + if [ -z "${trm_activesta}" ] then - if { [ "${encryption%-*}" != "sae" ] && [ "${encryption%-*}" != "wpa3" ] && [ "${encryption}" != "owe" ]; } || \ - { { [ "${encryption%-*}" = "sae" ] || [ "${encryption%-*}" = "wpa3" ]; } && [ "${trm_saecheck}" -eq 0 ]; } || \ - { [ "${encryption}" = "owe" ] && [ "${trm_owecheck}" -eq 0 ]; } - then - trm_stalist="$(f_trim "${trm_stalist} ${config}-${radio}")" - fi + trm_activesta="${section}" + else + uci_set "wireless" "${section}" "disabled" "1" fi fi + if [ "${status}" = "1" ] + then + trm_stalist="$(f_trim "${trm_stalist} ${section}-${radio}")" + fi fi - f_log "debug" "f_prepif ::: config: ${config}, mode: ${mode}, network: ${network}, radio: ${radio}, trm_radio: ${trm_radio:-"-"}, trm_active_sta: ${trm_active_sta:-"-"}, proactive: ${proactive}, disabled: ${disabled}" + f_log "debug" "f_prepif ::: status: ${status}, section: ${section}, active_sta: ${trm_activesta:-"-"}" } # check net status # f_net() { - local IFS raw result + local IFS err err_rc err_domain json_raw json_cp json_rc cp_domain result="net nok" - raw="$(${trm_fetch} --timeout=$((trm_maxwait/6)) "${trm_captiveurl}" -O /dev/null 2>&1 | tail -n 1)" - raw="$(printf "%s" "${raw//[\?\$\%\&\+\|\'\"\:\*\=\/]/ }")" - result="$(printf "%s" "${raw}" | awk '/^Failed to redirect|^Redirected/{printf "%s","net cp";exit}/^Download completed/{printf "%s","net ok";exit}/^Failed|Connection error/{printf "%s","net nok";exit}')" - if [ "${result}" = "net cp" ] + json_raw="$(${trm_fetch} --user-agent "${trm_useragent}" --referer "http://www.example.com" --write-out "%{json}" --silent --show-error --connect-timeout $((trm_maxwait/10)) "${trm_captiveurl}" 2>/tmp/trm_fetch.err)" + json_raw="${json_raw#*\{}" + if [ -s "/tmp/trm_fetch.err" ] then - result="$(printf "%s" "${raw//*on /}" | awk 'match($0,/^([[:alnum:]_-]+\.)+[[:alpha:]]+/){printf "%s","net cp \047"substr(tolower($0),RSTART,RLENGTH)"\047"}')" + err="$(awk 'BEGIN{FS="[()'\'' ]"}{printf "%s %s",$3,$(NF-1)}' "/tmp/trm_fetch.err")" + err_rc="${err% *}" + err_domain="${err#* }" + if [ "${err_rc}" -eq "6" ] + then + if [ -n "${err_domain}" ] && [ "${err_domain}" != "timed" ] && [ "${err_domain}" != "${trm_captiveurl#http*://*}" ] + then + result="net cp '${err_domain}'" + fi + fi + elif [ -n "${json_raw}" ] + then + json_cp="$(printf "%s" "{${json_raw}" | jsonfilter -l1 -e '@.redirect_url' 2>/dev/null)" + json_rc="$(printf "%s" "{${json_raw}" | jsonfilter -l1 -e '@.response_code' 2>/dev/null)" + if [ -n "${json_cp}" ] + then + cp_domain="${json_cp#http*://*}" + cp_domain="${cp_domain%%/*}" + result="net cp '${cp_domain}'" + else + if [ "${json_rc}" = "200" ] || [ "${json_rc}" = "204" ] + then + result="net ok" + fi + fi fi + rm -f "/tmp/trm_fetch.err" printf "%s" "${result}" - f_log "debug" "f_net ::: fetch: ${trm_fetch}, timeout: $((trm_maxwait/6)), url: ${trm_captiveurl}, result: ${result}" + f_log "debug" "f_net ::: fetch: ${trm_fetch}, timeout: $((trm_maxwait/6)), url: ${trm_captiveurl}, user_agent: ${trm_useragent}, result: ${result}, error: ${err:-"-"}" } # check interface status # f_check() { - local IFS ifname radio dev_status result uci_section login_command login_command_args wait_time=1 mode="${1}" status="${2:-"false"}" cp_domain="${3:-"false"}" + local IFS ifname radio dev_status result login_script login_script_args cp_domain wait_time="1" enabled="1" mode="${1}" status="${2}" sta_radio="${3}" sta_essid="${4}" sta_bssid="${5}" - if [ "${mode}" != "initial" ] && [ "${mode}" != "dev" ] && [ "${status}" = "false" ] + if [ "${mode}" = "initial" ] || [ "${mode}" = "dev" ] then - "${trm_wifi}" "${trm_wificmd}" - sleep $((trm_maxwait/6)) + json_get_var station_id "station_id" + sta_radio="${station_id%%/*}" + sta_essid="${station_id%/*}" + sta_essid="${sta_essid#*/}" + sta_bssid="${station_id##*/}" + sta_bssid="${sta_bssid//-/}" + fi + if [ "${mode}" != "rev" ] && [ -n "${sta_radio}" ] && [ "${sta_radio}" != "-" ] && [ -n "${sta_essid}" ] && [ "${sta_essid}" != "-" ] + then + enabled="$(f_uplink "enabled" "${sta_radio}" "${sta_essid}" "${sta_bssid}")" + fi + if { [ "${mode}" != "initial" ] && [ "${mode}" != "dev" ] && [ "${status}" = "false" ]; } || \ + { [ "${mode}" = "dev" ] && { [ "${status}" = "false" ] || { [ "${trm_ifstatus}" != "${status}" ] && [ "${enabled}" -eq "0" ]; }; }; } + then + f_reconf fi - while [ "${wait_time}" -le "${trm_maxwait}" ] do dev_status="$(ubus -S call network.wireless status 2>/dev/null)" @@ -263,95 +599,60 @@ f_check() trm_ifstatus="${status}" f_jsnup fi - for radio in ${trm_radiolist} - do - result="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e "@.${radio}.up")" - if [ "${result}" = "true" ] && [ -z "$(printf "%s" "${trm_devlist}" | grep -Fo "${radio}")" ] - then - trm_devlist="$(f_trim "${trm_devlist} ${radio}")" - fi - done - if [ "${trm_devlist}" = "${trm_radiolist}" ] || [ "${wait_time}" -eq "${trm_maxwait}" ] + if [ "${status}" = "false" ] then - ifname="${trm_devlist}" - break - else - unset trm_devlist + sleep "$((trm_maxwait/5))" fi + break elif [ "${mode}" = "rev" ] then break else ifname="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].ifname')" - if [ -n "${ifname}" ] + if [ -n "${ifname}" ] && [ "${enabled}" -eq "1" ] then - trm_ifquality="$(${trm_iwinfo} "${ifname}" info 2>/dev/null | awk -F "[ ]" '/Link Quality:/{split($NF,var0,"/");printf "%i\n",(var0[1]*100/var0[2])}')" - if [ "${mode}" = "initial" ] && [ "${trm_captive}" -eq 1 ] - then - result="$(f_net)" - if [ "${cp_domain}" = "true" ] - then - cp_domain="$(printf "%s" "${result}" | awk -F "[\\'| ]" '/^net cp/{printf "%s" $4}')" - uci_section="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')" - fi - fi - if [ "${trm_ifquality}" -ge "${trm_minquality}" ] && [ "${result}" != "net nok" ] + result="$(f_net)" + trm_ifquality="$(${trm_iwinfo} "${ifname}" info 2>/dev/null | awk -F '[ ]' '/Link Quality:/{split($NF,var0,"/");printf "%i\n",(var0[1]*100/var0[2])}')" + if [ "${trm_ifquality}" -ge "${trm_minquality}" ] then trm_ifstatus="$(ubus -S call network.interface dump 2>/dev/null | jsonfilter -l1 -e "@.interface[@.device=\"${ifname}\"].up")" if [ "${trm_ifstatus}" = "true" ] then - if [ "${mode}" = "sta" ] && [ "${trm_captive}" -eq 1 ] + if [ "${trm_captive}" -eq "1" ] then - while true - do - result="$(f_net)" - cp_domain="$(printf "%s" "${result}" | awk -F "[\\'| ]" '/^net cp/{printf "%s" $4}')" - uci_section="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')" - if [ "${trm_netcheck}" -eq 1 ] && [ "${result}" = "net nok" ] - then - trm_ifstatus="${status}" - f_jsnup - break 2 - fi - if [ -z "${cp_domain}" ] || [ -n "$(uci_get "dhcp" "@dnsmasq[0]" "rebind_domain" | grep -Fo "${cp_domain}")" ] - then - break - fi - uci -q add_list dhcp.@dnsmasq[0].rebind_domain="${cp_domain}" - f_log "info" "captive portal domain '${cp_domain}' added to to dhcp rebind whitelist" - if [ -z "$(uci_get "travelmate" "${uci_section}")" ] - then - uci_add travelmate "login" "${uci_section}" - uci_set travelmate "${uci_section}" "command" "none" - f_log "info" "captive portal login section '${uci_section}' added to travelmate config section" - fi - done - if [ -n "$(uci -q changes "dhcp")" ] + cp_domain="$(printf "%s" "${result}" | awk -F '['\''| ]' '/^net cp/{printf "%s",$4}')" + if [ -n "${cp_domain}" ] && [ -z "$(uci_get "dhcp" "@dnsmasq[0]" "rebind_domain" | grep -Fo "${cp_domain}")" ] then + uci_add_list "dhcp" "@dnsmasq[0]" "rebind_domain" "${cp_domain}" uci_commit "dhcp" /etc/init.d/dnsmasq reload + f_log "info" "captive portal domain '${cp_domain}' added to to dhcp rebind whitelist" fi - if [ -n "$(uci -q changes "travelmate")" ] + if [ -n "${cp_domain}" ] && [ "${trm_captive}" -eq "1" ] then - uci_commit "travelmate" + trm_connection="${result:-"-"}/${trm_ifquality}" + f_jsnup + login_script="$(f_uplink "script" "${sta_radio}" "${sta_essid}" "${sta_bssid}")" + if [ -x "${login_script}" ] + then + login_script_args="$(f_uplink "script_args" "${sta_radio}" "${sta_essid}" "${sta_bssid}")" + "${login_script}" ${login_script_args} >/dev/null 2>&1 + rc=${?} + f_log "info" "captive portal login '${login_script:0:40} ${login_script_args:0:20}' for '${cp_domain}' has been executed with rc '${rc}'" + if [ "${rc}" -eq "0" ] + then + result="$(f_net)" + fi + fi fi fi - if [ -n "${cp_domain}" ] && [ "${cp_domain}" != "false" ] && [ -n "${uci_section}" ] && [ "${trm_captive}" -eq 1 ] + if [ "${trm_netcheck}" -eq "1" ] && [ "${result}" = "net nok" ] then - trm_connection="${result:-"-"}/${trm_ifquality}" + f_log "info" "uplink has no internet (new connection)" + f_vpn "disable" + trm_ifstatus="${status}" f_jsnup - login_command="$(uci_get "travelmate" "${uci_section}" "command")" - if [ -x "${login_command}" ] - then - login_command_args="$(uci_get "travelmate" "${uci_section}" "command_args")" - "${login_command}" ${login_command_args} >/dev/null 2>&1 - rc=${?} - f_log "info" "captive portal login '${login_command:0:40} ${login_command_args:0:20}' for '${cp_domain}' has been executed with rc '${rc}'" - if [ "${rc}" -eq 0 ] - then - result="$(f_net)" - fi - fi + break fi trm_connection="${result:-"-"}/${trm_ifquality}" f_jsnup @@ -359,72 +660,89 @@ f_check() fi elif [ -n "${trm_connection}" ] then - uci_section="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')" if [ "${trm_ifquality}" -lt "${trm_minquality}" ] then + f_log "info" "uplink is out of range (${trm_ifquality}/${trm_minquality})" + f_vpn "disable" unset trm_connection trm_ifstatus="${status}" - f_log "info" "uplink '${uci_section}' is out of range (${trm_ifquality}/${trm_minquality})" - elif [ "${trm_netcheck}" -eq 1 ] && [ "${result}" = "net nok" ] + f_contrack "end" "${sta_radio}" "${sta_essid}" "${sta_bssid}" + elif [ "${trm_netcheck}" -eq "1" ] && [ "${result}" = "net nok" ] then + f_log "info" "uplink has no internet (existing connection)" + f_vpn "disable" unset trm_connection trm_ifstatus="${status}" - f_log "info" "uplink '${uci_section}' has no internet (${result})" fi f_jsnup break elif [ "${mode}" = "initial" ] then + trm_ifstatus="${status}" f_jsnup break fi elif [ -n "${trm_connection}" ] then + f_vpn "disable" unset trm_connection trm_ifstatus="${status}" f_jsnup break elif [ "${mode}" = "initial" ] then + trm_ifstatus="${status}" f_jsnup break fi fi fi - wait_time=$((wait_time+1)) + if [ "${mode}" = "initial" ] + then + trm_ifstatus="${status}" + f_jsnup + break + fi + wait_time="$((wait_time+1))" sleep 1 done - f_log "debug" "f_check ::: mode: ${mode}, name: ${ifname:-"-"}, status: ${trm_ifstatus}, connection: ${trm_connection:-"-"}, wait: ${wait_time}, max_wait: ${trm_maxwait}, min_quality: ${trm_minquality}, captive: ${trm_captive}, netcheck: ${trm_netcheck}" + f_log "debug" "f_check ::: mode: ${mode}, name: ${ifname:-"-"}, status: ${trm_ifstatus}, enabled: ${enabled}, connection: ${trm_connection:-"-"}, wait: ${wait_time}, max_wait: ${trm_maxwait}, min_quality: ${trm_minquality}, captive: ${trm_captive}, netcheck: ${trm_netcheck}" } # update runtime information # f_jsnup() { - local IFS uci_section d1 d2 d3 last_date last_station sta_iface sta_radio sta_essid sta_bssid last_status dev_status wpa_status status="${trm_ifstatus}" faulty_list faulty_station="${1}" - - dev_status="$(ubus -S call network.wireless status 2>/dev/null)" - if [ -n "${dev_status}" ] - then - uci_section="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')" - if [ -n "${uci_section}" ] - then - sta_iface="$(uci_get "wireless" "${uci_section}" "network")" - sta_radio="$(uci_get "wireless" "${uci_section}" "device")" - sta_essid="$(uci_get "wireless" "${uci_section}" "ssid")" - sta_bssid="$(uci_get "wireless" "${uci_section}" "bssid")" - fi - fi + local IFS section bg_pid last_date last_station sta_iface sta_radio sta_essid sta_bssid sta_mac dev_status last_status status="${trm_ifstatus}" ntp_sync="0" - json_get_var last_date "last_rundate" - json_get_var last_station "station_id" if [ "${status}" = "true" ] then status="connected (${trm_connection:-"-"})" + dev_status="$(ubus -S call network.wireless status 2>/dev/null)" + if [ -n "${dev_status}" ] + then + section="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')" + if [ -n "${section}" ] + then + sta_iface="$(uci_get "wireless" "${section}" "network")" + sta_radio="$(uci_get "wireless" "${section}" "device")" + sta_essid="$(uci_get "wireless" "${section}" "ssid")" + sta_bssid="$(uci_get "wireless" "${section}" "bssid")" + sta_mac="$(f_mac "get" "${section}")" + fi + fi + json_get_var last_date "last_run" + json_get_var last_station "station_id" json_get_var last_status "travelmate_status" - if [ "${last_status}" = "running / not connected" ] || [ "${last_station}" != "${sta_radio:-"-"}/${sta_essid:-"-"}/${sta_bssid:-"-"}" ] + + if { [ -f "${trm_ntpfile}" ] && [ ! -s "${trm_ntpfile}" ]; } || [ "${last_status}" = "running (not connected)" ] || \ + { [ -n "${last_station}" ] && [ "${last_station}" != "${sta_radio:-"-"}/${sta_essid:-"-"}/${sta_bssid:-"-"}" ]; } then last_date="$(date "+%Y.%m.%d-%H:%M:%S")" + if [ -f "${trm_ntpfile}" ] && [ ! -s "${trm_ntpfile}" ] + then + printf "%s" "${last_date}" > "${trm_ntpfile}" + fi fi elif [ "${status}" = "error" ] then @@ -432,62 +750,44 @@ f_jsnup() status="program error" else unset trm_connection - status="running / not connected" + status="running (not connected)" fi + if [ -z "${last_date}" ] then last_date="$(date "+%Y.%m.%d-%H:%M:%S")" fi - json_get_var faulty_list "faulty_stations" - if [ -n "${faulty_list}" ] && [ "${trm_listexpiry}" -gt 0 ] + if [ -s "${trm_ntpfile}" ] then - d1="$(date -d "${last_date}" "+%s")" - d2="$(date "+%s")" - d3=$(((d2 - d1)/60)) - if [ "${d3}" -ge "${trm_listexpiry}" ] - then - faulty_list="" - fi + ntp_sync="1" fi - if [ -n "${faulty_station}" ] - then - if [ -z "$(printf "%s" "${faulty_list}" | grep -Fo "${faulty_station}")" ] - then - faulty_list="$(f_trim "${faulty_list} ${faulty_station}")" - last_date="$(date "+%Y.%m.%d-%H:%M:%S")" - fi - fi - - if [ "${trm_eapcheck}" -eq 0 ] - then - wpa_status="EAP" - else - wpa_status="-" - fi - if [ "${trm_saecheck}" -eq 0 ] - then - wpa_status="${wpa_status}/SAE" - else - wpa_status="${wpa_status}/-" - fi - if [ "${trm_owecheck}" -eq 0 ] - then - wpa_status="${wpa_status}/OWE" - else - wpa_status="${wpa_status}/-" - fi json_add_string "travelmate_status" "${status}" json_add_string "travelmate_version" "${trm_ver}" json_add_string "station_id" "${sta_radio:-"-"}/${sta_essid:-"-"}/${sta_bssid:-"-"}" + json_add_string "station_mac" "${sta_mac:-"-"}" json_add_string "station_interface" "${sta_iface:-"-"}" - json_add_string "faulty_stations" "${faulty_list}" - json_add_string "wpa_capabilities" "${wpa_status:-"-"}" - json_add_string "last_rundate" "${last_date}" + json_add_string "wpa_flags" "${trm_wpaflags:-"-"}" + json_add_string "run_flags" "captive: $(f_char ${trm_captive}), proactive: $(f_char ${trm_proactive}), netcheck: $(f_char ${trm_netcheck}), autoadd: $(f_char ${trm_autoadd}), randomize: $(f_char ${trm_randomize})" + json_add_string "ext_hooks" "ntp: $(f_char ${ntp_sync}), vpn: $(f_char ${trm_vpn}), mail: $(f_char ${trm_mail})" + json_add_string "last_run" "${last_date}" json_add_string "system" "${trm_sysver}" json_dump > "${trm_rtfile}" - f_log "debug" "f_jsnup ::: uci_section: ${uci_section:-"-"}, status: ${status:-"-"}, sta_iface: ${sta_iface:-"-"}, sta_radio: ${sta_radio:-"-"}, sta_essid: ${sta_essid:-"-"}, sta_bssid: ${sta_bssid:-"-"}, faulty_list: ${faulty_list:-"-"}, list_expiry: ${trm_listexpiry}" + + if [ "${status%% (net ok/*}" = "connected" ] + then + f_vpn "enable" + if [ "${trm_mail}" -eq "1" ] && [ -x "${trm_mailpgm}" ] && [ -s "${trm_ntpfile}" ] && [ ! -f "${trm_mailfile}" ] + then + > "${trm_mailfile}" + ( "${trm_mailpgm}" >/dev/null 2>&1 )& + bg_pid="${!}" + fi + else + f_vpn "disable" + fi + f_log "debug" "f_jsnup ::: section: ${section:-"-"}, status: ${status:-"-"}, sta_iface: ${sta_iface:-"-"}, sta_radio: ${sta_radio:-"-"}, sta_essid: ${sta_essid:-"-"}, sta_bssid: ${sta_bssid:-"-"}, vpn: ${trm_vpn}, mail: ${trm_mail}, mail_pid: ${bg_pid:-"-"}" } # write to syslog @@ -496,13 +796,13 @@ f_log() { local IFS class="${1}" log_msg="${2}" - if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${trm_debug}" -eq 1 ]; } + if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${trm_debug}" -eq "1" ]; } then if [ -x "${trm_logger}" ] then - "${trm_logger}" -p "${class}" -t "travelmate-${trm_ver}[${$}]" "${log_msg}" + "${trm_logger}" -p "${class}" -t "trm-${trm_ver}[${$}]" "${log_msg}" else - printf "%s %s %s\\n" "${class}" "travelmate-${trm_ver}[${$}]" "${log_msg}" + printf "%s %s %s\\n" "${class}" "trm-${trm_ver}[${$}]" "${log_msg}" fi if [ "${class}" = "err" ] then @@ -518,74 +818,73 @@ f_log() # f_main() { - local IFS cnt dev config spec scan_dev scan_list scan_essid scan_bssid scan_open scan_quality uci_essid cfg_essid faulty_list - local station_id sta sta_essid sta_bssid sta_radio sta_iface active_essid active_bssid active_radio + local IFS cnt retrycnt spec scan_dev scan_list scan_essid scan_bssid scan_open scan_quality + local station_id section sta sta_essid sta_bssid sta_radio sta_iface sta_mac config_essid config_bssid config_radio - f_check "initial" "false" "true" + f_check "initial" "false" f_log "debug" "f_main ::: status: ${trm_ifstatus}, proactive: ${trm_proactive}" - if [ "${trm_ifstatus}" != "true" ] || [ "${trm_proactive}" -eq 1 ] + if [ "${trm_ifstatus}" != "true" ] || [ "${trm_proactive}" -eq "1" ] then config_load wireless config_foreach f_prepif wifi-iface ${trm_proactive} - if [ "${trm_ifstatus}" = "true" ] && [ -n "${trm_active_sta}" ] && [ "${trm_proactive}" -eq 1 ] + if [ "${trm_ifstatus}" = "true" ] && [ -n "${trm_activesta}" ] && [ "${trm_proactive}" -eq "1" ] then json_get_var station_id "station_id" - active_radio="${station_id%%/*}" - active_essid="${station_id%/*}" - active_essid="${active_essid#*/}" - active_bssid="${station_id##*/}" + config_radio="${station_id%%/*}" + config_essid="${station_id%/*}" + config_essid="${config_essid#*/}" + config_bssid="${station_id##*/}" + config_bssid="${config_bssid//-/}" f_check "dev" "true" - f_log "debug" "f_main ::: active_radio: ${active_radio}, active_essid: \"${active_essid}\", active_bssid: ${active_bssid:-"-"}" + f_log "debug" "f_main ::: config_radio: ${config_radio}, config_essid: \"${config_essid}\", config_bssid: ${config_bssid:-"-"}" else uci_commit "wireless" - f_check "dev" + f_check "dev" "false" fi - json_get_var faulty_list "faulty_stations" - f_log "debug" "f_main ::: iwinfo: ${trm_iwinfo:-"-"}, dev_list: ${trm_devlist:-"-"}, sta_list: ${trm_stalist:0:${trm_scanbuffer}}, faulty_list: ${faulty_list:-"-"}" + f_log "debug" "f_main ::: radio_list: ${trm_radiolist}, sta_list: ${trm_stalist:0:${trm_scanbuffer}}" + # radio loop # - for dev in ${trm_devlist} + for radio in ${trm_radiolist} do - if [ -z "$(printf "%s" "${trm_stalist}" | grep -o "\\-${dev}")" ] + if [ -z "$(printf "%s" "${trm_stalist}" | grep -o "\\-${radio}")" ] then - f_log "debug" "f_main ::: no station on '${dev}' - continue" + f_log "info" "no station on radio '${radio}'" continue fi + # station loop # for sta in ${trm_stalist} do - config="${sta%%-*}" - sta_radio="${sta##*-}" - sta_essid="$(uci_get "wireless" "${config}" "ssid")" - sta_bssid="$(uci_get "wireless" "${config}" "bssid")" - sta_iface="$(uci_get "wireless" "${config}" "network")" - json_get_var faulty_list "faulty_stations" - if [ -n "$(printf "%s" "${faulty_list}" | grep -Fo "${sta_radio}/${sta_essid}/${sta_bssid}")" ] + section="${sta%%-*}" + sta_radio="$(uci_get "wireless" "${section}" "device")" + sta_essid="$(uci_get "wireless" "${section}" "ssid")" + sta_bssid="$(uci_get "wireless" "${section}" "bssid")" + sta_iface="$(uci_get "wireless" "${section}" "network")" + sta_mac="$(f_mac "get" "${section}")" + if [ "${sta_radio}" = "${config_radio}" ] && [ "${sta_essid}" = "${config_essid}" ] && [ "${sta_bssid}" = "${config_bssid}" ] then - f_log "debug" "f_main ::: faulty station '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' - continue" - continue - fi - if [ "${dev}" = "${active_radio}" ] && [ "${sta_essid}" = "${active_essid}" ] && [ "${sta_bssid:-"-"}" = "${active_bssid}" ] - then - f_log "debug" "f_main ::: active station prioritized '${active_radio}/${active_essid}/${active_bssid:-"-"}' - break" + f_contrack "refresh" "${config_radio}" "${config_essid}" "${config_bssid}" + f_log "info" "uplink still in range '${config_radio}/${config_essid}/${config_bssid:-"-"}' with mac '${sta_mac:-"-"}'" break 2 fi f_log "debug" "f_main ::: sta_radio: ${sta_radio}, sta_essid: \"${sta_essid}\", sta_bssid: ${sta_bssid:-"-"}" if [ -z "${scan_list}" ] then - scan_dev="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -l1 -e "@.${dev}.interfaces[@.config.mode=\"sta\"].ifname")" - scan_list="$("${trm_iwinfo}" "${scan_dev:-${dev}}" scan 2>/dev/null | \ + scan_dev="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -l1 -e "@.${radio}.interfaces[0].ifname")" + scan_list="$("${trm_iwinfo}" "${scan_dev:-${radio}}" scan 2>/dev/null | \ awk 'BEGIN{FS="[[:space:]]"}/Address:/{var1=$NF}/ESSID:/{var2="";for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i}; gsub(/,/,".",var2)}/Quality:/{split($NF,var0,"/")}/Encryption:/{if($NF=="none"){var3="+"}else{var3="-"};printf "%i,%s,%s,%s\n",(var0[1]*100/var0[2]),var1,var2,var3}' | \ sort -rn | awk -v buf="${trm_scanbuffer}" 'BEGIN{ORS=","}{print substr($0,1,buf)}')" - f_log "debug" "f_main ::: scan_radio: ${dev}, scan_device: ${scan_dev:-"-"}, scan_buffer: ${trm_scanbuffer}, scan_list: ${scan_list:-"-"}" + f_log "debug" "f_main ::: radio: ${radio}, scan_device: ${scan_dev}, scan_buffer: ${trm_scanbuffer}, scan_list: ${scan_list:-"-"}" if [ -z "${scan_list}" ] then - f_log "debug" "f_main ::: no scan results on '${dev}/${scan_dev:-"-"}' - continue" + f_log "info" "no scan results on '${radio}'" continue 2 fi fi + # scan loop # IFS="," @@ -609,64 +908,64 @@ f_main() if [ "${scan_quality}" -ge "${trm_minquality}" ] then if { { [ "${scan_essid}" = "\"${sta_essid//,/.}\"" ] && { [ -z "${sta_bssid}" ] || [ "${scan_bssid}" = "${sta_bssid}" ]; } } || \ - { [ "${scan_bssid}" = "${sta_bssid}" ] && [ "${scan_essid}" = "unknown" ]; } } && [ "${dev}" = "${sta_radio}" ] + { [ "${scan_bssid}" = "${sta_bssid}" ] && [ "${scan_essid}" = "unknown" ]; } } && [ "${radio}" = "${sta_radio}" ] then + f_vpn "disable" f_log "debug" "f_main ::: scan_quality: ${scan_quality}, scan_essid: ${scan_essid}, scan_bssid: ${scan_bssid:-"-"}, scan_open: ${scan_open}" - if [ -n "${active_radio}" ] + if [ -n "${config_radio}" ] then - uci_set "wireless" "${trm_active_sta}" "disabled" "1" + uci_set "wireless" "${trm_activesta}" "disabled" "1" uci_commit "wireless" - f_log "debug" "f_main ::: active uplink connection '${active_radio}/${active_essid}/${active_bssid:-"-"}' terminated" - unset trm_connection active_radio active_essid active_bssid + f_contrack "end" "${config_radio}" "${config_essid}" "${config_bssid}" + f_log "info" "uplink connection terminated '${config_radio}/${config_essid}/${config_bssid:-"-"}'" + unset trm_connection config_radio config_essid config_bssid fi + # retry loop # - cnt=1 - while [ "${cnt}" -le "${trm_maxretry}" ] + retrycnt=1 + trm_radio="${sta_radio}" + while [ "${retrycnt}" -le "${trm_maxretry}" ] do - uci_set "wireless" "${config}" "disabled" "0" - trm_radio="${sta_radio}" - f_check "sta" + if [ "${trm_randomize}" -eq "1" ] + then + sta_mac="$(f_mac "set" "${section}")" + fi + uci_set "wireless" "${section}" "disabled" "0" + f_check "sta" "false" "${sta_radio}" "${sta_essid}" "${sta_bssid}" if [ "${trm_ifstatus}" = "true" ] then unset IFS scan_list + rm -f "${trm_mailfile}" uci_commit "wireless" - f_log "info" "connected to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}, ${trm_sysver})" + f_contrack "start" "${sta_radio}" "${sta_essid}" "${sta_bssid}" + if [ "${trm_randomize}" -eq "0" ] + then + sta_mac="$(f_mac "get" "${section}")" + fi + f_log "info" "connected to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' with mac '${sta_mac:-"-"}' (${retrycnt}/${trm_maxretry})" return 0 else uci -q revert "wireless" - f_check "rev" - if [ "${cnt}" -eq "${trm_maxretry}" ] + f_check "rev" "false" + if [ "${retrycnt}" -eq "${trm_maxretry}" ] then - faulty_station="${sta_radio}/${sta_essid}/${sta_bssid:-"-"}" - f_jsnup "${faulty_station}" - f_log "info" "uplink disabled '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}, ${trm_sysver})" + f_contrack "disabled" "${sta_radio}" "${sta_essid}" "${sta_bssid}" + f_log "info" "uplink has been disabled '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${retrycnt}/${trm_maxretry})" break 2 else f_jsnup - f_log "info" "can't connect to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}, ${trm_sysver})" + f_log "info" "can't connect to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${retrycnt}/${trm_maxretry})" fi fi - cnt=$((cnt+1)) - sleep $((trm_maxwait/6)) + retrycnt="$((retrycnt+1))" + sleep "$((trm_maxwait/6))" done - elif [ "${trm_autoadd}" -eq 1 ] && [ "${scan_open}" = "+" ] && [ "${scan_essid}" != "unknown" ] + elif [ "${trm_autoadd}" -eq "1" ] && [ "${scan_open}" = "+" ] && [ "${scan_essid}" != "unknown" ] then - cfg_essid="${scan_essid#*\"}" - cfg_essid="${cfg_essid%\"*}" - uci_essid="${cfg_essid//[^[:alnum:]_]/_}" - if [ -z "$(uci_get "wireless" "trm_${uci_essid}")" ] - then - uci_add "wireless" "wifi-iface" "trm_${uci_essid}" - uci_set "wireless" "trm_${uci_essid}" "mode" "sta" - uci_set "wireless" "trm_${uci_essid}" "network" "${trm_iface}" - uci_set "wireless" "trm_${uci_essid}" "device" "${sta_radio}" - uci_set "wireless" "trm_${uci_essid}" "ssid" "${cfg_essid}" - uci_set "wireless" "trm_${uci_essid}" "encryption" "none" - uci_set "wireless" "trm_${uci_essid}" "disabled" "1" - uci_commit "wireless" - f_log "info" "open uplink '${sta_radio}/${cfg_essid}' added to wireless config" - fi + scan_essid="${scan_essid%?}" + scan_essid="${scan_essid:1}" + f_addif "${sta_radio}" "${scan_essid}" fi unset scan_quality scan_bssid scan_essid scan_open continue @@ -703,32 +1002,35 @@ do rc=0 while true do - if [ "${rc}" -eq 0 ] + if [ "${rc}" -eq "0" ] then - f_check "initial" + f_check "initial" "false" fi - sleep ${trm_timeout} 0 + sleep "${trm_timeout}" 0 rc=${?} - if [ "${rc}" -ne 0 ] + if [ "${rc}" -ne "0" ] then - f_check "initial" + f_check "initial" "false" fi - if [ "${rc}" -eq 0 ] || { [ "${rc}" -ne 0 ] && [ "${trm_ifstatus}" = "false" ]; } + if [ "${rc}" -eq "0" ] || { [ "${rc}" -ne "0" ] && [ "${trm_ifstatus}" = "false" ]; } then break fi done elif [ "${trm_action}" = "stop" ] then - f_log "info" "travelmate instance stopped ::: action: ${trm_action}, pid: $(cat ${trm_pidfile} 2>/dev/null)" - > "${trm_rtfile}" - > "${trm_pidfile}" - exit 0 + if [ -s "${trm_pidfile}" ] + then + f_log "info" "travelmate instance stopped ::: action: ${trm_action}, pid: $(cat ${trm_pidfile} 2>/dev/null)" + > "${trm_rtfile}" + > "${trm_pidfile}" + fi + break else f_log "info" "travelmate instance started ::: action: ${trm_action}, pid: ${$}" - unset trm_action fi json_cleanup f_env f_main + unset trm_action done diff --git a/net/travelmate/files/travelmate.vpn b/net/travelmate/files/travelmate.vpn new file mode 100755 index 000000000..f0ca8fab6 --- /dev/null +++ b/net/travelmate/files/travelmate.vpn @@ -0,0 +1,141 @@ +#!/bin/sh +# vpn switch for travelmate +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +# set (s)hellcheck exceptions +# shellcheck disable=1091,2016,2039,2059,2086,2143,2181,2188 + +# Please note: you have to setup the package 'wireguard' or 'openvpn' before using this script + +LC_ALL=C +PATH="/usr/sbin:/usr/bin:/sbin:/bin" + +. "/lib/functions.sh" +trm_action="${1}" +trm_vpnservice="$(uci_get travelmate global trm_vpnservice)" +trm_vpniface="$(uci_get travelmate global trm_vpniface)" +trm_landevice="$(uci_get travelmate global trm_landevice)" +trm_maxwait="$(uci_get travelmate global trm_maxwait "30")" +trm_captiveurl="$(uci_get travelmate global trm_captiveurl "http://captive.apple.com")" +trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0")" +trm_iptrule_accept="FORWARD -i ${trm_landevice} -p tcp --match multiport --dports 80,443 -j ACCEPT" +trm_iptrule_drop="FORWARD -i ${trm_landevice} -j DROP" +trm_iptables="$(command -v iptables)" +trm_logger="$(command -v logger)" +trm_fetch="$(command -v curl)" + +f_log() +{ + local class="${1}" log_msg="${2}" + + if [ -x "${trm_logger}" ] + then + "${trm_logger}" -p "${class}" -t "trm-vpn [${$}]" "${log_msg}" + else + printf "%s %s %s\\n" "${class}" "trm-vpn [${$}]" "${log_msg}" + fi +} + +f_net() +{ + local IFS json_raw json_rc result="net nok" + + json_raw="$(${trm_fetch} --user-agent "${trm_useragent}" --referer "http://www.example.com" --write-out "%{json}" --silent --show-error --connect-timeout $((trm_maxwait/10)) "${trm_captiveurl}" 2>/dev/null)" + json_raw="${json_raw#*\{}" + if [ -n "${json_raw}" ] + then + json_rc="$(printf "%s" "{${json_raw}" | jsonfilter -l1 -e '@.response_code' 2>/dev/null)" + if [ "${json_rc}" = "200" ] || [ "${json_rc}" = "204" ] + then + result="net ok" + fi + fi + printf "%s" "${result}" +} + +if [ -n "${trm_vpnservice}" ] && [ -n "${trm_vpniface}" ] && [ -n "${trm_landevice}" ] && [ -f "/tmp/trm_runtime.json" ] +then + status="$(jsonfilter -i "/tmp/trm_runtime.json" -l1 -e '@.data.travelmate_status' 2>/dev/null)" + vpn_status="$(ubus -S call network.interface."${trm_vpniface}" status 2>/dev/null | jsonfilter -l1 -e '@.up')" + if [ "${trm_action}" = "disable" ] && [ "${vpn_status}" = "true" ] + then + if [ -n "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_drop} 2>&1)" ] + then + "${trm_iptables}" "-w $((trm_maxwait/6))" -I ${trm_iptrule_drop} 2>&1 + f_log "info" "lan forward blocked for device '${trm_landevice}'" + fi + if [ "${status%% (net cp *}" = "connected" ] + then + if [ -n "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_accept} 2>&1)" ] + then + "${trm_iptables}" "-w $((trm_maxwait/6))" -I ${trm_iptrule_accept} 2>&1 + f_log "info" "lan forward on ports 80/443 freed for device '${trm_landevice}'" + fi + fi + fi + + case "${trm_vpnservice}" in + "wireguard") + if [ "${trm_action}" = "enable" ] && [ "${vpn_status}" != "true" ] + then + ubus call network.interface."${trm_vpniface}" up + elif [ "${trm_action}" = "disable" ] && [ "${vpn_status}" = "true" ] + then + ubus call network.interface."${trm_vpniface}" down + f_log "info" "${trm_vpnservice} client connection disabled" + fi + ;; + "openvpn") + if [ "${trm_action}" = "enable" ] && [ "${vpn_status}" != "true" ] + then + ubus call network.interface."${trm_vpniface}" up + /etc/init.d/openvpn restart >/dev/null 2>&1 + elif [ "${trm_action}" = "disable" ] && [ "${vpn_status}" = "true" ] + then + ubus call network.interface."${trm_vpniface}" down + /etc/init.d/openvpn stop >/dev/null 2>&1 + f_log "info" "${trm_vpnservice} client connection disabled" + fi + ;; + esac + + if [ "${trm_action}" = "enable" ] && [ "${vpn_status}" != "true" ] + then + cnt=0 + while true + do + vpn_status="$(ubus -S call network.interface."${trm_vpniface}" status 2>/dev/null | jsonfilter -l1 -e '@.up')" + if [ "${vpn_status}" = "true" ] + then + net_status="$(f_net)" + if [ "${net_status}" = "net ok" ] + then + f_log "info" "${trm_vpnservice} client connection enabled" + if [ -z "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_drop} 2>&1)" ] + then + "${trm_iptables}" "-w $((trm_maxwait/6))" -D ${trm_iptrule_drop} 2>&1 + if [ -z "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_accept} 2>&1)" ] + then + "${trm_iptables}" "-w $((trm_maxwait/6))" -D ${trm_iptrule_accept} 2>&1 + fi + f_log "info" "lan forward freed for device '${trm_landevice}'" + fi + if [ -f "/etc/init.d/sysntpd" ] + then + /etc/init.d/sysntpd restart >/dev/null 2>&1 + fi + break + fi + fi + if [ "${cnt}" -ge "$((trm_maxwait/6))" ] + then + f_log "info" "${trm_vpnservice} restart failed, lan forward for device '${trm_landevice}' still blocked" + ubus call network.interface."${trm_vpniface}" down + break + fi + sleep 1 + cnt="$((cnt+1))" + done + fi +fi diff --git a/net/travelmate/files/travelmate_ntp.hotplug b/net/travelmate/files/travelmate_ntp.hotplug new file mode 100755 index 000000000..0da625124 --- /dev/null +++ b/net/travelmate/files/travelmate_ntp.hotplug @@ -0,0 +1,31 @@ +#!/bin/sh +# ntp hotplug script for travelmate +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) +# This is free software, licensed under the GNU General Public License v3. + +# set (s)hellcheck exceptions +# shellcheck disable=1091,2016,2039,2059,2086,2143,2181,2188 + +trm_init="/etc/init.d/travelmate" +trm_ntpfile="/var/state/travelmate.ntp" +trm_logger="$(command -v logger)" + +f_log() +{ + local class="${1}" log_msg="${2}" + + if [ -x "${trm_logger}" ] + then + "${trm_logger}" -p "${class}" -t "trm-ntp [${$}]" "${log_msg}" + else + printf "%s %s %s\\n" "${class}" "trm-ntp [${$}]" "${log_msg}" + fi +} + +if [ "${ACTION}" = "stratum" ] && [ ! -f "${trm_ntpfile}" ] && \ + [ "$("${trm_init}" enabled; printf "%u" ${?})" -eq "0" ] +then + > "${trm_ntpfile}" + f_log "info" "get ntp time sync" + "${trm_init}" restart +fi diff --git a/net/travelmate/files/wifionice.login b/net/travelmate/files/wifionice.login index 18779e131..6ff93b8f1 100755 --- a/net/travelmate/files/wifionice.login +++ b/net/travelmate/files/wifionice.login @@ -1,28 +1,20 @@ #!/bin/sh # captive portal auto-login script for german ICE hotspots -# written by Dirk Brenken (dev@brenken.org) - +# Copyright (c) 2020 Dirk Brenken (dev@brenken.org) # This is free software, licensed under the GNU General Public License v3. -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -domain="www.wifionice.de" -cmd="$(command -v curl)" -# curl check -# -if [ ! -x "${cmd}" ] -then - exit 1 -fi +trm_fetch="$(command -v curl)" +trm_domain="www.wifionice.de" +trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0")" +trm_maxwait="$(uci_get travelmate global trm_maxwait "30")" # initial get request to receive & extract a valid security token # -"${cmd}" "http://${domain}/en/" -s -o /dev/null -c "/tmp/${domain}.cookie" -if [ -f "/tmp/${domain}.cookie" ] +"${trm_fetch}" --user-agent "${trm_useragent}" --referer "http://www.example.com" --silent --connect-timeout $((trm_maxwait/6)) --cookie-jar "/tmp/${trm_domain}.cookie" --output /dev/null "http://${trm_domain}/en/" +if [ -f "/tmp/${trm_domain}.cookie" ] then - sec_token="$(awk '/csrf/{print $7}' "/tmp/${domain}.cookie")" - rm -f "/tmp/${domain}.cookie" + sec_token="$(awk '/csrf/{print $7}' "/tmp/${trm_domain}.cookie")" + rm -f "/tmp/${trm_domain}.cookie" else exit 2 fi @@ -31,7 +23,7 @@ fi # if [ -n "${sec_token}" ] then - "${cmd}" "http://${domain}/en/" -H "Cookie: csrf=${sec_token}" --data "login=true&CSRFToken=${sec_token}&connect=" -s -o /dev/null + "${trm_fetch}" --user-agent "${trm_useragent}" --silent --connect-timeout $((trm_maxwait/6)) --header "Cookie: csrf=${sec_token}" --data "login=true&CSRFToken=${sec_token}&connect=" --output /dev/null "http://${trm_domain}/en/" else exit 3 fi |