Skip to content
Snippets Groups Projects
config.c 25.2 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> // 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"
    
    #include "config.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(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;                                       \
    				____ptr->is_policy_diff = 1;                 \
    			}                                                    \
    			____p = list_entry(____p->list.next, type, 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_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;
    }
    
    int clean_bridgelist(struct controller_config *cfg)
    {
    	struct netif_vlan *p, *tmp;
    
    	list_for_each_entry_safe(p, tmp, &cfg->vlanlist, list) {
    		list_del(&p->list);
    		free(p);
    	}
    
    	return 0;
    }
    
    int clean_credslist(struct list_head *credslist)
    {
    	struct iface_credential *p, *tmp;
    
    	list_for_each_entry_safe(p, tmp, credslist, list) {
    		list_del(&p->list);
    		free(p);
    	}
    
    	return 0;
    }
    
    int clean_vlanlist(struct list_head *vlanlist)
    {
    	struct netif_vlan *p, *tmp;
    
    	list_for_each_entry_safe(p, tmp, vlanlist, 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) {
    		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;
    }
    
    
    
    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;
    }
    
    
    
    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;
    }
    
    
    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;
    }
    
    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", &section);
    	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)
    
    	int i;
    
    	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\n");
    	list_for_each_entry(cred, &c->bsslist, 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("  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
    
    
    	dbg("---------------------------\n");
    
    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->vlanlist);
    
    	INIT_LIST_HEAD(&c->bsslist);
    
    	return 0;
    
    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;
    
    		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_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_MAP,
    		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 = "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_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING }
    
    	};
    	struct uci_option *tb[NUM_CREDS];
    	struct iface_credential *cred;
    
    	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)
    	}
    
    	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);
    
    		cred->multi_ap = atoi(tb[CRED_MAP]->v.string);
    
    		if (cred->multi_ap == 1)
    			cred->mode = AP_WIFI_BBSS;
    		else if (cred->multi_ap == 2)
    			cred->mode = AP_WIFI_FBSS;
    		else if (cred->multi_ap == 3)
    			cred->mode = AP_WIFI_COMBINED;
    	} else {
    		cred->multi_ap = 2; /* default to fhbss */
    	}
    
    
    	if (tb[CRED_D_BSTA])
    		cred->disallow_bsta = atoi(tb[CRED_D_BSTA]->v.string);
    
    
    	if (tb[CRED_ENABLED])
    		cred->enabled = atoi(tb[CRED_ENABLED]->v.string);
    
    
    	c->num_bss++;
    	list_add_tail(&cred->list, &c->bsslist);
    
    	return 0;
    
    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->vlanlist, 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;
    
    			c->num_vlans++;
    			list_add(&vlan->list, &c->vlanlist);
    
    		}
    	} 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])
    		inet_pton(AF_INET, tb[VLAN_IP_ADDR]->v.string,
    				&(vlan->ipaddr.addr.ip4.s_addr));
    
    static int cntlr_config_get_agent_policy(struct controller_config *c,
    						struct uci_section *s)
    
    	enum {
    
    		POL_AGENT_ID,
    
    		POL_STEER_EXCLUDE,
    		POL_STEER_EXCLUDE_BTM,
    
    		POL_STEER,
    		POL_UTIL_TH,
    		POL_RCPI_TH,
    		POL_RPT_SCAN,
    		POL_RPT_ASSOC_FAILS,
    
    		POL_RPT_METRIC_PERIODIC,
    		POL_RPT_RCPI_TH,
    		POL_RPT_UTIL_TH,
    
    		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 = "band", .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 agent_policy *a;
    
    
    	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;
    
    		hwaddr_aton(val, a->agent_id);
    
    	if (tb[POL_BAND]) {
    		if (atoi(tb[POL_BAND]->v.string) == 5)
    			a->band = BAND_5;
    		else if (atoi(tb[POL_BAND]->v.string) == 2)
    			a->band = BAND_2;
    		else
    			a->band = BAND_UNKNOWN;
    	} else
    		a->band = BAND_UNKNOWN;
    
    
    	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);
    
    	if (tb[POL_STEER])
    
    		a->policy = atoi(tb[POL_STEER]->v.string);
    
    	if (tb[POL_UTIL_TH])
    
    		a->util_threshold = atoi(tb[POL_UTIL_TH]->v.string);
    
    	if (tb[POL_RCPI_TH])
    
    		a->rcpi_threshold = atoi(tb[POL_RCPI_TH]->v.string);
    
    	if (tb[POL_RPT_SCAN]) {
    
    		a->report_scan =
    
    			atoi(tb[POL_RPT_SCAN]->v.string) == 1 ? true : false;
    
    	if (tb[POL_RPT_ASSOC_FAILS]) {
    
    		a->report_sta_assocfails =
    
    			atoi(tb[POL_RPT_ASSOC_FAILS]->v.string) == 1 ?
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					true : false;
    
    	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_METRIC_PERIODIC]) {
    
    		a->report_metric_periodic =
    
    				atoi(tb[POL_RPT_METRIC_PERIODIC]->v.string);
    
    	if (tb[POL_RPT_RCPI_TH]) {
    
    		a->report_rcpi_threshold =
    
    				atoi(tb[POL_RPT_RCPI_TH]->v.string);
    	}
    
    	if (tb[POL_RPT_UTIL_TH]) {
    
    		a->report_util_threshold =
    
    				atoi(tb[POL_RPT_UTIL_TH]->v.string);
    
    	if (tb[POL_RPT_HYS_MARGIN]) {
    		a->rcpi_hysteresis_margin =
    				atoi(tb[POL_RPT_HYS_MARGIN]->v.string);
    	}
    
    
    	if (tb[POL_INC_STA_STATS]) {
    
    		a->include_sta_stats =
    
    			atoi(tb[POL_INC_STA_STATS]->v.string) == 1 ?
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					true : false;
    
    	if (tb[POL_INC_STA_METRIC]) {
    
    		a->include_sta_metric =
    
    			atoi(tb[POL_INC_STA_METRIC]->v.string) == 1 ?
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					true : false;
    
    	if (tb[POL_PVID])
    
    		a->pvid = atoi(tb[POL_PVID]->v.string);
    
    	if (tb[POL_PCP_DEFAULT])
    
    		a->pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);
    
    	if (tb[POL_DISALLOW_BSTA_P1]) {
    
    		a->disallow_bsta_p1 =
    
    			atoi(tb[POL_DISALLOW_BSTA_P1]->v.string) == 1 ?
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					true : false;
    
    	if (tb[POL_DISALLOW_BSTA_P2]) {
    
    		a->disallow_bsta_p2 =
    
    			atoi(tb[POL_DISALLOW_BSTA_P2]->v.string) == 1 ?
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					true : false;
    
    	list_add(&a->list, &c->policylist);
    
    	return 0;
    }
    
    static void config_map_creds_to_vlan(struct controller_config *cfg,
    		struct list_head *credslist)
    {
    	struct iface_credential *cred;
    
    	list_for_each_entry(cred, credslist, list) {
    		struct netif_vlan *vlan;
    
    		vlan = cntlr_config_get_vlan_by_id(cfg, cred->vlanid);
    		if (!vlan)
    			continue;
    
    		cred->br = vlan;
    	}
    }
    
    
    uint8_t cntlr_policy_exlist_diff(struct list_head *prev_policylist,
    		struct list_head *curr_policylist)
    {
    	uint8_t diff = 0;
    	struct agent_policy *prev, *curr;
    
    	list_for_multiple_entry(prev, curr, prev_policylist, curr_policylist, list, list) {
    		if ((prev->num_steer_stas != curr->num_steer_stas) ||
    				(prev->num_btmsteer_stas != curr->num_btmsteer_stas)) {
    			trace("num of exclude stas differ\n");
    			curr->is_policy_diff = 1;
    			diff |= CONFIG_DIFF_AGENT_POLICY;
    		} else if (list_memcmp(&prev->steer_exlist, &curr->steer_exlist,
    					struct stax, sizeof(struct list_head))) {
    			trace("steer_exlist differ\n");
    			curr->is_policy_diff = 1;
    			diff |= CONFIG_DIFF_AGENT_POLICY;
    		} else if (list_memcmp(&prev->btmsteer_exlist, &curr->btmsteer_exlist,
    					struct stax, sizeof(struct list_head))) {
    			trace("btmsteer_exlist differ\n");
    			curr->is_policy_diff = 1;
    			diff |= CONFIG_DIFF_AGENT_POLICY;
    		}
    	}
    
    	return diff;
    }
    
    
    uint8_t cntlr_config_diff(struct controller_config *cfg,
    		struct controller_config *prev)
    {
    	uint8_t diff = 0;
    
    	/* credentials diff */
    
    	if (prev->num_bss != cfg->num_bss) {
    
    		dbg("|%s:%d| number of credentials differed\n", __func__, __LINE__);
    		diff |= CONFIG_DIFF_CREDENTIALS;
    
    	} else if (list_memcmp(&prev->bsslist, &cfg->bsslist,
    
    				struct iface_credential,
    				(sizeof(struct list_head) + sizeof(struct netif_vlan *))
    			)) {
    
    		trace("|%s:%d| bss credentials have changed\n", __func__, __LINE__);
    
    		diff |= CONFIG_DIFF_CREDENTIALS;
    	}
    
    	/* vlan diff */
    	if (prev->num_vlans != cfg->num_vlans) {
    		dbg("|%s:%d| number of vlan credentials differed\n", __func__, __LINE__);
    		diff |= CONFIG_DIFF_VLAN;
    	} else if (list_memcmp(&prev->vlanlist, &cfg->vlanlist,
    				struct netif_vlan,
    				(sizeof(struct list_head))
    			)) {
    		trace("|%s:%d| vlan credentials have changed\n", __func__, __LINE__);
    		diff |= CONFIG_DIFF_VLAN;
    	}
    
    
    	/* agent policy diff */
    	if (prev->num_apolicy != cfg->num_apolicy) {
    		dbg("|%s:%d| number of agent policy differed\n", __func__, __LINE__);
    		diff |= CONFIG_DIFF_AGENT_POLICY_CNT;
    	} else if (list_policy_memcmp(&prev->policylist, &cfg->policylist,
    				struct agent_policy, (sizeof(struct list_head) +
    					sizeof(struct list_head) + sizeof(struct list_head)))) {
    		trace("|%s:%d| agent_policy section have changed\n", __func__, __LINE__);
    		diff |= CONFIG_DIFF_AGENT_POLICY;
    	} else {
    		/* exclude stalist diff */
    		diff |= cntlr_policy_exlist_diff(&prev->policylist, &cfg->policylist);
    	}
    
    
    	return diff;
    }
    
    void config_copy_cntlr_config(struct controller_config *curr,
    		struct controller_config *old)
    {
    
    	INIT_LIST_HEAD(&old->bsslist);
    
    	INIT_LIST_HEAD(&old->vlanlist);
    
    	INIT_LIST_HEAD(&old->policylist);
    
    	old->num_bss = curr->num_bss;
    
    	old->num_vlans = curr->num_vlans;
    
    	old->num_apolicy = curr->num_apolicy;
    
    	list_copy(&curr->bsslist, &old->bsslist, struct iface_credential);
    
    	list_copy(&curr->vlanlist, &old->vlanlist, struct netif_vlan);
    
    	list_copy(&curr->policylist, &old->policylist, struct agent_policy);
    
    }
    
    uint8_t cntlr_config_reload(struct controller_config *cfg)
    
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    
    	struct uci_element *e;
    
    	struct controller_config old = {0};
    	uint8_t diff = 0;
    
    
    	ctx = uci_alloc_context();
    
    	if (!ctx)
    
    	if (uci_load(ctx, "mapcontroller", &pkg)) {
    
    		uci_free_context(ctx);
    		return -1;
    	}
    
    
    	config_copy_cntlr_config(cfg, &old);
    
    	/* reset counters */
    
    	cfg->num_bss = cfg->num_vlans = 0;
    
    	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, "bss")) {
    
    			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);
    
    	/* link credentials to their respective vlan sections */
    
    	config_map_creds_to_vlan(cfg, &cfg->bsslist);
    
    	/* get bitmap of what sections changed */
    	diff = cntlr_config_diff(cfg, &old);
    
    	clean_credslist(&old.bsslist);
    
    	clean_vlanlist(&old.vlanlist);
    	clean_agent_policies(&old);
    
    	uci_free_context(ctx);
    
    int cntlr_config_clean(struct controller_config *cfg)
    {
    
    	clean_agent_policies(cfg);
    
    	clean_credslist(&cfg->bsslist);
    
    	clean_vlanlist(&cfg->vlanlist);
    
    	return 0;
    }