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.

2390 lines
101 KiB
Bash

#!/bin/sh
# Copyright (c) 2015-2017 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_BSSID_ASSOC_TIMEOUT='BSSIDAssocTimeout'
WIFIMON_STATE_ASSOC_TIMEOUT='AssocTimeout'
WIFIMON_STATE_RE_MOVE_CLOSER='RE_MoveCloser'
WIFIMON_STATE_RE_MOVE_FARTHER='RE_MoveFarther'
WIFIMON_STATE_RE_LOCATION_SUITABLE='RE_LocationSuitable'
WIFIMON_STATE_CL_LINK_SUFFICIENT='CL_LinkSufficient'
WIFIMON_STATE_CL_LINK_INADEQUATE='CL_LinkInadequate'
WIFIMON_STATE_CL_ACTING_AS_RE='CL_ActingAsRE'
WIFIMON_PIPE_NAME='/var/run/repacd.pipe'
RE_ROOT_AP_DISTANCE_INVALID='255'
RE_ZERO_BSSID="00:00:00:00:00:00"
config_load 'repacd'
config_get_bool wifimon_cfg config 'cfg80211_enable' '0'
if [ "$wifimon_cfg" == "1" ]; then
REPACD_CFG80211=-cfg80211
else
REPACD_CFG80211=
fi
. /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
measurement_sta_iface= measurement_sta_iface_config_name=
assoc_timeout_logged=0 wps_timeout_logged=0
wps_in_progress=0 wps_start_time=''
wps_stabilization=0 wps_assoc_count=0
auto_mode_stabilization=0 auto_mode_assoc_count=0
bssid_stabilization=0 bssid_assoc_count=0 bssid_resolve_complete=0
assoc_start_time='' last_assoc_state=0 backhaul_eval_time='' force_down_5g_timestamp=''
ping_running=0 last_ping_gw_ip=
rssi_num=0 rssi_filename=
force_down_5g=0 down_time_2g='' measurement_eval_count=0
force_down_24g=0 badlink_start_time_5g='' badlink_switch_inprogress=0
rate_num=0 rate_min=0 rate_max=0 rate_pref2G=0 rate_to_CAP_filename=''
rssi_counter=0
is_24G_down_by_independent_channel=0
rssi_idle_time=0
new_bestap='' new_bestap_otherband='' bestap_filename=''
bssid_resolve_state= nolink_detected_5g=0
curr_root_distance=$RE_ROOT_AP_DISTANCE_INVALID
ignore_24g_assoc=0
my_uplink_rate_filename=
MoveFromCAP_snr_threshold= CAP_snr_filename=
wifi_2G_interface_name=
rate_to_CAP=0
# Config parameters
device_type= config_re_mode= default_re_mode=
min_wps_assoc= min_auto_mode_assoc= min_bssid_assoc=
rssi_samples= rssi_far= rssi_near= rssi_min= rssi_pref2G=
assoc_timeout= wps_timeout= config_downtime2G= measuring_attempts=0
rate_samples= percent_rate_min5G= percent_rate_max5G= percent_rate_pref2G=
daisy_chain= bssid_assoc_timeout= badlink_timeout5G=
config_short_eval_time5g=0 config_long_eval_time5g=0 config_eval_time24g=0
traffic_separation_enabled=0 traffic_separation_active=0
guest_backhaul= network_guest=
guest_preflink_down_start_time=0 guest_preflink_down_timeout=15
guest_preflink_up_start_time=0 guest_preflink_up_timeout=5
guest_link_override=0 guest_vid=102
is_independent_channel=_selection_enable=
quality_5g_rssi_level=
rssi_check_total_counter=
CAP_snr_threshold= MoveFromCAP_snr_hysteresis=
# 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 >> $WIFIMON_DEBUG_COMMAND_FILE
echo "$1" >> $WIFIMON_DEBUG_COMMAND_FILE
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=`cat /proc/uptime | cut -d' ' -f1 | 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
if [ -n "$rate_to_CAP_filename" ]; then
# Clean up the temporary file
rm -f $rate_to_CAP_filename
rate_to_CAP_filename=
fi
if [ -n "$my_uplink_rate_filename" ]; then
# Clean up the temporary file
rm -f $my_uplink_rate_filename
my_uplink_rate_filename=
fi
if [ -n "$bestap_filename" ]; then
# Clean up the temporary file
rm -f $bestap_filename
bestap_filename=
fi
if [ -n "$CAP_snr_filename" ]; then
# Clean up the temporary file
rm -f $CAP_snr_filename
CAP_snr_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=$(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 [ "$sta_iface" = "$sta_iface_5g" ] &&
[ "$force_down_5g" -gt 0 ];then
return 0
elif [ "$sta_iface" = "$sta_iface_24g" ] \
&& [ "$force_down_24g" -gt 0 ]; then
return 0
elif [ "$sta_iface" = "$sta_iface_24g" ] \
&& [ "$is_24G_down_by_independent_channel" -gt 0 ]; then
return 0
fi
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 device is already operating in the desired range extender
# mode when using automatic mode switching.
#
# input: $1 - cur_re_mode: the current operating range extender mode
# input: $2 - cur_re_submode: the current operating range extender sub-mode
# output: $3 - new_re_mode: the new range extender mode
# output: $4 - new_re_submode: the new range extender sub-mode
# return: 0 if already operating in the desired mode & submode; otherwise 1
__repacd_wifimon_resolve_mode() {
local old_re_mode=$1
local old_re_submode=$2
local re_mode_changed=0
# Finally, if the serving AP is operating in one of the special
# modes, write back the association derived RE mode. Otherwise,
# write back the default mode.
if [ "$config_re_mode" = 'auto' ]; then
# Since when operating in SON mode we rely on wsplcd/daisy to force
# the association to the CAP, do not check whether the CAP is
# the serving AP when determining whether to switch modes.
if __repacd_wifimon_is_serving_ap_son; then
if [ ! "$old_re_mode" = 'son' ]; then
__repacd_wifimon_info "Serving AP has SON enabled"
eval "$3=son"
re_mode_changed=1
fi
elif __repacd_wifimon_is_serving_ap_wds; then
if [ ! "$old_re_mode" = 'wds' ]; then
__repacd_wifimon_info "Serving AP has WDS enabled"
eval "$3=wds"
re_mode_changed=1
fi
else
if [ ! "$old_re_mode" = "$default_re_mode" ]; then
__repacd_wifimon_info "Serving AP does not advertise WDS"
eval "$3=$default_re_mode"
re_mode_changed=1
fi
fi
fi
# By definition, when operating in non-auto mode, we are always in
# the desired mode. But if there is any special sub-modes, handle it here.
if __repacd_wifimon_is_serving_ap_son; then
__repacd_wifimon_get_son_submode submode
if [ ! "$old_re_submode" = "$submode" ]; then
eval "$4=$submode"
fi
fi
if [ "$re_mode_changed" -gt 0 ]; then
return 1
else
return 0
fi
}
# Determine if the peer BSSID has been written back to UCI.
# This assumes the wireless config file has already been loaded.
# input: $1 - iface_section: the name of the section in UCI
# return: 0 if the BSSID has been set or there is no section; otherwise 1
__repacd_wifimon_is_peer_bssid_set() {
local iface_section=$1
if [ -z "$iface_section" ]; then
# Nothing to clone.
return 0
elif [ -n "$iface_section" ]; then
config_get peer_bssid $iface_section 'bssid' ''
#Change peer to resolved iff BSSID non-zero.
if [ -n "$peer_bssid" -a "$peer_bssid" != "00:00:00:00:00:00" ]; then
return 0
fi
fi
return 1
}
# Determine if the deep cloning process has completed (if enabled).
# return: 0 if the process is complete or is not enabled; otherwise 1
__repacd_wifimon_is_deep_cloning_complete() {
# First check if wsplcd and deep cloning are even enabled. If not,
# then consider it complete.
local wsplcd_enabled
local deep_cloning_enabled
local config_sta_enabled
local peer_bssid
config_load wsplcd
config_get wsplcd_enabled 'config' 'HyFiSecurity' '0'
config_get deep_cloning_enabled 'config' 'DeepClone' '0'
config_get config_sta_enabled 'config' 'ConfigSta' '0'
if [ "$wsplcd_enabled" -eq 0 -o "$deep_cloning_enabled" -eq 0 -o \
"$config_sta_enabled" -eq 0 ]; then
return 0
fi
# For each of the STA interfaces, see if the peer BSSID has been set.
config_load wireless
__repacd_wifimon_is_peer_bssid_set $sta_iface_24g_config_name || return 1
__repacd_wifimon_is_peer_bssid_set $sta_iface_5g_config_name || return 1
return 0
}
# 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
elif [ "$bssid_stabilization" -gt 0 ]; then
if [ "$bssid_assoc_count" -ge "$min_bssid_assoc" ]; then
return 0
else
return 1
fi
elif [ "$wps_stabilization" -gt 0 ]; then
if [ "$wps_assoc_count" -ge "$min_wps_assoc" ]; then
return 0
else
return 1
fi
elif [ "$auto_mode_stabilization" -gt 0 ]; then
if [ "$auto_mode_assoc_count" -ge "$min_auto_mode_assoc" ]; then
return 0
else
return 1
fi
else
# No stabilization in progress
return 0
fi
}
# Determine if the guest network's backhaul interface should be changed
# from prefered interface to non-prefered interface based on link status.
# If change has already made then monitor prefered interface, if prefered
# interface link looks good then change back to prefered interface.
# input: $1 - pref_iface: prefered sta interface
# input: $2 - other_iface: other non prefered interface
__repacd_wifimon_override_guest_backhaul() {
local pref_iface=$1
local other_iface=$2
local force_pref_down=0
local force_other_down=0
local ifname
if [ "$pref_iface" = "$sta_iface_5g" ]; then
force_pref_down=$force_down_5g
force_other_down=$force_down_24g
elif [ "$pref_iface" = "$sta_iface_24g" ]; then
force_pref_down=$force_down_24g
force_other_down=$force_down_5g
fi
if ! __repacd_wifimon_is_assoc $pref_iface || \
[ "$force_pref_down" -gt 0 ]; then
if __repacd_wifimon_is_assoc $other_iface &&
[ "$force_other_down" -eq 0 ]; then
if [ "$guest_link_override" -eq 0 ]; then
if [ "$guest_preflink_down_start_time" -eq 0 ]; then
__repacd_wifimon_get_timestamp guest_preflink_down_start_time
guest_preflink_up_start_time=0
elif __repacd_wifimon_is_timeout $guest_preflink_down_start_time \
$guest_preflink_down_timeout; then
ifname=`iwconfig 2>&1 | grep "$other_iface.$guest_vid" | cut -d ' ' -f1`
if [ -z "$ifname" ]; then
vconfig add $other_iface $guest_vid
fi
brctl delif "br-$network_guest" "$pref_iface.$guest_vid"
brctl addif "br-$network_guest" "$other_iface.$guest_vid"
ifconfig $other_iface.$guest_vid up
guest_link_override=1
fi
fi
elif ! __repacd_wifimon_is_assoc $other_iface || \
[ "$force_other_down" -gt 0 ]; then
if [ -n "$guest_preflink_down_start_time" ]; then
guest_preflink_down_start_time=0
fi
if [ "$guest_link_override" -gt 0 ]; then
brctl delif "br-$network_guest" "$other_iface.$guest_vid"
brctl addif "br-$network_guest" "$pref_iface.$guest_vid"
ifconfig $pref_iface.$guest_vid up
guest_link_override=0
fi
fi
elif __repacd_wifimon_is_assoc $pref_iface &&
[ "$force_pref_down" -eq 0 ]; then
if [ "$guest_link_override" -gt 0 ]; then
if [ "$guest_preflink_up_start_time" -eq 0 ]; then
__repacd_wifimon_get_timestamp guest_preflink_up_start_time
guest_preflink_down_start_time=0
elif __repacd_wifimon_is_timeout $guest_preflink_up_start_time \
$guest_preflink_up_timeout; then
brctl delif "br-$network_guest" "$other_iface.$guest_vid"
brctl addif "br-$network_guest" "$pref_iface.$guest_vid"
ifconfig $pref_iface.$guest_vid up
guest_link_override=0
fi
fi
fi
}
# Determine the 5G RSSI backhaul quality
# input $1 -- 5G RSSI value
__repacd_wifimon_independent_channel_check() {
local rssi="$1"
if [ "$rssi_idle_time" -eq 0 ]; then
if [ -n "$sta_iface_24g" ]; then
__repacd_wifimon_debug "rssi = $rssi quality_5g_rssi_level=$quality_5g_rssi_level force_down_24g=$force_down_24g force_down_5g=$force_down_5g rssi_counter=$rssi_counter"
if [ "$rssi" -gt -95 -a "$rssi" -lt 0 ]; then
if [ "$rssi" -gt "$quality_5g_rssi_level" ]; then
__repacd_wifimon_debug "rssi good then quality_5g_rssi_level"
if [ "$is_24G_down_by_independent_channel" -eq 0 ]; then
rssi_counter=$((rssi_counter + 1))
else
rssi_counter=0
fi
else
__repacd_wifimon_debug "rssi less then quality_5g_rssi_level force_down_24g=$force_down_24g"
if [ "$is_24G_down_by_independent_channel" -eq 0 ]; then
rssi_counter=0
else
rssi_counter=$((rssi_counter + 1))
fi
fi
fi
else
__repacd_wifimon_debug "sta_iface_24g=$sta_iface_24g"
fi
else
rssi_idle_time=$((rssi_idle_time - 1))
__repacd_wifimon_debug "rssi_idle_time=$rssi_idle_time"
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 cur_re_mode=$2
local cur_re_submode=$3
local autoconf_start=$4
local associated=0 resolving=0
config_load repacd
config_get bssid_resolve_state WiFiLink 'BSSIDResolveState' 'resolving'
# In Daisy chaining, when we are at hop 2 and above, allow monitoring
# even if single backhaul is associated provided other backhaul is forced down.
if __repacd_wifimon_is_assoc $sta_iface_5g \
&& __repacd_wifimon_is_assoc $sta_iface_24g; then
associated=1
elif [ "$ignore_24g_assoc" -gt 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_5g; then
associated=1
elif [ "$bssid_resolve_state" = "resolving" ]; then
if __repacd_wifimon_is_assoc $sta_iface_5g; then
resolving=1
elif __repacd_wifimon_is_assoc $sta_iface_24g \
&& [ "$nolink_detected_5g" -gt 0 ]; then
resolving=1
fi
fi
if [ "$associated" -gt 0 -o "$resolving" -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
elif [ "$daisy_chain" -gt 0 -a "$bssid_stabilization" -eq 0 ]; then
if [ -n "$sta_iface_5g" -a "$force_down_5g" -eq 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_5g; then
# Daisy chaining enabled, so deep cloning would have been disabled.
# Update the 5G BSSID, because we will proceed assuming current
# connection is stable.
if [ "$badlink_switch_inprogress" -eq 0 ]; then
__repacd_wifimon_config_current_bssid $sta_iface_5g $sta_iface_5g_config_name
bssid_resolve_complete=0
else
bssid_resolve_complete=1
fi
__repacd_wifimon_get_timestamp assoc_start_time
bssid_stabilization=1
bssid_assoc_count=0
assoc_timeout_logged=0
eval "$5=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
elif [ "$nolink_detected_5g" -gt 0 -a -n "$sta_iface_24g" -a "$force_down_24g" -eq 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_24g; then
# We are here because we detected Nolink or Badlink in 5G band.
__repacd_wifimon_config_current_bssid $sta_iface_24g $sta_iface_24g_config_name
bssid_resolve_complete=0
__repacd_wifimon_get_timestamp assoc_start_time
bssid_stabilization=1
bssid_assoc_count=0
assoc_timeout_logged=0
eval "$5=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
fi
elif [ "$config_re_mode" = 'auto' ] && [ "$auto_mode_stabilization" -eq 0 ]; then
# When making mode switching decisions, we also want to allow
# for enough time for the interfaces to stabilize.
__repacd_wifimon_get_timestamp assoc_start_time
auto_mode_stabilization=1
auto_mode_assoc_count=0
assoc_timeout_logged=0
eval "$5=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
fi
if [ "$wps_stabilization" -gt 0 ]; then
wps_assoc_count=$((wps_assoc_count + 1))
__repacd_wifimon_debug "Assoc post WPS (#$wps_assoc_count)"
elif [ "$bssid_stabilization" -gt 0 ]; then
if [ "$bssid_resolve_complete" -gt 0 ]; then
bssid_assoc_count=$((bssid_assoc_count + 1))
__repacd_wifimon_debug "Assoc post BSSID resolve (#$bssid_assoc_count)"
elif [ "$cur_re_mode" = "son" -a "$bssid_resolve_state" = "resolving" ]; then
local otherband_sta_iface_config_name
# Try configuring otherband backhaul BSSID.
# Otherband BSSID is arrived from beacon of current associated link.
# If successfully configured, we are ready to enable
# otherband station and check for association.
if [ "$nolink_detected_5g" -eq 0 ]; then
__repacd_wifimon_find_and_config_otherband_bssid $sta_iface_5g
otherband_sta_iface_config_name=$sta_iface_24g_config_name
else
__repacd_wifimon_find_and_config_otherband_bssid $sta_iface_24g
otherband_sta_iface_config_name=$sta_iface_5g_config_name
fi
config_load wireless
if __repacd_wifimon_is_peer_bssid_set $otherband_sta_iface_config_name; then
# Marking BSSIDResolveState=resolved to indicate
# BSSID resolve completion.
uci_set repacd WiFiLink BSSIDResolveState 'resolved'
uci_commit repacd
bssid_resolve_complete=1
else
__repacd_wifimon_debug "Waiting for BSSID resolve"
fi
else
bssid_resolve_complete=1
fi
elif [ "$auto_mode_stabilization" -gt 0 ]; then
if __repacd_wifimon_is_deep_cloning_complete; then
auto_mode_assoc_count=$((auto_mode_assoc_count + 1))
__repacd_wifimon_debug "Auto mode stabilization (#$auto_mode_assoc_count)"
else
__repacd_wifimon_debug "Waiting for deep cloning"
fi
fi
if __repacd_wifimon_is_assoc_stable; then
auto_mode_stabilization=0
bssid_stabilization=0
# Check the mode to see if we are already in the desired mode.
# If not, we will want to trigger the mode switch first as
# otherwise the RSSI measurements may not be updated (due to
# pings not going through).
if __repacd_wifimon_resolve_mode $cur_re_mode $cur_re_submode $6 $7; then
eval "$5=$WIFIMON_STATE_MEASURING"
assoc_start_time=''
last_assoc_state=1
wps_stabilization=0
# If daisy chain mode is enabled, get the phyrate of the associated sta interface
# and calculate the min and max threshold rates from the configured percent_rate_min5G
# and percent_rate_max5G value.
if [ "$daisy_chain" -gt 0 ]; then
__repacd_wifimon_set_rate_measurement_iface "5g"
fi
# 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
rate_num=0
else
# RE mode switch is required. Do not start measuring link.
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, we measure the WiFi link
# based on RSSI or Rate, RSSI measurement is used in star topology where we
# know the APs location and REs placement is adjusted based on LED feedback.
# But in Daisy chain, REs might have connected to any best link, REs auto detects
# the best link so we will not know the position of connected AP and LED
# feedback is not useful for the user. First make sure all sta interfaces are under
# our control.
if [ "$force_down_24g" -gt 0 ] \
&& __repacd_wifimon_is_active_assoc $sta_iface_24g; then
__repacd_wifimon_bring_iface_down $sta_iface_24g
fi
if [ "$force_down_5g" -gt 0 ] \
&& __repacd_wifimon_is_active_assoc $sta_iface_5g; then
__repacd_wifimon_bring_iface_down $sta_iface_5g
fi
# Waiting for 2.4G backhaul to associate in case of badlink.
# Bring down 5G backhaul once 2.4G is associated and stable.
if [ "$badlink_switch_inprogress" -gt 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_24g; then
__repacd_wifimon_debug "Bringing down 5G due to bad link"
__repacd_wifimon_bring_iface_down $sta_iface_5g
badlink_switch_inprogress=0
badlink_start_time_5g=''
fi
if [ "$daisy_chain" -gt 0 -a "$force_down_5g" -eq 0 ]; then
# Measure the link rate, so that we trigger the best link selection logic.
if [ "$rate_num" -le "$rate_samples" ] && \
__repacd_start_ping $network; then
__repacd_wifimon_measure_rate $network $5
return 0
else
# Restart the rate measurement
rate_num=0
#make sure it can continue to check the RSSI
if [ "$is_independent_channel_selection_enable" -gt 0 ] &&\
__repacd_start_ping $network; then
__repacd_wifimon_independent_channel_check`iwconfig $sta_iface_5g | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}'`
fi
fi
else
if [ "$force_down_5g" -gt 0 ]; then
eval "$5=$WIFIMON_STATE_RE_MOVE_CLOSER"
return 0;
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
else
#make sure it can continue to check the RSSI
if [ "$is_independent_channel_selection_enable" -gt 0 ] &&\
__repacd_start_ping $network; then
__repacd_wifimon_independent_channel_check `iwconfig $sta_iface_5g | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}'`
fi
fi
fi
return 0
# All cases below are for not associated.
elif [ "$autoconf_start" -gt 0 ]; then
# When making mode switching decisions, we also want to allow
# for enough time for the interfaces to stabilize.
__repacd_wifimon_get_timestamp assoc_start_time
auto_mode_stabilization=1
auto_mode_assoc_count=0
assoc_timeout_logged=0
eval "$5=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
elif [ "$wps_in_progress" -eq 0 -a "$wps_stabilization" -eq 0 -a \
"$auto_mode_stabilization" -eq 0 -a "$bssid_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
if [ "$is_independent_channel_selection_enable" -gt 0 ];then
if __repacd_wifimon_is_assoc $sta_iface_5g && \
__repacd_start_ping $network; then
__repacd_wifimon_independent_channel_check `iwconfig $sta_iface_5g | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}'`
fi
else
__repacd_stop_ping
fi
last_assoc_state=0
eval "$5=$WIFIMON_STATE_NOT_ASSOCIATED"
elif [ "$wps_in_progress" -gt 0 -o "$auto_mode_stabilization" -gt 0 \
-o "$bssid_stabilization" -gt 0 ]; then
if [ "$wps_timeout_logged" -eq 0 -a "$assoc_timeout_logged" -eq 0 ]; then
# Suppress logs after we've timed out
__repacd_wifimon_debug "Auto config in progress - not assoc"
fi
bssid_assoc_count=0
wps_assoc_count=0
auto_mode_assoc_count=0
fi
# Not associated and WPS is in progress. No LED update.
return 1
}
# Check whether the given WHC feature is advertised by the AP on which the
# given STA interface is connected.
# input: $1 - sta_iface: the interface to check for the AP capabilities
# input: $2 - ioctl_name: the name of the private ioctl to use to check the
# feature
# return: 0 if the feature is on or the interface name is invalid; otherwise 1
__repacd_wifimon_is_whc_feature_on_iface() {
local sta_iface=$1
local ioctl_name=$2
local command_result
if [ -z "$sta_iface" ]; then
return 0
fi
if [ "$sta_iface" = "$sta_iface_5g" ] &&
[ "$force_down_5g" -gt 0 ]; then
return 0
elif [ "$sta_iface" = "$sta_iface_24g" ] &&
[ "$force_down_24g" -gt 0 -o "$ignore_24g_assoc" -gt 0 ]; then
return 0
fi
if [ -z $REPACD_CFG80211 ]; then
command_result=$(iwpriv $sta_iface $ioctl_name)
else
command_result=$(cfg80211tool $sta_iface $ioctl_name)
fi
__repacd_wifimon_dump_cmd "$ioctl_name on $sta_iface: $command_result"
if [ -n "$command_result" ]; then
local feature_enabled
feature_enabled=$(echo "$command_result" | cut -d: -f2)
if [ "$feature_enabled" -gt 0 ]; then
return 0
fi
else
__repacd_wifimon_debug "iwpriv failed on $sta_iface for $ioctl_name"
fi
# Feature must not be enabled or we cannot resolve it.
return 1
}
# Determine if the serving AP has WDS enabled or not on all valid interfaces.
# return: 0 if it does have WDS enabled; otherwise 1
__repacd_wifimon_is_serving_ap_wds() {
if __repacd_wifimon_is_whc_feature_on_iface "$sta_iface_24g" 'get_whc_wds' &&
__repacd_wifimon_is_whc_feature_on_iface "$sta_iface_5g" 'get_whc_wds'; then
return 0
fi
return 1
}
# Determine if the serving AP has SON mode enabled or not.
# return: 0 if it does have SON enabled; otherwise 1
__repacd_wifimon_is_serving_ap_son() {
if __repacd_wifimon_is_whc_feature_on_iface "$sta_iface_24g" 'get_whc_son' &&
__repacd_wifimon_is_whc_feature_on_iface "$sta_iface_5g" 'get_whc_son'; then
return 0
fi
return 1
}
# Determine if the serving AP has SON mode enabled and it is an RE.
# return: 0 if it is RE and does have SON enabled; otherwise 1
__repacd_wifimon_is_serving_ap_daisy_son() {
if [ "$daisy_chain" -gt 0 ] \
&& __repacd_wifimon_is_whc_feature_on_iface "$sta_iface_5g" 'get_whc_son'; then
__repacd_get_root_ap_dist $sta_iface_5g root_distance
if [ "$root_distance" -gt 1 ]; then
return 0
fi
fi
return 1
}
# Determine the number of hops a given STA interface is from the
# root AP.
# input: $1 - sta_iface: the name of the STA interface (eg. ath01)
# output: $2 - dist: the distance (in terms of hops) from the root AP
__repacd_get_root_ap_dist() {
local sta_iface=$1
local command_result
if [ -z "$sta_iface" ]; then
return 0
fi
if [ -z $REPACD_CFG80211 ]; then
command_result=$(iwpriv $sta_iface get_whc_dist)
else
command_result=$(cfg80211tool $sta_iface get_whc_dist)
fi
__repacd_wifimon_dump_cmd "root dist for $sta_iface: $command_result"
if [ -n "$command_result" ]; then
local root_dist
root_dist=$(echo "$command_result" | cut -d: -f2)
eval "$2=$root_dist"
fi
}
# Determine the maximum number of hops the STA interfaces are from the
# root AP.
# output: $1 - max_dist: the maximum distance (in terms of hops) from the
# root AP
__repacd_get_max_root_ap_dist() {
local root_dist_24g=0 root_dist_5g=0
if [ -n "$sta_iface_24g" ]; then
__repacd_get_root_ap_dist $sta_iface_24g root_dist_24g
fi
if [ -n "$sta_iface_5g" ]; then
__repacd_get_root_ap_dist $sta_iface_5g root_dist_5g
fi
# If daisy chaining enabled and 5G is active,
# determine root distance from 5G only.
if [ "$daisy_chain" -gt 0 ] \
&& [ -n "$sta_iface_5g" -a "$force_down_5g" -eq 0 ]; then
eval "$1=$root_dist_5g"
else
if [ "$root_dist_24g" -eq $RE_ROOT_AP_DISTANCE_INVALID ]; then
eval "$1=$root_dist_5g"
return
elif [ "$root_dist_5g" -eq $RE_ROOT_AP_DISTANCE_INVALID ]; then
eval "$1=$root_dist_24g"
return
fi
if [ "$root_dist_24g" -gt "$root_dist_5g" ]; then
eval "$1=$root_dist_24g"
else
eval "$1=$root_dist_5g"
fi
fi
}
# Determine if the serving AP is currently the CAP or not.
# return: 0 if associated to the CAP; otherwise 1
__repacd_wifimon_is_cap_serving() {
# If the root AP is not operating in WDS mode, it may not even be a WHC
# AP. To be conservative, we do not enable the AP functionality.
local wds_enabled root_dist
if __repacd_wifimon_is_serving_ap_wds; then
__repacd_get_max_root_ap_dist root_dist
if [ "$root_dist" -eq 1 ]; then
# The STA device saying it is 1 hop away means that the serving
# AP indicated it was 0 hops away from the CAP. This means that
# the serving AP is the CAP.
return 0
fi
else
__repacd_wifimon_debug "Serving AP is not WHC enabled"
fi
# Either we are more than 1 hop from the root or the distance is
# unknown. In either case, indicate that the CAP is not serving.
return 1
}
# Determine the minimum number of hops the STA interfaces are from the
# root AP.
# output: $1 - min_dist: the minimum distance (in terms of hops) from the
# root AP
__repacd_get_min_root_ap_dist() {
local root_distance_24g=$RE_ROOT_AP_DISTANCE_INVALID
local root_distance_5g=$RE_ROOT_AP_DISTANCE_INVALID
if [ -n "$sta_iface_5g" ]; then
__repacd_get_root_ap_dist $sta_iface_5g root_distance_5g
fi
if [ -n "$sta_iface_24g" ]; then
__repacd_get_root_ap_dist $sta_iface_24g root_distance_24g
fi
if [ "$root_distance_24g" -le "$root_distance_5g" ]; then
eval "$1=$root_distance_24g"
else
eval "$1=$root_distance_5g"
fi
}
# Set the Root AP distance based on STA association state to AP interface.
# input $1: config: interface configuration name
# input $2: distance: new root distance to configure on AP interface
__repacd_wifimon_set_root_distance() {
local config=$1
local distance=$2
local iface mode
config_get mode "$config" mode
config_get iface $config ifname
if [ "$mode" = "ap" ]; then
if [ -z $REPACD_CFG80211 ]; then
iwpriv $iface set_whc_dist $distance
else
cfg80211tool $iface set_whc_dist $distance
fi
fi
}
# Copy the Uplink rate from associated STA to non associated STA.
# input $1: source_iface: source(associated) interface name to get rate
# input $2: target_iface: target(non-associated) interface name to set rate
__repacd_wifimon_copy_uplink_rate() {
local src_iface=$1
local tgt_iface=$2
local src_rate
if [ -n "$src_iface" -a -n "$tgt_iface" ] \
&& __repacd_wifimon_is_assoc $src_iface; then
if [ -z $REPACD_CFG80211 ]; then
src_rate=`iwpriv $src_iface get_whc_ul_rate | awk -F':' '{print $2}'`
iwpriv $tgt_iface set_whc_ul_rate $src_rate
else
src_rate=`cfg80211tool $src_iface get_whc_ul_rate | awk -F':' '{print $2}'`
cfg80211tool $tgt_iface set_whc_ul_rate $src_rate
fi
fi
}
# Set the desired Uplink rate for the provided interace.
# input $1: iface: interface name to get rate
# input $2: rate: rate to set
__repacd_wifimon_set_uplink_rate() {
local iface=$1
local rate=$2
if [ -n "$iface" ]; then
if [ -z $REPACD_CFG80211 ]; then
iwpriv $iface set_whc_ul_rate $rate
else
cfg80211tool $iface set_whc_ul_rate $rate
fi
fi
}
# Determine the current SON sub-mode.
# output: $1 - submode: sub mode we are operating in.
__repacd_wifimon_get_son_submode() {
if __repacd_wifimon_is_cap_serving; then
eval "$1=star"
else
eval "$1=daisy"
fi
}
# Bring down sta vap interface.
# input: $1 - sta interface: the name of the interface for bringing down.
__repacd_wifimon_bring_iface_down() {
local sta_iface=$1
if [ -n "$sta_iface" ];then
wpa_cli -p /var/run/wpa_supplicant-$sta_iface disable_network 0
__repacd_wifimon_info "Interface $sta_iface Brought down "
if [ "$sta_iface" = "$sta_iface_5g" ]; then
force_down_5g=1
if [ -n "$force_down_5g_timestamp" ] ;then
backhaul_eval_time=$config_long_eval_time5g
else
backhaul_eval_time=$config_short_eval_time5g
fi
__repacd_wifimon_get_timestamp force_down_5g_timestamp
else
force_down_24g=1
fi
rssi_counter=0
fi
}
# Bring up sta vap interface.
# input: $1 - sta interface: the name of the interface for bringing up.
__repacd_wifimon_bring_iface_up() {
local sta_iface=$1
if [ -n "$sta_iface" ];then
wpa_cli -p /var/run/wpa_supplicant-$sta_iface enable_network 0
__repacd_wifimon_info "Interface $sta_iface Brought up "
if [ "$sta_iface" = "$sta_iface_5g" ]; then
force_down_5g=0
else
force_down_24g=0
#if 2.4G interface up, force reset independent_channel parameters to 0
is_24G_down_by_independent_channel=0
uci_set wireless $wifi_2G_interface_name independent_channel_set '0'
uci_commit wireless
fi
rssi_counter=0
last_assoc_state=0
fi
}
# Find the median from the samples file.
# 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=$(cat $samples_filename | sort -n |
head -n $median_index | tail -n 1)
eval "$3=$median"
}
# Find the maximum duplicated entry from the samples file.
# input: $1 - Samples filename
# output: $2 - Maximum duplicated entry
__repacd_wifimon_get_max_duplicated_entry() {
local max_duplicated
local samples_filename="$1"
max_duplicated="$(cat $samples_filename | sort -n |
uniq -c | sort -nr | head -n 1 |
awk -F " " '{print $2}')"
eval "$2='$max_duplicated'"
}
# Parse the provided BSSID list and set Best BSSID variables.
# input: $1 - best_bssid_list
__repacd_wifimon_parse_best_bssid_list() {
local best_bssid_list="$1"
__repacd_wifimon_debug "Best BSSID list: "$best_bssid_list""
new_bestap=`echo "$best_bssid_list" | awk -F ";" '{print $1}'`
new_bestap_otherband=`echo "$best_bssid_list" | awk -F ";" '{print $2}'`
}
# Measure the Rate 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_rate() {
local bestap bestap_otherband
local rssi CAP_snr my_uplink_rate
# Just check Gateway link and proceed to rate measurement.
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
fi
# Get the 5G RSSI level
rssi=`iwconfig $sta_iface_5g | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}'`
# Only the 5 GHz link is measured. This is especially done since we
# generally cannot control which interface is used to reach upstream.
# Generally 5 GHz will be used (per the rules to set the broadcast bit
# and choose a default path), so we may not have any valid Rate data on
# 2.4 GHz.
if [ -z $REPACD_CFG80211 ]; then
# Get the rate to CAP estimate.
rate_to_CAP=`iwpriv $measurement_sta_iface get_whc_rate | awk -F':' '{print $2}'`
# Get the uplink rate with connected node.
my_uplink_rate=`iwpriv $measurement_sta_iface get_whc_ul_rate | awk -F':' '{print $2}'`
# Get the current CAP SNR.
CAP_snr=`iwpriv $measurement_sta_iface g_curr_caprssi | awk -F':' '{print $2}'`
else
# Get the rate to CAP estimate.
rate_to_CAP=`cfg80211tool $measurement_sta_iface get_whc_rate | awk -F':' '{print $2}'`
# Get the uplink rate with connected node.
my_uplink_rate=`cfg80211tool $measurement_sta_iface get_whc_ul_rate | awk -F':' '{print $2}'`
# Get the current CAP SNR.
CAP_snr=`cfg80211tool $measurement_sta_iface g_curr_caprssi | awk -F':' '{print $2}'`
fi
# Get the current Best AP, later it will be used for finding the Best AP
# once the measurement is completed.
__repacd_wifimon_get_current_best_ap $measurement_sta_iface bestap bestap_otherband
if [ "$is_independent_channel_selection_enable" -gt 0 ]; then
# Only the 5 GHz link is measured. This is especially done since we
# generally cannot control which interface is used to reach upstream.
# Generally 5 GHz will be used (per the rules to set the broadcast bit
# and choose a default path), so we may not have any valid RSSI data on
# 2.4 GHz.
__repacd_wifimon_independent_channel_check $rssi
fi
# We explicitly ignore clearly bogus values. 0 Mbps seen in
# some instances where the STA is not associated by the time the Rate
# check is done.
if [ -n "$rate_to_CAP" ]; then
if [ "$rate_num" -lt "$rate_samples" ]; then
__repacd_wifimon_debug "Sample #$rate_num RSSI = $rssi dBm CAP SNR = $CAP_snr Rate to CAP = $rate_to_CAP Mbps"
# Ignore the very first sample since it is taken at the same time
# the ping is started (and thus the Rate might not have been
# updated).
if [ "$rate_num" -eq 0 ]; then
rssi_filename=`mktemp /tmp/repacd-rssi.XXXXXX`
rate_to_CAP_filename=`mktemp /tmp/repacd-rate-toCAP.XXXXXX`
CAP_snr_filename=`mktemp /tmp/repacd-CAP-snr.XXXXXX`
bestap_filename=`mktemp /tmp/repacd-bestap.XXXXXX`
if [ -n "$sta_iface_24g" ]; then
my_uplink_rate_filename=`mktemp /tmp/repacd-rate-myUplink.XXXXXX`
fi
else
# Not the first sample
echo $rssi >> $rssi_filename
echo $rate_to_CAP >> $rate_to_CAP_filename
echo $CAP_snr >> $CAP_snr_filename
echo "$bestap;$bestap_otherband" >> $bestap_filename
if [ -n "$sta_iface_24g" ]; then
echo $my_uplink_rate >> $my_uplink_rate_filename
fi
fi
elif [ "$rate_num" -eq "$rate_samples" ]; then
__repacd_wifimon_debug "Sample #$rate_num RSSI = $rssi dBm CAP SNR = $CAP_snr Rate to CAP = $rate_to_CAP Mbps"
# 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
echo $rate_to_CAP >> $rate_to_CAP_filename
echo $CAP_snr >> $CAP_snr_filename
echo "$bestap;$bestap_otherband" >> $bestap_filename
if [ -n "$sta_iface_24g" ]; then
echo $my_uplink_rate >> $my_uplink_rate_filename
fi
# We got the required number of samples, now derive the median rate.
local rssi_median rate_to_CAP_median my_uplink_rate_median CAP_snr_median bestap_bssid_list restarted=0 config_success=0
__repacd_wifimon_compute_median $rssi_filename $rate_num rssi_median
__repacd_wifimon_compute_median $rate_to_CAP_filename $rate_num rate_to_CAP_median
__repacd_wifimon_compute_median $CAP_snr_filename $rate_num CAP_snr_median
__repacd_wifimon_debug "Median RSSI = $rssi_median CAP SNR = $CAP_snr_median Rate to CAP = $rate_to_CAP_median Mbps"
if [ "$device_type" = 'RE' ]; then
# Set the LED state
if [ $rssi_median -lt $rssi_far ]; then
eval "$2=$WIFIMON_STATE_RE_MOVE_CLOSER"
elif [ $rssi_median -gt $rssi_near ]; then
eval "$2=$WIFIMON_STATE_RE_MOVE_FARTHER"
else
eval "$2=$WIFIMON_STATE_RE_LOCATION_SUITABLE"
fi
# Check if CAP SNR is greater than CAP SNR threshold, if greater then
# Connect to CAP and ignore rate measurement result.
if [ $CAP_snr_threshold -gt 0 -a $CAP_snr_median -ge $CAP_snr_threshold ]; then
__repacd_wifimon_get_max_duplicated_entry $bestap_filename bestap_bssid_list
__repacd_wifimon_parse_best_bssid_list "$bestap_bssid_list"
__repacd_wifimon_config_best_ap $measurement_sta_iface $measurement_sta_iface_config_name restarted config_success
if [ "$restarted" -gt 0 ]; then
__repacd_wifimon_debug "CAP SNR >= $CAP_snr_threshold, Connecting to CAP"
fi
else
if __repacd_wifimon_is_cap_serving \
&& [ $CAP_snr_threshold -gt 0 -a $CAP_snr_median -ge $MoveFromCAP_snr_threshold ]; then
__repacd_wifimon_debug "Stay connected to CAP, CAP SNR is good"
else
if [ "$rate_to_CAP_median" -lt "$rate_min" ]; then
__repacd_wifimon_get_max_duplicated_entry $bestap_filename bestap_bssid_list
__repacd_wifimon_parse_best_bssid_list "$bestap_bssid_list"
__repacd_wifimon_config_best_ap $measurement_sta_iface $measurement_sta_iface_config_name restarted config_success
elif [ "$rate_to_CAP_median" -gt "$rate_max" ]; then
__repacd_wifimon_get_max_duplicated_entry $bestap_filename bestap_bssid_list
__repacd_wifimon_parse_best_bssid_list "$bestap_bssid_list"
__repacd_wifimon_config_best_ap $measurement_sta_iface $measurement_sta_iface_config_name restarted config_success
fi
fi
fi
fi
# If 5G link is very bad and below the prefered limit,
# switch to 2.4G backhaul link.
if [ "$restarted" -eq 0 ]; then
if [ -n "$sta_iface_24g" ]; then
__repacd_wifimon_compute_median $my_uplink_rate_filename $rate_num my_uplink_rate_median
# RSSI based interface bring down prioritize to mantain back ward compatiblity
if [ -n "$rssi_median" -a "$rssi_median" -lt "$rssi_pref2G" ];then
if __repacd_wifimon_is_assoc $sta_iface_24g;then
__repacd_wifimon_debug "Bringing down 5G Due to Bad rssi"
__repacd_wifimon_bring_iface_down $sta_iface_5g
__repacd_wifimon_get_timestamp assoc_start_time
fi
elif [ "$my_uplink_rate_median" -lt "$rate_pref2G" ]; then
# Start the 5G bad link timer, if not started before.
if [ -z "$badlink_start_time_5g" ]; then
__repacd_wifimon_get_timestamp badlink_start_time_5g
elif __repacd_wifimon_is_timeout $badlink_start_time_5g $badlink_timeout5G; then
# Bring up 2.4G link if forced down before and wait for association
# and then disable 5G backhaul link. 5G backhaul will be
# back when the 2.4G link goes down or 5G evaluation timedout.
__repacd_wifimon_debug "Median My uplink rate = $my_uplink_rate_median Mbps"
__repacd_wifimon_debug "Bad 5G link detected, will bring down 5G once 2.4G is UP"
if [ "$force_down_24g" -gt 0 ]; then
__repacd_wifimon_bring_iface_up $sta_iface_24g
fi
badlink_switch_inprogress=1
badlink_start_time_5g=''
fi
elif [ "$my_uplink_rate_median" -ge "$rate_pref2G" ]; then
# We detected good link in 5G, disable the timer.
badlink_start_time_5g=''
fi
fi
else
# If VAP was restarted, restart the BSSID stabilization.
last_assoc_state=0
badlink_switch_inprogress=0
badlink_start_time_5g=''
fi
# We detected a link in 5G, clear the no-link flag.
if [ "$nolink_detected_5g" -gt 0 ]; then
nolink_detected_5g=0
fi
# 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=''
fi
rate_num=$((rate_num + 1))
fi
}
# 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
if [ -n "$sta_iface_24g" ] &&
__repacd_wifimon_is_assoc $sta_iface_24g ; then
__repacd_wifimon_bring_iface_down $sta_iface_5g
measuring_cnt=0
eval "$2=$WIFIMON_STATE_RE_MOVE_CLOSER"
return
fi
fi
# Only the 5 GHz link is measured. This is especially done since we
# generally cannot control which interface is used to reach upstream.
# Generally 5 GHz will be used (per the rules to set the broadcast bit
# and choose a default path), so we may not have any valid RSSI data on
# 2.4 GHz.
rssi=`iwconfig $sta_iface_5g | grep 'Signal level' | awk -F'=' '{print $3}' | awk '{print $1}'`
if [ "$is_independent_channel_selection_enable" -gt 0 ]; then
__repacd_wifimon_independent_channel_check $rssi
if [ "$rssi_num" -gt "$rssi_samples" ]; then
return 0
fi
fi
# 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 -a "$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"
measuring_cnt=0
if [ "$device_type" = 'RE' ]; then
if [ "$rssi_median" -lt "$rssi_far" ]; then
eval "$2=$WIFIMON_STATE_RE_MOVE_CLOSER"
elif [ "$rssi_median" -gt "$rssi_near" ]; then
eval "$2=$WIFIMON_STATE_RE_MOVE_FARTHER"
else
eval "$2=$WIFIMON_STATE_RE_LOCATION_SUITABLE"
fi
if [ "$rssi_median" -le "$rssi_pref2G" ] ;then
if [ -n "$sta_iface_24g" ] &&
__repacd_wifimon_is_assoc $sta_iface_24g ; then
__repacd_wifimon_bring_iface_down $sta_iface_5g
fi
fi
else # must be standalone client
if [ "$rssi_median" -lt "$rssi_min" ]; then
eval "$2=$WIFIMON_STATE_CL_LINK_INADEQUATE"
elif [ "$rssi_median" -gt "$rssi_near" ]; then
eval "$2=$WIFIMON_STATE_CL_LINK_SUFFICIENT"
elif __repacd_wifimon_is_cap_serving; then
eval "$2=$WIFIMON_STATE_CL_ACTING_AS_RE"
else
eval "$2=$WIFIMON_STATE_CL_LINK_SUFFICIENT"
fi
fi
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=''
fi
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.
#
# For now, only the 5 GHz band is monitored.
#
# 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 bssid 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 bssid "$config" bssid
config_get device "$config" device
config_get hwmode "$device" hwmode
if [ "$hwmode" != "11ad" ]; then
if [ "$network" = $network_to_match -a -n "$iface" -a "$mode" = "sta" \
-a "$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" -a "$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
}
# Back haul monitoring logic : in case 5G was brought down
# forcefully due to RSSI constraints and 2.4 G also went down
# and stayed of for more then 2GBackhaulSwitchDownTime sec
# bring back 5G.
# input: $1 - re_mode: Current RE mode we are operating in.
# input: $2 - re_submode: Current RE sub-mode we are operating in.
# output:None
__repacd_wifimon_evaluate_backhaul_link() {
local re_mode=$1
local re_submode=$2
if [ "$force_down_5g" -gt 0 ] ; then
if ! __repacd_wifimon_is_assoc $sta_iface_24g;then
if [ -z "$down_time_2g" ]; then
__repacd_wifimon_get_timestamp down_time_2g
fi
if __repacd_wifimon_is_timeout $down_time_2g $config_downtime2G; then
__repacd_wifimon_debug "Bringing up 5G: 2G backhaul went down"
__repacd_wifimon_bring_iface_up $sta_iface_5g
__repacd_wifimon_get_timestamp assoc_start_time
down_time_2g=''
badlink_switch_inprogress=0
fi
else
down_time_2g=''
fi
if __repacd_wifimon_is_timeout $force_down_5g_timestamp $backhaul_eval_time; then
__repacd_wifimon_debug "Bringing up 5G:eval interval $backhaul_eval_time sec expired"
__repacd_wifimon_bring_iface_up $sta_iface_5g
__repacd_wifimon_get_timestamp force_down_5g_timestamp
__repacd_wifimon_get_timestamp assoc_start_time
fi
elif [ "$force_down_24g" -gt 0 ]; then
# If we are associated to CAP and 2.4G was brought down due to badlink,
# Try enabling 2.4G after specified timeout.
if [ "$last_assoc_state" -gt 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_5g; then
if [ -z "$down_time_2g" ]; then
__repacd_wifimon_get_timestamp down_time_2g
fi
if __repacd_wifimon_is_timeout $down_time_2g $config_eval_time24g; then
__repacd_wifimon_debug "Bringing up 2.4G: Trying to associate"
__repacd_wifimon_bring_iface_up $sta_iface_24g
down_time_2g=''
fi
else
down_time_2g=''
fi
fi
}
# Get phyrate in mbps
# input: $1 - 'interface on which phyrate is to be calculated'
# output: $2 - current maximum phyrate possible in Mbps
__repacd_wifimon_get_current_phyrate_in_mbps() {
local measurement_iface=$1
local phyrate_in_mbps
local phyrate_unit
local padding="00000"
# Get the phyrate and rate units of the interface.
eval $(iwconfig $measurement_iface |grep 'Bit Rate' | awk -F':' '{print $2}' | awk '{ \
a = $1;b = $2} END{print "phyrate_in_mbps=\""a"\"; \
phyrate_unit=\""b"\""}')
# If the phyrate is in Gbps, then convert it to Mbps
if [ $phyrate_unit = "Gb/s" ]; then
phyrate_in_mbps=$(echo $phyrate_in_mbps${padding:${#phyrate_in_mbps}} | sed 's/\.//g' | cut -c1-4)
else
phyrate_in_mbps=$(echo "$phyrate_in_mbps" | sed 's/\.//g' | cut -c1-3)
fi
eval "$2=$phyrate_in_mbps"
}
# Set rate measurement interface on which rate measurement
# is to be done.
# input: $1 - '5g' or '24g'
__repacd_wifimon_set_rate_measurement_iface() {
local measurement_sta_iface_rate
if [ "$1" = '5g' ]; then
measurement_sta_iface=$sta_iface_5g
measurement_sta_iface_config_name=$sta_iface_5g_config_name
elif [ "$1" = '24g' ]; then
measurement_sta_iface=$sta_iface_24g
measurement_sta_iface_config_name=$sta_iface_24g_config_name
fi
# Not associated, we will not get valid rate, return
if [ "$force_down_5g" -gt 0 ] \
|| ! __repacd_wifimon_is_assoc $sta_iface_5g; then
return
fi
# Get the phyrate of the measurement sta interface.
__repacd_wifimon_get_current_phyrate_in_mbps $measurement_sta_iface measurement_sta_iface_rate
__repacd_wifimon_debug "Current maxrate is $measurement_sta_iface_rate"
# Update the state variables depending upon the phyrate
# of the associated sta interface. (Currently 5G)
rate_min=$(($percent_rate_min5G*${measurement_sta_iface_rate}/100))
rate_max=$(($percent_rate_max5G*${measurement_sta_iface_rate}/100))
rate_pref2G=$(($percent_rate_pref2G*${measurement_sta_iface_rate}/100))
MoveFromCAP_snr_threshold=$(($CAP_snr_threshold-$MoveFromCAP_snr_hysteresis))
__repacd_wifimon_debug "Rate thresholds Min=$rate_min Max=$rate_max Pref2G=$rate_pref2G"
__repacd_wifimon_debug "SNR thresholds CAP attach=$CAP_snr_threshold db CAP detach=$MoveFromCAP_snr_threshold db"
}
# Check for configured BSSID in wireless file and update if required.
# Driver will arrive at the best AP based on rate and return the BSSID,
# the BSSID is written to the wireless config file using UCI.
# input: $1 - sta_iface: sta interface to configure
# input: $2 - sta_iface_config_name: config name of the sta interface
# input: $3 - new_bssid: BSSID value to configure
# output: $4 - restart wifi required or not
# output: $5 - Configuration changed
__repacd_wifimon_check_and_config_bssid() {
local sta_iface=$1
local sta_iface_config_name=$2
local new_bssid=$3
local current_bssid peer_bssid
eval "$4=0"
eval "$5=0"
# If BSSID is empty, return
if [ -z "$new_bssid" ]; then
return
fi
current_bssid=`iwconfig $sta_iface | grep "Access Point" | awk -F" " '{print $6}'`
config_load wireless
# If supplied BSSID and configured BSSID are same, simply return.
if __repacd_wifimon_is_peer_bssid_set $sta_iface_config_name; then
if [ "$new_bssid" = "$peer_bssid" ]; then
__repacd_wifimon_debug "$sta_iface: BSSID up to date"
return
fi
elif [ "$new_bssid" = "$current_bssid" ]; then
# Associated BSSID and supplied BSSID are same, just update
# the config file, no restart required.
uci_set wireless $sta_iface_config_name bssid $new_bssid
uci_commit wireless
__repacd_wifimon_debug "$sta_iface: BSSID updated, NO RESTART required"
eval "$5=1"
return
fi
# Write the new BSSID to wireless config file
uci_set wireless $sta_iface_config_name bssid $new_bssid
uci_commit wireless
eval "$5=1"
eval "$4=1"
__repacd_wifimon_debug "$sta_iface: BSSID updated, RESTART required"
}
# Configure BSSID via wpa_cli interface of given sta interface
# and restart interface if requested.
# input: $1 - sta_iface: sta interface to configure
# input: $2 - new_bssid: BSSID value to configure
# output: $3 - restart wifi required or not
__repacd_wifimon_wpa_config_bssid() {
local sta_iface=$1
local new_bssid=$2
local restart=$3
if [ -n "$sta_iface" ]; then
if [ "$restart" -gt 0 ]; then
# Restart the network with configured BSSID
wpa_cli -p /var/run/wpa_supplicant-$sta_iface disable_network 0
wpa_cli -p /var/run/wpa_supplicant-$sta_iface set_network 0 bssid $new_bssid
wpa_cli -p /var/run/wpa_supplicant-$sta_iface enable_network 0
else
# Just configure the BSSID
wpa_cli -p /var/run/wpa_supplicant-$sta_iface set_network 0 bssid $new_bssid
fi
fi
}
# Get current best AP based on Rate estimate.
# Driver will arrive at the best AP based on rate and return the BSSIDs.
# Best AP is determined on provided STA interface which will
# also return otherband BSSID of the same device (i.e. Best uplink node).
# input: $1 - sta_iface - STA interface to get best uplink BSSID
# output: $2 - bestap BSSID
# output: $3 - bestap otherband BSSID
__repacd_wifimon_get_current_best_ap() {
local sta_iface=$1
local best_bssid best_otherband_bssid
# Driver returns BSSID without separator(:), add the separator before writting
# to config file.
# Send ioctl request to send bcast probe req with configured ssid to discover
# neighbor backhaul APs with hidden ssids. The initial reading may not contain
# the updated bssid list as newly discovered bssid(s) may not be a part of scan
# result. Subsequent reading should get updated scan list as reading is done
# every 2secs.
if [ -z $REPACD_CFG80211 ]; then
iwpriv $sta_iface sendprobereq 1
best_bssid=`iwpriv $sta_iface get_whc_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
best_otherband_bssid=`iwpriv $sta_iface g_best_ob_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
else
cfg80211tool $sta_iface sendprobereq 1
best_bssid=`cfg80211tool $sta_iface get_whc_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
best_otherband_bssid=`cfg80211tool $sta_iface g_best_ob_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
fi
eval "$2=$best_bssid"
eval "$3=$best_otherband_bssid"
}
# Configure Best AP based on Rate for given sta interface
# Driver will arrive at the best AP based on rate and return the BSSID,
# the BSSID is written to the wireless config file using UCI.
# input: $1 - sta_iface - station interface to configure best BSSID
# input: $2 - sta_iface_config_name - station interface config name
# output: $3 - VAP restart done or not
# output: $4 - BSSID configuration status 1 - success & 0 - failed
__repacd_wifimon_config_best_ap() {
local sta_iface=$1
local sta_iface_config_name=$2
local otherband_sta_iface otherband_sta_iface_config_name
local selected_bssid peer_bssid
local sta1_config_changed=0 sta2_config_changed=0
local config_status=0 sta1_restart=0 sta2_restart=0
eval "$3=0"
if [ -n "$sta_iface" ]; then
__repacd_wifimon_debug "$sta_iface: Configure Best AP"
if [ "$sta_iface" = "$sta_iface_5g" ]; then
otherband_sta_iface=$sta_iface_24g
otherband_sta_iface_config_name=$sta_iface_24g_config_name
else
otherband_sta_iface=$sta_iface_5g
otherband_sta_iface_config_name=$sta_iface_5g_config_name
fi
if [ -n "$new_bestap" -a "$new_bestap" != "00:00:00:00:00:00" ]; then
if [ -n "$otherband_sta_iface" -a -n "$new_bestap_otherband" \
-a "$new_bestap_otherband" != "00:00:00:00:00:00" ] \
|| [ -z "$otherband_sta_iface" ]; then
__repacd_wifimon_debug "Best AP = $new_bestap"
__repacd_wifimon_check_and_config_bssid $sta_iface \
$sta_iface_config_name $new_bestap sta1_restart sta1_config_changed
if [ "$sta1_restart" -gt 0 ]; then
wpa_cli -p /var/run/wpa_supplicant-$sta_iface disable_network 0
fi
# Configure the otherband BSSID also.
if [ -n "$otherband_sta_iface" ]; then
__repacd_wifimon_debug "Best AP otherband = $new_bestap_otherband"
__repacd_wifimon_check_and_config_bssid $otherband_sta_iface \
$otherband_sta_iface_config_name $new_bestap_otherband sta2_restart sta2_config_changed
if [ "$sta2_restart" -gt 0 ]; then
wpa_cli -p /var/run/wpa_supplicant-$otherband_sta_iface disable_network 0
fi
fi
# Enable disabled VAPs now, so that both bands connect to the same node.
if [ "$sta1_config_changed" -gt 0 ]; then
__repacd_wifimon_wpa_config_bssid $sta_iface $new_bestap 0
if [ "$sta1_restart" -gt 0 ]; then
wpa_cli -p /var/run/wpa_supplicant-$sta_iface enable_network 0
fi
fi
if [ "$sta2_config_changed" -gt 0 ]; then
__repacd_wifimon_wpa_config_bssid $otherband_sta_iface $new_bestap_otherband 0
if [ "$sta2_restart" -gt 0 ]; then
wpa_cli -p /var/run/wpa_supplicant-$otherband_sta_iface enable_network 0
fi
fi
eval "$3=$((sta1_restart + sta2_restart))"
config_status=1
fi
else
config_status=0
__repacd_wifimon_debug "No valid Best AP found"
fi
fi
eval "$4=$config_status"
}
# Configure BSSID of Current association to the provided sta interface.
# The BSSID is arrived from iwconfig command.
# The BSSID is written to the wireless config file using UCI.
# input: $1 - sta_iface: station interface to configure current BSSID
# input: $2 - sta_iface_config_name: station interface config name
__repacd_wifimon_config_current_bssid() {
local sta_iface=$1
local sta_iface_config_name=$2
local selected_bssid restart=0 config_changed=0
if [ -n "$sta_iface" ]; then
# Get the currently associated BSSID from iwconfig.
selected_bssid=`iwconfig $sta_iface | grep "Access Point" | awk -F" " '{print $6}'`
__repacd_wifimon_debug "$sta_iface: BSSID = $selected_bssid"
if [ "$selected_bssid" != "00:00:00:00:00:00" ]; then
__repacd_wifimon_check_and_config_bssid $sta_iface \
$sta_iface_config_name $selected_bssid restart config_changed
fi
# Restart the network with configured BSSID, if required.
if [ "$config_changed" -gt 0 ]; then
__repacd_wifimon_wpa_config_bssid $sta_iface $selected_bssid $restart
fi
fi
}
# Configure 2.4G BSSID of CAP for the provided sta interface.
# Driver will arrive at the CAP 2.4G BSSID,
# the BSSID is written to the wireless config file using UCI.
# input: None
__repacd_wifimon_config_cap_24g_bssid() {
local selected_bssid restart=0 config_changed=0
if [ -n "$sta_iface_24g" ]; then
# Driver returns BSSID without separator(:), add the separator before writting
# to config file.
if [ -z $REPACD_CFG80211 ]; then
selected_bssid=`iwpriv $sta_iface_24g g_whc_cap_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
else
selected_bssid=`cfg80211tool $sta_iface_24g g_whc_cap_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
fi
__repacd_wifimon_debug "$sta_iface_24g: CAP BSSID = $selected_bssid"
if [ "$selected_bssid" != "00:00:00:00:00:00" ]; then
__repacd_wifimon_check_and_config_bssid $sta_iface_24g \
$sta_iface_24g_config_name $selected_bssid restart config_changed
fi
if [ "$restart" -gt 0 ]; then
# Restart the network with configured BSSID
__repacd_wifimon_wpa_config_bssid $sta_iface_24g $selected_bssid $restart
elif [ "$config_changed" -gt 0 ]; then
__repacd_wifimon_wpa_config_bssid $sta_iface_24g $selected_bssid $restart
fi
fi
}
# Configure BSSID for the disabled sta interface.
# Driver will arrive at the otherband BSSIDs from provided STA interface,
# the BSSID is written to the wireless config file using UCI and update the network.
# input: $1 - sta_iface: station interface on which otherband BSSIDs to be found
__repacd_wifimon_find_and_config_otherband_bssid() {
local selected_bssid restart=0 config_changed=0
local otherband_sta_iface otherband_sta_iface_config_name
local sta_iface=$1
if [ -n "$sta_iface" ]; then
if [ "$sta_iface" = "$sta_iface_5g" ]; then
otherband_sta_iface=$sta_iface_24g
otherband_sta_iface_config_name=$sta_iface_24g_config_name
else
otherband_sta_iface=$sta_iface_5g
otherband_sta_iface_config_name=$sta_iface_5g_config_name
fi
if [ -n "$otherband_sta_iface" -a -n "$otherband_sta_iface_config_name" ]; then
# Driver returns BSSID without separator(:), add the separator before writting
# to config file.
if [ -z $REPACD_CFG80211 ]; then
selected_bssid=`iwpriv $sta_iface g_whc_ob_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
else
selected_bssid=`cfg80211tool $sta_iface g_whc_ob_bssid | awk -F":" '{print $2}' | \
sed -e "s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1:\2:\3:\4:\5:\6/"`
fi
__repacd_wifimon_debug "OtherBand BSSID = $selected_bssid"
if [ "$selected_bssid" != "00:00:00:00:00:00" ]; then
__repacd_wifimon_check_and_config_bssid $otherband_sta_iface \
$otherband_sta_iface_config_name $selected_bssid restart config_changed
fi
# Restart the network with configured BSSID, if required.
if [ "$config_changed" -gt 0 ]; then
__repacd_wifimon_wpa_config_bssid $otherband_sta_iface $selected_bssid $restart
fi
fi
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" -o -n "$sta_iface_24g" ]; then
local re_mode=$2
local re_submode=$3
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 device_type 'repacd' 'DeviceType' 'RE'
config_get config_re_mode 'repacd' 'ConfigREMode' 'auto'
config_get default_re_mode 'repacd' 'DefaultREMode' 'qwrap'
config_get traffic_separation_enabled repacd TrafficSeparationEnabled '0'
config_get traffic_separation_active repacd TrafficSeparationActive '0'
config_get guest_backhaul repacd NetworkGuestBackhaulInterface 'both'
config_get network_guest repacd NetworkGuest 'guest'
config_get min_auto_mode_assoc 'WiFiLink' 'MinAssocCheckAutoMode' '5'
config_get min_wps_assoc 'WiFiLink' 'MinAssocCheckPostWPS' '5'
config_get min_bssid_assoc 'WiFiLink' 'MinAssocCheckPostBSSIDConfig' '5'
config_get wps_timeout 'WiFiLink' 'WPSTimeout' '120'
config_get assoc_timeout 'WiFiLink' 'AssociationTimeout' '300'
config_get rssi_samples 'WiFiLink' 'RSSINumMeasurements' '5'
config_get rssi_far 'WiFiLink' 'RSSIThresholdFar' '-75'
config_get rssi_near 'WiFiLink' 'RSSIThresholdNear' '-60'
config_get rssi_min 'WiFiLink' 'RSSIThresholdMin' '-75'
config_get rssi_pref2G 'WiFiLink' 'RSSIThresholdPrefer2GBackhaul' '-100'
config_get config_downtime2G 'WiFiLink' '2GBackhaulSwitchDownTime' '10'
config_get measuring_attempts 'WiFiLink' 'MaxMeasuringStateAttempts' '3'
config_get daisy_chain 'WiFiLink' 'DaisyChain' '0'
config_get rate_samples 'WiFiLink' 'RateNumMeasurements' '5'
config_get percent_rate_min5G 'WiFiLink' 'RateThresholdMin5GInPercent' '40'
config_get percent_rate_max5G 'WiFiLink' 'RateThresholdMax5GInPercent' '70'
config_get percent_rate_pref2G 'WiFiLink' 'RateThresholdPrefer2GBackhaulInPercent' '5'
config_get bssid_assoc_timeout 'WiFiLink' 'BSSIDAssociationTimeout' '90'
config_get badlink_timeout5G 'WiFiLink' '5GBackhaulBadlinkTimeout' '60'
config_get config_short_eval_time5g 'WiFiLink' '5GBackhaulEvalTimeShort' '1800'
config_get config_long_eval_time5g 'WiFiLink' '5GBackhaulEvalTimeLong' '7200'
config_get config_eval_time24g 'WiFiLink' '2GBackhaulEvalTime' '1800'
config_get is_independent_channel_selection_enable 'WiFiLink' '2GIndependentChannelSelectionEnable' '0'
config_get quality_5g_rssi_level 'WiFiLink' '2GIndependentChannelSelectionRssiLevel' '-70'
config_get rssi_check_total_counter 'WiFiLink' '2GIndependentChannelSelectionTotalRssiCounter' '10'
config_get rssi_idle_time 'WiFiLink' '2GIndependentChannelSelectionStartRssiCheckTime' '60'
config_get CAP_snr_threshold 'WiFiLink' 'PreferCAPSNRThreshold5G' '0'
config_get MoveFromCAP_snr_hysteresis 'WiFiLink' 'MoveFromCAPSNRHysteresis5G' '3'
# Find the 2.4G wifi interface and set independent_channel_set to defalut value zero to avoid
# if device re-power on without unknow reason to keep it as 1.
uci show wireless | grep 11ng | sed 's/.hwmode.*//g' | sed 's/wireless.//g' > /tmp/wifi_2G_interface.txt
exec < '/tmp/wifi_2G_interface.txt'
while read line
do
__repacd_wifimon_debug $line
wifi_2G_interface_name=$line
uci_set wireless $wifi_2G_interface_name independent_channel_set '0'
uci_commit wireless
done
rm -rf /tmp/wifi_2G_interface.txt
# 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() {
if [ -n "$sta_iface_5g" ]; then
local new_root_distance
# Make sure all the AP interfaces are broadcasting right hop count.
# If required, inherit the root distance from active station interface.
__repacd_get_min_root_ap_dist new_root_distance
if [ "$new_root_distance" != "$curr_root_distance" ]; then
__repacd_wifimon_debug "Root AP distance: $new_root_distance"
config_load wireless
config_foreach __repacd_wifimon_set_root_distance wifi-iface $new_root_distance
curr_root_distance=$new_root_distance
if [ "$new_root_distance" -eq $RE_ROOT_AP_DISTANCE_INVALID ]; then
__repacd_wifimon_set_uplink_rate $sta_iface_5g 0
__repacd_wifimon_set_uplink_rate $sta_iface_24g 0
fi
fi
# We depend on 5G interface for measuring rate, if forced down,
# copy the rate from 2.4G interface.
if [ "$force_down_5g" -gt 0 ]; then
__repacd_wifimon_copy_uplink_rate $sta_iface_24g $sta_iface_5g
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
# Check if the WPS button was pressed.
local wps_pbc
read -t 1 wps_pbc <>$WIFIMON_PIPE_NAME
if [ $? -eq 0 ]; then
eval "$4=$WIFIMON_STATE_AUTOCONFIG_IN_PROGRESS"
wps_in_progress=1
__repacd_wifimon_get_timestamp wps_start_time
# Forcefully delete the BSSID from the STA interface in case
# the new AP does not support IEEE1905.1+QCA extensions deep
# cloning.
__repacd_wifimon_debug "Delete BSSID for $sta_iface_5g"
uci delete wireless.${sta_iface_5g_config_name}.bssid
if [ -n "$sta_iface_24g" ]; then
__repacd_wifimon_debug "Delete BSSID for $sta_iface_24g"
uci delete wireless.${sta_iface_24g_config_name}.bssid
fi
uci commit wireless
# Do not check the association timeout below.
return 0
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 2.4G inteface is down by independent channel selection
# and 5G itnerface is gone, wake up the 2.4G interface
if [ "$is_independent_channel_selection_enable" -gt 0 ]; then
if [ "$is_24G_down_by_independent_channel" -gt 0 ]; then
if __repacd_wifimon_is_assoc $sta_iface_5g; then
__repacd_wifimon_info "5G still alive"
else
__repacd_wifimon_bring_iface_up $sta_iface_24g
return 0
fi
fi
fi
if [ "$daisy_chain" -gt 0 ]; then
if __repacd_wifimon_is_timeout $assoc_start_time $bssid_assoc_timeout; then
local reset_bssid=0
local restarted=0 config_success=0
config_load wireless
# If one backhaul link is associated and other one is not associated,
# bring down non associated backhaul and proceed to link measurement.
if [ -n "$sta_iface_24g" ] && [ -n "$sta_iface_5g" ]; then
if [ "$force_down_24g" -eq 0 -a "$force_down_5g" -eq 0 ]; then
if __repacd_wifimon_is_assoc $sta_iface_5g; then
# Ignore 2.4G backhaul association check,
# we are not associated for long time.
# 5G backhaul is ready to take over.
__repacd_wifimon_debug "BSSID Assoc timeout: Ignoring 2.4G Association check"
__repacd_wifimon_get_timestamp assoc_start_time
ignore_24g_assoc=1
elif __repacd_wifimon_is_assoc $sta_iface_24g; then
# Before bringing down 5G backhaul try finding best AP,
# still if no best AP found bringdown 5G backhaul.
if __repacd_wifimon_is_peer_bssid_set $sta_iface_5g_config_name; then
__repacd_wifimon_debug "BSSID Assoc timeout: 5G not associated"
__repacd_wifimon_get_current_best_ap $sta_iface_5g new_bestap new_bestap_otherband
__repacd_wifimon_config_best_ap $sta_iface_5g $sta_iface_5g_config_name restarted config_success
if [ "$config_success" -gt 0 ]; then
__repacd_wifimon_get_timestamp assoc_start_time
eval "$4=$WIFIMON_STATE_BSSID_ASSOC_TIMEOUT"
return
fi
fi
# Bring down 5G backhaul, we are not associated for long time.
# 2.4G backhaul is ready to take over.
__repacd_wifimon_debug "BSSID Assoc timeout: bringing down 5G"
__repacd_wifimon_bring_iface_down $sta_iface_5g
__repacd_wifimon_get_timestamp assoc_start_time
else
# Both the backhaul links are not associated, try resetting BSSID.
__repacd_wifimon_debug "BSSID Assoc timeout: both 5G and 2.4G not associated"
reset_bssid=1
fi
else
# We are already operating in single link,
# try resetting BSSID.
__repacd_wifimon_debug "BSSID Assoc timeout: already in single link, not associated"
reset_bssid=1
fi
else
__repacd_wifimon_debug "BSSID Assoc timeout: try clearing BSSID, if any"
reset_bssid=1
fi
if [ "$reset_bssid" -gt 0 ]; then
# Configured BSSID association timed out,
# the desired BSSID might have powered down or not reachable.
# Try running Best AP selection to find an AP.
if [ -n "$sta_iface_5g" ]; then
if [ "$force_down_5g" -gt 0 ]; then
__repacd_wifimon_bring_iface_up $sta_iface_5g
fi
__repacd_wifimon_get_current_best_ap $sta_iface_5g new_bestap new_bestap_otherband
__repacd_wifimon_config_best_ap $sta_iface_5g $sta_iface_5g_config_name restarted config_success
if [ "$config_success" -gt 0 ]; then
__repacd_wifimon_get_timestamp assoc_start_time
fi
fi
eval "$4=$WIFIMON_STATE_BSSID_ASSOC_TIMEOUT"
fi
fi
fi
if __repacd_wifimon_is_timeout $assoc_start_time $assoc_timeout; then
# If we have two STA interfaces and only 5 GHz is
# associated, see if mode switching is necessary for it
# alone. Note that here we are temporarily resetting the
# other interface name to ensure mode switching only
# considers the one that is associated.
#
# If we eventually start supporting multiple STA interfaces
# even in the fallback modes, we may need to make this
# smarter and consider both possible STA interfaces that
# may be associated.
if [ "$daisy_chain" -gt 0 ]; then
if [ "$nolink_detected_5g" -eq 0 ]; then
if [ -n "$sta_iface_24g" ]; then
# Even after resetting BSSIDs 5G is not associated,
# Now time to check on 2.4G link.
nolink_detected_5g=1
# We are left with no option, link stays disconnected
# till something found on 2.4G link.
__repacd_wifimon_debug "Waiting for association in 2.4G"
fi
else
local restarted config_success
__repacd_wifimon_get_current_best_ap $sta_iface_24g new_bestap new_bestap_otherband
__repacd_wifimon_config_best_ap $sta_iface_24g $sta_iface_24g_config_name restarted config_success
if [ "$config_success" -gt 0 ]; then
__repacd_wifimon_get_timestamp assoc_start_time
fi
fi
elif [ -n "$sta_iface_24g" -a -n "$sta_iface_5g" ]; then
if [ "$force_down_5g" -eq 0 ] \
&& __repacd_wifimon_is_assoc $sta_iface_5g; then
local tmp_sta_iface_24g=$sta_iface_24g
sta_iface_24g=
if ! __repacd_wifimon_resolve_mode $2 $3 $5 $6; then
# Not currently in the right mode. Restore the
# interface name and return to allow for a restart.
sta_iface_24g=$tmp_sta_iface_24g
return
fi
fi
fi
if [ "$assoc_timeout_logged" -eq 0 ]; then
__repacd_wifimon_debug "Association timeout"
assoc_timeout_logged=1
fi
eval "$4=$WIFIMON_STATE_ASSOC_TIMEOUT"
fi
fi
fi
__repacd_wifimon_evaluate_backhaul_link $2 $3
if [ "$traffic_separation_enabled" -gt 0 ] &&
[ "$traffic_separation_active" -gt 0 ]; then
if ! __repacd_wifimon_is_assoc $sta_iface_5g || \
! __repacd_wifimon_is_assoc $sta_iface_24g || [ "$guest_link_override" -gt 0 ] || \
[ "$force_down_5g" -gt 0 ] || [ "$force_down_24g" -gt 0 ]; then
if [ -n "$sta_iface_24g" ] && [ -n "$sta_iface_5g" ]; then
if [ "$guest_backhaul" = '5G' ]; then
__repacd_wifimon_override_guest_backhaul $sta_iface_5g $sta_iface_24g
elif [ "$guest_backhaul" = '2.4G' ]; then
__repacd_wifimon_override_guest_backhaul $sta_iface_24g $sta_iface_5g
fi
fi
fi
fi
fi
}
# determin does it need to down the 2.4G backhaul or wake up 2.4G backhaul
repacd_wifimon_independent_channel_check() {
local inteface_24g
local i=0
local macaddress
local channel
if [ -n "$sta_iface_24g" ]; then
if [ "$is_independent_channel_selection_enable" -gt 0 ]; then
__repacd_wifimon_debug "rssi_counter=$rssi_counter rssi_check_total_counter=$rssi_check_total_counter
force_down_24g=$force_down_24g force_down_5g=$force_down_5g sta_iface_24g=$sta_iface_24g"
if [ "$rssi_counter" -ge "$rssi_check_total_counter" ]; then
if [ "$is_24G_down_by_independent_channel" -eq 0 ]; then
#__repacd_wifimon_debug "need to down 2.4G"
__repacd_wifimon_debug "5G backhaul Signal is good, down the 2.4G backhaul interface and 2.4G AP VAP to do the ACS"
is_24G_down_by_independent_channel=1
uci_set wireless $wifi_2G_interface_name independent_channel_set '1'
uci_commit wireless
((echo td s; sleep 1) | hyt | awk -F " " '/WLAN2G/ && $4 < 255 {printf "%s \n %s \n",$3,$4}' \
| sed 's/[[:space:]]//g') > /tmp/innetworkresult.txt
# find the AP VAP for 2.4G to down the interface first
# then set the channel to 0
# then wake up all interface
# in multiple VAP setting ACS will re-run when All VAPs channel is 0
(iwconfig | grep 'Mode:Master Frequency:2' -B1 | sed '/wireless/d' |\
sed 's/IEEE.* //g' | sed 's/Mode.* //g' | sed 's/--//g' | sed 's/[[:space:]]//g' \
| sed '/^$/d') > /tmp/all_interface
exec < '/tmp/all_interface'
while read line
do
__repacd_wifimon_debug $line
inteface_24g=$line
done
# clear the in network information
wifitool $inteface_24g set_innetwork_2g 00:00:00:00:00:00 0
exec < '/tmp/innetworkresult.txt'
while read line
do
__repacd_wifimon_debug $line
if [ "$i" -eq 0 ]; then
macaddress=$line
i=$((i + 1))
else
channel=$line
/usr/sbin/wifitool $inteface_24g set_innetwork_2g $macaddress $channel
i=0
fi
done
wpa_cli -p /var/run/wpa_supplicant-$sta_iface_24g disable_network 0
exec < '/tmp/all_interface'
while read line
do
__repacd_wifimon_debug $line
ifconfig $line down
iwconfig $line channel 0
done
exec < '/tmp/all_interface'
while read line
do
ifconfig $line up
done
rm -rf /tmp/all_interface
rm -rf /tmp/innetworkresult.txt
else
if [ "$force_down_24g" -eq 0 ]; then
__repacd_wifimon_debug "5G backhaul signal is bad, up the 2.4G backhaul"
is_24G_down_by_independent_channel=0
uci_set wireless $wifi_2G_interface_name independent_channel_set '0'
uci_commit wireless
wpa_cli -p /var/run/wpa_supplicant-$sta_iface_24g enable_network 0
else
__repacd_wifimon_debug "2.4G backhaul interface is down by anohter process don't wake up it"
fi
fi
rssi_counter=0
fi
fi
else
__repacd_wifimon_debug "sta_iface_24g=$sta_iface_24g is null, cannot do anything"
fi
}
# Terminate the Wi-Fi link monitoring, cleaning up any state in preparation
# for shutdown.
repacd_wifimon_fini() {
__repacd_wifimon_debug "sta_iface_24g=$sta_iface_24g is_24G_down_by_independent_channel=$is_24G_down_by_independent_channel force_down_24g=$force_down_24g"
if [ -n "$sta_iface_24g" ]; then
if [ "$is_24G_down_by_independent_channel" -gt 0 ]; then
if [ "$force_down_24g" -eq 0 ]; then
uci_set wireless $wifi_2G_interface_name independent_channel_set '0'
uci_commit wireless
wpa_cli -p /var/run/wpa_supplicant-$sta_iface_24g enable_network 0
fi
fi
fi
__repacd_stop_ping
}