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.

396 lines
12 KiB
Bash

4 years ago
#!/bin/sh
# Copyright (c) 2016 Qualcomm Atheros, Inc.
#
# All Rights Reserved.
# Qualcomm Atheros Confidential and Proprietary.
. /usr/share/libubox/jshn.sh
. /lib/functions/repacd-netdet.sh
LP_DIRECTION_UPSTREAM="upstream"
LP_DIRECTION_DOWNSTREAM="downstream"
LP_DIRECTION_UNKNOWN="unknown"
LP_ROLE_CAP="CAP"
LP_ROLE_RE="RE"
LP_VLAN_OFFSET=10
LP_DIRECTION_DB="/etc/ports.dir"
EDMA_ETH0_PORTBMP="/proc/sys/net/edma/default_group1_bmp"
EDMA_ETH1_PORTBMP="/proc/sys/net/edma/default_group2_bmp"
#TODO - these values need to be determined by board
#Port number of the port in the eth0 vlan by default
LP_ETH0_PORT=5
#Port number of the cpu port including the "t" for tagging if appropriate
LP_CPU_PORT="0t"
#TODO - these should probably be configurable from outside ths script
#Timeout for dhcp requests from CA
LP_DHCP_TO_CAP=15
#Timeout for dhcp requests from RE (needs to be longer than from CA)
LP_DHCP_TO_RE=30
__lp_loginfo() {
logger -t repacd.lp -p user.info "$1"
}
__lp_logdebug() {
logger -t repacd.lp -p user.debug "$1"
}
#Returns the default interface for the given port
lp_defaultinterface() {
if [ $port = $LP_ETH0_PORT ]; then
echo "eth0"
else
echo "eth1"
fi
}
#Returns the isolated inteface name for a given port
lp_interfacebyport() {
local port=$1
local baseinterface=$(lp_defaultinterface $port)
local interface="${baseinterface}.$(( $port + $LP_VLAN_OFFSET ))"
echo $interface
}
#Returns the role of the device, either "CAP" or "RE"
lp_devicerole() {
#A CAP should have an entry for network.wan.ifname, an RE should not
uci get network.wan.ifname >/dev/null
if [ $? = 0 ]; then
echo $LP_ROLE_CAP
else
echo $LP_ROLE_RE
fi
}
#Returns the determined direction (upstream, downstram, or unknown) for the
#given port
lp_getportdirection() {
local port=$1
local direction=$LP_DIRECTION_UNKNOWN
if [ -f $LP_DIRECTION_DB ]; then
json_load "$(cat $LP_DIRECTION_DB)"
json_select "port" >/dev/null
json_get_var direction $port
fi
echo $direction
}
#set the current direction (upstream, downstream, or unknown) in a
#persistent db
lp_setportdb() {
local port=$1
local direction=$2
local current
local new
local nports=$(lp_numports)
local dir
if [ -f $LP_DIRECTION_DB ]; then
current=$(cat $LP_DIRECTION_DB)
else
current="{}"
fi
json_init
json_add_array "port"
new=$(json_dump)
for p in $(seq 1 $nports); do
if [ $p = $port ]; then
dir=$direction
else
json_load "$current"
dir=$LP_DIRECTION_UNKNOWN
json_select "port" >/dev/null
json_get_var dir $p
fi
json_load "$new"
json_select "port"
json_add_string $p $dir
new=$(json_dump)
done
json_close_array
new=$(json_dump)
local tmpfile="$(dirname $LP_DIRECTION_DB)/.$(basename $LP_DIRECTION_DB)"
echo "$new" > $tmpfile
mv $tmpfile $LP_DIRECTION_DB
}
#Returns the current number of ports which are marked upstream
lp_numupstream() {
local nup=0
local dir
local nports=$(lp_numports)
if [ -f $LP_DIRECTION_DB ]; then
json_load "$(cat $LP_DIRECTION_DB)"
json_select "port" >/dev/null
for p in $(seq 1 $nports); do
dir=$LP_DIRECTION_UNKNOWN
json_get_var dir $p
if [ $dir = $LP_DIRECTION_UPSTREAM ]; then
nup=$(( nup + 1 ))
fi
done
fi
echo $nup
}
#Returns the number of ethernet ports on switch0
lp_numports() {
local lines=$(swconfig dev switch0 show | grep ports | wc -l)
local words=$(swconfig dev switch0 show | grep ports | wc -w)
echo $(( (( $words - $lines )) - $lines ))
}
#Determines whether the given interface is upstream or downstream
lp_determinedirection() {
local port=$1
#If netdet has already determined that this is a CAP device
#then we will already have an upstream connection, and all
#subsequent connections (including this one) should be
#considered downstream
local netdetmode=$(netdet_get_mode_db)
local upstrmports=$(lp_numupstream)
local portdirection=$(lp_getportdirection $port)
logger -t repacd.lp -p user.debug "netdetmode: $netdetmode"
logger -t repacd.lp -p user.debug "upstrmports: $upstrmports"
if [ $netdetmode = "cap" ] && [ $upstrmports -gt 0 ] && [ $portdirection != $LP_DIRECTION_UPSTREAM ]; then
logger -t repacd.lp -p user.info "Port $port assumed downstream on cap"
echo $LP_DIRECTION_DOWNSTREAM
else
local role=$2
local iface=$(lp_interfacebyport $port)
local timeout
if [ $role = $LP_ROLE_CAP ]; then
timeout=$LP_DHCP_TO_CAP
else
timeout=$LP_DHCP_TO_RE
fi
if udhcpc -i $iface -t $timeout -T1 -n -q -R > /dev/null 2>&1; then
__lp_loginfo "Port $port determined upstream"
echo $LP_DIRECTION_UPSTREAM
else
__lp_loginfo "Port $port determined downstream"
echo $LP_DIRECTION_DOWNSTREAM
fi
fi
}
#Isolate port in its own vlan and create virtual interface
lp_isolateport() {
local port=$1
local isovlan=$(( $port + $LP_VLAN_OFFSET ))
local currvlan=$(swconfig dev switch0 port $port get pvid)
local currports=$(swconfig dev switch0 vlan $currvlan show | grep ports | cut -d: -f2)
local newports=""
__lp_loginfo "Isolating switch port $port into its own vlan"
#remove port from its current vlan
for p in $currports; do
if [ $p != $port ]; then
newports="$newports $p"
fi
done
__lp_logdebug "swconfig dev switch0 vlan $currvlan set ports \"$LP_CPU_PORT $newports\""
swconfig dev switch0 vlan $currvlan set ports "$LP_CPU_PORT $newports"
#add port to isovlan
currports=$(swconfig dev switch0 vlan $isovlan show | grep ports | cut -d: -f2)
__lp_logdebug "swconfig dev switch0 vlan $isovlan set ports \"$LP_CPU_PORT $currports $port\""
swconfig dev switch0 vlan $isovlan set ports "$LP_CPU_PORT $currports $port"
__lp_logdebug "swconfig dev switch0 set apply"
swconfig dev switch0 set apply
if [ $port = $LP_ETH0_PORT ]; then
__lp_logdebug "vconfig add eth0 $isovlan"
vconfig add eth0 $isovlan
else
__lp_logdebug "vconfig add eth1 $isovlan"
vconfig add eth1 $isovlan
fi
lp_setedmamasks $port $(lp_defaultinterface $port)
}
lp_deisolateport() {
local port=$1
local isovlan=$(( $port + $LP_VLAN_OFFSET ))
local currvlan=$(swconfig dev switch0 port $port get pvid)
if [ $isovlan = $currvlan ]; then
#This check might not be necessary, but it prevents us from
#manipulating a port which is not isolated
local isoiface=$(lp_interfacebyport $port)
local unisovlan
#Remove port form the isolated vlan
swconfig dev switch0 vlan $isovlan set ports ""
#Remove virtual interface
vconfig rem $isoiface
#put port back in the appropriate eth0 or eth1 vlan
if [ $port = $LP_ETH0_PORT ]; then
unisovlan=2
else
unisovlan=1
fi
local currports=$(swconfig dev switch0 vlan $unisovlan show | grep ports | cut -d: -f2)
swconfig dev switch0 vlan $unisovlan set ports "$currports $port"
swconfig dev switch0 set apply
fi
}
#set the edma masks so the bitmap for the given iface (eth0 or eth1)
#contains the given port, and the bitmask fot the other iface does not
lp_setedmamasks() {
local port=$1
local dest_iface=$2
local currmask_eth0=$(cat $EDMA_ETH0_PORTBMP)
local currmask_eth1=$(cat $EDMA_ETH1_PORTBMP)
local newmask_eth0
local newmask_eth1
if [ $dest_iface = "eth0" ]; then
newmask_eth0=$(( $currmask_eth0 | (1 << $port) ))
newmask_eth1=$(( $currmask_eth1 & ~(1 << $port) ))
elif [ $dest_iface = "eth1" ]; then
newmask_eth0=$(( $currmask_eth0 & ~(1 << $port) ))
newmask_eth1=$(( $currmask_eth1 | (1 << $port) ))
else
#should not get here, but if we do do not adjust either bitmap
newmask_eth0=$currmask_eth0
newmask_eth1=$currmask_eth1
fi
printf "0x%x" $newmask_eth0 > $EDMA_ETH0_PORTBMP
printf "0x%x" $newmask_eth1 > $EDMA_ETH1_PORTBMP
}
#moves the given port to the eth0 vlan (2) or the eth1 vlan (1)
#and removes it from the other via swconfig
lp_moveportswconfig() {
local port=$1
local dest_iface=$2
local removefromvlan
local addtovlan
local isovlan=$(( $port + $LP_VLAN_OFFSET ))
local isoiface=$(lp_interfacebyport $port)
if [ $dest_iface = "eth0" ]; then
removefromvlan=1
addtovlan=2
elif [ $dest_iface = "eth1" ]; then
removefromvlan=2
addtovlan=1
else
#should not get here, but if we do do not move port
removefromvlan=3
addtovlan=3
fi
local currports=$(swconfig dev switch0 vlan $removefromvlan show | grep ports | cut -d: -f2)
local newports
for p in $currports; do
if [ $p != $port ]; then
newports="$newports $p"
fi
done
__lp_logdebug "swconfig dev switch0 vlan $removefromvlan set ports $newports"
swconfig dev switch0 vlan $removefromvlan set ports $newports
currports=$(swconfig dev switch0 vlan $addtovlan show | grep ports | cut -d: -f2)
__lp_logdebug "swconfig dev switch0 vlan $addtovlan set ports \"$currports $port\""
swconfig dev switch0 vlan $addtovlan set ports "$currports $port"
#eliminate the isolation vlan and virtual interface
__lp_logdebug "swconfig dev switch0 vlan $isovlan set ports \"\""
swconfig dev switch0 vlan $isovlan set ports ""
__lp_logdebug "vconfig rem $isoiface"
vconfig rem $isoiface
__lp_logdebug "swconfig dev switch0 set apply"
swconfig dev switch0 set apply
}
#Move the given port to the eth0 vlan
lp_moveporttoeth0() {
local port=$1
__lp_loginfo "Moving switch port $port into the eth0 vlan"
lp_moveportswconfig $port "eth0"
lp_setedmamasks $port "eth0"
}
#Move the given port to the eth1 vlan
lp_moveporttoeth1() {
local port=$1
__lp_loginfo "Moving switch port $port into the eth1 vlan"
lp_moveportswconfig $port "eth1"
lp_setedmamasks $port "eth1"
}
#Make the hotplug call to signal the direction has been determined for
#the given port
lp_signaldirectionhotplug() {
local port=$1
local direction=$2
(export PORT=$port; export EVENT="direction"; export STATE=$direction; hotplug-call switch)
}
#Called by hotplug whenever a port transtions to link-up status
lp_onlinkup() {
local port=$1
local role=$(lp_devicerole)
local direction
local current_direction=$(lp_getportdirection $port)
logger -t repacd.lp -p user.debug "did we miss a down notification for port $port? current_direction: $current_direction"
if [ $current_direction != $LP_DIRECTION_UNKNOWN ]; then
logger -t repacd.lp -p user.debug "we missed a down notification for port $port so act like we didn't"
lp_onlinkdown $port
fi
direction=$(lp_determinedirection $port $role)
lp_signaldirectionhotplug $port $direction
}
#Called by hotplug whenever a port transtions to link-down status
lp_onlinkdown() {
local port=$1
lp_setportdb $port $LP_DIRECTION_UNKNOWN
#isolate port back into its own iface
lp_isolateport $port
}
#Called by hotplug whenever a port direction has been determined upstream
lp_onupstream() {
local port=$1
lp_setportdb $port $LP_DIRECTION_UPSTREAM
lp_moveporttoeth0 $port
}
#Called by hotplug whenever a port direction has been determined downstream
lp_ondownstream() {
local port=$1
lp_setportdb $port $LP_DIRECTION_DOWNSTREAM
lp_moveporttoeth1 $port
}
repacd_lp_init() {
local nports=$(lp_numports)
for p in $(seq 1 $nports); do
lp_isolateport $p
lp_setportdb $p $LP_DIRECTION_UNKNOWN
done
}