iopsys-feed/netmode/docs/IMPLEMENTATION_GUIDE.md

1038 lines
26 KiB
Markdown

# 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