Skip to content
Snippets Groups Projects
config.c 47.4 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 <easy/easy.h>
    
    #include <wifidefs.h>
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    #include <bufutil.h>
    
    #include "utils/debug.h"
    #include "utils/utils.h"
    
    #include "config.h"
    
    #include "timer.h"
    #if (EASYMESH_VERSION > 2)
    #include "dpp.h"
    #endif
    
    #include "config.h"
    
    #include "easymesh.h"
    
    #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)
    
    #if (EASYMESH_VERSION > 2)
    struct dpp_controller_cfg *get_dpp_controller_by_band(struct controller_config *c,
    		enum wifi_band band)
    {
    	struct dpp_controller_cfg *p;
    
    	list_for_each_entry(p, &c->dpp_cntlrlist, list) {
    		if (band == p->band)
    			return p;
    	}
    
    	return NULL;
    }
    
    /* create ap config and initialize with default values */
    struct dpp_controller_cfg *create_dpp_controller_config(struct controller_config *cfg,
    							enum wifi_band band)
    {
    	struct dpp_controller_cfg *new;
    
    	if (!cfg)
    		return NULL;
    
    	new = calloc(1, sizeof(struct dpp_controller_cfg));
    	if (!new) {
    		warn("OOM! config\n");
    		return NULL;
    	}
    	new->band = band;
    
    	if (new->band == BAND_2)
    		new->port = 8902;
    	else if (new->band == BAND_5)
    		new->port = 8905;
    #if 0 /* TOOD: BAND_6 not yet supported */
    	else if (new->band == BAND_6)
    		new->port = 8906;
    #endif
    	else
    		new->port = 8910; /* default */
    
    	dbg("%s: %s dpp_controller->cfg = %p\n", __func__, new->ifname, new);
    
    	list_add(&new->list, &cfg->dpp_cntlrlist);
    	return new;
    }
    #endif
    
    
    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_radio_list(struct list_head *radiolist)
    
    	struct radio_policy *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, radiolist, list) {
    
    int clean_agent_policies(struct controller_config *cfg)
    
    	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);
    		clean_radio_list(&p->radiolist);
    
    		list_del(&p->list);
    		free(p);
    	}
    
    	return 0;
    }
    
    
    int clean_vendor_ie(struct wsc_vendor_ie *ext)
    {
    	free(ext->payload);
    	return 0;
    }
    
    
    int clean_vendor_ies(struct iface_credential *iface_cred)
    
    	for (i = 0; i < iface_cred->num_ven_ies; i++)
    		clean_vendor_ie(&iface_cred->ven_ies[i]);
    
    	iface_cred->num_ven_ies = 0;
    
    int clean_cred_list(struct controller_config *cfg)
    
    	struct iface_credential *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->aplist, list) {
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    int clean_scclist_list(struct controller_config *cfg)
    {
    	struct steer_control_config *p = NULL, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->scclist, list) {
    		list_del(&p->list);
    		free(p);
    	}
    
    	return 0;
    }
    
    
    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;
    }
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    int cntlr_config_add_node(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;
    }
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    int cntlr_config_add_node_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;
    
    	char name[32] = { 0 };
    	static const char s[2] = ":";
    	char *token;
    	char mac[18] = { 0 };
    	struct uci_ptr ptr = {0};
    
    	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)
    		goto out_pkg;
    
    	/* create section */
    	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;
    
    
    	/* add default values */
    
    	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->scclist);
    
    #if (EASYMESH_VERSION > 2)
    	INIT_LIST_HEAD(&cfg->dpp_cntlrlist);
    #endif
    
    	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_BCN_METRICS_MAX_NUM,
    
    		CNTLR_INITIAL_CHANNEL_SCAN,
    
    		CNTLR_CHANNEL_PLAN_TIMEOUT,
    		CNTLR_BGDFS_TIMEOUT,
    
    		CNTLR_PRIMARY_VID,
    		CNTLR_DEFAULT_PCP,
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		CNTLR_ENABLE_TS,
    
    		CNTLR_PROFILE,
    
    		NUM_CNTLR_ATTRS
    
    	const struct uci_parse_option opts[NUM_CNTLR_ATTRS] = {
    		[CNTLR_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
    		[CNTLR_REGISTRAR] = { .name = "registrar", .type = UCI_TYPE_STRING },
    		[CNTLR_DEBUG] = { .name = "debug", .type = UCI_TYPE_STRING },
    		[CNTLR_RESEND_NUM] = { .name = "resend_num", .type = UCI_TYPE_STRING },
    		[CNTLR_BCN_METRICS_MAX_NUM] = { .name = "bcn_metrics_max_num", .type = UCI_TYPE_STRING },
    		[CNTLR_INITIAL_CHANNEL_SCAN] = { .name = "initial_channel_scan", .type = UCI_TYPE_STRING },
    		[CNTLR_CHANNEL_PLAN_TIMEOUT] = { .name = "channel_plan", .type = UCI_TYPE_STRING },
    		[CNTLR_BGDFS_TIMEOUT] = { .name = "allow_bgdfs", .type = UCI_TYPE_STRING },
    		[CNTLR_PRIMARY_VID] = { .name = "primary_vid", .type = UCI_TYPE_STRING },
    		[CNTLR_DEFAULT_PCP] = { .name = "default_pcp", .type = UCI_TYPE_STRING },
    		[CNTLR_ENABLE_TS] = { .name = "enable_ts", .type = UCI_TYPE_STRING },
    		[CNTLR_PROFILE] = { .name = "profile", .type = UCI_TYPE_STRING },
    
    	};
    	struct uci_option *tb[NUM_CNTLR_ATTRS];
    
    	uci_parse_section(s, opts, NUM_CNTLR_ATTRS, tb);
    
    	if (tb[CNTLR_PROFILE]) {
    		c->map_profile = atoi(tb[CNTLR_PROFILE]->v.string);
    		if (c->map_profile < MULTIAP_PROFILE_1)
    			c->map_profile = MULTIAP_PROFILE_1;
    		else if (c->map_profile > EASYMESH_VERSION)
    			c->map_profile = EASYMESH_VERSION;
    	} else
    		c->map_profile = EASYMESH_VERSION;
    
    
    	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;
    
    Janusz Dziedzic's avatar
    Janusz Dziedzic committed
    		c->has_registrar_6g = !strstr(val, "6") ? false : true;
    
    		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_BCN_METRICS_MAX_NUM]) {
    		const char *val = tb[CNTLR_BCN_METRICS_MAX_NUM]->v.string;
    
    		c->bcn_metrics_max_num = atoi(val);
    	} else
    		c->bcn_metrics_max_num = BCN_METRICS_MAX_NUM;
    
    
    	if (tb[CNTLR_INITIAL_CHANNEL_SCAN]) {
    		const char *val = tb[CNTLR_INITIAL_CHANNEL_SCAN]->v.string;
    
    		c->initial_channel_scan = atoi(val) == 1 ? true : false;
    	}
    
    
    	if (tb[CNTLR_CHANNEL_PLAN_TIMEOUT]) {
    		const char *val = tb[CNTLR_CHANNEL_PLAN_TIMEOUT]->v.string;
    
    
    		c->acs_timeout = atoi(val);
    		/* TODO agree conf param - by default run each 3 hours */
    		if (c->acs_timeout < 180)
    			c->acs_timeout = 3600 * 3;
    	}
    
    
    	if (tb[CNTLR_BGDFS_TIMEOUT]) {
    		const char *val = tb[CNTLR_BGDFS_TIMEOUT]->v.string;
    
    
    		c->dfs_cleanup_timeout = atoi(val);
    		if (c->dfs_cleanup_timeout < 120)
    			c->dfs_cleanup_timeout = 120;
    	}
    
    
    	if (tb[CNTLR_PRIMARY_VID]) {
    		const char *val = tb[CNTLR_PRIMARY_VID]->v.string;
    
    		c->primary_vid = atoi(val);
    		if (c->primary_vid > 0xfff)
    			c->primary_vid = 0;
    	}
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	if (tb[CNTLR_ENABLE_TS]) {
    		const char *val = tb[CNTLR_ENABLE_TS]->v.string;
    
    		c->enable_ts = !!atoi(val);
    	}
    
    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;
    }
    
    
    /* returns steer_control_config for given (plugin/section) name */
    static struct steer_control_config *find_steer_control_config(
    		struct controller_config *cc, char *name)
    {
    	struct steer_control_config *e = NULL;
    
    	list_for_each_entry(e, &cc->scclist, list) {
    		if (!strncmp(e->name, name, 63))
    			return e;
    	}
    
    	return NULL;
    }
    
    static int cntlr_config_get_steer_params(struct controller_config *cc,
    						struct uci_section *s)
    {
    	enum {
    		STEER_MODULE,
    		STEER_PLUGIN_ENABLED,
    		STEER_STA_ENABLE,
    		STEER_BSTA_ENABLE,
    		STEER_BCN_METRICS,
    		STEER_USTA_METRICS,
    		STEER_BANDSTEER,
    		STEER_DIFFSNR,
    
    		STEER_RCPI_TH_2G,
    		STEER_RCPI_TH_5G,
    		STEER_RCPI_TH_6G,
    		STEER_RPT_RCPI_TH_2G,
    		STEER_RPT_RCPI_TH_5G,
    		STEER_RPT_RCPI_TH_6G,
    
    		NUM_STEER_ATTRS
    	};
    	const struct uci_parse_option opts[] = {
    		[STEER_MODULE] = { .name = "steer_module", .type = UCI_TYPE_STRING },
    		[STEER_PLUGIN_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
    		[STEER_STA_ENABLE] = { .name = "enable_sta_steer", .type = UCI_TYPE_STRING },
    		[STEER_BSTA_ENABLE] = { .name = "enable_bsta_steer", .type = UCI_TYPE_STRING },
    		[STEER_BCN_METRICS] = { .name = "use_bcn_metrics", .type = UCI_TYPE_STRING },
    		[STEER_USTA_METRICS] = { .name = "use_usta_metrics", .type = UCI_TYPE_STRING },
    		[STEER_BANDSTEER] = { .name = "bandsteer", .type = UCI_TYPE_STRING },
    		[STEER_DIFFSNR] = { .name = "diffsnr", .type = UCI_TYPE_STRING },
    
    		[STEER_RCPI_TH_2G] = { .name = "rcpi_threshold_2g", .type = UCI_TYPE_STRING },
    		[STEER_RCPI_TH_5G] = { .name = "rcpi_threshold_5g", .type = UCI_TYPE_STRING },
    		[STEER_RCPI_TH_6G] = { .name = "rcpi_threshold_6g", .type = UCI_TYPE_STRING },
    		[STEER_RPT_RCPI_TH_2G] = { .name = "report_rcpi_threshold_2g", .type = UCI_TYPE_STRING },
    		[STEER_RPT_RCPI_TH_5G] = { .name = "report_rcpi_threshold_5g", .type = UCI_TYPE_STRING },
    		[STEER_RPT_RCPI_TH_6G] = { .name = "report_rcpi_threshold_6g", .type = UCI_TYPE_STRING },
    
    	};
    	struct uci_option *tb[NUM_STEER_ATTRS];
    	struct steer_control_config *sc;
    	char name[64];
    
    	uci_parse_section(s, opts, NUM_STEER_ATTRS, tb);
    
    	/* Keep separate params for each steer module (section, plugin) */
    
    	if (!tb[STEER_MODULE]) {
    		dbg("|%s:%d| Missing steer module name\n", __func__, __LINE__);
    		return -1;
    	}
    
    	strncpy(name, tb[STEER_MODULE]->v.string, 63);
    
    	sc = find_steer_control_config(cc, name);
    	if (!sc) {
    		sc = calloc(1, sizeof(*sc));
    		if (!sc) {
    			warn("-ENOMEM!\n");
    			return -1;
    		}
    		strncpy(sc->name, name, 63);
    
    		list_add_tail(&sc->list, &cc->scclist);
    	}
    
    	dbg("|%s:%d| Steer module: %s ", __func__, __LINE__, sc->name);
    
    	if (tb[STEER_PLUGIN_ENABLED]) {
    		const char *val = tb[STEER_PLUGIN_ENABLED]->v.string;
    
    		sc->plugin_enabled = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_STA_ENABLE]) {
    		const char *val = tb[STEER_STA_ENABLE]->v.string;
    
    		sc->enable_sta_steer = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_BSTA_ENABLE]) {
    		const char *val = tb[STEER_BSTA_ENABLE]->v.string;
    
    		sc->enable_bsta_steer = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_BCN_METRICS]) {
    		const char *val = tb[STEER_BCN_METRICS]->v.string;
    
    		sc->use_bcn_metrics = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_USTA_METRICS]) {
    		const char *val = tb[STEER_USTA_METRICS]->v.string;
    
    		sc->use_usta_metrics = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_BANDSTEER]) {
    		const char *val = tb[STEER_BANDSTEER]->v.string;
    
    		sc->bandsteer = (atoi(val) == 1 ? true : false);
    	}
    
    	if (tb[STEER_DIFFSNR]) {
    		const char *val = tb[STEER_DIFFSNR]->v.string;
    		int diffsnr;
    
    		diffsnr = atoi(val);
    		if (diffsnr < 1)
    			sc->diffsnr = 1;
    		else if (diffsnr > 40)
    			sc->diffsnr = 40;
    		else
    			sc->diffsnr = diffsnr;
    	}
    
    
    	sc->rcpi_threshold_2g = CONFIG_DEFAULT_RCPI_TH_2G;
    	sc->rcpi_threshold_5g = CONFIG_DEFAULT_RCPI_TH_5G;
    	sc->rcpi_threshold_6g = CONFIG_DEFAULT_RCPI_TH_6G;
    	sc->report_rcpi_threshold_2g = CONFIG_DEFAULT_RCPI_TH_2G + 10;
    	sc->report_rcpi_threshold_5g = CONFIG_DEFAULT_RCPI_TH_5G + 10;
    	sc->report_rcpi_threshold_6g = CONFIG_DEFAULT_RCPI_TH_6G + 10;
    
    	if (tb[STEER_RCPI_TH_2G]) {
    		const char *val = tb[STEER_RCPI_TH_2G]->v.string;
    		int rcpi;
    
    		rcpi = atoi(val);
    		if (rcpi > 0 && rcpi <= 220)
    			sc->rcpi_threshold_2g = rcpi;
    	}
    
    	if (tb[STEER_RCPI_TH_5G]) {
    		const char *val = tb[STEER_RCPI_TH_5G]->v.string;
    		int rcpi;
    
    		rcpi = atoi(val);
    		if (rcpi > 0 && rcpi <= 220)
    			sc->rcpi_threshold_5g = rcpi;
    	}
    
    	if (tb[STEER_RCPI_TH_6G]) {
    		const char *val = tb[STEER_RCPI_TH_6G]->v.string;
    		int rcpi;
    
    		rcpi = atoi(val);
    		if (rcpi > 0 && rcpi <= 220)
    			sc->rcpi_threshold_6g = rcpi;
    	}
    
    	if (tb[STEER_RPT_RCPI_TH_2G]) {
    		const char *val = tb[STEER_RPT_RCPI_TH_2G]->v.string;
    		int rcpi;
    
    		rcpi = atoi(val);
    		if (rcpi > 0 && rcpi <= 220)
    			sc->report_rcpi_threshold_2g = rcpi;
    	}