add vfconfig

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2020-07-23 12:50:48 +02:00
parent 96b87196b0
commit 34553e8cc9
4 changed files with 292 additions and 0 deletions

View File

@ -0,0 +1,40 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=vfconfig
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0
include $(INCLUDE_DIR)/package.mk
define Package/vfconfig
SECTION:=net
CATEGORY:=Network
MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
TITLE:=UCI configuration support for VLAN filtering rules
DEPENDS:= +ip-bridge +ip-full
PKGARCH:=all
endef
define Package/vfconfig/description
This package provides UCI configuration abstraction for VLAN filter rules
on top of Linux bridge interfaces.
endef
define Build/Compile
endef
define Build/Configure
endef
define Package/vfconfig/install
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DATA) ./files/vfconfig.hotplug $(1)/etc/hotplug.d/iface/01-vfconfig
$(INSTALL_DIR) $(1)/lib/network
$(INSTALL_DATA) ./files/vfconfig.include $(1)/lib/network/vfconfig.sh
$(INSTALL_DIR) $(1)/sbin
$(INSTALL_BIN) ./files/vfconfig.sh $(1)/sbin/vfconfig
endef
$(eval $(call BuildPackage,vfconfig))

View File

@ -0,0 +1,7 @@
#!/bin/sh
if [ "$ACTION" = ifup ] && [ "$INTERFACE" = loopback ]; then
. /lib/network/dsaconfig.sh
setup_switch
fi

View File

@ -0,0 +1,11 @@
#!/bin/sh
setup_switch() {
# Skip switch setup on network restart. The netifd process
# will be started afterwards and remove all interfaces again...
if [ "$initscript" = /etc/init.d/network ] && [ "$action" = restart ]; then
return 0
fi
/sbin/dsaconfig apply 2>&1 | logger -t dsaconfig
}

View File

@ -0,0 +1,234 @@
#!/bin/sh
. /lib/functions.sh
. /lib/functions/network.sh
bridge_names=""
warn() {
echo "$@" >&2
}
clear_port_vlans() {
local port=$1
local self=$2
local vlans=$(bridge vlan show dev "$port" | sed -ne 's#^[^ ]* \+\([0-9]\+\).*$#\1#p')
local vlan
for vlan in $vlans; do
bridge vlan del vid "$vlan" dev "$port" $self
done
}
add_bridge() {
local cfg=$1
local brname
config_get brname "$cfg" bridge "switch0"
case " $bridge_names " in
*" $brname "*) return 1 ;;
esac
append bridge_names "$brname"
export -n "bridge=$brname"
}
validate_vid() {
local vid=$1
case "$vid" in
[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-4][0-9][0-9][0-9])
if [ $vid -gt 4096 ]; then
return 1
fi
;;
*)
return 1
;;
esac
return 0
}
setup_bridge() {
local cfg=$1
local bridge
add_bridge "$cfg" || return 0
# Prevent netifd from picking up our switch bridge just yet
network_defer_device "$bridge"
# (Re)create switch bridge device in case it is not yet set up
local filtering=$(cat "/sys/class/net/$bridge/bridge/vlan_filtering" 2>/dev/null)
if [ ${filtering:-0} != 1 ]; then
ip link set "$bridge" down 2>/dev/null
ip link delete dev "$bridge" 2>/dev/null
ip link add name "$bridge" type bridge
echo 1 > "/sys/class/net/$bridge/bridge/vlan_filtering"
fi
ip link set "$bridge" up
# Unbridge DSA ports and flush any VLAN filters on them, they're added back later
local port
for port in /sys/class/net/*"/upper_${cfg}"; do
if [ -e "$port" ]; then
port=${port%/upper_*}
port=${port##*/}
ip link set "$port" nomaster
# Unbridging the port should already clear VLANs, but be safe
clear_port_vlans "$port"
fi
done
# Clear any VLANs on the switch bridge, they're added back later
clear_port_vlans "$bridge" self
}
setup_bridge_vlan() {
local cfg=$1
local bridge vlan ports
config_get bridge "$cfg" bridge "switch0"
config_get vlan "$cfg" vlan
config_get ports "$cfg" ports
validate_vid "$vlan" || {
warn "VLAN section '$cfg' specifies an invalid VLAN ID '$vlan'"
return 1
}
# Setup ports
local port tag pvid
for port in $ports; do
tag=untagged
pvid=
case "$port" in
*"*")
pvid=pvid
port=${port%\*}
;;
esac
case "$port" in
*:u)
port=${port%:u}
;;
*:t)
tag=tagged
port=${port%:t}
;;
esac
# Add the port to the switch bridge and delete the default
# VLAN 1 if it is not yet joined to the bridge.
if [ ! -e "/sys/class/net/$port/upper_$bridge" ]; then
ip link set dev "$port" up
ip link set dev "$port" master "$bridge"
# Get rid of default VLAN 1
bridge vlan del vid 1 dev "$port"
fi
# Promote the first untagged VLAN of this port to the PVID
if [ "$tag" = untagged ] && ! bridge vlan show dev "$port" | grep -qi pvid; then
pvid=pvid
fi
# Add VLAN filter entry for port
bridge vlan add dev "$port" vid $vlan $pvid $tag
done
# Make the switch bridge itself handle the VLAN as well
bridge vlan add dev "$bridge" self vid $vlan tagged
}
apply_config() {
config_load network
config_foreach setup_bridge vlan_filter
config_foreach setup_bridge_vlan vlan_filter
# Ready switch bridge devices
local bridge
for bridge in $bridge_names; do
network_ready_device "$bridge"
done
}
show_bridge() {
local cfg=$1
local bridge
add_bridge "$cfg" || return 0
printf "Bridge: %s\n" "$bridge"
printf "VLAN/"
local port ports
for port in "/sys/class/net/$bridge/lower_"*; do
[ -e "$port" ] || continue
port=${port##*/lower_}
printf " | %-5s" "$port"
append ports "$port"
done
printf " |\nLink:"
for port in $ports; do
local carrier=$(cat "/sys/class/net/$port/carrier")
local duplex=$(cat "/sys/class/net/$port/duplex")
local speed=$(cat "/sys/class/net/$port/speed")
if [ ${carrier:-0} -eq 0 ]; then
printf " | %-5s" "down"
else
[ "$duplex" = "full" ] && duplex=F || duplex=H
printf " | %4d%s" "$speed" "$duplex"
fi
done
local vlans=$(bridge vlan show dev "$bridge" | sed -ne 's#^[^ ]* \+\([0-9]\+\).*$#\1#p')
local vlan
for vlan in $vlans; do
printf " |\n%4d " "$vlan"
for port in $ports; do
local pvid="" utag="" word
for word in $(bridge vlan show dev "$port" vid "$vlan"); do
case "$word" in
PVID) pvid="*" ;;
"$vlan") utag="t" ;;
Untagged) utag="u" ;;
esac
done
printf " | %-2s " "$utag$pvid"
done
done
printf " |\n\n"
}
show_config() {
config_load network
config_foreach show_bridge vlan_filter
}
case "$1" in
show) show_config ;;
apply) apply_config ;;
*)
echo "Usage: ${0##*/} show"
echo " ${0##*/} apply"
exit 1
;;
esac