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
This commit is contained in:
Mohd Husaam Mehdi 2026-01-22 16:11:20 +05:30 committed by Vivek Dutta
parent 76c0704dc5
commit 40240a7152
8 changed files with 55 additions and 41 deletions

View file

@ -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
@ -143,10 +143,12 @@ Modifiers can be appended to any interface type:
#### Notes
- For each interface individually, if more than one modifier is present, ordering among the modifiers should be as follows: none (or n), static, dhcp, dhcpv6, pppoe, mgmt, inet, iptv, d. Example: `uci set netmode.@supported_args[13].value='bridge:transparent-n,direct:vlan:100-dhcp-mgmt'`
- Some modifiers are mutually exclusive, for example mgmt and inet.
- 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 +178,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 +390,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
---

View file

@ -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

View file

@ -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
```

View file

@ -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
</ParameterValueStruct>
<ParameterValueStruct>
<Name>Device.X_IOWRT_EU_NetMode.SupportedModes.4.SupportedArguments.2.Value</Name>
<Value>direct:2501</Value>
<Value>direct:vlan:2501</Value>
</ParameterValueStruct>
<ParameterValueStruct>
<Name>Device.X_IOWRT_EU_NetMode.SupportedModes.4.SupportedArguments.3.Value</Name>
@ -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

View file

@ -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

View file

@ -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
```

View file

@ -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

View file

@ -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"
case "$vlan_type" in
vlan)
# Create VLAN device
local vlan_dev=$(create_vlan_device "$base_device" "$vlan_id" "8021q")
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}"