sysmngr/docs/guide/FirmwareUpgrade.md
2025-11-26 22:51:40 +01:00

1725 lines
49 KiB
Markdown

# Firmware Upgrade System - Complete Guide
## Table of Contents
1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Configuration Parameters](#configuration-parameters)
4. [Upgrade Methods](#upgrade-methods)
5. [Configuration Preservation](#configuration-preservation)
6. [Operator Configuration Guide](#operator-configuration-guide)
7. [Troubleshooting](#troubleshooting)
8. [API Reference](#api-reference)
---
## Overview
The iopsys firmware upgrade system provides a robust, dual-bank firmware management solution with flexible configuration preservation options. The system supports multiple upgrade methods and protocols while maintaining backward compatibility.
### Key Features
- **Dual-bank architecture** - Safe firmware upgrades with automatic fallback
- **Multiple upgrade protocols** - USP/TR-369 (via GUI or controller), TR-069/CWMP, DHCP onboarding, CLI
- **Selective configuration preservation** - Separate control for operator and user settings
- **Async operations** - Non-blocking upgrades with progress tracking
- **Scheduled activations** - Time-window based firmware activation
- **Auto-rollback** - Automatic fallback on boot failure
### System Components
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Upgrade Interfaces │
├──────────┬──────────────────────────┬──────────────────┬────────────────────┤
│ TR-069 │ USP/TR-369 │ DHCP │ fwbank CLI │
│ (CWMP) │ │ Onboarding │ │
│ │ │ │ │
│ icwmp │ ┌─────────┬─────────┐ │ udhcpc hooks │ /etc/sysmngr/ │
│ Agent │ │ Sulu │ dmcli │ │ │ fwbank │
│ │ │ GUI │ CLI │ │ │ │
│ │ └────┬────┴────┬────┘ │ │ │
│ │ │ │ │ │ │
│ │ └────┬────┘ │ │ │
│ │ │ │ │ │
│ │ ┌─────▼──────┐ │ │ │
│ │ │ OB-USPa │ │ │ │
│ │ │ Agent │ │ │ │
└────┬─────┴──────┴────┬───────┴──────┴────┬─────────────┴──────┬─────────────┘
│ │ │ │
├─────────────────┤ │ │
│ BBF Datamodel │ │ │
│ (sysmngr) │ │ │
│ Device.DeviceInfo │ │
│ .FirmwareImage.{i} │ │
│ Download()/Activate() │ │
└──────────┬──────┘ │ │
│ │ │
└────────────┬──────────────┴────────────────────┘
┌───────▼────────┐
│ fwbank │ (UBUS: fwbank.upgrade)
│ (Core logic) │
└───────┬────────┘
┌────────────────┼────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌─────▼─────┐
│ opconf │ │ sysupgrade │ │ ubus │
│ (config) │ │ (OpenWrt) │ │ (events) │
└───────────┘ └─────────────┘ └───────────┘
```
**Protocol Paths:**
- **TR-069/USP:** Use BBF datamodel → sysmngr → fwbank UBUS
- **DHCP/fwbank CLI:** Call fwbank UBUS directly (bypass BBF datamodel)
- **Sulu:** Web GUI using USP/TR-369 protocol via OB-USPa agent (WebSocket)
- **dmcli:** CLI using BBF datamodel via local UBUS or remote USP (MQTT/WebSocket)
---
## Architecture
### Dual-Bank System
The firmware is stored in two independent banks, allowing safe upgrades with automatic fallback.
```
┌──────────────────────────────────────────────────┐
│ Flash Memory Layout │
├────────────┬────────────┬─────────────────────────┤
│ Bank 1 │ Bank 2 │ /usr_data │
│ (Firmware) │ (Firmware) │ (Persistent Storage) │
├────────────┼────────────┼─────────────────────────┤
│ Active │ Inactive │ - opconf.json │
│ Boot │ Upgrade │ - userconf.json │
│ │ │ - userconf/keep.d/ │
└────────────┴────────────┴─────────────────────────┘
```
#### Bank States
Each bank can have one of the following states:
| State | Description | Can Upgrade | Can Activate |
|-------|-------------|-------------|--------------|
| **Active** | Currently running firmware | ❌ | N/A (already active) |
| **Boot** | Will boot on next reboot | ❌ | N/A (will boot) |
| **Upgrade** | Available for new firmware | ✅ | ✅ |
| **Invalid** | Corrupted or unavailable | ❌ | ❌ |
**Example Status:**
```json
{
"bank": [
{
"id": 1,
"fwver": "iop-v5.14.0",
"swver": "5.14.0",
"active": true,
"boot": true,
"upgrade": false,
"status": "Active"
},
{
"id": 2,
"fwver": "",
"swver": "",
"active": false,
"boot": false,
"upgrade": true,
"status": "Available"
}
]
}
```
### Core Components
#### 1. fwbank (Shell Script)
**Location:** `/etc/sysmngr/fwbank`
**Purpose:** Core firmware bank management
**Methods:**
- `dump` - Get bank information
- `upgrade` - Write firmware to bank
- `set_bootbank` - Set which bank boots next
- `copy_config` - Preserve configuration
**UBUS Interface:**
```bash
ubus call fwbank dump
ubus call fwbank upgrade '{"path": "/tmp/fw.bin", "bank": 2, "auto_activate": true, "keep_settings": true}'
ubus call fwbank set_bootbank '{"bank": 2}'
ubus call fwbank copy_config '{"keep_settings": true, "keep_opconf": true, "config_scope": "UserOnly"}'
```
#### 2. sysmngr (C Daemon)
**Location:** `/usr/sbin/sysmngr`
**Purpose:** BBF TR-181 datamodel implementation
**Key Files:**
- `src/fw_images.c` - FirmwareImage object implementation
- `src/fwbank.c` - fwbank integration
- `src/utils.c` - Download/validation utilities
**Datamodel Paths:**
```
Device.DeviceInfo.
├── ActiveFirmwareImage (Reference to active bank)
├── BootFirmwareImage (Reference to boot bank)
├── MaxNumberOfActivateTimeWindows (5)
└── FirmwareImage.{i}.
├── Alias (e.g., "cpe-1")
├── Name (Firmware name)
├── Version (Software version)
├── Available (true/false)
├── Status (Active/Available/Invalid)
├── Download() (Async operation)
└── Activate() (Async operation)
```
#### 3. opconf (Configuration Manager)
**Location:** `/usr/sbin/opconf_conf_handler`
**Purpose:** Manage operator and user configuration separation
**Storage:**
```
/usr_data/
├── opconf/
│ └── opconf.json # Operator configuration (ISP settings)
└── userconf/
├── userconf.json # User configuration (end-user settings)
└── keep.d/ # Backup of specific files
└── etc/config/
├── wifi
├── firewall
└── ...
```
---
## Configuration Parameters
### 1. keep_config (KeepConfig)
**Type:** Boolean (0/1 or "0"/"1")
**Default:** `1` (preserve settings)
**Purpose:** Master switch for configuration preservation
**Behavior:**
- `1` - Preserve configurations according to `config_scope`
- `0` - Factory reset, discard ALL configurations
**Usage:**
```json
{
"keep_settings": 1
}
```
or in BBF operations:
```json
{
"X_IOWRT_EU_KeepConfig": "1"
}
```
### 2. keep_opconf (KeepOpConf)
**Type:** Boolean (0/1 or "0"/"1")
**Default:** `1` (preserve operator config)
**Purpose:** Control operator configuration preservation
**Behavior:**
- `1` - Keep `/usr_data/opconf/opconf.json`
- `0` - Delete operator configuration (ISP reset)
**Only effective when:** `keep_config=1`
**Usage:**
```json
{
"keep_opconf": 1
}
```
or in BBF operations:
```json
{
"X_IOWRT_EU_KeepOpConf": "1"
}
```
**Operator Configuration Contains:**
- ACS/USP controller credentials
- WAN interface settings
- VLANs and firewall rules
- Management protocol settings
- Default WiFi credentials
### 3. config_scope (ConfigScope)
**Type:** String enum
**Default:** `"UserOnly"`
**Possible Values:**
- `"All"` - Full backup (sysupgrade.tgz + userconf.json)
- `"UserOnly"` - Selective backup (userconf.json only)
**Purpose:** Define granularity of configuration preservation
**Only effective when:** `keep_config=1`
**Usage:**
```json
{
"config_scope": "UserOnly"
}
```
or in BBF operations:
```json
{
"X_IOWRT_EU_ConfigScope": "UserOnly"
}
```
### Configuration Matrix
| keep_config | keep_opconf | config_scope | Result |
|-------------|-------------|--------------|--------|
| 0 | - | - | **Factory Reset** - All configs deleted |
| 1 | 0 | UserOnly | **User Only** - Operator config deleted, user settings preserved via userconf.json |
| 1 | 1 | UserOnly | **User+Operator (Lightweight)** - Both preserved, selective backup |
| 1 | 0 | All | **User+System** - Operator config deleted, full system backup + userconf.json |
| 1 | 1 | All | **Everything** - Full preservation, both userconf.json and sysupgrade.tgz created |
### User Configuration (userconf.json)
**Generated by:** `/usr/sbin/genuserconf`
**Applied on boot:** Via `/etc/init.d/opconf` init script
**Contains:**
- LAN network settings (IP, netmask, DHCP pool)
- UPnP enable/disable
- Firewall port forwarding rules
- User account passwords
- WiFi AP configuration (SSID, password, encryption)
- VoIP SIP client settings
- LED PWM settings
**Example:**
```json
{
"network": {
"lan": {
"ipaddr": "192.168.1.1",
"netmask": "255.255.255.0"
}
},
"wireless": {
"wifi0": {
"ssid": "MyHomeNetwork",
"key": "SecurePassword123"
}
}
}
```
### System Backup (sysupgrade.tgz)
**Created by:** `sysupgrade -b /tmp/sysupgrade.tgz`
**Copied by:** `platform_copy_config`
**Contains:** ALL UCI configs from `/etc/config/*`
**Size:** Typically 500KB - 2MB
**Only created when:** `config_scope="All"`
---
## Upgrade Methods
The firmware upgrade system supports **four upgrade methods**:
1. **USP/TR-369** - Modern BBF protocol (via Sulu GUI or external controller) → Uses BBF datamodel
2. **TR-069/CWMP** - Legacy ACS protocol → Uses BBF datamodel
3. **DHCP Onboarding** - Zero-touch provisioning → Direct UBUS to fwbank
4. **CLI** - Direct command-line access → Direct UBUS to fwbank
> **Important Notes:**
> - **Sulu and dmcli** are clients that use **USP/TR-369 protocol** to access the BBF datamodel. They are not separate upgrade methods but rather different interfaces for USP operations.
> - **Sulu:** Browser-based GUI with WebSocket to local OB-USPa agent
> - **dmcli:** Command-line interface that can connect locally (UBUS) or remotely (USP)
> - **DHCP and fwbank CLI** bypass the BBF datamodel layer and call fwbank UBUS methods directly, providing lower-level access but with limited protocol features (no async operations, no transfer complete events).
### Method 1: USP/TR-369 (Recommended)
**Protocol:** BBF USP (User Services Platform) / TR-369
**Agent:** OB-USPa (Open Broadband - USP Agent)
**Clients:**
- **Sulu Web GUI** - Browser-based interface (WebSocket connection to OB-USPa)
- **dmcli** - Command-line interface for BBF datamodel (local UBUS or remote USP)
- **External USP Controller** - Remote management platform (STOMP/MQTT/WebSocket)
**Configuration:** Per-operation parameters (highest flexibility)
**Datamodel:** `Device.DeviceInfo.FirmwareImage.{i}.Download()` / `Activate()`
#### USP Client 1: Sulu Web GUI
**Interface:** React-based web application
**Communication:** WebSocket to local OB-USPa agent
**Configuration Control:**
- **File Upload Mode:** `KeepConfig` toggle available
- **URL Download Mode:** Uses system defaults (no config control in GUI)
**GUI Configuration Options:**
| Feature | File Upload | URL Download |
|---------|-------------|--------------|
| KeepConfig | ✅ User toggle | ❌ System default |
| KeepOpConf | ❌ System default | ❌ System default |
| ConfigScope | ❌ System default | ❌ System default |
| Authentication | ❌ None | ✅ Username/Password |
| Progress Tracking | ✅ Upload + Install | ❌ No |
> **Note:** The Sulu GUI currently only exposes the `KeepConfig` toggle in file upload mode. To control `KeepOpConf` and `ConfigScope`, operators must use system defaults (configured in firmware) or use an external USP controller with full parameter support.
##### Example: Sulu GUI Firmware Upload
```javascript
// Sulu GUI uploads firmware file in chunks
await uploadFileChunked({
Filename: "firmware.itb",
Content: "<base64-encoded-firmware>",
Filesize: "52428800",
Checksum: "a3f5e1d2..."
});
// Trigger upgrade with config control
await downloadToDevice({
URL: "file:///tmp/obuspa/firmware.itb",
X_IOWRT_EU_KeepConfig: "1", // User toggle
X_IOWRT_EU_KeepOpConf: "1", // Default
X_IOWRT_EU_ConfigScope: "UserOnly", // Default
AutoActivate: "1"
});
```
#### USP Client 2: External USP Controller
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Download()",
"input": {
"URL": "https://firmware.isp.com/cpe/v5.15.0.itb",
"Username": "fwuser",
"Password": "secret123",
"FileSize": "52428800",
"CheckSumAlgorithm": "SHA-256",
"CheckSum": "a3f5e1d2c4b6...",
"AutoActivate": "1",
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "0",
"X_IOWRT_EU_ConfigScope": "UserOnly"
}
}
```
**Result:** Operator config reset, user WiFi/network settings preserved
#### USP Client 3: dmcli (Data Model CLI)
**Interface:** Interactive command-line interface for BBF data models
**Modes:**
- **Local Mode:** Run directly on device via SSH/console (uses local UBUS to sysmngr)
- **Remote Mode:** Run from computer and connect via USP (MQTT/WebSocket to OB-USPa)
**Configuration:** Full control via operation parameters
**Installation:**
```bash
# On device (pre-installed)
ssh operator@192.168.1.1
dmcli
# On host computer (requires NodeJS)
npm install -g @gnx/dmcli@latest
```
**Remote Connection Setup:**
```bash
# Configure USP connection profile
dmcli
# set Settings.USP.ActiveConnectionProfile iowrt-gnx-operator
# do Settings.USP.Connect()
```
##### Example: Firmware Upgrade via dmcli
**Interactive Mode:**
```bash
# Start dmcli
dmcli
# Navigate to firmware object
# cd Device.DeviceInfo.FirmwareImage.2
# Check current status
# get Status Version Name
# Download firmware
# do Download() URL=https://firmware.isp.com/v5.15.0.itb AutoActivate=1 X_IOWRT_EU_KeepConfig=1 X_IOWRT_EU_KeepOpConf=0 X_IOWRT_EU_ConfigScope=UserOnly
# For async operations, use -n flag to not wait
# do -n Download() URL=https://firmware.isp.com/v5.15.0.itb AutoActivate=1
```
**Non-Interactive Mode:**
```bash
# Single command execution
dmcli do Device.DeviceInfo.FirmwareImage.2.Download() \
URL=https://firmware.isp.com/v5.15.0.itb \
AutoActivate=1 \
X_IOWRT_EU_KeepConfig=1 \
X_IOWRT_EU_KeepOpConf=0 \
X_IOWRT_EU_ConfigScope=UserOnly
# Activate previously downloaded firmware
dmcli do Device.DeviceInfo.FirmwareImage.2.Activate() \
X_IOWRT_EU_KeepConfig=1 \
X_IOWRT_EU_KeepOpConf=1 \
X_IOWRT_EU_ConfigScope=All
```
**Configuration Control:**
| Feature | dmcli Support |
|---------|---------------|
| KeepConfig | ✅ Full control via parameter |
| KeepOpConf | ✅ Full control via parameter |
| ConfigScope | ✅ Full control via parameter |
| Authentication | ✅ HTTP auth in URL parameter |
| Progress Tracking | ✅ Async mode with `-n` flag |
| Auto-completion | ✅ Tab completion for paths and parameters |
**Advantages:**
- Full access to BBF datamodel with tab completion
- Can be scripted for automation
- Supports both local and remote operation
- Comprehensive parameter control
- Interactive exploration of data model
#### Example: Scheduled Activation
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Activate()",
"input": {
"TimeWindow.1.Start": "3600", // 1 hour from now
"TimeWindow.1.End": "7200", // 2 hours from now
"TimeWindow.1.Mode": "AnyTime",
"TimeWindow.1.MaxRetries": "3",
"TimeWindow.2.Start": "86400", // 24 hours from now
"TimeWindow.2.End": "90000", // 25 hours from now
"TimeWindow.2.Mode": "Immediately",
"TimeWindow.2.MaxRetries": "-1",
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "1",
"X_IOWRT_EU_ConfigScope": "All"
}
}
```
**Cron Jobs Created:**
```cron
# Window 1: Between 14:00-16:00 today, up to 3 retries
0 14 26 11 * sh /usr/share/bbfdm/scripts/bbf_activate_handler.sh 'AnyTime' '2' '7200' 'false' '3' '1' '1' 'All' ''
# Window 2: Between 14:00-15:00 tomorrow, infinite retries
0 14 27 11 * sh /usr/share/bbfdm/scripts/bbf_activate_handler.sh 'Immediately' '2' '3600' 'true' '-1' '1' '1' 'All' ''
```
### Method 2: TR-069/CWMP
**Controller:** ACS (Auto Configuration Server)
**Configuration:** UCI defaults in `/etc/config/cwmp`
**RPC:** `Download(1 Firmware Upgrade Image)`
#### UCI Configuration
**File:** `/etc/config/cwmp`
```uci
config cpe 'cpe'
option acs_url 'https://acs.isp.com/cwmp'
option acs_user 'cpe-user'
option acs_passwd 'secret'
# Firmware upgrade config defaults
option KeepConfig '1'
option KeepOpConf '1'
option ConfigScope 'UserOnly'
```
#### ACS Download RPC Flow
1. **ACS sends Download RPC:**
```xml
<Download>
<CommandKey>fw-upgrade-20251126</CommandKey>
<FileType>1 Firmware Upgrade Image</FileType>
<URL>https://firmware.isp.com/cpe/v5.15.0.itb</URL>
<Username>fwuser</Username>
<Password>secret123</Password>
<FileSize>52428800</FileSize>
</Download>
```
2. **icwmp processes download:**
```c
// src/download.c:445-497
cwmp_apply_multiple_firmware(true) {
// Read config from UCI
keep_config = cwmp_ctx.conf.cpe_keep_config;
keep_opconf = cwmp_ctx.conf.cpe_keep_opconf;
config_scope = cwmp_ctx.conf.cpe_config_scope;
// Call BBF datamodel via UBUS
ubus_call("bbfdm", "operate", {
"path": "Device.DeviceInfo.FirmwareImage.2.Download()",
"input": {
"URL": "file:///tmp/firmware.img",
"AutoActivate": true,
"X_IOWRT_EU_KeepConfig": keep_config,
"X_IOWRT_EU_KeepOpConf": keep_opconf,
"X_IOWRT_EU_ConfigScope": config_scope
}
});
}
```
3. **CPE sends TransferComplete event back to ACS**
#### Dynamic Configuration Override
To change config behavior before upgrade:
```bash
# ISP wants to reset operator config on next upgrade
ubus call cwmp set '{"cpe.KeepOpConf": "0"}'
uci set cwmp.cpe.KeepOpConf='0'
uci commit cwmp
# Then ACS initiates Download RPC
```
### Method 3: DHCP Onboarding
**Protocol:** Direct UBUS call to fwbank (does NOT use USP/TR-369 or BBF datamodel)
**Server:** DHCP server (ISP infrastructure)
**Client:** udhcpc hooks (`/etc/udhcpc.user.d/dhcp-on-boarding`)
**Configuration:** `reset_mode` flag only (limited control)
**DHCP Options:** Option 125 (sub-option 2) or Option 224
#### DHCP Option Format
**Option 125 (Vendor-Specific):**
```
Enterprise-ID: 25167 (Genexis)
Sub-option 2: Firmware upgrade URL
Hex format:
00006250 # Genexis Enterprise ID
14 # Length
02 # Sub-option code (firmware)
12 # URL length
687474703a2f2f... # http://192.168.1.5/fw.bin
```
**Option 224 (Legacy):**
```
Raw hex-encoded URL:
687474703a2f2f3139322e3136382e312e352f66772e62696e
```
#### URL Syntax
**Immediate upgrade, keep config:**
```
http://server.com/firmware.bin
```
**Immediate upgrade, factory reset:**
```
http://server.com/firmware.bin,reset_mode=full
```
**Scheduled upgrade:**
```
http://server.com/firmware.bin,20251215,02,4
# Upgrade on Dec 15, 2025, random time between 02:00-06:00
```
**Scheduled with reset:**
```
http://server.com/firmware.bin,reset_mode=full,14,2
# Today at random time between 14:00-16:00, factory reset
```
#### Configuration Mapping
```bash
# In dhcp_upgrade_handling.sh
if [ "$reset_mode" = "full" ]; then
keep_settings="0"
else
keep_settings="1"
fi
# Call fwbank
ubus call fwbank upgrade '{
"path": "/tmp/firmware.bin",
"bank": 2,
"auto_activate": true,
"reboot": true,
"keep_settings": '$keep_settings'
# Note: keep_opconf and config_scope NOT passed, use system defaults
}'
```
**Limitation:** Cannot control `keep_opconf` or `config_scope` via DHCP
**Workaround:** Pre-configure defaults in firmware
```uci
# /rom/etc/config/cwmp (in firmware image)
config cpe 'cpe'
option KeepConfig '1'
option KeepOpConf '1'
option ConfigScope 'UserOnly'
```
#### DHCP Server Example (Kea)
```json
{
"subnet4": [{
"subnet": "192.168.100.0/24",
"pools": [{"pool": "192.168.100.10 - 192.168.100.250"}],
"option-data": [
{
"name": "vendor-encapsulated-options",
"code": 125,
"data": "00006250 1E 02 1C 687474703a2f2f3139322e3136382e3130302e312f66772e69746220",
"always-send": true
}
]
}]
}
```
Decodes to: `http://192.168.100.1/fw.itb`
### Method 4: CLI (fwbank)
**Protocol:** Direct UBUS call to fwbank (does NOT use USP/TR-369 or BBF datamodel)
**Interface:** Direct shell script invocation
**Script:** `/etc/sysmngr/fwbank`
**Configuration:** Full control via parameters
**Usage:**
```bash
# Dump bank information
/etc/sysmngr/fwbank call dump
# Upgrade with full config control
/etc/sysmngr/fwbank call upgrade '{
"path": "/tmp/firmware.itb",
"bank": 2,
"auto_activate": true,
"reboot": false,
"keep_settings": true,
"keep_opconf": false,
"config_scope": "UserOnly"
}'
# Copy config separately
/etc/sysmngr/fwbank call copy_config '{
"keep_settings": true,
"keep_opconf": true,
"config_scope": "All"
}'
# Set boot bank
/etc/sysmngr/fwbank call set_bootbank '{"bank": 2}'
# Manual reboot
reboot
```
---
## Configuration Preservation
### Process Flow
```
┌────────────────────────────────────────────────────────────┐
│ Upgrade Initiated │
└────────────────────┬───────────────────────────────────────┘
┌───────────────────────┐
│ Parameters Received │
│ - keep_settings │
│ - keep_opconf │
│ - config_scope │
└───────────┬───────────┘
┌──────────────┐
│ keep_config │
│ = 1? │
└──┬───────┬───┘
│ │
NO ──┘ └── YES
│ │
▼ ▼
┌────────────┐ ┌──────────────────┐
│ Skip all │ │ opconf_conf_ │
│ config │ │ handler │
│ handling │ └────────┬─────────┘
└─────┬──────┘ │
│ ▼
│ ┌──────────────────┐
│ │ keep_opconf = 0?│
│ └────┬──────┬──────┘
│ │ │
│ YES ─┘ └─ NO
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Delete │ │
│ │ opconf.json │ │
│ └──────┬──────┘ │
│ │ │
│ └─────┬───────┘
│ │
│ ▼
│ ┌──────────────────────┐
│ │ config_scope = │
│ │ "All" / "UserOnly"? │
│ └────┬────────────┬────┘
│ │ │
│ All ─┘ └─ UserOnly
│ │ │
│ ▼ ▼
│ ┌──────────┐ ┌─────────────┐
│ │ Generate │ │ Generate │
│ │userconf +│ │ userconf. │
│ │sysupgrade│ │ json only │
│ │ .tgz │ └──────┬──────┘
│ └────┬─────┘ │
│ └─────────┬─────────┘
│ │
└────────┬────────┘
┌──────────────┐
│ sysupgrade │
│ executes │
└──────┬───────┘
┌──────────────┐
│ Reboot │
└──────┬───────┘
┌────────────────┐
│ Apply opconf │
│ (if exists) │
└────────┬───────┘
┌────────────────┐
│ Apply userconf │
│ (if exists) │
└────────┬───────┘
┌────────────────┐
│ Delete userconf│
│ .json │
└────────────────┘
```
### opconf_conf_handler Details
**Location:** `/usr/sbin/opconf_conf_handler`
**Called by:** fwbank script before sysupgrade
**Parameters:**
```bash
opconf_conf_handler -k <keep_config> -o <keep_opconf> -s <config_scope>
```
**Logic:**
```bash
#!/bin/sh
KEEP_CONFIG="$1" # 0 or 1
KEEP_OPCONF="$2" # 0 or 1
CONFIG_SCOPE="$3" # "All" or "UserOnly"
# Step 1: Handle opconf.json
if [ "$KEEP_OPCONF" -eq "0" ]; then
rm -f /usr_data/opconf/opconf.json
fi
# Step 2: Handle userconf
if [ "$KEEP_CONFIG" -eq "0" ]; then
rm -f /usr_data/userconf/userconf.json
rm -rf /usr_data/userconf/keep.d/
else
if [ "$CONFIG_SCOPE" = "All" ] || [ "$CONFIG_SCOPE" = "UserOnly" ]; then
# Generate userconf.json from current system state
genuserconf > /usr_data/userconf/userconf.json
# Backup specific config files
copy_config_over_upgrade
fi
fi
```
### genuserconf Script
**Purpose:** Extract user-configurable settings into JSON
**Source Files:**
```bash
/etc/config/network → LAN settings
/etc/config/wireless → WiFi settings
/etc/config/firewall → Port forwarding
/etc/config/upnpd → UPnP state
/etc/config/voice → VoIP settings
/etc/config/system → LED settings
```
**Example Output:**
```json
{
"network": {
"lan": {
"proto": "static",
"ipaddr": "192.168.100.1",
"netmask": "255.255.255.0",
"ip6assign": "60"
},
"lan_dhcp": {
"start": "100",
"limit": "150",
"leasetime": "12h"
}
},
"wireless": {
"radio0_ap0": {
"ssid": "MyNetwork",
"encryption": "psk2",
"key": "MyPassword123"
}
},
"firewall": {
"portforward_ssh": {
"src": "wan",
"dest": "lan",
"proto": "tcp",
"src_dport": "2222",
"dest_ip": "192.168.100.50",
"dest_port": "22"
}
}
}
```
### On-Boot Application
**Init Script:** `/etc/init.d/opconf`
**Boot Sequence:**
```bash
#!/bin/sh /etc/rc.common
START=19 # Early boot, before network
start() {
# Check if /usr_data is mounted
if ! mountpoint -q /usr_data; then
return 0
fi
# Step 1: Apply opconf.json (if exists)
if [ -f /usr_data/opconf/opconf.json ]; then
opconf_apply /usr_data/opconf/opconf.json
# Note: opconf.json is NOT deleted, persists across reboots
fi
# Step 2: Apply userconf.json (if exists)
if [ -f /usr_data/userconf/userconf.json ]; then
userconf_apply /usr_data/userconf/userconf.json
# Delete after applying (one-time use)
rm -f /usr_data/userconf/userconf.json
fi
# Step 3: Restore files from keep.d/
if [ -d /usr_data/userconf/keep.d ]; then
cp -a /usr_data/userconf/keep.d/* /
fi
}
```
---
## Operator Configuration Guide
### Scenario 1: ISP wants full control over CPE, no user customization preserved
**Method:** TR-069 or USP Controller
**Configuration:**
**/etc/config/cwmp (firmware defaults):**
```uci
config cpe 'cpe'
option KeepConfig '0'
# KeepOpConf and ConfigScope ignored when KeepConfig=0
```
**Result:** Every firmware upgrade = factory reset
**Use Case:** Managed CPE, ISP owns all settings
### Scenario 2: ISP wants to preserve user WiFi passwords but reset ISP settings
**Method:** USP Controller (per-upgrade control)
**Download RPC:**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Download()",
"input": {
"URL": "https://firmware.isp.com/v5.15.0.itb",
"AutoActivate": "1",
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "0",
"X_IOWRT_EU_ConfigScope": "UserOnly"
}
}
```
**Result:**
- ✅ User WiFi SSID/password preserved
- ✅ User LAN IP preserved
- ✅ User port forwards preserved
- ❌ ACS URL reset to firmware default
- ❌ WAN settings reset to firmware default
- ❌ VLAN settings reset to firmware default
**Use Case:** ISP changing backend infrastructure, wants fresh provisioning
### Scenario 3: Home user upgrading via GUI, wants to keep everything
**Method:** USP/TR-369 (Sulu Web GUI)
**User Action:**
1. Navigate to System → Firmware
2. Upload firmware.itb
3. ✅ Enable "Keep Settings" toggle
4. Click "Start Upgrade"
**Sulu sends USP Download() operation:**
```json
{
"URL": "file:///tmp/obuspa/firmware.itb",
"X_IOWRT_EU_KeepConfig": "1",
"AutoActivate": "1"
}
```
**Result:**
- ✅ All settings preserved (defaults to keep_opconf=1, config_scope="UserOnly")
- ✅ WiFi, LAN, port forwards preserved
- ✅ ACS/ISP settings preserved
**Limitation:** Current Sulu GUI cannot selectively reset operator config. To control `KeepOpConf` and `ConfigScope`, use an external USP controller or configure system defaults in firmware.
### Scenario 4: DHCP onboarding in new deployment, zero-touch provisioning
**Method:** DHCP Option 125
**DHCP Server sends:**
```
Option 125: http://bootstrap.isp.com/fw-v5.14.0.itb,reset_mode=full
Option 125 Sub-3: http://bootstrap.isp.com/config-$MAC$.uci.enc
```
**CPE boots with factory firmware:**
1. Gets DHCP lease
2. Downloads firmware → factory reset (reset_mode=full)
3. Downloads encrypted config → applies ISP settings
4. Reboots into new firmware with ISP provisioning
**Configuration:** Entirely driven by DHCP, no pre-config needed
### Scenario 5: Scheduled maintenance window upgrade
**Method:** USP Controller with Activate()
**Controller sends Download() first:**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Download()",
"input": {
"URL": "https://firmware.isp.com/v5.15.0.itb",
"AutoActivate": "0" // Don't activate yet
}
}
```
**Then sends Activate() with time windows:**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Activate()",
"input": {
"TimeWindow.1.Start": "3600", // 1 hour from now
"TimeWindow.1.End": "10800", // 3 hours from now
"TimeWindow.1.Mode": "WhenIdle",
"TimeWindow.1.MaxRetries": "5",
"TimeWindow.2.Start": "86400", // 24 hours (fallback)
"TimeWindow.2.End": "93600",
"TimeWindow.2.Mode": "Immediately",
"TimeWindow.2.MaxRetries": "-1",
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "1",
"X_IOWRT_EU_ConfigScope": "All"
}
}
```
**Cron Jobs:**
```cron
# Primary window: Tonight 01:00-04:00, activate when idle, up to 5 retries
0 1 27 11 * sh /usr/share/bbfdm/scripts/bbf_activate_handler.sh 'WhenIdle' '2' '10800' 'false' '5' '1' '1' 'All' ''
# Fallback: Tomorrow night, force activation, infinite retries
0 1 28 11 * sh /usr/share/bbfdm/scripts/bbf_activate_handler.sh 'Immediately' '2' '7200' 'true' '-1' '1' '1' 'All' ''
```
**Use Case:** ISP wants to upgrade during low-traffic hours, with automatic fallback
---
## Troubleshooting
### Issue: Configuration not preserved after upgrade
**Symptoms:**
- WiFi password reset to default
- LAN IP back to 192.168.1.1
- Port forwarding rules gone
**Diagnosis:**
1. Check if `/usr_data` partition exists:
```bash
mount | grep usr_data
df -h | grep usr_data
```
2. Check opconf_conf_handler was called:
```bash
logread | grep opconf_conf_handler
```
3. Check for userconf.json:
```bash
ls -la /usr_data/userconf/
```
4. Check config parameters used:
```bash
logread | grep -E "keep_settings|keep_opconf|config_scope"
```
**Solutions:**
- **Missing /usr_data:** Partition not created, config preservation disabled
```bash
# Check flash layout
cat /proc/mtd
```
- **opconf not available:** System falls back to legacy mode
```bash
which opconf_conf_handler
# If missing: config_scope forced to "All"
```
- **Parameters not passed:** Check upgrade method's config support
```bash
# DHCP upgrade - add reset_mode parameter
# GUI upgrade - toggle "Keep Settings"
# TR-069 - check /etc/config/cwmp defaults
```
### Issue: Operator config deleted unexpectedly
**Symptoms:**
- ACS URL reset to default
- WAN interface unconfigured
- Need to re-provision device
**Diagnosis:**
```bash
# Check if opconf.json exists
ls -la /usr_data/opconf/opconf.json
# Check upgrade parameters
logread | grep keep_opconf
```
**Possible Causes:**
1. **USP controller sent KeepOpConf=0:**
- Check USP controller logs for Download() parameters
2. **TR-069 UCI default was 0:**
```bash
uci show cwmp.cpe.KeepOpConf
# Should be '1', if '0' → operator config deleted
```
3. **DHCP upgrade with no opconf support:**
- DHCP onboarding doesn't support keep_opconf
- Pre-configure firmware with ISP settings baked in
**Prevention:**
```uci
# Set system-wide defaults in firmware
cat > /rom/etc/config/cwmp << EOF
config cpe 'cpe'
option KeepConfig '1'
option KeepOpConf '1'
option ConfigScope 'UserOnly'
EOF
```
### Issue: Firmware upgrade stuck at "Downloading"
**Symptoms:**
- Bank status shows "Downloading" indefinitely
- No "Available" status after 10+ minutes
- Device not rebooted
**Diagnosis:**
```bash
# Check sysupgrade events
ubus listen sysupgrade &
# Check fwbank status
ubus call fwbank dump
# Check for hung sysupgrade process
ps | grep sysupgrade
```
**Possible Causes:**
1. **Invalid firmware image:**
```bash
sysupgrade -T /tmp/firmware.itb
# Should output: Image check succeeded
```
2. **Insufficient flash space:**
```bash
df -h /tmp
df -h /overlay
```
3. **sysupgrade process crashed:**
```bash
logread | grep sysupgrade | tail -50
```
**Recovery:**
```bash
# Kill hung sysupgrade
killall sysupgrade
# Clean up temp files
rm -f /tmp/firmware-*
# Reset bank status manually (if needed)
ubus call fwbank set_bootbank '{"bank": 1}' # Back to current bank
reboot
```
### Issue: Device boots into wrong bank after upgrade
**Symptoms:**
- Upgraded bank 2, but still running bank 1
- Boot bank doesn't match expectation
**Diagnosis:**
```bash
# Check current running bank
ubus call fwbank dump | jsonfilter -e '@.bank[@.active=true]'
# Check boot bank
ubus call fwbank dump | jsonfilter -e '@.bank[@.boot=true]'
# Check bootloader environment
fw_printenv boot_bank
```
**Possible Causes:**
1. **AutoActivate was 0:**
- Download() completed but didn't set boot bank
- Need to manually call Activate()
2. **Bootloader failed to switch:**
- U-Boot issue
- Environment not writable
**Solution:**
```bash
# Manually set boot bank
ubus call fwbank set_bootbank '{"bank": 2}'
# Copy config
ubus call fwbank copy_config '{
"keep_settings": true,
"keep_opconf": true,
"config_scope": "All"
}'
# Reboot
reboot
```
---
## API Reference
### fwbank UBUS Methods
#### fwbank.dump
**Description:** Get firmware bank information
**Parameters:** None
**Returns:**
```json
{
"bank": [
{
"id": 1,
"fwver": "iop-v5.14.0",
"swver": "5.14.0",
"omci_swver": "5.14",
"active": true,
"boot": true,
"upgrade": false,
"status": "Active"
},
{
"id": 2,
"fwver": "iop-v5.15.0",
"swver": "5.15.0",
"omci_swver": "5.15",
"active": false,
"boot": false,
"upgrade": true,
"status": "Available"
}
]
}
```
**Example:**
```bash
ubus call fwbank dump
```
#### fwbank.upgrade
**Description:** Upgrade firmware to specified bank
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| path | string | ✅ | - | Path to firmware file |
| bank | integer | ✅ | - | Target bank ID (1 or 2) |
| auto_activate | boolean | ❌ | false | Set as boot bank after upgrade |
| reboot | boolean | ❌ | false | Reboot after upgrade |
| keep_settings | boolean | ❌ | true | Preserve configurations |
| keep_opconf | boolean | ❌ | true | Preserve operator config |
| config_scope | string | ❌ | "UserOnly" | Config scope ("All" or "UserOnly") |
**Returns:**
```json
{
"result": "ok"
}
```
or
```json
{
"result": "error",
"message": "Invalid firmware image"
}
```
**Example:**
```bash
ubus call fwbank upgrade '{
"path": "/tmp/firmware.itb",
"bank": 2,
"auto_activate": true,
"reboot": true,
"keep_settings": true,
"keep_opconf": false,
"config_scope": "UserOnly"
}'
```
#### fwbank.set_bootbank
**Description:** Set which bank will boot on next reboot
**Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| bank | integer | ✅ | Bank ID to boot (1 or 2) |
**Returns:**
```json
{
"result": "ok"
}
```
**Example:**
```bash
ubus call fwbank set_bootbank '{"bank": 2}'
```
#### fwbank.copy_config
**Description:** Copy configuration from active bank to boot bank
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| keep_settings | boolean | ❌ | true | Copy user configs |
| keep_opconf | boolean | ❌ | true | Copy operator config |
| config_scope | string | ❌ | "UserOnly" | Scope ("All" or "UserOnly") |
**Returns:**
```json
{
"result": "ok"
}
```
**Example:**
```bash
ubus call fwbank copy_config '{
"keep_settings": true,
"keep_opconf": true,
"config_scope": "All"
}'
```
### BBF Datamodel Operations
#### Device.DeviceInfo.FirmwareImage.{i}.Download()
**Description:** Download firmware to specified bank
**Input Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| URL | string | ✅ | Firmware download URL |
| AutoActivate | boolean | ❌ | Set as boot bank after download |
| Username | string | ❌ | HTTP authentication username |
| Password | string | ❌ | HTTP authentication password |
| FileSize | unsignedInt | ❌ | Expected file size (bytes) |
| CheckSumAlgorithm | string | ❌ | "MD5", "SHA-1", "SHA-256" |
| CheckSum | string | ❌ | Expected checksum value |
| X_IOWRT_EU_KeepConfig | boolean | ❌ | Preserve configurations |
| X_IOWRT_EU_KeepOpConf | boolean | ❌ | Preserve operator config |
| X_IOWRT_EU_ConfigScope | string | ❌ | "All" or "UserOnly" |
**Returns:** Async operation, sends TransferComplete event when done
**USP Example:**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Download()",
"input": {
"URL": "https://fw.example.com/firmware.itb",
"AutoActivate": "1",
"FileSize": "52428800",
"CheckSumAlgorithm": "SHA-256",
"CheckSum": "a3f5e1d2c4b6...",
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "0",
"X_IOWRT_EU_ConfigScope": "UserOnly"
}
}
```
#### Device.DeviceInfo.FirmwareImage.{i}.Activate()
**Description:** Activate downloaded firmware (optionally scheduled)
**Input Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| TimeWindow.{i}.Start | unsignedInt | ❌ | Seconds from now to start |
| TimeWindow.{i}.End | unsignedInt | ❌ | Seconds from now to end |
| TimeWindow.{i}.Mode | string | ❌ | "AnyTime", "Immediately", "WhenIdle", "ConfirmationNeeded" |
| TimeWindow.{i}.UserMessage | string | ❌ | Message to display to user |
| TimeWindow.{i}.MaxRetries | int | ❌ | -1 (infinite) to 10 |
| X_IOWRT_EU_KeepConfig | boolean | ❌ | Preserve configurations |
| X_IOWRT_EU_KeepOpConf | boolean | ❌ | Preserve operator config |
| X_IOWRT_EU_ConfigScope | string | ❌ | "All" or "UserOnly" |
**Returns:** Immediate success, activation occurs at scheduled time
**USP Example (immediate):**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Activate()",
"input": {
"X_IOWRT_EU_KeepConfig": "1",
"X_IOWRT_EU_KeepOpConf": "1",
"X_IOWRT_EU_ConfigScope": "All"
}
}
```
**USP Example (scheduled):**
```json
{
"command": "Device.DeviceInfo.FirmwareImage.2.Activate()",
"input": {
"TimeWindow.1.Start": "3600",
"TimeWindow.1.End": "7200",
"TimeWindow.1.Mode": "WhenIdle",
"TimeWindow.1.MaxRetries": "3",
"X_IOWRT_EU_KeepConfig": "1"
}
}
```
---
## Appendices
### Appendix A: Configuration Files Reference
| Path | Purpose | Format |
|------|---------|--------|
| `/etc/sysmngr/fwbank` | Bank management script | Shell script |
| `/etc/config/cwmp` | TR-069 config defaults | UCI |
| `/etc/config/dhcp-on-boarding` | DHCP upgrade settings | UCI |
| `/usr_data/opconf/opconf.json` | Operator configuration | JSON |
| `/usr_data/userconf/userconf.json` | User configuration (temp) | JSON |
| `/usr_data/userconf/keep.d/` | Backed up config files | Directory tree |
| `/tmp/sysupgrade.tgz` | Full config backup | Tarball |
| `/tmp/firmware-XXXXXX` | Downloaded firmware | Binary |
| `/etc/crontabs/root` | Scheduled activations | Crontab |
### Appendix B: Event Types
| Event | Source | Payload | Purpose |
|-------|--------|---------|---------|
| `sysupgrade` | fwbank | `{"bank_id": "2", "status": "Available"}` | Upgrade progress |
| `transfer_complete` | sysmngr | BBF TransferComplete format | Notify controller |
**Example sysupgrade event:**
```bash
ubus listen sysupgrade
# Output during upgrade:
{ "sysupgrade": { "bank_id": "2", "status": "Downloading" } }
{ "sysupgrade": { "bank_id": "2", "status": "Available" } }
```
### Appendix C: Error Codes
| Code | Message | Cause | Solution |
|------|---------|-------|----------|
| 9001 | Command failure | Generic operation error | Check logs, retry |
| 9007 | Invalid arguments | Bad parameter value | Validate input |
| 9010 | Resources exceeded | Out of memory/storage | Free space, retry |
| - | "Invalid firmware image" | sysupgrade -T failed | Check image format |
| - | "Checksum mismatch" | File corrupted | Re-download |
| - | "Download failed" | HTTP error | Check URL/credentials |
### Appendix D: Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2024-01 | Initial dual-bank support |
| 1.1 | 2024-06 | Added opconf/userconf separation |
| 1.1.3 | 2024-11 | Enhanced BBF USP support, scheduled activation |
---
## Quick Reference
### Default Configuration Values
```
keep_config: 1 (preserve)
keep_opconf: 1 (preserve)
config_scope: "UserOnly"
auto_activate: 0 (manual)
reboot: 0 (manual)
```
### Common Commands
#### Using dmcli (BBF Datamodel - Recommended)
```bash
# Check firmware status
dmcli get Device.DeviceInfo.FirmwareImage.
# Upgrade with full config preservation
dmcli do Device.DeviceInfo.FirmwareImage.2.Download() \
URL=https://fw.example.com/firmware.itb \
AutoActivate=1 \
X_IOWRT_EU_KeepConfig=1 \
X_IOWRT_EU_KeepOpConf=1 \
X_IOWRT_EU_ConfigScope=All
# Upgrade with operator reset, keep user settings
dmcli do Device.DeviceInfo.FirmwareImage.2.Download() \
URL=https://fw.example.com/firmware.itb \
AutoActivate=1 \
X_IOWRT_EU_KeepConfig=1 \
X_IOWRT_EU_KeepOpConf=0 \
X_IOWRT_EU_ConfigScope=UserOnly
# Factory reset upgrade
dmcli do Device.DeviceInfo.FirmwareImage.2.Download() \
URL=https://fw.example.com/firmware.itb \
AutoActivate=1 \
X_IOWRT_EU_KeepConfig=0
```
#### Using fwbank UBUS (Direct Access)
```bash
# Check current status
ubus call fwbank dump
# Upgrade with full config preservation
ubus call fwbank upgrade '{"path":"/tmp/fw.itb","bank":2,"auto_activate":true,"reboot":true,"keep_settings":true,"keep_opconf":true,"config_scope":"All"}'
# Upgrade with operator reset, keep user settings
ubus call fwbank upgrade '{"path":"/tmp/fw.itb","bank":2,"auto_activate":true,"keep_settings":true,"keep_opconf":false,"config_scope":"UserOnly"}'
# Factory reset upgrade
ubus call fwbank upgrade '{"path":"/tmp/fw.itb","bank":2,"auto_activate":true,"keep_settings":false}'
# Check upgrade events
logread -f | grep -E "sysupgrade|fwbank|opconf"
```
### Decision Tree: Which Config Parameters?
```
Are you upgrading via...
├─ USP/TR-369 Protocol
│ ├─ Sulu Web GUI
│ │ └─ Toggle: "Keep Settings" checkbox in File Upload mode
│ │ Basic control, KeepConfig only (URL download uses defaults)
│ │
│ ├─ dmcli (Data Model CLI)
│ │ └─ Use: X_IOWRT_EU_KeepConfig, X_IOWRT_EU_KeepOpConf, X_IOWRT_EU_ConfigScope
│ │ Full control via operation parameters
│ │ Works locally (UBUS) or remotely (USP)
│ │ Example: dmcli do Device.DeviceInfo.FirmwareImage.2.Download() URL=... AutoActivate=1 X_IOWRT_EU_KeepConfig=1
│ │
│ └─ External USP Controller
│ └─ Use: X_IOWRT_EU_KeepConfig, X_IOWRT_EU_KeepOpConf, X_IOWRT_EU_ConfigScope
│ Per-operation control, full flexibility
├─ TR-069 ACS (CWMP)
│ └─ Configure: /etc/config/cwmp (KeepConfig, KeepOpConf, ConfigScope)
│ Firmware defaults, applies to all upgrades
├─ DHCP Onboarding
│ └─ Use: reset_mode=full (or omit)
│ Limited control, only factory reset vs preserve
└─ CLI (fwbank)
└─ Use: keep_settings, keep_opconf, config_scope parameters
Direct control, all options available
```
---
**Document Version:** 1.0
**Last Updated:** 2025-11-26
**Author:** iopsys Technical Documentation
**Contact:** support@iopsys.eu