fixed vlan config + 5Ghz Radio #2
@@ -0,0 +1 @@
|
|||||||
|
**/.env
|
||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
# inform_interval: seconds between inform requests (minimum 5)
|
# inform_interval: seconds between inform requests (minimum 5)
|
||||||
# enable_announce: 1=enable UDP discovery (port 10001)
|
# enable_announce: 1=enable UDP discovery (port 10001)
|
||||||
# enable_inform: 1=enable HTTP inform requests (adoption and telemetry)
|
# enable_inform: 1=enable HTTP inform requests (adoption and telemetry)
|
||||||
# enable_logging: 1=enable logging to /var/log/openuf.log
|
# enable_logging: 1=enable logging to /var/log/openuf.log (default: off)
|
||||||
|
|
||||||
controller_ip = 10.10.10.1
|
controller_ip = 10.10.10.1
|
||||||
lan_if = br-lan
|
lan_if = br-lan
|
||||||
@@ -14,4 +14,4 @@ ufmodel = uapg2-ac-lr
|
|||||||
inform_interval = 10
|
inform_interval = 10
|
||||||
enable_announce = 1
|
enable_announce = 1
|
||||||
enable_inform = 1
|
enable_inform = 1
|
||||||
enable_logging = 1
|
enable_logging = 0
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,8 @@ void config_load(openuf_config_t *cfg)
|
|||||||
cfg->inform_interval = DEFAULT_INFORM_INTERVAL;
|
cfg->inform_interval = DEFAULT_INFORM_INTERVAL;
|
||||||
cfg->enable_announce = 1;
|
cfg->enable_announce = 1;
|
||||||
cfg->enable_inform = 1;
|
cfg->enable_inform = 1;
|
||||||
cfg->enable_logging = 1;
|
/* File logging is opt-in to avoid flash wear and unbounded log growth. */
|
||||||
|
cfg->enable_logging = 0;
|
||||||
|
|
||||||
FILE *f = fopen(OPENUF_CONF_FILE, "r");
|
FILE *f = fopen(OPENUF_CONF_FILE, "r");
|
||||||
if (!f) return;
|
if (!f) return;
|
||||||
|
|||||||
+5
-2
@@ -359,11 +359,13 @@ static struct json_object *build_vap_table(const uf_model_t *m)
|
|||||||
const char *vap_name = "";
|
const char *vap_name = "";
|
||||||
const char *radio = "ng";
|
const char *radio = "ng";
|
||||||
const char *bssid = "00:00:00:00:00:00";
|
const char *bssid = "00:00:00:00:00:00";
|
||||||
|
const char *vap_id = NULL;
|
||||||
|
|
||||||
if (json_object_object_get_ex(vap, "essid", &v)) essid = json_object_get_string(v);
|
if (json_object_object_get_ex(vap, "essid", &v)) essid = json_object_get_string(v);
|
||||||
if (json_object_object_get_ex(vap, "name", &v)) vap_name = json_object_get_string(v);
|
if (json_object_object_get_ex(vap, "name", &v)) vap_name = json_object_get_string(v);
|
||||||
if (json_object_object_get_ex(vap, "radio", &v)) radio = json_object_get_string(v);
|
if (json_object_object_get_ex(vap, "radio", &v)) radio = json_object_get_string(v);
|
||||||
if (json_object_object_get_ex(vap, "bssid", &v)) bssid = json_object_get_string(v);
|
if (json_object_object_get_ex(vap, "bssid", &v)) bssid = json_object_get_string(v);
|
||||||
|
if (json_object_object_get_ex(vap, "id", &v)) vap_id = json_object_get_string(v);
|
||||||
|
|
||||||
/* Mapear banda → interfaz wlan y canal actual */
|
/* Mapear banda → interfaz wlan y canal actual */
|
||||||
char wlan_iface[32] = "wlan0";
|
char wlan_iface[32] = "wlan0";
|
||||||
@@ -430,8 +432,9 @@ static struct json_object *build_vap_table(const uf_model_t *m)
|
|||||||
json_object_new_int64(iface_st.rx_dropped));
|
json_object_new_int64(iface_st.rx_dropped));
|
||||||
json_object_object_add(o, "tx_dropped",
|
json_object_object_add(o, "tx_dropped",
|
||||||
json_object_new_int64(iface_st.tx_dropped));
|
json_object_new_int64(iface_st.tx_dropped));
|
||||||
json_object_object_add(o, "id",
|
/* Only controller-issued ObjectIds are valid in this field. */
|
||||||
json_object_new_string("user"));
|
if (vap_id)
|
||||||
|
json_object_object_add(o, "id", json_object_new_string(vap_id));
|
||||||
json_object_object_add(o, "usage",
|
json_object_object_add(o, "usage",
|
||||||
json_object_new_string("user"));
|
json_object_new_string("user"));
|
||||||
json_object_object_add(o, "ccq",
|
json_object_object_add(o, "ccq",
|
||||||
|
|||||||
+190
-27
@@ -109,7 +109,20 @@ static const char *sec_to_unifi(const char *uci)
|
|||||||
return "wpa2psk";
|
return "wpa2psk";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Nombre de sección UCI seguro (máx 15 chars) ──────────────── */
|
/* Return true only for the 24-character hexadecimal IDs used by UniFi. */
|
||||||
|
static int valid_object_id(const char *id)
|
||||||
|
{
|
||||||
|
if (!id || strlen(id) != 24)
|
||||||
|
return 0;
|
||||||
|
for (size_t i = 0; i < 24; i++)
|
||||||
|
if (!((id[i] >= '0' && id[i] <= '9') ||
|
||||||
|
(id[i] >= 'a' && id[i] <= 'f') ||
|
||||||
|
(id[i] >= 'A' && id[i] <= 'F')))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Safe UCI section name (maximum 15 characters). */
|
||||||
static void safe_section_name(const char *ssid, char *out, size_t sz)
|
static void safe_section_name(const char *ssid, char *out, size_t sz)
|
||||||
{
|
{
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
@@ -137,6 +150,40 @@ static int uci_set_val(struct uci_context *ctx,
|
|||||||
if (ret != UCI_OK) return -1;
|
if (ret != UCI_OK) return -1;
|
||||||
return (uci_set(ctx, &ptr) == UCI_OK) ? 0 : -1;
|
return (uci_set(ctx, &ptr) == UCI_OK) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
/* Set and verify an option whose absence would make a VAP unusable. */
|
||||||
|
static int uci_set_required(struct uci_context *ctx,
|
||||||
|
struct uci_package *pkg,
|
||||||
|
const char *section_name,
|
||||||
|
const char *option_name,
|
||||||
|
const char *value)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
snprintf(path, sizeof(path), "%s.%s.%s",
|
||||||
|
pkg->e.name, section_name, option_name);
|
||||||
|
if (uci_set_val(ctx, path, value) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
struct uci_section *section =
|
||||||
|
uci_lookup_section(ctx, pkg, section_name);
|
||||||
|
const char *stored = section ?
|
||||||
|
uci_lookup_option_string(ctx, section, option_name) : NULL;
|
||||||
|
return stored && !strcmp(stored, value) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add one value to a UCI list option. */
|
||||||
|
static int uci_add_list_val(struct uci_context *ctx,
|
||||||
|
const char *path, const char *val)
|
||||||
|
{
|
||||||
|
struct uci_ptr ptr;
|
||||||
|
char *assignment = malloc(strlen(path) + strlen(val) + 2);
|
||||||
|
if (!assignment) return -1;
|
||||||
|
sprintf(assignment, "%s=%s", path, val);
|
||||||
|
int ret = uci_lookup_ptr(ctx, &ptr, assignment, true);
|
||||||
|
free(assignment);
|
||||||
|
if (ret != UCI_OK) return -1;
|
||||||
|
return uci_add_list(ctx, &ptr) == UCI_OK ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Wrapper que formatea path y value en printf style */
|
/* Wrapper que formatea path y value en printf style */
|
||||||
#define UCI_SET(ctx, pkg, sec, opt, val) do { \
|
#define UCI_SET(ctx, pkg, sec, opt, val) do { \
|
||||||
@@ -175,6 +222,53 @@ static int uci_ensure_section(struct uci_context *ctx,
|
|||||||
return ret == UCI_OK ? 0 : -1;
|
return ret == UCI_OK ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolve the physical port below network.lan's bridge. VLAN tagging must
|
||||||
|
* happen on that port (for example eth0.11), not above the management bridge.
|
||||||
|
*/
|
||||||
|
static void find_vlan_uplink(struct uci_context *ctx,
|
||||||
|
struct uci_package *pkg,
|
||||||
|
char *out, size_t out_size)
|
||||||
|
{
|
||||||
|
const char *lan_device = "br-lan";
|
||||||
|
struct uci_element *element;
|
||||||
|
|
||||||
|
uci_foreach_element(&pkg->sections, element) {
|
||||||
|
struct uci_section *section = uci_to_section(element);
|
||||||
|
if (!strcmp(section->type, "interface") &&
|
||||||
|
!strcmp(section->e.name, "lan")) {
|
||||||
|
const char *device = uci_lookup_option_string(ctx, section,
|
||||||
|
"device");
|
||||||
|
if (device && device[0]) lan_device = device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uci_foreach_element(&pkg->sections, element) {
|
||||||
|
struct uci_section *section = uci_to_section(element);
|
||||||
|
const char *name;
|
||||||
|
struct uci_option *ports;
|
||||||
|
if (strcmp(section->type, "device")) continue;
|
||||||
|
name = uci_lookup_option_string(ctx, section, "name");
|
||||||
|
if (!name || strcmp(name, lan_device)) continue;
|
||||||
|
ports = uci_lookup_option(ctx, section, "ports");
|
||||||
|
if (!ports) break;
|
||||||
|
if (ports->type == UCI_TYPE_STRING) {
|
||||||
|
snprintf(out, out_size, "%s", ports->v.string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ports->type == UCI_TYPE_LIST && !uci_list_empty(&ports->v.list)) {
|
||||||
|
struct uci_element *port =
|
||||||
|
list_to_element(ports->v.list.next);
|
||||||
|
snprintf(out, out_size, "%s", port->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(out, out_size, "eth0");
|
||||||
|
}
|
||||||
|
|
||||||
static int ensure_vlan_network(int vid)
|
static int ensure_vlan_network(int vid)
|
||||||
{
|
{
|
||||||
struct uci_context *ctx = uci_alloc_context();
|
struct uci_context *ctx = uci_alloc_context();
|
||||||
@@ -186,32 +280,52 @@ static int ensure_vlan_network(int vid)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char device_section[48], interface_section[32];
|
char vlan_section[48], bridge_section[48], interface_section[32];
|
||||||
char device_name[32], vid_string[16];
|
char vlan_uplink[32], vlan_device[32], bridge_device[32], vid_string[16];
|
||||||
snprintf(device_section, sizeof(device_section),
|
find_vlan_uplink(ctx, pkg, vlan_uplink, sizeof(vlan_uplink));
|
||||||
|
snprintf(vlan_section, sizeof(vlan_section),
|
||||||
"openuf_vlan%d", vid);
|
"openuf_vlan%d", vid);
|
||||||
|
snprintf(bridge_section, sizeof(bridge_section), "openuf_br%d", vid);
|
||||||
snprintf(interface_section, sizeof(interface_section),
|
snprintf(interface_section, sizeof(interface_section),
|
||||||
"vlan%d", vid);
|
"vlan%d", vid);
|
||||||
snprintf(device_name, sizeof(device_name), "br-lan.%d", vid);
|
snprintf(vlan_device, sizeof(vlan_device), "%s.%d", vlan_uplink, vid);
|
||||||
|
snprintf(bridge_device, sizeof(bridge_device), "br-openuf-%d", vid);
|
||||||
snprintf(vid_string, sizeof(vid_string), "%d", vid);
|
snprintf(vid_string, sizeof(vid_string), "%d", vid);
|
||||||
|
|
||||||
int ok = uci_ensure_section(ctx, pkg, device_section, "device") == 0 &&
|
int ok = uci_ensure_section(ctx, pkg, vlan_section, "device") == 0 &&
|
||||||
|
uci_ensure_section(ctx, pkg, bridge_section, "device") == 0 &&
|
||||||
uci_ensure_section(ctx, pkg, interface_section, "interface") == 0;
|
uci_ensure_section(ctx, pkg, interface_section, "interface") == 0;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
UCI_SET(ctx, "network", device_section, "type", "8021q");
|
UCI_SET(ctx, "network", vlan_section, "type", "8021q");
|
||||||
UCI_SET(ctx, "network", device_section, "ifname", "br-lan");
|
UCI_SET(ctx, "network", vlan_section, "ifname", vlan_uplink);
|
||||||
UCI_SET(ctx, "network", device_section, "vid", vid_string);
|
UCI_SET(ctx, "network", vlan_section, "vid", vid_string);
|
||||||
UCI_SET(ctx, "network", device_section, "name", device_name);
|
UCI_SET(ctx, "network", vlan_section, "name", vlan_device);
|
||||||
|
|
||||||
|
/* A VAP needs a bridge containing the tagged wired device. */
|
||||||
|
UCI_SET(ctx, "network", bridge_section, "type", "bridge");
|
||||||
|
UCI_SET(ctx, "network", bridge_section, "name", bridge_device);
|
||||||
|
char ports_path[128];
|
||||||
|
snprintf(ports_path, sizeof(ports_path), "network.%s.ports",
|
||||||
|
bridge_section);
|
||||||
|
/* Replace the list so repeated provisioning never duplicates ports. */
|
||||||
|
struct uci_ptr ports_ptr;
|
||||||
|
char ports_lookup[128];
|
||||||
|
snprintf(ports_lookup, sizeof(ports_lookup), "%s", ports_path);
|
||||||
|
if (uci_lookup_ptr(ctx, &ports_ptr, ports_lookup, true) == UCI_OK &&
|
||||||
|
ports_ptr.o)
|
||||||
|
uci_delete(ctx, &ports_ptr);
|
||||||
|
ok = uci_add_list_val(ctx, ports_path, vlan_device) == 0;
|
||||||
|
|
||||||
UCI_SET(ctx, "network", interface_section, "proto", "none");
|
UCI_SET(ctx, "network", interface_section, "proto", "none");
|
||||||
UCI_SET(ctx, "network", interface_section, "device", device_name);
|
UCI_SET(ctx, "network", interface_section, "device", bridge_device);
|
||||||
ok = uci_commit(ctx, &pkg, false) == UCI_OK;
|
ok = ok && uci_commit(ctx, &pkg, false) == UCI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uci_unload(ctx, pkg);
|
uci_unload(ctx, pkg);
|
||||||
uci_free_context(ctx);
|
uci_free_context(ctx);
|
||||||
if (ok)
|
if (ok)
|
||||||
printf("[openuf] Configured VLAN %d as network '%s' on br-lan\n",
|
printf("[openuf] Configured VLAN %d on uplink %s as network '%s'\n",
|
||||||
vid, interface_section);
|
vid, vlan_uplink, interface_section);
|
||||||
return ok ? 0 : -1;
|
return ok ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +508,25 @@ static int apply_vap(struct uci_context *ctx,
|
|||||||
UCI_SET(ctx, "wireless", sec_name, "network", "lan");
|
UCI_SET(ctx, "wireless", sec_name, "network", "lan");
|
||||||
UCI_SET(ctx, "wireless", sec_name, "encryption", sec_to_uci(security));
|
UCI_SET(ctx, "wireless", sec_name, "encryption", sec_to_uci(security));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preserve the controller's WLAN configuration ID. Inform telemetry must
|
||||||
|
* refer to this ObjectId; a label such as "user" is not a valid VAP ID.
|
||||||
|
* Controller versions use different keys, so accept the known variants.
|
||||||
|
*/
|
||||||
|
const char *vap_id = NULL;
|
||||||
|
const char *id_keys[] = { "_id", "id", "wlanconf_id" };
|
||||||
|
for (size_t i = 0; i < sizeof(id_keys) / sizeof(id_keys[0]); i++) {
|
||||||
|
if (json_object_object_get_ex(vap_json, id_keys[i], &v)) {
|
||||||
|
const char *candidate = json_object_get_string(v);
|
||||||
|
if (valid_object_id(candidate)) {
|
||||||
|
vap_id = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vap_id)
|
||||||
|
UCI_SET(ctx, "wireless", sec_name, "openuf_vap_id", vap_id);
|
||||||
|
|
||||||
/* Contraseña */
|
/* Contraseña */
|
||||||
if (pass && pass[0] && strcmp(security,"open") != 0)
|
if (pass && pass[0] && strcmp(security,"open") != 0)
|
||||||
UCI_SET(ctx, "wireless", sec_name, "key", pass);
|
UCI_SET(ctx, "wireless", sec_name, "key", pass);
|
||||||
@@ -493,8 +626,16 @@ static int apply_vap(struct uci_context *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("[openuf] VAP '%s' → %s enc=%s ft=%d bs=%d pmf=%d\n",
|
/* Reassert the binding last and reject incomplete UCI sections. */
|
||||||
essid, sec_name, sec_to_uci(security), ft, band_steer, pmf);
|
if (uci_set_required(ctx, pkg, sec_name, "device", device_name) != 0) {
|
||||||
|
printf("[openuf] Failed to bind VAP '%s' to %s\n",
|
||||||
|
essid, device_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[openuf] VAP '%s' -> %s device=%s enc=%s ft=%d bs=%d pmf=%d\n",
|
||||||
|
essid, sec_name, device_name, sec_to_uci(security),
|
||||||
|
ft, band_steer, pmf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +670,11 @@ int wlan_apply_config(struct json_object *config_json,
|
|||||||
mac_str[strcspn(mac_str, "\r\n")] = '\0';
|
mac_str[strcspn(mac_str, "\r\n")] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1. Limpiar VAPs antiguas */
|
/* Stop hostapd so a deleted BSS cannot survive a netifd reload race. */
|
||||||
|
printf("[openuf] Stopping Wi-Fi before controller provisioning...\n");
|
||||||
|
system("wifi down >/dev/null 2>&1");
|
||||||
|
|
||||||
|
/* Remove every existing VAP so UniFi becomes the sole Wi-Fi owner. */
|
||||||
wlan_clear();
|
wlan_clear();
|
||||||
|
|
||||||
/* 2. Aplicar radio_table */
|
/* 2. Aplicar radio_table */
|
||||||
@@ -599,18 +744,33 @@ int wlan_apply_config(struct json_object *config_json,
|
|||||||
struct json_object *vap = json_object_array_get_idx(vt_arr, i);
|
struct json_object *vap = json_object_array_get_idx(vt_arr, i);
|
||||||
if (!vap) continue;
|
if (!vap) continue;
|
||||||
|
|
||||||
/* Buscar device UCI para este VAP */
|
/* A VAP without an explicit band is a model-wide WLAN. */
|
||||||
const char *radio_band = "ng";
|
const char *radio_band = NULL;
|
||||||
if (json_object_object_get_ex(vap, "radio", &v))
|
if (json_object_object_get_ex(vap, "radio", &v))
|
||||||
radio_band = json_object_get_string(v);
|
radio_band = json_object_get_string(v);
|
||||||
const char *device_name = "radio0";
|
if (radio_band && !strcmp(radio_band, "2g")) radio_band = "ng";
|
||||||
|
if (radio_band && !strcmp(radio_band, "5g")) radio_band = "na";
|
||||||
|
if (radio_band && !strcmp(radio_band, "6GHz")) radio_band = "6g";
|
||||||
|
int all_radios = !radio_band || !radio_band[0] ||
|
||||||
|
!strcmp(radio_band, "both") ||
|
||||||
|
!strcmp(radio_band, "all");
|
||||||
|
int applied = 0;
|
||||||
for (int j = 0; j < model->radio_map_len; j++) {
|
for (int j = 0; j < model->radio_map_len; j++) {
|
||||||
if (!strcmp(model->radio_map[j].band, radio_band)) {
|
if (!all_radios &&
|
||||||
device_name = model->radio_map[j].device;
|
strcmp(model->radio_map[j].band, radio_band))
|
||||||
break;
|
continue;
|
||||||
|
int section_idx = i * model->radio_map_len + j;
|
||||||
|
if (apply_vap(ctx, pkg, vap, model->radio_map[j].device,
|
||||||
|
mac_str, section_idx) != 0) {
|
||||||
|
uci_unload(ctx, pkg);
|
||||||
|
uci_free_context(ctx);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
applied++;
|
||||||
}
|
}
|
||||||
if (apply_vap(ctx, pkg, vap, device_name, mac_str, i) != 0) {
|
if (!applied) {
|
||||||
|
printf("[openuf] Ignoring VAP with unknown radio '%s'\n",
|
||||||
|
radio_band ? radio_band : "");
|
||||||
uci_unload(ctx, pkg);
|
uci_unload(ctx, pkg);
|
||||||
uci_free_context(ctx);
|
uci_free_context(ctx);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -632,10 +792,10 @@ int wlan_apply_config(struct json_object *config_json,
|
|||||||
uci_unload(ctx, pkg);
|
uci_unload(ctx, pkg);
|
||||||
uci_free_context(ctx);
|
uci_free_context(ctx);
|
||||||
|
|
||||||
/* 5. Aplicar cambios sin reiniciar (wifi reload recarga hostapd) */
|
/* Reload netifd for generated VLAN devices, then start only the new VAPs. */
|
||||||
printf("[openuf] Running wifi reload...\n");
|
printf("[openuf] Starting controller-managed Wi-Fi...\n");
|
||||||
system("ubus call network reload >/dev/null 2>&1");
|
system("ubus call network reload >/dev/null 2>&1");
|
||||||
system("wifi reload 2>/dev/null &");
|
system("wifi up >/dev/null 2>&1");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,6 +980,7 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
|
|||||||
const char *k11 = UCI_GET("ieee80211k");
|
const char *k11 = UCI_GET("ieee80211k");
|
||||||
const char *w11 = UCI_GET("ieee80211w");
|
const char *w11 = UCI_GET("ieee80211w");
|
||||||
const char *hidden = UCI_GET("hidden");
|
const char *hidden = UCI_GET("hidden");
|
||||||
|
const char *vap_id = UCI_GET("openuf_vap_id");
|
||||||
|
|
||||||
if (!ssid) ssid = "";
|
if (!ssid) ssid = "";
|
||||||
if (!device) device = "radio0";
|
if (!device) device = "radio0";
|
||||||
@@ -875,6 +1036,8 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
|
|||||||
json_object_object_add(o, "band_steering", json_object_new_boolean(bs_on));
|
json_object_object_add(o, "band_steering", json_object_new_boolean(bs_on));
|
||||||
json_object_object_add(o, "pmf_mode", json_object_new_string(pmf));
|
json_object_object_add(o, "pmf_mode", json_object_new_string(pmf));
|
||||||
json_object_object_add(o, "num_sta", json_object_new_int(0));
|
json_object_object_add(o, "num_sta", json_object_new_int(0));
|
||||||
|
if (valid_object_id(vap_id))
|
||||||
|
json_object_object_add(o, "id", json_object_new_string(vap_id));
|
||||||
json_object_array_add(arr, o);
|
json_object_array_add(arr, o);
|
||||||
#undef UCI_GET
|
#undef UCI_GET
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user