Skip to content
Snippets Groups Projects
config.c 61.24 KiB
/*
 * config.c - configurations handling
 *
 * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

//Security and encryption
#define WPS_AUTH_OPEN          (0x0001)
#define WPS_AUTH_WPAPSK        (0x0002)
#define WPS_AUTH_SHARED        (0x0004) /* deprecated */
#define WPS_AUTH_WPA           (0x0008)
#define WPS_AUTH_WPA2          (0x0010)
#define WPS_AUTH_WPA2PSK       (0x0020)
#define ATTR_ENCR_TYPE_FLAGS   (0x1010)
#define WPS_ENCR_NONE          (0x0001)
#define WPS_ENCR_WEP           (0x0002) /* deprecated */
#define WPS_ENCR_TKIP          (0x0004)
#define WPS_ENCR_AES           (0x0008)

#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 <i1905_wsc.h>
#include <1905_tlvs.h>
#include <map22.h>
#include <easy/easy.h>
#include <map_module22.h>
#include <bufutil.h>

#include <easy/easy.h>

#include "debug.h"
#include "utils.h"
#include "steer_rules.h"
#include "comm.h"
#include "msgqueue.h"
#include "worker.h"
#include "config.h"
#include "agent.h"

// UCI sections
#define UCI_BK_AGENT "bk-iface"
#define UCI_FH_AGENT "fh-iface"
#define UCI_WLAN_IFACE "wifi-iface"
#define UCI_WL_DEVICE "wifi-device"
#define UCI_WIRELESS "wireless"
#define UCI_IEEE1905 "ieee1905"
#define UCI_AGENT "mapagent"
int verbose;

char *replace_char(char *str, char find, char replace)
{
	char *current_pos = strchr(str, find);

	while (current_pos) {
		*current_pos = replace;
		current_pos = strchr(current_pos, find);
	}

	return str;
}

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_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;
}

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;
}

/* TODO: causes segfault in disc? */
char *uci_get_bridge(char *ifname, char *bridge)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	strncpy(bridge, "lan", 15);
	return bridge;

	pkg = uci_load_pkg(&ctx, UCI_WIRELESS);
	if (!pkg)
		return NULL;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		struct uci_option *opt;

		if (strcmp(s->type, UCI_WLAN_IFACE))
			continue;

		opt = uci_lookup_option(ctx, s,	"ifname");
		if (!opt || opt->type != UCI_TYPE_STRING)
			continue;

		if (!strncmp(opt->v.string, ifname, 16))
			continue;

		opt = uci_lookup_option(ctx, s,	"network");
		if (!opt || opt->type != UCI_TYPE_STRING)
			continue;

		strncpy(bridge, opt->v.string, 16);
		break;
	}

	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return bridge;
}

int wifi_get_iface_bssid(char *ifname, uint8_t *bssid)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	struct uci_ptr ptr = {0};
	int ret = -1;

	if (!bssid)
		return ret;

	pkg = uci_load_pkg(&ctx, "wireless");
	if (!pkg)
		return ret;

	section = config_get_section(ctx, pkg, "wifi-iface", "ifname", ifname);
	if (!section)
		goto out_pkg;

	ptr.p = pkg;
	ptr.s = section;
	ptr.option = "bssid";
	ptr.target = UCI_TYPE_OPTION;

	ret = uci_lookup_ptr(ctx, &ptr, NULL, false);
	if (!ret && ptr.o)
		hwaddr_aton(ptr.o->v.string, bssid);
	else
		memset(bssid, 0, 6);

out_pkg:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return ret;
}


/* TODO: can it be generalized? */
int wifi_set_iface_bssid(struct netif_bk *bk, uint8_t *bssid)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	char bssid_str[18] = {0};
	int ret = -1;

	pkg = uci_load_pkg(&ctx, "wireless");
	if (!pkg)
		return ret;

	section = config_get_section(ctx, pkg, "wifi-iface", "ifname", bk->name);
	if (!section)
		goto out_pkg;

	if (bssid && !hwaddr_is_zero(bssid))
		hwaddr_ntoa(bssid, bssid_str);

	dbg("|%s:%d| setting bssid to %s\n", bssid_str);

	ret = set_value(ctx, pkg, section, "bssid", bssid_str, UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);

	uci_reload_services("wireless");
out_pkg:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return ret;
}

int config_del_iface(const char *config, const char *type, const char *ifname)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *section;
	struct uci_ptr ptr = {0};
	int rv = -1;

	ctx = uci_alloc_context();
	if (!ctx)
		goto out;

	if (uci_load(ctx, config, &pkg) != UCI_OK) {
		dbg("config file 'wireless' not found!\n");
		goto out_uci;
	}

	section = config_get_section(ctx, pkg, type, "ifname", ifname);
	if (!section)
		goto out_pkg;

	ptr.p = pkg;
	ptr.s = section;

	uci_delete(ctx, &ptr);
	uci_commit(ctx, &pkg, false);
out_pkg:
	uci_unload(ctx, pkg);
out_uci:
	uci_free_context(ctx);
out:
	return rv;
}

int wifi_apply_iface_cfg(const char *ifname, const char *encryption,
		const char *ssid, const char *key)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *section;
	int rv = -1;

	ctx = uci_alloc_context();
	if (!ctx)
		goto out;

	if (uci_load(ctx, "wireless", &pkg) != UCI_OK) {
		dbg("config file 'wireless' not found!\n");
		goto out_uci;
	}

	section = config_get_section(ctx, pkg, "wifi-iface", "ifname", ifname);
	if (!section)
		goto out_pkg;

	set_value(ctx, pkg, section, "encryption", encryption, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "key", key, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "wds", "1", UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);
out_pkg:
	uci_unload(ctx, pkg);
out_uci:
	uci_free_context(ctx);
out:
	return rv;
}

char *agent_config_get_ethwan(char *ifname)
{
	struct uci_context *ctx;
	struct uci_ptr ptr = {0};

	ctx = uci_alloc_context();
	if (!ctx)
		return NULL;

	//ptr.value = value;
	ptr.package = "ports";
	ptr.section = "WAN";
	ptr.option = "ifname";
	ptr.target = UCI_TYPE_OPTION;

	if (uci_lookup_ptr(ctx, &ptr, NULL, false)) {
		uci_free_context(ctx);
		return NULL;
	}

	strncpy(ifname, ptr.o->v.string, 15);

	uci_free_context(ctx);
	return ifname;
}


struct uci_section *config_add_section(struct uci_context *ctx,
		struct uci_package *pkg, const char *config, const char *type,
		const char *key, const char *value)
{
	struct uci_section *section = NULL;
	struct uci_ptr ptr = {0};
	int rv = -1;

	section = config_get_section(ctx, pkg, type, key, value);
	if (!section) {
		rv = uci_add_section(ctx, pkg, type, &section);
		if (rv)
			goto out_pkg;

		rv = uci_save(ctx, pkg);
		if (rv)
			goto out_pkg;
	}

	ptr.value = value;
	ptr.package = config;
	ptr.section = section->e.name;
	ptr.option = key;
	ptr.target = UCI_TYPE_OPTION;

	uci_lookup_ptr(ctx, &ptr, NULL, false);
	uci_set(ctx, &ptr);
	uci_save(ctx, ptr.p);

out_pkg:
	return section;
}

int config_add_default_wifi_iface(const char *config, const char *type,
		const char *ifname, const char *device, const char *network,
		const char *mode)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *section;
	int rv = -1;

	ctx = uci_alloc_context();
	if (!ctx)
		goto out;

	if (uci_load(ctx, config, &pkg) != UCI_OK) {
		dbg("config file 'wireless' not found!\n");
		goto out_uci;
	}

	section = config_add_section(ctx, pkg, config, type, "ifname", ifname);
	if (!section)
		return -1;


	set_value(ctx, pkg, section, "device", device, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "network", network, UCI_TYPE_STRING);
	//set_value(ctx, pkg, section, "mode", mode, UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);

out_pkg:
	uci_unload(ctx, pkg);
out_uci:
	uci_free_context(ctx);
out:
	return 0;
}

int config_add_default_agent_iface(const char *config, const char *type,
		const char *ifname, enum wifi_band band)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *section;
	int rv = -1;

	ctx = uci_alloc_context();
	if (!ctx)
		goto out;

	if (uci_load(ctx, config, &pkg) != UCI_OK) {
		dbg("config file 'wireless' not found!\n");
		goto out_uci;
	}

	section = config_add_section(ctx, pkg, config, type, "ifname", ifname);
	if (!section)
		return -1;

	trace("band = %d\n", band);

	if (band == BAND_5)
		set_value(ctx, pkg, section, "band", "5", UCI_TYPE_STRING);
	if (band == BAND_2)
		set_value(ctx, pkg, section, "band", "2", UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);

out_pkg:
	uci_unload(ctx, pkg);
out_uci:
	uci_free_context(ctx);
out:
	return 0;
}

/* below functions are mostly taken from ieee1905d */
bool uci_check_wifi_iface(char *package_name, char *ifname,
		char *section)
{
	bool ret;
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	if (!package_name || !ifname)
		return false;

	ctx = uci_alloc_context();
	if (!ctx)
		return false;

	if (uci_load(ctx, package_name, &pkg)) {
		uci_free_context(ctx);
		return false;
	}

	ret = false;
	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);

		if (!strcmp(s->type, section)) {
			struct uci_option *opt = uci_lookup_option(ctx, s,
					"ifname");

			if (!opt || opt->type != UCI_TYPE_STRING)
				continue;
			if (strcmp(opt->v.string, ifname) == 0) {
				ret = true;
				break;
			}
		}
	}
	uci_unload(ctx, pkg);
	uci_free_context(ctx);

	return ret;
}

bool uci_set_wireless_interface_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;
}

static bool get_encryption_value(uint16_t auth_type, uint16_t encryption_type,
		char *encrypt_val, size_t elen)
{
	if (!encrypt_val)
		return false;

	if ((auth_type & WPS_AUTH_WPAPSK) && (auth_type & WPS_AUTH_WPA2PSK))
		strncat(encrypt_val, "psk-mixed", elen);
	else if ((auth_type & WPS_AUTH_WPA) && (auth_type & WPS_AUTH_WPA2))
		strncat(encrypt_val, "wpa-mixed", elen);
	else if (auth_type & WPS_AUTH_WPAPSK)
		strncat(encrypt_val, "psk", elen);
	else if (auth_type & WPS_AUTH_WPA2PSK)
		strncat(encrypt_val, "psk2", elen);
	else if (auth_type & WPS_AUTH_WPA)
		strncat(encrypt_val, "wpa", elen);
	else if (auth_type & WPS_AUTH_WPA2)
		strncat(encrypt_val, "wpa2", elen);
	else if (auth_type & WPS_AUTH_OPEN)
		strncat(encrypt_val, "none", elen);
	else
		return false;

	//Check for the encryption type
	if ((encryption_type & WPS_ENCR_TKIP) &&
			(encryption_type & WPS_ENCR_AES))
		strncat(encrypt_val, "+tkip+aes", elen);
	else if (encryption_type & WPS_ENCR_TKIP)
		strncat(encrypt_val, "+tkip", elen);
	else if (encryption_type & WPS_ENCR_AES)
		strncat(encrypt_val, "+aes", elen);

	return true;
}

bool uci_add_wireless_iface_sec(char *package_name, char *interface_name,
		char *section_type, char *section_name)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *s;
	struct uci_ptr ptr = {0};
	bool ret = false;

	if (!interface_name || !package_name)
		return false;
	ctx = uci_alloc_context();
	if (!ctx)
		return false;

	if (uci_load(ctx, package_name, &pkg))
		goto out_ctx;

	ptr.p = pkg;

	if (section_name) {
		ptr.section = section_name;
		ptr.value = section_type;
		ptr.option = NULL;
		uci_set(ctx, &ptr);
		if (uci_save(ctx, ptr.p) != UCI_OK)
			goto out_unload;
	} else {
		if (uci_add_section(ctx, pkg, section_type, &s) != UCI_OK)
			goto out_unload;

		if (uci_save(ctx, pkg) != UCI_OK)
			goto out_unload;

		ptr.section = s->e.name;
	}

	ptr.value = interface_name;
	ptr.option = "ifname";
	ptr.target = UCI_TYPE_OPTION;
	uci_lookup_ptr(ctx, &ptr, NULL, false);

	uci_set(ctx, &ptr);
	uci_save(ctx, ptr.p);
	uci_commit(ctx, &pkg, false);

	ret = true;
out_unload:
	uci_unload(ctx, pkg);
out_ctx:
	uci_free_context(ctx);
	return ret;
}

static int ubus_call(const char *object, const char *method,
		struct blob_buf *data, void *callback, void *cb_arg)
{
	uint32_t id;
	struct ubus_context *ctx = ubus_connect(NULL);

	if (!ctx) {
		err("ubus_connect failed\n");
		return UBUS_STATUS_UNKNOWN_ERROR;
	}

	if (ubus_lookup_id(ctx, object, &id)) {
		err("(%s) not present\n", object);
		ubus_free(ctx);
		return UBUS_STATUS_UNKNOWN_ERROR;
	}

	// Invoke Ubus to get data from uspd
	if (ubus_invoke(ctx, id, method, data->head, callback, cb_arg, 1000)) {
		err("ubus call failed\n");
		ubus_free(ctx);
		return UBUS_STATUS_UNKNOWN_ERROR;
	}

	ubus_free(ctx);
	return UBUS_STATUS_OK;
}

bool uci_reload_services(char *services)
{
	struct blob_buf bb;
	int rv = 0;
	memset(&bb, 0, sizeof(struct blob_buf));
	blob_buf_init(&bb, 0);

	blobmsg_add_string(&bb, "config", services);

	rv = ubus_call("uci", "commit", &bb, NULL, NULL);

	info("## Reloading uci config %d\n", rv);
	//if (!ubus_call("uci", "reload_config", &bb, NULL, NULL))
	//	return true;

	//ubus_call("uci", "reload_config", &bb, NULL, NULL);

	blob_buf_free(&bb);

	return false;
}

#if 0 /* Deprecated for 6.1 - Possibly bring back in 6.2 with modifications */
/* TODO: introduce option and vendor extension to make this logic redundant */
int uci_apply_wps_credentials(struct agent_config *cfg, enum wifi_band band)
{
	struct netif_bkcfg *bk;
	struct netif_fhcfg *fh;

	list_for_each_entry(bk, &cfg->bklist, list) {
		if (bk->band != band)
			continue;

		list_for_each_entry(fh, &cfg->fhlist, list) {
			if (fh->band != band)
				continue;

			dbg("Applying bBSS credentials to %s:\n", fh->name);
			dbg("  - SSID            : %s\n", bk->ssid);
			dbg("  - NETWORK_KEY     : %s\n", bk->key);

			uci_set_wireless_interface_option(UCI_WIRELESS,
					UCI_WLAN_IFACE,
					"ifname",
					fh->name,
					"multi_ap_backhaul_ssid",
					bk->ssid);
			uci_set_wireless_interface_option(UCI_WIRELESS,
					UCI_WLAN_IFACE,
					"ifname",
					fh->name,
					"multi_ap_backhaul_key",
					bk->key);
			uci_set_wireless_interface_option(UCI_WIRELESS,
					UCI_WLAN_IFACE,	"ifname", fh->name, "wps", "1");
			uci_set_wireless_interface_option(UCI_WIRELESS,
					UCI_WLAN_IFACE,	"ifname", fh->name,
					"wps_pushbutton", "1");
		}

		break;
	}

	return 0;
}
#endif


/*
 * Will only be successful if uci_apply_m2 is done prior, or interfaces already
 * exist and are configured through other means
 */
void uci_apply_traffic_sep(struct tlv_traffic_sep_policy *tlv)
{
	int i;
	uint8_t *ptr;

	ptr = tlv;
	ptr++;
	for (i = 0; i < tlv->num_ssid; i++) {
		char ssid[33] = {0};
		char vid[8] = {0};
		uint8_t len = 0;

		len = *ptr;
		ptr++;

		memcpy(ssid, ptr, len);
		ptr += len;

		snprintf(vid, sizeof(vid), "%u", buf_get_be16(ptr));
		ptr += 2;
		uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT,
				"ssid",	ssid, "vid", vid);
	}
}

/* TODO: batch the changes arther than commit oneby one */
int uci_apply_m2(struct agent_config *cfg, char *interface_name, char *device,
		struct wps_credential *out, bool onboarded, struct iop_ext *exts)
{
	bool ret;
	char auth_type_str[20] = {0};
	char multiap_str[2] = {0};
	uint8_t multi_ap = 0;
	char band_str[2] = {0};
	char ipaddr_str[INET_ADDRSTRLEN] = {0};
	char ssid[33] = {0}, network_key[65] = {0}, bridge_buf[16] = {0}, *bridge;
	/* step past br- prefix if present*/

	bridge = bridge_buf;
	if (!strncmp("br-", bridge, 3))
		bridge += 3;

	dbg("%s %d band = %d\n", __func__, __LINE__, out->band);
	if (out->band == BAND_5)
		strncpy(band_str, "5", 1);
	else if (out->band == BAND_2)
		strncpy(band_str, "2", 1);
	else /* TODO: 60 */
		return M2_PROCESS_ERROR;

	memcpy(ssid, out->ssid, out->ssidlen);
	memcpy(network_key, out->key, out->keylen);
	memcpy(bridge, exts->bridge, 15);

	inet_ntop(AF_INET, &exts->br_ip, ipaddr_str, INET_ADDRSTRLEN);

	dbg("Applying WSC configuration (%s):\n", interface_name);
	dbg("  - SSID            : %s\n", ssid);
	dbg("  - AUTH_TYPE       : 0x%04x\n", out->auth_type);
	dbg("  - ENCRYPTION_TYPE : 0x%04x\n", out->enc_type);
	dbg("  - NETWORK_KEY     : %s\n", network_key);
	dbg("  - MAPIE_EXTENSION : 0x%02x\n", out->mapie);
	dbg("  - BRIDGE          : %s\n", bridge);
	dbg("  - PROTO           : %s\n", exts->proto);
	dbg("  - VID             : 0x%02x\n", exts->vid);
	dbg("  - BR_IP           : %s\n", ipaddr_str);
	dbg("  - BAND            : %s\n", band_str);

	// if teardown bit is set, return
	if (BIT(4, out->mapie))
		return M2_PROCESS_TEARDOWN;

	multi_ap |= (BIT(5, out->mapie) << 1);
	multi_ap |= BIT(6, out->mapie);

	snprintf(multiap_str, sizeof(multiap_str), "%d", multi_ap);

	//snprintf(ipaddr_str, sizeof(ipaddr_str), "%d.%d.%d.%d", br_ip[0],
	//		br_ip[1], br_ip[2], br_ip[3]);

	if (!get_encryption_value(out->auth_type, out->enc_type,
			auth_type_str, 20)) {
		info("Unsupported encryption or cipher received!!\n");
		return M2_PROCESS_ERROR;
	}

	ret = uci_set_bridge("network", bridge, exts->proto, ipaddr_str);
	if (ret) {
		info("Error seting up bridge from M2!\n");
		return M2_PROCESS_ERROR;
	}

	// Set uci in agent
	ret = uci_check_wifi_iface(UCI_AGENT, interface_name,
			UCI_FH_AGENT);
	if (!ret) {
		ret = uci_add_wireless_iface_sec(UCI_AGENT, interface_name,
				UCI_FH_AGENT, NULL);
		if (!ret)
			return M2_PROCESS_ERROR;
	}

	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name, "band",	band_str);
	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name,	"device", device);
	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name,	"ssid", ssid);
	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name,	"key", network_key);
	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name,	"encryption", auth_type_str);
	uci_set_wireless_interface_option(UCI_AGENT, UCI_FH_AGENT, "ifname",
			interface_name, "multi_ap", multiap_str);
	if (multi_ap & 0x01) {
		char disallow_str[2] = {0};

		snprintf(disallow_str, sizeof(disallow_str), "%d",
				((out->mapie >> 2) & 0x03));
		uci_set_wireless_interface_option(UCI_AGENT,
				UCI_FH_AGENT, "ifname",
				interface_name,
				"disallow_bsta", disallow_str);
	}

	// Set uci in wireless
	ret = uci_check_wifi_iface(UCI_WIRELESS, interface_name,
			UCI_WLAN_IFACE);
	if (!ret) {
		char name[32] = {0};

		snprintf(name, sizeof(name), "%s_ap", interface_name);
		replace_char(name, '.', '_');
		ret = uci_add_wireless_iface_sec(UCI_WIRELESS, interface_name,
				UCI_WLAN_IFACE, name);
		if (!ret)
			return M2_PROCESS_ERROR;
	}

	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "network", bridge);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "ssid", (char *) ssid);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "key", (char *) network_key);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "encryption", auth_type_str);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "mode", "ap");
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "device", device);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE, "ifname",
			interface_name, "multi_ap", multiap_str);
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,	"ifname",
			interface_name, "ieee80211k", "1");
	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,	"ifname",
			interface_name, "ieee80211v", "1");

	do {
		char buf[512] = {0};
		char basemacstr[18] = {0};
		uint8_t basemac[6] = {0};
		//uint8_t uuid[16] = {0};

		chrCmd(buf, sizeof(buf), "db -q get hw.board.basemac");
		if (buf[0] != '\0' && strlen(buf) == 17)
			strncpy(basemacstr, buf, 17);

		dbg("basemac: %s\n", basemacstr);
		hwaddr_aton(buf, basemac);

		memset(buf, 0, sizeof(buf));
		chrCmd(buf, sizeof(buf),
		       "uuidgen -s --namespace @dns --name www.iopsys.eu");

		if (buf[0] == '\0' || strlen(buf) != 36) {
			dbg("uuidgen error!\n");
			//TODO
		}

		snprintf(buf + 24, 13, "%02x%02x%02x%02x%02x%02x", MAC2STR(basemac));
		dbg("UUID: %s\n", buf);
		uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,	"ifname",
			interface_name, "uuid", buf);
	} while(0);

	if (multi_ap == 0x01) {
		uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
			"ifname", interface_name, "hidden", "1");
	} else {
		uci_set_wireless_interface_option(UCI_WIRELESS,
			UCI_WLAN_IFACE,	"ifname", interface_name, "wps", "1");
		uci_set_wireless_interface_option(UCI_WIRELESS,
			UCI_WLAN_IFACE,	"ifname", interface_name,
			"wps_pushbutton", "1");
	}

	/* TODO: don't support guest network for the moment */
	//uci_add_dhcp(bridge);
	//uci_add_fw(cfg, bridge);

	return M2_PROCESS_OK;
}
/* end of functions taken from ieee1905d */

static struct netif_bkcfg *get_netif_bkcfg_by_name(struct agent_config *c,
		const char *name)
{
	struct netif_bkcfg *p;

	list_for_each_entry(p, &c->bklist, list) {
		if (!strcmp(name, p->name))
			return p;
	}

	return NULL;
}

static struct netif_fhcfg *get_netif_fhcfg_by_name(struct agent_config *c,
							const char *name)
{
	struct netif_fhcfg *p;

	list_for_each_entry(p, &c->fhlist, list) {
		if (!strcmp(name, p->name))
			return p;
	}

	return NULL;
}

static struct steer_policy *get_steer_policy_by_name(struct netif_fhcfg *c,
							const char *name)
{
	struct steer_policy *p;

	if (!c)
		return NULL;

	list_for_each_entry(p, &c->steer_policylist, list) {
		if (!strcmp(name, p->name))
			return p;
	}

	return NULL;
}

static struct agent_config_radio *get_agent_config_radio(struct agent_config *c,
							const char *ifname)
{
	struct agent_config_radio *p;

	list_for_each_entry(p, &c->radiolist, list) {
		if (!strcmp(ifname, p->name))
			return p;
	}

	return NULL;
}

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);
	}
}

void stax_del_entry(struct list_head *h, char *sta_macstr)
{
	struct stax *s, *tmp;

	list_for_each_entry_safe(s, tmp, h, list) {
		if (!strncmp(s->macstring, sta_macstr, sizeof(s->macstring))) {
			list_del(&s->list);
			free(s);
			return;
		}
	}
}

static int clean_steer_btm_excl(struct agent_config *p)
{
	struct stax *n, *tmp;

	list_for_each_entry_safe(n, tmp, &p->steer_btm_excludelist, list) {
		list_del(&n->list);
		free(n);
	}

	return 0;
}
static int clean_steer_excl(struct agent_config *p)
{
	struct stax *n, *tmp;

	list_for_each_entry_safe(n, tmp, &p->steer_excludelist, list) {
		list_del(&n->list);
		free(n);
	}

	return 0;
}

void agent_config_dump(struct agent_config *cfg)
{
	struct netif_fhcfg *n;
	struct steer_policy *pol;
	struct stax *x;

	if (!cfg)
		return;

	dbg("  Steer Exclude Lists -------\n");
	list_for_each_entry(x, &cfg->steer_excludelist, list) {
		dbg("    mac: %s\n", x->macstring);
	}

	dbg("  Steer BTM Exclude Lists -------\n");
	list_for_each_entry(x, &cfg->steer_btm_excludelist, list) {
		dbg("    mac: %s\n", x->macstring);
	}

	list_for_each_entry(n, &cfg->fhlist, list) {
		dbg("name: %s\n", n->name);
		dbg("  enabled  : %s\n", n->enabled ? "true" : "false");
		dbg("  assocctrl: %s\n", n->assoc_control ? "true" : "false");

		dbg("  Policies -------\n");
		list_for_each_entry(pol, &n->steer_policylist, list) {
			dbg("    name: %s\n", pol->name);
			dbg("    enabled  : %s\n",
					pol->enabled ? "true" : "false");
			/* if (pol->dump_config)
			 *	pol->dump_config(pol, pol->policy);
			 */
		}

		dbg("  Assoc Ctrl Lists -------\n");
		list_for_each_entry(x, &n->assoc_ctrllist, list) {
			dbg("    mac: %s\n", x->macstring);
		}
	}
}

/* create fh-iface config and initialize with default values */
struct netif_fhcfg *create_fronthaul_iface_config(struct agent_config *cfg,
							const char *ifname)
{
	struct netif_fhcfg *new;
	struct steer_rule *r;

	if (!cfg)
		return NULL;

	new = calloc(1, sizeof(struct netif_fhcfg));
	if (!new) {
		warn("OOM! config\n");
		return NULL;
	}

	snprintf(new->name, 16, "%s", ifname);
	new->enabled = true;
	new->fallback_legacy = STEER_LEGACY_FALLBACK_INT;
	new->steer_btm_retry_secs = STEER_BTM_RETRY_INT;
	new->steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
	new->steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
	new->assoc_control_time = ASSOC_CONTROL_INT;
	new->band = BAND_UNKNOWN;
	INIT_LIST_HEAD(&new->steer_policylist);
	/* nrules = get_registered_steer_rules(&pollist); */ /* TODO */
	list_for_each_entry(r, &regd_steer_rules, list) {
		struct steer_policy *pol;

		pol = calloc(1, sizeof(struct steer_policy));
		if (!pol)
			goto err_oom;

		snprintf(pol->name, 16, "%s", r->name);
		pol->enabled = false;
		if (r->init_config)
			r->init_config(r, &pol->policy);
		list_add(&pol->list, &new->steer_policylist);
	}

	INIT_LIST_HEAD(&new->assoc_ctrllist);

	/* f->cfg = new; */
	dbg("%s: %s netif_fh->cfg = %p\n", __func__, new->name, new);

	list_add(&new->list, &cfg->fhlist);

	return new;

err_oom:
	list_flush(&new->steer_policylist, struct steer_policy, list);
	free(new);
	return NULL;
}

/* create fh-iface config and initialize with default values */
struct netif_bkcfg *create_backhaul_iface_config(struct agent_config *cfg,
							const char *ifname)
{
	struct netif_bkcfg *new;

	if (!cfg)
		return NULL;

	new = calloc(1, sizeof(struct netif_bkcfg));
	if (!new) {
		warn("OOM! config\n");
		return NULL;
	}

	snprintf(new->name, 16, "%s", ifname);
	new->enabled = true;
	new->onboarded = false;
	new->band = BAND_UNKNOWN;

	/* f->cfg = new; */
	dbg("%s: %s netif_fh->cfg = %p\n", __func__, new->name, new);

	list_add(&new->list, &cfg->bklist);

	return new;
}

static void config_update_entry(struct uci_context *ctx, struct uci_package *p,
				struct uci_section *s, const char *optname,
				int add, void *val, int len)
{
	struct uci_ptr ptr;

	memset(&ptr, 0, sizeof(struct uci_ptr));
	ptr.p = p;
	ptr.s = s;
	ptr.package = p->e.name;
	ptr.section = s->e.name;
	ptr.option = optname;
	ptr.target = UCI_TYPE_OPTION;
	ptr.flags |= UCI_LOOKUP_EXTENDED;
	ptr.value = (char *)val;

	if (add) {
		dbg("config: add list option: %s\n", (char *)val);
		uci_add_list(ctx, &ptr);
	} else {
		dbg("config: del list option: %s\n", (char *)val);
		uci_del_list(ctx, &ptr);
	}
	uci_commit(ctx, &p, false);
}

int config_update(const char *confname, struct agent_config *cfg,
			const char *section, const char *option,
			int add,
			void *value, int len)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg = NULL;
	struct uci_element *e;

	ctx = uci_alloc_context();
	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
		dbg("config file '%s' not found!\n", confname);
		free(ctx);
		return -1;
	}

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		struct uci_element *x, *tmp;
		struct uci_option *op;

		if (strcmp(s->type, section))
			continue;

		/* iter through matched 'section' for the 'option' */
		uci_foreach_element_safe(&s->options, tmp, x) {
			if (strcmp(x->name, option))
				continue;

			op = uci_to_option(x);
			if (op->type == UCI_TYPE_LIST) {
				uci_foreach_element(&op->v.list, x) {
					if (!strncmp(x->name, value, len)) {
						if (!add)
							config_update_entry(ctx,
								pkg, s,
								option, 0,
								value, len);

						goto out_exit;
					}
				}
				/* add new exclude at end of list */
				if (add)
					config_update_entry(ctx, pkg, s, option,
							1, value, len);

				goto out_exit;
			}
		}
		/* 'option' name not present in 'section'
		 * Create a new one at end of 'section'.
		 */
		if (add)
			config_update_entry(ctx, pkg, s, option, 1, value, len);

		goto out_exit;
	}
out_exit:
	uci_free_context(ctx);
	return 0;
}

int config_update2(const char *confname, struct agent_config *cfg,
		const char *section_type,
		const char *match_option,
		const char *match_option_value,
		const char *option,
		int add,
		void *value, int len)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg = NULL;
	struct uci_element *e;

	ctx = uci_alloc_context();
	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
		dbg("config file '%s' not found!\n", confname);
		free(ctx);
		return -1;
	}

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		struct uci_element *x, *tmp;
		struct uci_option *op;
		const char *optstring;

		if (strcmp(s->type, section_type))
			continue;

		if (match_option && match_option_value) {
			optstring = uci_lookup_option_string(ctx, s,
				match_option);
			if (!optstring || strcmp(optstring, match_option_value))
				continue;
		}

		/* iter through matched 'section' for the 'option' */
		uci_foreach_element_safe(&s->options, tmp, x) {
			if (strcmp(x->name, option))
				continue;

			op = uci_to_option(x);
			if (op->type == UCI_TYPE_LIST) {
				uci_foreach_element(&op->v.list, x) {
					if (!strncmp(x->name, value, len)) {
						if (!add) {
							config_update_entry(ctx,
								pkg, s, option,
								0, value, len);
						}

						goto out_exit;
					}
				}
				/* add new 'option' at end of list */
				if (add) {
					config_update_entry(ctx, pkg, s, option,
								1, value, len);
				}

				goto out_exit;
			}
		}
		/* 'option' name not present in 'section'
		 * Create a new one at end of 'section'.
		 */
		if (add)
			config_update_entry(ctx, pkg, s, option,
							1, value, len);

		goto out_exit;
	}
out_exit:
	uci_free_context(ctx);
	return 0;
}


#if 0
struct config uci_config = {
	.name = "uci",
	.priv = uci_ctx;
	.get = uci_get_config,
	.set = uci_set_config,
	.init = uci_setup,
	.exit = uci_exit,
};

#define priv_get_config(priv)	container_of(priv, struct config, priv)

void register_config(struct config *c)
{
	static struct uci_context *ctx;
	static struct uci_package *pkg;
	struct uci_element *e;
	int ret = 0;

	if (uci_ctx)
		return priv_get_config(uci_ctx);

	ctx = uci_alloc_context();
	if (ctx) {
		uci_ctx = ctx;
		memcpy(c, &uci_config, sizeof(*cfg));
	}
	if (uci_load(ctx, "wifiagent", &pkg))
		return -1;
	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		const char *option_val;

		if (strcmp(s->type, "wifiagent"))
			continue;

		option_val = uci_lookup_option_string(ctx, s, name);
		if (option_val)
			sprintf(val, "%s", option_val);
		else
			ret = -1;
	}
	uci_free_context(ctx);
	return ret;

}
#endif

static int agent_config_get_wifi_agent(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		A_ENABLED,
		A_DEBUG,
		A_PROFILE,
		A_BRCM_SETUP,
		/*A_CONFIGURED,*/
		A_CNTLR_MAC,
		A_EXCLUDE,
		A_EXCLUDE_BTM,
		A_AL_BRIDGE,
		A_NETDEV,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		{ .name = "enabled", .type = UCI_TYPE_STRING },
		{ .name = "debug", .type = UCI_TYPE_STRING },
		{ .name = "profile", .type = UCI_TYPE_STRING },
		{ .name = "brcm_setup", .type = UCI_TYPE_STRING },
		/*{ .name = "configured", .type = UCI_TYPE_STRING },*/
		{ .name = "controller_macaddr", .type = UCI_TYPE_STRING },
		{ .name = "exclude", .type = UCI_TYPE_LIST },
		{ .name = "exclude_btm", .type = UCI_TYPE_LIST },
		{ .name = "al_bridge", .type = UCI_TYPE_STRING },
		{ .name = "netdev", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_POLICIES];

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	if (tb[A_ENABLED])
		a->enabled = atoi(tb[A_ENABLED]->v.string) == 1 ? true : false;

	if (tb[A_DEBUG]) {
		a->debug_level = atoi(tb[A_DEBUG]->v.string);
		if (verbose < a->debug_level)
			verbose = a->debug_level;
	}

	if (tb[A_PROFILE])
		a->profile = atoi(tb[A_PROFILE]->v.string);

	if (tb[A_BRCM_SETUP])
		a->brcm_setup = atoi(tb[A_BRCM_SETUP]->v.string);

	/*if (tb[A_CONFIGURED])
		a->configured = atoi(tb[A_CONFIGURED]->v.string);*/

	if (tb[A_CNTLR_MAC])
		hwaddr_aton(tb[A_CNTLR_MAC]->v.string, a->cntlr_almac);

	if (tb[A_EXCLUDE]) {
		struct uci_element *xi;

		dbg("Steer: exclude: ");
		uci_foreach_element(&tb[A_EXCLUDE]->v.list, xi) {
			dbg("%s ", xi->name);
			stax_add_entry(&a->steer_excludelist, xi->name);
		}
		dbg("\n");
	}

	if (tb[A_EXCLUDE_BTM]) {
		struct uci_element *xi;

		dbg("Steer: exclude_btm: ");
		uci_foreach_element(&tb[A_EXCLUDE_BTM]->v.list, xi) {
			dbg("%s ", xi->name);
			stax_add_entry(&a->steer_btm_excludelist, xi->name);
		}
		dbg("\n");
	}

	if (tb[A_AL_BRIDGE]) {
		const char *iface;

		iface = tb[A_AL_BRIDGE]->v.string;
		strncpy(a->al_bridge, iface, sizeof(a->al_bridge) - 1);
	} else /* Default to br-lan if non-specfied */
		strncpy(a->al_bridge, "br-lan", sizeof(a->al_bridge) - 1);

	if (tb[A_NETDEV]) {
		const char *ifname;
		ifname = tb[A_NETDEV]->v.string;
		strncpy(a->netdev, ifname, sizeof(a->netdev) - 1);
	} else { /* Default to wl/wlan if not specfied */
		strncpy(a->netdev, (a->brcm_setup)?"wl":"wlan", sizeof(a->netdev) - 1);
	}

	return 0;
}

static int agent_config_get_controller_select(struct agent_config *a,
				       struct uci_section *s)
{	enum {
		CTRL_SELECT_LOCAL,
		CTRL_SELECT_ID,
		CTRL_SELECT_PROBE_INT,
		CTRL_SELECT_RETRY_INT,
		CTRL_SELECT_AUTOSTART,
		NUM_CTRL_SELECT_POLICIES,
	};

	const struct uci_parse_option opts[] = {
		{ .name = "local", .type = UCI_TYPE_STRING },
		{ .name = "id", .type = UCI_TYPE_STRING },
		{ .name = "probe_int", .type = UCI_TYPE_STRING },
		{ .name = "retry_int", .type = UCI_TYPE_STRING },
		{ .name = "autostart", .type = UCI_TYPE_STRING },
	};

	struct uci_option *tb[NUM_CTRL_SELECT_POLICIES];
	struct ctrl_select_cfg *cscfg;

	uci_parse_section(s, opts, NUM_CTRL_SELECT_POLICIES, tb);

	cscfg = (struct ctrl_select_cfg *)calloc(1, sizeof(struct ctrl_select_cfg));
	if (!cscfg)
		return -1;

	if (!tb[CTRL_SELECT_LOCAL]) {
		warn("Required option 'local' not found!\n");
		return -1;
	}
	cscfg->local = atoi(tb[CTRL_SELECT_LOCAL]->v.string);

	if (tb[CTRL_SELECT_ID]) {
		cscfg->auto_detect = true;
		if (strncmp(tb[CTRL_SELECT_ID]->v.string, "auto", 4)) {
			cscfg->auto_detect = false;
			hwaddr_aton(tb[CTRL_SELECT_ID]->v.string, cscfg->alid);
		}
	}
	if (tb[CTRL_SELECT_PROBE_INT])
		cscfg->probe_int = atoi(tb[CTRL_SELECT_PROBE_INT]->v.string);
	if (tb[CTRL_SELECT_RETRY_INT])
		cscfg->retry_int = atoi(tb[CTRL_SELECT_RETRY_INT]->v.string);
	if (tb[CTRL_SELECT_AUTOSTART])
		cscfg->autostart = atoi(tb[CTRL_SELECT_AUTOSTART]->v.string);

	if (a->cscfg)
		free(a->cscfg);
	a->cscfg = cscfg;

	return 0;
}

static int agent_config_get_wifi_radio(struct agent_config *a,
				       struct uci_section *s)
{
	enum {
		WIFI_RADIO_DEVICE,
		WIFI_RADIO_BAND,
		WIFI_RADIO_CONFIGURED,
		WIFI_RADIO_ONBOARDED,
		NUM_WIFI_RADIO_POLICIES,
	};
	const struct uci_parse_option opts[] = {
		{ .name = "device", .type = UCI_TYPE_STRING },
		{ .name = "band", .type = UCI_TYPE_STRING },
		{ .name = "configured", .type = UCI_TYPE_STRING },
		{ .name = "onboarded", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_WIFI_RADIO_POLICIES];
	const char *ifname = NULL;
	uint32_t band = 0;
	struct agent_config_radio *n;

	uci_parse_section(s, opts, NUM_WIFI_RADIO_POLICIES, tb);

	if (!tb[WIFI_RADIO_DEVICE] || !tb[WIFI_RADIO_BAND]) {
		warn("No radio name or band option found!\n");
		return -1;
	}

	if (tb[WIFI_RADIO_DEVICE])
		ifname = tb[WIFI_RADIO_DEVICE]->v.string;

	if (tb[WIFI_RADIO_BAND]) {
		band = atoi(tb[WIFI_RADIO_BAND]->v.string);
		if (band != 2 && band != 5) {
			warn("Incorrect band '%d' in config\n", band);
			return -1;
		}
	}

	if (ifname && band) {
		n = get_agent_config_radio(a, ifname);
		if (!n) {
			n = calloc(1, sizeof(*n));
			if (!n) {
				warn("-ENOMEM!\n");
				return -1;
			}

			list_add_tail(&n->list, &a->radiolist);
		}

		strncpy(n->name, ifname, 16);
		n->name[15] = '\0';
		n->band = band;
	}

	if (tb[WIFI_RADIO_CONFIGURED]) {
		n->configured = atoi(tb[WIFI_RADIO_CONFIGURED]->v.string) == 1 ?
								true : false;
	}

	if (tb[WIFI_RADIO_ONBOARDED]) {
		n->onboarded = atoi(tb[WIFI_RADIO_ONBOARDED]->v.string) == 1 ?
								true : false;
	}

	return 0;
}

static int agent_config_get_bk_iface(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		BK_IFNAME,
		BK_DEVICE,
		BK_BAND,
		BK_ENABLED,
		BK_ONBOARDED,
		BK_SSID,
		BK_KEY,
		BK_ENCRYPTION,
		BK_DISALLOWED_BSTA_P1,
		BK_DISALLOWED_BSTA_P2,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		{ .name = "ifname", .type = UCI_TYPE_STRING },
		{ .name = "device", .type = UCI_TYPE_STRING },
		{ .name = "band", .type = UCI_TYPE_STRING },
		{ .name = "enabled", .type = UCI_TYPE_STRING },
		{ .name = "onboarded", .type = UCI_TYPE_STRING },
		{ .name = "ssid", .type = UCI_TYPE_STRING },
		{ .name = "key", .type = UCI_TYPE_STRING },
		{ .name = "encryption", .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 netif_bkcfg *bk;
	const char *ifname;

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	if (tb[BK_IFNAME]) {
		ifname = tb[BK_IFNAME]->v.string;
		bk = get_netif_bkcfg_by_name(a, ifname);
		if (!bk) {
			bk = create_backhaul_iface_config(a, ifname);
			if (!bk) {
				warn("%s: OOM!\n", __func__);
				return -1;
			}
		} else {
			warn("Duplicate 'bk-iface %s' config!! ignore\n",
					ifname);
		}
	} else {
		warn("No ifname in bk-iface section!\n");
		return -1;
	}

	if (tb[BK_DEVICE]) {
		const char *device;

		device = tb[BK_DEVICE]->v.string;
		strncpy(bk->device, device, sizeof(bk->device) - 1);
	}

	if (tb[BK_ENABLED])
		bk->enabled = atoi(tb[BK_ENABLED]->v.string);

	if (tb[BK_ONBOARDED])
		bk->onboarded = atoi(tb[BK_ONBOARDED]->v.string);

	if (tb[BK_BAND]) {
		int band = atoi(tb[BK_BAND]->v.string);

		if (band == 2)
			bk->band = BAND_2;
		else if (band == 5)
			bk->band = BAND_5;
	}

	if (tb[BK_SSID]) {
		const char *ssid;

		ssid = tb[BK_SSID]->v.string;
		strncpy(bk->ssid, ssid, sizeof(bk->ssid) - 1);
	}

	if (tb[BK_KEY]) {
		const char *key;

		key = tb[BK_KEY]->v.string;
		strncpy(bk->key, key, sizeof(bk->key) - 1);
	}

	if (tb[BK_ENCRYPTION]) {
		const char *encryption;

		encryption = tb[BK_ENCRYPTION]->v.string;
		strncpy(bk->encryption, encryption, sizeof(bk->encryption) - 1);
	}

	if (tb[BK_DISALLOWED_BSTA_P1])
		bk->disallowed_bsta_p1 =
			atoi(tb[BK_DISALLOWED_BSTA_P1]->v.string);

	if (tb[BK_DISALLOWED_BSTA_P2])
		bk->disallowed_bsta_p2 =
			atoi(tb[BK_DISALLOWED_BSTA_P2]->v.string);

	return 0;
}

static int agent_config_get_fh_iface(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		FH_IFNAME,
		FH_BAND,
		FH_STEER,
		FH_DEVICE,
		FH_ASSOC_CTRL,
		FH_BTM_RETRY,
		FH_BTM_RETRY_SECS,
		FH_FLBK_LEGACY,
		FH_STEER_LEGACY_RASSOC_SECS,
		FH_STEER_LEGACY_RETRY_SECS,
		FH_ASSOC_CTRL_SECS,
		FH_SSID,
		FH_KEY,
		FH_ENCRYPTION,
		FH_POLICY,
		FH_UTIL_THRESHOLD,
		FH_RCPI_THRESHOLD,
		FH_REPORT_RCPI_THRESHOLD,
		FH_RCPI_HYSTERESIS_MARGIN,
		FH_REPORT_UTIL_THRESHOLD,
		FH_INCLUDE_STA_STATS,
		FH_INCLUDE_STA_METRIC,
		NUM_POLICIES,
	};
	const struct uci_parse_option opts[] = {
		{ .name = "ifname", .type = UCI_TYPE_STRING },
		{ .name = "band", .type = UCI_TYPE_STRING },
		{ .name = "steer", .type = UCI_TYPE_LIST },
		{ .name = "device", .type = UCI_TYPE_STRING },
		{ .name = "assoc_ctrl", .type = UCI_TYPE_LIST },
		{ .name = "btm_retry", .type = UCI_TYPE_STRING },
		{ .name = "btm_retry_secs", .type = UCI_TYPE_STRING },
		{ .name = "fallback_legacy", .type = UCI_TYPE_STRING },
		{ .name = "steer_legacy_reassoc_secs", .type = UCI_TYPE_STRING },
		{ .name = "steer_legacy_retry_secs", .type = UCI_TYPE_STRING },
		{ .name = "assoc_ctrl_secs", .type = UCI_TYPE_STRING },
		{ .name = "ssid", .type = UCI_TYPE_STRING },
		{ .name = "key", .type = UCI_TYPE_STRING },
		{ .name = "encryption", .type = UCI_TYPE_STRING },
		{ .name = "policy", .type = UCI_TYPE_STRING },
		{ .name = "util_threshold", .type = UCI_TYPE_STRING },
		{ .name = "rcpi_threshold", .type = UCI_TYPE_STRING },
		{ .name = "report_rcpi_threshold", .type = UCI_TYPE_STRING },
		{ .name = "rcpi_hysteresis_margin", .type = UCI_TYPE_STRING },
		{ .name = "report_util_threshold", .type = UCI_TYPE_STRING },
		{ .name = "include_sta_stats", .type = UCI_TYPE_STRING },
		{ .name = "include_sta_metric", .type = UCI_TYPE_STRING }
	};
	struct uci_option *tb[NUM_POLICIES];
	struct netif_fhcfg *fh;

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	if (tb[FH_IFNAME]) {
		const char *ifname;

		ifname = tb[FH_IFNAME]->v.string;

		fh = get_netif_fhcfg_by_name(a, ifname);
		if (!fh) {
			fh = create_fronthaul_iface_config(a, ifname);
			if (!fh) {
				warn("%s: OOM!\n", __func__);
				return -1;
			}
		} else {
			warn("Duplicate 'fh-iface %s' config!! ignore\n",
					ifname);
		}
	} else {
		warn("No ifname in fh-iface section!\n");
		return -1;
	}

	if (tb[FH_BAND]) {
		int band = atoi(tb[FH_BAND]->v.string);

		if (band == 2)
			fh->band = BAND_2;
		else if (band == 5)
			fh->band = BAND_5;
	}

	if (tb[FH_STEER]) {
		struct uci_element *xi;

		dbg("Steer: param: ");
		uci_foreach_element(&tb[FH_STEER]->v.list, xi) {
			struct steer_policy *p = NULL;
			struct steer_rule *r;

			dbg("%s ", xi->name);
			p = get_steer_policy_by_name(fh, xi->name);
			if (!p) {
				/* TODO? */
				dbg("TODO!! steer before ifname\n");
				continue;
			}
			p->enabled = true;
			r = get_steer_rule_by_name(xi->name);
			if (r)
				r->enabled = true;
		}
		dbg("\n");
	}

	if (tb[FH_DEVICE]) {
		const char *device;

		device = tb[FH_DEVICE]->v.string;

		strncpy(fh->device, device, sizeof(fh->device) - 1);
	}

	if (tb[FH_BTM_RETRY])
		fh->steer_btm_retry = atoi(tb[FH_BTM_RETRY]->v.string);

	if (tb[FH_BTM_RETRY_SECS])
		fh->steer_btm_retry_secs = atoi(tb[FH_BTM_RETRY_SECS]->v.string);

	if (tb[FH_FLBK_LEGACY])
		fh->fallback_legacy = atoi(tb[FH_FLBK_LEGACY]->v.string);

	if (tb[FH_STEER_LEGACY_RASSOC_SECS])
		fh->steer_legacy_reassoc_secs =
				atoi(tb[FH_STEER_LEGACY_RASSOC_SECS]->v.string);

	if (tb[FH_STEER_LEGACY_RETRY_SECS])
		fh->steer_legacy_retry_secs =
				atoi(tb[FH_STEER_LEGACY_RETRY_SECS]->v.string);

	if (tb[FH_ASSOC_CTRL_SECS])
		fh->assoc_control_time =
				atoi(tb[FH_ASSOC_CTRL_SECS]->v.string);

	if (tb[FH_SSID]) {
		const char *ssid;

		ssid = tb[FH_SSID]->v.string;
		strncpy(fh->ssid, ssid, sizeof(fh->ssid) - 1);
	}

	if (tb[FH_KEY]) {
		const char *key;

		key = tb[FH_KEY]->v.string;
		strncpy(fh->key, key, sizeof(fh->key) - 1);
	}

	if (tb[FH_ENCRYPTION]) {
		const char *encryption;

		encryption = tb[FH_ENCRYPTION]->v.string;
		strncpy(fh->encryption, encryption, sizeof(fh->encryption) - 1);
	}

	if (tb[FH_POLICY])
		fh->policy = atoi(tb[FH_POLICY]->v.string);

	if (tb[FH_UTIL_THRESHOLD])
		fh->util_threshold = atoi(tb[FH_UTIL_THRESHOLD]->v.string);

	if (tb[FH_RCPI_THRESHOLD])
		fh->rcpi_threshold = atoi(tb[FH_RCPI_THRESHOLD]->v.string);

	if (tb[FH_REPORT_RCPI_THRESHOLD])
		fh->report_rcpi_threshold =
			atoi(tb[FH_REPORT_RCPI_THRESHOLD]->v.string);

	if (tb[FH_RCPI_HYSTERESIS_MARGIN])
		fh->rcpi_hysteresis_margin =
			atoi(tb[FH_RCPI_HYSTERESIS_MARGIN]->v.string);

	if (tb[FH_REPORT_UTIL_THRESHOLD])
		fh->report_util_threshold =
			atoi(tb[FH_REPORT_UTIL_THRESHOLD]->v.string);

	if (tb[FH_INCLUDE_STA_STATS])
		fh->include_sta_stats =
			atoi(tb[FH_INCLUDE_STA_STATS]->v.string);

	if (tb[FH_INCLUDE_STA_METRIC])
		fh->include_sta_metric =
			atoi(tb[FH_INCLUDE_STA_METRIC]->v.string);

	return 0;
}

static int agent_config_get_steer_param(struct agent_config *a,
		struct uci_section *s)
{
	struct steer_rule *r;

	dbg("Steer-param: %s\n", s->e.name);
	r = get_steer_rule_by_name(s->e.name);
	if (!r)
		return -1;

	dbg("Rule to handle steer-param '%s' available\n", s->e.name);
	r->config(r, a, s);

	return 0;
}

static int agent_config_get_policy_param(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		POL_REPORT_INTERVAL,
		POL_PVID,
		POL_PCP_DEFAULT,
		POL_REPORT_SCAN,
		POL_REPORT_STA_ASSOCFAILS,
		POL_REPORT_STA_ASSOCFAILS_RATE,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		{ .name = "report_interval", .type = UCI_TYPE_STRING },
		{ .name = "pvid", .type = UCI_TYPE_STRING },
		{ .name = "pcp_default", .type = UCI_TYPE_STRING },
		{ .name = "repost_scan", .type = UCI_TYPE_STRING },
		{ .name = "report_sta_assocfails", .type = UCI_TYPE_STRING },
		{ .name = "report_sta_assocfails_rate", .type = UCI_TYPE_STRING },
	};

	struct uci_option *tb[NUM_POLICIES];
	struct policy_cfg *cfg;

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	cfg = (struct policy_cfg *)calloc(1, sizeof(struct policy_cfg));
	if (!cfg)
		return -1;

	if (tb[POL_REPORT_INTERVAL])
		cfg->report_interval = atoi(tb[POL_REPORT_INTERVAL]->v.string);

	if (tb[POL_PVID])
		cfg->pvid = atoi(tb[POL_PVID]->v.string);

	if (tb[POL_PCP_DEFAULT])
		cfg->pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);

	if (tb[POL_REPORT_SCAN])
		cfg->report_scan = atoi(tb[POL_REPORT_SCAN]->v.string);

	if (tb[POL_REPORT_STA_ASSOCFAILS])
		cfg->report_sta_assocfails =
			atoi(tb[POL_REPORT_STA_ASSOCFAILS]->v.string);

	if (tb[POL_REPORT_STA_ASSOCFAILS_RATE])
		cfg->report_sta_assocfails_rate =
			atoi(tb[POL_REPORT_STA_ASSOCFAILS_RATE]->v.string);

	if (a->pcfg)
		free(a->pcfg);

	a->pcfg = cfg;

	return 0;
}

int agent_config_reload(struct agent_config *cfg)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	cfg->enabled = false;
	cfg->runfreq = AGENT_RUN_AUTO;

	ctx = uci_alloc_context();
	if (!ctx)
		return -1;

	if (uci_load(ctx, "mapagent", &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, "agent"))
			agent_config_get_wifi_agent(cfg, s);
		else if (!strcmp(s->type, "controller_select"))
			agent_config_get_controller_select(cfg, s);
		else if (!strcmp(s->type, "wifi-radio"))
			agent_config_get_wifi_radio(cfg, s);
		else if (!strcmp(s->type, "fh-iface"))
			agent_config_get_fh_iface(cfg, s);
		else if (!strcmp(s->type, "bk-iface"))
			agent_config_get_bk_iface(cfg, s);
		else if (!strcmp(s->type, "steer"))
			agent_config_get_steer_param(cfg, s);
		else if (!strcmp(s->type, "policy"))
			agent_config_get_policy_param(cfg, s);
	}

	uci_free_context(ctx);
	return 0;
}

int config_generate_radio(struct agent_config *cfg, struct uci_context *ctx,
		char *device, uint8_t band)
{
	struct uci_package *pkg;
	struct uci_section *s;
	int rv;

	rv = uci_load(ctx, UCI_AGENT, &pkg);
	if (rv)
		return -1;

	s = config_add_section(ctx, pkg, UCI_AGENT, "wifi-radio", "device",
			device);
	if (!s)
		return -1;

	if (band == BAND_2)
		set_value(ctx, pkg, s, "band", "2", UCI_TYPE_STRING);
	else if (band == BAND_5)
		set_value(ctx, pkg, s, "band", "5", UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);
	uci_unload(ctx, pkg);
	return 0;
}

int config_generate_bsta_agent(struct agent_config *cfg, struct uci_context *ctx,
		const char *device, const char *ifname,
		uint8_t band)
{
	struct uci_section *s;
	struct uci_package *pkg;
	int rv;

	rv = uci_load(ctx, UCI_AGENT, &pkg);
	if (rv)
		return -1;

	s = config_add_section(ctx, pkg, UCI_AGENT, UCI_BK_AGENT, "device",
			device);
	if (!s)
		return -1;

	if (band == BAND_2)
		set_value(ctx, pkg, s, "band", "2", UCI_TYPE_STRING);
	else if (band == BAND_5)
		set_value(ctx, pkg, s, "band", "5", UCI_TYPE_STRING);

	set_value(ctx, pkg, s, "ifname", ifname, UCI_TYPE_STRING);
	uci_commit(ctx, &pkg, false);
	uci_unload(ctx, pkg);
	return 0;
}

int config_find_radio(struct agent_config *cfg, struct uci_context *ctx,
		char *radio)
{
	struct uci_package *pkg;
	struct uci_section *s;
	struct uci_element *e;
	bool found = false;
	int rv;

	rv = uci_load(ctx, UCI_AGENT, &pkg);
	if (rv)
		return found;

	s = config_get_section(ctx, pkg, "wifi-radio", "device", radio);
	if (s)
		found = true;

	uci_unload(ctx, pkg);
	return found;
}

static void wifi_radio_status_cb(struct ubus_request *req, int type,
		struct blob_attr *msg)
{
	struct blob_attr *tb[1];
	static const struct blobmsg_policy ap_attr[1] = {
		[0] = { .name = "band", .type = BLOBMSG_TYPE_STRING },
	};
	char band_str[8] = {0};
	uint8_t *band = req->priv;

	blobmsg_parse(ap_attr, 1, tb, blob_data(msg), blob_len(msg));

	if (!tb[0])
		return;

	strncpy(band_str, blobmsg_data(tb[0]), sizeof(band_str));

	if (!strncmp(band_str, "5GHz", sizeof(band_str)))
		*band = BAND_5;
	else if (!strncmp(band_str, "2.4GHz", sizeof(band_str)))
		*band = BAND_2;
	else
		*band = BAND_UNKNOWN;
}

void config_disable_bstas(struct agent_config *cfg)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	struct netif_bkcfg *bk;
	bool reload = false;


	list_for_each_entry(bk, &cfg->bklist, list) {
		/* only disable onboarded bsta */
//		if (!bk->cfg->onboarded)
//			continue;

		/* disable from wireless config */
		pkg = uci_load_pkg(&ctx, UCI_WIRELESS);
		if (!pkg)
			continue;

		section = config_get_section(ctx, pkg, UCI_WLAN_IFACE, "ifname", bk->name);
		if (!section) {
			uci_unload(ctx, pkg);
			continue;
		}

		set_value(ctx, pkg, section, "disabled", "1", UCI_TYPE_STRING);
		uci_save(ctx, pkg);
		reload = true;
	}

	if (reload) {
		uci_commit(ctx, &pkg, false);
		uci_unload(ctx, pkg);
	}


	list_for_each_entry(bk, &cfg->bklist, list) {
		pkg = uci_load_pkg(&ctx, UCI_AGENT);
		if (!pkg)
			continue;

		section = config_get_section(ctx, pkg, UCI_BK_AGENT, "ifname", bk->name);
		if (!section) {
			uci_unload(ctx, pkg);
			continue;
		}

		set_value(ctx, pkg, section, "enabled", "0", UCI_TYPE_STRING);
		uci_save(ctx, pkg);
		bk->enabled = 0;
	}

	if (reload) {
		uci_commit(ctx, &pkg, false);
		uci_reload_services("wireless");
	}

	uci_free_context(ctx);
}

int config_disable_bsta(struct netif_bkcfg *bk)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	int ret = -1;

	/* disable mapagent bk-iface section */
	pkg = uci_load_pkg(&ctx, UCI_AGENT);
	if (!pkg)
		return -1;

	section = config_get_section(ctx, pkg, UCI_BK_AGENT, "ifname", bk->name);
	if (!section)
		goto out_pkg;

	set_value(ctx, pkg, section, "enabled", "0", UCI_TYPE_STRING);
	uci_save(ctx, pkg);
	bk->enabled = 0;
	uci_commit(ctx, &pkg, false);
	uci_unload(ctx, pkg);

	/* disable wireless config bsta wifi-iface section */
	pkg = uci_load_pkg(&ctx, UCI_WIRELESS);
	if (!pkg)
		goto out;

	section = config_get_section(ctx, pkg, UCI_WLAN_IFACE, "ifname", bk->name);
	if (!section)
		goto out_pkg;

	set_value(ctx, pkg, section, "disabled", "1", UCI_TYPE_STRING);
	uci_save(ctx, pkg);
	uci_commit(ctx, &pkg, false);
	ret = 0;

out_pkg:
	uci_unload(ctx, pkg);
out:
	uci_free_context(ctx);
	return ret;
}

bool config_find_bsta_agent(struct agent_config *cfg, struct uci_context *ctx,
		char *device)
{
	struct uci_package *pkg;
	struct uci_element *e;
	struct uci_section *section = NULL;
	int rv;
	bool found = false;

	rv = uci_load(ctx, UCI_AGENT, &pkg);
	if (rv)
		return found;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		char *c_device;

		if (strncmp(s->type, UCI_BK_AGENT, strlen(UCI_BK_AGENT)))
			continue;

		c_device = uci_lookup_option_string(ctx, s, "device");
		if (!c_device)
			continue;

		if (strncmp(device, c_device, 16))
			continue;

		found = true;
		break;
	}

	uci_unload(ctx, pkg);
	return found;
}

struct uci_section *config_find_bsta_wireless(struct agent_config *cfg,
		struct uci_context *ctx, struct uci_package *pkg, const char *device)
{
	struct uci_element *e;
	int rv;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		char *c_device, *mode;

		if (strncmp(s->type, UCI_WLAN_IFACE, strlen(UCI_WLAN_IFACE)))
			continue;

		c_device = uci_lookup_option_string(ctx, s, "device");
		if (!c_device || strncmp(device, c_device, 16))
			continue;

		mode = uci_lookup_option_string(ctx, s, "mode");
		if (!mode || strcmp(mode, "sta"))
			continue;

		return s;
	}


	return NULL;
}

int agent_config_prepare(struct agent_config *cfg)
{
	// TODO: iterate through 'wifi-device' sections in wireless config.
	// If corresponding 'wifi-radio <device-name>' section is not available
	// in 'mapagent' config, create one. Check supported bands of the new
	// wifi-device and add option 'band' to the wifi-radio section.
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e;
	struct blob_buf bb = {0};

	pkg = uci_load_pkg(&ctx, UCI_WIRELESS);
	if (!pkg)
		return -1;

	blob_buf_init(&bb, 0);

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		struct uci_section *wl_s;
		const char *device, *ifname;
		uint8_t band = 0;
		char obj_name[64] = {0};
		int rv;

		if (strncmp(s->type, UCI_WL_DEVICE, strlen(UCI_WL_DEVICE)))
			continue;

		device = s->e.name;

		snprintf(obj_name, sizeof(obj_name), "wifi.radio.%s",
				device);
		rv = ubus_call(obj_name, "status", &bb,
				wifi_radio_status_cb, &band);
		if (rv)
			continue;

		if (!config_find_radio(cfg, ctx, device))
			config_generate_radio(cfg, ctx, device, band);

		if (config_find_bsta_agent(cfg, ctx, device))
			continue;

		wl_s = config_find_bsta_wireless(cfg, ctx, pkg, device);
		if (!wl_s)
			continue;

		ifname = uci_lookup_option_string(ctx, wl_s, "ifname");
		if (!ifname)
			continue;

		config_generate_bsta_agent(cfg, ctx, device, ifname, band);
	}

	blob_buf_free(&bb);
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return 0;
}

int agent_config_init(struct agent_config *cfg)
{
	INIT_LIST_HEAD(&cfg->fhlist);
	INIT_LIST_HEAD(&cfg->bklist);
	INIT_LIST_HEAD(&cfg->radiolist);
	INIT_LIST_HEAD(&cfg->steer_excludelist);
	INIT_LIST_HEAD(&cfg->steer_btm_excludelist);

	agent_config_prepare(cfg);
	agent_config_reload(cfg);
	return 0;
}

void clean_bk(struct netif_bkcfg *p)
{
	list_del(&p->list);
	free(p);
}

int clean_all_bk(struct agent_config *cfg)
{
	struct netif_bkcfg *p, *tmp;

	list_for_each_entry_safe(p, tmp, &cfg->bklist, list)
		clean_bk(p);

	return 0;
}

void clean_fh(struct netif_fhcfg *p)
{
	list_del(&p->list);
	free(p);
}

int clean_all_fh(struct agent_config *cfg)
{
	struct netif_fhcfg *p, *tmp;

	list_for_each_entry_safe(p, tmp, &cfg->fhlist, list)
		clean_fh(p);

	return 0;
}

void clean_radio_cfg(struct agent_config_radio *p)
{
	list_del(&p->list);
	free(p);
}

int clean_all_radios(struct agent_config *cfg)
{
	struct agent_config_radio *p, *tmp;

	list_for_each_entry_safe(p, tmp, &cfg->radiolist, list)
		clean_radio_cfg(p);

	return 0;
}

int agent_config_clean(struct agent_config *cfg)
{
	clean_all_fh(cfg);
	clean_all_bk(cfg);
	clean_steer_btm_excl(cfg);
	clean_steer_excl(cfg);
	clean_all_radios(cfg);
	if (cfg->pcfg)
		free(cfg->pcfg);

	return 0;
}
int wifi_reorder_interfaces_by_device(struct agent_config *ac,
		struct uci_context *ctx, struct uci_package *pkg, char *device)
{
	int i, j = 0;
	int devnum = 0;
	char ifname[IFNAMSIZ] = {0};
	enum {
		W_IFNAME,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		{ .name = "ifname", .type = UCI_TYPE_STRING }
	};
	struct uci_option *tb[NUM_POLICIES];
	struct uci_element *e;

	trace("reordering interfaces for device %s\n", device);

	devnum = get_device_num_from_name(device);
	snprintf(ifname, IFNAMSIZ, "%s%d",
			ac->netdev,
			devnum);

	for (i = 1; i < 16; i++) {
		trace("iterating in search of %s\n", ifname);
		uci_foreach_element(&pkg->sections, e) {
			struct uci_section *s = uci_to_section(e);

			if (strncmp(s->type, UCI_WLAN_IFACE,
					strlen(UCI_WLAN_IFACE)))
				continue;

			uci_parse_section(s, opts, NUM_POLICIES, tb);

			if (!tb[W_IFNAME])
				continue;

			if (strncmp(tb[W_IFNAME]->v.string, ifname, sizeof(ifname)))
				continue;
			trace("found interface %s, reordering to %d\n", ifname, j);
			uci_reorder_section(ctx, s, j);
			j++;
			break;
		}

		snprintf(ifname, IFNAMSIZ, "%s%d%s%d",
				ac->netdev,
				devnum,
				(ac->brcm_setup ? "." : "_"),
				i);
	}

	return 0;
}

int wifi_reorder_interfaces(struct agent_config *ac)
{

	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	ctx = uci_alloc_context();
	if (!ctx)
		return -1;

	if (uci_load(ctx, UCI_WIRELESS, &pkg)) {
		uci_free_context(ctx);
		return -1;
	}

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);

		if (strncmp(s->type, UCI_WL_DEVICE, strlen(UCI_WL_DEVICE)))
			continue;

		wifi_reorder_interfaces_by_device(ac, ctx, pkg, s->e.name);
	}

	uci_commit(ctx, &pkg, false);
	uci_free_context(ctx);
	return 0;
}

int uci_set_bridge(char *config, char *bridge, char *proto, char *ipaddress)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e;
	struct uci_section *section = NULL;

	/** if bridge starts with br prefix, step past */
	if (!strncmp(bridge, "br-", 3))
		bridge += 3;

	pkg = uci_load_pkg(&ctx, "network");
	if (!pkg)
		return -1;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);

		if (strncmp(s->e.name, bridge, 16))
			continue;

		section = s;
		break;
	}

	if (!section) {
		struct uci_ptr ptr = {0};

		ptr.p = pkg;
		ptr.section = bridge;
		ptr.value = "interface";
		ptr.option = NULL;
		uci_set(ctx, &ptr);
		section = ptr.s;
	}

	set_value(ctx, pkg, section, "type", "bridge", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "is_lan", "1", UCI_TYPE_STRING);
	if (strlen(proto))
		set_value(ctx, pkg, section, "proto", proto, UCI_TYPE_STRING);
	if (!strcmp(proto, "static")) {
		set_value(ctx, pkg, section, "ipaddr", ipaddress,
				UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "netmask", "255.255.255.0",
				UCI_TYPE_STRING);
	}

	uci_commit(ctx, &pkg, false);
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return false;
}

int uci_add_dhcp(char *iface)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e;
	struct uci_section *section = NULL;
	struct uci_ptr ptr = {0};

	/** if bridge starts with br prefix, step past */
	if (!strncmp(iface, "br-", 3))
		iface += 3;

	pkg = uci_load_pkg(&ctx, "dhcp");
	if (!pkg)
		return -1;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);

		if (strncmp(s->e.name, iface, 16))
			continue;

		trace("Existing section found for ifname %s\n", iface);
		goto out;
	}

	trace("Adding DHCP section for ifname %s\n", iface);

	ptr.p = pkg;
	ptr.section = iface;
	ptr.value = "dhcp";
	ptr.option = NULL;
	uci_set(ctx, &ptr);
	section = ptr.s;

	set_value(ctx, pkg, section, "interface", iface, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "start", "100", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "limit", "150", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "leasetime", "1h", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "dhcpv6", "server", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ra", "server", UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);
out:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return false;
}

int uci_add_fw(struct agent_config *cfg, char *iface)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e;
	struct uci_section *section = NULL;
	int rv;

	/** if bridge starts with br prefix, step past */
	if (!strncmp(iface, "br-", 3))
		iface += 3;

	pkg = uci_load_pkg(&ctx, "firewall");
	if (!pkg)
		return -1;

	section = config_get_section(ctx, pkg, "zone", "name", iface);
	if (!section) {
		trace("No fw section found for %s\n", iface);
		rv = uci_add_section(ctx, pkg, "zone", &section);
		if (rv)
			goto out_pkg;
		set_value(ctx, pkg, section, "name", iface, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "network", iface, UCI_TYPE_LIST);
		set_value(ctx, pkg, section, "input", "ACCEPT", UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "output", "ACCEPT", UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "forward", "ACCEPT", UCI_TYPE_STRING);
		rv = uci_save(ctx, pkg);
		if (rv)
			goto out_pkg;
		uci_commit(ctx, &pkg, false);
	}

	section = config_get_section(ctx, pkg, "forwarding", "src", iface);
	if (!section) {
		//section = config_add_section(ctx, pkg, "firewall", "forwarding", "src", iface);
		//if (!section)
		//	goto out;
		rv = uci_add_section(ctx, pkg, "forwarding", &section);
		if (rv)
			goto out_pkg;

		set_value(ctx, pkg, section, "src", iface, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "dest", cfg->al_bridge, UCI_TYPE_STRING);
		uci_commit(ctx, &pkg, false);
	}

out_pkg:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return false;
}