#!/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 }