Fixed Topology Graph Mappings (#14)

Added advanced debug levels

Reviewed-on: #14
This commit was merged in pull request #14.
This commit is contained in:
2026-07-13 20:31:21 +01:00
parent 855b282c54
commit 96217c45f6
8 changed files with 183 additions and 1 deletions
+3
View File
@@ -78,6 +78,9 @@ ufmodel = u6-inwall # emulated model
inform_interval = 10 # inform interval
enable_announce = 1
enable_inform = 1 # Enable logging to /var/log/openuf.log if set to 1
enable_logging = 1 # Write diagnostics to /var/log/openuf.log
protocol_debug_level = 0 # 0=off, 1=safe response details,
# 2=full decrypted JSON (contains secrets)
```
+3
View File
@@ -7,6 +7,8 @@
# enable_announce: 1=enable UDP discovery (port 10001)
# enable_inform: 1=enable HTTP inform requests (adoption and telemetry)
# enable_logging: 1=enable logging to /var/log/openuf.log (default: off)
# protocol_debug_level: 0=off, 1=safe response structure,
# 2=full decrypted response; may expose credentials
controller_ip = 10.10.10.1
lan_if = br-lan
@@ -15,3 +17,4 @@ inform_interval = 10
enable_announce = 1
enable_inform = 1
enable_logging = 0
protocol_debug_level = 0
+3
View File
@@ -14,6 +14,7 @@ void config_load(openuf_config_t *cfg)
cfg->enable_inform = 1;
/* File logging is opt-in to avoid flash wear and unbounded log growth. */
cfg->enable_logging = 0;
cfg->protocol_debug_level = 0;
FILE *f = fopen(OPENUF_CONF_FILE, "r");
if (!f) return;
@@ -35,6 +36,8 @@ void config_load(openuf_config_t *cfg)
else if (!strcmp(key, "enable_announce")) cfg->enable_announce = atoi(val);
else if (!strcmp(key, "enable_inform")) cfg->enable_inform = atoi(val);
else if (!strcmp(key, "enable_logging")) cfg->enable_logging = atoi(val);
else if (!strcmp(key, "protocol_debug_level"))
cfg->protocol_debug_level = atoi(val);
}
fclose(f);
}
+1
View File
@@ -35,6 +35,7 @@ typedef struct {
int enable_announce;
int enable_inform;
int enable_logging;
int protocol_debug_level;
} openuf_config_t;
/* Parse /etc/openuf/openuf.conf (simple key=value).
+61 -1
View File
@@ -94,6 +94,61 @@ static int valid_authkey(const char *key)
return 1;
}
static int protocol_debug_level;
void inform_set_debug_level(int level)
{
protocol_debug_level = level < 0 ? 0 : level > 2 ? 2 : level;
}
static int debug_system_cfg_key(const char *key)
{
if (!key || (strncmp(key, "aaa.", 4) &&
strncmp(key, "wireless.", 9)))
return 0;
return strstr(key, ".ssid") || strstr(key, ".id") ||
strstr(key, ".vap_ind") || strstr(key, ".parent");
}
static void debug_log_controller_response(struct json_object *response,
const char *raw_json)
{
if (protocol_debug_level <= 0 || !response)
return;
LOG("Protocol debug: decrypted controller response fields follow");
json_object_object_foreach(response, key, value) {
LOG("Protocol field: %s type=%s", key,
json_type_to_name(json_object_get_type(value)));
}
struct json_object *system_cfg_object;
if (json_object_object_get_ex(response, "system_cfg",
&system_cfg_object)) {
const char *system_cfg = json_object_get_string(system_cfg_object);
char *copy = system_cfg ? strdup(system_cfg) : NULL;
if (copy) {
char *save = NULL;
for (char *line = strtok_r(copy, "\n", &save);
line; line = strtok_r(NULL, "\n", &save)) {
line[strcspn(line, "\r")] = '\0';
char *equals = strchr(line, '=');
if (!equals)
continue;
*equals = '\0';
if (debug_system_cfg_key(line))
LOG("Protocol system_cfg: %s=%s", line, equals + 1);
}
free(copy);
}
}
if (protocol_debug_level >= 2) {
LOG("WARNING: full decrypted response may contain credentials");
LOG("Protocol response JSON: %s", raw_json ? raw_json : "");
}
}
/* ═══════════════════════════════════════════════════════════════════
sys_stats — CPU and memory of the system
═══════════════════════════════════════════════════════════════════
@@ -465,6 +520,9 @@ static struct json_object *build_vap_table(const uf_model_t *m)
/* Only controller-issued ObjectIds are valid in this field. */
if (vap_id)
json_object_object_add(o, "id", json_object_new_string(vap_id));
if (vap_id)
json_object_object_add(o, "wlanconf_id",
json_object_new_string(vap_id));
json_object_object_add(o, "usage",
json_object_new_string("user"));
json_object_object_add(o, "ccq",
@@ -1070,13 +1128,15 @@ int inform_send(openuf_state_t *st,
LOG("Parsed response JSON, length=%zu", strlen(resp_json));
struct json_object *resp_obj = json_tokener_parse(resp_json);
free(resp_json);
if (!resp_obj) {
LOG("Failed to parse JSON");
snprintf(err_out, 127, "JSON parse failed");
free(resp_json);
return -1;
}
debug_log_controller_response(resp_obj, resp_json);
free(resp_json);
struct json_object *response_type;
if (json_object_object_get_ex(resp_obj, "_type", &response_type))
LOG("Parsed response type: %s",
+3
View File
@@ -24,6 +24,9 @@
#define INFORM_FLAG_ENCRYPTED 0x0001
#define INFORM_FLAG_GCM 0x0008
/* 0=off, 1=safe response structure, 2=full decrypted response (secrets). */
void inform_set_debug_level(int level);
/* Send one inform cycle.
* Updates *st in place (adopted flag, auth key, inform_url, cfgversion).
* Returns 0 on success, -1 on error (sets err_out[0..127]). */
+2
View File
@@ -80,6 +80,8 @@ int main(int argc, char *argv[])
LOG("Logging enabled");
}
}
inform_set_debug_level(cfg.protocol_debug_level);
LOG("Protocol debug level=%d", cfg.protocol_debug_level);
#endif
const uf_model_t *model = ufmodel_find(cfg.ufmodel);
+107
View File
@@ -81,6 +81,7 @@
#include "wlan.h"
#include "ufmodel.h"
#include "crypto.h"
#define MAX_RESOLVED_RADIOS 8
@@ -263,6 +264,102 @@ static int valid_object_id(const char *id)
return 0;
return 1;
}
/*
* UniFi uses a 24-character hexadecimal MongoDB ObjectId to associate VAP
* telemetry and stations with a WLAN. Older legacy system_cfg payloads do
* not include that ID, so allocate a local one and keep it stable in UCI.
* All radio instances of the same WLAN must report the same ID.
*/
static int ensure_local_vap_ids(struct json_object *vaps)
{
if (!vaps || !json_object_is_type(vaps, json_type_array))
return 0;
struct uci_context *ctx = uci_alloc_context();
struct uci_package *pkg = NULL;
if (ctx)
uci_load(ctx, "wireless", &pkg);
int count = json_object_array_length(vaps);
for (int i = 0; i < count; i++) {
struct json_object *vap = json_object_array_get_idx(vaps, i);
struct json_object *value;
if (!vap)
continue;
const char *id = NULL;
const char *id_keys[] = { "_id", "id", "wlanconf_id" };
for (size_t n = 0; n < sizeof(id_keys) / sizeof(id_keys[0]); n++) {
if (json_object_object_get_ex(vap, id_keys[n], &value)) {
const char *candidate = json_object_get_string(value);
if (valid_object_id(candidate)) {
id = candidate;
break;
}
}
}
if (id)
continue;
const char *ssid = "";
if (json_object_object_get_ex(vap, "essid", &value))
ssid = json_object_get_string(value);
/* Reuse an ID already assigned to this WLAN in the same payload. */
for (int j = 0; j < i && !id; j++) {
struct json_object *previous = json_object_array_get_idx(vaps, j);
struct json_object *previous_ssid;
struct json_object *previous_id;
if (previous &&
json_object_object_get_ex(previous, "essid", &previous_ssid) &&
!strcmp(json_object_get_string(previous_ssid), ssid) &&
json_object_object_get_ex(previous, "id", &previous_id) &&
valid_object_id(json_object_get_string(previous_id)))
id = json_object_get_string(previous_id);
}
/* Reuse the ID committed by an earlier provisioning cycle. */
if (!id && pkg) {
struct uci_element *element;
uci_foreach_element(&pkg->sections, element) {
struct uci_section *section = uci_to_section(element);
const char *stored_ssid;
const char *stored_id;
if (strcmp(section->type, "wifi-iface") ||
strncmp(section->e.name, "openuf_", 7))
continue;
stored_ssid = uci_lookup_option_string(ctx, section, "ssid");
stored_id = uci_lookup_option_string(ctx, section,
"openuf_vap_id");
if (stored_ssid && !strcmp(stored_ssid, ssid) &&
valid_object_id(stored_id)) {
id = stored_id;
break;
}
}
}
char generated[25];
if (!id) {
if (crypto_random_hex((unsigned char *)generated, 12) != 0) {
printf("[openuf] Failed to generate a VAP ID for '%s'\n", ssid);
if (pkg) uci_unload(ctx, pkg);
if (ctx) uci_free_context(ctx);
return -1;
}
id = generated;
printf("[openuf] Generated persistent VAP ID %s for '%s'\n",
id, ssid);
}
json_object_object_add(vap, "id", json_object_new_string(id));
json_object_object_add(vap, "wlanconf_id", json_object_new_string(id));
}
if (pkg) uci_unload(ctx, pkg);
if (ctx) uci_free_context(ctx);
return 0;
}
/* Build one stable 802.11r mobility domain shared by every AP for an SSID. */
static void mobility_domain_for_ssid(const char *ssid, char out[5])
@@ -970,6 +1067,9 @@ int wlan_apply_config(struct json_object *config_json,
struct json_object *rt_arr = NULL, *vt_arr = NULL, *v;
json_object_object_get_ex(config_json, "radio_table", &rt_arr);
json_object_object_get_ex(config_json, "vap_table", &vt_arr);
/* Fill IDs omitted by legacy provisioning before old UCI VAPs are removed. */
if (ensure_local_vap_ids(vt_arr) != 0)
return -1;
/* Get the AP's MAC for mobility_domain */
char mac_str[32] = "00:00:00:00:00:00";
@@ -1224,6 +1324,8 @@ static int system_cfg_get(const char *cfg, const char *key,
if (line_len > key_len && !strncmp(line, key, key_len) &&
line[key_len] == '=') {
size_t value_len = line_len - key_len - 1;
while (value_len && line[key_len + value_len] == '\r')
value_len--;
if (value_len >= out_size) value_len = out_size - 1;
memcpy(out, line + key_len + 1, value_len);
out[value_len] = '\0';
@@ -1291,6 +1393,8 @@ int wlan_apply_system_cfg(const char *system_cfg,
if (system_cfg_get(system_cfg, key, value, sizeof(value)) &&
valid_object_id(value)) {
json_object_object_add(vap, "id", json_object_new_string(value));
json_object_object_add(vap, "wlanconf_id",
json_object_new_string(value));
break;
}
}
@@ -1541,6 +1645,9 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
json_object_object_add(o, "vlan_id", json_object_new_int(atoi(vlan)));
if (valid_object_id(vap_id))
json_object_object_add(o, "id", json_object_new_string(vap_id));
if (valid_object_id(vap_id))
json_object_object_add(o, "wlanconf_id",
json_object_new_string(vap_id));
json_object_array_add(arr, o);
#undef UCI_GET
}