diff --git a/netmode/files/etc/netmodes/advanced/scripts/10-advanced b/netmode/files/etc/netmodes/advanced/scripts/10-advanced index e68b06ac2..661949eec 100755 --- a/netmode/files/etc/netmodes/advanced/scripts/10-advanced +++ b/netmode/files/etc/netmodes/advanced/scripts/10-advanced @@ -40,6 +40,7 @@ configure_interfaces() { _log "Interface configs: $interface_configs" _log "MAC addresses: $mac_addrs" + _log "Bridge backend: $BRIDGE_BACKEND" # Parse the combined configuration into parallel arrays local OLD_IFS="$IFS" @@ -280,11 +281,17 @@ configure_interfaces() { MACVLAN_PRESENT=1 fi - _log "Parsed: mode=$mode, vlan_type=$vlan_type, cvid=$cvid, svid=$svid, mac=$mac_addr, proto=$proto, purpose=$purpose" + _log "Parsed: mode=$mode, vlan_type=$vlan_type, cvid=$cvid, svid=$svid, mac=$mac_addr, proto=$proto, purpose=$purpose, legacy=$PARSE_LEGACY" case "$mode" in bridge) # Create bridge using helper function + # If legacy=false (default), bridge-vlan will be created and we need shared bridge + if [ "$PARSE_LEGACY" = "0" ] && { [ "$vlan_type" = "tagged" ] || [ "$vlan_type" = "wan-tagged" ] || [ "$vlan_type" = "transparent-qinq" ] || [ "$vlan_type" = "tagged-qinq" ] || [ "$vlan_type" = "qinq" ]; }; then + BRIDGE_VLAN_PRESENT=1 + bridge_vlan_mtu="$if_mtu" + _log "Bridge type with legacy=false detected, will create shared bridge for bridge-vlan configurations" + fi create_bridge "$if_name" "$if_type" "$port_list" "$if_mac" "$if_mtu" ;; diff --git a/netmode/files/lib/netmode/advanced_helper.sh b/netmode/files/lib/netmode/advanced_helper.sh index fb573363c..54e187d96 100644 --- a/netmode/files/lib/netmode/advanced_helper.sh +++ b/netmode/files/lib/netmode/advanced_helper.sh @@ -11,6 +11,7 @@ BRIDGE_ALL_PORTS="" BRIDGE_CREATED="0" SHARED_BRIDGE_NAME="br-vlan" +BRIDGE_BACKEND="$(uci -q get netmode.global.bridge_backend || echo "bridge-vlan")" # # Validation Functions @@ -633,7 +634,6 @@ parse_interface_type() { local _disabled="0" local _purpose="" # mgmt, inet (internet) or iptv - # Check for modifiers (must be at the end of the string) # Process -disabled and -d (both set disabled flag) if echo "$type_spec" | grep -qE -- '-(disabled|d)$'; then _disabled="1" @@ -1204,7 +1204,7 @@ create_routed_interface() { logger -s -p user.info -t "$_log_prefix" "Routed interface $if_name created: device=$device_name" } -# Create bridge device and interface +# Create bridge device and interface (with optional bridge-vlan support) create_bridge() { local bridge_name="$1" local bridge_type="$2" @@ -1229,8 +1229,16 @@ create_bridge() { local actual_ports=$(parse_port_list "$port_list") logger -s -p user.info -t "$_log_prefix" "Parsed ports: $actual_ports" - logger -s -p user.info -t "$_log_prefix" "VLAN type: $vlan_type, cvid: $cvid, svid: $svid" + logger -s -p user.info -t "$_log_prefix" "VLAN type: $vlan_type, cvid: $cvid, svid: $svid, legacy: $legacy" + # Check if we should use bridge-vlan mode (new default) + # Use bridge-vlan for tagged, wan-tagged, and qinq types when BRIDGE_BACKEND != legacy + if [ "$BRIDGE_BACKEND" != "legacy" ]; then + create_bridge_vlan_from_bridge_type "$bridge_name" "$bridge_type" "$port_list" "$mac_addr" "$mtu" + return 0 + fi + + # Legacy mode: Create traditional bridge device # Create interface local if_name="${bridge_name}" local br_dev_name="br-${bridge_name}" @@ -1349,6 +1357,192 @@ create_bridge() { logger -s -p user.info -t "$_log_prefix" "Bridge $bridge_name created successfully" } +# Create bridge-vlan configuration from bridge:tagged, bridge:wan-tagged, etc. types (legacy=false) +# This creates bridge-vlan stanzas using a shared br-wan bridge +# For QinQ scenarios, uses 8021ad device for svid +create_bridge_vlan_from_bridge_type() { + local bridge_name="$1" + local bridge_type="$2" + local port_list="$3" + local mac_addr="$4" + local mtu="$5" + + local _log_prefix="netmode-advanced" + + logger -s -p user.info -t "$_log_prefix" "Creating bridge-vlan from bridge type: $bridge_name, type: $bridge_type, ports: $port_list" + + # Parse bridge type + parse_interface_type "$bridge_type" + + local proto="$PARSE_PROTO" + local vlan_type="$PARSE_VLAN_TYPE" + local cvid="$PARSE_CVID" + local svid="$PARSE_SVID" + local disabled="$PARSE_DISABLED" + + # Parse ports + local actual_ports=$(parse_port_list "$port_list") + + logger -s -p user.info -t "$_log_prefix" "Parsed ports: $actual_ports" + logger -s -p user.info -t "$_log_prefix" "Bridge-VLAN type: $vlan_type, cvid: $cvid, svid: $svid" + + # Determine VLAN ID for bridge-vlan + # For transparent-qinq with only svid, use svid as the VLAN ID + # For other types, use cvid as the VLAN ID + local vlan_id="" + if [ "$vlan_type" = "transparent-qinq" ] && [ -z "$cvid" ] && [ -n "$svid" ]; then + vlan_id="$svid" + elif [ -n "$cvid" ]; then + vlan_id="$cvid" + elif [ -n "$svid" ]; then + vlan_id="$svid" + fi + + if [ -z "$vlan_id" ]; then + logger -s -p user.err -t "$_log_prefix" "ERROR: No VLAN ID specified for bridge-vlan mode" + return 1 + fi + + # Create interface section + local if_name="${bridge_name}" + local br_vlan_dev="${SHARED_BRIDGE_NAME}.${vlan_id}" + + uci -q delete "network.${if_name}" + uci -q set "network.${if_name}=interface" + uci -q set "network.${if_name}.proto=${proto}" + uci -q set "network.${if_name}.device=${br_vlan_dev}" + + # Set MAC address if provided + if [ -n "$mac_addr" ]; then + local resolved_mac=$(resolve_mac_address "$mac_addr") + uci -q set "network.${if_name}.macaddr=${resolved_mac}" + logger -s -p user.info -t "$_log_prefix" "Setting MAC address: $mac_addr -> $resolved_mac" + fi + + [ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1" + + # Create bridge-vlan section + local brvlan_sec_name="brv_${vlan_id}_${bridge_name}" + + uci -q delete "network.${brvlan_sec_name}" + uci -q set "network.${brvlan_sec_name}=bridge-vlan" + uci -q set "network.${brvlan_sec_name}.device=${SHARED_BRIDGE_NAME}" + uci -q set "network.${brvlan_sec_name}.vlan=${vlan_id}" + uci -q delete "network.${brvlan_sec_name}.ports" + + # Get WAN port for tagging decisions + local wan_port=$WAN_PORT + + # Handle QinQ for transparent-qinq, tagged-qinq, qinq modes + # Create 8021ad device for svid if needed + if { [ "$vlan_type" = "transparent-qinq" ] || [ "$vlan_type" = "tagged-qinq" ] || [ "$vlan_type" = "qinq" ]; } && [ -n "$svid" ]; then + logger -s -p user.info -t "$_log_prefix" "Creating 8021ad device for QinQ svid: $svid" + + # First, collect ports for the 8021ad device + for port in $actual_ports; do + local port_tpid=$(_extract_tpid "$port") + local port_clean=$(_strip_tpid "$port") + + # Add this port to the base bridge + add_to_bridge_ports "$port_clean" + done + + # Create 8021ad device for svid with first port (convention: use eth/ae interface) + # The 8021ad device will be created separately per port combination if needed + # For simplicity, we'll use one 8021ad device per svid + local first_port=$(echo "$actual_ports" | awk '{print $1}') + first_port=$(_strip_tpid "$first_port") + + # Create the 8021ad device for svid + logger -s -p user.info -t "$_log_prefix" "Creating 8021ad device: ${first_port}.${svid} for svid=$svid" + local ad_device=$(create_vlan_device "$first_port" "$svid" "8021ad") + + # Add the 8021ad device to bridge ports + add_to_bridge_ports "$ad_device" + fi + + # Add ports with appropriate tagging + for port in $actual_ports; do + local port_to_add="$port" + local is_wan="0" + + # Extract and strip tpid suffix before processing + local port_tpid=$(_extract_tpid "$port") + local port=$(_strip_tpid "$port") + + # Check if explicit untagged is set + if echo "$port_to_add" | grep -qE '(:u\*?|:u)'; then + # Port has explicit untagged marker + if echo "$port_to_add" | grep -q ':u\*'; then + # Untagged egress-only + port_to_add="${port}:u*" + logger -s -p user.info -t "$_log_prefix" "Port $port set as untagged egress-only" + else + # Untagged + port_to_add="${port}:u" + logger -s -p user.info -t "$_log_prefix" "Port $port set as untagged" + fi + else + # Determine tagging based on VLAN type + [ "$port" = "$wan_port" ] && is_wan="1" + + case "$vlan_type" in + none) + # Transparent - untagged + port_to_add="${port}:u" + ;; + + tagged) + # All ports tagged + port_to_add="${port}:t" + ;; + + wan-tagged) + # Only WAN tagged + if [ "$is_wan" = "1" ]; then + port_to_add="${port}:t" + else + port_to_add="${port}:u" + fi + ;; + + transparent-qinq) + # For transparent-qinq: + # If svid only: ports are untagged for cvid, or untagged egress-only for bare egress + # LAN ports: untagged egress-only (:u*) + # WAN port: add to s-vlan as untagged + if [ "$is_wan" = "1" ]; then + port_to_add="${port}:u" + else + port_to_add="${port}:u*" + fi + ;; + + tagged-qinq) + # All ports single-tagged (for c-vlan bridging) + port_to_add="${port}:t" + ;; + + qinq) + # All ports double-tagged + port_to_add="${port}:t" + ;; + esac + fi + + logger -s -p user.info -t "$_log_prefix" "Adding port to bridge-vlan: $port -> $port_to_add" + + # Add base port to shared bridge + add_to_bridge_ports "${port}" + + # Add port to bridge-vlan with tagging + uci -q add_list "network.${brvlan_sec_name}.ports=${port_to_add}" + set_mtu_for_port "${port}" "${mtu}" + done + + logger -s -p user.info -t "$_log_prefix" "Bridge-VLAN $bridge_name (VLAN ${vlan_id}) created successfully" +} + # Clean up all existing bridges, VLANs, and routed interfaces cleanup_interfaces() { logger -s -p user.info -t "netmode-advanced" "Cleaning up existing interfaces"