# Netmode Implementation Guide

## Table of Contents
1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Directory Structure](#directory-structure)
4. [Configuration System](#configuration-system)
5. [Mode Implementation](#mode-implementation)
6. [Creating a New Network Mode](#creating-a-new-network-mode)
7. [Environment Variables](#environment-variables)
8. [Hooks and Scripts](#hooks-and-scripts)
9. [Data Model Integration](#data-model-integration)
10. [Testing and Validation](#testing-and-validation)

---

## Overview

The netmode package provides a flexible mechanism for switching between different WAN network configurations on OpenWrt/iopsys systems. It enables easy transitions between routing modes (DHCP, PPPoE, Static IP) and bridged mode without manual configuration changes.

### Key Features
- Dynamic WAN mode switching with reboot
- Support for routed and bridged modes
- UCI-based configuration
- TR-069/USP data model integration via bbfdm
- Extensible architecture for custom modes
- Environment variable-based parameter passing
- Pre/post mode switch hooks

### Version Information
- Package Version: 1.1.11
- License: GPL-2.0-only

---

## Architecture

### Core Components

```
┌─────────────────────────────────────────────────────────────┐
│                      Netmode System                          │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  ┌─────────────┐    ┌──────────────┐    ┌───────────────┐  │
│  │ UCI Config  │───▶│ Init Service │───▶│ Mode Scripts  │  │
│  │ /etc/config │    │   (START=11) │    │ /etc/netmodes │  │
│  │  /netmode   │    │              │    │     /<mode>   │  │
│  └─────────────┘    └──────────────┘    └───────────────┘  │
│         │                   │                     │          │
│         │                   ▼                     │          │
│         │         ┌──────────────────┐            │          │
│         └────────▶│ Env Var Manager  │◀───────────┘          │
│                   │  (NETMODE_*)     │                       │
│                   └──────────────────┘                       │
│                            │                                 │
│                            ▼                                 │
│                   ┌──────────────────┐                       │
│                   │  Hook Execution  │                       │
│                   │  pre/post/lib    │                       │
│                   └──────────────────┘                       │
│                            │                                 │
│                            ▼                                 │
│                   ┌──────────────────┐                       │
│                   │  UCI Reconfig    │                       │
│                   │  network/dhcp/   │                       │
│                   │  firewall/etc    │                       │
│                   └──────────────────┘                       │
└─────────────────────────────────────────────────────────────┘
```

### Execution Flow

1. **Trigger**: System boot or `service netmode restart`
2. **Configuration Load**: Read UCI netmode config
3. **Mode Detection**: Get mode from `netmode.global.mode` or `fw_printenv`
4. **Change Detection**: Compare with last saved mode in `/etc/netmodes/.last_mode`
5. **Environment Setup**: Configure `NETMODE_*` environment variables
6. **Pre-hooks**: Execute `/lib/netmode/pre/*` scripts
7. **UCI Copy**: Copy mode-specific UCI files from `/etc/netmodes/<mode>/uci/`
8. **Generic Scripts**: Execute `/lib/netmode/*` scripts
9. **Mode Scripts**: Execute `/etc/netmodes/<mode>/scripts/*` in order
10. **Last Mode Update**: Save current mode to `.last_mode`
11. **Post-hooks**: Execute `/lib/netmode/post/*` scripts
12. **Cleanup**: Unset environment variables and clear argument values

---

## Directory Structure

```
netmode/
├── Makefile                          # OpenWrt package definition
├── bbfdm_service.json                # BBF data model service registration
├── IMPLEMENTATION_GUIDE.md           # This document
├── DEVELOPER_GUIDE.md                # Developer documentation
├── USER_GUIDE.md                     # User documentation
└── files/
    ├── etc/
    │   ├── config/
    │   │   └── netmode               # UCI configuration file
    │   ├── init.d/
    │   │   └── netmode               # Procd init script (START=11)
    │   ├── uci-defaults/
    │   │   ├── 40_netmode_populated_supported_modes
    │   │   ├── 40_netmode_set_default_netmode
    │   │   └── 62-netmode.l2mode
    │   └── netmodes/
    │       ├── supported_modes.json  # Mode definitions for import
    │       ├── .last_mode            # Runtime: last applied mode
    │       ├── routed-dhcp/
    │       │   ├── scripts/
    │       │   │   └── 10-routed-dhcp
    │       │   └── uci/              # Optional: pre-configured UCI files
    │       ├── routed-pppoe/
    │       │   └── scripts/
    │       │       └── 10-routed-pppoe
    │       ├── routed-static/
    │       │   └── scripts/
    │       │       └── 10-routed-static
    │       └── bridged/
    │           └── scripts/
    │               └── 10-bridged
    └── lib/
        ├── upgrade/
        │   └── keep.d/
        │       └── netmode           # Files to keep during sysupgrade
        └── netmode/
            ├── pre/                  # Pre-mode-switch hooks
            ├── post/                 # Post-mode-switch hooks
            │   └── datamodel_init.sh # Reboot after mode switch
            └── [default]/            # Generic mode-switch hooks

```

---

## Configuration System

### UCI Configuration Format

**File**: `/etc/config/netmode`

```uci
config netmode 'global'
    option enabled '1'
    option mode 'routed-dhcp'

config supported_modes
    option name 'routed-dhcp'
    option description 'DHCP'

config supported_args
    option dm_parent '@supported_modes[0]'
    option name 'vlanid'
    option description 'VLAN ID'
    option required '0'
    option type 'integer'
    option value ''

config supported_args
    option dm_parent '@supported_modes[0]'
    option name 'dns_servers'
    option description 'DNS Servers'
    option required '0'
    option type 'string'
    option value ''

config supported_modes
    option name 'routed-pppoe'
    option description 'PPPoE'

config supported_args
    option dm_parent '@supported_modes[1]'
    option name 'username'
    option description 'PPPoE Username'
    option required '1'
    option type 'string'
    option value ''

config supported_args
    option dm_parent '@supported_modes[1]'
    option name 'password'
    option description 'PPPoE Password'
    option required '1'
    option type 'string'
    option value ''
```

### Supported Modes JSON Schema

**File**: `/etc/netmodes/supported_modes.json`

```json
{
  "supported_modes": [
    {
      "name": "routed-dhcp",
      "description": "DHCP",
      "supported_args": [
        {
          "name": "vlanid",
          "description": "VLAN ID",
          "required": false,
          "type": "integer"
        },
        {
          "name": "dns_servers",
          "description": "DNS Servers",
          "required": false,
          "type": "string"
        }
      ]
    }
  ]
}
```

### Configuration Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Unique identifier for the mode (must match directory name) |
| `description` | string | Human-readable mode description |
| `required` | boolean | If true, argument value must be provided |
| `type` | string | Data type: `integer`, `string`, `boolean` |
| `value` | string | Runtime value (set via TR-069/CLI) |
| `dm_parent` | reference | Links argument to parent mode section |

---

## Mode Implementation

### Standard Modes

#### 1. Routed DHCP Mode
- **Name**: `routed-dhcp`
- **Description**: Router with DHCP WAN
- **Configuration**:
  - LAN: Static IP (192.168.1.1/24)
  - WAN: DHCP client
  - WAN6: DHCPv6 client
  - Firewall: Enabled
  - DHCP Server: Enabled on LAN
  - Multicast: L3 proxy mode

#### 2. Routed PPPoE Mode
- **Name**: `routed-pppoe`
- **Description**: Router with PPPoE WAN
- **Required Arguments**: `username`, `password`
- **Optional Arguments**: `vlanid`, `mtu`, `dns_servers`
- **Configuration**:
  - LAN: Static IP (192.168.1.1/24)
  - WAN: PPPoE
  - Firewall: Enabled
  - DHCP Server: Enabled on LAN
  - Multicast: L3 proxy mode

#### 3. Routed Static Mode
- **Name**: `routed-static`
- **Description**: Router with Static IP WAN
- **Required Arguments**: `ipaddr`, `netmask`, `gateway`
- **Optional Arguments**: `vlanid`, `dns_servers`
- **Configuration**:
  - LAN: Static IP (192.168.1.1/24)
  - WAN: Static IP
  - Firewall: Enabled
  - DHCP Server: Enabled on LAN
  - Multicast: L3 proxy mode

#### 4. Bridged Mode
- **Name**: `bridged`
- **Description**: L2 Bridge Mode
- **Configuration**:
  - LAN: DHCP client (bridged to WAN)
  - WAN: Deleted (ports added to br-lan)
  - Firewall: Disabled
  - DHCP Server: Disabled
  - Multicast: L2 snooping mode

---

## Creating a New Network Mode

### Step-by-Step Guide

#### Step 1: Create Mode Directory Structure

```bash
mkdir -p /etc/netmodes/my-custom-mode/scripts
mkdir -p /etc/netmodes/my-custom-mode/uci
```

#### Step 2: Define Mode in supported_modes.json

Add to `/etc/netmodes/supported_modes.json`:

```json
{
  "supported_modes": [
    {
      "name": "my-custom-mode",
      "description": "My Custom WAN Mode",
      "supported_args": [
        {
          "name": "custom_param",
          "description": "Custom Parameter",
          "required": true,
          "type": "string"
        }
      ]
    }
  ]
}
```

#### Step 3: Import Mode to UCI

Run the uci-defaults script to populate UCI:

```bash
sh /etc/uci-defaults/40_netmode_populated_supported_modes
```

Or manually add to `/etc/config/netmode`:

```uci
config supported_modes
    option name 'my-custom-mode'
    option description 'My Custom WAN Mode'

config supported_args
    option dm_parent '@supported_modes[N]'  # Replace N with index
    option name 'custom_param'
    option description 'Custom Parameter'
    option required '1'
    option type 'string'
    option value ''
```

#### Step 4: Create Mode Script

**File**: `/etc/netmodes/my-custom-mode/scripts/10-my-custom-mode`

```bash
#!/bin/sh

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

source "/etc/device_info"

logger -s -p user.info -t "netmode" "Applying custom mode configuration"

# Access environment variables
custom_value="$NETMODE_custom_param"

# Get WAN device from board configuration
wandev="$(uci -q get network.WAN.ifname)"

# Configure network
uci -q set network.lan=interface
uci -q set network.lan.device='br-lan'
uci -q set network.lan.proto='static'
uci -q set network.lan.ipaddr='192.168.1.1'
uci -q set network.lan.netmask='255.255.255.0'

uci -q set network.wan=interface
uci -q set network.wan.device="$wandev"
uci -q set network.wan.proto='dhcp'
# Add your custom configuration here

uci -q commit network

# Configure other subsystems
uci -q set dhcp.lan.ignore=0
uci -q commit dhcp

uci -q set firewall.globals.enabled="1"
uci -q commit firewall

logger -s -p user.info -t "netmode" "Custom mode configuration complete"
```

Make it executable:

```bash
chmod +x /etc/netmodes/my-custom-mode/scripts/10-my-custom-mode
```

#### Step 5: (Optional) Pre-configure UCI Files

If you have complex UCI configurations, place them in:

```bash
/etc/netmodes/my-custom-mode/uci/network
/etc/netmodes/my-custom-mode/uci/dhcp
/etc/netmodes/my-custom-mode/uci/firewall
```

These will be copied to `/etc/config/` before mode scripts execute.

#### Step 6: Test the Mode

```bash
# Set the mode
uci set netmode.global.mode='my-custom-mode'

# Set required arguments (if any)
uci set netmode.@supported_args[N].value='test-value'
uci commit netmode

# Restart netmode service
service netmode restart

# Check logs
logread | grep netmode
```

---

## Environment Variables

### Variable Naming Convention

All mode arguments are exported as environment variables with the prefix `NETMODE_`:

```bash
NETMODE_<argument_name>="<value>"
```

### Examples

For PPPoE mode:
```bash
NETMODE_username="user@isp.com"
NETMODE_password="secret123"
NETMODE_vlanid="100"
NETMODE_mtu="1492"
NETMODE_dns_servers="8.8.8.8,8.8.4.4"
```

### Accessing in Scripts

```bash
#!/bin/sh

# Direct access
echo "Username: $NETMODE_username"
echo "VLAN ID: $NETMODE_vlanid"

# With validation
if [ -n "$NETMODE_vlanid" ] && [ "$NETMODE_vlanid" -ge 1 ]; then
    # Configure VLAN
    uci set network.wan_vlan.vid="$NETMODE_vlanid"
fi

# CSV parsing for lists
if [ -n "$NETMODE_dns_servers" ]; then
    dns_servers="$(echo $NETMODE_dns_servers | tr ',' ' ')"
    for server in $dns_servers; do
        uci add_list network.wan.dns="$server"
    done
fi
```

### Variable Lifecycle

1. **Set**: Before pre-hooks (if mode is being switched)
2. **Available**: During pre-hooks, generic scripts, and mode scripts
3. **Cleared**: After post-hooks via `cleanup_env_vars()`

### Security Considerations

- Password values marked as "Secure" in data model
- Environment variables are process-local
- Cleared after mode application
- UCI values are cleared after use (`cleanup_arg_values()`)

---

## Hooks and Scripts

### Hook Execution Order

```
1. /lib/netmode/pre/*          (Pre-mode-switch hooks)
   ↓
2. Copy /etc/netmodes/<mode>/uci/* → /etc/config/
   ↓
3. /lib/netmode/*               (Generic mode-switch scripts)
   ↓
4. /etc/netmodes/<mode>/scripts/*  (Mode-specific scripts)
   ↓
5. Save .last_mode
   ↓
6. /lib/netmode/post/*         (Post-mode-switch hooks)
   ↓
7. cleanup_env_vars()
```

### Script Naming Convention

Scripts are executed in **lexicographical order**. Use numeric prefixes:

```
10-first-script
20-second-script
30-third-script
```

### Pre-hooks (`/lib/netmode/pre/`)

**Purpose**: Prepare system before mode switch

**Example Use Cases**:
- Backup current configuration
- Stop conflicting services
- Validate prerequisites

**Example**:
```bash
#!/bin/sh
# /lib/netmode/pre/10-backup

logger -t "netmode" "Backing up current network config"
cp /etc/config/network /tmp/network.backup
```

### Post-hooks (`/lib/netmode/post/`)

**Purpose**: Finalize mode switch and cleanup

**Default Post-hook**: `datamodel_init.sh`
```bash
#!/bin/sh
# Reboot system after mode switch

if [ ! -f /var/run/boot_complete ]; then
    exit 0
fi

# Clear data model caches
rm -f /etc/bbfdm/dmmap/PPP
rm -f /etc/bbfdm/dmmap/IP
rm -f /etc/bbfdm/dmmap/Ethernet

sleep 5
reboot -f
```

**Example Additional Post-hook**:
```bash
#!/bin/sh
# /lib/netmode/post/20-notify

logger -t "netmode" "Mode switch complete, notifying ACS"
# Trigger TR-069 inform
```

### Generic Scripts (`/lib/netmode/`)

**Purpose**: Common operations for all modes

**Example**:
```bash
#!/bin/sh
# /lib/netmode/10-common-setup

logger -t "netmode" "Applying common network settings"

# Set common timezone
uci set system.@system[0].timezone='UTC'
uci commit system
```

---

## Data Model Integration

### BBF Data Model Structure

**TR-181 Path**: `Device.X_IOWRT_EU_NetMode.`

```
Device.X_IOWRT_EU_NetMode.
├── Enable                           (boolean, r/w)
├── Mode                             (string, r/w) [Reference]
├── SupportedModesNumberOfEntries    (unsignedInt, r)
└── SupportedModes.{i}.
    ├── Name                         (string, r) [Unique, Linker]
    ├── Description                  (string, r)
    ├── SupportedArgumentsNumberOfEntries (unsignedInt, r)
    └── SupportedArguments.{i}.
        ├── Name                     (string, r)
        ├── Type                     (string, r/w)
        ├── Description              (string, r)
        ├── Required                 (boolean, r)
        └── Value                    (string, r/w) [Secure]
```

### Data Model Operations

#### Get Current Mode
```xml
<GetParameterValues>
  <ParameterNames>
    <string>Device.X_IOWRT_EU_NetMode.Mode</string>
  </ParameterNames>
</GetParameterValues>
```

#### Set PPPoE Mode with Credentials
```xml
<SetParameterValues>
  <ParameterList>
    <ParameterValueStruct>
      <Name>Device.X_IOWRT_EU_NetMode.Mode</Name>
      <Value>routed-pppoe</Value>
    </ParameterValueStruct>
    <ParameterValueStruct>
      <Name>Device.X_IOWRT_EU_NetMode.SupportedModes.2.SupportedArguments.1.Value</Name>
      <Value>username@isp.com</Value>
    </ParameterValueStruct>
    <ParameterValueStruct>
      <Name>Device.X_IOWRT_EU_NetMode.SupportedModes.2.SupportedArguments.2.Value</Name>
      <Value>password123</Value>
    </ParameterValueStruct>
  </ParameterList>
</SetParameterValues>
```

#### Trigger Mode Switch
```bash
# After setting parameters via TR-069/USP
service netmode restart
# Or reload
service netmode reload
```

### BBF Service Configuration

**File**: `bbfdm_service.json`

```json
{
  "daemon": {
    "enable": "1",
    "service_name": "netmode",
    "unified_daemon": false,
    "services": [
      {
        "parent_dm": "Device.",
        "object": "{BBF_VENDOR_PREFIX}NetMode"
      }
    ],
    "config": {
      "loglevel": "3"
    }
  }
}
```

**Vendor Prefix Configuration** (Makefile):
```make
config NETMODE_VENDOR_PREFIX
    depends on PACKAGE_netmode
    string "Vendor Extension used for netmode datamodel"
    default ""
```

If empty, inherits from `CONFIG_BBF_VENDOR_PREFIX`.

---

## Testing and Validation

### Pre-deployment Checklist

- [ ] Mode directory created: `/etc/netmodes/<mode-name>/`
- [ ] Mode script created and executable: `scripts/10-<mode-name>`
- [ ] Mode defined in `supported_modes.json`
- [ ] UCI configuration populated (via uci-defaults)
- [ ] Required arguments identified and documented
- [ ] Environment variable handling implemented
- [ ] UCI subsystems configured (network, dhcp, firewall)
- [ ] Multicast configuration appropriate for mode
- [ ] Service dependencies updated (cwmp, gateway, ssdpd)

### Manual Testing Procedure

#### 1. Enable Debug Logging
```bash
logger -s -p user.info -t "netmode-test" "Starting mode test"
```

#### 2. Set Mode via UCI
```bash
uci set netmode.global.enabled='1'
uci set netmode.global.mode='<mode-name>'

# Set required arguments
uci set netmode.@supported_args[X].value='<value>'
uci commit netmode
```

#### 3. Trigger Mode Switch
```bash
service netmode restart
```

#### 4. Monitor Logs
```bash
logread -f | grep netmode
```

Expected output:
```
netmode: Switching to [<mode>] Mode
netmode: Executing /lib/netmode/pre scripts
netmode: Generating L3 network configuration
netmode: Executing [<mode>], script [10-<mode>]
netmode: Switching to Mode [<mode>] done, last mode updated
netmode: Executing /lib/netmode/post scripts
```

#### 5. Verify Configuration
```bash
# Check network configuration
uci show network | grep -E "lan|wan"

# Check DHCP configuration
uci show dhcp

# Check firewall status
uci show firewall.globals.enabled

# Check last mode
cat /etc/netmodes/.last_mode

# Verify environment cleanup
env | grep NETMODE
```

#### 6. Test Network Connectivity
```bash
# Ping gateway
ping -c 3 $(ip route | grep default | awk '{print $3}')

# DNS resolution
nslookup example.com

# Check WAN IP
ip addr show wan
```

### Automated Testing Script

```bash
#!/bin/sh
# test-netmode.sh

MODE="$1"
ARGS="$2"

if [ -z "$MODE" ]; then
    echo "Usage: $0 <mode> [args]"
    exit 1
fi

echo "Testing mode: $MODE"

# Set mode
uci set netmode.global.mode="$MODE"

# Parse and set arguments (format: "arg1=val1,arg2=val2")
if [ -n "$ARGS" ]; then
    IFS=',' read -ra ARGLIST <<< "$ARGS"
    for arg in "${ARGLIST[@]}"; do
        name="${arg%%=*}"
        value="${arg#*=}"

        # Find matching argument section
        idx=$(uci show netmode | grep "name='$name'" | cut -d. -f2 | cut -d= -f1)
        uci set "netmode.$idx.value=$value"
    done
fi

uci commit netmode

# Apply mode
echo "Applying mode..."
service netmode restart

# Wait for completion
sleep 2

# Verify
echo "Verifying configuration..."
cat /etc/netmodes/.last_mode

echo "Mode test complete"
```

Usage:
```bash
./test-netmode.sh routed-pppoe "username=test@isp.com,password=secret"
```

### Common Issues and Debugging

#### Issue: Mode not switching

**Symptoms**: `.last_mode` not updated

**Debug**:
```bash
# Check if enabled
uci get netmode.global.enabled

# Check if mode exists
ls /etc/netmodes/<mode>/scripts/

# Check script permissions
ls -la /etc/netmodes/<mode>/scripts/
```

**Solution**:
```bash
chmod +x /etc/netmodes/<mode>/scripts/*
```

#### Issue: Required arguments missing

**Symptoms**: Mode script exits early

**Debug**:
```bash
logread | grep "value.*missing"
```

**Solution**: Set all required argument values before switching mode.

#### Issue: Environment variables not set

**Symptoms**: Mode script cannot access parameters

**Debug**:
```bash
# Add to mode script:
env | grep NETMODE | logger -t netmode-debug
```

**Solution**: Verify `dm_parent` references in UCI configuration.

#### Issue: UCI configuration not applied

**Symptoms**: Network doesn't change after mode switch

**Debug**:
```bash
# Check if UCI commit succeeded
uci changes

# Verify UCI syntax
uci show network
```

**Solution**: Check for UCI syntax errors in mode script.

---

## Advanced Topics

### Custom Board Integration

If using custom hardware, update board.json parsing in mode scripts:

```bash
if [ -f /etc/board.json ]; then
    json_load_file /etc/board.json
    json_select network

    # Custom board handling
    json_select custom_wan 2>/dev/null
    json_get_var custom_dev device
    [ -n "$custom_dev" ] && wandev="$custom_dev"

    json_cleanup
fi
```

### VLAN Handling

All routed modes support VLAN tagging via `vlanid` argument:

```bash
if [ -n "$wandev" ] && echo "$NETMODE_vlanid" | grep -Eq '^[0-9]+$' && [ "$NETMODE_vlanid" -ge 1 ]; then
    vlandev="$wandev.$NETMODE_vlanid"
    vlandev_sec=$(echo $vlandev | tr '.' '_')

    uci -q set network.${vlandev_sec}=device
    uci -q set network.${vlandev_sec}.type="8021q"
    uci -q set network.${vlandev_sec}.name="$vlandev"
    uci -q set network.${vlandev_sec}.ifname="$wandev"
    uci -q set network.${vlandev_sec}.vid=$NETMODE_vlanid

    wandev="$vlandev"
fi
```

### Multicast Configuration

**L3 Modes** (routed-*): Use multicast proxy
```bash
l3_mcast_config() {
    logger -s -p user.info -t "netmode" "Generating L3 mcast configuration"
    rm -f /etc/config/mcast
    sh /rom/etc/uci-defaults/61-mcast_config_generate
    uci -q commit mcast
}
```

**L2 Mode** (bridged): Use IGMP/MLD snooping
```bash
l2_mcast_config() {
    # 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.interface='br-lan'

    # 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 commit mcast
}
```

### Preserving Configuration During Upgrade

**File**: `/lib/upgrade/keep.d/netmode`

```
/etc/config/netmode
/etc/netmodes/.last_mode
```

Add custom files:
```bash
echo "/etc/netmodes/custom-mode/custom.conf" >> /lib/upgrade/keep.d/netmode
```

---

## Best Practices

### Script Development

1. **Always source required libraries**:
   ```bash
   . /lib/functions.sh
   . /usr/share/libubox/jshn.sh
   ```

2. **Use logger for all output**:
   ```bash
   logger -s -p user.info -t "netmode" "Message"
   ```

3. **Validate environment variables**:
   ```bash
   if [ -z "$NETMODE_required_arg" ]; then
       logger -s -p user.err -t "netmode" "Missing required argument"
       exit 1
   fi
   ```

4. **Use `uci -q` for non-critical operations**:
   ```bash
   uci -q delete network.wan.old_option
   ```

5. **Always commit after changes**:
   ```bash
   uci -q commit network
   ```

### Security

1. **Clear sensitive environment variables**:
   - Automatic via `cleanup_env_vars()`
   - Mark sensitive parameters as "Secure" in data model

2. **Validate input**:
   ```bash
   # VLAN ID validation
   echo "$NETMODE_vlanid" | grep -Eq '^[0-9]+$' && [ "$NETMODE_vlanid" -ge 1 ]

   # IP address validation
   echo "$NETMODE_ipaddr" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$'
   ```

3. **Avoid command injection**:
   ```bash
   # BAD
   eval "uci set network.wan.ip=$NETMODE_ipaddr"

   # GOOD
   uci set network.wan.ip="$NETMODE_ipaddr"
   ```

### Performance

1. **Minimize reboots**: Use `service reload` when possible
2. **Batch UCI operations**: Commit once at the end
3. **Avoid redundant mode switches**: Compare with `.last_mode`

---

## References

- OpenWrt UCI Documentation: https://openwrt.org/docs/guide-user/base-system/uci
- Procd Init Scripts: https://openwrt.org/docs/guide-developer/procd-init-scripts
- BBF TR-181 Data Model: https://usp-data-models.broadband-forum.org/
- iopsys bbfdm: https://dev.iopsys.eu/iopsys/bbfdm

---

## Support and Contributing

For issues and contributions, please contact the iopsys development team.

**Package Maintainer**: iopsys
**License**: GPL-2.0-only
**Version**: 1.1.11
