[PATCH 1/1] dsaconfig: introduce package for UCI configuration of VLAN filter rules

John Crispin john at phrozen.org
Tue Jul 7 09:43:14 EDT 2020


On 07.07.20 15:34, Jo-Philipp Wich wrote:
> This package provides the necessary files to translate `config switch_vlan`
> and `config switch_port` sections  of `/etc/config/network` into appropriate
> bridge vlan filter rules.
>
> The approach of the configuration is to bridge all DSA ports into a logical
> bridge device, called "switch0" by default, and to set VLAN port membership,
> tagging state and PVID as specified by UCI on each port and on the switch
> bridge device itself, allowing logical interfaces to reference port VLAN
> groups by using "switch0.N" as ifname, where N denotes the VLAN ID.
>
> Signed-off-by: Jo-Philipp Wich <jo at mein.io>


very nice, now we need to move this to netifd :-)

     John

> ---
>   package/network/config/dsaconfig/Makefile     |  40 +++
>   .../config/dsaconfig/files/dsaconfig.hotplug  |   7 +
>   .../config/dsaconfig/files/dsaconfig.include  |  11 +
>   .../config/dsaconfig/files/dsaconfig.sh       | 296 ++++++++++++++++++
>   4 files changed, 354 insertions(+)
>   create mode 100644 package/network/config/dsaconfig/Makefile
>   create mode 100644 package/network/config/dsaconfig/files/dsaconfig.hotplug
>   create mode 100755 package/network/config/dsaconfig/files/dsaconfig.include
>   create mode 100755 package/network/config/dsaconfig/files/dsaconfig.sh
>
> diff --git a/package/network/config/dsaconfig/Makefile b/package/network/config/dsaconfig/Makefile
> new file mode 100644
> index 0000000000..4069022224
> --- /dev/null
> +++ b/package/network/config/dsaconfig/Makefile
> @@ -0,0 +1,40 @@
> +include $(TOPDIR)/rules.mk
> +
> +PKG_NAME:=dsaconfig
> +PKG_RELEASE:=1
> +PKG_LICENSE:=GPL-2.0
> +
> +include $(INCLUDE_DIR)/package.mk
> +
> +define Package/dsaconfig
> +  SECTION:=net
> +  CATEGORY:=Network
> +  MAINTAINER:=Jo-Philipp Wich <jo at mein.io>
> +  TITLE:=UCI configuration support for DSA switch VLAN filtering
> +  DEPENDS:= +ip-bridge +ip-full
> +  PKGARCH:=all
> +endef
> +
> +define Package/dsaconfig/description
> + This package provides UCI configuration abstraction for VLAN filter rules
> + on top of DSA switches.
> +endef
> +
> +define Build/Compile
> +endef
> +
> +define Build/Configure
> +endef
> +
> +define Package/dsaconfig/install
> +	$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
> +	$(INSTALL_DATA) ./files/dsaconfig.hotplug $(1)/etc/hotplug.d/iface/01-dsaconfig
> +
> +	$(INSTALL_DIR) $(1)/lib/network
> +	$(INSTALL_DATA) ./files/dsaconfig.include $(1)/lib/network/dsaconfig.sh
> +
> +	$(INSTALL_DIR) $(1)/sbin
> +	$(INSTALL_BIN) ./files/dsaconfig.sh $(1)/sbin/dsaconfig
> +endef
> +
> +$(eval $(call BuildPackage,dsaconfig))
> diff --git a/package/network/config/dsaconfig/files/dsaconfig.hotplug b/package/network/config/dsaconfig/files/dsaconfig.hotplug
> new file mode 100644
> index 0000000000..fc2a489dea
> --- /dev/null
> +++ b/package/network/config/dsaconfig/files/dsaconfig.hotplug
> @@ -0,0 +1,7 @@
> +#!/bin/sh
> +
> +if [ "$ACTION" = ifup ] && [ "$INTERFACE" = loopback ]; then
> +	. /lib/network/dsaconfig.sh
> +	setup_switch
> +fi
> +
> diff --git a/package/network/config/dsaconfig/files/dsaconfig.include b/package/network/config/dsaconfig/files/dsaconfig.include
> new file mode 100755
> index 0000000000..4ac11d8061
> --- /dev/null
> +++ b/package/network/config/dsaconfig/files/dsaconfig.include
> @@ -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
> +}
> diff --git a/package/network/config/dsaconfig/files/dsaconfig.sh b/package/network/config/dsaconfig/files/dsaconfig.sh
> new file mode 100755
> index 0000000000..0182f340ba
> --- /dev/null
> +++ b/package/network/config/dsaconfig/files/dsaconfig.sh
> @@ -0,0 +1,296 @@
> +#!/bin/sh
> +
> +. /lib/functions.sh
> +. /lib/functions/network.sh
> +
> +switch_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
> +}
> +
> +lookup_switch() {
> +	local cfg=$1
> +	local swname
> +
> +	config_get swname "$cfg" switch
> +
> +	# Auto-determine switch if not specified ...
> +	if [ -z "$swname" ]; then
> +		case "$switch_names" in
> +			*\ *)
> +				warn "VLAN section '$cfg' does not specify a switch but multiple switches present, using first one"
> +				swname=${switch_names%% *}
> +			;;
> +			*)
> +				swname=${switch_names}
> +			;;
> +		esac
> +
> +	# ... otherwise check if the referenced switch is declared
> +	else
> +		case " $switch_names " in
> +			*" $swname "*) : ;;
> +			*)
> +				warn "Switch '$swname' specified by VLAN section '$cfg' does not exist"
> +				return 1
> +			;;
> +		esac
> +	fi
> +
> +	export -n "switch=$swname"
> +}
> +
> +validate_vid() {
> +	local vid=$1
> +
> +	case "$vid" in
> +		[0-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_switch() {
> +	local cfg=$1
> +	local cpu
> +
> +	# Read configured CPU port from uci ...
> +	config_get cpu "$cfg" cpu_port
> +
> +	# ... if unspecified, find first CPU port
> +	if [ -z "$cpu" ]; then
> +		local e
> +		for e in /sys/class/net/*/dsa; do
> +			if [ -d "$e" ]; then
> +				cpu=${e%/dsa}
> +				cpu=${cpu##*/}
> +				break
> +			fi
> +		done
> +	fi
> +
> +	# Bail out if we cannot determine the CPU port
> +	if [ -z "$cpu" ]; then
> +		warn "Unable to determine CPU port for switch '$cfg'"
> +		return 1
> +	fi
> +
> +	append switch_names "$cfg"
> +
> +	# Prevent netifd from picking up our switch bridge just yet
> +	network_defer_device "$cfg"
> +
> +	# Increase MTU of CPU port to 1508 to accomodate for VID + DSA tag
> +	ip link set "$cpu" mtu 1508
> +	ip link set "$cpu" up
> +
> +	# (Re)create switch bridge device in case it is not yet set up
> +	local filtering=$(cat "/sys/class/net/$cfg/bridge/vlan_filtering" 2>/dev/null)
> +	if [ ${filtering:-0} != 1 ]; then
> +		ip link set "$cfg" down 2>/dev/null
> +		ip link delete dev "$cfg" 2>/dev/null
> +		ip link add name "$cfg" type bridge
> +		echo 1 > "/sys/class/net/$cfg/bridge/vlan_filtering"
> +	fi
> +
> +	ip link set "$cfg" 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 "$cfg" self
> +}
> +
> +setup_switch_vlan() {
> +	local cfg=$1
> +	local switch vlan ports
> +
> +	config_get switch "$cfg" switch
> +	config_get vlan "$cfg" vlan
> +	config_get ports "$cfg" ports
> +
> +	lookup_switch "$cfg" || return 1
> +	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=${port#*.}
> +		port=${port%.*}
> +		pvid=
> +
> +		if [ "$tag" != "$port" ] && [ "$tag" = t ]; then
> +			tag=tagged
> +		else
> +			tag=untagged
> +		fi
> +
> +		# Add the port to the switch bridge and delete the default
> +		# VLAN 1 if it is not yet joined to the switch.
> +		if [ ! -e "/sys/class/net/$port/master" ]; then
> +			ip link set dev "$port" up
> +			ip link set dev "$port" master "$switch"
> +
> +			# 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 "$switch" self vid $vlan tagged
> +}
> +
> +setup_switch_port() {
> +	local cfg=$1
> +	local switch port pvid tag
> +
> +	config_get port "$cfg" port
> +	config_get pvid "$cfg" pvid
> +
> +	lookup_switch "$cfg" || return 1
> +	validate_vid "$pvid" || {
> +		warn "Port section '$cfg' specifies an invalid PVID '$pvid'"
> +		return 1
> +	}
> +
> +	# Disallow setting the PVID of the switch bridge itself
> +	[ "$port" != "$switch" ] || {
> +		warn "Port section '$cfg' must not change PVID of the switch bridge"
> +		return 1
> +	}
> +
> +	# Determine existing VLAN config
> +	local vlanspec=$(bridge vlan show dev "$port" vid "$pvid" 2>/dev/null | sed -ne2p)
> +	echo "$vlanspec" | grep -qi untagged && tag=untagged || tag=tagged
> +
> +	bridge vlan add vid "$pvid" dev "$port" pvid $tag
> +}
> +
> +apply_config() {
> +	config_load network
> +	config_foreach setup_switch switch
> +
> +	# If no switch is explicitely declared, synthesize switch0
> +	if [ -z "$switch_names" ] && ! setup_switch switch0; then
> +		warn "No DSA switches found"
> +		return 1
> +	fi
> +
> +	config_foreach setup_switch_vlan switch_vlan
> +	config_foreach setup_switch_port switch_port
> +
> +	# Ready switch bridge devices
> +	local switch
> +	for switch in $switch_names; do
> +		network_ready_device "$switch"
> +	done
> +}
> +
> +show_switch() {
> +	local switch=$1
> +
> +	printf "Switch: %s\n" "$switch"
> +	printf "VLAN/"
> +
> +	local port ports
> +	for port in "/sys/class/net/$switch/lower_"*; do
> +		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 "$switch" | 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="t" word
> +			for word in $(bridge vlan show dev "$port" vid "$vlan"); do
> +				case "$word" in
> +					PVID) pvid="*" ;;
> +					Untagged) utag="u" ;;
> +				esac
> +			done
> +
> +			printf " |  %-2s  " "$utag$pvid"
> +		done
> +	done
> +
> +	printf " |\n\n"
> +}
> +
> +show_config() {
> +	config_load network
> +	config_foreach show_switch switch
> +}
> +
> +
> +case "$1" in
> +	show) show_config ;;
> +	apply) apply_config ;;
> +	*)
> +		echo "Usage: ${0##*/} show"
> +		echo "       ${0##*/} apply"
> +		exit 1
> +	;;
> +esac



More information about the openwrt-devel mailing list