Skip to content
Snippets Groups Projects
config.c 32.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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 <string.h>
    
    #include <unistd.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>
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    #include <bufutil.h>
    
    #include "utils/debug.h"
    #include "utils/utils.h"
    
    #include "config.h"
    #include "cntlr.h"
    
    #include "config.h"
    
    #define CONFIG_DEFAULT_RCPI_TH_5G 86
    #define CONFIG_DEFAULT_RCPI_TH_2G 70
    
    
    #define list_copy(a, b, type)                                           \
    	({                                                              \
    		typeof(type *) ____ptr, ____tmp;                        \
    		list_for_each_entry_safe(____ptr, ____tmp, a, list) {   \
    			list_del(&____ptr->list);                       \
    			list_add_tail(&____ptr->list, b);               \
    		}                                                       \
    	})
    
    
    
    #define list_memcmp(a, b, type, offset)                                      \
    	({                                                                   \
    		int z = 0;                                                   \
    		typeof(type *) ____ptr, ____p;                               \
    		____p = list_first_entry(a, type, list);                     \
    		list_for_each_entry(____ptr, b, list) {                      \
    			if (memcmp(____p, ____ptr, sizeof(type) - offset)) { \
    				z = 1;                                       \
    				break;                                       \
    			}                                                    \
    			____p = list_entry(____p->list.next, type, list);    \
    		}                                                            \
    		z;                                                           \
    	})
    
    
    
    
    #define list_policy_memcmp(c, d, t, o)                                       \
    
    		typeof(t *) ____d, ____c;                                    \
    		____c = list_first_entry(c, t, list);                        \
    		list_for_each_entry(____d, d, list) {                        \
    			if (memcmp(____c, ____d, sizeof(t) - o)) {           \
    
    				____d->is_policy_diff = 1;                   \
    			} else {                                             \
    				if (list_memcmp(&____d->radiolist,           \
    				    &____c->radiolist, struct radio_policy,  \
    				    (sizeof(struct radio_policy) -           \
    				    offsetof(struct radio_policy, list)))) { \
    					z = 1;                               \
    					____d->is_policy_diff = 1;           \
    				}                                            \
    
    			____c = list_entry(____c->list.next, t, list);       \
    
    		}                                                            \
    		z;                                                           \
    	})
    
    #define list_for_multiple_entry(pos, pos1, head, head1, field, field1)					\
    	for (pos = list_first_entry(head, __typeof__(*pos), field),					\
    			pos1 = list_first_entry(head1, __typeof__(*pos1), field1);			\
    			(&pos->field != (head)) && (&pos1->field1 != (head1));				\
    			pos = list_entry(pos->field.next, __typeof__(*pos), field),			\
    			pos1 = list_entry(pos1->field1.next, __typeof__(*pos1), field1))
    
    
    static int clean_agentlist(struct node_policy *p)
    
    static int clean_steer_btm_excl(struct node_policy *p)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct stax *n = NULL, *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 node_policy *p)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct stax *n = NULL, *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)
    {
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct node_policy *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->nodelist, list) {
    
    		clean_steer_btm_excl(p);
    		clean_steer_excl(p);
    
    		clean_agentlist(p);
    
    int clean_cred_list(struct controller_config *cfg)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct iface_credential *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->aplist, list) {
    		list_del(&p->list);
    		free(p);
    	}
    
    	return 0;
    }
    
    int clean_radio_list(struct controller_config *cfg)
    {
    	struct radio_policy *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->radiolist, list) {
    
    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);
    	}
    }
    
    
    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) {
    
    		uci_free_context(*ctx);
    		*ctx = NULL;
    
    		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;
    }
    
    
    
    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;
    
    
    	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;
    }
    
    
    bool uci_set_option(char *package_name, char *section_type,
    		char *search_key, char *search_val,
    		char *option, char *value)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_element *e;
    
    	if (!package_name || !search_val || !option || !value)
    		return false;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		return false;
    
    	if (uci_load(ctx, package_name, &pkg)) {
    		uci_free_context(ctx);
    		return false;
    	}
    
    	uci_foreach_element(&pkg->sections, e) {
    		struct uci_section *s = uci_to_section(e);
    
    		if (!strcmp(s->type, section_type)) {
    			struct uci_option *opt = uci_lookup_option(ctx, s,
    					search_key);
    
    			if (!opt || opt->type != UCI_TYPE_STRING)
    				continue;
    			if (strcmp(opt->v.string, search_val) == 0) {
    				struct uci_ptr ptr = {0};
    
    				ptr.value = value;
    				ptr.package = package_name;
    				ptr.section = s->e.name;
    				ptr.option = option;
    				ptr.target = UCI_TYPE_OPTION;
    				if (uci_lookup_ptr(ctx, &ptr, NULL, false) ||
    						!UCI_LOOKUP_COMPLETE)
    					break;
    				if (uci_set(ctx, &ptr) == UCI_OK)
    					uci_save(ctx, ptr.p);
    				break;
    			}
    		}
    	}
    	uci_commit(ctx, &pkg, false);
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    	return false;
    }
    
    
    struct uci_section *config_get_section(struct uci_context *ctx,
    		struct uci_package *pkg, const char *type, const char *key,
    		const char *value)
    {
    	struct uci_element *e;
    	struct uci_section *section;
    
    	/* get the wet iface section */
    	uci_foreach_element(&pkg->sections, e) {
    		const char *c_value;
    
    		section = uci_to_section(e);
    		if (strcmp(section->type, type))
    			continue;
    
    		c_value = uci_lookup_option_string(ctx, section, key);
    		if (c_value && !strcmp(c_value, value))
    			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;
    
    	struct uci_ptr ptr = {0};
    	char name[32] = { 0 };
    	static const char s[2] = ":";
    	char *token;
    	char mac[18] = { 0 };
    
    	int ret = -1;
    
    	pkg = uci_load_pkg(&ctx, "mapcontroller");
    	if (!pkg)
    		return ret;
    
    
    	section = config_get_section(ctx, pkg, "node", "agent_id", al_mac);
    
    	ret = uci_add_section(ctx, pkg, "node", &section);
    
    	strncpy(mac, al_mac, sizeof(mac) - 1);
    	strncpy(name, "node_", sizeof(name) - 1);
    
    	token = strtok(mac, s);
    	while (token != NULL) {
    		snprintf(name + strlen(name),
    			 (sizeof(name) - strlen(name)),
    			 "%s", token);
    		token = strtok(NULL, s);
    	}
    
    	ptr.p = pkg;
    	ptr.s = section;
    	ptr.value = name;
    	uci_rename(ctx, &ptr);
    
    
    	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;
    }
    
    
    int cntlr_config_add_agent_radio(struct controller_config *c, char *al_mac,
    		char *radio_mac, char *band)
    {
    	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_section(ctx, pkg, "radio", "macaddr", radio_mac);
    	if (!section) {
    
    		char name[32] = { 0 };
    		static const char s[2] = ":";
    		char *token;
    		char mac[18] = { 0 };
    		struct uci_ptr ptr = {0};
    
    
    		ret = uci_add_section(ctx, pkg, "radio", &section);
    		if (ret)
    			goto out_pkg;
    
    
    		strncpy(mac, radio_mac, 18);
    		strncpy(name, "radio_", sizeof(name));
    
    		token = strtok(mac, s);
    		while (token != NULL) {
    			snprintf(name + strlen(name),
    				(sizeof(name) - strlen(name)),
    				"%s", token);
    			token = strtok(NULL, s);
    		}
    
    		ptr.p = pkg;
    		ptr.s = section;
    		ptr.value = name;
    		uci_rename(ctx, &ptr);
    
    	}
    
    	ret = uci_save(ctx, pkg);
    	if (ret)
    		goto out_pkg;
    
    	ret = set_value(ctx, pkg, section, "agent_id", al_mac, UCI_TYPE_STRING);
    
    	if (ret)
    		goto out_pkg;
    
    
    	ret = set_value(ctx, pkg, section, "macaddr", radio_mac, UCI_TYPE_STRING);
    
    	if (ret)
    		goto out_pkg;
    
    
    	ret = set_value(ctx, pkg, section, "band", band, UCI_TYPE_STRING);
    
    	if (ret)
    		goto out_pkg;
    
    
    	uci_commit(ctx, &pkg, false);
    
    out_pkg:
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    	return ret;
    }
    
    
    
    #if 0
    void cntlr_dump_node_policy(struct node_policy *np)
    {
    	dbg("Dump node policy for agent "MACFMT"\n", MAC2STR(np->agent_id));
    	dbg("agent_id "MACFMT"\n", MAC2STR(np->agent_id));
    	dbg("bk_ul_mac "MACFMT"\n", MAC2STR(np->bk_ul_mac));
    	dbg("bk_dl_mac "MACFMT"\n", MAC2STR(np->bk_dl_mac));
    	dbg("type %d\n", np->type);
    	dbg("pvid %u\n", np->pvid);
    	dbg("pcp %u\n", np->pcp);
    	dbg("report_scan %d\n", np->report_scan);
    	dbg("report_sta_assocfails %d\n", np->report_sta_assocfails);
    	dbg("report_sta_assocfails_rate %d\n", np->report_sta_assocfails_rate);
    	dbg("report_metric_periodic %u\n", np->report_metric_periodic);
    	dbg("steer_disallow %d\n", np->steer_disallow);
    	dbg("coordinated_cac %d\n", np->coordinated_cac);
    	dbg("traffic_separation %d\n", np->traffic_separation);
    	dbg("sta_steer %d\n", np->sta_steer);
    	dbg("num_steer_stas %d\n", np->num_steer_stas);
    	dbg("num_btmsteer_stas %d\n", np->num_btmsteer_stas);
    }
    
    
    
    void cntlr_config_dump(struct controller_config *c)
    
    	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("Enable STA steer: %d\n", c->enable_sta_steer);
    	dbg("Enable BSTA steer: %d\n", c->enable_bsta_steer);
    
    	dbg("Use bcn metrics to steer: %d\n", c->use_bcn_metrics);
    	dbg("Use uSTA metrics to steer: %d\n", c->use_usta_metrics);
    
    	list_for_each_entry(cred, &c->aplist, list) {
    
    		dbg("  Band    : %d\n", cred->band);
    		dbg("  Security: 0x%x\n", cred->sec);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		dbg("  Key     :\n");
    
    		dbg("  ssid    : %s\n", cred->ssid);
    		dbg("  vlan    : %d\n\n", cred->vlanid);
    
    	dbg("Agents policy: Default\n");
    
    	//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("  Disallow bSTA P1      : %d\n", c->apolicy->disallow_bsta_p1);
    	dbg("  Disallow bSTA P2      : %d\n", c->apolicy->disallow_bsta_p2);
    	dbg("  Is policy diff        : %d\n", c->apolicy->is_policy_diff);
    
    #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
    
    
    	dbg("---------------------------\n");
    
    int cntlr_config_defaults(struct controller *cntlr, struct controller_config *cfg)
    
    	memset(cfg, 0, sizeof(*cfg));
    	INIT_LIST_HEAD(&cfg->radiolist);
    	INIT_LIST_HEAD(&cfg->nodelist);
    	INIT_LIST_HEAD(&cfg->aplist);
    
    	INIT_LIST_HEAD(&cfg->sclist);
    
    	return 0;
    
    static int cntlr_config_get_base(struct controller_config *c,
    						struct uci_section *s)
    
    	enum {
    		CNTLR_ENABLED,
    		CNTLR_REGISTRAR,
    
    		CNTLR_RESEND_NUM,
    
    		CNTLR_ENABLE_STA_STEER,
    		CNTLR_ENABLE_BSTA_STEER,
    
    		CNTLR_USE_BCN_METRICS,
    		CNTLR_USE_USTA_METRICS,
    
    		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 = "resend_num", .type = UCI_TYPE_STRING },
    
    		{ .name = "enable_sta_steer", .type = UCI_TYPE_STRING },
    		{ .name = "enable_bsta_steer", .type = UCI_TYPE_STRING },
    
    		{ .name = "use_bcn_metrics", .type = UCI_TYPE_STRING },
    		{ .name = "use_usta_metrics", .type = UCI_TYPE_STRING },
    
    		{ .name = "steer_module", .type = UCI_TYPE_LIST },
    
    	};
    	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;
    
    		c->enabled = atoi(val) == 1 ? true : false;
    
    	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;
    
    	if (tb[CNTLR_DEBUG]) {
    
    		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_RESEND_NUM]) {
    		const char *val = tb[CNTLR_RESEND_NUM]->v.string;
    
    		c->resend_num = atoi(val);
    	}
    
    
    	if (tb[CNTLR_ENABLE_STA_STEER]) {
    		const char *val = tb[CNTLR_ENABLE_STA_STEER]->v.string;
    
    		c->enable_sta_steer = atoi(val) == 1 ? true : false;
    	}
    
    	if (tb[CNTLR_ENABLE_BSTA_STEER]) {
    		const char *val = tb[CNTLR_ENABLE_BSTA_STEER]->v.string;
    
    		c->enable_bsta_steer = atoi(val) == 1 ? true : false;
    	}
    
    
    	if (tb[CNTLR_USE_BCN_METRICS]) {
    		const char *val = tb[CNTLR_USE_BCN_METRICS]->v.string;
    
    		c->use_bcn_metrics = atoi(val) == 1 ? true : false;
    	}
    
    	if (tb[CNTLR_USE_USTA_METRICS]) {
    		const char *val = tb[CNTLR_USE_USTA_METRICS]->v.string;
    
    		c->use_usta_metrics = atoi(val) == 1 ? true : false;
    	}
    
    
    	if (tb[CNTLR_STEER_MODULE]) {
    		struct uci_element *e;
    		struct steer_control_config *sc;
    
    		dbg("Steer module: ");
    		uci_foreach_element(&tb[CNTLR_STEER_MODULE]->v.list, e) {
    			dbg("%s ", e->name);
    
    			sc = calloc(1, sizeof(*sc));
    			if (sc) {
    				strncpy(sc->name, e->name, 63);
    				list_add_tail(&sc->list, &c->sclist);
    			}
    		}
    		dbg("\n");
    	}
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    static int cntlr_config_get_wsc_attributes(struct controller_config *cfg,
    					   struct iface_credential *cred)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_element *e;
    
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		return -1;
    
    	if (uci_load(ctx, "ieee1905", &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, "ap")) {
    			uint8_t default_dev_type[8] = { 0x00, 0x06, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01 }; /* default WPS oui */
    			const char *manufacturer, *model_name, *device_name;
    			const char *model_number, *serial_number, *device_type;
    			uint32_t freqband;
    			const char *band;
    
    			band = uci_lookup_option_string(ctx, s, "band");
    			if (!band || atoi(band) == 0)
    				continue;
    
    			if (atoi(band) == 5)
    				freqband = BAND_5;
    			else if (atoi(band) == 2)
    				freqband = BAND_2;
    			else if (atoi(band) == 6)
    				freqband = BAND_6;
    			else
    				continue;
    
    			if (cred->band != freqband)
    				continue;
    
    			manufacturer = uci_lookup_option_string(ctx, s, "manufacturer");
    			if (manufacturer)
    				strncpy(cred->manufacturer, manufacturer, 64);
    
    			model_name = uci_lookup_option_string(ctx, s, "model_name");
    			if (model_name)
    				strncpy(cred->model_name, model_name, 32);
    
    			device_name = uci_lookup_option_string(ctx, s, "device_name");
    			if (device_name)
    				strncpy(cred->device_name, device_name, 32);
    
    			model_number = uci_lookup_option_string(ctx, s, "model_number");
    			if (model_number)
    				strncpy(cred->model_number, model_number, 32);
    
    			serial_number = uci_lookup_option_string(ctx, s, "serial_number");
    			if (serial_number)
    				strncpy(cred->serial_number, serial_number, 32);
    
    			memcpy(cred->device_type, default_dev_type, 8);
    			device_type = uci_lookup_option_string(ctx, s, "device_type");
    			if (device_type) {
    				int ret;
    				uint8_t oui[4] = {0};
    				uint16_t category = 0, sub_category = 0;
    
    				ret = sscanf(device_type, "%02hu-%02hhx%02hhx%02hhx%02hhx-%02hu",
    					     &category,
    					     &oui[0], &oui[1], &oui[2], &oui[3],
    					     &sub_category);
    				if (ret == 6) {
    					buf_put_be16(&cred->device_type[0], category);
    					memcpy(&cred->device_type[2], oui, 4);
    					buf_put_be16(&cred->device_type[6], sub_category);
    				}
    			}
    
    			break;
    		}
    	}
    
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    	return 0;
    }
    
    
    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_TYPE,
    
    		CRED_D_BSTA,
    
    		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 = "vid", .type = UCI_TYPE_STRING },
    		[CRED_TYPE] = { .name = "type", .type = UCI_TYPE_STRING },
    		[CRED_D_BSTA] = { .name = "disallow_bsta", .type = UCI_TYPE_LIST },
    
    		[CRED_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING }
    
    	};
    	struct uci_option *tb[NUM_CREDS];
    	struct iface_credential *cred;
    
    	bool use_default_security = false;
    
    	if (c->num_bss >= 32)
    		return -1;
    
    	cred = calloc(1, sizeof(*cred));
    	if (!cred)
    		return -1;
    
    	uci_parse_section(s, opts, NUM_CREDS, tb);
    
    	if (tb[CRED_BAND]) {
    		if (atoi(tb[CRED_BAND]->v.string) == 5)
    
    		else if (atoi(tb[CRED_BAND]->v.string) == 2)
    
    		else
    			cred->band = BAND_UNKNOWN;
    	} else
    		cred->band = BAND_UNKNOWN;
    
    	if (tb[CRED_SSID])
    
    		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, "sae-mixed", 9)) {
    			cred->sec |= WIFI_SECURITY_WPA3PSK;
    			cred->sec |= WIFI_SECURITY_WPA3PSK_T;
    		} else if (!strncmp(sec, "sae", 3)) {
    			cred->sec |= WIFI_SECURITY_WPA3PSK;
    		} else 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;
    		}
    
    
    		//TODO: ciphers (if any)
    
    	} else {
    		use_default_security = true;
    
    	if (tb[CRED_KEY])
    
    		strncpy((char *) cred->key, tb[CRED_KEY]->v.string, 64);
    
    	if (tb[CRED_VLAN])
    
    		cred->vlanid = (uint16_t) atoi(tb[CRED_VLAN]->v.string);
    
    	if (tb[CRED_TYPE]) {
    		const char *type = tb[CRED_TYPE]->v.string;
    
    
    		if (!strcmp(type, "backhaul")) {
    
    			cred->multi_ap = 1;
    
    			cred->mode = AP_WIFI_BBSS;
    
    		} else if (!strcmp(type, "fronthaul")) {
    
    			cred->multi_ap = 2;
    
    			cred->mode = AP_WIFI_FBSS;
    
    		} else if (!strcmp(type, "combined")) {
    			cred->multi_ap = 3;
    
    			cred->mode = AP_WIFI_COMBINED;
    
    		} else {
    			free(cred);
    			return -1;
    		}
    
    	} else {
    		cred->multi_ap = 2; /* default to fhbss */
    
    		cred->mode = AP_WIFI_FBSS;
    
    	if (use_default_security) {
    		cred->sec |= WIFI_SECURITY_WPA3PSK;
    		if (!!(cred->multi_ap & 2))
    			cred->sec |= WIFI_SECURITY_WPA3PSK_T;
    	}
    
    
    	if (tb[CRED_D_BSTA]) {
    		struct uci_element *x;
    
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    		uci_foreach_element(&tb[CRED_D_BSTA]->v.list, x) {
    
    			cred->disallow_bsta |= atoi(x->name);
    
    	if (tb[CRED_ENABLED])
    		cred->enabled = atoi(tb[CRED_ENABLED]->v.string);
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    
    	cntlr_config_get_wsc_attributes(c, cred);
    
    
    	list_add_tail(&cred->list, &c->aplist);
    
    	return 0;
    
    struct node_policy *cntlr_config_get_node_by_mac(struct controller_config *cfg,
    							uint8_t *macaddr)
    {
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct node_policy *node = NULL;
    
    
    	list_for_each_entry(node, &cfg->nodelist, list) {
    		if (!memcmp(node->agent_id, macaddr, 6))
    			return node;
    	}
    
    	return NULL;
    }
    
    static int cntlr_config_get_agent_node(struct controller_config *c,
    
    						struct uci_section *s)
    
    	enum {
    
    		NODE_AGENT_ID,
    		NODE_BK_UL_MAC,
    		NODE_BK_DL_MAC,
    		NODE_BK_TYPE,
    		NODE_PVID,
    		NODE_PCP,
    		NODE_RPT_ASSOC_FAILS,
    		NODE_RPT_ASSOC_FAILS_RATE,
    		NODE_RPT_METRIC_PERIODIC,
    		NODE_RPT_SCAN,
    		NODE_STEER_EXCLUDE,
    		NODE_STEER_EXCLUDE_BTM,
    		NODE_STEER_DISALLOW,
    		NODE_C_CAC,
    		NODE_TRAFFIC_SEPARATION,
    		NODE_STA_STEER,
    
    		NUM_POLICIES,
    	};
    	const struct uci_parse_option opts[] = {
    
    		{ .name = "agent_id", .type = UCI_TYPE_STRING },
    		{ .name = "backhaul_ul_macaddr", .type = UCI_TYPE_STRING },
    		{ .name = "backhaul_dl_macaddr", .type = UCI_TYPE_STRING },
    		{ .name = "backhaul_type", .type = UCI_TYPE_STRING },
    		{ .name = "primary_vid", .type = UCI_TYPE_STRING },
    		{ .name = "primary_pcp", .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_scan", .type = UCI_TYPE_STRING },
    
    		{ .name = "steer_exclude", .type = UCI_TYPE_LIST },
    		{ .name = "steer_exclude_btm", .type = UCI_TYPE_LIST },
    
    		{ .name = "steer_disallow", .type = UCI_TYPE_STRING },
    		{ .name = "coordinated_cac", .type = UCI_TYPE_STRING },
    		{ .name = "traffic_separation", .type = UCI_TYPE_STRING },
    		{ .name = "sta_steer", .type = UCI_TYPE_STRING },
    
    	};
    	struct uci_option *tb[NUM_POLICIES];
    
    	uci_parse_section(s, opts, NUM_POLICIES, tb);
    
    
    		a = calloc(1, sizeof(*a));
    		if (!a)
    			return -1;
    
    
    		list_add(&a->list, &c->nodelist);
    
    		INIT_LIST_HEAD(&a->radiolist);
    		hwaddr_aton(tb[NODE_AGENT_ID]->v.string, a->agent_id);
    		INIT_LIST_HEAD(&a->steer_exlist);
    		INIT_LIST_HEAD(&a->btmsteer_exlist);
    
    	if (tb[NODE_BK_UL_MAC])
    		hwaddr_aton(tb[NODE_BK_UL_MAC]->v.string, a->bk_ul_mac);
    
    	if (tb[NODE_BK_DL_MAC])
    		hwaddr_aton(tb[NODE_BK_DL_MAC]->v.string, a->bk_dl_mac);
    
    	if (tb[NODE_BK_TYPE]) {
    		char *type = tb[NODE_BK_TYPE]->v.string;
    
    		if (strcmp(type, "none"))
    			a->type = BK_TYPE_NONE;
    		else
    			a->type = BK_TYPE_NONE;
    	}
    
    	if (tb[NODE_PVID])
    		a->pvid = atoi(tb[NODE_PVID]->v.string);
    
    	if (tb[NODE_PCP])
    		a->pcp = atoi(tb[NODE_PCP]->v.string);
    
    	if (tb[NODE_RPT_ASSOC_FAILS]) {
    		a->report_sta_assocfails =
    			atoi(tb[NODE_RPT_ASSOC_FAILS]->v.string) == 1 ?
    					true : false;
    	}
    
    	if (tb[NODE_RPT_ASSOC_FAILS_RATE]) {
    		a->report_sta_assocfails_rate =
    				atoi(tb[NODE_RPT_ASSOC_FAILS_RATE]->v.string);
    	}
    
    	if (tb[NODE_STEER_EXCLUDE]) {
    		uci_foreach_element(&tb[NODE_STEER_EXCLUDE]->v.list, x) {
    
    			stax_add_entry(&a->steer_exlist, x->name);
    
    	if (tb[NODE_STEER_EXCLUDE_BTM]) {
    		uci_foreach_element(&tb[NODE_STEER_EXCLUDE_BTM]->v.list, x) {
    
    			stax_add_entry(&a->btmsteer_exlist, x->name);
    
    	if (tb[NODE_RPT_SCAN]) {
    		a->report_scan =
    			atoi(tb[NODE_RPT_SCAN]->v.string) == 1 ? true : false;
    	}
    
    	if (tb[NODE_RPT_METRIC_PERIODIC])
    		a->report_metric_periodic = atoi(tb[NODE_RPT_METRIC_PERIODIC]->v.string);
    
    	if (tb[NODE_STEER_DISALLOW])
    		a->steer_disallow = atoi(tb[NODE_STEER_DISALLOW]->v.string) == 1 ? true : false;
    
    	if (tb[NODE_C_CAC])
    		a->coordinated_cac = atoi(tb[NODE_C_CAC]->v.string) == 1 ? true : false;
    
    	if (tb[NODE_TRAFFIC_SEPARATION])
    		a->traffic_separation = atoi(tb[NODE_TRAFFIC_SEPARATION]->v.string) == 1 ? true : false;