diff --git a/net/switchdev-poller/Makefile b/net/switchdev-poller/Makefile new file mode 100644 index 000000000..3716f159c --- /dev/null +++ b/net/switchdev-poller/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (C) 2018-2019 Luiz Angelo Daros de Luca +# +# This is free software, licensed under the GNU General Public License v2. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=switchdev-poller +PKG_VERSION:=1.0.1 +PKG_RELEASE:=1 +PKG_MAINTAINER:=Luiz Angelo Daros de Luca +PKG_LICENSE:=GPLv2 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) +include $(INCLUDE_DIR)/package.mk + +define Package/switchdev-poller + SECTION:=net + CATEGORY:=Network + DEPENDS:=+swconfig + TITLE:=Poll switchdev port to bring CPU ports up/down + PKGARCH:=all +endef + +define Package/switchdev-poller/description +This service monitors switchdev ports and brings down CPU ports when +all related non-CPU vlan ports are also down. Otherwise, it brings +the port up. + +endef + +define Build/Compile +endef + +define Package/switchdev-poller/install + $(CP) ./files/* $(1) +endef + +$(eval $(call BuildPackage,switchdev-poller)) diff --git a/net/switchdev-poller/files/etc/init.d/switchdev-poller b/net/switchdev-poller/files/etc/init.d/switchdev-poller new file mode 100755 index 000000000..a6a5dc6d5 --- /dev/null +++ b/net/switchdev-poller/files/etc/init.d/switchdev-poller @@ -0,0 +1,18 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2018-2019 Luiz Angelo Daros de Luca + +START=99 +USE_PROCD=1 + +start_service() { + procd_open_instance switchdev-poller + procd_set_param command /usr/lib/switchdev-poller/switchdev-poller + procd_set_param stdout 1 + procd_set_param stderr 0 # enable for extra debug info + procd_set_param respawn 3600 5 5 + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger network +} diff --git a/net/switchdev-poller/files/usr/lib/switchdev-poller/switchdev-poller b/net/switchdev-poller/files/usr/lib/switchdev-poller/switchdev-poller new file mode 100755 index 000000000..1e2f79e25 --- /dev/null +++ b/net/switchdev-poller/files/usr/lib/switchdev-poller/switchdev-poller @@ -0,0 +1,159 @@ +#!/bin/sh +# shellcheck disable=SC2039,SC1091 +# https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, SC2039: In POSIX sh, string replacement is undefined. +# https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, string indexing is undefined. +# https://www.shellcheck.net/wiki/SC1091 -- Not following: /lib/functions.sh:... +# Copyright (C) 2018 Luiz Angelo Daros de Luca +# +# Pools switchdev for port changes +# + +[ -n "$NICED" ] && NICED=1 exec nice -n 19 "$0" "$@" + +. /usr/share/libubox/jshn.sh +. /lib/functions.sh + +cpu_ports="" + +json_init +json_load "$(cat /etc/board.json)" + +switches= +json_get_keys switches switch +json_select switch +for switch in $switches; do + echo Loading $switch >&2 + json_select "$switch" + if json_is_a ports array; then + ports= + json_get_keys ports ports + json_select ports + + for port in $ports; do + echo Checking port "$port" in "$switch" >&2 + json_select "$port" + num= + json_get_vars num device + if [ -n "$device" ]; then + echo "Port ${switch}_$num is CPU port as $device" >&2 + cpu_ports="$cpu_ports ${switch}_$num=$device" + fi + json_select .. + done + json_select .. + fi + json_select .. +done + +each_switch_vlan() { + switch= + vlan= + ports= + config_get switch "$1" device + config_get vlan "$1" vlan + config_get ports "$1" ports + + [ -n "$vlan" ] || { echo "No vlan for '$1'" >&2; return 1; } + [ -n "$switch" ] || { echo "No device for '$1'" >&2; return 1; } + + vlan_ifnames="" + vlan_non_cpu_ports="" + echo Checking vlan "$vlan" in $switch >&2 + for port in $ports; do + case $port in + *t) tagged=1; port=${port:0:-1} ;; + *) tagged= ;; + esac + echo "Checking port $port in $switch used by vlan $vlan" >&2 + + cpu_port_ifname="" + for cpu_port in $cpu_ports; do + device=${cpu_port#*=} + cpu_switchport=${cpu_port%=*} + [ "${cpu_switchport}" = "${switch}_${port}" ] || continue + echo "Port $port in $switch used by $vlan is a CPU port at $device" >&2 + + for device_tagged in $device_tagged; do + [ "${tagged}" = 1 ] && not_tagged=0 || not_tagged=1 + if [ "$device_tagged" = "$device=${not_tagged}" ]; then + echo "Cannot control CPU port ${cpu_switchport} when it is used both as tagged and not tagged" + exit 1 + fi + done + device_tagged="${device_tagged} ${device}=${tagged:-0}" + cpu_port_ifname=${device}${tagged+.$vlan} + done + + if [ -n "$cpu_port_ifname" ]; then + vlan_ifnames="$vlan_ifnames $cpu_port_ifname" + continue + fi + vlan_non_cpu_ports="$vlan_non_cpu_ports ${port}" + done + vlan_non_cpu_switch_ports="${vlan_non_cpu_ports// /:${switch}_}" + vlan_non_cpu_switch_ports="${vlan_non_cpu_switch_ports:1}" + vlan_non_cpu_ports="${vlan_non_cpu_ports:1}" + for vlan_ifname in $vlan_ifnames; do + devices2ports="$devices2ports $vlan_ifname=${vlan_non_cpu_switch_ports}" + echo "Monitoring $switch (ports ${vlan_non_cpu_ports// /,}) for $vlan_ifname" + done +} + +device_tagged="" +devices2ports="" +config_load network +config_foreach each_switch_vlan switch_vlan + +cleanup() { + for device2ports in $devices2ports; do + device=${device2ports%=*} + ip link show dev ${device}_down >/dev/null 2>&1 && { + echo "Bringing up $device on exit..." + ip link set dev ${device}_down name ${device} + ip link set dev ${device} up >/dev/null 2>&1; + } + done + echo "Stopped poller" + exit +} +trap cleanup INT TERM +echo "Starting poller" +while true; do ( +for device2ports in $devices2ports; do + device=${device2ports%=*} + switch_ports=${device2ports#*=} + switch_ports=${switch_ports//:/ } + should_be_up=false + for switch_port in $switch_ports; do + state=$(eval echo \$${switch_port}) + if [ -z "${state}" ]; then + switch=${switch_port%_*} + port=${switch_port#*_} + echo "Polling $switch_port..." >&2 + swconfig dev $switch port $port get link | grep -q 'link:up' && + state=up || state=down + eval "$switch_port=$state" + echo "State of $switch_port, used by $device, is $state" >&2 + else + echo "State of $switch_port, used by $device, is $state (cached)" >&2 + fi + [ $state = up ] && should_be_up=true + done + + if $should_be_up; then + ip link show dev ${device}_down >/dev/null 2>&1 && { + echo "Bringing up $device..." + ip link set dev ${device}_down name ${device} + ip link set dev ${device} up >/dev/null 2>&1; + } + else + ip link show dev ${device} >/dev/null 2>&1 && { + echo "Bringing down $device..." + ip link set dev ${device} down >/dev/null 2>&1; + ip link set dev ${device} name ${device}_down + } + + fi +done ) +sleep 3 +done