Skip to content

Instantly share code, notes, and snippets.

@jow-
Last active October 22, 2023 12:58
Show Gist options
  • Save jow-/ae0f51a27f20a14e4b9e8416956f60bb to your computer and use it in GitHub Desktop.
Save jow-/ae0f51a27f20a14e4b9e8416956f60bb to your computer and use it in GitHub Desktop.
UCI configuration support for DSA VLAN filtering
#!/bin/sh
# This is /lib/network/dsaconfig.sh
. /lib/functions/network.sh
switch_names=""
clear_port_vlans() {
local port=$1
local self=$2
local word
for word in $(bridge vlan show dev "$port"); do
case "$word" in
[0-9]*) bridge vlan del vid "$word" dev "$port" $self ;;
esac
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
*\ *)
logger -t dsaconfig "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 "*) : ;;
*)
logger -t dsaconfig "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_switchdev() {
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
logger -t dsaconfig "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
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" || {
logger -t dsaconfig "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" || {
logger -t dsaconfig "Port section '$cfg' specifies an invalid PVID '$pvid'"
return 1
}
# Disallow setting the PVID of the switch bridge itself
[ "$port" != "$switch" ] || {
logger -t dsaconfig "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
}
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
config_load network
config_foreach setup_switchdev switch
# If no switch is explicitely declared, synthesize switch0
if [ -z "$switch_names" ] && ! setup_switchdev switch0; then
logger -t dsaconfig "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
}
#!/bin/sh
# This is /etc/hotplug.d/iface/01-dsaconfig
if [ "$ACTION" = ifup ] && [ "$INTERFACE" = loopback ]; then
. /lib/network/dsaconfig.sh
setup_switch
fi
# This is /etc/config/network
config interface 'loopback'
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config globals 'globals'
option ula_prefix 'fd5d:ea15:562d::/48'
config device 'wan_wan_dev'
option name 'wan'
option macaddr '32:23:03:dc:13:a8'
config interface 'wwan'
option proto 'dhcp'
config switch switch0
## Override CPU port. Normally it is auto-discovered.
#option cpu_port eth0
config switch_vlan
## Specify the switch this vlan belongs to.
## If there is only one switch on the system, it may be omitted.
#option device switch0
option vlan 1
option ports 'lan1 lan2.t'
config switch_vlan
#option device switch0
option vlan 2
option ports 'wan'
config switch_vlan
#option device switch0
option vlan 5
option ports 'lan2.t lan3'
config switch_vlan
#option device switch0
option vlan 8
option ports 'lan2.t lan4'
config switch_vlan
#option device switch0
option vlan 11
option ports 'lan2.t lan4.t'
config switch_port
#option device switch0
option port lan1
## By default, the port PVID is set to the ID of the first
## VLAN the port is member of. It can be overriden here.
option pvid 8
config interface lan
option ifname switch0.1
option proto static
option ipaddr 192.168.1.1/24
config interface wan
option ifname switch0.2
option proto dhcp
config interface vlan5
option ifname switch0.5
option proto static
option ipaddr 10.255.5.1/24
config interface vlan8
option ifname switch0.8
option proto static
option ipaddr 10.255.8.1/24
config interface vlan11
option ifname switch0.11
option proto static
option ipaddr 10.255.11.1/24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment