Generate and publish developer Wiki / publish-wiki (push) Canceled after 0s
Generate and publish developer Wiki / validate-wiki (push) Canceled after 9s
Build and publish release / create-release (push) Canceled after 0s
Build and publish release / build (push) Canceled after 0s
Build and publish release / publish-release (push) Canceled after 0s
Build and publish release / metadata (push) Canceled after 10s
Reviewed-on: #42 Co-authored-by: Koda YeenBean <n122330@gmail.com>
271 lines
11 KiB
C
271 lines
11 KiB
C
/*
|
|
* Parse the legacy newline-separated system_cfg representation and translate
|
|
* it into the same JSON configuration consumed by modern provisioning.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <uci.h>
|
|
#include <json-c/json.h>
|
|
|
|
#include "wlan.h"
|
|
#include "ufmodel.h"
|
|
#include "crypto.h"
|
|
#include "wlan_internal.h"
|
|
|
|
|
|
static int system_cfg_get(const char *cfg, const char *key,
|
|
char *out, size_t out_size)
|
|
{
|
|
size_t key_len = strlen(key);
|
|
const char *line = cfg;
|
|
|
|
while (line && *line) {
|
|
const char *end = strchr(line, '\n');
|
|
size_t line_len = end ? (size_t)(end - line) : strlen(line);
|
|
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';
|
|
return 1;
|
|
}
|
|
line = end ? end + 1 : NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int wlan_apply_system_cfg(const char *system_cfg,
|
|
const uf_model_t *model)
|
|
{
|
|
if (!system_cfg || !system_cfg[0])
|
|
return -1;
|
|
|
|
struct json_object *root = json_object_new_object();
|
|
struct json_object *radios = json_object_new_array();
|
|
struct json_object *vaps = json_object_new_array();
|
|
char key[64], value[256];
|
|
|
|
for (int i = 1; i <= 4; i++) {
|
|
snprintf(key, sizeof(key), "radio.%d.ieee_mode", i);
|
|
if (!system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
continue;
|
|
|
|
struct json_object *radio = json_object_new_object();
|
|
const char *band = strstr(value, "11na") ? "na" : "ng";
|
|
json_object_object_add(radio, "radio",
|
|
json_object_new_string(band));
|
|
|
|
const char *ht = strstr(value, "ht80") ? "HT80" :
|
|
strstr(value, "ht40") ? "HT40" : "HT20";
|
|
json_object_object_add(radio, "ht", json_object_new_string(ht));
|
|
|
|
snprintf(key, sizeof(key), "radio.%d.channel", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(radio, "channel",
|
|
json_object_new_int(!strcmp(value, "auto") ? 0 : atoi(value)));
|
|
|
|
snprintf(key, sizeof(key), "radio.%d.txpower", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)) &&
|
|
strcmp(value, "auto"))
|
|
json_object_object_add(radio, "tx_power",
|
|
json_object_new_int(atoi(value)));
|
|
json_object_array_add(radios, radio);
|
|
}
|
|
|
|
for (int i = 1; i <= 32; i++) {
|
|
snprintf(key, sizeof(key), "aaa.%d.ssid", i);
|
|
if (!system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
continue;
|
|
|
|
struct json_object *vap = json_object_new_object();
|
|
json_object_object_add(vap, "essid",
|
|
json_object_new_string(value));
|
|
|
|
/* Preserve the WLAN ObjectId used to attach clients in topology. */
|
|
const char *id_suffixes[] = { "id", "_id", "wlanconf_id" };
|
|
for (size_t id_index = 0;
|
|
id_index < sizeof(id_suffixes) / sizeof(id_suffixes[0]);
|
|
id_index++) {
|
|
snprintf(key, sizeof(key), "aaa.%d.%s", i,
|
|
id_suffixes[id_index]);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)) &&
|
|
wlan_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;
|
|
}
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.status", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)) &&
|
|
strcmp(value, "enabled")) {
|
|
json_object_put(vap);
|
|
continue;
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "wireless.%d.parent", i);
|
|
const char *band = "ng";
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)) &&
|
|
!strcmp(value, "wifi1"))
|
|
band = "na";
|
|
json_object_object_add(vap, "radio", json_object_new_string(band));
|
|
|
|
/* UniFi's legacy Force WiFi 4 mode is carried as wireless.N.iot. */
|
|
snprintf(key, sizeof(key), "wireless.%d.iot", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "iot", json_object_new_boolean(
|
|
wlan_feature_enabled(value)));
|
|
|
|
char passphrase[256] = {0};
|
|
snprintf(key, sizeof(key), "aaa.%d.wpa.psk", i);
|
|
int has_passphrase = system_cfg_get(system_cfg, key, passphrase,
|
|
sizeof(passphrase));
|
|
if (!has_passphrase) {
|
|
snprintf(key, sizeof(key), "aaa.%d.sae.psk.1.psk", i);
|
|
has_passphrase = system_cfg_get(system_cfg, key, passphrase,
|
|
sizeof(passphrase));
|
|
}
|
|
|
|
char key_mgmt[256] = {0};
|
|
snprintf(key, sizeof(key), "aaa.%d.wpa.key.1.mgmt", i);
|
|
int has_key_mgmt = system_cfg_get(system_cfg, key, key_mgmt,
|
|
sizeof(key_mgmt));
|
|
|
|
int wpa3_support = 0;
|
|
snprintf(key, sizeof(key), "aaa.%d.wpa3.support", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
wpa3_support = wlan_feature_enabled(value);
|
|
|
|
int wpa3_transition = 0;
|
|
snprintf(key, sizeof(key), "aaa.%d.wpa3.transition", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
wpa3_transition = wlan_feature_enabled(value);
|
|
|
|
const char *security = "open";
|
|
if (has_key_mgmt && strstr(key_mgmt, "WPA-EAP")) {
|
|
security = wpa3_support ? "wpa3enterprise" : "wpa2enterprise";
|
|
} else if (has_key_mgmt && strstr(key_mgmt, "SAE")) {
|
|
/* A transition BSS permits both WPA2-PSK and SAE. */
|
|
security = wpa3_transition || strstr(key_mgmt, "WPA-PSK")
|
|
? "wpa3transition" : "wpa3";
|
|
} else if (has_passphrase) {
|
|
security = "wpa2psk";
|
|
}
|
|
json_object_object_add(vap, "security",
|
|
json_object_new_string(security));
|
|
|
|
int enterprise = !strcmp(security, "wpa2enterprise") ||
|
|
!strcmp(security, "wpa3enterprise");
|
|
if (has_passphrase && !enterprise) {
|
|
json_object_object_add(vap, "x_passphrase",
|
|
json_object_new_string(passphrase));
|
|
}
|
|
|
|
if (enterprise) {
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.auth.1.ip", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "auth_server",
|
|
json_object_new_string(value));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.auth.1.port", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "auth_port",
|
|
json_object_new_int(atoi(value)));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.auth.1.secret", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "auth_secret",
|
|
json_object_new_string(value));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.acct.1.ip", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "acct_server",
|
|
json_object_new_string(value));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.acct.1.port", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "acct_port",
|
|
json_object_new_int(atoi(value)));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.radius.acct.1.secret", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "acct_secret",
|
|
json_object_new_string(value));
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.hide_ssid", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "hide_ssid",
|
|
json_object_new_boolean(!strcmp(value, "true")));
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.ft.status", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value)))
|
|
json_object_object_add(vap, "fast_roaming_enabled",
|
|
json_object_new_boolean(!strcmp(value, "enabled")));
|
|
|
|
const char *band_steer_suffixes[] = {
|
|
"band_steering", "band_steering_enabled", "band_steering_mode",
|
|
"steering"
|
|
};
|
|
for (size_t n = 0;
|
|
n < sizeof(band_steer_suffixes) /
|
|
sizeof(band_steer_suffixes[0]); n++) {
|
|
snprintf(key, sizeof(key), "aaa.%d.%s", i,
|
|
band_steer_suffixes[n]);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value))) {
|
|
json_object_object_add(vap, "band_steering",
|
|
json_object_new_boolean(wlan_feature_enabled(value)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *handoff_suffixes[] = {
|
|
"bss_transition", "bss_transition_enabled",
|
|
"handoff_suggestions", "handoff_suggestions_enabled"
|
|
};
|
|
for (size_t n = 0;
|
|
n < sizeof(handoff_suffixes) / sizeof(handoff_suffixes[0]); n++) {
|
|
snprintf(key, sizeof(key), "aaa.%d.%s", i,
|
|
handoff_suffixes[n]);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value))) {
|
|
json_object_object_add(vap, "bss_transition",
|
|
json_object_new_boolean(wlan_feature_enabled(value)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.pmf.mode", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value))) {
|
|
const char *pmf = !strcmp(value, "2") ? "required" :
|
|
!strcmp(value, "1") ? "optional" : "disabled";
|
|
json_object_object_add(vap, "pmf_mode",
|
|
json_object_new_string(pmf));
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "aaa.%d.br.devname", i);
|
|
if (system_cfg_get(system_cfg, key, value, sizeof(value))) {
|
|
const char *dot = strrchr(value, '.');
|
|
if (dot && atoi(dot + 1) > 0)
|
|
json_object_object_add(vap, "vlan_id",
|
|
json_object_new_int(atoi(dot + 1)));
|
|
}
|
|
json_object_array_add(vaps, vap);
|
|
}
|
|
|
|
json_object_object_add(root, "radio_table", radios);
|
|
json_object_object_add(root, "vap_table", vaps);
|
|
LOGF(stdout, "Parsed legacy system_cfg: %zu radios, %zu VAPs",
|
|
json_object_array_length(radios), json_object_array_length(vaps));
|
|
int result = wlan_apply_config(root, model);
|
|
json_object_put(root);
|
|
return result;
|
|
}
|