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.

442 lines
14 KiB
Bash

#!/bin/sh
# Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
# All Rights Reserved.
# Confidential and Proprietary - Qualcomm Technologies, Inc.
#
# 2015-2016 Qualcomm Atheros, Inc.
# All Rights Reserved.
# Qualcomm Atheros Confidential and Proprietary.
GWMON_DEBUG_OUTOUT=0
GWMON_SWITCH_CONFIG_COMMAND=swconfig
GWMON_MODE_NO_CHANGE=0
GWMON_MODE_CAP=1
GWMON_MODE_NON_CAP=2
GWMON_KERNEL="4.4.60"
GWMON_PLATFORM=QCA
config_load 'repacd'
config_get_bool gwmon_cfg config 'cfg80211_enable' '0'
if [ "$gwmon_cfg" == "1" ]; then
REPACD_CFG80211=-cfg80211
else
REPACD_CFG80211=
fi
. /lib/functions.sh
. /lib/functions/hyfi-iface.sh
. /lib/functions/hyfi-network.sh
. /lib/functions/repacd-wifimon.sh
prev_gw_link='' router_detected=0 gw_iface="" gw_switch_port=""
managed_network='' switch_iface="" vlan_group="" switch_ports=''
cpu_portmap=0
eswitch_support="0"
gw_mac=""
last_hop_count=255
IS_GW_REACHABLE=0
GW_NOT_REACHABLE_TIMESTAMP=0
#Check for Platform & Kernel version
dut_kernel=$(uname -a | awk '{print $3}')
dut_platform=$(grep -w DISTRIB_RELEASE /etc/openwrt_release | awk -F "='" '{print $2}' | awk '{gsub(/.{3}/,"& ")}1' | awk '{print $1}')
# Emit a log message
# input: $1 - level: the symbolic log level
# input: $2 - msg: the message to log
__gwmon_log() {
local stderr=''
if [ "$GWMON_DEBUG_OUTOUT" -gt 0 ]; then
stderr='-s'
fi
logger $stderr -t repacd.gwmon -p "user.$1" "$*"
}
# Emit a log message at debug level
# input: $1 - msg: the message to log
__gwmon_debug() {
__gwmon_log 'debug' "$1"
}
# Emit a log message at info level
# input: $1 - msg: the message to log
__gwmon_info() {
__gwmon_log 'info' "$1"
}
# Emit a log message at warning level
# input: $1 - msg: the message to log
__gwmon_warn() {
__gwmon_log 'warn' "$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)
__gwmon_get_timestamp() {
timestamp=$(cut -d' ' -f1 < /proc/uptime | cut -d. -f 1)
eval "$1=$timestamp"
}
__gwmon_find_switch() {
local vlan_grp
#Ignore value returned by eswitch_support in repacd. It is to be used by hyd only.
__hyfi_get_switch_iface switch_iface eswitch_support
if [ -n "$switch_iface" ]; then
$GWMON_SWITCH_CONFIG_COMMAND dev switch0 set flush_arl 2>/dev/null
vlan_grp="$(echo $switch_iface | awk -F. '{print $2}' 2>/dev/null)"
fi
if [ -z "$vlan_grp" ]; then
vlan_group="1"
else
vlan_group="$vlan_grp"
fi
}
__gwmon_get_switch_ports() {
local config="$1"
local vlan_group="$2"
local ports vlan cpu_port __cpu_portmap
config_get vlan "$config" vlan
config_get ports "$config" ports
[ ! "$vlan" = "$vlan_group" ] && return
cpu_port=$(echo "$ports" | awk '{print $1}')
ports=$(echo "$ports" | sed "s/$cpu_port //g")
eval "$3='$ports'"
cpu_port=$(echo "$cpu_port" | awk -Ft '{print $1}')
case $cpu_port in
0) __cpu_portmap=0x01;;
1) __cpu_portmap=0x02;;
2) __cpu_portmap=0x04;;
3) __cpu_portmap=0x08;;
4) __cpu_portmap=0x10;;
5) __cpu_portmap=0x20;;
6) __cpu_portmap=0x40;;
7) __cpu_portmap=0x80;;
esac
eval "$4='$__cpu_portmap'"
}
__gwmon_set_hop_count() {
local config=$1
local iface mode
config_get mode "$config" mode
config_get iface "$config" ifname
if [ "$mode" = "ap" ]; then
__gwmon_info "Setting intf [$iface] hop count $2"
if [ -z $REPACD_CFG80211 ]; then
iwpriv "$iface" set_whc_dist "$2"
else
cfg80211tool "$iface" set_whc_dist "$2"
fi
fi
}
# Check GW reachability over Ethernet backhaul,
# Set hop count correctly to prevent isolated island condition
__gwmon_prevent_island_loop() {
local retries=3
local gw_ip next_hop_count=255
while [ "$retries" -gt 0 ]; do
gw_ip=$(route -n | grep ^0.0.0.0 | grep "br-$1" | awk '{print $2}')
[ -z "$gw_ip" ] && break
# Ping returns zero if at least one response was heard from the specified host
if ping -W 2 "$gw_ip" -c1 > /dev/null; then
__gwmon_debug "Ping to GW IP[$gw_ip] success"
next_hop_count=1
break
else
# no ping response was received, retry
retries=$((retries -1))
__gwmon_debug "Ping to GW IP[$gw_ip] failed ($retries retries left)"
fi
done
if [ $last_hop_count -ne $next_hop_count ]; then
__gwmon_info "Changing hop_count from $last_hop_count to $next_hop_count"
config_load wireless
config_foreach __gwmon_set_hop_count wifi-iface $next_hop_count
last_hop_count=$next_hop_count
fi
}
__gwmon_check_gwinterface() {
local gw_iface_found=1
local giface
local siface
# Check the inteface is the vlan of ether_iface
giface=${1//.[0-9]*/}
siface=${2//.[0-9]*/}
if [ "$siface" = "$giface" ]; then
eval "$3=$gw_iface_found"
__gwmon_info "Both Switch and Gw Interface Match"
else
eval "$3=0"
fi
}
# Check the link status of the interface connected to the gataway
# returns: 0 if gateway is detected; non-zero if it is not
__gwmon_check_gw_iface_link() {
local ret
local interf
__gwmon_check_gwinterface $gw_iface $switch_iface interf
if [ "$interf" -gt 0 ]; then
local link_status
# Before we check local link status, make sure gw_iface (eth) is up
ret=$(ifconfig $gw_iface | grep "UP[A-Z' ']*RUNNING")
__gwmon_info "Checking ifconfig $gw_iface is up and running, ret= $ret"
[ -z "$ret" ] && prev_gw_link="down" && return 1
for switch_port in $gw_switch_port;do
if [ "$switch_port" = '0' ]; then
continue
fi
link_status=$($GWMON_SWITCH_CONFIG_COMMAND dev switch0 port $gw_switch_port show | grep link | awk -F: '{print $NF}')
done
if [ ! "$link_status" = "down" ]; then
# link is up
if [ ! "$prev_gw_link" = "up" ]; then
__gwmon_info "Link to GW UP"
prev_gw_link="up"
fi
# Check if GW is reachable, set appropriate hop count to avoid isolated island condition
__gwmon_prevent_island_loop $managed_network
return 0
fi
else
ret=$(ifconfig $gw_iface | grep "UP[A-Z' ']*RUNNING")
[ -n "$ret" ] && return 0
fi
if [ ! "$prev_gw_link" = "down" ]; then
__gwmon_info "Link to GW DOWN"
prev_gw_link="down"
fi
return 1
}
# __gwmon_check_gateway
# input: $1 1905.1 managed bridge
# output: $2 Gateway interface
# returns: 0 if gateway is detected; non-zero if not detected
__gwmon_check_gateway() {
local gw_ip gw_br_port __gw_iface
local ether_ifaces_full ether_ifaces
local ether_iface ret
local interface_gw
gw_ip=$(route -n | grep ^0.0.0.0 | grep "br-$1" | awk '{print $2}')
[ -z "$gw_ip" ] && return 1
if ping -W 2 "$gw_ip" -c1 > /dev/null; then
if [ "$IS_GW_REACHABLE" -eq 0 ]; then
__gwmon_info "GW ($gw_ip) reachable"
fi
IS_GW_REACHABLE=1
GW_NOT_REACHABLE_TIMESTAMP=0
else
if [ "$IS_GW_REACHABLE" -eq 1 ]; then
__gwmon_info "GW ($gw_ip) NOT reachable"
fi
IS_GW_REACHABLE=0
if [ "$GW_NOT_REACHABLE_TIMESTAMP" -eq 0 ]; then
__gwmon_get_timestamp GW_NOT_REACHABLE_TIMESTAMP
fi
fi
gw_mac=$(grep -w "$gw_ip" /proc/net/arp | grep -w "$1" | awk '{print $4}')
[ -z "$gw_mac" ] && return 1
# Instead of using hyctl (which may not be installed if not running the
# full Hy-Fi build), use brctl instead. One additional step of mapping
# the port number to an interface name is needed though.
gw_br_port=$(brctl showmacs "br-$1" | grep -i "$gw_mac" | awk '{print $1}')
[ -z "$gw_br_port" ] && return 1
__gw_iface=$(brctl showstp "br-$1" | grep \("$gw_br_port"\) | awk '{print $1}')
[ -z "$__gw_iface" ] && return 1
# Get all Ethernet interfaces
hyfi_get_ether_ifaces "$1" ether_ifaces_full
hyfi_strip_list "$ether_ifaces_full" ether_ifaces
# Check if this interface belongs to our network
for ether_iface in $ether_ifaces; do
if [ "$ether_iface" = "$__gw_iface" ]; then
gw_iface=$__gw_iface
__gwmon_info "Detected Gateway on interface $gw_iface"
__gwmon_check_gwinterface "$gw_iface" "$switch_iface" interface_gw
if [ "$interface_gw" -gt 0 ]; then
local portmap __gw_switch_port=99
if [[ "$dut_kernel" = "$GWMON_KERNEL" && "$dut_platform" = "$GWMON_PLATFORM" ]]; then
__gw_switch_port=$($GWMON_SWITCH_CONFIG_COMMAND dev switch0 get arl_table | grep -i "$gw_mac" | awk -F ":" '{print $1}' | awk '{print $2}')
else
portmap=$($GWMON_SWITCH_CONFIG_COMMAND dev switch0 get dump_arl | grep -i "$gw_mac" | grep -v $cpu_portmap | awk '{print $4}')
case $portmap in
0x01) __gw_switch_port=0;;
0x02) __gw_switch_port=1;;
0x04) __gw_switch_port=2;;
0x08) __gw_switch_port=3;;
0x10) __gw_switch_port=4;;
0x20) __gw_switch_port=5;;
0x40) __gw_switch_port=6;;
0x80) __gw_switch_port=7;;
esac
fi
if [ "$__gw_switch_port" -eq 99 ]; then
__gwmon_warn "invalid port map $portmap"
return 1
fi
gw_switch_port=$__gw_switch_port
fi
__repacd_wifimon_bring_iface_down $sta_iface_5g
__repacd_wifimon_bring_iface_down $sta_iface_24g
return 0
fi
done
# also check the loop prevention code to see if it believes we have
# an upstream facing Ethernet interface
local num_upstream
num_upstream=$(lp_numupstream)
if [ "${num_upstream}" -gt 0 ]; then
return 0
fi
return 1
}
# Determine if the GW is reachable and update the router_detected global
# variable accordingly.
__gwmon_update_router_detected() {
if __gwmon_check_gateway "$managed_network"; then
router_detected=1
else
router_detected=0
fi
}
# Check whether the configured mode matches the mode that is determined by
# checking for connectivity to the gateway.
#
# input: $1 cur_role: the current mode that is configured
# input: $2 start_mode: the mode in which the auto-configuration script is being
# run; This is used by the init script to help indicate
# that it was an explicit change into this mode.
# If the mode was CAP, then it should take some time
# before it is willing to switch back to non-CAP due
# to lack of a gateway.
# input: $3 managed_network: the logical name for the network interfaces to
# monitor
#
# return: value indicating the desired mode of operation
# - $GWMON_MODE_CAP to act as the main AP
# - $GWMON_MODE_NON_CAP to switch to being a secondary AP
# - $GWMON_MODE_NO_CHANGE for now change in the mode
__gwmon_init() {
local cur_mode=$1
local start_mode=$2
local eth_mon_enabled
managed_network=$3
__gwmon_find_switch "$managed_network"
[ -n "$switch_iface" ] && __gwmon_info "found switch on $switch_iface VLAN=$vlan_group"
config_load repacd
config_get eth_mon_enabled repacd 'EnableEthernetMonitoring' '0'
config_load network
config_foreach __gwmon_get_switch_ports switch_vlan "$vlan_group" switch_ports cpu_portmap
__gwmon_info "switch ports in the $managed_network network: $switch_ports"
__gwmon_update_router_detected
if [ "$cur_mode" = "CAP" ]; then
if [ "$router_detected" -eq 0 ]; then
if [ "$eth_mon_enabled" -eq 0 ] && [ ! "$start_mode" = "CAP" ]; then
return $GWMON_MODE_NON_CAP
else
local retries=3
while [ "$retries" -gt 0 ]; do
__gwmon_update_router_detected
[ "$router_detected" -gt 0 ] && break
retries=$((retries - 1))
__gwmon_debug "redetecting gateway ($retries retries left)"
done
# If gateway was still not detected after our attempts,
# indicate we should change to non-CAP mode.
if [ "$router_detected" -eq 0 ]; then
if [ "$eth_mon_enabled" -eq 0 ]; then
return $GWMON_MODE_NON_CAP
else
return $GWMON_MODE_NO_CHANGE
fi
fi
fi
fi
else # non-CAP mode
if [ "$router_detected" -eq 1 ]; then
local mixedbh
mixedbh=$(uci get repacd.repacd.EnableMixedBackhaul 2>/dev/null)
if [ "$mixedbh" != "1" ]; then
return $GWMON_MODE_CAP
fi
fi
fi
return $GWMON_MODE_NO_CHANGE
}
# return: 2 to indicate CAP mode; 1 for non-CAP mode; 0 for no change
__gwmon_check() {
if [ "$router_detected" -eq 0 ]; then
__gwmon_update_router_detected
if [ "$router_detected" -gt 0 ]; then
local mixedbh
mixedbh=$(uci get repacd.repacd.EnableMixedBackhaul 2>/dev/null)
# if we want to support mixed backhaul, e.g., if we want to
# enable both WiFi and Ethernet backhaul, then we stay in
# non-cap mode so the STA interfaces remain up. otherwise,
# set to cap mode which brings down the STA interfaces.
if [ "$mixedbh" != "1" ]; then
return $GWMON_MODE_CAP
fi
fi
else
if ! __gwmon_check_gw_iface_link "$managed_network"; then
# Gateway is gone
router_detected=0
gw_iface=""
gw_switch_port=""
return $GWMON_MODE_NON_CAP
fi
fi
return $GWMON_MODE_NO_CHANGE
}