You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
949 lines
36 KiB
Bash
949 lines
36 KiB
Bash
#!/bin/sh
|
|
# Copyright (c) 2015-2018 Qualcomm Technologies, Inc.
|
|
#
|
|
# All Rights Reserved.
|
|
# Confidential and Proprietary - Qualcomm Technologies, Inc.
|
|
#
|
|
# 2015-2016 Qualcomm Atheros, Inc.
|
|
#
|
|
# All Rights Reserved.
|
|
# Qualcomm Atheros Confidential and Proprietary.
|
|
|
|
WIFIMON_DEBUG_OUTOUT=0
|
|
|
|
# Set this to a filename to log all commands executed.
|
|
# The output of relevant commands will be appended to the file.
|
|
WIFIMON_DEBUG_COMMAND_FILE=
|
|
|
|
WIFIMON_STATE_NOT_ASSOCIATED='NotAssociated'
|
|
WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS='AutoConfigInProgress'
|
|
WIFIMON_STATE_MEASURING='Measuring'
|
|
WIFIMON_STATE_WPS_TIMEOUT='WPSTimeout'
|
|
WIFIMON_STATE_ASSOC_TIMEOUT='AssocTimeout'
|
|
WIFIMON_STATE_RE_BACKHAUL_GOOD='RE_BackhaulGood'
|
|
WIFIMON_STATE_RE_BACKHAUL_FAIR='RE_BackhaulFair'
|
|
WIFIMON_STATE_RE_BACKHAUL_POOR='RE_BackhaulPoor'
|
|
WIFIMON_STATE_RE_SWITCH_BSTA='RE_SwitchingBSTA'
|
|
|
|
WIFIMON_PIPE_NAME='/var/run/repacd.pipe'
|
|
|
|
. /lib/functions.sh
|
|
. /lib/functions/whc-network.sh
|
|
|
|
# State information
|
|
sta_iface_24g='' sta_iface_24g_config_name=''
|
|
sta_iface_5g='' sta_iface_5g_config_name='' unknown_ifaces=0
|
|
assoc_timeout_logged=0 wps_timeout_logged=0
|
|
wps_in_progress=0 wps_start_time=''
|
|
wps_stabilization=0 wps_assoc_count=0
|
|
assoc_start_time='' last_assoc_state=0
|
|
ping_running=0 last_ping_gw_ip=
|
|
rssi_num=0 rssi_filename=
|
|
bsta_max_preference=
|
|
|
|
# Config parameters
|
|
rssi_samples=''
|
|
backhaul_rssi_2='' backhaul_rssi_5='' backhaul_rssi_offset=''
|
|
min_wps_assoc=
|
|
assoc_timeout='' wps_timeout=''
|
|
measuring_cnt=0 measuring_attempts=0
|
|
force_bsses_down_on_all_bsta_switches=0
|
|
cnt_5g_attempts=0 max_5g_attempts=0
|
|
|
|
# Emit a message at debug level.
|
|
# input: $1 - the message to log
|
|
__repacd_wifimon_debug() {
|
|
local stderr=''
|
|
if [ "$WIFIMON_DEBUG_OUTOUT" -gt 0 ]; then
|
|
stderr='-s'
|
|
fi
|
|
|
|
logger $stderr -t repacd.wifimon -p user.debug "$1"
|
|
}
|
|
|
|
# Log the output of a command to a file (when enabled).
|
|
# This is a nop unless WIFIMON_DEBUG_COMMAND_FILE is set.
|
|
# input: $1 - command output to log
|
|
__repacd_wifimon_dump_cmd() {
|
|
if [ -n "$WIFIMON_DEBUG_COMMAND_FILE" ]; then
|
|
touch $WIFIMON_DEBUG_COMMAND_FILE
|
|
{ date; echo "$1"; echo; } >> $WIFIMON_DEBUG_COMMAND_FILE
|
|
fi
|
|
}
|
|
|
|
# Emit a message at info level.
|
|
__repacd_wifimon_info() {
|
|
local stderr=''
|
|
if [ "$WIFIMON_DEBUG_OUTOUT" -gt 0 ]; then
|
|
stderr='-s'
|
|
fi
|
|
|
|
logger $stderr -t repacd.wifimon -p user.info "$1"
|
|
}
|
|
|
|
# Obtain a timestamp from the system.
|
|
#
|
|
# These timestamps will be monontonically increasing and be unaffected by
|
|
# any time skew (eg. via NTP or manual date commands).
|
|
#
|
|
# output: $1 - the timestamp as an integer (with any fractional time truncated)
|
|
__repacd_wifimon_get_timestamp() {
|
|
timestamp=$(cut -d' ' -f1 < /proc/uptime | cut -d. -f 1)
|
|
eval "$1=$timestamp"
|
|
}
|
|
|
|
# Terminate any background ping that may be running.
|
|
# If no background pings are running, this will be a nop.
|
|
__repacd_stop_ping() {
|
|
if [ "$ping_running" -gt 0 ]; then
|
|
kill "$(jobs -p)"
|
|
ping_running=0
|
|
__repacd_wifimon_debug "Stopped ping to GW IP $last_ping_gw_ip"
|
|
fi
|
|
|
|
if [ -n "$rssi_filename" ]; then
|
|
# Clean up the temporary file
|
|
rm -f $rssi_filename
|
|
rssi_filename=
|
|
fi
|
|
}
|
|
|
|
# Start a background ping to the gateway address (if it can be resolved).
|
|
# This helps ensure the RSSI values are updated (as firmware will not report
|
|
# updates if only beacons are being received on the STA interface).
|
|
# input: $1 - network: the name of the network being managed
|
|
# return: 0 if the ping was started or is already running; otherwise 1
|
|
__repacd_start_ping() {
|
|
gw_ip=$(route -n | grep ^0.0.0.0 | grep "br-$1" | awk '{print $2}')
|
|
if [ -n "$gw_ip" ]; then
|
|
if [ ! "$gw_ip" = "$last_ping_gw_ip" ]; then
|
|
# First need to kill the existing one due to the IP change.
|
|
__repacd_stop_ping
|
|
# This will leave ping_running set to 0.
|
|
fi
|
|
|
|
if [ "$ping_running" -eq 0 ]; then
|
|
__repacd_wifimon_debug "Pinging GW IP $gw_ip"
|
|
|
|
# Unfortunately the busybox ping command does not support an
|
|
# interval. Thus, we can only ping once per second so there will
|
|
# only be a handful of measurements over the course of our RSSI
|
|
# sampling.
|
|
ping "$gw_ip" > /dev/null &
|
|
ping_running=1
|
|
last_ping_gw_ip=$gw_ip
|
|
fi
|
|
|
|
# Ping is running now or was started.
|
|
return 0
|
|
fi
|
|
|
|
__repacd_wifimon_info "Failed to resolve GW when starting ping; will re-attempt"
|
|
return 1
|
|
}
|
|
|
|
# Determine if the gateway is reachable.
|
|
#
|
|
# Ideally this would be limited to only the 5 GHz STA interface, but there
|
|
# is no good way to do this (since packets would need to be received on the
|
|
# bridge interface).
|
|
#
|
|
# return: 0 if the gateway is reachable; otherwise 1
|
|
__repacd_is_gw_reachable() {
|
|
if [ -n "$last_ping_gw_ip" ]; then
|
|
if ping -c 1 -W 1 "${last_ping_gw_ip}" > /dev/null; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Gateway is unknown or is not reachable
|
|
return 1
|
|
}
|
|
|
|
# Determine if the STA interface named is current associated and active.
|
|
#
|
|
# input: $1 - sta_iface: the name of the interface (eg. ath01)
|
|
# return: 0 if associated; 1 if not associated or empty interface name
|
|
__repacd_wifimon_is_active_assoc() {
|
|
local sta_iface=$1
|
|
if [ -n "$sta_iface" ]; then
|
|
local assoc_str=
|
|
assoc_str=$(iwconfig "$sta_iface")
|
|
__repacd_wifimon_dump_cmd "State of $sta_iface: $assoc_str"
|
|
|
|
if echo "$assoc_str" | grep 'Access Point: ' | grep -v 'Not-Associated' > /dev/null; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
else
|
|
# An unknown STA interface is considered not associated.
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Determine if the STA interface named is current associated.
|
|
#
|
|
# Note that for the purposes of this function, an empty interface name is
|
|
# considered associated. This is done because in some configurations, only
|
|
# one interface is enabled.
|
|
#
|
|
# input: $1 - sta_iface: the name of the interface (eg. ath01)
|
|
# return: 0 if associated or if the interface name is empty; otherwise 1
|
|
__repacd_wifimon_is_assoc() {
|
|
local sta_iface=$1
|
|
if [ -n "$sta_iface" ];
|
|
then
|
|
if __repacd_wifimon_is_active_assoc "$sta_iface"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
else
|
|
# An unknown STA interface is considered associated.
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Determine if the STA association is stable enough to be able to start
|
|
# the next step of the process.
|
|
# return: 0 if the association is stable; non-zero if it is not yet deemed
|
|
# stable
|
|
__repacd_wifimon_is_assoc_stable() {
|
|
if [ "$wps_stabilization" -gt 0 ]; then
|
|
if [ "$wps_assoc_count" -ge "$min_wps_assoc" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
else
|
|
# No stabilization in progress
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Determine if the STA is associated and update the state accordingly.
|
|
# input: $1 - network: the name of the network being managed
|
|
# input: $2 - cur_re_mode: the currently configured range extender mode
|
|
# input: $3 - cur_re_submode: the currently configured range extender sub-mode
|
|
# input: $4 - whether this is a check during init for a restart triggered
|
|
# by mode switching
|
|
# output: $5 - state: the variable to update with the new state name (if there
|
|
# was a change)
|
|
# output: $6 - re_mode: the desired range extender mode
|
|
# output: $7 - re_submode: the desired range extender sub-mode
|
|
# return: 0 if associated; otherwise 1
|
|
__repacd_wifimon_check_associated() {
|
|
local network=$1
|
|
local associated=0
|
|
|
|
if __repacd_wifimon_is_assoc $sta_iface_24g \
|
|
&& __repacd_wifimon_is_assoc $sta_iface_5g; then
|
|
associated=1
|
|
fi
|
|
|
|
if [ "$associated" -gt 0 ]; then
|
|
# Only update the LED state if we transitioned from not associated
|
|
# to associated.
|
|
if [ "$last_assoc_state" -eq 0 ]; then
|
|
if [ "$wps_in_progress" -gt 0 ]; then
|
|
# If WPS was triggered, it could take some time for the
|
|
# interfaces to settle into their final state. Thus, update
|
|
# the start time for the measurement to the point at which
|
|
# the WPS button was pressed.
|
|
assoc_start_time=$wps_start_time
|
|
|
|
# Clear this as we only want to extend the association time
|
|
# for this one instance. All subsequent ones should be based
|
|
# on the time we detect a disassociation (unless WPS is
|
|
# triggered again).
|
|
wps_start_time=''
|
|
|
|
# Clear this flag so that we now use the association timer
|
|
# instead of the WPS one.
|
|
wps_in_progress=0
|
|
|
|
wps_stabilization=1
|
|
fi
|
|
|
|
if [ "$wps_stabilization" -gt 0 ]; then
|
|
wps_assoc_count=$((wps_assoc_count + 1))
|
|
__repacd_wifimon_debug "Assoc post WPS (#$wps_assoc_count)"
|
|
fi
|
|
|
|
if __repacd_wifimon_is_assoc_stable; then
|
|
# Assume we are going into the measuring state. This may be
|
|
# overruled if we decide to switch to a different bSTA
|
|
# interface.
|
|
eval "$5=$WIFIMON_STATE_MEASURING"
|
|
assoc_start_time=''
|
|
last_assoc_state=1
|
|
wps_stabilization=0
|
|
|
|
# Restart the measurements. We do not remember any past
|
|
# ones as they might not reflect the current state (eg.
|
|
# if the root AP was moved).
|
|
rssi_num=0
|
|
|
|
if [ "$wps_stabilization" -gt 0 ]; then
|
|
# Once WPS completes, switch to the most preferred bSTA.
|
|
local sta_iface_config=''
|
|
if [ -n "$sta_iface_24g" ]; then
|
|
sta_iface_config="$sta_iface_24g_config_name"
|
|
else
|
|
sta_iface_config="$sta_iface_5g_config_name"
|
|
fi
|
|
|
|
# Be optimistic that the most preferred bSTA will have a
|
|
# good link, so don't force the BSSes down.
|
|
local force_bsses_down=0
|
|
__repacd_wifimon_select_next_bsta "$sta_iface_config" \
|
|
$force_bsses_down \
|
|
"$WIFIMON_STATE_MEASURING" "$5"
|
|
|
|
# Although we're stable, since in most cases we're going to
|
|
# change the bSTA radio, we don't want to proceed into the
|
|
# measuring state below. If it turns out we didn't switch
|
|
# to a new radio, we'll pick up the measurements in the
|
|
# next iteration.
|
|
return 0
|
|
fi
|
|
else
|
|
# Pretend like we are not associated since we need it to be
|
|
# stable.
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Association is considered stable. Measure the link RSSI.
|
|
if [ "$rssi_num" -le "$rssi_samples" ] && \
|
|
__repacd_start_ping "$network"; then
|
|
__repacd_wifimon_measure_link "$network" "$5"
|
|
fi
|
|
return 0
|
|
elif [ "$wps_in_progress" -eq 0 ] && [ "$wps_stabilization" -eq 0 ]; then
|
|
# Record the first time we detected ourselves as not being associated.
|
|
# This will drive a timer in the check function that will change the
|
|
# state if we stay disassociated for too long.
|
|
if [ -z "$assoc_start_time" ]; then
|
|
__repacd_wifimon_get_timestamp assoc_start_time
|
|
fi
|
|
|
|
last_assoc_state=0
|
|
eval "$5=$WIFIMON_STATE_NOT_ASSOCIATED"
|
|
elif [ "$wps_in_progress" -gt 0 ]; then
|
|
if [ "$wps_timeout_logged" -eq 0 ] && [ "$assoc_timeout_logged" -eq 0 ]; then
|
|
# Suppress logs after we've timed out
|
|
__repacd_wifimon_debug "Auto config in progress - not assoc"
|
|
fi
|
|
|
|
wps_assoc_count=0
|
|
fi
|
|
|
|
# Not associated and WPS is in progress. No LED update.
|
|
return 1
|
|
}
|
|
|
|
# Find the median from the samples file.
|
|
# This is a crude way to compute the median when the number of
|
|
# samples is odd. It is not strictly correct for an even number
|
|
# of samples since it does not compute the average of the two
|
|
# samples in the middle and rather just takes the lower one, but
|
|
# this should be sufficient for our purposes. The average is not
|
|
# performed due to the values being on the logarithmic scale and
|
|
# because shell scripts do not directly support floating point
|
|
# arithmetic.
|
|
# input: $1 - Samples filename
|
|
# input: $2 - Number of samples in the samples file
|
|
# output: $3 - computed Median
|
|
__repacd_wifimon_compute_median() {
|
|
local median median_index
|
|
local samples_filename="$1"
|
|
|
|
median_index=$((($2 + 1) / 2))
|
|
median=$(sort -n < "$samples_filename" | head -n $median_index | tail -n 1)
|
|
|
|
eval "$3=$median"
|
|
}
|
|
|
|
# Measure the RSSI to the serving AP and update the state accordingly.
|
|
# input: $1 - network: the name of the network being monitored
|
|
# output: $2 - state: the variable to update with the new state name (if there
|
|
# was a change)
|
|
__repacd_wifimon_measure_link() {
|
|
local rssi
|
|
|
|
if ! __repacd_is_gw_reachable; then
|
|
if [ -n "$last_ping_gw_ip" ]; then
|
|
__repacd_wifimon_debug "GW ${last_ping_gw_ip} not reachable"
|
|
else
|
|
__repacd_wifimon_debug "GW unknown"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
if [ "$rssi_num" -eq 0 ]; then
|
|
if [ "$measuring_cnt" -gt 0 ]; then
|
|
__repacd_wifimon_debug "Measurement failed attempt # $measuring_cnt"
|
|
fi
|
|
measuring_cnt=$((measuring_cnt + 1))
|
|
fi
|
|
|
|
if [ "$measuring_cnt" -gt "$measuring_attempts" ]; then
|
|
# Could not complete measurements with this interface as a bSTA.
|
|
#
|
|
# Try switching to the next most preferred interface.
|
|
# Since this interface was so flaky, the next one might be too, so
|
|
# tell the fronthaul manager to force the BSSes down at the next
|
|
# startup.
|
|
local force_bsses_down=1
|
|
__repacd_wifimon_select_next_bsta "$sta_iface_config_name" \
|
|
$force_bsses_down "$WIFIMON_STATE_RE_BACKHAUL_POOR" "$2"
|
|
return
|
|
fi
|
|
|
|
# Currently only one STA interface is allocated at a time. Determine the
|
|
# active one so that it can be used to check the RSSI.
|
|
local sta_iface=''
|
|
local rssi_threshold=''
|
|
local rssi_led_state='' # state of LEDs if above RSSI threshold
|
|
if [ -n "$sta_iface_5g" ]; then
|
|
sta_iface="$sta_iface_5g"
|
|
sta_iface_config_name="$sta_iface_5g_config_name"
|
|
rssi_threshold="$backhaul_rssi_5"
|
|
rssi_led_state="$WIFIMON_STATE_RE_BACKHAUL_GOOD"
|
|
else
|
|
sta_iface="$sta_iface_24g"
|
|
sta_iface_config_name="$sta_iface_24g_config_name"
|
|
rssi_threshold="$backhaul_rssi_2"
|
|
rssi_led_state="$WIFIMON_STATE_RE_BACKHAUL_FAIR"
|
|
fi
|
|
|
|
rssi=$(iwconfig $sta_iface | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}')
|
|
|
|
# We explicitly ignore clearly bogus values. -95 dBm has been seen in
|
|
# some instances where the STA is not associated by the time the RSSI
|
|
# check is done. The check against 0 tries to guard against scenarios
|
|
# where the firmware has yet to report an RSSI value (although this may
|
|
# never happen if the RSSI gets primed through the association messaging).
|
|
if [ "$rssi" -gt -95 ] && [ "$rssi" -lt 0 ]; then
|
|
if [ "$rssi_num" -lt "$rssi_samples" ]; then
|
|
__repacd_wifimon_debug "RSSI sample #$rssi_num = $rssi dBm"
|
|
|
|
# Ignore the very first sample since it is taken at the same time
|
|
# the ping is started (and thus the RSSI might not have been
|
|
# updated).
|
|
if [ "$rssi_num" -eq 0 ]; then
|
|
rssi_filename=$(mktemp /tmp/repacd-rssi.XXXXXX)
|
|
else
|
|
# Not the first sample
|
|
echo "$rssi" >> "$rssi_filename"
|
|
fi
|
|
rssi_num=$((rssi_num + 1))
|
|
elif [ "$rssi_num" -eq "$rssi_samples" ]; then
|
|
__repacd_wifimon_debug "RSSI sample #$rssi_num = $rssi dBm"
|
|
|
|
# We will take one more sample and then draw the conclusion.
|
|
# No further measurements will be taken (although this may be
|
|
# changed in the future).
|
|
echo "$rssi" >> "$rssi_filename"
|
|
|
|
# We got the required number of samples, now derive the median rssi.
|
|
local rssi_median
|
|
__repacd_wifimon_compute_median "$rssi_filename" $rssi_num rssi_median
|
|
__repacd_wifimon_debug "Median RSSI = $rssi_median dBm"
|
|
__repacd_wifimon_debug "RSSI threshold = $rssi_threshold dBm"
|
|
|
|
measuring_cnt=0
|
|
rssi_num=$((rssi_num + 1)) # to prevent future samples
|
|
|
|
# We have our measurement, so the ping is no longer needed.
|
|
__repacd_stop_ping
|
|
|
|
# In case we disassociate after this, we will want to start the
|
|
# association timer again, so clear our state of the last time we
|
|
# started it so that it can be started afresh upon disassociation.
|
|
assoc_start_time=''
|
|
|
|
if [ "$rssi_median" -gt "$rssi_threshold" ]; then
|
|
local keep_24g_bsta_threshold=$((rssi_threshold + backhaul_rssi_offset))
|
|
if [ "$sta_iface" = "$sta_iface_24g" ]; then
|
|
if [ "$cnt_5g_attempts" -ge "$max_5g_attempts" ] ||
|
|
[ "$rssi_median" -lt "$keep_24g_bsta_threshold" ]; then
|
|
eval "$2=$rssi_led_state"
|
|
else
|
|
# 2.4 GHz is good enough that 5 GHz might be viable. Try it again.
|
|
__repacd_wifimon_debug "2.4 GHz good; considering 5 GHz"
|
|
|
|
# Let's be optimistic that 5 GHz will be good enough
|
|
# and thus we should leave the BSSes up at startup.
|
|
local force_bsses_down=0
|
|
__repacd_wifimon_select_next_bsta "$sta_iface_config_name" \
|
|
$force_bsses_down "$rssi_led_state" "$2"
|
|
return
|
|
fi
|
|
else
|
|
# 5 GHz bSTA with sufficient; we are done
|
|
eval "$2=$rssi_led_state"
|
|
fi
|
|
elif [ "$sta_iface" = "$sta_iface_24g" ]; then
|
|
__repacd_wifimon_debug "2.4 GHz bSTA weak; not re-attempting 5 GHz"
|
|
eval "$2=$WIFIMON_STATE_RE_BACKHAUL_POOR"
|
|
else
|
|
# Try next best bSTA radio
|
|
__repacd_wifimon_debug "5 GHz bSTA too weak; trying next radio"
|
|
|
|
# Until we know we can get a good bSTA link, let's be
|
|
# conservative and bring the BSSes down at startup.
|
|
local force_bsses_down=1
|
|
__repacd_wifimon_select_next_bsta "$sta_iface_config_name" \
|
|
$force_bsses_down "$WIFIMON_STATE_RE_BACKHAUL_POOR" "$2"
|
|
return
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Determine the radio that is next most preferred radio.
|
|
#
|
|
# If the current radio's preference value is not set, pick the highest
|
|
# preference radio.
|
|
#
|
|
# input: $1 config: section name
|
|
# input: $2 cur_pref: the preference level for the current bSTA
|
|
# output: $3 preferred_radio: the radio with the highest preference, but below
|
|
# the cur_pref value if it is set
|
|
__repacd_wifimon_resolve_next_bsta_radio() {
|
|
local config="$1"
|
|
local cur_pref="$2"
|
|
|
|
local bsta_preference=''
|
|
config_get bsta_preference "$config" repacd_map_bsta_preference 0
|
|
|
|
# Skip radios that have no preference set. Maybe the OEM never wants
|
|
# to use that radio.
|
|
if [ -n "$bsta_preference" ]; then
|
|
if [ -n "$cur_pref" ]; then
|
|
if [ "$bsta_preference" -lt "$cur_pref" ] &&
|
|
[ "$bsta_preference" -gt "$bsta_max_preference" ]; then
|
|
eval "$3=$config"
|
|
bsta_max_preference="$bsta_preference"
|
|
fi
|
|
else
|
|
if [ "$bsta_preference" -gt "$bsta_max_preference" ]; then
|
|
eval "$3=$config"
|
|
bsta_max_preference="$bsta_preference"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Mark the desired radio as selected for the bSTA.
|
|
#
|
|
# If a radio is marked as selected (using the repacd_map_bsta_selected config
|
|
# option), it will be used. If instead none is marked, the radio with the
|
|
# highest repacd_map_bsta_pref value will be used.
|
|
#
|
|
# input: $1 config: section name
|
|
# input: $2 selected_radio: the radio that should be selected
|
|
__repacd_wifimon_select_bsta_radio() {
|
|
local config="$1"
|
|
local selected_radio="$2"
|
|
|
|
if [ "$config" = "$selected_radio" ]; then
|
|
uci_set wireless "$config" repacd_map_bsta_selected '1'
|
|
else
|
|
uci_set wireless "$config" repacd_map_bsta_selected '0'
|
|
fi
|
|
}
|
|
|
|
# Update the radio on which to instantiate the bSTA.
|
|
#
|
|
# If the currently selected bSTA is 2.4 GHz, select the highest priority radio.
|
|
# Otherwise, select the highest priority radio that is less than the current
|
|
# priority.
|
|
#
|
|
# input: $1 - config: the name of the config section for the bSTA
|
|
# input: $2 - force_bsses_down: whether to force the BSSes down on the next
|
|
# startup
|
|
# input: $3 - state_no_bsta_radios: the state to transition into if there are
|
|
# no other bSTA radios to switch to
|
|
# output: $4 - state: the variable to update with the new state name (if there
|
|
# was a change)
|
|
__repacd_wifimon_select_next_bsta() {
|
|
local config="$1"
|
|
local force_bsses_down="$2"
|
|
local state_no_bsta_radios="$3"
|
|
|
|
# Resolve the current preference value
|
|
local device
|
|
config_load wireless
|
|
config_get device "$config" device
|
|
|
|
local cur_pref
|
|
config_get cur_pref "$device" repacd_map_bsta_preference
|
|
|
|
if [ -n "$sta_iface_24g" ]; then
|
|
# We want to take the maximum one
|
|
cur_pref=''
|
|
fi
|
|
|
|
local preferred_radio=''
|
|
bsta_max_preference=0
|
|
config_foreach __repacd_wifimon_resolve_next_bsta_radio wifi-device \
|
|
"$cur_pref" preferred_radio
|
|
|
|
if [ "$device" != "$preferred_radio" ]; then
|
|
__repacd_wifimon_debug "Selected $preferred_radio as next bSTA"
|
|
|
|
# If switching from 5 GHz to 2.4 GHz, increment the count to record we
|
|
# have tried all 5 GHz bSTAs. The assumption here is that 2.4 GHz will
|
|
# always come after 5 GHz in the preference order.
|
|
if [ -n "$sta_iface_5g" ] && ! whc_is_5g_radio "$preferred_radio"; then
|
|
cnt_5g_attempts=$((cnt_5g_attempts + 1))
|
|
uci_set repacd MAPWiFiLink '5gAttemptsCount' "$cnt_5g_attempts"
|
|
uci_commit repacd
|
|
|
|
__repacd_wifimon_debug "Completed 5 GHz bSTA attempt #$cnt_5g_attempts"
|
|
fi
|
|
|
|
config_foreach __repacd_wifimon_select_bsta_radio wifi-device \
|
|
"$preferred_radio"
|
|
uci_commit wireless
|
|
|
|
if [ "$force_bsses_down_on_all_bsta_switches" -gt 0 ]; then
|
|
force_bsses_down=1
|
|
fi
|
|
|
|
__repacd_wifimon_debug "Updating ForceDownOnStart to $force_bsses_down"
|
|
uci_set repacd FrontHaulMgr ForceDownOnStart "$force_bsses_down"
|
|
uci_commit repacd
|
|
|
|
eval "$4=$WIFIMON_STATE_RE_SWITCH_BSTA"
|
|
else
|
|
__repacd_wifimon_debug "No other bSTA radios available"
|
|
eval "$4=$state_no_bsta_radios"
|
|
fi
|
|
}
|
|
|
|
# Determine if a provided amount of time has elapsed.
|
|
# input: $1 - start_time: the timestamp (in seconds)
|
|
# input: $2 - duration: the amount of time to check against (in seconds)
|
|
# return: 0 on timeout; non-zero if no timeout
|
|
__repacd_wifimon_is_timeout() {
|
|
local start_time=$1
|
|
local duration=$2
|
|
|
|
# Check if the amount of elapsed time exceeds the timeout duration.
|
|
local cur_time
|
|
__repacd_wifimon_get_timestamp cur_time
|
|
local elapsed_time=$((cur_time - start_time))
|
|
if [ "$elapsed_time" -gt "$duration" ]; then
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Check whether the given interface is the STA interface on the desired
|
|
# network and the desired band.
|
|
#
|
|
# input: $1 - config: the name of the interface config section
|
|
# input: $2 - network: the name of the network to which the STA interface
|
|
# must belong to be matched
|
|
# output: $3 - iface: the resolved STA interface name on 2.4 GHz (if found)
|
|
# output: $4 - iface_config_name: the resolved name of the config section
|
|
# for the STA interface on 2.4 GHz (if found)
|
|
# output: $5 - iface: the resolved STA interface name on 5 GHz (if found)
|
|
# output: $6 - iface_config_name: the resolved name of the config section
|
|
# for the STA interface on 5 GHz (if found)
|
|
# output: $7 - unknown_ifaces: whether any Wi-Fi interfaces are as yet
|
|
# unknown (in terms of their interface name)
|
|
__repacd_wifimon_is_sta_iface() {
|
|
local config="$1"
|
|
local network_to_match="$2"
|
|
local iface disabled mode device hwmode
|
|
|
|
config_get network "$config" network
|
|
config_get iface "$config" ifname
|
|
config_get disabled "$config" disabled '0'
|
|
config_get mode "$config" mode
|
|
config_get device "$config" device
|
|
config_get hwmode "$device" hwmode
|
|
|
|
if [ "$hwmode" != "11ad" ]; then
|
|
if [ "$network" = "$network_to_match" ] && [ -n "$iface" ] && [ "$mode" = "sta" ] && \
|
|
[ "$disabled" -eq 0 ]; then
|
|
if whc_is_5g_vap "$config"; then
|
|
eval "$5=$iface"
|
|
eval "$6=$config"
|
|
else
|
|
eval "$3=$iface"
|
|
eval "$4=$config"
|
|
fi
|
|
elif [ -z "$iface" ] && [ "$disabled" -eq 0 ]; then
|
|
# If an interface is showing as enabled but no name is known for it,
|
|
# mark it as such. Without doing this, we can resolve the interface
|
|
# names improperly.
|
|
eval "$7=1"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Initialize the sta_iface_5g variable with the STA interface that is enabled
|
|
# on the specified network (if any).
|
|
# input: $1 - network: the name of the network being managed
|
|
__repacd_wifimon_get_sta_iface() {
|
|
unknown_ifaces=0
|
|
|
|
config_load wireless
|
|
config_foreach __repacd_wifimon_is_sta_iface wifi-iface "$1" \
|
|
sta_iface_24g sta_iface_24g_config_name \
|
|
sta_iface_5g sta_iface_5g_config_name unknown_ifaces
|
|
|
|
if [ "$unknown_ifaces" -gt 0 ]; then
|
|
# Clear out everything because we cannot be certain we have the
|
|
# right names (eg. interfaces may not all be up yet).
|
|
sta_iface_24g=
|
|
sta_iface_24g_config_name=
|
|
sta_iface_5g=
|
|
sta_iface_5g_config_name=
|
|
fi
|
|
}
|
|
|
|
# Initialize the Wi-Fi monitoring logic with the name of the network being
|
|
# monitored.
|
|
# input: $1 - network: the name of the network being managed
|
|
# input: $2 - cur_re_mode: the current operating range extender mode
|
|
# input: $3 - cur_re_submode: the current operating range extender submode
|
|
# input: $4 - autoconfig: whether it was an auto-config restart
|
|
# output: $5 - state: the name of the initial state
|
|
# output: $6 - new_re_mode: the resolved range extender mode
|
|
# output: $7 - new_re_submode: the resolved range extender submode
|
|
repacd_wifimon_init() {
|
|
# Resolve the STA interfaces.
|
|
# Here we assume that if we have the 5 GHz interface, that is sufficient,
|
|
# as not all modes will have a 2.4 GHz interface.
|
|
__repacd_wifimon_get_sta_iface "$1"
|
|
if [ -n "$sta_iface_5g" ] || [ -n "$sta_iface_24g" ]; then
|
|
if [ -n "$sta_iface_24g" ]; then
|
|
__repacd_wifimon_debug "Resolved 2.4 GHz STA interface to $sta_iface_24g"
|
|
__repacd_wifimon_debug "2.4 GHz STA interface section $sta_iface_24g_config_name"
|
|
fi
|
|
|
|
if [ -n "$sta_iface_5g" ]; then
|
|
__repacd_wifimon_debug "Resolved 5 GHz STA interface to $sta_iface_5g"
|
|
__repacd_wifimon_debug "5 GHz STA interface section $sta_iface_5g_config_name"
|
|
fi
|
|
|
|
# First resolve the config parameters.
|
|
config_load repacd
|
|
config_get min_wps_assoc 'MAPWiFiLink' 'MinAssocCheckPostWPS' '5'
|
|
config_get wps_timeout 'MAPWiFiLink' 'WPSTimeout' '120'
|
|
config_get assoc_timeout 'MAPWiFiLink' 'AssociationTimeout' '300'
|
|
config_get rssi_samples 'MAPWiFiLink' 'RSSINumMeasurements' '5'
|
|
config_get backhaul_rssi_2 'MAPWiFiLink' 'BackhaulRSSIThreshold_2' '-78'
|
|
config_get backhaul_rssi_5 'MAPWiFiLink' 'BackhaulRSSIThreshold_5' '-78'
|
|
config_get backhaul_rssi_offset 'MAPWiFiLink' 'BackhaulRSSIThreshold_offset' '10'
|
|
config_get max_5g_attempts 'MAPWiFiLink' 'Max5gAttempts' '3'
|
|
config_get cnt_5g_attempts 'MAPWiFiLink' '5gAttemptsCount' '0'
|
|
config_get measuring_attempts 'MAPWiFiLink' 'MaxMeasuringStateAttempts' '3'
|
|
config_get force_bsses_down_on_all_bsta_switches 'MAPWiFiLink' \
|
|
'ForceBSSesDownOnAllBSTASwitches' '0'
|
|
|
|
# Create ourselves a named pipe so we can be informed of WPS push
|
|
# button events.
|
|
if [ -e $WIFIMON_PIPE_NAME ]; then
|
|
rm -f $WIFIMON_PIPE_NAME
|
|
fi
|
|
|
|
mkfifo $WIFIMON_PIPE_NAME
|
|
|
|
# If already associated, go to the InProgress state.
|
|
__repacd_wifimon_check_associated "$1" "$2" "$3" "$4" "$5" "$6" "$7"
|
|
fi
|
|
# Otherwise, must be operating in CAP mode.
|
|
}
|
|
|
|
# Check the status of the Wi-Fi link (WPS, association, and RSSI).
|
|
# input: $1 - network: the name of the network being managed
|
|
# input: $2 - cur_re_mode: the currently configured range extender mode
|
|
# input: $3 - cur_re_submode: the currently configured range extender sub-mode
|
|
# output: $4 - state: the name of the new state (only set upon a change)
|
|
# output: $5 - re_mode: the desired range extender mode (updated only once
|
|
# the link to the AP is considered stable)
|
|
# output: $6 - re_submode: the desired range extender submode (updated only once
|
|
# the link to the AP is considered stable)
|
|
repacd_wifimon_check() {
|
|
local wps_pressed=0
|
|
if [ -n "$sta_iface_24g" ] || [ -n "$sta_iface_5g" ]; then
|
|
local wps_pbc
|
|
if read -r -t 1 wps_pbc <>$WIFIMON_PIPE_NAME; then
|
|
__repacd_wifimon_debug "Received $wps_pbc on wifimon pipe"
|
|
|
|
eval "$4=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
|
|
wps_in_progress=1
|
|
__repacd_wifimon_get_timestamp wps_start_time
|
|
fi
|
|
|
|
if __repacd_wifimon_check_associated "$1" "$2" "$3" 0 "$4" "$5" "$6"; then
|
|
assoc_timeout_logged=0
|
|
wps_timeout_logged=0
|
|
else # not associated
|
|
# If the WPS button was pressed, do not proceed to checking the
|
|
# association timeouts. That will happen in subsequent checks.
|
|
if [ "$wps_pressed" -gt 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
if [ "$wps_in_progress" -gt 0 ]; then
|
|
if __repacd_wifimon_is_timeout $wps_start_time $wps_timeout; then
|
|
if [ "$wps_timeout_logged" -eq 0 ]; then
|
|
__repacd_wifimon_debug "WPS timeout"
|
|
wps_timeout_logged=1
|
|
fi
|
|
|
|
eval "$4=$WIFIMON_STATE_WPS_TIMEOUT"
|
|
fi
|
|
else
|
|
if __repacd_wifimon_is_timeout $assoc_start_time $assoc_timeout; then
|
|
if [ "$assoc_timeout_logged" -eq 0 ]; then
|
|
__repacd_wifimon_debug "Association timeout"
|
|
assoc_timeout_logged=1
|
|
fi
|
|
|
|
if [ -n "$sta_iface_5g" ]; then
|
|
# Try falling back to another interface
|
|
__repacd_wifimon_debug "Trying next bSTA radio"
|
|
|
|
# Since this bSTA could not associate, assume the next
|
|
# one might not be able to either. Thus, tell the
|
|
# fronthaul manager to force the BSSes down at startup.
|
|
local force_bsses_down=1
|
|
__repacd_wifimon_select_next_bsta "$sta_iface_5g_config_name" \
|
|
$force_bsses_down \
|
|
"$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS" "$4"
|
|
else
|
|
# Already on 2.4 GHz. If we cannot associate here, we
|
|
# probably cannot associate on 5 GHz.
|
|
eval "$4=$WIFIMON_STATE_ASSOC_TIMEOUT"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Hook function that, in SON mode, would determine whether to bring up/down the
|
|
# 2.4 GHz backhaul. In Multi-AP SIG mode, this is not currently relevant
|
|
# (given the 1 backhaul link assumption).
|
|
repacd_wifimon_independent_channel_check() {
|
|
/bin/true
|
|
}
|
|
|
|
# Terminate the Wi-Fi link monitoring, cleaning up any state in preparation
|
|
# for shutdown.
|
|
repacd_wifimon_fini() {
|
|
/bin/true
|
|
}
|
|
|
|
# Resolve the credentials for any VAP that has bBSS support.
|
|
#
|
|
# input: $1 - config: the name of the interface config section
|
|
# input: $2 - network: the name of the network to which the AP interface
|
|
# must belong to be matched
|
|
# output: $3 - ssid: the backhaul SSID
|
|
# output: $4 - key: the backhaul key (aka. passphrase)
|
|
# output: $5 - enc: the backhaul encryption mode
|
|
__repacd_wifimon_resolve_bbss_creds() {
|
|
local config="$1"
|
|
local network_to_match="$2"
|
|
|
|
local network mode device hwmode disabled
|
|
local ssid key enc map map_type
|
|
|
|
config_get network "$config" network
|
|
config_get mode "$config" mode
|
|
config_get device "$config" device
|
|
config_get hwmode "$device" hwmode
|
|
config_get disabled "$config" disabled '0'
|
|
|
|
config_get ssid "$config" ssid
|
|
config_get key "$config" key
|
|
config_get enc "$config" encryption
|
|
config_get map "$config" map '0'
|
|
config_get map_type "$config" MapBSSType
|
|
|
|
if [ "$hwmode" != "11ad" ]; then
|
|
if [ "$network" = "$network_to_match" ] && [ "$mode" = "ap" ] && \
|
|
[ "$disabled" -eq 0 ] && [ "$map" -gt 0 ]; then
|
|
local bbss=$(( map_type & 64 ))
|
|
if [ ${bbss} -eq 64 ]; then
|
|
# Has bBSS capabilty, so record the credentials
|
|
eval "$3='${ssid}'"
|
|
eval "$4='${key}'"
|
|
eval "$5='${enc}'"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Set the credentials on the bSTA VAP(s).
|
|
#
|
|
# input: $1 - config: the name of the interface config section
|
|
# input: $2 - network: the name of the network to which the STA interface
|
|
# must belong to be matched
|
|
# input: $3 - ssid: the backhaul SSID
|
|
# input: $4 - key: the backhaul key (aka. passphrase)
|
|
# input: $5 - enc: the backhaul encryption mode
|
|
__repacd_wifimon_set_bsta_creds() {
|
|
local config="$1"
|
|
local network_to_match="$2"
|
|
local ssid="$3"
|
|
local key="$4"
|
|
local enc="$5"
|
|
|
|
local network mode hwmode device disabled
|
|
local ssid key enc map map_type
|
|
|
|
config_get network "$config" network
|
|
config_get mode "$config" mode
|
|
config_get device "$config" device
|
|
config_get hwmode "$device" hwmode
|
|
config_get disabled "$config" disabled '0'
|
|
|
|
config_get map "$config" map '0'
|
|
config_get map_type "$config" MapBSSType
|
|
|
|
if [ "$hwmode" != "11ad" ]; then
|
|
if [ "$network" = "$network_to_match" ] && [ "$mode" = "sta" ] && \
|
|
[ "$map" -gt 0 ]; then
|
|
if [ "$map_type" -eq 128 ]; then # bSTA
|
|
uci_set wireless "$config" ssid "$ssid"
|
|
uci_set wireless "$config" key "$key"
|
|
uci_set wireless "$config" encryption "$enc"
|
|
uci_commit wireless
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Copy the backhaul credentials from a bBSS interface (or a combined
|
|
# fBSS + bBSS) to the bSTA interface. This ensures the credentials obtained
|
|
# during Ethernet onboarding can be used when switching into RE mode.
|
|
repacd_wifimon_config_bsta() {
|
|
bbss_ssid=''
|
|
bbss_key=''
|
|
bbss_enc=''
|
|
|
|
# First resolve the SSID, passphrase, and encryption from the bBSS
|
|
# capable AP interfaces.
|
|
config_load wireless
|
|
config_foreach __repacd_wifimon_resolve_bbss_creds wifi-iface "$1" \
|
|
bbss_ssid bbss_key bbss_enc
|
|
|
|
# Now apply these values to the bSTA interface.
|
|
config_foreach __repacd_wifimon_set_bsta_creds wifi-iface "$1" \
|
|
"${bbss_ssid}" "${bbss_key}" "${bbss_enc}"
|
|
}
|