travelmate: update 0.3.0

* switch to procd interface trigger
    * no additional active monitor/polling in the background
    * simplified code
* new option "trm_maxwait", how long (in seconds)
    should travelmate wait for wlan interface reload action
    (default: '20')
* documentation update

Signed-off-by: Dirk Brenken <dev@brenken.org>
This commit is contained in:
Dirk Brenken 2016-12-31 14:14:29 +01:00
parent 811bda6982
commit c4356a02d5
5 changed files with 161 additions and 250 deletions

View file

@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=travelmate PKG_NAME:=travelmate
PKG_VERSION:=0.2.7 PKG_VERSION:=0.3.0
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_LICENSE:=GPL-3.0+ PKG_LICENSE:=GPL-3.0+
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org> PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>

View file

@ -3,15 +3,15 @@
## Description ## Description
If youre planning an upcoming vacation or a business trip, taking your laptop, tablet or smartphone give you the ability to connect with friends or complete work on the go. But many hotels dont have a secure wireless network setup or youre limited on using a single device at once. Investing in a portable, mini travel router is a great way to connect all of your devices at once while having total control over your own personalized wireless network. If youre planning an upcoming vacation or a business trip, taking your laptop, tablet or smartphone give you the ability to connect with friends or complete work on the go. But many hotels dont have a secure wireless network setup or youre limited on using a single device at once. Investing in a portable, mini travel router is a great way to connect all of your devices at once while having total control over your own personalized wireless network.
A logical combination of AP+STA mode on one physical radio allows most of OpenWrt/LEDE supported router devices to connect to a wireless hotspot/station (STA) and provide a wireless access point (AP) from that hotspot at the same time. Downside of this solution: whenever the STA interface looses the connection it will go into an active scan cycle which renders the radio unusable for AP mode operation, therefore the AP is taken down if the STA looses its association. A logical combination of AP+STA mode on one physical radio allows most of OpenWrt/LEDE supported router devices to connect to a wireless hotspot/station (STA) and provide a wireless access point (AP) from that hotspot at the same time. Downside of this solution: whenever the STA interface looses the connection it will go into an active scan cycle which renders the radio unusable for AP mode operation, therefore the AP is taken down if the STA looses its association.
To avoid these kind of deadlocks, travelmate set all station interfaces in an "always off" mode, connects automatically to available hotspots and monitor & change these uplink connections automatically if required. To avoid these kind of deadlocks, travelmate set all station interfaces in an "always off" mode and connects automatically to available/configured hotspots.
## Main Features ## Main Features
* STA interfaces operating in an "always off" mode, to make sure that the AP is always accessible * STA interfaces operating in an "always off" mode, to make sure that the AP is always accessible
* zero-conf like automatic installation & setup, usually no manual changes needed
* fast uplink connections * fast uplink connections
* reliable connection tracking
* easy setup within normal OpenWrt/LEDE environment
* status & debug logging to syslog
* procd init system support * procd init system support
* procd based hotplug support, the travelmate start will be triggered by interface triggers
* status & debug logging to syslog
## Prerequisites ## Prerequisites
* [OpenWrt](https://openwrt.org) or [LEDE](https://www.lede-project.org) trunk * [OpenWrt](https://openwrt.org) or [LEDE](https://www.lede-project.org) trunk
@ -22,7 +22,7 @@ To avoid these kind of deadlocks, travelmate set all station interfaces in an "a
* install 'travelmate' (_opkg install travelmate_) * install 'travelmate' (_opkg install travelmate_)
* configure your network to support (multiple) wlan uplinks and set travelmate config options (details see below) * configure your network to support (multiple) wlan uplinks and set travelmate config options (details see below)
* set 'trm\_enabled' option in travelmate config to '1' * set 'trm\_enabled' option in travelmate config to '1'
* travelmate starts automatically during boot, triggered by procd as soon as the wireless subsystem is up & running * travelmate starts automatically during boot and will be triggered by procd interface triggers
## LuCI travelmate companion package ## LuCI travelmate companion package
* download the package [here](https://downloads.lede-project.org/snapshots/packages/x86_64/luci) * download the package [here](https://downloads.lede-project.org/snapshots/packages/x86_64/luci)
@ -37,14 +37,13 @@ To avoid these kind of deadlocks, travelmate set all station interfaces in an "a
* install the packages as described above * install the packages as described above
## Travelmate config options ## Travelmate config options
* mandatory config options: * travelmate config options:
* trm\_enabled => main switch to enable/disable the travelmate service (default: '0', disabled) * trm\_enabled => main switch to enable/disable the travelmate service (default: '0', disabled)
* trm\_loop => loop timeout in seconds for wlan monitoring (default: '30')
* trm\_maxretry => how many times should travelmate try to connect to a certain uplink, to disable this check at all set it to '0' (default: '3')
* optional config options:
* trm\_debug => enable/disable debug logging (default: '0', disabled) * trm\_debug => enable/disable debug logging (default: '0', disabled)
* trm\_device => limit travelmate to a dedicated radio, i.e 'radio0' (default: use all radios) * trm\_maxwait => how long (in seconds) should travelmate wait for wlan interface reload action (default: '20')
* trm\_maxretry => how many times should travelmate try to find an uplink after a trigger event (default: '3')
* trm\_iw => set this option to '0' to use iwinfo for wlan scanning (default: '1', use iw) * trm\_iw => set this option to '0' to use iwinfo for wlan scanning (default: '1', use iw)
* trm\_iface => restrict the procd interface trigger to a (list of) certain wan interface(s) or disable it at all (default: not set, disabled)
## Setup ## Setup
**1. configure a wwan interface in /etc/config/network:** **1. configure a wwan interface in /etc/config/network:**
@ -117,16 +116,6 @@ config wifi-iface
/etc/init.d/travelmate start /etc/init.d/travelmate start
</code></pre> </code></pre>
**Common runtime outputs (visible via logread)**
**Success:** Sun Oct 9 17:02:21 2016 user.notice root: travelmate-0.2.1[712] info : wlan interface "wwan06" connected to uplink "blackhole.nl"
**Disabled service:** Sun Oct 9 18:06:32 2016 user.notice root: travelmate-0.2.1[2379] info : travelmate is currently disabled, please set 'trm_enabled' to use this service
**Misconfigured/broken uplink:** Sun Oct 9 18:56:42 2016 user.notice root: travelmate-0.2.1[2435] info : uplink "blackhole.nl" disabled due to permanent connection failures
**Uplink disappeared:** Sun Oct 9 19:00:28 2016 user.notice root: travelmate-0.2.1[3876] info : uplink "Neffos C5L" get lost
## Support ## Support
Please join the travelmate discussion in this [forum thread](https://forum.openwrt.org/viewtopic.php?id=67697) or contact me by [mail](mailto:dev@brenken.org) Please join the travelmate discussion in this [forum thread](https://forum.openwrt.org/viewtopic.php?id=67697) or contact me by [mail](mailto:dev@brenken.org)

View file

@ -3,5 +3,7 @@
config travelmate 'global' config travelmate 'global'
option trm_enabled '0' option trm_enabled '0'
option trm_loop '30' option trm_debug '0'
option trm_maxwait '20'
option trm_maxretry '3' option trm_maxretry '3'
option trm_iw '1'

View file

@ -3,23 +3,37 @@
START=85 START=85
USE_PROCD=1 USE_PROCD=1
exec 2>/dev/null
trm_script="/usr/bin/travelmate.sh" trm_script="/usr/bin/travelmate.sh"
trm_iface="$(uci -q get travelmate.global.trm_iface)"
boot()
{
ubus -t 30 wait_for network.interface
rc_procd start_service
}
start_service() start_service()
{ {
ubus -t 30 wait_for network.wireless procd_open_instance "travelmate"
if [ $(($?)) -eq 0 ] procd_set_param env trm_procd="true"
then procd_set_param command "${trm_script}" "${@}"
procd_open_instance procd_set_param stdout 1
procd_set_param command "${trm_script}" procd_set_param stderr 1
procd_close_instance procd_close_instance
else
logger -s "travelmate [procd]:" "no wlan devices found, travelmate startup canceled"
fi
} }
service_triggers() service_triggers()
{ {
procd_add_reload_trigger "travelmate" local iface
procd_add_config_trigger "config.change" "travelmate" /etc/init.d/travelmate start
if [ -z "${trm_iface}" ]
then
procd_add_raw_trigger "interface.*.down" 1000 /etc/init.d/travelmate start
else
for iface in ${trm_iface}
do
procd_add_interface_trigger "interface.*.down" "${iface}" /etc/init.d/travelmate start
done
fi
} }

View file

@ -10,144 +10,96 @@
# #
LC_ALL=C LC_ALL=C
PATH="/usr/sbin:/usr/bin:/sbin:/bin" PATH="/usr/sbin:/usr/bin:/sbin:/bin"
trm_pid="${$}" trm_ver="0.3.0"
trm_ver="0.2.7" trm_enabled=1
trm_debug=0 trm_debug=0
trm_loop=30 trm_maxwait=20
trm_maxretry=3 trm_maxretry=3
trm_iw=1 trm_iw=1
trm_device=""
# function to prepare all relevant AP and STA interfaces f_envload()
{
# source required system libraries
#
if [ -r "/lib/functions.sh" ]
then
. "/lib/functions.sh"
else
f_log "error" "required system library not found"
fi
# load uci config and check 'enabled' option
#
option_cb()
{
local option="${1}"
local value="${2}"
eval "${option}=\"${value}\""
}
config_load travelmate
if [ ${trm_enabled} -ne 1 ]
then
f_log "info " "status ::: travelmate is currently disabled, please set 'trm_enabled' to '1' to use this service"
exit 0
fi
# check for preferred wireless tool
#
if [ ${trm_iw} -eq 1 ]
then
trm_scanner="$(which iw)"
else
trm_scanner="$(which iwinfo)"
fi
if [ -z "${trm_scanner}" ]
then
f_log "error" "status ::: no wireless tool for wlan scanning found, please install 'iw' or 'iwinfo'"
fi
}
# function to bring down all STA interfaces
# #
f_prepare() f_prepare()
{ {
local config="${1}" local config="${1}"
local mode="$(uci -q get wireless."${config}".mode)" local mode="$(uci -q get wireless."${config}".mode)"
local device="$(uci -q get wireless."${config}".device)"
local network="$(uci -q get wireless."${config}".network)" local network="$(uci -q get wireless."${config}".network)"
local disabled="$(uci -q get wireless."${config}".disabled)" local disabled="$(uci -q get wireless."${config}".disabled)"
if [ "${mode}" = "ap" ] && [ -n "${network}" ] && if [ "${mode}" = "sta" ] && [ -n "${network}" ]
([ -z "${trm_device}" ] || [ "${trm_device}" = "${device}" ])
then
f_ifname "${device}"
if [ -z "${disabled}" ] || [ "${disabled}" = "1" ]
then
f_set "none" "${config}" "${network}" "up"
fi
elif [ "${mode}" = "sta" ] && [ -n "${network}" ]
then then
trm_stalist="${trm_stalist} ${config}_${network}" trm_stalist="${trm_stalist} ${config}_${network}"
if [ -z "${disabled}" ] || [ "${disabled}" = "0" ] if [ -z "${disabled}" ] || [ "${disabled}" = "0" ]
then then
f_set "none" "${config}" "${network}" "down" uci -q set wireless."${config}".disabled=1
f_log "debug" "prepare ::: config: ${config}, interface: ${network}"
fi fi
fi fi
} }
# function to set different wlan interface status
#
f_set()
{
local change="${1}"
local config="${2}"
local interface="${3}"
local command="${4}"
if [ "${command}" = "up" ]
then
uci -q set wireless."${config}".disabled=0
ubus call network.interface."${interface}" "${command}"
trm_checklist="${trm_checklist} ${interface}"
elif [ "${command}" = "down" ]
then
uci -q set wireless."${config}".disabled=1
ubus call network.interface."${interface}" "${command}"
fi
f_log "debug" "set ::: change: ${change}, config: ${config}, interface: ${interface}, command: ${command}, checklist: ${trm_checklist}, uci-changes: $(uci -q changes wireless)"
if [ -n "$(uci -q changes wireless)" ]
then
if [ "${change}" = "commit" ]
then
uci -q commit wireless
ubus call network reload
f_check
elif [ "${change}" = "partial" ]
then
ubus call network reload
f_check
elif [ "${change}" = "defer" ]
then
uci -q revert wireless
elif [ "${change}" = "revert" ]
then
uci -q revert wireless
ubus call network reload
f_check
fi
fi
}
# function to get ap ifnames by ubus call
#
f_ifname()
{
local device="${1}"
local name cfg
json_load "$(ubus -S call network.wireless status)"
json_select "${device}"
json_get_keys if_list interfaces
json_select interfaces
for iface in ${if_list}
do
json_select "${iface}"
json_get_var name ifname
json_select "config"
json_get_var cfg mode
if [ -n "${name}" ] && [ "${cfg}" = "ap" ]
then
trm_aplist="${trm_aplist} ${name}"
fi
done
}
# function to check interface status on "up" event
#
f_check() f_check()
{ {
local interface value local ifname cnt=0 mode="${1}"
local cnt=0 trm_ifstatus="false"
for interface in ${trm_checklist} while [ ${cnt} -lt ${trm_maxwait} ]
do do
while [ $((cnt)) -lt 15 ] ifname="$(ubus -S call network.wireless status | jsonfilter -l 1 -e "@.*.interfaces[@.config.mode=\"${mode}\"].ifname")"
do if [ "${mode}" = "sta" ]
json_load "$(ubus -S call network.interface."${interface}" status)" then
json_get_var trm_status up trm_ifstatus="$(ubus -S call network.interface dump | jsonfilter -e "@.interface[@.device=\"${ifname}\"].up")"
if [ "${trm_status}" = "1" ] || [ -n "${trm_uplink}" ] else
then trm_ifstatus="$(ubus -S call network.wireless status | jsonfilter -l1 -e '@.*.up')"
f_log "debug" "check::: interface: ${interface}, status: ${trm_status}, uplink-cfg: ${trm_uplink}, uplink-ssid: ${trm_ssid}, loop-cnt: ${cnt}, error-cnt: $((trm_count_${trm_config}))" fi
json_cleanup if [ "${trm_ifstatus}" = "true" ]
break then
fi break
cnt=$((cnt+1)) fi
sleep 1 cnt=$((cnt+1))
done sleep 1
done done
if [ -n "${trm_uplink}" ] && [ "${trm_status}" = "0" ] f_log "debug" "check ::: name: ${ifname}, status: ${trm_ifstatus}, count: ${cnt}"
then
ubus call network reload
eval "trm_count_${trm_uplink}=\$((trm_count_${trm_uplink}+1))"
trm_checklist=""
trm_uplink=""
f_log "info" "uplink ${trm_ssid} get lost"
elif [ -z "${trm_uplink}" ] && [ -n "${trm_checklist}" ]
then
trm_checklist=""
fi
} }
# function to write to syslog # function to write to syslog
@ -157,125 +109,79 @@ f_log()
local class="${1}" local class="${1}"
local log_msg="${2}" local log_msg="${2}"
if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || ([ "${class}" = "debug" ] && [ $((trm_debug)) -eq 1 ])) if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || [ ${trm_debug} -eq 1 ])
then then
logger -t "travelmate-${trm_ver}[${trm_pid}] ${class}" "${log_msg}" 2>&1 logger -t "travelmate-[${trm_ver}] ${class}" "${log_msg}"
if [ "${class}" = "error" ]
then
exit 255
fi
fi fi
} }
# source required system libraries f_main()
#
if [ -r "/lib/functions.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]
then
. "/lib/functions.sh"
. "/usr/share/libubox/jshn.sh"
json_init
else
f_log "error" "required system libraries not found"
exit 255
fi
# load uci config and check 'enabled' option
#
option_cb()
{ {
local option="${1}" local ap_list ssid_list config network ssid cnt=0
local value="${2}"
eval "${option}=\"${value}\""
}
config_load travelmate f_check "sta"
if [ "${trm_ifstatus}" != "true" ]
if [ "${trm_enabled}" != "1" ]
then
f_log "info" "travelmate is currently disabled, please set 'trm_enabled' to '1' to use this service"
exit 0
fi
# check for preferred wireless tool
#
if [ $((trm_iw)) -eq 1 ]
then
trm_scanner="$(which iw)"
else
trm_scanner="$(which iwinfo)"
fi
if [ -z "${trm_scanner}" ]
then
f_log "error" "no wireless tool for wlan scanning found, please install 'iw' or 'iwinfo'"
exit 1
fi
# infinitive loop to establish and track STA uplink connections
#
while true
do
if [ -z "${trm_uplink}" ] || [ "${trm_status}" = "0" ]
then then
trm_aplist=""
trm_stalist=""
config_load wireless config_load wireless
config_foreach f_prepare wifi-iface config_foreach f_prepare wifi-iface
f_set "commit" if [ -n "$(uci -q changes wireless)" ]
if [ -z "${trm_aplist}" ]
then then
f_log "error" "no usable AP configuration found, please check '/etc/config/wireless'" uci -q commit wireless
exit 1 ubus call network reload
fi fi
for ap in ${trm_aplist} f_check "ap"
ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.*.interfaces[@.config.mode="ap"].ifname')"
f_log "debug" "main ::: ap-list: ${ap_list}, sta-list: ${trm_stalist}"
if [ -z "${ap_list}" ] || [ -z "${trm_stalist}" ]
then
f_log "error" "main ::: no usable AP/STA configuration found"
fi
for ap in ${ap_list}
do do
ubus -t 10 wait_for hostapd."${ap}" while [ ${cnt} -lt ${trm_maxretry} ]
if [ $((trm_iw)) -eq 1 ] do
then if [ ${trm_iw} -eq 1 ]
trm_ssidlist="$(${trm_scanner} dev "${ap}" scan 2>/dev/null | awk '/SSID: /{if(!seen[$0]++){printf "\"";for(i=2; i<=NF; i++)if(i==2)printf $i;else printf " "$i;printf "\" "}}')"
else
trm_ssidlist="$(${trm_scanner} "${ap}" scan | awk '/ESSID: ".*"/{ORS=" ";if (!seen[$0]++) for(i=2; i<=NF; i++) print $i}')"
fi
f_log "debug" "main ::: scan-tool: ${trm_scanner}, aplist: ${trm_aplist}, ssidlist: ${trm_ssidlist}"
if [ -n "${trm_ssidlist}" ]
then
if [ -z "${trm_stalist}" ]
then then
f_log "error" "no usable STA configuration found, please check '/etc/config/wireless'" ssid_list="$(${trm_scanner} dev "${ap}" scan 2>/dev/null | \
exit 1 awk '/SSID: /{if(!seen[$0]++){printf "\"";for(i=2; i<=NF; i++)if(i==2)printf $i;else printf " "$i;printf "\" "}}')"
else
ssid_list="$(${trm_scanner} "${ap}" scan | \
awk '/ESSID: ".*"/{ORS=" ";if (!seen[$0]++) for(i=2; i<=NF; i++) print $i}')"
fi fi
for sta in ${trm_stalist} f_log "debug" "main ::: scan-tool: ${trm_scanner}, ssidlist: ${ssid_list}"
do if [ -n "${ssid_list}" ]
trm_config="${sta%%_*}" then
trm_network="${sta##*_}" for sta in ${trm_stalist}
trm_ssid="\"$(uci -q get wireless."${trm_config}".ssid)\"" do
if [ $((trm_count_${trm_config})) -lt $((trm_maxretry)) ] || [ $((trm_maxretry)) -eq 0 ] config="${sta%%_*}"
then network="${sta##*_}"
if [ -n "$(printf "${trm_ssidlist}" | grep -Fo "${trm_ssid}")" ] ssid="\"$(uci -q get wireless."${config}".ssid)\""
if [ -n "$(printf "${ssid_list}" | grep -Fo "${ssid}")" ]
then then
f_set "partial" "${trm_config}" "${trm_network}" "up" uci -q set wireless."${config}".disabled=0
if [ "${trm_status}" = "1" ] uci -q commit wireless
then ubus call network.interface."${network}" up
trm_checklist="${trm_network}" ubus call network reload
trm_uplink="${trm_config}" f_log "info " "main ::: wwan interface connected to uplink ${ssid}"
f_set "defer" return 0
f_log "info" "wwan interface connected to uplink ${trm_ssid}"
break 2
else
f_set "revert"
eval "trm_count_${trm_config}=\$((trm_count_${trm_config}+1))"
fi
fi fi
elif [ $((trm_count_${trm_config})) -eq $((trm_maxretry)) ] && [ $((trm_maxretry)) -ne 0 ] done
then fi
eval "trm_count_${trm_config}=\$((trm_count_${trm_config}+1))" cnt=$((cnt+1))
f_log "info" "uplink ${trm_ssid} disabled due to permanent connection failures" sleep 5
fi done
done
fi
sleep 5
done done
else f_log "info " "main ::: no wwan uplink found"
f_check
if [ -n "${trm_uplink}" ]
then
sleep ${trm_loop}
fi
fi fi
done }
if [ "${trm_procd}" = "true" ]
then
f_envload
f_main
fi
exit 0