diff options
-rw-r--r-- | package/network/config/vfconfig/Makefile | 40 | ||||
-rw-r--r-- | package/network/config/vfconfig/files/vfconfig.hotplug | 7 | ||||
-rwxr-xr-x | package/network/config/vfconfig/files/vfconfig.include | 11 | ||||
-rwxr-xr-x | package/network/config/vfconfig/files/vfconfig.sh | 234 |
4 files changed, 292 insertions, 0 deletions
diff --git a/package/network/config/vfconfig/Makefile b/package/network/config/vfconfig/Makefile new file mode 100644 index 0000000000..1fb62d728f --- /dev/null +++ b/package/network/config/vfconfig/Makefile @@ -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)) diff --git a/package/network/config/vfconfig/files/vfconfig.hotplug b/package/network/config/vfconfig/files/vfconfig.hotplug new file mode 100644 index 0000000000..fc2a489dea --- /dev/null +++ b/package/network/config/vfconfig/files/vfconfig.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/vfconfig/files/vfconfig.include b/package/network/config/vfconfig/files/vfconfig.include new file mode 100755 index 0000000000..4ac11d8061 --- /dev/null +++ b/package/network/config/vfconfig/files/vfconfig.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/vfconfig/files/vfconfig.sh b/package/network/config/vfconfig/files/vfconfig.sh new file mode 100755 index 0000000000..5a73c62b27 --- /dev/null +++ b/package/network/config/vfconfig/files/vfconfig.sh @@ -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 |