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.

349 lines
9.9 KiB
Bash

#!/bin/sh
# Copyright (c) 2016 Qualcomm Atheros, Inc.
#
# All Rights Reserved.
# Qualcomm Atheros Confidential and Proprietary.
# NOTE: At boot, even if we add 'list interface "wan"' to /etc/config/lldpd,
# lldpd doesn't listen on eth0. We have to restart lldpd and then it starts
# listening.
NETDET_DEBUG_OUTOUT=0
NETDET_MODE_DB=/etc/ap.mode
NETDET_RESULT_ROOTAP=0
NETDET_RESULT_RE=1
NETDET_RESULT_INDETERMINATE=2
NETDET_CAP_BRIDGE_ROUTER_RESULT_ROUTER=0
NETDET_CAP_BRIDGE_ROUTER_RESULT_BRIDGE=1
NETDET_IS_WIFISON_DEVICE_VISIBLE_TRUE=0
NETDET_IS_WIFISON_DEVICE_VISIBLE_FALSE=1
NETDET_LLDP_WIFISON_CUSTOM_TLV_OUI=33,44,55
NETDET_LLDP_WIFISON_CUSTOM_TLV_SUBTYPE=44
NETDET_LLDP_WIFISON_CUSTOM_TLV_DATA=45,45,45,45,45
NETDET_CURRENT_MODE_ROOTAP=0
NETDET_CURRENT_MODE_RE=1
#Note: On some systems, the lan bridge might be named br0 or something
#else other than br-lan
NETDET_LANBRIDGE="br-lan"
# Emit a message at debug level.
# input: $1 - the message to log
__netdet_debug() {
local stderr=''
if [ "$NETDET_DEBUG_OUTOUT" -gt 0 ]; then
stderr='-s'
fi
logger $stderr -t repacd.netdet -p user.debug "$1"
}
# Check to see if lldpd is listening on a particular port
__netdet_is_lldpd_listening() {
local port=$1
lldpcli show configuration | grep "Interface pattern: " | grep $port > /dev/null
}
# Are there any Wi-Fi SON neighbors reachable via a particular port?
__netdet_has_wifison_neighbors() {
local port=$1
lldpcli show neighbors ports $port details | grep "TLV:.*OUI: ${NETDET_LLDP_WIFISON_CUSTOM_TLV_OUI}, SubType: ${NETDET_LLDP_WIFISON_CUSTOM_TLV_SUBTYPE},.* ${NETDET_LLDP_WIFISON_CUSTOM_TLV_DATA}$" > /dev/null
}
# Does a particular interface have an IPv4 address?
__netdet_has_address() {
local intf=$1
ifconfig $intf | grep "inet addr:[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" > /dev/null
}
# Is dhcpc running on a particular interface?
__netdet_is_running_dhcp_client() {
local intf=$1
ps -w | grep dhcpc | grep $intf > /dev/null
}
# Does a particular interface have a DHCP-assigned IPv4 address?
__netdet_has_dhcp_address() {
local intf=$1
__netdet_is_running_dhcp_client $intf && __netdet_has_address $intf
}
# Can you see WiFiSON devices on a particular port?
__netdet_is_wifison_device_visible() {
local port=$1
if ! __netdet_is_lldpd_listening $port; then
__netdet_debug "warning: lldpd wasn't listening on $port so restarting"
repacd_netdet_lldpd_init restart
fi
local timeout=30 # secs
local currtime=$(date +%s)
local timeouttime=$(($currtime + $timeout))
while [ $currtime -le $timeouttime ]; do
if __netdet_has_wifison_neighbors $port; then
return $NETDET_IS_WIFISON_DEVICE_VISIBLE_TRUE
fi
sleep 1
currtime=$(date +%s)
done
return $NETDET_IS_WIFISON_DEVICE_VISIBLE_FALSE
}
# Return a list of all the network interfaces that we care about
__netdet_get_interfaces() {
ifconfig | grep "^\(eth\|ath\|br\)" | awk '{ print $1 }'
}
# Enable/disable DHCP server
__repacd_netdet_dhcpd() {
case $1 in
disable | off | stop)
uci set dhcp.lan.ignore=1
;;
enable | on | start)
uci set dhcp.lan.ignore=0
;;
*)
echo "error: unknown parameter: $1" >&2
return 2
;;
esac
uci commit dhcp
/etc/init.d/dnsmasq restart
}
# Returns the determined mode (cap, re, or unknown)
netdet_get_mode_db() {
local mode="unknown"
if [ -f $NETDET_MODE_DB ]; then
mode="$(cat $NETDET_MODE_DB)"
fi
echo $mode
}
# Saves the given mode to the database
netdet_set_mode_db() {
local mode="$1"
local tmpfile="$(dirname $NETDET_MODE_DB)/.$(basename $NETDET_MODE_DB)"
echo "$mode" > $tmpfile
mv $tmpfile $NETDET_MODE_DB
}
# Add our custom WiFi SON TLV to lldpd
netdet_set_custom_wifison_lldp_tlv() {
# remove any existing custom TLVs
lldpcli unconfigure lldp custom-tlv > /dev/null
lldpcli configure lldp custom-tlv oui $NETDET_LLDP_WIFISON_CUSTOM_TLV_OUI subtype $NETDET_LLDP_WIFISON_CUSTOM_TLV_SUBTYPE oui-info $NETDET_LLDP_WIFISON_CUSTOM_TLV_DATA > /dev/null
}
# Set lldpd interfaces
netdet_set_lldpd_interfaces() {
lldpcli configure system interface pattern $(__netdet_get_interfaces | tr '\n' ',')
}
# Set lldp tx interval
# input: $1 - the interval in seconds
netdet_set_lldpd_tx_interval() {
lldpcli configure lldp tx-interval $1
}
# Do WAN/LAN detection
netdet_wan_lan_detect() {
__netdet_debug "running wan/lan detection"
local is_uplink_no_wifison="false"
local is_uplink_with_wifison="false"
local int="eth0"
__netdet_is_wifison_device_visible $int
case $? in
$NETDET_IS_WIFISON_DEVICE_VISIBLE_TRUE)
is_uplink_with_wifison="true"
__netdet_debug "$int: LAN (with visible Wi-Fi SON device)"
;;
$NETDET_IS_WIFISON_DEVICE_VISIBLE_FALSE)
is_uplink_no_wifison="true"
__netdet_debug "$int: WAN"
;;
*)
__netdet_debug "got unknown return from __netdet_is_wifison_device_visible"
;;
esac
if [ "$is_uplink_no_wifison" == "true" ]; then
__netdet_debug "looks like a root AP"
return $NETDET_RESULT_ROOTAP
elif [ "$is_uplink_with_wifison" == "true" ]; then
__netdet_debug "looks like a range extender"
return $NETDET_RESULT_RE
else
__netdet_debug "indeterminate"
return $NETDET_RESULT_INDETERMINATE
fi
}
# determines if the local device should be configured for bridge mode or router
# mode. the decision is based on whether the WAN interface is configured on
# a private network.
netdet_detect_cap_bridge_router_mode() {
local dev
local ipaddr
local net
local privnets
__netdet_debug "detecting whether this cap should be a bridge or router"
dev=$(uci get network.wan.ifname 2>/dev/null)
[ -z "${dev}" ] && {
dev=$NETDET_LANBRIDGE
}
ipaddr=$(ip addr show dev $dev | grep "inet " | awk '{print $2}')
[ -z '${ipaddr}' ] && {
# assume router so the firewall will be up when the interface comes up
__netdet_debug "no IP addr configured, should configure as router"
return $NETDET_CAP_BRIDGE_ROUTER_RESULT_ROUTER
}
net=$(ipcalc.sh ${ipaddr} | grep NETWORK)
privnets=$(uci get repacd.repacd.rfc1918 2>/dev/null)
[ -z "${privnets}" ] && {
privnets='10.0.0.0/8 172.16.0.0/12 192.168.0.0/16'
}
__netdet_debug "checking the wan iface against the list of private networks"
for privcidr in ${privnets}; do
local privnet=$(ipcalc.sh ${privcidr} | grep NETWORK)
[ "${net}" = "${privnet}" ] && {
__netdet_debug "the wan iface is on a private network, should configure as bridge"
return $NETDET_CAP_BRIDGE_ROUTER_RESULT_BRIDGE
}
done
__netdet_debug "should configure as router"
return $NETDET_CAP_BRIDGE_ROUTER_RESULT_ROUTER
}
# modifies the local device to run in bridge mode
netdet_configure_cap_bridge_mode() {
__netdet_debug "configuring cap in bridge mode"
uci set dhcp.lan.ignore=1
uci commit dhcp
/etc/init.d/dnsmasq restart
uci set network.lan.ifname='eth0 eth1'
uci set network.lan.proto='dhcp'
uci delete network.wan
uci commit network
/etc/init.d/network restart
uci set repacd.GatewayConnectedMode='CAP'
uci commit repacd
/etc/init.d/repacd restart
}
# modifies the local device to run in router mode
netdet_configure_cap_router_mode() {
__netdet_debug "configuring cap in router mode"
uci set network.lan.ifname='eth1'
uci set network.lan.proto='static'
uci set network.lan.ipaddr='192.168.1.1'
uci set network.wan=interface
uci set network.wan.ifname='eth0'
uci set network.wan.proto='dhcp'
uci commit network
/etc/init.d/network restart
uci delete dhcp.lan.ignore
uci commit dhcp
/etc/init.d/dnsmasq restart
/etc/init.d/repacd restart
}
# setup lldpd parameters since these don't persist across restart
repacd_netdet_lldpd_setup() {
netdet_set_lldpd_interfaces
netdet_set_lldpd_tx_interval 10
netdet_set_custom_wifison_lldp_tlv
}
repacd_netdet_init() {
repacd_netdet_lldpd_setup
}
repacd_netdet_lldpd_init() {
/etc/init.d/lldpd $1
if [ $1 != "stop" ]; then
while ! [ -e /var/run/lldpd.socket ]; do
sleep 1
done
repacd_netdet_lldpd_setup
fi
}
# Get the device's currently configured mode
repacd_netdet_get_current_device_mode() {
if uci get network.wan > /dev/null; then
return $NETDET_CURRENT_MODE_ROOTAP
else
return $NETDET_CURRENT_MODE_RE
fi
}
# Configure this device as either a rootap or an re
repacd_netdet_set_current_device_mode() {
local mode="$1"
case $mode in
rootap)
netdet_detect_cap_bridge_router_mode
local result=$?
case $result in
$NETDET_CAP_BRIDGE_ROUTER_RESULT_ROUTER)
netdet_configure_cap_router_mode
;;
$NETDET_CAP_BRIDGE_ROUTER_RESULT_BRIDGE)
netdet_configure_cap_bridge_mode
;;
*)
echo "error: unknown mode: $result" >&2
return 3
;;
esac
;;
re)
__repacd_netdet_dhcpd disable
uci set network.lan.ifname='eth0 eth1'
uci set network.lan.proto=dhcp
uci delete network.wan
uci commit network
/etc/init.d/network restart
/etc/init.d/repacd restart
;;
*)
echo "error: unknown mode: $mode" >&2
return 2
;;
esac
}
repacd_netdet_wait_for_dhcp_addr() {
local intf=$1
local timeout=$2
if [ -z "$timeout" ]; then
timeout=15
fi
local endtime=$(( $(date +%s) + $timeout ))
while ! __netdet_has_dhcp_address $intf; do
sleep 1
if [ $(date +%s) -gt $endtime ]; then
return 1
fi
done
return 0
}