iopsys-feed/netmode/files/etc/netmodes/advanced/scripts/10-advanced
Mohd Husaam Mehdi 40240a7152 netmode: update handling of direct interfaces
the idea is to make them similar to route interface, to avoid
confusion

* they can be also be mgmt/inet/iptv
* they will have default proto set to dhcp
* syntax is now direct:vlan:100 or direct:transparent
2026-01-23 18:34:10 +05:30

576 lines
15 KiB
Bash
Executable file

#!/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 | 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 -qE "route|direct"; 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