Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/naming.md
Original file line number Diff line number Diff line change
Expand Up @@ -1083,3 +1083,27 @@ public capability identity.
The appliance-level wired view is published under `state/wired/...` and should
use stable product surface identifiers such as `cm5-eth0`, `switch-uplink-cm5`,
`lan-1` and `lan-2`.

## GSM uplink state consumed by NET

GSM publishes curated semantic uplink state under:

```text
state/gsm/uplink/<role>
```

`<role>` is stable product identity, for example `primary` or `secondary`. It
must not be a volatile Linux device name. The Linux interface name observed from
ModemManager or the kernel is payload data, normally `linux.ifname`, and may
change between boots.

NET may consume this state as its GSM source contract. NET should not consume
raw modem or HAL provider topics for WAN source identity.

During migration GSM may also publish the legacy compatibility topic:

```text
state/gsm/modem/<role>/uplink
```

New code should use `state/gsm/uplink/<role>`.
10 changes: 6 additions & 4 deletions docs/specs/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,21 @@ The OpenWrt provider applies the current Big Box config domains as follows:

```text
network
unconditional loopback and globals baseline
segment trunk VLAN devices on the configured base interface
bridge-backed LAN/system/user/guest segments by default
direct WAN/uplink segments by default
segment logical interfaces
explicit interfaces
bridge devices where configured
map-shaped and array-shaped static routes

dhcp / dnsmasq
per-segment dnsmasq sections where local DNS/DHCP/host files are required
grouped dnsmasq instances by effective DNS policy
dns cache size
upstream DNS servers
top-level DNS records as dnsmasq address entries
segment content-filter host files through addnhosts/addnmount
segment-local DHCP pools
segment-local DHCP pools bound to their dnsmasq instance
DHCP reservations
per-segment DHCP options where configured

Expand All @@ -302,7 +304,7 @@ vpn
OpenWrt provider currently reports configured tunnels as unsupported
```

Provider apply is fully reconciliatory for the UCI packages it owns. Sections absent from the desired set are removed. UCI application uses the scoped UCI manager transaction path with package snapshots and rollback on partial failure.
Provider apply is a complete rewrite for the UCI packages it owns. Devicecode-generated OpenWrt packages do not carry `devicecode_*` watermark options; the provider instead returns the semantic-to-generated-name map from `plan_op` and `apply_op` under `openwrt_names`. UCI application uses the scoped UCI manager transaction path with package snapshots and rollback on partial failure. Missing package files are created before apply.

## Traffic shaping

Expand Down
8 changes: 4 additions & 4 deletions src/configs/bigbox-v1-cm-2.json
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,8 @@
"dynamic_weight": true,
"family": "ipv4",
"source": {
"kind": "modem",
"modem_id": "primary"
"kind": "gsm-uplink",
"id": "primary"
}
},
"mdm1": {
Expand All @@ -478,8 +478,8 @@
"dynamic_weight": true,
"family": "ipv4",
"source": {
"kind": "modem",
"modem_id": "secondary"
"kind": "gsm-uplink",
"id": "secondary"
}
}
},
Expand Down
34 changes: 29 additions & 5 deletions src/services/gsm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
-- obs/v1/gsm/event/<key> per-modem observability events
-- state/gsm/modem/<name>/connected retained: true when APN connected, false otherwise
-- state/gsm/modem/<name>/wwan-iface retained: kernel wwan interface name, false when unknown
-- state/gsm/modem/<name>/uplink retained: canonical cellular uplink record for NET
-- state/gsm/uplink/<name> retained: canonical cellular uplink record for NET
-- state/gsm/modem/<name>/uplink retained: legacy compatibility cellular uplink record

local fibers = require "fibers"
local op = require "fibers.op"
Expand Down Expand Up @@ -74,6 +75,10 @@ local function t_state_gsm_modem(name, field)
end

local function t_state_gsm_uplink(name)
return { 'state', 'gsm', 'uplink', name }
end

local function t_legacy_state_gsm_uplink(name)
return { 'state', 'gsm', 'modem', name, 'uplink' }
end

Expand Down Expand Up @@ -414,6 +419,7 @@ function GsmModem.new(cap, svc)
self.wwan_iface = nil
self.last_access = nil
self.last_signal = nil
self.uplink_generation = 0
self.scope = nil
self.config_pulse = pulse.new()
self.svc = svc
Expand Down Expand Up @@ -475,16 +481,33 @@ end
function GsmModem:_publish_uplink_state(connected, iface)
if connected ~= nil then self.connected = connected == true end
if type(iface) == 'string' and iface ~= '' then self.wwan_iface = iface end
self.uplink_generation = (self.uplink_generation or 0) + 1

local access_techs = modem_get_field(self.cap, 'access_techs', REQUEST_TIMEOUT)
local access_tech = derive_access_tech(access_techs)
local operator = modem_get_field(self.cap, 'operator', REQUEST_TIMEOUT)
local signal = modem_get_field(self.cap, 'signal', REQUEST_TIMEOUT)
local logical = (self.cfg and (self.cfg.openwrt_interface or self.cfg.network_interface)) or self.name
local ifname = self.wwan_iface
local payload = {
modem = self.name,
schema = 'devicecode.gsm.uplink/1',
id = self.name,
role = self.name,
state = self.connected and 'connected' or 'disconnected',
connected = self.connected == true,
interface = self.wwan_iface,
openwrt_interface = (self.cfg and (self.cfg.openwrt_interface or self.cfg.network_interface)) or self.name,
device = self.wwan_iface,
available = self.connected == true and type(ifname) == 'string' and ifname ~= '',
generation = self.uplink_generation,
linux = { ifname = ifname },
network = { logical_interface = logical },
modem = {
id = tostring(self.id),
role = self.name,
device = self.device,
},
-- Compatibility fields for existing consumers.
interface = ifname,
openwrt_interface = logical,
device = ifname,
access = {
tech = access_tech ~= '' and access_tech or nil,
family = access_tech ~= '' and get_access_family(access_tech) or nil,
Expand All @@ -494,6 +517,7 @@ function GsmModem:_publish_uplink_state(connected, iface)
at = self.svc and self.svc.wall and self.svc:wall() or nil,
}
self.conn:retain(t_state_gsm_uplink(self.name), payload)
self.conn:retain(t_legacy_state_gsm_uplink(self.name), payload)
end

function GsmModem:_emit_metrics_once()
Expand Down
Loading