iopsys-feed/netmode/docs/DEVELOPER_GUIDE.md

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