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"
int clean_fhlist(struct controller_config *cfg);
int clean_bklist(struct controller_config *cfg);
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);
}
}
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
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;
}
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
int set_value_by_string(const char *package, const char *section,
const char *key, const char *value, enum uci_option_type type)
{
struct uci_ptr ptr = {0};
struct uci_context *ctx;
int rv;
ctx = uci_alloc_context();
if (!ctx)
return -1;
ptr.package = package;
ptr.section = section;
ptr.option = key;
ptr.value = value;
if (type == UCI_TYPE_STRING)
rv = uci_set(ctx, &ptr);
if (type == UCI_TYPE_LIST)
rv = uci_add_list(ctx, &ptr);
uci_commit(ctx, &ptr.p, false);
uci_free_context(ctx);
return rv;
}
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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;
struct iface_credential *cred;
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");
list_for_each_entry(cred, &c->fhlist, list) {
dbg(" Band : %d\n", cred->band);
dbg(" Security: 0x%x\n", cred->sec);
dbg(" ssid : %s\n", cred->ssid);
dbg(" vlan : %d\n\n", cred->vlanid);
list_for_each_entry(cred, &c->bklist, list) {
dbg(" Band : %d\n", cred->band);
dbg(" Security: 0x%x\n", cred->sec);
dbg(" ssid : %s\n", cred->ssid);
dbg(" vlan : %d\n\n", cred->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);
INIT_LIST_HEAD(&c->bridgelist);
INIT_LIST_HEAD(&c->bklist);
INIT_LIST_HEAD(&c->fhlist);
static int cntlr_config_get_base(struct controller_config *c,
struct uci_section *s)
enum {
CNTLR_ENABLED,
CNTLR_REGISTRAR,
CNTLR_AL_BRIDGE,
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 = "al_bridge", .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;
if (tb[CNTLR_AL_BRIDGE]) {
const char *iface;
iface = tb[CNTLR_AL_BRIDGE]->v.string;
strncpy(c->al_bridge, iface, sizeof(c->al_bridge) - 1);
} else /* Default to br-lan if non-specfied */
strncpy(c->al_bridge, "br-lan", sizeof(c->al_bridge) - 1);
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,
CRED_BK_SSID,
CRED_BK_KEY,
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 },
[CRED_BK_SSID] = { .name = "bk_ssid", .type = UCI_TYPE_STRING },
[CRED_BK_KEY] = { .name = "bk_key", .type = UCI_TYPE_STRING },
};
struct uci_option *tb[NUM_CREDS];
struct iface_credential *cred;
struct list_head *iface_list;
int *num;
if (c->num_fh >= 32)
return -1;
cred = calloc(1, sizeof(*cred));
if (!cred)
num = &c->num_fh;
cred->mode = AP_WIFI_FBSS;
iface_list = &c->fhlist;
if (c->num_bk >= 32)
cred = calloc(1, sizeof(*cred));
if (!cred)
return -1;
num = &c->num_bk;
cred->mode = AP_WIFI_BBSS;
iface_list = &c->bklist;
if (tb[CRED_BAND]) {
if (atoi(tb[CRED_BAND]->v.string) == 5)
cred->band = BAND_5;
cred->band = BAND_2;
else
cred->band = BAND_UNKNOWN;
} else
cred->band = BAND_UNKNOWN;
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, "psk-mixed", 9)) {
cred->sec |= WIFI_SECURITY_WPAPSK;
cred->sec |= WIFI_SECURITY_WPA2PSK;
} else if (!strncmp(sec, "psk2", 4)) {
cred->sec |= WIFI_SECURITY_WPA2PSK;
} else if (!strncmp(sec, "psk", 3)) {
cred->sec |= WIFI_SECURITY_WPAPSK;
} else if (!strncmp(sec, "wpa-mixed", 9)) {
cred->sec |= WIFI_SECURITY_WPA;
cred->sec |= WIFI_SECURITY_WPA2;
} else if (!strncmp(sec, "wpa2", 4)) {
cred->sec |= WIFI_SECURITY_WPA2;
} else if (!strncmp(sec, "wpa", 3)) {
cred->sec |= WIFI_SECURITY_WPA;
} else if (!strncmp(sec, "none", 4)) {
cred->sec |= WIFI_SECURITY_NONE;
} else if (!strncmp(sec, "open", 4)) {
cred->sec |= WIFI_SECURITY_NONE;
} else {
free(cred);
return -1;
}
strncpy((char *) cred->key, tb[CRED_KEY]->v.string, 64);
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);
if (tb[CRED_BK_SSID])
strncpy((char *) cred->bk_ssid, tb[CRED_BK_SSID]->v.string, sizeof(cred->bk_ssid) - 1);
if (tb[CRED_BK_KEY])
strncpy((char *) cred->bk_key, tb[CRED_BK_KEY]->v.string, sizeof(cred->bk_key) - 1);
(*num)++;
list_add_tail(&cred->list, iface_list);
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
static struct netif_vlan *cntlr_config_get_vlan_by_id(struct controller_config *cfg,
uint8_t id)
{
struct netif_vlan *vlan;
list_for_each_entry(vlan, &cfg->bridgelist, list) {
if (vlan->id == id)
return vlan;
}
return NULL;
}
static int cntlr_config_get_vlan(struct controller_config *c,
struct uci_section *s)
{
enum {
VLAN_NETWORK,
VLAN_PROTO,
VLAN_ID,
VLAN_IP_ADDR,
NUM_OPTS,
};
const struct uci_parse_option opts[] = {
[VLAN_NETWORK] = { .name = "network", .type = UCI_TYPE_STRING },
[VLAN_PROTO] = { .name = "proto", .type = UCI_TYPE_STRING },
[VLAN_ID] = { .name = "id", .type = UCI_TYPE_STRING },
[VLAN_IP_ADDR] = { .name = "ipaddr", .type = UCI_TYPE_STRING },
};
struct uci_option *tb[NUM_OPTS];
struct netif_vlan *vlan;
uci_parse_section(s, opts, NUM_OPTS, tb);
if (tb[VLAN_ID]) {
uint8_t id;
id = atoi(tb[VLAN_ID]->v.string);
vlan = cntlr_config_get_vlan_by_id(c, id);
if (!vlan) {
vlan = calloc(1, sizeof(*vlan));
if (!vlan)
return -1;
vlan->id = id;
}
} else {
dbg("No vlan ID provided with vlan section!\n");
return -1;
}
if (tb[VLAN_NETWORK])
strncpy((char *) vlan->network, tb[VLAN_NETWORK]->v.string,
sizeof(vlan->network) - 1);
else
strncpy((char *) vlan->network, "br-lan", sizeof(vlan->network) - 1);
if (tb[VLAN_PROTO])
strncpy((char *) vlan->proto, tb[VLAN_PROTO]->v.string,
sizeof(vlan->proto) - 1);
if (tb[VLAN_IP_ADDR]) {
trace("ip addr = %s\n", tb[VLAN_IP_ADDR]->v.string);
inet_pton(AF_INET, tb[VLAN_IP_ADDR]->v.string, &(vlan->ipaddr.addr.ip4.s_addr));
trace("ipaddr = %d\n", vlan->ipaddr.addr.ip4.s_addr);
}
list_add(&vlan->list, &c->bridgelist);
return 0;
}
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;
struct iface_credential *cred;
int i;
ctx = uci_alloc_context();
if (uci_load(ctx, "mapcontroller", &pkg)) {
uci_free_context(ctx);
return -1;
}
/**
* As credentials don't have any unique identifier, clean and recreate
* every reload
*/
clean_fhlist(cfg);
clean_bklist(cfg);
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);
} else if (!strcmp(s->type, "vlan")) {
cntlr_config_get_vlan(cfg, s);
}
/** Map vlan to bk-credentials */
list_for_each_entry(cred, &cfg->bklist, list) {
struct netif_vlan *vlan;
vlan = cntlr_config_get_vlan_by_id(cfg, cred->vlanid);
if (!vlan)
continue;
cred->br = vlan;
}
/** Map vlan to fh-credentials */
list_for_each_entry(cred, &cfg->fhlist, list) {
struct netif_vlan *vlan;
vlan = cntlr_config_get_vlan_by_id(cfg, cred->vlanid);
if (!vlan)
continue;
cred->br = vlan;
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);
list_del(&p->list);
free(p);
}
return 0;
}
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
int clean_bridgelist(struct controller_config *cfg)
{
struct netif_vlan *p, *tmp;
list_for_each_entry_safe(p, tmp, &cfg->bridgelist, list) {
list_del(&p->list);
free(p);
}
return 0;
}
int clean_fhlist(struct controller_config *cfg)
{
struct iface_credential *p, *tmp;
list_for_each_entry_safe(p, tmp, &cfg->fhlist, list) {
list_del(&p->list);
free(p);
}
return 0;
}
int clean_bklist(struct controller_config *cfg)
{
struct iface_credential *p, *tmp;
list_for_each_entry_safe(p, tmp, &cfg->bklist, list) {
list_del(&p->list);
free(p);
}
return 0;
}
int cntlr_config_clean(struct controller_config *cfg)
{
clean_bridgelist(cfg);
clean_fhlist(cfg);
clean_bklist(cfg);