travelmate: release 2.2.0
authorDirk Brenken <[email protected]>
Tue, 14 Oct 2025 20:12:33 +0000 (22:12 +0200)
committerDirk Brenken <[email protected]>
Tue, 14 Oct 2025 20:12:55 +0000 (22:12 +0200)
- drop iwinfo, use iw/ip instead
- support passive wlan scanning (active scanning is still the default)
- drop qrencode, use the LuCI internal qrcode js library instead
- more vpn fixes
- various LuCI changes/enhancements
- fix #27599
- disable proactive scanning in the default config

Signed-off-by: Dirk Brenken <[email protected]>
net/travelmate/Makefile
net/travelmate/files/README.md
net/travelmate/files/travelmate.conf
net/travelmate/files/travelmate.init
net/travelmate/files/travelmate.sh
net/travelmate/files/travelmate.vpn

index f7925caa63e5471d0e4984a9b1fe47980f2f8f2d..3457d068a56e9574cac5ab4e3bcab6d03a7ae406 100644 (file)
@@ -6,7 +6,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=travelmate
-PKG_VERSION:=2.1.4
+PKG_VERSION:=2.2.0
 PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Dirk Brenken <[email protected]>
@@ -17,12 +17,12 @@ define Package/travelmate
        SECTION:=net
        CATEGORY:=Network
        TITLE:=A wlan connection manager for travel router
-       DEPENDS:=+iwinfo +jshn +jsonfilter +curl +ca-bundle +rpcd +rpcd-mod-rpcsys
+       DEPENDS:=+iw +ip +jshn +jsonfilter +curl +ca-bundle +rpcd +rpcd-mod-rpcsys
        PKGARCH:=all
 endef
 
 define Package/travelmate/description
-A wlan connection manager for travel routers.
+A wlan connection manager for travel router.
 Please see https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md for further information.
 
 endef
index a279f4e1891c9ed1ece37fe4ea3ccc99a3ddee13..99c40168141c2bdf0e1cee16386f6d891edefd97 100644 (file)
@@ -61,11 +61,11 @@ automatically (re)connnects to configured APs/hotspots as they become available.
 * [OpenWrt](https://openwrt.org), tested/compatible with current stable 23.x and latest OpenWrt snapshot
 * The `luci-app-travelmate` ensures these packages are present:
   * 'dnsmasq' as dns backend
-  * 'iwinfo' for wlan scanning
+  * 'iw' 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
+    (WEP-based uplinks are no longer supported!)
 * optional: 'wireguard' or 'openvpn' for vpn client connections
 * optional: 'msmtp' to send out Travelmate related status messages via email
 
@@ -101,6 +101,7 @@ automatically (re)connnects to configured APs/hotspots as they become available.
 | 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_scanmode       | -, active                          | send active probe requests or passively listen for beacon frames with 'passive'                       |
 | 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 |
@@ -120,10 +121,10 @@ automatically (re)connnects to configured APs/hotspots as they become available.
 | 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                                           |
+| trm_vpn            | 0, disabled                        | VPN connections will be managed by travelmate                                                         |
 | trm_stdvpnservice  | -, not set                         | standard vpn service which will be automatically added to new STA profiles                            |
 | trm_stdvpniface    | -, not set                         | standard vpn interface which will be automatically added to new STA profiles                          |
   
-
 * per uplink exist an additional 'uplink' section in the travelmate config, with the following options:  
 
 | Option             | Default                            | Description/Valid Values                                                                              |
index 6e72ca381d7604a903dcb25239a40f0c9d24f56d..171122f06933281de2b69dd8a95857d98614760e 100644 (file)
@@ -2,7 +2,7 @@
 config travelmate 'global'
        option trm_enabled '0'
        option trm_captive '1'
-       option trm_proactive '1'
+       option trm_proactive '0'
        option trm_netcheck '0'
        option trm_autoadd '0'
        option trm_mail '0'
index 37b301950a7459a7aabcccbfe351b17d39cbde00..2348ddd92718fea1e6cb0e45881fd94e44a044e8 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh /etc/rc.common
-# Copyright (c) 2016-2024 Dirk Brenken ([email protected])
+# Copyright (c) 2016-2025 Dirk Brenken ([email protected])
 # This is free software, licensed under the GNU General Public License v3.
 
 # set (s)hellcheck exceptions
@@ -9,7 +9,6 @@ START=25
 USE_PROCD=1
 
 extra_command "scan" "[<radio>|<ifname>] Scan for available nearby uplinks"
-extra_command "assoc" "[<radio>|<ifname>] Get MAC adresses of associated wlan stations"
 extra_command "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"
@@ -34,7 +33,7 @@ start_service() {
                procd_set_param pidfile "${trm_pidfile}"
                procd_set_param nice "$(uci_get travelmate global trm_nice "0")"
                procd_set_param stdout 0
-               procd_set_param stderr 0
+               procd_set_param stderr 1
                procd_close_instance
        fi
 }
@@ -77,25 +76,39 @@ status_service() {
 }
 
 scan() {
-       local result radio="${1}"
+       local scan_dev scan_mode radio_num radio_phy radio="${1}"
 
        : > "${trm_scanfile}"
-       if [ -z "${radio}" ]; then
-               radio="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -q -l1 -e '@[@.up=true].interfaces[0].ifname')"
+       scan_dev="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -ql1 -e "@.${radio}.interfaces[0].ifname")"
+       if [ -z "${scan_dev}" ]; then
+               radio_num="${radio//[a-z]/}"
+               radio_phy="phy#${radio_num}"
+               scan_dev="$(iw dev 2>/dev/null | awk -v iw_phy="${radio_phy}" '{if($0==iw_phy){inside=1;next}if(inside&&/^phy#/){exit}if(inside&&$1=="Interface"){print $2;exit}}')"
+               if [ -z "${scan_dev}" ]; then
+                       if iw phy "phy${radio_num}" interface add "trmscan${radio_num}" type managed >/dev/null 2>&1; then
+                               if ip link set "trmscan${radio_num}" up >/dev/null 2>&1; then
+                                       scan_dev="trmscan${radio_num}"
+                               fi
+                       fi
+               fi
        fi
-       result="$(iwinfo "${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:::\n" "Available nearby uplinks on '${radio}'"
-       if [ -n "${result}" ]; then
-               printf "%s\n" "${result}" > "${trm_scanfile}"
-               printf "%-15s%-10s%-35s%-20s%s\n" "    Strength" "Channel" "ESSID" "BSSID" "Encryption"
-               printf "%s\n" "    --------------------------------------------------------------------------------------"
-               printf "%s\n" "${result}"
-       else
-               printf "%s\n" "::: Empty resultset"
+       if [ -n "${scan_dev}" ]; then
+               scan_mode="$(uci_get travelmate global trm_scanmode "active")"
+               [ "${scan_mode}" != "passive" ] && scan_mode=""
+               printf "%b\n" "$(iw "${scan_dev}" scan ${scan_mode} 2>/dev/null |
+                       awk '/^BSS /{if(bssid!=""){if(ssid=="")ssid="unknown";printf "%3s %3s %17s %s %s %10s %30s %s\n",signal,channel,bssid,rsn,wpa,cipher,auth,ssid};signal="";channel="";rsn="-";wpa="-";cipher="-";auth="-";bssid=toupper(substr($2,1,17))}
+                       /signal:/{signal=2*($2 + 100)}
+                       /SSID:/{$1="";sub(/^ /,"",$0);ssid=$0}
+                       /freq:/{channel=int($2);if(channel>=2400&&channel<=2500)channel=int((channel-2407)/5);else if(channel>=4900&&channel<=5900)channel=int((channel-5000)/5);else if(channel>=5925&&channel<=7125)channel=int(((channel-5950)/5)+1)}
+                       /WPA:/{wpa="+"}
+                       /RSN:/{rsn="+"}
+                       /Group cipher:/{cipher=$4}
+                       /Authentication suites:/{auth="";for(i=4;i<=NF;i++){auth=auth (i==4?"":",")$i}}
+                       END{if(bssid!=""){if(ssid=="")ssid="unknown";printf "%3s %3s %17s %s %s %10s %30s %s\n",signal,channel,bssid,rsn,wpa,cipher,auth,ssid}}' | sort -rn)" > "${trm_scanfile}"
+       fi
+       if [ -n "${radio_phy}" ] && [ -n "${radio_num}" ]; then
+               ip link set "trmscan${radio_num}" down >/dev/null 2>&1
+               iw dev "trmscan${radio_num}" del >/dev/null 2>&1
        fi
 }
 
@@ -108,7 +121,7 @@ setup() {
        metric="${metric//[^0-9]/}"
 
        if [ -n "${iface}" ] && [ "${iface}" = "${input}" ]; then
-               printf "%s\n" "The uplink interface '${input}' has been already configured"
+               return 1
        elif [ -n "${input}" ]; then
                if [ -n "${iface}" ]; then
                        uci -q batch <<-EOC
@@ -163,24 +176,6 @@ setup() {
        fi
 }
 
-assoc() {
-       local result radio="${1}"
-
-       if [ -z "${radio}" ]; then
-               radio="$(ubus -S call network.wireless status 2>/dev/null | jsonfilter -q -l1 -e '@[@.*.*.config.mode="ap"].interfaces[0].ifname')"
-       fi
-       result="$(iwinfo "${radio}" assoc 2>/dev/null | awk '/^[A-Z0-9:]+/{printf "    %s\n",$1}')"
-       printf "%s\n" "::: Associated wlan stations on '${radio}'"
-       printf "%s\n" ":::"
-       if [ -n "${result}" ]; then
-               printf "%s\n" "    MAC addresses"
-               printf "%s\n" "    -----------------"
-               printf "%s\n" "${result}"
-       else
-               printf "%s\n" "::: Empty resultset"
-       fi
-}
-
 service_triggers() {
        local iface delay
 
index db9c35e62dd963d89d47031877fdedef5865296d..11e52f0783f1cfa17ffb65fe4a00272045fbf6dd 100755 (executable)
@@ -13,7 +13,7 @@ trm_enabled="0"
 trm_debug="0"
 trm_iface=""
 trm_captive="1"
-trm_proactive="1"
+trm_proactive="0"
 trm_vpn="0"
 trm_netcheck="0"
 trm_autoadd="0"
@@ -27,8 +27,8 @@ trm_maxwait="30"
 trm_maxautoadd="5"
 trm_timeout="60"
 trm_radio=""
+trm_scanmode="active"
 trm_connection=""
-trm_wpaflags=""
 trm_ovpninfolist=""
 trm_vpnifacelist=""
 trm_vpninfolist=""
@@ -36,7 +36,7 @@ trm_stdvpnservice=""
 trm_stdvpniface=""
 trm_rtfile="/tmp/trm_runtime.json"
 trm_captiveurl="http://detectportal.firefox.com"
-trm_useragent="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0"
+trm_useragent="Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0"
 trm_ntpfile="/var/state/travelmate.ntp"
 trm_vpnfile="/var/state/travelmate.vpn"
 trm_mailfile="/var/state/travelmate.mail"
@@ -68,8 +68,6 @@ f_cmd() {
 # load travelmate environment
 #
 f_env() {
-       local check wpa_checks result
-
        if [ "${trm_action}" = "stop" ]; then
                return
        fi
@@ -116,20 +114,6 @@ f_env() {
                /etc/init.d/travelmate stop
        fi
 
-       if [ -z "${trm_wpaflags}" ]; then
-               wpa_checks="sae owe eap suiteb192"
-               for check in ${wpa_checks}; do
-                       if [ -x "${trm_wpacmd}" ]; then
-                               if "${trm_wpacmd}" -v"${check}" >/dev/null 2>&1; then
-                                       result="$(f_trim "${result} ${check}: $(f_char 1)")"
-                               else
-                                       result="$(f_trim "${result} ${check}: $(f_char 0)")"
-                               fi
-                       fi
-               done
-               trm_wpaflags="$(printf "%s" "${result}" | "${trm_awkcmd}" '{printf "%s %s, %s %s, %s %s, %s %s",$1,$2,$3,$4,$5,$6,$7,$8}')"
-       fi
-
        config_load wireless
        config_foreach f_setdev "wifi-device"
        if [ -n "$(uci -q changes "wireless")" ]; then
@@ -148,7 +132,7 @@ f_env() {
                config_load network
                config_foreach f_getvpn "interface"
        fi
-       f_log "debug" "f_env     ::: auto_sta: ${trm_opensta:-"-"}, wpa_flags: ${trm_wpaflags}, sys_ver: ${trm_sysver}"
+       f_log "debug" "f_env     ::: auto_sta: ${trm_opensta:-"-"}, sys_ver: ${trm_sysver}"
 }
 
 # trim helper function
@@ -209,7 +193,7 @@ f_wifi() {
 # vpn helper function
 #
 f_vpn() {
-       local rc result info iface vpn vpn_service vpn_iface vpn_instance vpn_status vpn_action="${1}"
+       local rc info iface vpn vpn_service vpn_iface vpn_instance vpn_status vpn_action="${1}"
 
        if  [ "${trm_vpn}" = "1" ] && [ -n "${trm_vpninfolist}" ]; then
                vpn="$(f_getval "vpn")"
@@ -232,23 +216,24 @@ f_vpn() {
                                fi
                        done
                        rm -f "${trm_vpnfile}"
+                       sleep 1
                elif [ "${vpn}" = "1" ] && [ -n "${vpn_iface}" ] && [ "${vpn_action}" = "enable_keep" ]; then
                        for info in ${trm_vpninfolist}; do
                                iface="${info%%&&*}"
+                               [ "${iface}" = "${info}" ] && vpn_instance="" || vpn_instance="${info##*&&}"
                                vpn_status="$(ifstatus "${iface}" | "${trm_jsoncmd}" -ql1 -e '@.up')"
                                if [ "${vpn_status}" = "true" ] && [ "${iface}" != "${vpn_iface}" ]; then
                                        /sbin/ifdown "${iface}"
                                        f_log "info" "take down vpn interface '${iface}' (switch)"
-                                       rc="1"
-                               fi
-                               [ "${iface}" = "${info}" ] && vpn_instance="" || vpn_instance="${info##*&&}"
-                               if [ -x "/etc/init.d/openvpn" ] && [ -n "${vpn_instance}" ] && /etc/init.d/openvpn running "${vpn_instance}"; then
-                                       /etc/init.d/openvpn stop "${vpn_instance}"
-                                       f_log "info" "take down openvpn instance '${vpn_instance}' (switch)"
+                                       if [ -x "/etc/init.d/openvpn" ] && [ -n "${vpn_instance}" ] && /etc/init.d/openvpn running "${vpn_instance}"; then
+                                               /etc/init.d/openvpn stop "${vpn_instance}"
+                                               f_log "info" "take down openvpn instance '${vpn_instance}' (switch)"
+                                       fi
                                        rc="1"
                                fi
                                if [ "${rc}" = "1" ]; then
                                        rm -f "${trm_vpnfile}"
+                                       sleep 1
                                        break
                                fi
                        done
@@ -265,7 +250,6 @@ f_vpn() {
                                                                break
                                                        fi
                                                done
-                                               f_log "debug" "f_vpn     ::: vpn: ${vpn:-"0"}, action: ${vpn_action}, service: ${vpn_service}, iface: ${vpn_iface}, instance: ${vpn_instance}"
                                                "${trm_vpnpgm}" "${vpn:-"0"}" "${vpn_action}" "${vpn_service}" "${vpn_iface}" "${vpn_instance}" >/dev/null 2>&1
                                                rc="${?}"
                                        fi
@@ -273,7 +257,7 @@ f_vpn() {
                        [ -n "${rc}" ] && f_jsnup
                fi
        fi
-       f_log "debug" "f_vpn     ::: vpn: ${trm_vpn:-"-"}, enabled: ${vpn:-"-"}, action: ${vpn_action}, vpn_service: ${vpn_service:-"-"}, vpn_iface: ${vpn_iface:-"-"}, vpn_instance: ${vpn_instance:-"-"}, vpn_infolist: ${trm_vpninfolist:-"-"}, result: ${result}, rc: ${rc:-"-"}"
+       f_log "debug" "f_vpn     ::: vpn: ${trm_vpn:-"-"}, enabled: ${vpn:-"-"}, action: ${vpn_action}, vpn_service: ${vpn_service:-"-"}, vpn_iface: ${vpn_iface:-"-"}, vpn_instance: ${vpn_instance:-"-"}, vpn_infolist: ${trm_vpninfolist:-"-"}, connection: ${trm_connection%%/*}, rc: ${rc:-"-"}"
 }
 
 # mac helper function
@@ -294,13 +278,13 @@ f_mac() {
                else
                        uci_remove "wireless" "${section}" "macaddr" 2>/dev/null
                        ifname="$("${trm_ubuscmd}" -S call network.wireless status 2>/dev/null | "${trm_jsoncmd}" -ql1 -e '@.*.interfaces[@.config.mode="sta"].ifname')"
-                       result="$("${trm_iwinfocmd}" "${ifname}" info 2>/dev/null | "${trm_awkcmd}" '/Access Point:/{printf "%s",$3}')"
+                       result="$("${trm_iwcmd}" dev "${ifname}" info 2>/dev/null | "${trm_awkcmd}" '/addr /{printf "%s",toupper($2)}')"
                fi
        elif [ "${action}" = "get" ]; then
                result="$(uci_get "wireless" "${section}" "macaddr")"
                if [ -z "${result}" ]; then
                        ifname="$("${trm_ubuscmd}" -S call network.wireless status 2>/dev/null | "${trm_jsoncmd}" -ql1 -e '@.*.interfaces[@.config.mode="sta"].ifname')"
-                       result="$("${trm_iwinfocmd}" "${ifname}" info 2>/dev/null | "${trm_awkcmd}" '/Access Point:/{printf "%s",$3}')"
+                       result="$("${trm_iwcmd}" dev "${ifname}" info 2>/dev/null | "${trm_awkcmd}" '/addr /{printf "%s",toupper($2)}')"
                fi
        fi
        printf "%s" "${result}"
@@ -485,25 +469,31 @@ f_getval() {
 f_setdev() {
        local disabled radio="${1}"
 
-       disabled="$(uci_get "wireless" "${radio}" "disabled")"
-       if [ "${disabled}" = "1" ]; then
-               uci_set wireless "${radio}" "disabled" "0"
-       fi
-       if [ -n "${trm_radio}" ] && [ -z "${trm_radiolist}" ]; then
-               trm_radiolist="${trm_radio}"
-       elif [ -z "${trm_radio}" ] && ! printf "%s" "${trm_radiolist}" | "${trm_grepcmd}" -q "${radio}"; then
-               trm_radiolist="$(f_trim "${trm_radiolist} ${radio}")"
+       if { [ -z "${trm_radio}" ] && ! printf "%s" "${trm_radiolist}" | "${trm_grepcmd}" -q "${radio}"; } ||
+               { [ -n "${trm_radio}" ] && printf "%s" "${trm_radio}" | "${trm_grepcmd}" -q "${radio}"; }; then
+               if [ -n "${trm_radio}" ] && [ "${trm_radio}" = "radio1 radio0" ]; then
+                       trm_radiolist="$(f_trim "${radio} ${trm_radiolist}")"
+               else
+                       trm_radiolist="$(f_trim "${trm_radiolist} ${radio}")"
+               fi
+               disabled="$(uci_get "wireless" "${radio}" "disabled")"
+               if [ "${disabled}" = "1" ]; then
+                       uci_set wireless "${radio}" "disabled" "0"
+               fi
        fi
-       f_log "debug" "f_setdev  ::: radio: ${radio:-"-"}, radio_list(cnf/cur): ${trm_radio:-"-"}/${trm_radiolist:-"-"}, disabled: ${disabled:-"-"}"
+       f_log "debug" "f_setdev  ::: radio: ${radio:-"-"}, radio_conf: ${trm_radio:-"-"}, radio_list: ${trm_radiolist:-"-"}, disabled: ${disabled:-"-"}"
 }
 
 # set 'wifi-iface' sections
 #
 f_setif() {
-       local mode radio essid bssid enabled disabled con_start con_end con_start_expiry con_end_expiry section="${1}" proactive="${2}"
+       local mode radio essid bssid enabled disabled d1 d2 d3 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")"
+       if ! printf "%s" "${trm_radiolist}" | "${trm_grepcmd}" -q "${radio}"; then
+               return
+       fi
+       mode="$(uci_get "wireless" "${section}" "mode")"
        essid="$(uci_get "wireless" "${section}" "ssid")"
        bssid="$(uci_get "wireless" "${section}" "bssid")"
        disabled="$(uci_get "wireless" "${section}" "disabled")"
@@ -607,8 +597,8 @@ f_addsta() {
                        EOC
                fi
                trm_opensta="$((trm_opensta + 1))"
-               uci_commit "travelmate"
-               uci_commit "wireless"
+               [ -n "$(uci -q changes "travelmate")" ] && uci_commit "travelmate"
+               [ -n "$(uci -q changes "wireless")" ] && uci_commit "wireless"
                f_wifi
                if [ ! -f "${trm_refreshfile}" ]; then
                        printf "%s" "ui_reload" >"${trm_refreshfile}"
@@ -624,7 +614,7 @@ f_addsta() {
 f_net() {
        local err_msg raw json_raw html_raw html_cp js_cp json_ec json_rc json_cp json_ed result="net nok"
 
-       raw="$("${trm_fetchcmd}" --user-agent "${trm_useragent}" --referer "http://www.example.com" --header "Cache-Control: no-cache, no-store, must-revalidate, max-age=0" --write-out "%{json}" --silent --retry 5 --max-time $((trm_maxwait / 6)) "${trm_captiveurl}")"
+       raw="$("${trm_fetchcmd}" --user-agent "${trm_useragent}" --referer "http://www.example.com" --header "Cache-Control: no-cache, no-store, must-revalidate, max-age=0" --write-out "%{json}" --silent --retry $((trm_maxwait / 6)) --max-time $((trm_maxwait / 6)) "${trm_captiveurl}")"
        json_raw="${raw#*\{}"
        html_raw="${raw%%\{*}"
        if [ -n "${json_raw}" ]; then
@@ -686,6 +676,10 @@ f_check() {
        if [ "${mode}" = "sta" ]; then
                "${trm_ubuscmd}" -S call network.interface."${trm_iface}" down >/dev/null 2>&1
                "${trm_ubuscmd}" -S call network.interface."${trm_iface}" up >/dev/null 2>&1
+               if ! "${trm_ubuscmd}" -t "$((trm_maxwait / 6))" wait_for network.interface."${trm_iface}" >/dev/null 2>&1; then
+                       f_log "info" "travelmate interface '${trm_iface}' does not appear on ubus on ifup event"
+               fi
+               sleep 1
        fi
 
        while [ "${wait_time}" -le "${trm_maxwait}" ]; do
@@ -699,23 +693,23 @@ f_check() {
                                        f_jsnup
                                fi
                                if [ "${status}" = "false" ]; then
-                                       sleep "$((trm_maxwait / 5))"
+                                       sleep "$((trm_maxwait / 6))"
                                fi
                                break
                        elif [ "${mode}" = "rev" ]; then
-                               unset trm_connection
+                               trm_connection=""
                                trm_ifstatus="${status}"
                                break
                        else
                                ifname="$(printf "%s" "${dev_status}" | "${trm_jsoncmd}" -ql1 -e '@.*.interfaces[@.config.mode="sta"].ifname')"
                                if [ -n "${ifname}" ] && [ "${enabled}" = "1" ]; then
-                                       trm_ifquality="$("${trm_iwinfocmd}" "${ifname}" info 2>/dev/null | "${trm_awkcmd}" -F '[ ]' '/Link Quality: [0-9]+\/[0-9]+/{split($NF,var0,"/");printf "%i\n",(var0[1]*100/var0[2])}')"
+                                       trm_ifquality="$("${trm_iwcmd}" dev "${ifname}" link 2>/dev/null | "${trm_awkcmd}" '/signal: /{printf "%s",2*($2+100)}')"
                                        if [ -z "${trm_ifquality}" ]; then
                                                trm_ifstatus="$("${trm_ubuscmd}" -S call network.interface dump 2>/dev/null | "${trm_jsoncmd}" -ql1 -e "@.interface[@.device=\"${ifname}\"].up")"
                                                if { [ -n "${trm_connection}" ] && [ "${trm_ifstatus}" = "false" ]; } || [ "${wait_time}" -eq "${trm_maxwait}" ]; then
                                                        f_log "info" "no signal from uplink"
                                                        f_vpn "disable"
-                                                       unset trm_connection
+                                                       trm_connection=""
                                                        trm_ifstatus="${status}"
                                                        f_ctrack "end"
                                                        f_jsnup
@@ -732,7 +726,7 @@ f_check() {
                                                                        if [ -x "/etc/init.d/dnsmasq" ] && [ -f "/etc/config/dhcp" ] &&
                                                                                [ -n "${cp_domain}" ] && ! uci_get "dhcp" "@dnsmasq[0]" "rebind_domain" | "${trm_grepcmd}" -q "${cp_domain}"; then
                                                                                uci_add_list "dhcp" "@dnsmasq[0]" "rebind_domain" "${cp_domain}"
-                                                                               uci_commit "dhcp"
+                                                                               [ -n "$(uci -q changes "dhcp")" ] && uci_commit "dhcp"
                                                                                /etc/init.d/dnsmasq reload
                                                                                f_log "info" "captive portal domain '${cp_domain}' added to to dhcp rebind whitelist"
                                                                        else 
@@ -771,20 +765,20 @@ f_check() {
                                        elif [ -n "${trm_connection}" ] && { [ "${trm_netcheck}" = "1" ] || [ "${mode}" = "initial" ]; }; then
                                                f_log "info" "uplink is out of range (${trm_ifquality}/${trm_minquality})"
                                                f_vpn "disable"
-                                               unset trm_connection
+                                               trm_connection=""
                                                trm_ifstatus="${status}"
                                                f_ctrack "end"
                                                f_jsnup
                                                break
                                        elif [ "${mode}" = "initial" ] || [ "${mode}" = "sta" ]; then
-                                               unset trm_connection
+                                               trm_connection=""
                                                trm_ifstatus="${status}"
                                                f_jsnup
                                                break
                                        fi
                                elif [ -n "${trm_connection}" ]; then
                                        f_vpn "disable"
-                                       unset trm_connection
+                                       trm_connection=""
                                        trm_ifstatus="${status}"
                                        f_jsnup
                                        break
@@ -829,10 +823,10 @@ f_jsnup() {
                        vpn_done="1"
                fi
        elif [ "${status}" = "error" ]; then
-               unset trm_connection
+               trm_connection=""
                status="program error"
        else
-               unset trm_connection
+               trm_connection=""
                status="running (not connected)"
        fi
        if [ -z "${last_date}" ]; then
@@ -849,8 +843,7 @@ f_jsnup() {
        json_add_string "station_id" "${sta_radio:-"-"}/${sta_essid:-"-"}/${sta_bssid:-"-"}"
        json_add_string "station_mac" "${sta_mac:-"-"}"
        json_add_string "station_interfaces" "${sta_iface:-"-"}, ${vpn_iface:-"-"}"
-       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 "run_flags" "scan: ${trm_scanmode}, 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_done}), vpn: $(f_char ${vpn_done}), mail: $(f_char ${mail_done})"
        json_add_string "last_run" "${last_date}"
        json_add_string "system" "${trm_sysver}"
@@ -888,11 +881,18 @@ f_log() {
 # main function for connection handling
 #
 f_main() {
-       local radio cnt retrycnt scan_dev scan_list scan_essid scan_bssid scan_open scan_quality station_id section
-       local sta sta_essid sta_bssid sta_radio sta_mac open_sta open_essid config_radio config_essid config_bssid
+       local radio radio_num radio_phy cnt retrycnt scan_dev scan_mode scan_list scan_essid scan_bssid scan_rsn scan_wpa scan_open scan_quality
+       local station_id section sta sta_essid sta_bssid sta_radio sta_mac open_sta open_essid config_radio config_essid config_bssid
 
        f_check "initial" "false"
-       f_log "debug" "f_main-1  ::: status: ${trm_ifstatus}, proactive: ${trm_proactive}"
+       if [ "${trm_proactive}" = "0" ]; then
+               if [ "${trm_connection%%/*}" = "net ok" ]; then
+                       f_vpn "enable_keep"
+               else
+                       f_vpn "disable"
+               fi
+       fi
+       f_log "debug" "f_main-1  ::: status: ${trm_ifstatus}, connection: ${trm_connection%%/*}, proactive: ${trm_proactive}"
        if [ "${trm_ifstatus}" != "true" ] || [ "${trm_proactive}" = "1" ]; then
                config_load wireless
                config_foreach f_setif wifi-iface "${trm_proactive}"
@@ -906,7 +906,7 @@ f_main() {
                        f_check "dev" "true"
                        f_log "debug" "f_main-2  ::: config_radio: ${config_radio}, config_essid: \"${config_essid}\", config_bssid: ${config_bssid:-"-"}"
                else
-                       uci_commit "wireless"
+                       [ -n "$(uci -q changes "wireless")" ] && uci_commit "wireless"
                        f_check "dev" "false"
                fi
                f_log "debug" "f_main-3  ::: radio_list: ${trm_radiolist:-"-"}, sta_list: ${trm_stalist:-"-"}"
@@ -945,11 +945,37 @@ f_main() {
                                fi
                                if [ -z "${scan_list}" ]; then
                                        scan_dev="$("${trm_ubuscmd}" -S call network.wireless status 2>/dev/null | "${trm_jsoncmd}" -ql1 -e "@.${radio}.interfaces[0].ifname")"
-                                       scan_list="$("${trm_iwinfocmd}" "${scan_dev:-${radio}}" scan 2>/dev/null |
-                                               "${trm_awkcmd}" 'BEGIN{FS="[[:space:]]"}/Address:/{var1=$NF}/ESSID:/{var2="";for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i}}
-                                               /Quality:/{split($NF,var0,"/")}/Encryption:/{if($NF=="none"){var3="+"}else{var3="-"};
-                                               printf "%i %s %s %s\n",(var0[1]*100/var0[2]),var3,var1,var2}' | "${trm_sortcmd}" -rn)"
-                                       f_log "debug" "f_main-6  ::: radio: ${radio}, scan_device: ${scan_dev}, scan_cnt: $(printf "%s" "${scan_list}" | "${trm_grepcmd}" -c "^")"
+                                       if [ -z "${scan_dev}" ]; then
+                                               radio_num="${radio//[a-z]/}"
+                                               radio_phy="phy#${radio_num}"
+                                               scan_dev="$("${trm_iwcmd}" dev 2>/dev/null | "${trm_awkcmd}" -v iw_phy="${radio_phy}" '{if($0==iw_phy){inside=1;next}if(inside&&/^phy#/){exit}if(inside&&$1=="Interface"){print $2;exit}}')"
+                                               if [ -z "${scan_dev}" ]; then
+                                                       if "${trm_iwcmd}" phy "phy${radio_num}" interface add "trmscan${radio_num}" type managed >/dev/null 2>&1; then
+                                                               if "${trm_ipcmd}" link set "trmscan${radio_num}" up >/dev/null 2>&1; then
+                                                                       scan_dev="trmscan${radio_num}"
+                                                               fi
+                                                       fi
+                                               fi
+                                       fi
+                                       if [ -n "${scan_dev}" ]; then
+                                               [ "${trm_scanmode}" != "passive" ] && scan_mode=""
+                                               scan_list="$(printf "%b" "$("${trm_iwcmd}" "${scan_dev}" scan ${scan_mode} 2>/dev/null |
+                                                       "${trm_awkcmd}" '/^BSS /{if(bssid!=""){if(ssid=="")ssid="unknown";printf "%s %s %s %s %s\n",signal,rsn,wpa,bssid,ssid};bssid=toupper(substr($2,1,17));ssid="";signal="";rsn="+";wpa="+"}
+                                                       /signal:/{signal=2*($2 + 100)}
+                                                       /SSID:/{$1="";sub(/^ /,"",$0);ssid="\""$0"\""}
+                                                       /WPA:/{wpa="-"}
+                                                       /RSN:/{rsn="-"}
+                                                       END{if(bssid!=""){if(ssid=="")ssid="unknown";printf "%s %s %s %s %s\n",signal,rsn,wpa,bssid,ssid}}' | "${trm_sortcmd}" -rn)")"
+                                               f_log "debug" "f_main-6  ::: radio: ${radio}, scan_device: ${scan_dev}, scan_mode: ${trm_scanmode:-"active"}, scan_cnt: $(printf "%s" "${scan_list}" | "${trm_grepcmd}" -c "^")"
+                                       fi
+                                       if [ -n "${radio_phy}" ] && [ -n "${radio_num}" ]; then
+                                               "${trm_ipcmd}" link set "trmscan${radio_num}" down >/dev/null 2>&1
+                                               "${trm_iwcmd}" dev "trmscan${radio_num}" del >/dev/null 2>&1
+                                       fi
+                                       if [ -z "${scan_dev}" ]; then
+                                               f_log "info" "no scan device on '${radio}'"
+                                               continue 2
+                                       fi
                                        if [ -z "${scan_list}" ]; then
                                                f_log "info" "no scan results on '${radio}'"
                                                continue 2
@@ -958,7 +984,12 @@ f_main() {
 
                                # scan loop
                                #
-                               while read -r scan_quality scan_open scan_bssid scan_essid; do
+                               while read -r scan_quality scan_rsn scan_wpa scan_bssid scan_essid; do
+                                       if [ "${scan_rsn}" = "-" ] && [ "${scan_wpa}" = "-" ]; then
+                                               scan_open="+"
+                                       else
+                                               scan_open="-"
+                                       fi
                                        if [ -n "${scan_quality}" ] && [ -n "${scan_open}" ] && [ -n "${scan_bssid}" ] && [ -n "${scan_essid}" ]; then
                                                f_log "debug" "f_main-7  ::: radio(sta/scan): ${sta_radio}/${radio}, essid(sta/scan): \"${sta_essid}\"/${scan_essid}, bssid(sta/scan): ${sta_bssid}/${scan_bssid}, quality(min/scan): ${trm_minquality}/${scan_quality}, open: ${scan_open}"
                                                if [ "${scan_quality}" -lt "${trm_minquality}" ]; then
@@ -981,7 +1012,7 @@ f_main() {
                                                                if [ -n "${config_radio}" ]; then
                                                                        f_vpn "disable"
                                                                        uci_set "wireless" "${trm_activesta}" "disabled" "1"
-                                                                       uci_commit "wireless"
+                                                                       [ -n "$(uci -q changes "wireless")" ] && uci_commit "wireless"
                                                                        f_check "rev" "false"
                                                                        f_ctrack "end"
                                                                        f_log "info" "uplink connection terminated '${config_radio}/${config_essid}/${config_bssid:-"-"}'"
@@ -998,7 +1029,7 @@ f_main() {
                                                                        f_check "sta" "false" "${sta_radio}" "${sta_essid}" "${sta_bssid}"
                                                                        if [ "${trm_ifstatus}" = "true" ]; then
                                                                                rm -f "${trm_mailfile}"
-                                                                               uci_commit "wireless"
+                                                                               [ -n "$(uci -q changes "wireless")" ] && uci_commit "wireless"
                                                                                f_ctrack "start"
                                                                                f_log "info" "connected to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' with mac '${sta_mac:-"-"}' (${retrycnt}/${trm_maxretry})"
                                                                                f_vpn "enable"
@@ -1049,7 +1080,8 @@ trm_ubuscmd="$(f_cmd ubus)"
 trm_loggercmd="$(f_cmd logger)"
 trm_wificmd="$(f_cmd wifi)"
 trm_fetchcmd="$(f_cmd curl)"
-trm_iwinfocmd="$(f_cmd iwinfo)"
+trm_ipcmd="$(f_cmd ip)"
+trm_iwcmd="$(f_cmd iw)"
 trm_wpacmd="$(f_cmd wpa_supplicant)"
 
 # get travelmate version
@@ -1076,7 +1108,7 @@ while true; do
                f_log "info" "travelmate instance started ::: action: ${trm_action}, pid: ${$}"
                f_env
                f_main
-               unset trm_action
+               trm_action=""
        fi
        while true; do
                sleep "${trm_timeout}" 0
index 053e159217c35ac343b0740384e021198161225e..fe213b1d1ac61154a193b7ef314e1dc11ac47f83 100755 (executable)
@@ -18,7 +18,7 @@ vpn_iface="${4}"
 vpn_instance="${5}"
 trm_maxwait="$(uci_get travelmate global trm_maxwait "30")"
 trm_captiveurl="$(uci_get travelmate global trm_captiveurl "http://detectportal.firefox.com")"
-trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0")"
+trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0")"
 trm_ubuscmd="$(command -v ubus)"
 trm_jsoncmd="$(command -v jsonfilter)"
 trm_logger="$(command -v logger)"
@@ -28,7 +28,7 @@ trm_vpnfile="/var/state/travelmate.vpn"
 f_net() {
        local json_rc
 
-       json_rc="$(${trm_fetch} --user-agent "${trm_useragent}" --referer "http://www.example.com" --header "Cache-Control: no-cache, no-store, must-revalidate, max-age=0" --write-out "%{response_code}" --silent --retry 5 --output /dev/null --max-time $((trm_maxwait / 6)) "${trm_captiveurl}")"
+       json_rc="$(${trm_fetch} --user-agent "${trm_useragent}" --referer "http://www.example.com" --header "Cache-Control: no-cache, no-store, must-revalidate, max-age=0" --write-out "%{response_code}" --silent --retry $((trm_maxwait / 6)) --output /dev/null --max-time $((trm_maxwait / 6)) "${trm_captiveurl}")"
        if [ "${json_rc}" = "200" ] || [ "${json_rc}" = "204" ]; then
                json_rc="net ok"
        fi
@@ -39,21 +39,25 @@ if [ "${vpn}" = "1" ] && [ "${vpn_action%_*}" = "enable" ]; then
        if [ "${vpn_action}" = "enable_keep" ]; then
                vpn_status="$("${trm_ubuscmd}" -S call network.interface."${vpn_iface}" status 2>/dev/null | "${trm_jsoncmd}" -ql1 -e '@.up')"
        fi
+       "${trm_logger}" -p "info" -t "trm-vpn  [${$}]" "start vpn processing (vpn: ${vpn:-"-"}, action: ${vpn_action:-"-"}, interface: ${vpn_iface:-"-"}, instance: ${vpn_instance:-"-"}, status: ${vpn_status:-"-"})"
        if [ "${vpn_action}" = "enable" ] || [ "${vpn_status}" != "true" ]; then
                if [ "${vpn_status}" != "true" ]; then
                        /sbin/ifdown "${vpn_iface}"
                        "${trm_ubuscmd}" -S call network.interface."${vpn_iface}" remove >/dev/null 2>&1
+                       if [ "${vpn_service}" = "openvpn" ] && [ -n "${vpn_instance}" ] && [ -x "/etc/init.d/openvpn" ]; then
+                               if /etc/init.d/openvpn running "${vpn_instance}"; then
+                                       /etc/init.d/openvpn stop "${vpn_instance}"
+                               fi
+                               /etc/init.d/openvpn start "${vpn_instance}"
+                       fi
                fi
-               if [ "${vpn_service}" = "openvpn" ] && [ -n "${vpn_instance}" ] && [ -x "/etc/init.d/openvpn" ] && /etc/init.d/openvpn running "${vpn_instance}"; then
-                       /etc/init.d/openvpn stop "${vpn_instance}"
-                       sleep 1
-               fi
-               if [ "${vpn_service}" = "openvpn" ] && [ -n "${vpn_instance}" ] && [ -x "/etc/init.d/openvpn" ] && ! /etc/init.d/openvpn running "${vpn_instance}"; then
-                       /etc/init.d/openvpn start "${vpn_instance}"
-               fi
+               sleep 1
                /sbin/ifup "${vpn_iface}"
+               if ! "${trm_ubuscmd}" -t "$((trm_maxwait / 6))" wait_for network.interface."${vpn_iface}" >/dev/null 2>&1; then
+                       "${trm_logger}" -p "info" -t "trm-vpn  [${$}]" "travelmate vpn interface '${vpn_iface}' does not appear on ubus on ifup event"
+               fi
                cnt=0
-               while true; do
+               while :; do
                        vpn_status="$("${trm_ubuscmd}" -S call network.interface."${vpn_iface}" status 2>/dev/null | "${trm_jsoncmd}" -ql1 -e '@.up')"
                        if [ "${vpn_status}" = "true" ]; then
                                net_status="$(f_net)"
@@ -73,8 +77,8 @@ if [ "${vpn}" = "1" ] && [ "${vpn_action%_*}" = "enable" ]; then
                                "${trm_logger}" -p "info" -t "trm-vpn  [${$}]" "${vpn_service} client connection can't be established '${vpn_iface}/${vpn_instance:-"-", rc: ${net_status:-"-"}}'" 2>/dev/null
                                return 1
                        fi
-                       sleep 1
                        cnt="$((cnt + 1))"
+                       sleep 1
                done
        fi
 elif { [ "${vpn}" != "1" ] && [ "${vpn_action%_*}" = "enable" ]; } || [ "${vpn_action}" = "disable" ]; then