Clone
2
Developer Guide
openUF documentation bot edited this page 2026-07-19 21:49:40 +00:00

Developer Guide

This guide describes the current openUF implementation. The daemon is a single-process, single-threaded OpenWrt service. Its main loop wakes once per second and schedules discovery, LLDP, and controller inform work.

Safety first

openUF runs as root. It can replace managed Wi-Fi configuration, create VLAN devices, send raw Ethernet frames, reboot the device, and persist adoption credentials. Do not run the daemon itself on a development workstation. Compile it with the OpenWrt toolchain and perform runtime tests on a disposable OpenWrt access point.

Protocol debug level 2 can log decrypted credentials. Never enable it by default, attach those logs to issues, or commit controller payload captures.

Source layout

Module Responsibility
src/announce.c UniFi layer-2 UDP discovery.
src/clients.c Wireless and bridge client telemetry.
src/config.c Static daemon configuration parser and defaults.
src/crypto.c AES-CBC, AES-GCM, and encoding helpers.
src/http.c Minimal HTTP/1.0 transport.
src/inform/inform.c One inform request/response exchange.
src/inform/packet.c TNBU binary envelope codec.
src/inform/payload.c Inform JSON telemetry assembly.
src/inform/response.c Controller command and provisioning dispatch.
src/lldp.c LLDP frame transmission and neighbor collection.
src/main.c Daemon lifecycle and one-second scheduler.
src/models.c Emulated hardware model registry.
src/state.c Persistent adoption and controller state.
src/sysinfo.c Kernel and nl80211 device telemetry.
src/wlan/common.c Shared Wi-Fi translations and validators.
src/wlan/legacy.c Legacy system_cfg translation.
src/wlan/provision.c Modern setstate Wi-Fi provisioning.
src/wlan/radio.c Runtime radio mapping and radio settings.
src/wlan/telemetry.c Managed VAP telemetry from UCI and nl80211.
src/wlan/uci.c Reusable UCI, VLAN, steering, and cleanup operations.

Public contracts remain in src/*.h. The src/inform/ and src/wlan/ directories contain private implementation units; their *_internal.h files are not stable interfaces for other subsystems.

Runtime architecture

main() loads static configuration, persistent controller state, and the emulated model. It discovers the LAN MAC/IP and an initial controller from the default route when no explicit controller is configured. The main loop then schedules:

  1. announce_send() for UniFi UDP discovery.
  2. lldp_send_frame() for each model Ethernet port.
  3. inform_send() for adoption, telemetry, commands, and provisioning.

See Runtime Flow for the decision flow and Function Call Graph for generated caller/callee relationships.

Inform and adoption invariants

  • The TNBU layout, big-endian fields, flag meanings, authenticated header, and cipher selection are controller compatibility constraints.
  • An unadopted device always encrypts with DEFAULT_AUTH_KEY, even if stale state contains another key.
  • The controller response is decoded before command dispatch. Adoption can be completed through either legacy set-adopt or modern setparam data.
  • cfgversion records configuration that was successfully applied locally. Failed provisioning resets it to "0" so the controller retries.
  • Increase OPENUF_CONFIG_SCHEMA only when an existing persisted configuration must be reapplied after an upgrade.

Wi-Fi ownership and provisioning

Only UCI wifi-iface sections prefixed with openuf_ belong to this daemon. Cleanup must preserve every unrelated section. Controller VAP ObjectIds are stored in openuf_vap_id so client topology remains stable across reprovision.

Modern setstate data is applied by wlan_apply_config(). Legacy newline-separated system_cfg data is first translated to the same JSON shape by wlan_apply_system_cfg(), keeping one provisioning path responsible for UCI commits and radio startup.

Model band and port assumptions belong in models.c. The runtime radio mapper may resolve a model band to a different local PHY, but protocol and telemetry code must not invent model-specific mappings.

Common development tasks

Add telemetry

  1. Add a bounded reader to sysinfo.c, clients.c, or another focused module.
  2. Add the controller field in inform/payload.c.
  3. Document units, fallback behavior, ownership, and any sensitive content.
  4. Regenerate this Wiki and cross-build the package.

Add a controller command

  1. Extend dispatch in inform/response.c.
  2. Validate controller values before changing state or invoking a command.
  3. Save state only after a coherent transition.
  4. Preserve adoption-key and cfgversion behavior.

Add a Wi-Fi setting

  1. Parse controller aliases in wlan/provision.c or wlan/legacy.c.
  2. Put reusable UCI work in wlan/uci.c and translations in wlan/common.c.
  3. Report the effective value from wlan/telemetry.c when the controller expects it in vap_table.
  4. Test on device; a successful cross-build cannot validate netifd/hostapd behavior.

Add or change a model

Update ufmodel.h, the complete model entry in models.c, and every affected telemetry or WLAN consumer together. Do not scatter model checks across the protocol implementation.

Build and validation

From the OpenWrt root:

make package/OpenUniFi/compile

Use V=s for detailed compiler diagnostics. If a clean package rebuild is needed, clean only this package:

make package/OpenUniFi/clean
make package/OpenUniFi/compile

Makefile.standalone is only for compiling directly on an OpenWrt device with development packages installed. It is not a host-side test substitute.

Documentation workflow

Check out the separate openunifi.wiki repository, then generate pages after changing C code or architecture:

./scripts/docs/generate-wiki.py --output /path/to/openunifi.wiki

Run the linter-style drift and structure check in CI or before committing:

./scripts/docs/generate-wiki.py --check --output /path/to/openunifi.wiki

The generator uses only the Python 3 standard library. The publisher uses POSIX shell utilities and Git, so neither path depends on the runner CPU architecture.

The checker parses C functions recursively, rebuilds internal call edges, validates required runtime entry points, rejects known non-English comment fragments, and limits each implementation unit to 900 lines.

Publishing to the Gitea Wiki

Gitea stores this documentation in the separate lowercase openunifi.wiki repository. Run scripts/docs/publish-wiki.sh from a trusted machine with suitable credentials. It derives that repository from origin; an explicit URL may be passed when needed. The publisher checks out the Wiki repository before generating, updates only the generated Markdown files, commits changed pages, and pushes them back to the Wiki repository.