diff --git a/netmode/docs/ADVANCED_MODE_GUIDE.md b/netmode/docs/ADVANCED_MODE_GUIDE.md index 1bc4abcfa..cb7d21112 100644 --- a/netmode/docs/ADVANCED_MODE_GUIDE.md +++ b/netmode/docs/ADVANCED_MODE_GUIDE.md @@ -17,7 +17,7 @@ The **advanced** mode is a unified, flexible network configuration mode for Open - **Bridge interfaces** with VLAN/QinQ support (traditional VLAN devices) - **Bridge VLAN filtering** (modern kernel bridge features - recommended) - **Routed interfaces** with VLAN/MACVLAN support -- **Standalone interfaces** (direct VLAN without bridge) +- **Standalone interfaces** (direct VLAN without bridge, similar to Routed, but does not support macvlan, or inet or iptv modifiers (those require routing), but it can be set to mgmt). - **Mixed scenarios** (combine bridges and routed interfaces) ### Key Features @@ -37,7 +37,7 @@ The **advanced** mode is a unified, flexible network configuration mode for Open | Parameter | Description | Example | |-----------|-------------|---------| | `interface_names` | Comma-separated interface names | `wan,iptv,mgmt` | -| `interface_types` | Comma-separated interface types | `bridge:transparent,brvlan:wan-tagged:1499,route:vlan:100,direct:200` | +| `interface_types` | Comma-separated interface types | `bridge:transparent,brvlan:wan-tagged:1499,route:vlan:100,direct:vlan:200` | | `ports` | Comma-separated port assignments | `ALL,LAN1-LAN2-WAN,WAN` | | `macaddrs` | Comma-separated MAC addresses (optional) | `BaseMACAddress,BaseMACAddressP1,AA:BB:CC:DD:EE:FF` | @@ -93,11 +93,11 @@ Routed types create L3 routed interfaces (with NAT/firewall). ### Standalone Types -Standalone types create VLAN interfaces without bridges or routing (proto=none by default). +Standalone types create VLAN interfaces without bridges or routing (proto=dhcp by default). | Type | Syntax | Description | |------|--------|-------------| -| **Direct VLAN** | `direct:VID` | Standalone VLAN interface, proto=none | +| **Direct VLAN** | `direct:vlan:VID` | Standalone VLAN interface, proto=dhcp | ### Device Reference Types @@ -146,7 +146,7 @@ Modifiers can be appended to any interface type: - The `-none` and `-n` modifiers are equivalent, as are `-disabled` and `-d`. - If no protocol modifier is specified, interfaces default to `proto=dhcp`. - Protocols and disabled can be clubbed together, and disabled should be in the last, for example: `transparent-qinq:2-n-d` will set proto as none and disable the interface, similarly other protocols can be used. -- iptv, inet and mgmt modifier can only be used with route interfaces, and they can be clubbed with disabled modifier, but disable should be in the last. +- iptv and inet modifier can only be used with route interfaces, mgmt can be used with route or direct interfaces, and they can be clubbed with disabled modifier, but disable should be in the last. #### Static IP Auto-Configuration @@ -176,7 +176,7 @@ ports='ALL_LAN,WAN' For non-LAN interfaces with `-static`, only `proto=static` is set without additional configuration. -**Note**: Direct interfaces default to `proto=none`, so `-n` is implicit. +**Note**: Direct interfaces default to `proto=dhcp`. ### MAC Address Assignment @@ -388,13 +388,13 @@ service netmode restart ```bash uci set netmode.global.mode='advanced' uci set netmode.@supported_args[12].value='wan' -uci set netmode.@supported_args[13].value='direct:2501' +uci set netmode.@supported_args[13].value='direct:vlan:2501' uci set netmode.@supported_args[14].value='WAN' uci commit netmode service netmode restart ``` -**Result**: Creates WAN.2501 interface, proto=none (no DHCP) +**Result**: Creates WAN.2501 interface, proto=DHCP --- diff --git a/netmode/docs/ADVANCED_MODE_IMPLEMENTATION.md b/netmode/docs/ADVANCED_MODE_IMPLEMENTATION.md index 16b940fff..ffcc28aef 100644 --- a/netmode/docs/ADVANCED_MODE_IMPLEMENTATION.md +++ b/netmode/docs/ADVANCED_MODE_IMPLEMENTATION.md @@ -20,7 +20,7 @@ The advanced mode uses a **declarative, array-based configuration**: ``` interface_names: wan, iptv, mgmt -interface_types: route:vlan:100, bridge:tagged:200, direct:300 +interface_types: route:vlan:100, bridge:tagged:200, direct:vlan:300 ports: WAN, LAN1-LAN2-WAN, WAN ``` @@ -107,7 +107,7 @@ Examples: - `bridge:transparent` - Mode=bridge, Subtype=transparent - `bridge:tagged:100` - Mode=bridge, Subtype=tagged, Param=VID - `route:vlan:100:AA:BB:CC:DD:EE:FF` - Mode=route, Subtype=vlan, Params=VID+MAC -- `direct:2501-n` - Mode=direct, Param=VID, Modifier=proto_none +- `direct:vlan:2501-n` - Mode=direct, Param=VID, Modifier=proto_none ### Parsing Logic diff --git a/netmode/docs/ADVANCED_MODE_QUICK_REFERENCE.md b/netmode/docs/ADVANCED_MODE_QUICK_REFERENCE.md index 1c567d6e4..b35d70af0 100644 --- a/netmode/docs/ADVANCED_MODE_QUICK_REFERENCE.md +++ b/netmode/docs/ADVANCED_MODE_QUICK_REFERENCE.md @@ -30,7 +30,8 @@ route:vlan:VID:MAC # VLAN + custom MAC ### Standalone Types ``` -direct:VID # Standalone VLAN (proto=none) +direct:vlan:VID # Standalone VLAN (proto=dhcp) +direct:transparent # No VLAN ``` ### Device Reference Types @@ -150,7 +151,7 @@ uci commit netmode && service netmode restart ```bash uci set netmode.global.mode='advanced' uci set netmode.@supported_args[0].value='wan' -uci set netmode.@supported_args[1].value='direct:2501' +uci set netmode.@supported_args[1].value='direct:vlan:2501' uci set netmode.@supported_args[2].value='WAN' uci commit netmode && service netmode restart ``` diff --git a/netmode/docs/CONFIGURATION_SCENARIOS.md b/netmode/docs/CONFIGURATION_SCENARIOS.md index 72fffebd6..14bf6c62b 100644 --- a/netmode/docs/CONFIGURATION_SCENARIOS.md +++ b/netmode/docs/CONFIGURATION_SCENARIOS.md @@ -594,7 +594,7 @@ uci commit netmode && service netmode restart **Network Topology**: ``` -WAN.2501 → wan (standalone, proto=none) +WAN.2501 → wan (standalone, proto=dhcp) ``` ### UCI Configuration @@ -602,7 +602,7 @@ WAN.2501 → wan (standalone, proto=none) ```bash uci set netmode.global.mode='advanced' uci set netmode.mode_4_supprted_args_1.value='wan' -uci set netmode.mode_4_supprted_args_2.value='direct:2501' +uci set netmode.mode_4_supprted_args_2.value='direct:vlan:2501' uci set netmode.mode_4_supprted_args_3.value='WAN' uci commit netmode && service netmode restart ``` @@ -622,7 +622,7 @@ uci commit netmode && service netmode restart Device.X_IOWRT_EU_NetMode.SupportedModes.4.SupportedArguments.2.Value - direct:2501 + direct:vlan:2501 Device.X_IOWRT_EU_NetMode.SupportedModes.4.SupportedArguments.3.Value @@ -705,7 +705,7 @@ uci commit netmode && service netmode restart | QinQ Bridge | `bridge:qinq:CVID:SVID` | Wholesale provider, double tagging | | Routed VLAN | `route:vlan:VID` | Need routing/NAT per service | | Routed MACVLAN | `route:macvlan:MAC` | Different MAC per service | -| Direct VLAN | `direct:VID` | Standalone VLAN for custom protocols | +| Direct VLAN | `direct:vlan:VID` | Standalone VLAN for custom protocols | ### Modifiers diff --git a/netmode/docs/USER_GUIDE.md b/netmode/docs/USER_GUIDE.md index 8b38c03c2..e10ee2e1e 100644 --- a/netmode/docs/USER_GUIDE.md +++ b/netmode/docs/USER_GUIDE.md @@ -257,7 +257,7 @@ service netmode restart **Configuration Parameters**: - `interface_names` (optional): Comma-separated interface names (default: wan) -- `interface_types` (optional): Comma-separated types (transparent, tagged:VID, direct:VID, qinq:C:S, etc.) +- `interface_types` (optional): Comma-separated types (transparent, tagged:VID, direct:vlan:VID, qinq:C:S, etc.) - `ports` (optional): Comma-separated port lists (default: ALL) **Basic Configuration**: @@ -281,7 +281,7 @@ service netmode restart # Standalone VLAN interface (no bridge) uci set netmode.global.mode='bridged' uci set netmode.@supported_args[0].value='wan' -uci set netmode.@supported_args[1].value='direct:2501' # Direct VLAN interface +uci set netmode.@supported_args[1].value='direct:vlan:2501' # Direct VLAN interface uci set netmode.@supported_args[2].value='WAN' uci commit netmode service netmode restart diff --git a/netmode/docs/VALIDATION_AND_ERROR_HANDLING.md b/netmode/docs/VALIDATION_AND_ERROR_HANDLING.md index f7ed0487f..c01725742 100644 --- a/netmode/docs/VALIDATION_AND_ERROR_HANDLING.md +++ b/netmode/docs/VALIDATION_AND_ERROR_HANDLING.md @@ -76,7 +76,7 @@ ERROR: Invalid port identifier in 'LAN1-LAN9-WAN': 'LAN9' **Example Errors**: ``` ERROR: Unknown interface type: 'bridge:unknown:100' -Valid types: bridge:transparent, bridge:tagged:VID, brvlan:wan-tagged:VID, route:vlan:VID, route:macvlan:MAC, direct:VID +Valid types: bridge:transparent, bridge:tagged:VID, brvlan:wan-tagged:VID, route:vlan:VID, route:macvlan:MAC, direct:vlan:VID, direct:transparent ERROR: VLAN ID in type 'route:vlan:9999' out of range (must be 1-4094): 9999 ``` diff --git a/netmode/files/:w b/netmode/files/:w new file mode 100644 index 000000000..c36104cfa --- /dev/null +++ b/netmode/files/:w @@ -0,0 +1,576 @@ +#!/bin/sh +# +# Advanced Mode Script +# Unified configuration for bridges, routed interfaces, and standalone interfaces +# Replaces: bridged mode and routed-multi-service mode +# + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh +. /lib/netmode/advanced_helper.sh + +[ -f /etc/device_info ] && source "/etc/device_info" + +_log() { + logger -s -p user.info -t "netmode-advanced" "$*" +} + +IPTV_IFACES="" +MGMT_IFACES="" +INET_IFACES="" +IPTV_DEVS="" +WAN_PORT="" +MACVLAN_PRESENT=0 +BRIDGE_VLAN_PRESENT=0 + +# +# Main Interface Configuration +# +configure_interfaces() { + _log "Starting advanced interface configuration" + + # Get configuration from environment variables + local interface_names="${NETMODE_interface_names:-wan}" + local interface_types="${NETMODE_interface_types:-bridge:transparent}" + local ports="${NETMODE_ports:-ALL}" + local mac_addrs="${NETMODE_macaddrs:-}" + + _log "Interface names: $interface_names" + _log "Interface types: $interface_types" + _log "Ports: $ports" + _log "MAC addresses: $mac_addrs" + + # Validate configuration before proceeding + _log "Validating configuration..." + + # Count elements in each parameter + local name_count=$(echo "$interface_names" | tr ',' '\n' | wc -l) + local type_count=$(echo "$interface_types" | tr ',' '\n' | wc -l) + local port_count=$(echo "$ports" | tr ',' '\n' | wc -l) + local mac_count=0 + [ -n "$mac_addrs" ] && mac_count=$(echo "$mac_addrs" | tr ',' '\n' | wc -l) + + _log "Element counts: names=$name_count, types=$type_count, ports=$port_count, macs=$mac_count" + + # Validate counts match + if [ "$name_count" != "$type_count" ]; then + _log "ERROR: Number of interface names ($name_count) does not match number of interface types ($type_count)" + _log "interface_names: $interface_names" + _log "interface_types: $interface_types" + return 1 + fi + + if [ "$name_count" != "$port_count" ]; then + _log "ERROR: Number of interface names ($name_count) does not match number of ports ($port_count)" + _log "interface_names: $interface_names" + _log "ports: $ports" + return 1 + fi + + if [ "$mac_count" -gt 0 -a "$mac_count" != "$name_count" ]; then + _log "WARNING: Number of MAC addresses ($mac_count) does not match number of interfaces ($name_count)" + _log "Some interfaces will use default MAC addresses" + fi + + # Validate each parameter + local idx=1 + local OLD_IFS="$IFS" + IFS=',' + + for name in $interface_names; do + validate_interface_name "$name" || { + IFS="$OLD_IFS" + return 1 + } + done + + for type in $interface_types; do + validate_interface_type "$type" || { + IFS="$OLD_IFS" + return 1 + } + done + + for port_spec in $ports; do + validate_port_spec "$port_spec" || { + IFS="$OLD_IFS" + return 1 + } + done + + if [ -n "$mac_addrs" ]; then + for mac in $mac_addrs; do + validate_mac_address "$mac" "1" || { + IFS="$OLD_IFS" + return 1 + } + done + fi + + IFS="$OLD_IFS" + + _log "Configuration validation passed" + + # Clean up existing configuration + cleanup_interfaces + + # Split comma-separated values into arrays + local names_arr="" + local types_arr="" + local ports_arr="" + local macs_arr="" + + # Save and set IFS for comma splitting + local OLD_IFS="$IFS" + IFS=',' + + for name in $interface_names; do + names_arr="$names_arr $name" + done + + for type in $interface_types; do + types_arr="$types_arr $type" + done + + for port_spec in $ports; do + ports_arr="$ports_arr $port_spec" + done + + for mac in $mac_addrs; do + macs_arr="$macs_arr $mac" + done + + # Restore IFS + IFS="$OLD_IFS" + + # Convert to arrays for indexing + set -- $names_arr + local total_interfaces=$# + + _log "Total interfaces to create: $total_interfaces" + + # Get WAN port for routed interfaces + local wan_port=$(get_wan_port) + WAN_PORT="$wan_port" + _log "WAN port: $WAN_PORT" + + # Create each interface + local idx=1 + for if_name in $names_arr; do + # Get corresponding type, ports, and MAC address + local type_idx=$idx + local ports_idx=$idx + local mac_idx=$idx + + set -- $types_arr + shift $((type_idx - 1)) + local if_type="${1:-bridge:transparent}" + + set -- $ports_arr + shift $((ports_idx - 1)) + local port_list="${1:-ALL}" + + set -- $macs_arr + shift $((mac_idx - 1)) + local if_mac="${1:-}" + + _log "Creating interface $idx/$total_interfaces: name=$if_name, type=$if_type, ports=$port_list, mac=$if_mac" + + # Parse interface type + parse_interface_type "$if_type" + + local mode="$PARSE_MODE" + local vlan_type="$PARSE_VLAN_TYPE" + local cvid="$PARSE_CVID" + local svid="$PARSE_SVID" + local mac_addr="$PARSE_MAC_ADDR" + local proto="$PARSE_PROTO" + local disabled="$PARSE_DISABLED" + local purpose="$PARSE_PURPOSE" + + if [ "$purpose" = "iptv" ]; then + IPTV_IFACES="$IPTV_IFACES $if_name" + elif [ "$purpose" = "inet" ]; then + INET_IFACES="$INET_IFACES $if_name" + elif [ "$purpose" = "mgmt" ]; then + MGMT_IFACES="$MGMT_IFACES $if_name" + fi + + if [ "$vlan_type" = "macvlan" ] || [ "$mac_count" -gt 0 ]; then + MACVLAN_PRESENT=1 + fi + + _log "Parsed: mode=$mode, vlan_type=$vlan_type, cvid=$cvid, svid=$svid, mac=$mac_addr, proto=$proto, purpose=$purpose" + + case "$mode" in + bridge) + # Create bridge using helper function + create_bridge "$if_name" "$if_type" "$port_list" "$if_mac" + ;; + + brvlan) + # Create bridge with VLAN filtering + BRIDGE_VLAN_PRESENT=1 + create_bridge_vlan_filtering "$if_name" "$if_type" "$port_list" "$if_mac" + ;; + + device-ref) + # Create interface that references device from another interface + # cvid contains the reference interface name + local ref_if_name="$cvid" + local ref_device=$(uci -q get "network.${ref_if_name}.device") + + if [ -z "$ref_device" ]; then + _log "ERROR: Reference interface '$ref_if_name' not found or has no device" + exit 1 + fi + + _log "Creating interface $if_name referencing device $ref_device from interface $ref_if_name" + + # Create interface using the same device as reference interface + 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=${ref_device}" + + # Set MAC address if provided + if [ -n "$if_mac" ]; then + local resolved_mac=$(resolve_mac_address "$if_mac") + uci -q set "network.${if_name}.macaddr=${resolved_mac}" + _log "Setting MAC address: $if_mac -> $resolved_mac" + fi + + [ "$disabled" = "1" ] && uci -q set "network.${if_name}.disabled=1" + ;; + + route) + # Create routed interface + port_list="${port_list//:u/}" + local base_device="" + if [ "$port_list" = "WAN" -o "$port_list" = "wan" ]; then + base_device="$wan_port" + else + # Use first port from list + local actual_ports=$(parse_port_list "$port_list") + base_device=$(echo "$actual_ports" | awk '{print $1}') + fi + + create_routed_interface "$if_name" "$vlan_type" "$cvid" "$mac_addr" "$proto" "$base_device" "$disabled" "$purpose" + ;; + + direct) + # Create standalone VLAN interface + port_list="${port_list//:u/}" + local base_device="" + if [ "$port_list" = "WAN" -o "$port_list" = "wan" ]; then + base_device="$wan_port" + else + local actual_ports=$(parse_port_list "$port_list") + base_device=$(echo "$actual_ports" | awk '{print $1}') + fi + + create_standalone_interface "$if_name" "$vlan_type" "$cvid" "$if_mac" "$proto" "$base_device" "$disabled" ;; + esac + + idx=$((idx + 1)) + done + + if [ "$BRIDGE_VLAN_PRESENT" -eq 1 ]; then + # create the shared bridge once with all collected ports from bridge-vlan interfaces + create_shared_bridge + fi + + # Commit network changes + uci -q commit network + + IPTV_IFACES="$(echo "$IPTV_IFACES" | xargs)" + INET_IFACES="$(echo "$INET_IFACES" | xargs)" + MGMT_IFACES="$(echo "$MGMT_IFACES" | xargs)" + + _log "Interface configuration completed" +} + +# +# Configure L3 Multicast (Proxy) +# +configure_l3_mcast() { + _log "Configuring L3 multicast (Proxy) for $IPTV_DEVS" + + # Remove proxy sections + uci -q delete mcast.igmp_proxy_1 + uci -q delete mcast.mc_proxy_MLD + uci -q delete mcast.igmp_snooping_1 + uci -q delete mcast.mld_snooping_1 + + IPTV_DEVS="$(echo "$IPTV_DEVS" | xargs | tr ' ' '\n' | sort -u)" + + uci add mcast proxy + uci rename mcast.@proxy[-1]="mc_proxy_MLD" + uci set mcast.@proxy[-1].enable="1" + uci set mcast.@proxy[-1].proto="mld" + uci set mcast.@proxy[-1].version="2" + uci set mcast.@proxy[-1].robustness="2" + uci set mcast.@proxy[-1].query_interval="125" + uci set mcast.@proxy[-1].query_response_interval="100" + uci set mcast.@proxy[-1].last_member_query_interval="10" + uci set mcast.@proxy[-1].fast_leave="1" + uci set mcast.@proxy[-1].snooping_mode="2" + uci add_list mcast.@proxy[-1].downstream_interface="br-lan" + + IFS=" " + for itf in $IPTV_DEVS; do + uci add_list mcast.@proxy[-1].upstream_interface="$itf" + done + + + uci add mcast proxy + uci rename mcast.@proxy[-1]="igmp_proxy_1" + uci set mcast.@proxy[-1].enable="1" + uci set mcast.@proxy[-1].proto="igmp" + uci set mcast.@proxy[-1].version="2" + uci set mcast.@proxy[-1].robustness="2" + uci set mcast.@proxy[-1].query_interval="125" + uci set mcast.@proxy[-1].query_response_interval="100" + uci set mcast.@proxy[-1].last_member_query_interval="10" + uci set mcast.@proxy[-1].fast_leave="1" + uci set mcast.@proxy[-1].snooping_mode="2" + uci add_list mcast.@proxy[-1].downstream_interface="br-lan" + + IFS=" " + for itf in $IPTV_DEVS; do + uci add_list mcast.@proxy[-1].upstream_interface="$itf" + done + + uci add_list mcast.@proxy[-1].filter="239.0.0.0/8" + + uci -q commit mcast + + _log "L3 multicast configuration complete" +} + +# +# Configure L2 Multicast (Snooping) +# +configure_l2_mcast() { + _log "Configuring L2 multicast (snooping)" + # Remove proxy sections + uci -q delete mcast.igmp_proxy_1 + uci -q delete mcast.mc_proxy_MLD + + # Get all bridge names from network UCI + local bridge_list="" + local bridge_names="" + local br_device="" + + # Query all network sections and filter for bridge type + bridge_list=$(uci -q show network | grep "\.type='bridge'" | cut -d'.' -f2) + + # Convert to space-separated list + for bridge in $bridge_list; do + br_device="$(uci -q get network.${bridge}.name)" + if [ -z "$bridge_names" ]; then + [ -n "$br_device" ] && bridge_names="$br_device" + else + [ -n "$br_device" ] && bridge_names="$bridge_names $br_device" + fi + done + + if [ -z "$bridge_names" ]; then + _log "No bridges found for multicast configuration" + return + fi + + _log "Found bridges: $bridge_names" + + # Add IGMP snooping + uci -q set mcast.igmp_snooping_1=snooping + uci -q set mcast.igmp_snooping_1.enable='1' + uci -q set mcast.igmp_snooping_1.proto='igmp' + uci -q set mcast.igmp_snooping_1.version='2' + uci -q set mcast.igmp_snooping_1.robustness='2' + uci -q set mcast.igmp_snooping_1.query_interval='125' + uci -q set mcast.igmp_snooping_1.query_response_interval='100' + uci -q set mcast.igmp_snooping_1.last_member_query_interval='10' + uci -q set mcast.igmp_snooping_1.fast_leave='1' + uci -q set mcast.igmp_snooping_1.snooping_mode='2' + uci -q set mcast.igmp_snooping_1.interface="$bridge_names" + # to avoid multiple additions over the course of netmode reloads + uci -q del_list mcast.igmp_snooping_1.filter='239.0.0.0/8' + uci -q add_list mcast.igmp_snooping_1.filter='239.0.0.0/8' + + # Add MLD snooping + uci -q set mcast.mld_snooping_1=snooping + uci -q set mcast.mld_snooping_1.enable='1' + uci -q set mcast.mld_snooping_1.proto='mld' + uci -q set mcast.mld_snooping_1.version='2' + uci -q set mcast.mld_snooping_1.robustness='2' + uci -q set mcast.mld_snooping_1.query_interval='125' + uci -q set mcast.mld_snooping_1.query_response_interval='100' + uci -q set mcast.mld_snooping_1.last_member_query_interval='10' + uci -q set mcast.mld_snooping_1.fast_leave='1' + uci -q set mcast.mld_snooping_1.snooping_mode='2' + uci -q set mcast.mld_snooping_1.interface="$bridge_names" + + uci -q commit mcast + _log "L2 multicast configuration complete" +} + +# +# Configure DHCP +# +configure_dhcp() { + _log "Configuring DHCP" + + # Check if we have any static interfaces (will be configured by post-hook) + local interface_names="${NETMODE_interface_names:-wan}" + local interface_types="${NETMODE_interface_types:-bridge:transparent}" + local has_static_lan=0 + + local OLD_IFS="$IFS" + IFS=',' + local idx=1 + for if_name in $interface_names; do + # Get corresponding type + local type_idx=$idx + set -- $interface_types + shift $((type_idx - 1)) + local if_type="${1:-bridge:transparent}" + + # Check if this is lan interface with static proto + if [ "$if_name" = "lan" ] && echo "$if_type" | grep -q -- '-static$'; then + has_static_lan=1 + break + fi + + idx=$((idx + 1)) + done + IFS="$OLD_IFS" + + uci -q get network.lan && has_static_lan="1" + + if [ "$has_static_lan" = "1" ]; then + _log "LAN interface with static IP detected - DHCP server will be configured by post-hook" + # Don't disable DHCP for LAN, it will be configured by 15-static_lan.sh + # Only disable DHCP on WAN + uci -q set dhcp.wan.ignore=1 2>/dev/null + /etc/init.d/odhcpd enable + else + # Disable DHCP server on LAN (advanced mode without static LAN) + uci -q set dhcp.lan.ignore=1 + # Disable DHCP on WAN if it exists + uci -q set dhcp.wan.ignore=1 2>/dev/null + /etc/init.d/odhcpd disable + _log "DHCP server disabled" + fi + + local dhcp_ifaces="$(collect_interfaces_with_wan_port)" + + _log "Disabling DHCP server on interfaces: $dhcp_ifaces" + for iface in $dhcp_ifaces; do + uci -q set dhcp.$iface=dhcp + uci -q set dhcp.$iface.interface="$iface" + uci -q set dhcp.$iface.ignore=1 + done + + uci -q commit dhcp +} + +# +# Configure Firewall +# +configure_firewall() { + _log "Configuring firewall" + + # Check if any interface is routed + local interface_types="${NETMODE_interface_types:-bridge:transparent}" + local has_routed=0 + + local OLD_IFS="$IFS" + IFS=',' + for if_type in $interface_types; do + if echo "$if_type" | grep -q "^route:"; then + has_routed=1 + break + fi + done + IFS="$OLD_IFS" + + if [ "$has_routed" = "1" ]; then + # Enable firewall for routed interfaces + ensure_firewall_layout + fi + + uci -q set firewall.globals.enabled="1" + uci -q commit firewall + _log "Firewall enabled" +} + +# +# Update Service Dependencies +# +configure_services() { + _log "Updating service configurations" + + # Get first interface name for services + local interface_names="${NETMODE_interface_names:-wan}" + local IFS=',' + local first_interface="" + + for if_name in $interface_names; do + first_interface="$if_name" + break + done + + # Update CWMP Agent WAN Interface + uci -q set cwmp.cpe.default_wan_interface="$first_interface" + uci -q commit cwmp + + # Update gateway WAN Interface + uci -q set gateway.global.wan_interface="$first_interface" + uci -q commit gateway + + # Disable SSDPD + uci -q set ssdpd.ssdp.enabled="0" + uci -q commit ssdpd + + _log "Service configurations updated" +} + +# +# Main Execution +# +_log "=========================================" +_log "Starting Advanced Mode Configuration" +_log "=========================================" + +# Main execution with error handling +if ! configure_interfaces; then + _log "=========================================" + _log "ERROR: Advanced Mode Configuration Failed" + _log "Please check the logs above for details" + _log "=========================================" + exit 1 +fi + +if [ "$MACVLAN_PRESENT" -eq 1 ] || echo "$NETMODE_interface_types" | grep -q "BaseMACAddress"; then + _log "Macvlan interface with mac addr present, not generating default macoffset file" +else + _log "Macvlan interface with mac addr not present, generating default macoffset file" + configure_macoffset +fi + +if [ -n "$IPTV_DEVS" ]; then + configure_l3_mcast +else + configure_l2_mcast +fi + +configure_dhcp +configure_firewall +configure_services + +_log "=========================================" +_log "Advanced Mode Configuration Complete" +_log "=========================================" + +exit 0 diff --git a/netmode/files/etc/netmodes/advanced/scripts/10-advanced b/netmode/files/etc/netmodes/advanced/scripts/10-advanced index 20c75ff38..38e42f9f4 100755 --- a/netmode/files/etc/netmodes/advanced/scripts/10-advanced +++ b/netmode/files/etc/netmodes/advanced/scripts/10-advanced @@ -196,7 +196,7 @@ configure_interfaces() { MGMT_IFACES="$MGMT_IFACES $if_name" fi - if [ "$vlan_type" = "macvlan" ] && [ "$mac_count" -gt 0 ]; then + if [ "$vlan_type" = "macvlan" ] || [ "$mac_count" -gt 0 ]; then MACVLAN_PRESENT=1 fi @@ -269,8 +269,7 @@ configure_interfaces() { base_device=$(echo "$actual_ports" | awk '{print $1}') fi - create_standalone_interface "$if_name" "$cvid" "$proto" "$base_device" "$disabled" "$if_mac" - ;; + create_standalone_interface "$if_name" "$vlan_type" "$cvid" "$if_mac" "$proto" "$base_device" "$disabled" ;; esac idx=$((idx + 1)) @@ -482,14 +481,14 @@ configure_dhcp() { configure_firewall() { _log "Configuring firewall" - # Check if any interface is routed + # Check if any interface is routed | direct local interface_types="${NETMODE_interface_types:-bridge:transparent}" local has_routed=0 local OLD_IFS="$IFS" IFS=',' for if_type in $interface_types; do - if echo "$if_type" | grep -q "^route:"; then + if echo "$if_type" | grep -qE "route|direct"; then has_routed=1 break fi diff --git a/netmode/files/lib/netmode/advanced_helper.sh b/netmode/files/lib/netmode/advanced_helper.sh index bed9c50ed..24222321f 100644 --- a/netmode/files/lib/netmode/advanced_helper.sh +++ b/netmode/files/lib/netmode/advanced_helper.sh @@ -148,7 +148,7 @@ validate_interface_type() { # Validate based on type case "$base_type" in - bridge:transparent|route:transparent) + bridge:transparent|route:transparent|direct:transparent) return 0 ;; device-ref:*) @@ -195,8 +195,8 @@ validate_interface_type() { local mac="${base_type#route:macvlan:}" validate_mac_address "$mac" "1" || return 1 ;; - direct:*) - local vid="${base_type#direct:}" + direct:vlan:*) + local vid="${base_type#direct:vlan:}" validate_vlan_id "$vid" "VLAN ID in type '$type_spec'" || return 1 ;; *) @@ -569,7 +569,7 @@ parse_port_list() { } # Parse advanced interface type specification -# Format: bridge:TYPE or route:TYPE or direct:VID or brvlan:TYPE (bridge VLAN filtering) +# Format: bridge:TYPE or route:TYPE or direct:vlan:VID or brvlan:TYPE (bridge VLAN filtering) # Returns: mode, vlan_type, cvid, svid, mac_addr, proto, disabled parse_interface_type() { local type_spec="$1" @@ -583,7 +583,6 @@ parse_interface_type() { 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" @@ -593,7 +592,7 @@ parse_interface_type() { # route interfaces can have different purposes, which will # affect firewall and mcast uci - if echo "$type_spec" | grep -qE -- 'route'; then + if echo "$type_spec" | grep -qE -- 'route|direct'; then if echo "$type_spec" | grep -qE -- '-iptv$'; then _purpose="iptv" type_spec="${type_spec%-iptv}" @@ -728,11 +727,15 @@ parse_interface_type() { ;; # Direct (standalone) mode - direct:*) + direct:vlan:*) _mode="direct" _vlan_type="vlan" - _cvid="${type_spec#direct:}" - [ "$_proto" != "none" ] && _proto="none" # Direct defaults to none + _cvid="${type_spec#direct:vlan:}" + ;; + + direct:transparent) + _mode="direct" + _vlan_type="none" ;; *) @@ -984,18 +987,27 @@ create_bridge_vlan_filtering() { # Create standalone interface (no bridge, direct VLAN device) create_standalone_interface() { local if_name="$1" - local vlan_id="$2" - local proto="$3" - local base_device="$4" - local disabled="$5" - local mac_addr="$6" + local vlan_type="$2" + local vlan_id="$3" + local mac_addr="$4" + local proto="$5" + local base_device="$6" + local disabled="$7" local _log_prefix="netmode-advanced" - logger -s -p user.info -t "$_log_prefix" "Creating standalone interface: $if_name, VLAN: $vlan_id, device: $base_device, mac: $mac_addr" + logger -s -p user.info -t "$_log_prefix" "Creating standalone interface: $if_name, type: $vlan_type, VLAN: $vlan_id, device: $base_device, mac: $mac_addr" - # Create VLAN device - local vlan_dev=$(create_vlan_device "$base_device" "$vlan_id" "8021q") + case "$vlan_type" in + vlan) + # Create VLAN device + vlan_dev=$(create_vlan_device "$base_device" "$vlan_id" "8021q") + ;; + none) + # Direct on base device + vlan_dev="$base_device" + ;; + esac # Create interface pointing directly to VLAN device (no bridge) uci -q delete "network.${if_name}"