mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2026-03-14 21:10:11 +01:00
1220 lines
26 KiB
Markdown
1220 lines
26 KiB
Markdown
# Netmode Developer Guide
|
|
|
|
## Table of Contents
|
|
1. [Development Environment Setup](#development-environment-setup)
|
|
2. [Package Build System](#package-build-system)
|
|
3. [Code Organization](#code-organization)
|
|
4. [API Reference](#api-reference)
|
|
5. [Mode Script Development](#mode-script-development)
|
|
6. [UCI Integration](#uci-integration)
|
|
7. [Data Model Development](#data-model-development)
|
|
8. [Debugging Techniques](#debugging-techniques)
|
|
9. [Testing Framework](#testing-framework)
|
|
10. [Release Process](#release-process)
|
|
|
|
---
|
|
|
|
## Development Environment Setup
|
|
|
|
### Prerequisites
|
|
|
|
```bash
|
|
# OpenWrt build environment
|
|
sudo apt-get install build-essential libncurses5-dev gawk git \
|
|
subversion libssl-dev gettext unzip zlib1g-dev file python3
|
|
|
|
# Clone iopsys repository
|
|
git clone https://dev.iopsys.eu/iopsys/iopsyswrt.git
|
|
cd iopsyswrt
|
|
|
|
# Update feeds
|
|
./scripts/feeds update -a
|
|
./scripts/feeds install -a
|
|
```
|
|
|
|
### Building netmode Package
|
|
|
|
```bash
|
|
# Configure build
|
|
make menuconfig
|
|
# Navigate to: Utilities -> netmode
|
|
|
|
# Build package only
|
|
make package/feeds/iopsys/netmode/compile V=s
|
|
|
|
# Build with debug symbols
|
|
make package/feeds/iopsys/netmode/compile V=s CONFIG_DEBUG=y
|
|
```
|
|
|
|
### Installing to Device
|
|
|
|
```bash
|
|
# Copy to device
|
|
scp bin/packages/*/iopsys/netmode_*.ipk root@192.168.1.1:/tmp/
|
|
|
|
# Install on device
|
|
ssh root@192.168.1.1
|
|
opkg install /tmp/netmode_*.ipk
|
|
```
|
|
|
|
### Development Workflow
|
|
|
|
```bash
|
|
# Make changes to files in feeds/iopsys/netmode/files/
|
|
|
|
# Clean and rebuild
|
|
make package/feeds/iopsys/netmode/clean
|
|
make package/feeds/iopsys/netmode/compile V=s
|
|
|
|
# Test on device
|
|
scp bin/packages/*/iopsys/netmode_*.ipk root@192.168.1.1:/tmp/
|
|
ssh root@192.168.1.1 "opkg remove netmode; opkg install /tmp/netmode_*.ipk"
|
|
```
|
|
|
|
---
|
|
|
|
## Package Build System
|
|
|
|
### Makefile Analysis
|
|
|
|
```makefile
|
|
PKG_NAME:=netmode
|
|
PKG_VERSION:=1.1.11
|
|
PKG_RELEASE:=1
|
|
```
|
|
|
|
**Version Scheme**: `MAJOR.MINOR.PATCH`
|
|
- **MAJOR**: Breaking changes
|
|
- **MINOR**: New features, backward compatible
|
|
- **PATCH**: Bug fixes
|
|
|
|
### Build Configuration
|
|
|
|
```makefile
|
|
define Package/$(PKG_NAME)/config
|
|
config NETMODE_VENDOR_PREFIX
|
|
depends on PACKAGE_netmode
|
|
string "Vendor Extension used for netmode datamodel"
|
|
default ""
|
|
endef
|
|
```
|
|
|
|
**Usage in code**:
|
|
```json
|
|
"object": "{BBF_VENDOR_PREFIX}NetMode"
|
|
```
|
|
|
|
Replaced by `BBFDM_REGISTER_SERVICES` during build.
|
|
|
|
### Installation Targets
|
|
|
|
```makefile
|
|
define Package/netmode/install
|
|
$(INSTALL_DIR) $(1)/etc
|
|
$(INSTALL_DIR) $(1)/lib
|
|
$(CP) ./files/etc/* $(1)/etc/
|
|
$(CP) ./files/lib/* $(1)/lib/
|
|
$(BBFDM_REGISTER_SERVICES) -v ${VENDOR_PREFIX} ./bbfdm_service.json $(1) $(PKG_NAME)
|
|
$(BBFDM_INSTALL_MS_DM) -v ${VENDOR_PREFIX} ./files/datamodel.json $(1) $(PKG_NAME)
|
|
endef
|
|
```
|
|
|
|
**File Permissions**:
|
|
- Scripts in `/etc/init.d/`: `0755` (executable)
|
|
- UCI configs: `0644` (readable)
|
|
- Mode scripts: Must be made executable in postinst
|
|
|
|
---
|
|
|
|
## Code Organization
|
|
|
|
### Module Responsibilities
|
|
|
|
| Module | File | Responsibility |
|
|
|--------|------|----------------|
|
|
| **Init System** | `/etc/init.d/netmode` | Service lifecycle, mode switching orchestration |
|
|
| **Mode Scripts** | `/etc/netmodes/<mode>/scripts/*` | Mode-specific UCI configuration |
|
|
| **Pre-hooks** | `/lib/netmode/pre/*` | Pre-switch preparation |
|
|
| **Post-hooks** | `/lib/netmode/post/*` | Post-switch cleanup, reboot |
|
|
| **UCI Defaults** | `/etc/uci-defaults/*` | First-boot configuration |
|
|
| **Data Model** | `/files/datamodel.json` | BBF TR-181 mappings |
|
|
| **Service Def** | `bbfdm_service.json` | BBF service registration |
|
|
|
|
### Init Script Architecture
|
|
|
|
```
|
|
/etc/init.d/netmode
|
|
├── start_service() # Main entry point
|
|
│ ├── configure_env_vars() # Setup NETMODE_* vars
|
|
│ ├── libnetmode_exec("pre")
|
|
│ ├── Copy UCI files
|
|
│ ├── libnetmode_exec() # Generic scripts
|
|
│ ├── Execute mode scripts
|
|
│ ├── libnetmode_exec("post")
|
|
│ └── cleanup_env_vars()
|
|
├── service_triggers() # Procd reload trigger
|
|
└── Helper functions:
|
|
├── _log()
|
|
├── _get_modes_sec_name()
|
|
├── _set_env_args()
|
|
└── cleanup_arg_values()
|
|
```
|
|
|
|
---
|
|
|
|
## API Reference
|
|
|
|
### Init Script Functions
|
|
|
|
#### `start_service()`
|
|
|
|
Main service entry point called by procd.
|
|
|
|
**Execution Conditions**:
|
|
- `/etc/config/netmode` exists
|
|
- `netmode.global.enabled = 1`
|
|
- `netmode.global.mode` is set
|
|
- Current mode differs from `.last_mode`
|
|
|
|
**Side Effects**:
|
|
- Modifies UCI configuration
|
|
- Exports environment variables
|
|
- Writes `.last_mode` file
|
|
- May trigger system reboot (via post-hook)
|
|
|
|
#### `configure_env_vars(mode)`
|
|
|
|
Exports mode arguments as environment variables.
|
|
|
|
**Parameters**:
|
|
- `mode`: Mode name (e.g., "routed-pppoe")
|
|
|
|
**Behavior**:
|
|
1. Find UCI section matching mode name
|
|
2. Iterate `supported_args` with matching `dm_parent`
|
|
3. Export `NETMODE_<name>=<value>` if value is non-empty
|
|
4. Exit if required arguments are missing
|
|
|
|
**Example**:
|
|
```bash
|
|
configure_env_vars "routed-pppoe"
|
|
# Exports:
|
|
# NETMODE_username="user@isp.com"
|
|
# NETMODE_password="secret123"
|
|
```
|
|
|
|
#### `cleanup_env_vars(mode)`
|
|
|
|
Unsets NETMODE_* environment variables and clears UCI values.
|
|
|
|
**Parameters**:
|
|
- `mode`: Current mode name
|
|
|
|
**Behavior**:
|
|
1. Unset all `NETMODE_*` environment variables
|
|
2. Clear `value` field in UCI for all arguments of current mode
|
|
3. Commit UCI changes
|
|
|
|
**Security Note**: Prevents leaking credentials to subsequent processes.
|
|
|
|
#### `libnetmode_exec(when)`
|
|
|
|
Executes hook scripts from `/lib/netmode/<when>/`.
|
|
|
|
**Parameters**:
|
|
- `when`: Hook type ("pre", "post", or empty for default)
|
|
|
|
**Execution**:
|
|
- Scripts executed in lexicographical order
|
|
- Runs in subshell via `sh <script>`
|
|
- Logs execution to syslog
|
|
|
|
**Example**:
|
|
```bash
|
|
libnetmode_exec "pre"
|
|
# Executes: /lib/netmode/pre/10-backup
|
|
# /lib/netmode/pre/20-prepare
|
|
```
|
|
|
|
#### `_log(message)`
|
|
|
|
Logs message to syslog with netmode tag.
|
|
|
|
**Parameters**:
|
|
- `message`: Log message
|
|
|
|
**Implementation**:
|
|
```bash
|
|
logger -s -p user.info -t "netmode" "$*"
|
|
```
|
|
|
|
---
|
|
|
|
## Mode Script Development
|
|
|
|
### Script Template
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
#
|
|
# Mode: <mode-name>
|
|
# Description: <brief description>
|
|
# Required Arguments: <list>
|
|
# Optional Arguments: <list>
|
|
#
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Source device-specific info if available
|
|
[ -f /etc/device_info ] && source "/etc/device_info"
|
|
|
|
# Logging function
|
|
_log() {
|
|
logger -s -p user.info -t "netmode-<mode>" "$*"
|
|
}
|
|
|
|
# Main configuration function
|
|
configure_network() {
|
|
_log "Starting network configuration"
|
|
|
|
# Get WAN device
|
|
wandev="$(uci -q get network.WAN.ifname)"
|
|
[ -z "$wandev" ] && wandev="eth0" # Fallback
|
|
|
|
# Validate required arguments
|
|
if [ -z "$NETMODE_required_arg" ]; then
|
|
_log "ERROR: Missing required argument: required_arg"
|
|
return 1
|
|
fi
|
|
|
|
# Configure network interface
|
|
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'
|
|
|
|
# Mode-specific WAN configuration
|
|
uci -q set network.wan=interface
|
|
uci -q set network.wan.device="$wandev"
|
|
# Add mode-specific settings here
|
|
|
|
uci -q commit network
|
|
|
|
_log "Network configuration complete"
|
|
}
|
|
|
|
# Configure DHCP server
|
|
configure_dhcp() {
|
|
_log "Configuring DHCP"
|
|
|
|
uci -q set dhcp.lan.ignore=0
|
|
uci -q set dhcp.wan.ignore=1
|
|
uci -q commit dhcp
|
|
}
|
|
|
|
# Configure firewall
|
|
configure_firewall() {
|
|
_log "Configuring firewall"
|
|
|
|
uci -q set firewall.globals.enabled="1"
|
|
uci -q commit firewall
|
|
}
|
|
|
|
# Configure multicast
|
|
configure_multicast() {
|
|
_log "Configuring multicast"
|
|
|
|
# L3 or L2 multicast config
|
|
rm -f /etc/config/mcast
|
|
sh /rom/etc/uci-defaults/61-mcast_config_generate
|
|
uci -q commit mcast
|
|
}
|
|
|
|
# Update service dependencies
|
|
configure_services() {
|
|
_log "Updating service configurations"
|
|
|
|
# CWMP WAN interface
|
|
uci -q set cwmp.cpe.default_wan_interface="wan"
|
|
uci -q commit cwmp
|
|
|
|
# Gateway WAN interface
|
|
uci -q set gateway.global.wan_interface="wan"
|
|
uci -q commit gateway
|
|
|
|
# SSDPD
|
|
uci -q set ssdpd.ssdp.enabled="1"
|
|
uci -q commit ssdpd
|
|
}
|
|
|
|
# Main execution
|
|
configure_network || exit 1
|
|
configure_dhcp
|
|
configure_firewall
|
|
configure_multicast
|
|
configure_services
|
|
|
|
_log "Mode configuration complete"
|
|
exit 0
|
|
```
|
|
|
|
### Common Patterns
|
|
|
|
#### VLAN Configuration
|
|
|
|
```bash
|
|
configure_vlan() {
|
|
local wandev="$1"
|
|
|
|
# Delete existing VLANs
|
|
for vlandev_sec in $(uci show network | grep "type=.*8021q" | cut -d'.' -f1,2); do
|
|
uci -q delete $vlandev_sec
|
|
done
|
|
|
|
# Create new VLAN if vlanid provided
|
|
if [ -n "$wandev" ] && echo "$NETMODE_vlanid" | grep -Eq '^[0-9]+$' && [ "$NETMODE_vlanid" -ge 1 ]; then
|
|
local vlandev="$wandev.$NETMODE_vlanid"
|
|
local 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
|
|
|
|
echo "$vlandev"
|
|
else
|
|
echo "$wandev"
|
|
fi
|
|
}
|
|
|
|
# Usage
|
|
wandev="$(uci -q get network.WAN.ifname)"
|
|
wandev=$(configure_vlan "$wandev")
|
|
uci -q set network.wan.device="$wandev"
|
|
```
|
|
|
|
#### DNS Server Configuration
|
|
|
|
```bash
|
|
configure_dns() {
|
|
uci -q delete network.wan.dns
|
|
|
|
if [ -n "$NETMODE_dns_servers" ]; then
|
|
local dns_servers="$(echo $NETMODE_dns_servers | tr ',' ' ')"
|
|
for server in $dns_servers; do
|
|
# Validate IP address format
|
|
if echo "$server" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
|
uci add_list network.wan.dns="$server"
|
|
else
|
|
_log "WARNING: Invalid DNS server: $server"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
```
|
|
|
|
#### Bridge Port Configuration
|
|
|
|
```bash
|
|
configure_bridge_ports() {
|
|
uci -q delete network.br_lan.ports
|
|
uci -q set network.br_lan.bridge_empty='1'
|
|
|
|
add_port_to_br_lan() {
|
|
local port="$1"
|
|
[ -n "$port" -a -d /sys/class/net/$port ] || return
|
|
uci add_list network.br_lan.ports="$port"
|
|
_log "Added $port to br-lan"
|
|
}
|
|
|
|
if [ -f /etc/board.json ]; then
|
|
json_load_file /etc/board.json
|
|
json_select network
|
|
|
|
# LAN ports
|
|
json_select lan
|
|
if json_is_a ports array; then
|
|
json_for_each_item add_port_to_br_lan ports
|
|
else
|
|
json_get_var device device
|
|
[ -n "$device" ] && add_port_to_br_lan "$device"
|
|
fi
|
|
json_select ..
|
|
|
|
# WAN ports (for bridged mode)
|
|
if [ "$1" = "bridged" ]; then
|
|
json_select wan 2>/dev/null
|
|
json_get_var device device
|
|
[ -n "$device" ] && add_port_to_br_lan "$device"
|
|
fi
|
|
|
|
json_cleanup
|
|
fi
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```bash
|
|
# Validate critical operations
|
|
configure_network() {
|
|
if ! uci -q get network.WAN >/dev/null; then
|
|
_log "ERROR: WAN interface not found in board config"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$NETMODE_required_param" ]; then
|
|
_log "ERROR: Required parameter missing"
|
|
return 1
|
|
fi
|
|
|
|
# Perform configuration
|
|
uci -q set network.wan.setting="$NETMODE_required_param" || {
|
|
_log "ERROR: Failed to set UCI option"
|
|
return 1
|
|
}
|
|
|
|
uci -q commit network || {
|
|
_log "ERROR: Failed to commit network config"
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check return value
|
|
if ! configure_network; then
|
|
_log "Configuration failed, aborting mode switch"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
---
|
|
|
|
## UCI Integration
|
|
|
|
### Reading UCI Values
|
|
|
|
```bash
|
|
# Get single value
|
|
enabled=$(uci -q get netmode.global.enabled)
|
|
|
|
# Get with default
|
|
mode=$(uci -q get netmode.global.mode || echo "routed-dhcp")
|
|
|
|
# Check if option exists
|
|
if uci -q get network.wan >/dev/null; then
|
|
echo "WAN interface exists"
|
|
fi
|
|
|
|
# Iterate sections
|
|
config_load netmode
|
|
config_foreach handle_mode supported_modes
|
|
|
|
handle_mode() {
|
|
local section="$1"
|
|
local name
|
|
config_get name "$section" name
|
|
echo "Found mode: $name"
|
|
}
|
|
```
|
|
|
|
### Writing UCI Values
|
|
|
|
```bash
|
|
# Set option
|
|
uci set network.wan.proto='dhcp'
|
|
|
|
# Delete option
|
|
uci delete network.wan.username
|
|
|
|
# Delete section
|
|
uci delete network.wan6
|
|
|
|
# Add list item
|
|
uci add_list network.wan.dns='8.8.8.8'
|
|
|
|
# Delete all list items
|
|
uci delete network.wan.dns
|
|
|
|
# Always commit after changes
|
|
uci commit network
|
|
```
|
|
|
|
### UCI Section References
|
|
|
|
```bash
|
|
# Reference by index
|
|
uci set netmode.@supported_modes[0].name='routed-dhcp'
|
|
|
|
# Reference by name (if section is named)
|
|
uci set netmode.global.mode='routed-pppoe'
|
|
|
|
# Get section index
|
|
idx=$(uci show netmode | grep "name='routed-pppoe'" | cut -d. -f2 | cut -d= -f1)
|
|
uci set "netmode.$idx.description='PPPoE Mode'"
|
|
```
|
|
|
|
### Advanced UCI Operations
|
|
|
|
```bash
|
|
# Create anonymous section
|
|
uci add netmode supported_modes
|
|
|
|
# Rename section
|
|
uci rename netmode.@supported_modes[0]=pppoe_mode
|
|
|
|
# Reorder sections
|
|
uci reorder netmode.pppoe_mode=1
|
|
|
|
# Batch operations
|
|
uci batch <<EOF
|
|
set network.wan=interface
|
|
set network.wan.proto='dhcp'
|
|
set network.wan.device='eth0'
|
|
delete network.wan.ipaddr
|
|
commit network
|
|
EOF
|
|
```
|
|
|
|
---
|
|
|
|
## Data Model Development
|
|
|
|
### Adding New Parameters
|
|
|
|
#### 1. Update datamodel.json
|
|
|
|
```json
|
|
{
|
|
"Device.{BBF_VENDOR_PREFIX}NetMode.SupportedModes.{i}.": {
|
|
"NewParameter": {
|
|
"type": "string",
|
|
"read": true,
|
|
"write": true,
|
|
"description": "New parameter description",
|
|
"protocols": ["cwmp", "usp"],
|
|
"mapping": [
|
|
{
|
|
"type": "uci_sec",
|
|
"data": "@Parent",
|
|
"key": "new_param"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Update UCI Schema
|
|
|
|
Add to `/etc/config/netmode`:
|
|
|
|
```uci
|
|
config supported_modes
|
|
option name 'mode-name'
|
|
option new_param 'default-value'
|
|
```
|
|
|
|
#### 3. Handle in Mode Script
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
new_value="$NETMODE_new_param"
|
|
if [ -n "$new_value" ]; then
|
|
uci set some_config.section.option="$new_value"
|
|
fi
|
|
```
|
|
|
|
### Data Type Mappings
|
|
|
|
| BBF Type | UCI Type | Shell Validation |
|
|
|----------|----------|------------------|
|
|
| `string` | text | `[ -n "$var" ]` |
|
|
| `unsignedInt` | integer | `echo "$var" \| grep -Eq '^[0-9]+$'` |
|
|
| `boolean` | 0/1 | `[ "$var" = "1" ]` |
|
|
| `dateTime` | timestamp | `date -d "$var"` |
|
|
| `base64` | base64 | `echo "$var" \| base64 -d` |
|
|
|
|
### Mapping Types
|
|
|
|
#### `uci` Mapping (Static Section)
|
|
|
|
```json
|
|
{
|
|
"mapping": [
|
|
{
|
|
"type": "uci",
|
|
"uci": {
|
|
"file": "netmode",
|
|
"section": {
|
|
"name": "global"
|
|
},
|
|
"option": {
|
|
"name": "enabled"
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### `uci_sec` Mapping (Dynamic Section)
|
|
|
|
```json
|
|
{
|
|
"mapping": [
|
|
{
|
|
"type": "uci_sec",
|
|
"data": "@Parent",
|
|
"key": "name"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Special Flags
|
|
|
|
| Flag | Purpose | Example |
|
|
|------|---------|---------|
|
|
| `Linker` | Reference target | Mode name for linking |
|
|
| `Reference` | Reference source | Mode selection |
|
|
| `Unique` | Value must be unique | Mode names |
|
|
| `Secure` | Hidden from GET | Passwords |
|
|
|
|
---
|
|
|
|
## Debugging Techniques
|
|
|
|
### Enable Verbose Logging
|
|
|
|
```bash
|
|
# In mode script
|
|
set -x # Enable shell tracing
|
|
trap 'set +x' EXIT
|
|
|
|
# Or use logger extensively
|
|
_log() {
|
|
logger -s -p user.debug -t "netmode-debug" "$*"
|
|
}
|
|
```
|
|
|
|
### Live Log Monitoring
|
|
|
|
```bash
|
|
# Terminal 1: Monitor logs
|
|
logread -f | grep -E "netmode|network|firewall"
|
|
|
|
# Terminal 2: Trigger mode switch
|
|
service netmode restart
|
|
```
|
|
|
|
### Capture Environment State
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
# Add to mode script for debugging
|
|
|
|
DEBUG_DIR="/tmp/netmode-debug"
|
|
mkdir -p "$DEBUG_DIR"
|
|
|
|
# Save environment
|
|
env > "$DEBUG_DIR/env.txt"
|
|
|
|
# Save UCI state
|
|
uci export network > "$DEBUG_DIR/network.uci"
|
|
uci export netmode > "$DEBUG_DIR/netmode.uci"
|
|
|
|
# Save script output
|
|
exec 2>&1 | tee "$DEBUG_DIR/script-output.log"
|
|
```
|
|
|
|
### Interactive Testing
|
|
|
|
```bash
|
|
# Manually execute mode script
|
|
sh -x /etc/netmodes/routed-pppoe/scripts/10-routed-pppoe
|
|
|
|
# Set environment manually
|
|
export NETMODE_username="test"
|
|
export NETMODE_password="test123"
|
|
sh /etc/netmodes/routed-pppoe/scripts/10-routed-pppoe
|
|
|
|
# Check UCI changes without commit
|
|
uci changes
|
|
```
|
|
|
|
### Common Debug Commands
|
|
|
|
```bash
|
|
# Check last mode
|
|
cat /etc/netmodes/.last_mode
|
|
|
|
# Check netmode UCI
|
|
uci show netmode
|
|
|
|
# Check network UCI
|
|
uci show network | grep -E "lan|wan"
|
|
|
|
# Check process environment
|
|
cat /proc/$(pgrep netmode)/environ | tr '\0' '\n' | grep NETMODE
|
|
|
|
# Check procd service status
|
|
ubus call service list '{"name":"netmode"}'
|
|
```
|
|
|
|
### Procd Debugging
|
|
|
|
```bash
|
|
# Check procd service state
|
|
procd_status() {
|
|
ubus call service list '{"name":"netmode"}' | jsonfilter -e '@.netmode'
|
|
}
|
|
|
|
# Manual procd trigger
|
|
ubus call service event '{"type":"config.change","data":{"package":"netmode"}}'
|
|
|
|
# Check init script syntax
|
|
sh -n /etc/init.d/netmode
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Framework
|
|
|
|
### Unit Test Template
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
# test-mode-routed-dhcp.sh
|
|
|
|
. /lib/functions.sh
|
|
|
|
PASSED=0
|
|
FAILED=0
|
|
|
|
assert_equal() {
|
|
local expected="$1"
|
|
local actual="$2"
|
|
local test_name="$3"
|
|
|
|
if [ "$expected" = "$actual" ]; then
|
|
echo "✓ PASS: $test_name"
|
|
PASSED=$((PASSED + 1))
|
|
else
|
|
echo "✗ FAIL: $test_name"
|
|
echo " Expected: $expected"
|
|
echo " Actual: $actual"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
}
|
|
|
|
assert_exists() {
|
|
local path="$1"
|
|
local test_name="$2"
|
|
|
|
if [ -e "$path" ]; then
|
|
echo "✓ PASS: $test_name"
|
|
PASSED=$((PASSED + 1))
|
|
else
|
|
echo "✗ FAIL: $test_name (path not found: $path)"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
}
|
|
|
|
test_mode_applied() {
|
|
local expected_mode="$1"
|
|
local actual_mode=$(cat /etc/netmodes/.last_mode 2>/dev/null)
|
|
assert_equal "$expected_mode" "$actual_mode" "Mode applied"
|
|
}
|
|
|
|
test_wan_proto() {
|
|
local expected_proto="$1"
|
|
local actual_proto=$(uci -q get network.wan.proto)
|
|
assert_equal "$expected_proto" "$actual_proto" "WAN protocol"
|
|
}
|
|
|
|
test_dhcp_enabled() {
|
|
local expected="$1"
|
|
local actual=$(uci -q get dhcp.lan.ignore)
|
|
[ "$expected" = "enabled" ] && expected="0" || expected="1"
|
|
assert_equal "$expected" "$actual" "DHCP server state"
|
|
}
|
|
|
|
# Run tests
|
|
echo "Testing routed-dhcp mode..."
|
|
|
|
test_mode_applied "routed-dhcp"
|
|
test_wan_proto "dhcp"
|
|
test_dhcp_enabled "enabled"
|
|
assert_exists "/etc/config/network" "Network config exists"
|
|
|
|
echo ""
|
|
echo "Results: $PASSED passed, $FAILED failed"
|
|
[ $FAILED -eq 0 ] && exit 0 || exit 1
|
|
```
|
|
|
|
### Integration Test Script
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
# test-mode-switch.sh
|
|
|
|
MODE="$1"
|
|
[ -z "$MODE" ] && { echo "Usage: $0 <mode>"; exit 1; }
|
|
|
|
echo "=== Mode Switch Test: $MODE ==="
|
|
|
|
# Backup config
|
|
cp /etc/config/network /tmp/network.backup
|
|
cp /etc/config/netmode /tmp/netmode.backup
|
|
|
|
# Set mode
|
|
echo "Setting mode to $MODE..."
|
|
uci set netmode.global.mode="$MODE"
|
|
uci commit netmode
|
|
|
|
# Trigger switch
|
|
echo "Applying mode..."
|
|
service netmode restart
|
|
sleep 2
|
|
|
|
# Verify
|
|
echo "Verifying..."
|
|
LAST_MODE=$(cat /etc/netmodes/.last_mode)
|
|
|
|
if [ "$LAST_MODE" = "$MODE" ]; then
|
|
echo "✓ Mode switch successful"
|
|
|
|
# Run mode-specific tests
|
|
case "$MODE" in
|
|
routed-dhcp)
|
|
[ "$(uci -q get network.wan.proto)" = "dhcp" ] && echo "✓ WAN proto correct"
|
|
;;
|
|
routed-pppoe)
|
|
[ "$(uci -q get network.wan.proto)" = "pppoe" ] && echo "✓ WAN proto correct"
|
|
;;
|
|
bridged)
|
|
[ -z "$(uci -q get network.wan)" ] && echo "✓ WAN interface removed"
|
|
;;
|
|
esac
|
|
else
|
|
echo "✗ Mode switch failed"
|
|
echo " Expected: $MODE"
|
|
echo " Actual: $LAST_MODE"
|
|
fi
|
|
|
|
# Restore config
|
|
echo "Restoring config..."
|
|
cp /tmp/network.backup /etc/config/network
|
|
cp /tmp/netmode.backup /etc/config/netmode
|
|
uci commit
|
|
```
|
|
|
|
### Automated Test Suite
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
# run-all-tests.sh
|
|
|
|
TESTS_DIR="$(dirname $0)/tests"
|
|
RESULTS_DIR="/tmp/netmode-test-results"
|
|
|
|
mkdir -p "$RESULTS_DIR"
|
|
|
|
TOTAL=0
|
|
PASSED=0
|
|
FAILED=0
|
|
|
|
for test_script in "$TESTS_DIR"/test-*.sh; do
|
|
[ -f "$test_script" ] || continue
|
|
|
|
TOTAL=$((TOTAL + 1))
|
|
test_name=$(basename "$test_script" .sh)
|
|
|
|
echo "Running $test_name..."
|
|
if sh "$test_script" > "$RESULTS_DIR/$test_name.log" 2>&1; then
|
|
echo "✓ $test_name PASSED"
|
|
PASSED=$((PASSED + 1))
|
|
else
|
|
echo "✗ $test_name FAILED"
|
|
FAILED=$((FAILED + 1))
|
|
cat "$RESULTS_DIR/$test_name.log"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "=== Test Summary ==="
|
|
echo "Total: $TOTAL"
|
|
echo "Passed: $PASSED"
|
|
echo "Failed: $FAILED"
|
|
|
|
[ $FAILED -eq 0 ] && exit 0 || exit 1
|
|
```
|
|
|
|
---
|
|
|
|
## Release Process
|
|
|
|
### Version Bump Procedure
|
|
|
|
1. **Update version in Makefile**:
|
|
```makefile
|
|
PKG_VERSION:=1.2.0
|
|
PKG_RELEASE:=1
|
|
```
|
|
|
|
2. **Update changelog**:
|
|
```bash
|
|
# Create CHANGELOG.md if not exists
|
|
echo "## [1.2.0] - $(date +%Y-%m-%d)" >> CHANGELOG.md
|
|
echo "### Added" >> CHANGELOG.md
|
|
echo "- New feature description" >> CHANGELOG.md
|
|
```
|
|
|
|
3. **Test on target hardware**:
|
|
```bash
|
|
make package/feeds/iopsys/netmode/clean
|
|
make package/feeds/iopsys/netmode/compile V=s
|
|
# Deploy and test
|
|
```
|
|
|
|
4. **Commit changes**:
|
|
```bash
|
|
git add Makefile CHANGELOG.md
|
|
git commit -m "netmode: update to version 1.2.0"
|
|
```
|
|
|
|
5. **Tag release**:
|
|
```bash
|
|
git tag -a netmode-1.2.0 -m "Release version 1.2.0"
|
|
git push origin netmode-1.2.0
|
|
```
|
|
|
|
### Pre-release Checklist
|
|
|
|
- [ ] All tests passing
|
|
- [ ] Documentation updated
|
|
- [ ] CHANGELOG.md updated
|
|
- [ ] Version numbers bumped
|
|
- [ ] Tested on at least 2 different hardware platforms
|
|
- [ ] Backward compatibility verified
|
|
- [ ] Data model compatibility checked
|
|
- [ ] No hardcoded paths or device-specific code
|
|
- [ ] Logging is appropriate (not too verbose)
|
|
- [ ] UCI defaults handle upgrade scenarios
|
|
|
|
### Package Validation
|
|
|
|
```bash
|
|
# Check package structure
|
|
tar -tzf netmode_*.ipk
|
|
|
|
# Verify file permissions
|
|
tar -xzf netmode_*.ipk -O ./data.tar.gz | tar -tzv
|
|
|
|
# Check dependencies
|
|
opkg info netmode_*.ipk | grep Depends
|
|
|
|
# Validate UCI config syntax
|
|
tar -xzf netmode_*.ipk -O ./data.tar.gz | tar -xz ./etc/config/netmode
|
|
uci import netmode < netmode
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices Summary
|
|
|
|
### Code Quality
|
|
|
|
1. **Always use shellcheck**:
|
|
```bash
|
|
shellcheck /etc/init.d/netmode
|
|
shellcheck /etc/netmodes/*/scripts/*
|
|
```
|
|
|
|
2. **Use set -e for critical scripts**:
|
|
```bash
|
|
#!/bin/sh
|
|
set -e # Exit on error
|
|
```
|
|
|
|
3. **Quote all variables**:
|
|
```bash
|
|
# Good
|
|
uci set network.wan.device="$wandev"
|
|
|
|
# Bad
|
|
uci set network.wan.device=$wandev
|
|
```
|
|
|
|
4. **Check return values**:
|
|
```bash
|
|
if ! configure_network; then
|
|
logger -t netmode "Configuration failed"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### Performance Optimization
|
|
|
|
1. **Minimize UCI commits**:
|
|
```bash
|
|
# Bad: Multiple commits
|
|
uci set network.lan.ipaddr='192.168.1.1'
|
|
uci commit network
|
|
uci set network.lan.netmask='255.255.255.0'
|
|
uci commit network
|
|
|
|
# Good: Single commit
|
|
uci set network.lan.ipaddr='192.168.1.1'
|
|
uci set network.lan.netmask='255.255.255.0'
|
|
uci commit network
|
|
```
|
|
|
|
2. **Avoid unnecessary mode switches**:
|
|
```bash
|
|
# Check .last_mode before proceeding
|
|
[ "$mode" = "$(cat /etc/netmodes/.last_mode)" ] && return
|
|
```
|
|
|
|
3. **Use batch UCI operations**:
|
|
```bash
|
|
uci batch <<EOF
|
|
set network.wan=interface
|
|
set network.wan.proto='dhcp'
|
|
commit network
|
|
EOF
|
|
```
|
|
|
|
### Security Hardening
|
|
|
|
1. **Validate all input**:
|
|
```bash
|
|
# VLAN ID validation
|
|
if ! echo "$NETMODE_vlanid" | grep -Eq '^[0-9]+$'; then
|
|
logger -t netmode "Invalid VLAN ID"
|
|
return 1
|
|
fi
|
|
```
|
|
|
|
2. **Clear sensitive data**:
|
|
```bash
|
|
# Automatic via cleanup_env_vars()
|
|
# But also clear from UCI
|
|
uci set netmode.@supported_args[X].value=''
|
|
```
|
|
|
|
3. **Use secure file permissions**:
|
|
```bash
|
|
# Scripts should be 0755
|
|
# Configs should be 0644
|
|
# Never 0777
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Build Issues
|
|
|
|
**Issue**: Package fails to build
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Clean build
|
|
make package/feeds/iopsys/netmode/clean
|
|
rm -rf build_dir/target-*/netmode-*
|
|
|
|
# Rebuild with verbose output
|
|
make package/feeds/iopsys/netmode/compile V=s
|
|
```
|
|
|
|
**Issue**: Data model registration fails
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Check vendor prefix
|
|
grep BBF_VENDOR_PREFIX .config
|
|
|
|
# Verify bbfdm is installed
|
|
opkg list-installed | grep bbfdm
|
|
```
|
|
|
|
### Runtime Issues
|
|
|
|
**Issue**: Mode not switching
|
|
|
|
**Debug**:
|
|
```bash
|
|
# Check service status
|
|
service netmode status
|
|
|
|
# Check logs
|
|
logread | grep netmode
|
|
|
|
# Verify UCI
|
|
uci show netmode
|
|
```
|
|
|
|
**Issue**: Environment variables not set
|
|
|
|
**Debug**:
|
|
```bash
|
|
# Check dm_parent references
|
|
uci show netmode | grep dm_parent
|
|
|
|
# Verify argument values
|
|
uci show netmode | grep value
|
|
```
|
|
|
|
---
|
|
|
|
## Contributing Guidelines
|
|
|
|
### Code Style
|
|
|
|
- Use tabs for indentation (OpenWrt standard)
|
|
- Max line length: 120 characters
|
|
- Function names: `lowercase_with_underscores`
|
|
- Variables: `lowercase` or `UPPERCASE` for constants
|
|
|
|
### Commit Messages
|
|
|
|
```
|
|
netmode: brief description (max 50 chars)
|
|
|
|
Detailed explanation of changes (wrap at 72 chars).
|
|
- Bullet points for multiple changes
|
|
- Reference issue numbers if applicable
|
|
|
|
Signed-off-by: Your Name <email@example.com>
|
|
```
|
|
|
|
### Pull Request Process
|
|
|
|
1. Fork the repository
|
|
2. Create feature branch: `git checkout -b feature/my-feature`
|
|
3. Make changes and test thoroughly
|
|
4. Update documentation
|
|
5. Submit PR with clear description
|
|
6. Address review comments
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
- **OpenWrt Documentation**: https://openwrt.org/docs/
|
|
- **UCI Documentation**: https://openwrt.org/docs/guide-user/base-system/uci
|
|
- **Procd**: https://openwrt.org/docs/guide-developer/procd-init-scripts
|
|
- **Shell Style Guide**: https://google.github.io/styleguide/shellguide.html
|
|
- **iopsys Development**: https://dev.iopsys.eu/
|
|
|
|
---
|
|
|
|
**Maintainer**: iopsys Development Team
|
|
**License**: GPL-2.0-only
|
|
**Version**: 1.1.11
|