Newer
Older
/*
* config.c - controller configuration handling
*
* Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
*
* Author: anjan.chanda@iopsys.eu
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <json-c/json.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <uci.h>
#include <easy/easy.h> // TODO: remove wifi.h
#include <wifi.h> // TODO: remove wifi.h
#include "debug.h"
#include "utils.h"
#include "config.h"
#include "comm.h"
#include "msgqueue.h"
#include "worker.h"
#include "cntlr.h"
static void stax_add_entry(struct list_head *h, char *sta_macstr)
{
struct stax *n;
n = calloc(1, sizeof(struct stax));
if (n) {
snprintf(n->macstring, 18, "%s", sta_macstr);
list_add(&n->list, h);
}
}
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
struct uci_package *uci_load_pkg(struct uci_context **ctx, const char *config)
{
struct uci_package *pkg;
if (!*ctx) {
*ctx = uci_alloc_context();
if (!*ctx)
return NULL;
}
if (uci_load(*ctx, config, &pkg) != UCI_OK) {
free(*ctx);
return NULL;
}
return pkg;
}
int set_value(struct uci_context *ctx, struct uci_package *pkg,
struct uci_section *section, const char *key,
const char *value, enum uci_option_type type)
{
struct uci_ptr ptr = {0};
ptr.p = pkg;
ptr.s = section;
ptr.option = key;
ptr.value = value;
if (type == UCI_TYPE_STRING)
return uci_set(ctx, &ptr);
if (type == UCI_TYPE_LIST)
return uci_add_list(ctx, &ptr);
return -1;
}
struct uci_section *config_get_agent_section(struct uci_context *ctx,
struct uci_package *pkg, const char *type, const char *al_mac)
{
struct uci_element *e;
struct uci_section *section;
uci_foreach_element(&pkg->sections, e) {
const char *agent_id;
section = uci_to_section(e);
if (strcmp(section->type, type))
continue;
agent_id = uci_lookup_option_string(ctx, section, "agent_id");
if (agent_id && !strcmp(agent_id, al_mac))
return section;
}
return NULL;
}
int cntlr_config_add_agent(struct controller_config *c, char *al_mac)
{
struct uci_context *ctx = NULL;
struct uci_package *pkg;
struct uci_section *section;
int ret = -1;
pkg = uci_load_pkg(&ctx, "mapcontroller");
if (!pkg)
return ret;
section = config_get_agent_section(ctx, pkg, "agent-policy", al_mac);
if (section)
goto out_pkg;
ret = uci_add_section(ctx, pkg, "agent-policy", §ion);
if (ret)
goto out_pkg;
ret = uci_save(ctx, pkg);
if (ret)
goto out_pkg;
ret = set_value(ctx, pkg, section, "agent_id", al_mac, UCI_TYPE_STRING);
uci_commit(ctx, &pkg, false);
out_pkg:
uci_unload(ctx, pkg);
uci_free_context(ctx);
return ret;
}
void cntlr_config_dump(struct controller_config *c)
struct stax *x = NULL;
dbg("Controller config ---------\n");
dbg("Enabled: %d\n", c->enabled);
dbg("Registrar @5Ghz: %d\n", c->has_registrar_5g);
dbg("Registrar @2Ghz: %d\n", c->has_registrar_2g);
dbg("Credentials: Fronthaul\n");
for (i = 0; i < c->num_fh; i++) {
dbg(" Band : %d\n", c->fh[i].band);
dbg(" Security: 0x%x\n", c->fh[i].sec);
dbg(" ssid : %s\n", c->fh[i].ssid);
dbg(" vlan : %d\n\n", c->fh[i].vlanid);
dbg("Credentials: Backhaul\n");
for (i = 0; i < c->num_bk; i++) {
dbg(" Band : %d\n", c->bk[i].band);
dbg(" Security: 0x%x\n", c->bk[i].sec);
dbg(" ssid : %s\n", c->bk[i].ssid);
dbg(" vlan : %d\n\n", c->bk[i].vlanid);
dbg(" Id : " MACFMT "\n", MAC2STR(c->apolicy.agent_id));
dbg(" Steer-policy : %d\n", c->apolicy.policy);
dbg(" Util-threshold : %d\n", c->apolicy.util_threshold);
dbg(" RCPI-threshold : %d\n", c->apolicy.rcpi_threshold);
dbg(" Report scan : %d\n", c->apolicy.report_scan);
dbg(" Report assocfails : %d\n", c->apolicy.report_sta_assocfails);
dbg(" Report assocfails rate: %d\n", c->apolicy.report_sta_assocfails_rate);
dbg(" Report metric : %d\n", c->apolicy.report_metric_periodic);
dbg(" Report RCPI-thresh : %d\n", c->apolicy.report_rcpi_threshold);
dbg(" Report Util-thresh : %d\n", c->apolicy.report_util_threshold);
dbg(" RCPI hysteresis margin: %d\n", c->apolicy.rcpi_hysteresis_margin);
dbg(" Include STA stats : %d\n", c->apolicy.include_sta_stats);
dbg(" Include STA metric : %d\n", c->apolicy.include_sta_metric);
dbg(" Primary VLAN ID : %d\n", c->apolicy.pvid);
dbg(" PCP Default : %d\n", c->apolicy.pcp_default);
dbg(" Disallow bSTA P1 : %d\n", c->apolicy.disallow_bsta_p1);
dbg(" Disallow bSTA P2 : %d\n", c->apolicy.disallow_bsta_p2);
#if 0
// INIT_LIST_HEAD(&c->apolicy.steer_exlist); // TODO: remove INIT_LIST_HEAD
// INIT_LIST_HEAD(&c->apolicy.btmsteer_exlist);
list_for_each_entry(x, &c->apolicy.steer_exlist, list) {
dbg(" Disallowed STA : %s\n", x->macstring);
}
list_for_each_entry(x, &c->apolicy.btmsteer_exlist, list) {
dbg(" Disallowed BTM STA : %s\n", x->macstring);
}
#endif
int cntlr_config_defaults(struct controller *cntlr, struct controller_config *c)
memset(c, 0, sizeof(*c));
INIT_LIST_HEAD(&c->policylist);
static int cntlr_config_get_base(struct controller_config *c,
struct uci_section *s)
enum {
CNTLR_ENABLED,
CNTLR_REGISTRAR,
NUM_CNTLR_ATTRS,
};
const struct uci_parse_option opts[] = {
{ .name = "enabled", .type = UCI_TYPE_STRING },
{ .name = "registrar", .type = UCI_TYPE_STRING },
{ .name = "debug", .type = UCI_TYPE_STRING },
{ .name = "debug", .type = UCI_TYPE_STRING }
};
struct uci_option *tb[NUM_CNTLR_ATTRS];
uci_parse_section(s, opts, NUM_CNTLR_ATTRS, tb);
if (tb[CNTLR_ENABLED]) {
const char *val = tb[CNTLR_ENABLED]->v.string;
if (tb[CNTLR_REGISTRAR]) {
const char *val = tb[CNTLR_REGISTRAR]->v.string;
c->has_registrar_5g = !strstr(val, "5") ? false : true;
c->has_registrar_2g = !strstr(val, "2") ? false : true;
const char *debug = tb[CNTLR_DEBUG]->v.string;
c->debug_level = atoi(debug);
if (c->debug_level > verbose)
verbose = c->debug_level;
static int cntlr_config_get_credentials(struct controller_config *c,
struct uci_section *s)
enum {
CRED_BAND,
CRED_SSID,
CRED_SEC,
CRED_KEY,
CRED_VLAN,
NUM_CREDS,
};
const struct uci_parse_option opts[] = {
[CRED_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
[CRED_SSID] = { .name = "ssid", .type = UCI_TYPE_STRING },
[CRED_SEC] = { .name = "encryption", .type = UCI_TYPE_STRING },
[CRED_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
[CRED_VLAN] = { .name = "vlan", .type = UCI_TYPE_STRING },
[CRED_MAP] = { .name = "multi_ap", .type = UCI_TYPE_STRING },
[CRED_D_BSTA] = { .name = "disallow_bsta", .type = UCI_TYPE_STRING },
};
struct uci_option *tb[NUM_CREDS];
struct iface_credential *cred;
if (!strcmp(s->type, "fh-credentials")) {
if (c->num_fh >= 2)
return -1;
cred = &c->fh[c->num_fh++];
cred->mode = AP_WIFI_FBSS;
} else {
if (c->num_bk >= 2)
return -1;
cred->mode = AP_WIFI_BBSS;
if (tb[CRED_BAND]) {
if (atoi(tb[CRED_BAND]->v.string) == 5)
cred->band = BAND_5;
cred->band = BAND_2;
strncpy((char *) cred->ssid, tb[CRED_SSID]->v.string, 32);
if (tb[CRED_SEC]) {
const char *sec = tb[CRED_SEC]->v.string;
if (!strncmp(sec, "psk3", 4))
cred->sec = WIFI_SECURITY_WPA3PSK;
else if (!strncmp(sec, "psk2", 4))
cred->sec = WIFI_SECURITY_WPA2PSK;
else if (!strncmp(sec, "psk", 3))
cred->sec = WIFI_SECURITY_WPAPSK;
//TODO: ciphers (if any)
}
strncpy((char *) cred->key, tb[CRED_KEY]->v.string, 63);
if (tb[CRED_VLAN])
cred->vlanid = atoi(tb[CRED_VLAN]->v.string);
if (tb[CRED_MAP])
cred->multi_ap = atoi(tb[CRED_MAP]->v.string);
if (tb[CRED_D_BSTA])
cred->disallow_bsta = atoi(tb[CRED_D_BSTA]->v.string);
static int cntlr_config_get_agent_policy(struct controller_config *c,
struct uci_section *s)
POL_STEER_EXCLUDE,
POL_STEER_EXCLUDE_BTM,
POL_STEER,
POL_UTIL_TH,
POL_RCPI_TH,
POL_RPT_SCAN,
POL_RPT_ASSOC_FAILS,
POL_RPT_ASSOC_FAILS_RATE,
POL_RPT_METRIC_PERIODIC,
POL_RPT_RCPI_TH,
POL_RPT_UTIL_TH,
POL_RPT_HYS_MARGIN,
POL_INC_STA_STATS,
POL_INC_STA_METRIC,
POL_PVID,
POL_PCP_DEFAULT,
POL_DISALLOW_BSTA_P1,
POL_DISALLOW_BSTA_P2,
NUM_POLICIES,
};
const struct uci_parse_option opts[] = {
{ .name = "agent_id", .type = UCI_TYPE_STRING },
{ .name = "steer_exclude", .type = UCI_TYPE_LIST },
{ .name = "steer_exclude_btm", .type = UCI_TYPE_LIST },
{ .name = "steer_policy", .type = UCI_TYPE_STRING },
{ .name = "util_threshold", .type = UCI_TYPE_STRING },
{ .name = "rcpi_threshold", .type = UCI_TYPE_STRING },
{ .name = "report_scan", .type = UCI_TYPE_STRING },
{ .name = "report_sta_assocfails", .type = UCI_TYPE_STRING },
{ .name = "report_sta_assocfails_rate", .type = UCI_TYPE_STRING },
{ .name = "report_metric_periodic", .type = UCI_TYPE_STRING },
{ .name = "report_rcpi_threshold", .type = UCI_TYPE_STRING },
{ .name = "report_util_threshold", .type = UCI_TYPE_STRING },
{ .name = "rcpi_hysteresis_margin", .type = UCI_TYPE_STRING },
{ .name = "include_sta_stats", .type = UCI_TYPE_STRING },
{ .name = "include_sta_metric", .type = UCI_TYPE_STRING },
{ .name = "pvid", .type = UCI_TYPE_STRING },
{ .name = "pcp_default", .type = UCI_TYPE_STRING },
{ .name = "disallow_bsta_p1", .type = UCI_TYPE_STRING },
{ .name = "disallow_bsta_p2", .type = UCI_TYPE_STRING },
};
struct uci_option *tb[NUM_POLICIES];
struct uci_element *x;
a = calloc(1, sizeof(*a));
if (!a)
return -1;
INIT_LIST_HEAD(&a->steer_exlist);
INIT_LIST_HEAD(&a->btmsteer_exlist);
uci_parse_section(s, opts, NUM_POLICIES, tb);
if (tb[POL_AGENT_ID]) {
const char *val = tb[POL_AGENT_ID]->v.string;
if (tb[POL_STEER_EXCLUDE]) {
uci_foreach_element(&tb[POL_STEER_EXCLUDE]->v.list, x) {
stax_add_entry(&a->steer_exlist, x->name);
}
}
if (tb[POL_STEER_EXCLUDE_BTM]) {
uci_foreach_element(&tb[POL_STEER_EXCLUDE_BTM]->v.list, x) {
stax_add_entry(&a->btmsteer_exlist, x->name);
}
}
a->util_threshold = atoi(tb[POL_UTIL_TH]->v.string);
a->rcpi_threshold = atoi(tb[POL_RCPI_TH]->v.string);
atoi(tb[POL_RPT_SCAN]->v.string) == 1 ? true : false;
atoi(tb[POL_RPT_ASSOC_FAILS]->v.string) == 1 ?
if (tb[POL_RPT_ASSOC_FAILS_RATE]) {
a->report_sta_assocfails_rate =
atoi(tb[POL_RPT_ASSOC_FAILS_RATE]->v.string);
}
if (tb[POL_RPT_HYS_MARGIN]) {
a->rcpi_hysteresis_margin =
atoi(tb[POL_RPT_HYS_MARGIN]->v.string);
}
a->pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);
atoi(tb[POL_DISALLOW_BSTA_P1]->v.string) == 1 ?
atoi(tb[POL_DISALLOW_BSTA_P2]->v.string) == 1 ?
int cntlr_config_reload(struct controller_config *cfg)
struct uci_context *ctx;
struct uci_package *pkg;
struct uci_element *e;
ctx = uci_alloc_context();
if (uci_load(ctx, "mapcontroller", &pkg)) {
uci_free_context(ctx);
return -1;
}
uci_foreach_element(&pkg->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "controller")) {
cntlr_config_get_base(cfg, s);
} else if (!strcmp(s->type, "fh-credentials") ||
!strcmp(s->type, "bk-credentials")) {
cntlr_config_get_credentials(cfg, s);
} else if (!strcmp(s->type, "agent-policy")) {
cntlr_config_get_agent_policy(cfg, s);
}
static int clean_steer_btm_excl(struct agent_policy *p)
{
struct stax *n, *tmp;
list_for_each_entry_safe(n, tmp, &p->btmsteer_exlist, list) {
list_del(&n->list);
free(n);
}
return 0;
}
static int clean_steer_excl(struct agent_policy *p)
{
struct stax *n, *tmp;
list_for_each_entry_safe(n, tmp, &p->steer_exlist, list) {
list_del(&n->list);
free(n);
}
return 0;
}
int clean_agent_policies(struct controller_config *cfg)
{
struct agent_policy *p, *tmp;
list_for_each_entry_safe(p, tmp, &cfg->policylist, list) {
clean_steer_btm_excl(p);
clean_steer_excl(p);