Table of Contents
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:
announce_send()for UniFi UDP discovery.lldp_send_frame()for each model Ethernet port.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-adoptor modernsetparamdata. cfgversionrecords configuration that was successfully applied locally. Failed provisioning resets it to"0"so the controller retries.- Increase
OPENUF_CONFIG_SCHEMAonly 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
- Add a bounded reader to
sysinfo.c,clients.c, or another focused module. - Add the controller field in
inform/payload.c. - Document units, fallback behavior, ownership, and any sensitive content.
- Regenerate this Wiki and cross-build the package.
Add a controller command
- Extend dispatch in
inform/response.c. - Validate controller values before changing state or invoking a command.
- Save state only after a coherent transition.
- Preserve adoption-key and
cfgversionbehavior.
Add a Wi-Fi setting
- Parse controller aliases in
wlan/provision.corwlan/legacy.c. - Put reusable UCI work in
wlan/uci.cand translations inwlan/common.c. - Report the effective value from
wlan/telemetry.cwhen the controller expects it invap_table. - 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.