mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2026-01-27 17:37:18 +01:00
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
576 lines
15 KiB
Bash
Executable file
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
|