mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2026-03-14 21:10:11 +01:00
bridgemngr: add support for bridge config in bridging UCI
This commit is contained in:
parent
1129a3b1f1
commit
c2de70ada2
3 changed files with 1050 additions and 22 deletions
|
|
@ -68,6 +68,7 @@ endif
|
|||
define Package/bridgemngr/install
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
|
||||
ifeq ($(CONFIG_BRIDGEMNGR_USE_DM_FRAMEWORK),y)
|
||||
$(call Build/Install/DM,$(PKG_BUILD_DIR)/dmf,$(PKG_BUILD_DIR)/dmf,$(1),bridgemngr)
|
||||
|
|
@ -82,6 +83,7 @@ endif
|
|||
|
||||
$(INSTALL_BIN) ./files/etc/init.d/bridging $(1)/etc/init.d/
|
||||
$(INSTALL_DATA) ./files/etc/config/bridging $(1)/etc/config/
|
||||
$(INSTALL_DATA) ./files/etc/uci-defaults/65-update_bridging $(1)/etc/uci-defaults/
|
||||
endef
|
||||
|
||||
ifeq ($(LOCAL_DEV),1)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,169 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
|
||||
# Start after bdmf shell, wanconf, and switch-script but before the network-script
|
||||
START=20
|
||||
START=19
|
||||
STOP=10
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
BRIDGING_CONFIG="/etc/config/bridging"
|
||||
NETWORK_CONFIG="/etc/config/network"
|
||||
|
||||
# TODO:
|
||||
# Currently all the bridges are wiped out and written in network UCI
|
||||
# We might want to optimize it and only write the delta in the network UCI
|
||||
|
||||
# TODO:
|
||||
# propagate vlanfiltering option from bridging to network uci, in case backend is legacy
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
has_interface_for_device() {
|
||||
local dev="$1"
|
||||
local ifsec
|
||||
|
||||
for ifsec in $(uci -q show network | awk -F'[.=]' '/^network\..*=interface/ {print $2}'); do
|
||||
[ "$(uci -q get network.$ifsec.device 2>/dev/null)" = "$dev" ] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Return 0 if a section name looks like a preserved ethernet base port
|
||||
# (LAN1, LAN2, ..., LANn, WAN — case-insensitive)
|
||||
is_preserved_section() {
|
||||
local name="$1"
|
||||
echo "$name" | grep -qiE '^(lan[0-9]+|wan)$'
|
||||
}
|
||||
|
||||
# Remove all device and interface sections from /etc/config/network that were
|
||||
# created by a previous run of this script, while preserving base-port sections.
|
||||
cleanup_network() {
|
||||
config_load network
|
||||
|
||||
# First, find all bridge devices and delete interfaces that use them
|
||||
config_foreach _cleanup_bridge_interfaces device
|
||||
|
||||
# Then delete all VLAN devices (8021ad/8021q) that were created by this script
|
||||
config_foreach _cleanup_vlan_devices device
|
||||
|
||||
# Also remove any anonymous bridge-vlan sections left from a prior run
|
||||
local idx=0
|
||||
while uci -q get "network.@bridge-vlan[$idx]" >/dev/null 2>&1; do
|
||||
uci -q delete "network.@bridge-vlan[$idx]"
|
||||
done
|
||||
}
|
||||
|
||||
# Delete interfaces that reference a bridge device
|
||||
_cleanup_bridge_interfaces() {
|
||||
local cfg="$1"
|
||||
local type devname
|
||||
|
||||
type=$(uci -q get "network.$cfg.type" 2>/dev/null)
|
||||
devname=$(uci -q get "network.$cfg.name" 2>/dev/null)
|
||||
|
||||
[ "$type" = "bridge" ] || return
|
||||
|
||||
# Find and delete all interfaces that use this bridge
|
||||
config_foreach _check_delete_interface_for_bridge interface "$devname"
|
||||
|
||||
# Delete the bridge device section itself
|
||||
uci -q delete "network.$cfg"
|
||||
}
|
||||
|
||||
# Check if an interface uses a specific bridge, delete if it does
|
||||
_check_delete_interface_for_bridge() {
|
||||
local cfg="$1"
|
||||
local bridge_name="$2"
|
||||
local ifdevice managed_by_bridging
|
||||
|
||||
managed_by_bridging=$(uci -q get "network.$cfg.managed_by_bridging" 2>/dev/null)
|
||||
[ "$managed_by_bridging" = "1" ] || return
|
||||
|
||||
ifdevice=$(uci -q get "network.$cfg.device" 2>/dev/null)
|
||||
|
||||
# Delete if device matches the bridge or is a sub-interface of it
|
||||
if [ "$ifdevice" = "$bridge_name" ] || echo "$ifdevice" | grep -q "^${bridge_name}\."; then
|
||||
uci -q delete "network.$cfg"
|
||||
fi
|
||||
}
|
||||
|
||||
# Delete VLAN devices (8021ad/8021q) created by this script
|
||||
_cleanup_vlan_devices() {
|
||||
local cfg="$1"
|
||||
local type managed
|
||||
|
||||
type=$(uci -q get "network.$cfg.type" 2>/dev/null)
|
||||
managed=$(uci -q get "network.$cfg.managed_by_bridging" 2>/dev/null)
|
||||
|
||||
if [ "$managed" = "1" ] && { [ "$type" = "8021ad" ] || [ "$type" = "8021q" ]; }; then
|
||||
uci -q delete "network.$cfg"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Determine 8021q / 8021ad type from tpid value (decimal or hex string)
|
||||
# Sets global _vlan_type
|
||||
# ---------------------------------------------------------------------------
|
||||
vlan_type_from_tpid() {
|
||||
local tpid="$1"
|
||||
case "$tpid" in
|
||||
88a8|0x88a8|34984) _vlan_type="8021ad" ;;
|
||||
8100|0x8100|33024) _vlan_type="8021q" ;;
|
||||
*) _vlan_type="8021q" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ensure an anonymous VLAN device section exists; create if absent.
|
||||
# Usage: ensure_vlan_device <name> <type> <vid> <ifname>
|
||||
# Sets _ensured_devname to the section name used (same as <name>).
|
||||
# ---------------------------------------------------------------------------
|
||||
ensure_vlan_device() {
|
||||
local devname="$1" devtype="$2" vid="$3" ifname="$4"
|
||||
|
||||
# Check if already added in this run (track by interface name)
|
||||
echo "$_created_devices" | grep -qF "|${devname}|" && return
|
||||
|
||||
uci -q add network device
|
||||
uci -q set "network.@device[-1].name=${devname}"
|
||||
uci -q set "network.@device[-1].type=${devtype}"
|
||||
uci -q set "network.@device[-1].vid=${vid}"
|
||||
uci -q set "network.@device[-1].ifname=${ifname}"
|
||||
uci -q set "network.@device[-1].enabled=1"
|
||||
uci -q set "network.@device[-1].managed_by_bridging=1"
|
||||
|
||||
_created_devices="${_created_devices}|${devname}|"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Iface counter — generates iface1, iface2, ...
|
||||
# ---------------------------------------------------------------------------
|
||||
_iface_counter=0
|
||||
next_iface_name() {
|
||||
_iface_counter=$(( _iface_counter + 1 ))
|
||||
_iface_name="iface${_iface_counter}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Add a port to a space-separated list, avoiding duplicates.
|
||||
# Usage: _list_add <varname> <value>
|
||||
# ---------------------------------------------------------------------------
|
||||
_list_add() {
|
||||
local _varname="$1"
|
||||
local _val="$2"
|
||||
eval "local _cur=\"\${${_varname}}\""
|
||||
echo " ${_cur} " | grep -qF " ${_val} " && return
|
||||
eval "${_varname}=\"\${_cur} \${_val}\""
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
# ebtables helpers (unchanged)
|
||||
# ===========================================================================
|
||||
|
||||
handle_ebtables_chain() {
|
||||
local sid="$1"
|
||||
local table
|
||||
local chain
|
||||
local target
|
||||
local policy
|
||||
local append
|
||||
local enabled
|
||||
local ret
|
||||
local table chain target policy append enabled ret
|
||||
|
||||
config_get table "$sid" table filter
|
||||
config_get chain "$sid" chain
|
||||
|
|
@ -34,7 +181,7 @@ handle_ebtables_chain() {
|
|||
append="-I"
|
||||
fi
|
||||
|
||||
ebtables --concurrent -t "$table" -N "$target" -P "$policy" 2> /dev/null
|
||||
ebtables --concurrent -t "$table" -N "$target" -P "$policy" 2>/dev/null
|
||||
ret=$?
|
||||
|
||||
if [ $ret -eq 0 ]; then
|
||||
|
|
@ -47,13 +194,7 @@ handle_ebtables_chain() {
|
|||
|
||||
handle_ebtables_rule() {
|
||||
local sid="$1"
|
||||
local table
|
||||
local chain
|
||||
local target
|
||||
local match
|
||||
local value
|
||||
local enabled
|
||||
local ret
|
||||
local table chain target match value append enabled
|
||||
|
||||
config_get table "$sid" table filter
|
||||
config_get chain "$sid" chain
|
||||
|
|
@ -72,22 +213,419 @@ handle_ebtables_rule() {
|
|||
append="-I"
|
||||
fi
|
||||
|
||||
ebtables --concurrent -t "$table" -D "$chain" ${match} -j "$target" ${value} 2> /dev/null
|
||||
ebtables --concurrent -t "$table" -D "$chain" ${match} -j "$target" ${value} 2>/dev/null
|
||||
ebtables --concurrent -t "$table" ${append} "$chain" ${match} -j "$target" ${value}
|
||||
}
|
||||
|
||||
start_service() {
|
||||
# ===========================================================================
|
||||
# bridge-vlan backend
|
||||
# ===========================================================================
|
||||
|
||||
# Per-bridge globals (reset per bridge):
|
||||
# _bv_bridge_ports — ports list for the bridge device section
|
||||
# _bv_count — number of bridge-vlan sections found
|
||||
|
||||
convert_bridge_vlan() {
|
||||
local bridge_section="$1"
|
||||
local bridge_name
|
||||
local bridge_enabled bridge_stp bridge_priority bridge_hello_time bridge_max_age bridge_forward_delay
|
||||
|
||||
config_get_bool bridge_enabled "$bridge_section" enabled 1
|
||||
config_get bridge_name "$bridge_section" name
|
||||
config_get bridge_stp "$bridge_section" stp
|
||||
config_get bridge_priority "$bridge_section" priority
|
||||
config_get bridge_hello_time "$bridge_section" hello_time
|
||||
config_get bridge_max_age "$bridge_section" max_age
|
||||
config_get bridge_forward_delay "$bridge_section" forward_delay
|
||||
|
||||
_bv_bridge_ports=""
|
||||
_bv_count=0
|
||||
|
||||
# Collect all bridge-ports for this bridge (the logical member ports)
|
||||
config_foreach _collect_bridge_port bridge-port "$bridge_section"
|
||||
|
||||
# Walk bridge-vlan sections; for each, walk their vlan-ports to build
|
||||
# network bridge-vlan sections and any required intermediate devices.
|
||||
config_foreach _bv_handle_bridge_vlan bridge-vlan "$bridge_section" "$bridge_name"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Create the bridge device section
|
||||
# ------------------------------------------------------------------
|
||||
uci -q set "network.${bridge_section}=device"
|
||||
uci -q set "network.${bridge_section}.name=${bridge_name}"
|
||||
uci -q set "network.${bridge_section}.type=bridge"
|
||||
uci -q set "network.${bridge_section}.bridge_empty=1"
|
||||
uci -q set "network.${bridge_section}.enabled=${bridge_enabled}"
|
||||
[ -n "$bridge_stp" ] && uci -q set "network.${bridge_section}.stp=${bridge_stp}"
|
||||
[ -n "$bridge_priority" ] && uci -q set "network.${bridge_section}.priority=${bridge_priority}"
|
||||
[ -n "$bridge_hello_time" ] && uci -q set "network.${bridge_section}.hello_time=${bridge_hello_time}"
|
||||
[ -n "$bridge_max_age" ] && uci -q set "network.${bridge_section}.max_age=${bridge_max_age}"
|
||||
[ -n "$bridge_forward_delay" ] && uci -q set "network.${bridge_section}.forward_delay=${bridge_forward_delay}"
|
||||
uci -q set "network.${bridge_section}.managed_by_bridging=1"
|
||||
|
||||
for p in $_bv_bridge_ports; do
|
||||
uci -q add_list "network.${bridge_section}.ports=${p}"
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Interface management
|
||||
# ------------------------------------------------------------------
|
||||
if [ "$_bv_count" -eq 0 ]; then
|
||||
# No VLANs — create a plain interface for the bridge
|
||||
if ! has_interface_for_device "${bridge_name}"; then
|
||||
next_iface_name
|
||||
uci -q set "network.${_iface_name}=interface"
|
||||
uci -q set "network.${_iface_name}.proto=none"
|
||||
uci -q set "network.${_iface_name}.disabled=0"
|
||||
uci -q set "network.${_iface_name}.device=${bridge_name}"
|
||||
uci -q set "network.${_iface_name}.managed_by_bridging=1"
|
||||
fi
|
||||
else
|
||||
# VLANs present — remove any stale plain interface for the bridge itself
|
||||
local ifsec
|
||||
for ifsec in $(uci -q show network | awk -F'[.=]' '/^network\..*=interface/ {print $2}'); do
|
||||
local ifdevice managed_by_bridging
|
||||
ifdevice=$(uci -q get "network.$ifsec.device" 2>/dev/null)
|
||||
managed_by_bridging=$(uci -q get "network.$ifsec.managed_by_bridging" 2>/dev/null)
|
||||
[ "$ifdevice" = "$bridge_name" ] && [ "$managed_by_bridging" = "1" ] && \
|
||||
uci -q delete "network.$ifsec"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Collect enabled bridge-port members into _bv_bridge_ports
|
||||
_collect_bridge_port() {
|
||||
local cfg="$1"
|
||||
local want_bridge="$2"
|
||||
|
||||
local bsec portname enabled management
|
||||
config_get bsec "$cfg" bridge
|
||||
config_get portname "$cfg" name
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
config_get_bool management "$cfg" management 0
|
||||
|
||||
[ "$enabled" = "1" ] || return
|
||||
[ "$management" = "0" ] || return
|
||||
[ "$bsec" = "$want_bridge" ] || return
|
||||
|
||||
_list_add _bv_bridge_ports "$portname"
|
||||
}
|
||||
|
||||
# Handle one bridge-vlan section (bridge-vlan backend)
|
||||
_bv_handle_bridge_vlan() {
|
||||
local cfg="$1"
|
||||
local want_bridge="$2"
|
||||
local bridge_name="$3"
|
||||
|
||||
local bsec vlan enabled
|
||||
config_get bsec "$cfg" bridge
|
||||
config_get vlan "$cfg" vlan
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
|
||||
[ "$bsec" = "$want_bridge" ] || return
|
||||
[ "$enabled" = "1" ] || return
|
||||
|
||||
_bv_count=$(( _bv_count + 1 ))
|
||||
|
||||
# Create the network bridge-vlan section
|
||||
uci -q add network bridge-vlan
|
||||
uci -q set "network.@bridge-vlan[-1].device=${bridge_name}"
|
||||
uci -q set "network.@bridge-vlan[-1].vlan=${vlan}"
|
||||
|
||||
# Walk vlan-port sections that belong to this bridge-vlan
|
||||
config_foreach _bv_handle_vlan_port vlan-port "$want_bridge" "$cfg" "$bridge_name" "$vlan"
|
||||
|
||||
# Create an interface for the VLAN sub-interface of the bridge
|
||||
if ! has_interface_for_device "${bridge_name}.${vlan}"; then
|
||||
next_iface_name
|
||||
uci -q set "network.${_iface_name}=interface"
|
||||
uci -q set "network.${_iface_name}.proto=none"
|
||||
uci -q set "network.${_iface_name}.disabled=0"
|
||||
uci -q set "network.${_iface_name}.device=${bridge_name}.${vlan}"
|
||||
uci -q set "network.${_iface_name}.managed_by_bridging=1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Handle one vlan-port section for the bridge-vlan backend.
|
||||
#
|
||||
# Bridging options:
|
||||
# port — base interface name (e.g. eth0, ae_wan)
|
||||
# vlan — reference to a bridge-vlan section name
|
||||
# untagged — 0=tagged, 1=untagged
|
||||
# pvid — 1=this is the PVID for the port (adds * in annotation)
|
||||
# tpid — outer tag protocol (decimal or hex); present → svid stacking
|
||||
# svid — outer VLAN id for stacking
|
||||
#
|
||||
# Scenarios:
|
||||
# A) untagged=0, no svid → port:t
|
||||
# B) untagged=1, no svid → port:u[*]
|
||||
# C) untagged=0, svid set → create s-vlan dev; sdev:t
|
||||
# D) untagged=1, pvid=1, svid set,
|
||||
# tpid matches vlan (svid==vlan) → create s-vlan dev; sdev:u*
|
||||
# E) untagged=1, pvid=1, svid set,
|
||||
# tpid != vlan (svid!=vlan) → create s-vlan dev; sdev:u*
|
||||
# (covers scenarios 1 and 3 from the spec)
|
||||
# F) untagged=0, pvid=1, svid set → create s-vlan dev; sdev:t
|
||||
# (scenario 4 — double tag, c-vlan still goes in as tagged)
|
||||
#
|
||||
# Also adds both the raw port AND the s-vlan device to _bv_bridge_ports
|
||||
# when an s-vlan device is created.
|
||||
_bv_handle_vlan_port() {
|
||||
local cfg="$1"
|
||||
local want_bridge="$2"
|
||||
local want_bv_cfg="$3" # bridge-vlan section name (e.g. bv1)
|
||||
local bridge_name="$4"
|
||||
local vlan="$5"
|
||||
|
||||
local bsec bvcfg portname untagged pvid tpid svid enabled
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
config_get bsec "$cfg" bridge
|
||||
config_get bvcfg "$cfg" vlan
|
||||
config_get portname "$cfg" port
|
||||
config_get_bool untagged "$cfg" untagged 0
|
||||
config_get_bool pvid "$cfg" pvid 0
|
||||
config_get tpid "$cfg" tpid ""
|
||||
config_get svid "$cfg" svid ""
|
||||
|
||||
[ "$enabled" = "1" ] || return
|
||||
[ "$bsec" = "$want_bridge" ] || return
|
||||
[ "$bvcfg" = "$want_bv_cfg" ] || return
|
||||
|
||||
if [ -n "$svid" ] && [ -n "$tpid" ]; then
|
||||
# There is an outer tag — create an intermediate device
|
||||
vlan_type_from_tpid "$tpid"
|
||||
local outer_devname="${portname}.${svid}"
|
||||
|
||||
ensure_vlan_device "$outer_devname" "$_vlan_type" "$svid" "$portname"
|
||||
|
||||
# The raw port must also be in the bridge's ports list
|
||||
_list_add _bv_bridge_ports "$portname"
|
||||
# And the s-vlan device itself
|
||||
_list_add _bv_bridge_ports "$outer_devname"
|
||||
|
||||
# Annotation for the bridge-vlan ports entry
|
||||
local annotation
|
||||
if [ "$untagged" = "1" ] && [ "$pvid" = "1" ]; then
|
||||
annotation="u*"
|
||||
elif [ "$untagged" = "1" ]; then
|
||||
annotation="u"
|
||||
else
|
||||
annotation="t"
|
||||
fi
|
||||
|
||||
uci -q add_list "network.@bridge-vlan[-1].ports=${outer_devname}:${annotation}"
|
||||
|
||||
else
|
||||
# No outer tag — plain port
|
||||
# pvid only makes sense with untagged; pvid adds '*' suffix
|
||||
local annotation
|
||||
if [ "$untagged" = "1" ] && [ "$pvid" = "1" ]; then
|
||||
annotation="u*"
|
||||
elif [ "$untagged" = "1" ]; then
|
||||
annotation="u"
|
||||
else
|
||||
annotation="t"
|
||||
fi
|
||||
|
||||
uci -q add_list "network.@bridge-vlan[-1].ports=${portname}:${annotation}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
# Legacy backend
|
||||
# ===========================================================================
|
||||
|
||||
# Per-bridge globals (reset per bridge):
|
||||
# _legacy_bridge_ports — ports list for the bridge device section
|
||||
|
||||
convert_legacy() {
|
||||
local bridge_section="$1"
|
||||
local bridge_name
|
||||
local bridge_enabled bridge_stp bridge_priority bridge_hello_time bridge_max_age bridge_forward_delay
|
||||
|
||||
config_get_bool bridge_enabled "$bridge_section" enabled 1
|
||||
config_get bridge_name "$bridge_section" name
|
||||
config_get bridge_stp "$bridge_section" stp
|
||||
config_get bridge_priority "$bridge_section" priority
|
||||
config_get bridge_hello_time "$bridge_section" hello_time
|
||||
config_get bridge_max_age "$bridge_section" max_age
|
||||
config_get bridge_forward_delay "$bridge_section" forward_delay
|
||||
|
||||
logger -t bridging -p daemon.warn \
|
||||
"Legacy bridge backend: all VLANs share the same bridge '${bridge_name}'. Consider creating separate bridges per VLAN."
|
||||
|
||||
_legacy_bridge_ports=""
|
||||
|
||||
# Walk bridge-vlan sections; for each, walk their vlan-ports
|
||||
config_foreach _legacy_handle_bridge_vlan bridge-vlan "$bridge_section" "$bridge_name"
|
||||
|
||||
# Create the bridge device
|
||||
uci -q set "network.${bridge_section}=device"
|
||||
uci -q set "network.${bridge_section}.name=${bridge_name}"
|
||||
uci -q set "network.${bridge_section}.type=bridge"
|
||||
uci -q set "network.${bridge_section}.bridge_empty=1"
|
||||
uci -q set "network.${bridge_section}.enabled=${bridge_enabled}"
|
||||
[ -n "$bridge_stp" ] && uci -q set "network.${bridge_section}.stp=${bridge_stp}"
|
||||
[ -n "$bridge_priority" ] && uci -q set "network.${bridge_section}.priority=${bridge_priority}"
|
||||
[ -n "$bridge_hello_time" ] && uci -q set "network.${bridge_section}.hello_time=${bridge_hello_time}"
|
||||
[ -n "$bridge_max_age" ] && uci -q set "network.${bridge_section}.max_age=${bridge_max_age}"
|
||||
[ -n "$bridge_forward_delay" ] && uci -q set "network.${bridge_section}.forward_delay=${bridge_forward_delay}"
|
||||
uci -q set "network.${bridge_section}.managed_by_bridging=1"
|
||||
|
||||
for p in $_legacy_bridge_ports; do
|
||||
uci -q add_list "network.${bridge_section}.ports=${p}"
|
||||
done
|
||||
|
||||
# Single interface for the whole bridge
|
||||
if ! has_interface_for_device "${bridge_name}"; then
|
||||
next_iface_name
|
||||
uci -q set "network.${_iface_name}=interface"
|
||||
uci -q set "network.${_iface_name}.proto=none"
|
||||
uci -q set "network.${_iface_name}.disabled=0"
|
||||
uci -q set "network.${_iface_name}.device=${bridge_name}"
|
||||
uci -q set "network.${_iface_name}.managed_by_bridging=1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Handle one bridge-vlan section (legacy backend)
|
||||
_legacy_handle_bridge_vlan() {
|
||||
local cfg="$1"
|
||||
local want_bridge="$2"
|
||||
local bridge_name="$3"
|
||||
|
||||
local bsec vlan enabled
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
config_get bsec "$cfg" bridge
|
||||
config_get vlan "$cfg" vlan
|
||||
|
||||
[ "$enabled" = "1" ] || return
|
||||
[ "$bsec" = "$want_bridge" ] || return
|
||||
|
||||
# Walk vlan-port sections that belong to this bridge-vlan
|
||||
config_foreach _legacy_handle_vlan_port vlan-port "$want_bridge" "$cfg" "$vlan"
|
||||
}
|
||||
|
||||
# Handle one vlan-port section for the legacy backend.
|
||||
#
|
||||
# In legacy mode the bridge has no VLAN awareness — we create actual
|
||||
# VLAN sub-interface devices and add those as physical ports of the bridge.
|
||||
#
|
||||
# Decision tree for port device name added to the bridge:
|
||||
#
|
||||
# Case 1: no svid, untagged=1 → use portname directly
|
||||
# Case 2: no svid, untagged=0 → create portname.vlan (8021q), add portname.vlan
|
||||
# Case 3: svid set, untagged=1, pvid=1
|
||||
# and svid == vlan (tpid is 8021ad or 8021q matching the bridge vlan)
|
||||
# → frames leave with ONE tag (the s-vlan).
|
||||
# create outer device (portname.svid), add portname.svid
|
||||
# Case 4: svid set, untagged=1, pvid=1
|
||||
# svid != vlan
|
||||
# → frames leave with ONE tag (svid replaces the c-vlan).
|
||||
# create outer device (portname.svid), add portname.svid
|
||||
# (scenarios 1 and 3 collapse here for legacy)
|
||||
# Case 5: svid set, untagged=0
|
||||
# → frames get both outer and inner tag.
|
||||
# create outer device (portname.svid),
|
||||
# create inner device (portname.svid.vlan),
|
||||
# add portname.svid.vlan
|
||||
# (scenario 4 for legacy)
|
||||
_legacy_handle_vlan_port() {
|
||||
local cfg="$1"
|
||||
local want_bridge="$2"
|
||||
local want_bv_cfg="$3"
|
||||
local vlan="$4"
|
||||
|
||||
local bsec bvcfg portname untagged pvid tpid svid enabled
|
||||
config_get_bool enabled "$cfg" enabled 1
|
||||
config_get bsec "$cfg" bridge
|
||||
config_get bvcfg "$cfg" vlan
|
||||
config_get portname "$cfg" port
|
||||
config_get_bool untagged "$cfg" untagged 0
|
||||
config_get_bool pvid "$cfg" pvid 0
|
||||
config_get tpid "$cfg" tpid ""
|
||||
config_get svid "$cfg" svid ""
|
||||
|
||||
[ "$enabled" = "1" ] || return
|
||||
[ "$bsec" = "$want_bridge" ] || return
|
||||
[ "$bvcfg" = "$want_bv_cfg" ] || return
|
||||
|
||||
if [ -n "$svid" ] && [ -n "$tpid" ]; then
|
||||
vlan_type_from_tpid "$tpid"
|
||||
local outer_devname="${portname}.${svid}"
|
||||
|
||||
ensure_vlan_device "$outer_devname" "$_vlan_type" "$svid" "$portname"
|
||||
|
||||
if [ "$untagged" = "1" ] && [ "$pvid" = "1" ]; then
|
||||
# Frames leave with just the outer tag — bridge port is the outer device
|
||||
_list_add _legacy_bridge_ports "$outer_devname"
|
||||
else
|
||||
# untagged=0 with svid — frames get outer AND inner (c-vlan) tag
|
||||
local inner_devname="${outer_devname}.${vlan}"
|
||||
ensure_vlan_device "$inner_devname" "8021q" "$vlan" "$outer_devname"
|
||||
_list_add _legacy_bridge_ports "$inner_devname"
|
||||
fi
|
||||
|
||||
elif [ "$untagged" = "1" ]; then
|
||||
# No outer tag, untagged — use raw port
|
||||
_list_add _legacy_bridge_ports "$portname"
|
||||
|
||||
else
|
||||
# No outer tag, tagged — create a c-vlan device
|
||||
local sub_devname="${portname}.${vlan}"
|
||||
ensure_vlan_device "$sub_devname" "8021q" "$vlan" "$portname"
|
||||
_list_add _legacy_bridge_ports "$sub_devname"
|
||||
fi
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
# Top-level apply
|
||||
# ===========================================================================
|
||||
|
||||
apply_config() {
|
||||
if [ ! -f "$BRIDGING_CONFIG" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
config_load bridging
|
||||
|
||||
config_foreach handle_ebtables_chain chain
|
||||
config_foreach handle_ebtables_rule rule
|
||||
config_foreach handle_ebtables_rule rule
|
||||
|
||||
local backend
|
||||
config_get backend global bridge_backend "bridge-vlan"
|
||||
|
||||
case "$backend" in
|
||||
bridge-vlan)
|
||||
_created_devices=""
|
||||
cleanup_network
|
||||
config_load bridging
|
||||
config_foreach convert_bridge_vlan bridge
|
||||
uci -q commit network
|
||||
/etc/init.d/network reload
|
||||
;;
|
||||
legacy)
|
||||
_created_devices=""
|
||||
cleanup_network
|
||||
config_load bridging
|
||||
config_foreach convert_legacy bridge
|
||||
uci -q commit network
|
||||
/etc/init.d/network reload
|
||||
;;
|
||||
*)
|
||||
logger -t bridging -p daemon.err "Unknown bridge_backend '$backend', aborting."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
start_service() {
|
||||
apply_config
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
apply_config
|
||||
}
|
||||
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger bridging
|
||||
}
|
||||
|
|
|
|||
488
bridgemngr/files/etc/uci-defaults/65-update_bridging
Normal file
488
bridgemngr/files/etc/uci-defaults/65-update_bridging
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
#!/bin/sh
|
||||
. /lib/functions.sh
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Read network UCI bridges and sync to bridging config
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reads bridge devices from /etc/config/network and creates corresponding
|
||||
# sections in /etc/config/bridging:
|
||||
#
|
||||
# - bridge — one per bridge device
|
||||
# - bridge-port — one per physical member port of the bridge
|
||||
# - bridge-vlan — one per network bridge-vlan section (bridge-vlan backend)
|
||||
# OR inferred from device chain (legacy backend)
|
||||
# - vlan-port — one per port entry within a bridge-vlan
|
||||
#
|
||||
# Sections already written by the bridging init script (managed_by_bridging=1)
|
||||
# are skipped so we never import our own output back.
|
||||
#
|
||||
# The global config section 'global' is NOT modified; the caller is responsible
|
||||
# for setting bridge_backend before or after calling this script.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Counters for generated anonymous-style section names
|
||||
_bp_counter=0
|
||||
_bv_counter=0
|
||||
_vp_counter=0
|
||||
|
||||
_next_bp() { _bp_counter=$(( _bp_counter + 1 )); _bp_name="bp${_bp_counter}"; }
|
||||
_next_bv() { _bv_counter=$(( _bv_counter + 1 )); _bv_name="bv${_bv_counter}"; }
|
||||
_next_vp() { _vp_counter=$(( _vp_counter + 1 )); _vp_name="vp${_vp_counter}"; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Device map — built once from all non-managed, non-bridge device sections.
|
||||
#
|
||||
# Two flat string maps, keyed by device name:
|
||||
# _devmap_type — "|name=type|name=type|..."
|
||||
# _devmap_ifname — "|name=ifname|name=ifname|..."
|
||||
#
|
||||
# Lookup helpers set globals _dm_type / _dm_ifname; return 1 if not found.
|
||||
# ---------------------------------------------------------------------------
|
||||
_devmap_type=""
|
||||
_devmap_ifname=""
|
||||
|
||||
_build_device_map() {
|
||||
local sec sectype secname secifname managed
|
||||
|
||||
for sec in $(uci -q show network | awk -F'[.=]' '/^network\..*=device$/ {print $2}'); do
|
||||
managed=$(uci -q get "network.$sec.managed_by_bridging" 2>/dev/null)
|
||||
[ "$managed" = "1" ] && continue
|
||||
|
||||
sectype=$(uci -q get "network.$sec.type" 2>/dev/null)
|
||||
secname=$(uci -q get "network.$sec.name" 2>/dev/null)
|
||||
secifname=$(uci -q get "network.$sec.ifname" 2>/dev/null)
|
||||
|
||||
[ -z "$secname" ] && continue
|
||||
# Skip bridge devices — they are processed separately
|
||||
[ "$sectype" = "bridge" ] && continue
|
||||
|
||||
_devmap_type="${_devmap_type}|${secname}=${sectype}|"
|
||||
[ -n "$secifname" ] && \
|
||||
_devmap_ifname="${_devmap_ifname}|${secname}=${secifname}|"
|
||||
done
|
||||
}
|
||||
|
||||
# Look up type for a device name. Sets _dm_type; returns 1 if not found.
|
||||
_devmap_get_type() {
|
||||
local name="$1"
|
||||
_dm_type=$(printf '%s' "$_devmap_type" | \
|
||||
sed -n "s/.*|${name}=\([^|]*\)|.*/\1/p" | head -1)
|
||||
[ -n "$_dm_type" ]
|
||||
}
|
||||
|
||||
# Look up ifname (parent) for a device name. Sets _dm_ifname; returns 1 if not found.
|
||||
_devmap_get_ifname() {
|
||||
local name="$1"
|
||||
_dm_ifname=$(printf '%s' "$_devmap_ifname" | \
|
||||
sed -n "s/.*|${name}=\([^|]*\)|.*/\1/p" | head -1)
|
||||
[ -n "$_dm_ifname" ]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Given a device name that appears as a bridge port, walk the device map
|
||||
# to reconstruct the full VLAN chain back to the physical base interface.
|
||||
#
|
||||
# Sets globals (all may be empty if not applicable):
|
||||
# _chain_base — the physical / base interface name (no VLAN device entry)
|
||||
# _chain_svid — outer VLAN id (s-vlan), if present
|
||||
# _chain_tpid — outer tpid decimal (34984=8021ad, 33024=8021q), if svid set
|
||||
# _chain_vid — inner / only VLAN id, if this port has a VLAN device
|
||||
# _chain_cvtpid — inner tpid decimal, if double-stacked
|
||||
#
|
||||
# Strategy: follow ifname pointers upward until we reach a name that has
|
||||
# no entry in the device map (i.e. it is a physical interface). Collect
|
||||
# type + vid at each hop.
|
||||
# ---------------------------------------------------------------------------
|
||||
_resolve_port_chain() {
|
||||
local devname="$1"
|
||||
|
||||
_chain_base=""
|
||||
_chain_svid=""
|
||||
_chain_tpid=""
|
||||
_chain_vid=""
|
||||
_chain_cvtpid=""
|
||||
|
||||
# Collect hops: each hop is "type:vid:name" from innermost to outermost
|
||||
# We build the list by walking upward (devname → ifname → ifname → ...)
|
||||
local hops="" # space-separated, innermost first
|
||||
local cur="$devname"
|
||||
|
||||
while _devmap_get_type "$cur"; do
|
||||
local hop_type="$_dm_type"
|
||||
# Extract vid from the device section (the vid option, not name-based)
|
||||
local hop_vid
|
||||
hop_vid=$(_get_device_vid "$cur")
|
||||
hops="${hops} ${hop_type}:${hop_vid}:${cur}"
|
||||
|
||||
# Walk up to parent
|
||||
if _devmap_get_ifname "$cur"; then
|
||||
cur="$_dm_ifname"
|
||||
else
|
||||
# No ifname pointer — stop; cur is the base
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# cur is now the base interface (no device map entry or no ifname)
|
||||
_chain_base="$cur"
|
||||
|
||||
# Count hops
|
||||
local nhops=0
|
||||
local h
|
||||
for h in $hops; do nhops=$(( nhops + 1 )); done
|
||||
|
||||
if [ "$nhops" -eq 0 ]; then
|
||||
# No VLAN device — plain physical port, nothing to set
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$nhops" -eq 1 ]; then
|
||||
# Single VLAN layer
|
||||
local h1="$hops"
|
||||
_chain_vid=$( echo "$h1" | cut -d: -f2)
|
||||
local t1=$( echo "$h1" | cut -d: -f1)
|
||||
_chain_tpid=$( _tpid_from_type "$t1")
|
||||
# Reuse tpid field as the only tag's tpid; no svid
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$nhops" -ge 2 ]; then
|
||||
# Two (or more) VLAN layers — outermost hop is the s-vlan
|
||||
# hops are listed innermost-first, so last = outermost
|
||||
local outer="" inner=""
|
||||
local idx=0
|
||||
for h in $hops; do
|
||||
idx=$(( idx + 1 ))
|
||||
[ "$idx" -eq 1 ] && inner="$h"
|
||||
outer="$h" # last one wins
|
||||
done
|
||||
|
||||
_chain_svid=$( echo "$outer" | cut -d: -f2)
|
||||
local ot=$( echo "$outer" | cut -d: -f1)
|
||||
_chain_tpid=$( _tpid_from_type "$ot")
|
||||
|
||||
_chain_vid=$( echo "$inner" | cut -d: -f2)
|
||||
local it=$( echo "$inner" | cut -d: -f1)
|
||||
_chain_cvtpid=$(_tpid_from_type "$it")
|
||||
fi
|
||||
}
|
||||
|
||||
# Return decimal tpid for a device type string
|
||||
_tpid_from_type() {
|
||||
case "$1" in
|
||||
8021ad) echo "34984" ;;
|
||||
*) echo "33024" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Return the vid option of a named device section (looks up by name).
|
||||
_get_device_vid() {
|
||||
local devname="$1"
|
||||
local sec
|
||||
for sec in $(uci -q show network | awk -F'[.=]' '/^network\..*=device$/ {print $2}'); do
|
||||
local n
|
||||
n=$(uci -q get "network.$sec.name" 2>/dev/null)
|
||||
[ "$n" = "$devname" ] || continue
|
||||
uci -q get "network.$sec.vid" 2>/dev/null
|
||||
return
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main entry: read all bridge devices from network UCI
|
||||
# ---------------------------------------------------------------------------
|
||||
read_bridges_from_network_uci() {
|
||||
config_load network
|
||||
# Build device map once — used by all chain-walking helpers below
|
||||
_build_device_map
|
||||
config_foreach _process_network_bridge_device device
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Process a single device section from network config
|
||||
# ---------------------------------------------------------------------------
|
||||
_process_network_bridge_device() {
|
||||
local cfg="$1"
|
||||
local devtype devname managed
|
||||
|
||||
devtype=$(uci -q get "network.$cfg.type" 2>/dev/null)
|
||||
devname=$(uci -q get "network.$cfg.name" 2>/dev/null)
|
||||
managed=$(uci -q get "network.$cfg.managed_by_bridging" 2>/dev/null)
|
||||
|
||||
# Skip sections we wrote ourselves
|
||||
[ "$managed" = "1" ] && return
|
||||
# Only process bridge devices
|
||||
[ "$devtype" = "bridge" ] || return
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Create the bridge section in bridging config
|
||||
# ------------------------------------------------------------------
|
||||
uci -q set "bridging.${cfg}=bridge"
|
||||
uci -q set "bridging.${cfg}.name=${devname}"
|
||||
uci -q set "bridging.${cfg}.from_network_uci=1"
|
||||
uci -q set "bridging.${cfg}.enabled=1"
|
||||
|
||||
# add management port section
|
||||
_next_bp
|
||||
uci -q set "bridging.${_bp_name}=bridge-port"
|
||||
uci -q set "bridging.${_bp_name}.bridge=${cfg}"
|
||||
uci -q set "bridging.${_bp_name}.name=${devname}"
|
||||
uci -q set "bridging.${_bp_name}.management=1"
|
||||
uci -q set "bridging.${_bp_name}.from_network_uci=1"
|
||||
|
||||
# Optional bridge parameters
|
||||
local v
|
||||
for opt in stp priority hello_time max_age forward_delay; do
|
||||
v=$(uci -q get "network.$cfg.$opt" 2>/dev/null)
|
||||
[ -n "$v" ] && uci -q set "bridging.${cfg}.${opt}=${v}"
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Determine which backend to use for this bridge.
|
||||
# Prefer the global bridging backend if already set; otherwise probe
|
||||
# for the presence of network bridge-vlan sections that reference
|
||||
# this bridge device — if found, treat as bridge-vlan backend.
|
||||
# ------------------------------------------------------------------
|
||||
local backend
|
||||
backend=$(uci -q get "bridging.global.bridge_backend" 2>/dev/null)
|
||||
if [ -z "$backend" ]; then
|
||||
# Auto-detect: look for any network bridge-vlan section for this device
|
||||
if uci -q show network | grep -q "\.device='${devname}'"; then
|
||||
backend="bridge-vlan"
|
||||
else
|
||||
backend="legacy"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Read member ports
|
||||
# ------------------------------------------------------------------
|
||||
local raw_ports
|
||||
raw_ports=$(uci -q get "network.$cfg.ports" 2>/dev/null)
|
||||
|
||||
if [ "$backend" = "bridge-vlan" ]; then
|
||||
_import_bvbackend "$cfg" "$devname" "$raw_ports"
|
||||
else
|
||||
_import_legacy "$cfg" "$devname" "$raw_ports"
|
||||
fi
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
# bridge-vlan backend import
|
||||
# ===========================================================================
|
||||
# In bridge-vlan mode:
|
||||
# - Each entry in the bridge's ports list becomes a bridge-port section.
|
||||
# If the entry is a VLAN sub-interface (e.g. ae_wan.300) we also create
|
||||
# the intermediate device record (captured via tpid/svid on vlan-port).
|
||||
# - Each network bridge-vlan section for this bridge becomes a bridge-vlan
|
||||
# section in bridging config; each ports entry within it becomes a
|
||||
# vlan-port section.
|
||||
# ===========================================================================
|
||||
_import_bvbackend() {
|
||||
local bridge_cfg="$1" # bridging section name
|
||||
local bridge_name="$2" # e.g. br-lan
|
||||
local raw_ports="$3" # space-separated ports from device section
|
||||
|
||||
# -- bridge-port sections for physical members --------------------------
|
||||
for portname in $raw_ports; do
|
||||
_next_bp
|
||||
uci -q set "bridging.${_bp_name}=bridge-port"
|
||||
uci -q set "bridging.${_bp_name}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${_bp_name}.name=${portname}"
|
||||
uci -q set "bridging.${_bp_name}.enabled=1"
|
||||
uci -q set "bridging.${_bp_name}.from_network_uci=1"
|
||||
done
|
||||
|
||||
# -- bridge-vlan + vlan-port sections -----------------------------------
|
||||
# Walk all network bridge-vlan sections and find those for this bridge
|
||||
local idx=0
|
||||
while uci -q get "network.@bridge-vlan[$idx]" >/dev/null 2>&1; do
|
||||
local bv_dev bv_vlan bv_managed
|
||||
bv_dev=$(uci -q get "network.@bridge-vlan[$idx].device" 2>/dev/null)
|
||||
bv_vlan=$(uci -q get "network.@bridge-vlan[$idx].vlan" 2>/dev/null)
|
||||
bv_managed=$(uci -q get "network.@bridge-vlan[$idx].managed_by_bridging" 2>/dev/null)
|
||||
|
||||
# Skip if not for this bridge or written by us
|
||||
if [ "$bv_dev" = "$bridge_name" ] && [ "$bv_managed" != "1" ]; then
|
||||
_next_bv
|
||||
local bv_sec="$_bv_name"
|
||||
|
||||
uci -q set "bridging.${bv_sec}=bridge-vlan"
|
||||
uci -q set "bridging.${bv_sec}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${bv_sec}.vlan=${bv_vlan}"
|
||||
uci -q set "bridging.${bv_sec}.from_network_uci=1"
|
||||
|
||||
# Iterate over the ports list of this bridge-vlan section
|
||||
local port_list
|
||||
port_list=$(uci -q get "network.@bridge-vlan[$idx].ports" 2>/dev/null)
|
||||
local raw_port
|
||||
for raw_port in $port_list; do
|
||||
_import_bv_port "$bv_sec" "$bridge_cfg" "$raw_port"
|
||||
done
|
||||
fi
|
||||
|
||||
idx=$(( idx + 1 ))
|
||||
done
|
||||
}
|
||||
|
||||
# Parse one ports entry from a network bridge-vlan section and create a vlan-port.
|
||||
# raw_port is e.g.: eth0:t ae_wan.300:u* eth1:u eth0.1.300:t
|
||||
_import_bv_port() {
|
||||
local bv_sec="$1" # bridging bridge-vlan section name
|
||||
local bridge_cfg="$2" # bridging bridge section name
|
||||
local raw_port="$3"
|
||||
|
||||
# Split annotation from interface name
|
||||
local portname annotation pvid_flag
|
||||
portname="${raw_port%%:*}"
|
||||
local rest="${raw_port#*:}"
|
||||
[ "$rest" = "$raw_port" ] && rest=""
|
||||
|
||||
pvid_flag=0
|
||||
case "$rest" in
|
||||
u\*) annotation="u"; pvid_flag=1 ;;
|
||||
u) annotation="u" ;;
|
||||
t) annotation="t" ;;
|
||||
*) annotation="t" ;;
|
||||
esac
|
||||
|
||||
_next_vp
|
||||
uci -q set "bridging.${_vp_name}=vlan-port"
|
||||
uci -q set "bridging.${_vp_name}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${_vp_name}.vlan=${bv_sec}"
|
||||
uci -q set "bridging.${_vp_name}.from_network_uci=1"
|
||||
|
||||
if [ "$annotation" = "u" ]; then
|
||||
uci -q set "bridging.${_vp_name}.untagged=1"
|
||||
else
|
||||
uci -q set "bridging.${_vp_name}.untagged=0"
|
||||
fi
|
||||
[ "$pvid_flag" = "1" ] && uci -q set "bridging.${_vp_name}.pvid=1"
|
||||
|
||||
# Walk the device chain to find base port and any s-vlan wrapping.
|
||||
# If portname has no device map entry it is already a plain physical port.
|
||||
_resolve_port_chain "$portname"
|
||||
# _chain_base, _chain_svid, _chain_tpid are now set
|
||||
|
||||
uci -q set "bridging.${_vp_name}.port=${_chain_base}"
|
||||
|
||||
if [ -n "$_chain_svid" ]; then
|
||||
uci -q set "bridging.${_vp_name}.svid=${_chain_svid}"
|
||||
uci -q set "bridging.${_vp_name}.tpid=${_chain_tpid}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
# Legacy backend import
|
||||
# ===========================================================================
|
||||
# In legacy mode there are no network bridge-vlan sections. The bridge ports
|
||||
# list contains VLAN sub-interface device names whose structure is recorded in
|
||||
# device sections (type, vid, ifname). We walk the device map chain for each
|
||||
# port to reconstruct bridge-vlan and vlan-port sections.
|
||||
#
|
||||
# Chain possibilities (innermost device is the bridge port entry):
|
||||
# no device entry — plain untagged physical port
|
||||
# one hop (type, vid) — single-tagged port; vid is the bridge-vlan vid
|
||||
# two hops (outer, inner) — double-tagged; outer=s-vlan, inner=c-vlan
|
||||
# ===========================================================================
|
||||
_import_legacy() {
|
||||
local bridge_cfg="$1"
|
||||
local bridge_name="$2"
|
||||
local raw_ports="$3"
|
||||
|
||||
_legacy_bv_seen=""
|
||||
_legacy_bp_seen=""
|
||||
|
||||
for portname in $raw_ports; do
|
||||
_import_legacy_port "$bridge_cfg" "$portname"
|
||||
done
|
||||
}
|
||||
|
||||
_import_legacy_port() {
|
||||
local bridge_cfg="$1"
|
||||
local portname="$2"
|
||||
|
||||
# Walk the device chain for this port entry
|
||||
_resolve_port_chain "$portname"
|
||||
# Globals: _chain_base, _chain_svid, _chain_tpid, _chain_vid, _chain_cvtpid
|
||||
|
||||
if [ -z "$_chain_vid" ]; then
|
||||
# No VLAN device at all — plain untagged port
|
||||
_ensure_legacy_bp "$bridge_cfg" "$_chain_base"
|
||||
return
|
||||
fi
|
||||
|
||||
# Ensure bridge-port for the physical base
|
||||
_ensure_legacy_bp "$bridge_cfg" "$_chain_base"
|
||||
|
||||
# Ensure bridge-vlan for the inner (c-vlan) vid
|
||||
local bv_sec
|
||||
_ensure_legacy_bv "$bridge_cfg" "$_chain_vid"
|
||||
bv_sec="$_ensured_bv"
|
||||
|
||||
_next_vp
|
||||
uci -q set "bridging.${_vp_name}=vlan-port"
|
||||
uci -q set "bridging.${_vp_name}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${_vp_name}.vlan=${bv_sec}"
|
||||
uci -q set "bridging.${_vp_name}.port=${_chain_base}"
|
||||
uci -q set "bridging.${_vp_name}.untagged=0"
|
||||
uci -q set "bridging.${_vp_name}.from_network_uci=1"
|
||||
|
||||
if [ -n "$_chain_svid" ]; then
|
||||
# Double-tagged: record outer s-vlan
|
||||
uci -q set "bridging.${_vp_name}.svid=${_chain_svid}"
|
||||
uci -q set "bridging.${_vp_name}.tpid=${_chain_tpid}"
|
||||
else
|
||||
# Single-tagged: tpid comes from the single hop's type
|
||||
uci -q set "bridging.${_vp_name}.tpid=${_chain_tpid}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure a bridge-port section exists for a given base interface.
|
||||
_ensure_legacy_bp() {
|
||||
local bridge_cfg="$1"
|
||||
local base="$2"
|
||||
|
||||
echo "$_legacy_bp_seen" | grep -qF "|${bridge_cfg}:${base}|" && return
|
||||
|
||||
_next_bp
|
||||
uci -q set "bridging.${_bp_name}=bridge-port"
|
||||
uci -q set "bridging.${_bp_name}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${_bp_name}.name=${base}"
|
||||
uci -q set "bridging.${_bp_name}.enabled=1"
|
||||
uci -q set "bridging.${_bp_name}.from_network_uci=1"
|
||||
|
||||
_legacy_bp_seen="${_legacy_bp_seen}|${bridge_cfg}:${base}|"
|
||||
}
|
||||
|
||||
# Ensure a bridge-vlan section exists for (bridge_cfg, vid).
|
||||
# Sets _ensured_bv to the section name.
|
||||
_ensure_legacy_bv() {
|
||||
local bridge_cfg="$1"
|
||||
local vid="$2"
|
||||
|
||||
# Check if already created
|
||||
local key="${bridge_cfg}:${vid}"
|
||||
if echo "$_legacy_bv_seen" | grep -qF "|${key}="; then
|
||||
_ensured_bv=$(echo "$_legacy_bv_seen" | \
|
||||
sed -n "s/.*|\(${key}=\([^|]*\)\)|.*/\2/p")
|
||||
return
|
||||
fi
|
||||
|
||||
_next_bv
|
||||
uci -q set "bridging.${_bv_name}=bridge-vlan"
|
||||
uci -q set "bridging.${_bv_name}.bridge=${bridge_cfg}"
|
||||
uci -q set "bridging.${_bv_name}.vlan=${vid}"
|
||||
uci -q set "bridging.${_bv_name}.from_network_uci=1"
|
||||
|
||||
_legacy_bv_seen="${_legacy_bv_seen}|${key}=${_bv_name}|"
|
||||
_ensured_bv="$_bv_name"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
read_bridges_from_network_uci
|
||||
uci commit
|
||||
|
||||
sync
|
||||
|
||||
exit 0
|
||||
Loading…
Add table
Reference in a new issue