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