/*
 * config.c - configurations handling
 *
 * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#include "config.h"

#if (EASYMESH_VERSION >= 3)
#ifdef USE_LIBDPP
#include <dpp_api.h>
#endif /* USE_LIBDPP */
#endif
#include <easy/bufutil.h>
#include <easy/utils.h>
#include <easymesh.h>
#include <i1905_wsc.h>
#include <libubox/blob.h>
#include <libubox/blobmsg.h>
#include <libubus.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ubusmsg.h>
#include <uci.h>

#include "agent.h"
#include "assoc_ctrl.h"
#include "steer_rules.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "wifi_opclass.h"

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

//Security and encryption
#define WPS_AUTH_OPEN          (0x0001)
#define WPS_AUTH_WPAPSK        (0x0002) /* deprecated */
#define WPS_AUTH_SHARED        (0x0004) /* deprecated */
#define WPS_AUTH_WPA           (0x0008) /* deprecated */
#define WPS_AUTH_WPA2          (0x0010)
#define WPS_AUTH_WPA2PSK       (0x0020)
#define WPS_AUTH_SAE           (0x0040)
#define WPS_AUTH_WPA3_T        (WPS_AUTH_WPA2PSK | WPS_AUTH_SAE)
#define WPS_AUTH_DPP           (0x0080)
#define WPS_AUTH_SAE_AKM24     (0x0100)
#define WPS_AUTH_INVALID       (0x0200) /* provided by RSN params config */

#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)


// UCI sections
#define UCI_BK_AGENT "bsta"
#define UCI_AP_AGENT "ap"
#define UCI_WLAN_IFACE "wifi-iface"
#define UCI_WL_DEVICE "wifi-device"
#define UCI_WIRELESS "wireless"
#define UCI_IEEE1905 "ieee1905"
#define UCI_AGENT "mapagent"
#define UCI_POLICY "policy"



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

int del_value_list(struct uci_context *ctx, struct uci_package *pkg,
		struct uci_section *s, const char *option,
		enum uci_option_type type);


struct netif_apcfg *create_fronthaul_iface_config(struct agent_config *cfg,
							const char *ifname);
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 = 0;

	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) {
		uci_free_context(*ctx);
		*ctx = NULL;
		return NULL;
	}

	return pkg;
}

/* expects buf to len 2 */
char *agent_get_controller_enabled(struct agent *a, char *buf)
{
	struct uci_context *ctx;
	struct uci_ptr ptr = {0};
	int ret;

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

	ptr.package = "mapcontroller";
	ptr.section = "controller";
	ptr.option = "enabled";
	ptr.target = UCI_TYPE_OPTION;

	ret = uci_lookup_ptr(ctx, &ptr, NULL, false);
	if (ret != UCI_OK ||!(ptr.flags & UCI_LOOKUP_DONE)) {
		goto error;
	}

	if (ptr.flags & UCI_LOOKUP_COMPLETE) {
		/* option found */
		strncpy(buf, ptr.o->v.string, 1);
	}

	uci_unload(ctx, ptr.p);
error:
	uci_free_context(ctx);
	return buf;
}


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, "mapagent");
	if (!pkg)
		return ret;

	section = config_get_section(ctx, pkg, UCI_BK_AGENT, "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;
}


/* buf must be allocated buffer of len size, len must be greater than 0 */
int wifi_get_section_option(const char *package, const char *sec_type,
			    const char *sec_key, const char *sec_value,
			    const char *get_key, char *buf, int len)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	struct uci_ptr ptr = {0};
	int ret = -1;

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

	section = config_get_section(ctx, pkg, sec_type, sec_key, sec_value);
	if (!section)
		goto out_pkg;

	ptr.p = pkg;
	ptr.s = section;
	ptr.option = get_key;
	ptr.target = UCI_TYPE_OPTION;

	ret = uci_lookup_ptr(ctx, &ptr, NULL, false);
	if (ret != UCI_OK || !(ptr.flags & UCI_LOOKUP_DONE)) {
		goto out_pkg;
	}

	/* only return 0 if option is found */
	ret = -1;

	if (ptr.flags & UCI_LOOKUP_COMPLETE) {
		/* option found */
		strncpy(buf, ptr.o->v.string, len - 1);
		ret = 0;
	}

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


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, "mapagent");
	if (!pkg)
		return ret;

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

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

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

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

	/* keep in memory copy in sync without reload */
	memcpy(bk->bssid, bssid, 6);
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, UCI_AGENT, &pkg) != UCI_OK) {
		dbg("config file 'wireless' not found!\n");
		goto out_uci;
	}

	section = config_get_section(ctx, pkg, UCI_BK_AGENT, "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, "onboarded", "1", UCI_TYPE_STRING);

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

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

	ptr.p = pkg;

	section = config_get_section(ctx, pkg, type, key, value);
	if (!section) {
		if (name) {
			ptr.section = name;
			ptr.value = type;
			ptr.option = NULL;
			uci_set(ctx, &ptr);
			if (uci_save(ctx, ptr.p) != UCI_OK)
				goto out;
			section = ptr.s;
		} else {
			if (uci_add_section(ctx, pkg, type, &section) != UCI_OK)
				goto out;

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

	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:
	return section;
}


/* below functions are mostly taken from ieee1905d */
bool uci_check_wifi_iface(const char *package_name, const char *ifname,
		const 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;
}

int uci_set_wireless_interface_option(const char *package_name,
		const char *section_type, const char *search_key, const char *search_val,
		const char *option, const char *value)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	if (!package_name || !search_val || !option || !value)
		return -1;

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

	if (uci_load(ctx, package_name, &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, 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 0;
}

uint16_t get_encryption(char *sec)
{
	uint16_t enc = 0;

	if (!strncmp(sec, "sae-mixed", 9)) {
		enc |= WPS_AUTH_WPA2PSK;
		enc |= WPS_AUTH_SAE;
	} else if (!strncmp(sec, "sae", 3)) {
		enc |= WPS_AUTH_SAE;
	} else if (!strncmp(sec, "psk-mixed", 9)) {
		enc |= WPS_AUTH_WPAPSK;
		enc |= WPS_AUTH_WPA2PSK;
	} else if (!strncmp(sec, "psk2", 4)) {
		enc |= WPS_AUTH_WPA2PSK;
	} else if (!strncmp(sec, "psk", 3)) {
		enc |= WPS_AUTH_WPAPSK;
	} else if (!strncmp(sec, "wpa-mixed", 9)) {
		enc |= WPS_AUTH_WPA;
		enc |= WPS_AUTH_WPA2;
	} else if (!strncmp(sec, "wpa2", 4)) {
		enc |= WPS_AUTH_WPA2;
	} else if (!strncmp(sec, "wpa", 3)) {
		enc |= WPS_AUTH_WPA;
	} else if (!strncmp(sec, "owe", 3)) {
		enc |= WPS_AUTH_INVALID;
	} else if (!strncmp(sec, "none", 4) || !strncmp(sec, "open", 4)) {
		enc |= WPS_AUTH_OPEN;
	}

	return enc;
}

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

	*mfp = 0;

	if ((auth_type & WPS_AUTH_WPA2PSK) && (auth_type & WPS_AUTH_SAE)) {
		strncat(encrypt_val, "sae-mixed", elen);
		*mfp = 1;
	} else if (auth_type & WPS_AUTH_SAE) {
		strncat(encrypt_val, "sae", elen);
		*mfp = 2;
	} else if ((auth_type & WPS_AUTH_WPAPSK) && (auth_type & WPS_AUTH_WPA2PSK)) {
		strncat(encrypt_val, "psk-mixed", elen);
		*mfp = 0;
	} else if ((auth_type & WPS_AUTH_WPA) && (auth_type & WPS_AUTH_WPA2)) {
		strncat(encrypt_val, "wpa-mixed", elen);
		*mfp = 0;
	} else if (auth_type & WPS_AUTH_WPAPSK) {
		strncat(encrypt_val, "psk", elen);
		*mfp = 0;
	} else if (auth_type & WPS_AUTH_WPA2PSK) {
		strncat(encrypt_val, "psk2", elen);
		*mfp = 0;
	} else if (auth_type & WPS_AUTH_WPA) {
		strncat(encrypt_val, "wpa", elen);
		*mfp = 0;
	} else if (auth_type & WPS_AUTH_WPA2) {
		strncat(encrypt_val, "wpa2", elen);
		*mfp = 0;
	} else if (auth_type & WPS_AUTH_INVALID) {
		strncat(encrypt_val, "invalid", elen);	/* update from 'RSN Parameters Config TLV' */
	} 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);
#if 0
	else if (encryption_type & WPS_ENCR_AES)
		strncat(encrypt_val, "+aes", elen);
#endif
	return true;
}

bool uci_add_wireless_iface_sec(const char *package_name, const char *interface_name,
		const char *section_type, const char *section_name)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_section *s = NULL;
	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;

	/* Don't use ubus call for wifi */
	if (!strcmp(services, "wireless")) {
		runCmd("uci commit wireless");
		agent_exec_platform_scripts("wireless_reload");
		return false;
	}

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

int uci_clear_traffic_sep(struct agent_config *cfg)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e, *tmp;
	struct uci_section *section = NULL;
	struct uci_ptr ptr = {0};
	struct netif_apcfg *ap = NULL;


	dbg("Clearing previous TS VIDs\n");

	/* remove from cfg */
	list_for_each_entry(ap, &cfg->aplist, list) {
		ap->vid = 1;
		uci_set_wireless_interface_option(UCI_AGENT, UCI_AP_AGENT,
						  "ifname", ap->name, "vid", "");

		uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
						  "ifname", ap->name,
						  "multi_ap_primary_vlan_id",
						  "");
	}

	if (cfg && cfg->pcfg)
		cfg->pcfg->pvid = 0;

	/* remove from uci */
	pkg = uci_load_pkg(&ctx, UCI_AGENT);
	if (!pkg)
		return -1;

	uci_foreach_element_safe(&pkg->sections, tmp, e) {
	        struct uci_option *option;

		section = uci_to_section(e);
		if (!strcmp(section->type, "policy")) {
			option = uci_lookup_option(ctx, section, "pvid");
			if (option) {
				ptr.p = pkg;
				ptr.s = section;
				ptr.o = option;
				uci_delete(ctx, &ptr);
				uci_save(ctx, pkg);
				break;
			}
		}

	}

	set_value_by_string("ieee1905", "ieee1905", "primary_vid", "", UCI_TYPE_STRING);

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

	return 0;
}

void uci_apply_traffic_sep(struct tlv_traffic_sep_policy *tlv)
{
	int i;
	uint8_t *ptr;

	ptr = (uint8_t *)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_AP_AGENT,
				"ssid",	ssid, "vid", vid);
	}
}

/* buf is expected to be 512byte empty buf */
bool config_find_uuid(struct agent_config *cfg, char *buf)
{
	struct uci_package *pkg;
	struct uci_context *ctx = NULL;
	struct uci_element *e;
	bool found = false;

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

	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, "wifi-iface"))
			continue;

		uci_foreach_element_safe(&s->options, tmp, x) {
			if (strcmp(x->name, "uuid"))
				continue;

			op = uci_to_option(x);
			strncpy(buf, op->v.string, 511);
			found = true;
			break;
		}

		if (found)
			break;
	}

	uci_unload(ctx, pkg);
	uci_free_context(ctx);

	return found;
}

static char *get_separator(char *netdev)
{
	char separators[] = { '_', '.', '-', '\0' };
	size_t i = 0;
	char *pos = NULL;

	while (separators[i] != '\0') {
		pos = strchr(netdev, separators[i++]);
		if (pos)
			break;
	}

	return pos;
}

/* TODO: batch the changes arther than commit oneby one */
struct netif_apcfg *uci_apply_wps_credentials(struct agent_config *cfg, char *ifname,
				 char *device, struct wps_credential *out,
				 struct wsc_ext *exts)
{
	char auth_type_str[20] = {0};
	char multiap_type[16] = {0};
	char multiap_str[4] = {0};
	char multiap_profile_str[4] = {0};
	struct uci_section *section;
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct netif_apcfg *ap;
	uint8_t multi_ap = 0;
	char band_str[2] = {0};
	char ssid[33] = {0}, network_key[65] = {0}, *bridge;
	int mfp = 0;

	bridge = cfg->al_bridge;
#ifdef VENDOR_EXTENSION
	if (strlen(exts->bridge))
		bridge = exts->bridge;
#endif
	/* step past br- prefix if present*/
	if (!strncmp("br-", bridge, 3))
		bridge += 3;

	if (out->band == BAND_5)
		strcpy(band_str, "5");
	else if (out->band == BAND_2)
		strcpy(band_str, "2");
	else if (out->band == BAND_6)
		strcpy(band_str, "6");
	else /* TODO: 60 */
		return NULL;

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

	agnt_dbg(LOG_APCFG, "Applying WSC configuration (%s):\n", ifname);
	agnt_dbg(LOG_APCFG, "  - SSID            : %s\n", ssid);
	agnt_dbg(LOG_APCFG, "  - AUTH_TYPE       : 0x%04x\n", out->auth_type);
	agnt_dbg(LOG_APCFG, "  - ENCRYPTION_TYPE : 0x%04x\n", out->enc_type);
	agnt_dbg(LOG_APCFG, "  - NETWORK_KEY     : %s\n", network_key);
	agnt_dbg(LOG_APCFG, "  - MAPIE_EXTENSION : 0x%02x\n", out->mapie);
	agnt_dbg(LOG_APCFG, "  - BAND            : %s\n", band_str);
	agnt_dbg(LOG_APCFG, "  - ENABLED        : %d\n", exts->enabled);

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

	if (multi_ap == 1)
		strncpy(multiap_type, "backhaul", 15);
	else if (multi_ap == 3)
		strncpy(multiap_type, "combined", 15);
	else
		strncpy(multiap_type, "fronthaul", 15);

	if (!get_encryption_value(out->auth_type, out->enc_type,
			auth_type_str, 20, &mfp)) {
		agnt_dbg(LOG_APCFG, "Unsupported encryption '%s' or cipher received!!\n", auth_type_str);
		return NULL;
	}

	ap = get_netif_apcfg_by_name(cfg, ifname);
	if (!ap) {
		ap = create_fronthaul_iface_config(cfg, ifname);
		if (!ap) {
			agnt_warn(LOG_APCFG, "%s: OOM! Failed to create 'ap' interface\n", __func__);
			return NULL;
		}
	}

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

	/* set mapagent config */
	if (uci_load(ctx, UCI_AGENT, &pkg)) {
		uci_free_context(ctx);
		return false;
	}

	section = config_get_section(ctx, pkg, UCI_AP_AGENT, "ifname", ifname);
	if (!section) {
		section = config_add_section(ctx, pkg, UCI_AGENT, UCI_AP_AGENT, NULL, "ifname", ifname);
		if (!section) {
			agnt_warn(LOG_APCFG, "%s: failed to add ap section for ifname:%s\n", __func__, ifname);
			goto out;
		}
	}

	set_value(ctx, pkg, section, "band", band_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "device", device, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "key", network_key, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "encryption", auth_type_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "type", multiap_type, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "enabled", (exts->enabled ? "1" : "0"), UCI_TYPE_STRING);

	ap->band = out->band;
	strncpy(ap->device, device, sizeof(ap->device));
	strncpy(ap->ssid, ssid, sizeof(ap->ssid));
	strncpy(ap->key, network_key, sizeof(ap->key));
	strncpy(ap->encryption, auth_type_str, sizeof(ap->encryption));
	ap->multi_ap = multi_ap;
	ap->enabled = exts->enabled;

	if (multi_ap & 0x01) {
		bool add;
		uint8_t disallow_profile = (out->mapie >> 2);

		add = (disallow_profile & BBSS_CONFIG_P1_BSTA_DISALLOWED);
		config_update2("mapagent", cfg, "ap", "ifname",
				ifname, "disallow_bsta_profile",
				add, "1", 1);
		ap->bsta_disallow = !!add;

		add = (disallow_profile & BBSS_CONFIG_P2_BSTA_DISALLOWED);
		config_update2("mapagent", cfg, "ap", "ifname",
				ifname, "disallow_bsta_profile",
				add, "2", 1);
		ap->bsta_disallow |= (add ? 2 : 0);
	}

	do {
		int i;

		del_value_list(ctx, pkg, section, "vendor_ie", UCI_TYPE_LIST);
		uci_commit(ctx, &pkg, false);

		for (i = 0; i < exts->num_ven_ies; i++) {
			char *buf, *p;
			struct wsc_vendor_ie *ext = &exts->ven_ies[i];
			int len = (3 * 2) /* oui */ +
				  (1 * 2) /* len */ +
				  (ext->len * 2) /* payload */ +
				  1 /* '\0' */;

			buf = p = calloc(1, len);
			if (!buf)
				continue;

			btostr((uint8_t *)ext->oui, 3, p);
			p+= 3 * 2; /* oui */
			btostr(ext->payload, ext->len, p);

			config_update2(UCI_AGENT, cfg, UCI_AP_AGENT, "ifname",
				       ifname, "vendor_ie", true, buf,
				       strlen(buf));
			free(buf);
		}

	} while(0);

	uci_commit(ctx, &pkg, false);
	uci_unload(ctx, pkg);

	/* set wireless config */
	if (uci_load(ctx, UCI_WIRELESS, &pkg)) {
		uci_free_context(ctx);
		return false;
	}

	section = config_get_section(ctx, pkg, UCI_WLAN_IFACE, "ifname", ifname);
	if (!section) {
		char section_name[32] = {0};
		char *psep;

		snprintf(section_name, sizeof(section_name),
				 "%s_ap", ifname);

		psep = get_separator(ifname);
		if (psep && *psep != '_') /* e.g.: '.' or '-' */
			/* only '_' allowed in section names */
			replace_char(section_name, *psep, '_');

		section = config_add_section(ctx, pkg, UCI_WIRELESS, UCI_WLAN_IFACE, section_name, "ifname", ifname);
		if (!section) {
			agnt_warn(LOG_APCFG, "%s: failed to add ap section for ifname:%s\n", __func__, ifname);
			goto out;
		}

		set_value(ctx, pkg, section, "ieee80211k", "1", UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "bss_transition", "1", UCI_TYPE_STRING);
	}

	do {
		char buf[512] = {0};
		uint8_t basemac[6] = {0};

		if (!config_find_uuid(cfg, buf)) {
			chrCmd(buf, sizeof(buf), "/lib/wifi/multiap db_get device.deviceinfo.BaseMACAddress");

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

			memset(buf, 0, sizeof(buf));
			chrCmd(buf, sizeof(buf), "uuidgen -s -r | cut -c 1-24");

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

			snprintf(buf + 24, 13, "%02X%02X%02X%02X%02X%02X",
				MAC2STR(basemac));
		}

		dbg("UUID: %s\n", buf);
		set_value(ctx, pkg, section, "uuid", buf, UCI_TYPE_STRING);

	} while(0);

	if (multi_ap == 0x01) {
		set_value(ctx, pkg, section, "hidden", (cfg->hide_bbss ? "1" : ""), UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "max_inactivity", "10", UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wds_sta", "1", UCI_TYPE_STRING);
#if (EASYMESH_VERSION >= 6)
		uci_set_wireless_interface_option("mapagent", "ap", "ifname", ifname,
						  "hidden", cfg->hide_bbss ? "1" : "");
		ap->hidden = cfg->hide_bbss;
#endif
	} else {
		char buf[2] = {0};

		if (out->band == BAND_2 || out->band == BAND_5) {
			if (wifi_get_section_option(UCI_WIRELESS, UCI_WLAN_IFACE,
							"ifname", ifname,
							"wps_pushbutton", buf,
							sizeof(buf))) {
				set_value(ctx, pkg, section, "wps_pushbutton", "1", UCI_TYPE_STRING);
			}

			if (wifi_get_section_option(UCI_WIRELESS, UCI_WLAN_IFACE,
							"ifname", ifname,
							"wps_cred_add_sae", buf,
							sizeof(buf))) {
				set_value(ctx, pkg, section, "wps_cred_add_sae", "1", UCI_TYPE_STRING);
			}
		}
	}

	snprintf(multiap_str, sizeof(multiap_str), "%d", multi_ap);
	snprintf(multiap_profile_str, sizeof(multiap_profile_str), "%d", cfg->map_profile);

	set_value(ctx, pkg, section, "network", bridge, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ssid", (char *)ssid, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "key", (char *)network_key, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "encryption", auth_type_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "mode", "ap", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "device", device, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "multi_ap", multiap_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "multi_ap_profile", multiap_profile_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "disabled", (exts->enabled ? "0" : "1"), UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "mbo", (mfp ? "1" : "0"), UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ieee80211v", "1", UCI_TYPE_STRING);
	if (mfp > 0)
		set_value(ctx, pkg, section, "ieee80211w", (mfp == 1 ? "1" : "2"), UCI_TYPE_STRING);
	else
		set_value(ctx, pkg, section, "ieee80211w", "0", UCI_TYPE_STRING);

	if (multi_ap != 0x01) {
		char device_type[32] = {0};
		uint16_t category, sub_category;

		category = buf_get_be16(out->device_type);
		sub_category = buf_get_be16(&out->device_type[6]);

		snprintf(device_type, sizeof(device_type),
			 "%d-%02x%02x%02x%02x-%d",
			 category,
			 out->device_type[2], out->device_type[3],
			 out->device_type[4], out->device_type[5],
			 sub_category);

		set_value(ctx, pkg, section, "wps_device_name", out->device_name, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wps_manufacturer", out->manufacturer, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wps_device_type", device_type, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wps_model_name", out->model_name, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wps_model_number", out->model_number, UCI_TYPE_STRING);
		set_value(ctx, pkg, section, "wps_serial_number", out->serial_number, UCI_TYPE_STRING);
	}

	if (multi_ap == 0x01) {
		if (cfg->eth_onboards_wifi_bhs) {
			struct netif_bkcfg *bk = NULL;

			list_for_each_entry(bk, &cfg->bklist, list) {
				int ret;

				if (bk->band != out->band)
					continue;

				ret = wifi_apply_iface_cfg(bk->name, auth_type_str,
							ssid, network_key);
				if (!ret) {
					strncpy(bk->ssid, ssid, sizeof(bk->ssid) - 1);
					strncpy(bk->key, network_key, sizeof(bk->key) - 1);
					strncpy(bk->encryption, auth_type_str, sizeof(bk->encryption) - 1);
					bk->onboarded = 1;
				}
			}
		}
	}

	do {
		char buf[2] = {0};

		if (wifi_get_section_option(UCI_WIRELESS, UCI_WLAN_IFACE,
					    "ifname", ifname,
					    "multicast_to_unicast_all", buf,
					    sizeof(buf)) &&
		    wifi_get_section_option(UCI_WIRELESS, UCI_WLAN_IFACE,
					    "ifname", ifname,
					    "multicast_to_unicast", buf,
					    sizeof(buf))) {
			/* if option was not found - fh = enabled, bh = disabled */
			set_value(ctx, pkg, section, "multicast_to_unicast_all", (multi_ap == 1 ? "0" : "1"), UCI_TYPE_STRING);
		}

		if (wifi_get_section_option(UCI_WIRELESS, UCI_WLAN_IFACE,
					    "ifname", ifname,
					    "isolate", buf,
					    sizeof(buf))) {
			set_value(ctx, pkg, section, "isolate", "0", UCI_TYPE_STRING);
		}
	} while(0);


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

	return ap;
out:
	clean_ap(ap);
	uci_free_context(ctx);

	return NULL;
}
/* 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 = NULL;

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

	return NULL;
}

/* create ap 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_ap->cfg = %p\n", __func__, new->name, new);

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

	return new;
}

#if (EASYMESH_VERSION >= 6)
uint8_t agent_get_first_available_mld_id(struct agent_config *cfg)
{
	uint8_t i;

	/* TODO: maximum ap/bsta number of MLDs, rather than arbitrary 16 */
	for (i = 1; i <= 16; i++) {
		struct mld_credential *mld;

		mld = agent_get_mld_credential_by_id(cfg, i);
		if (!mld || !mld->enabled)
			return i;
	}

	return 255;
}
struct mld_credential *agent_get_bsta_mld_credential(struct agent_config *cfg)
{
	struct mld_credential *mld = NULL;

	list_for_each_entry(mld, &cfg->mldlist, list) {
		if (mld->type == MLD_TYPE_BACKHAUL_STA)
			return mld;
	}

	return NULL;
}

struct mld_credential *agent_get_ap_mld_credential_by_ssid(struct agent_config *cfg,
							     char *ssid)
{
	struct mld_credential *mld = NULL;

	list_for_each_entry(mld, &cfg->mldlist, list) {
		if ((mld->type == MLD_TYPE_FRONTHAUL_BSS || mld->type == MLD_TYPE_BACKHAUL_BSS) &&
		    !strcmp(ssid, (char *)mld->ssid))
			return mld;
	}


	return NULL;
}

struct mld_credential *agent_get_mld_credential_by_id(struct agent_config *cfg,
						      uint8_t id)
{
	struct mld_credential *mld = NULL;

	list_for_each_entry(mld, &cfg->mldlist, list) {
		if (mld->id == id)
			return mld;
	}


	return NULL;
}

struct mld_credential *agent_get_mld_credential_by_ifname(struct agent_config *cfg,
						      const char *ifname)
{
	struct mld_credential *mld = NULL;

	list_for_each_entry(mld, &cfg->mldlist, list) {
		if (!strncmp(ifname, mld->ifname, sizeof(mld->ifname)))
			return mld;
	}


	return NULL;
}

struct mld_credential *agent_create_mld(struct agent_config *cfg, uint8_t id)
{
	struct mld_credential *mld;

	mld = calloc(1, sizeof(*mld));
	if (!mld)
		return NULL;

	mld->id = id;
	mld->enabled = true;
	mld->vlanid = 1;
	list_add(&mld->list, &cfg->mldlist);
	cfg->num_mld++;
	return mld;
}

/* get base bsta section for given band */
struct netif_bkcfg *agent_config_bsta_by_band(struct agent_config *cfg,
					      enum wifi_band band)
{
	struct netif_bkcfg *bk = NULL;

	list_for_each_entry(bk, &cfg->bklist, list) {
		/* return base bsta section for band - not netdev section */
		if (band == bk->band && !bk->is_mld_netdev)
			return bk;
	}

	return NULL;
}

int agent_config_mld_rem_bsta_all(struct agent_config *cfg)
{
	struct netif_bkcfg *bk = NULL;
	struct uci_context *ctx = NULL;
	struct uci_package *pkg_map, *pkg_wifi;

	pkg_map = uci_load_pkg(&ctx, UCI_AGENT);
	pkg_wifi = uci_load_pkg(&ctx, UCI_WIRELESS);

	if (!pkg_map || !pkg_wifi)
		return -1;

	list_for_each_entry(bk, &cfg->bklist, list) {
		struct uci_section *sct_map, *sct_wifi;

		/* remove from mapagent */
		sct_map = config_get_section(ctx, pkg_map, "bsta", "ifname", bk->name);
		if (sct_map) {

			if (bk->is_mld_netdev) {
				/* BSTA netdev section */
				bk->band = BAND_UNKNOWN;
				del_value_list(ctx, pkg_map, sct_map, "band", UCI_TYPE_LIST);
			} else {
				/* BSTA base section */
				bk->mld_id = 0;
				set_value(ctx, pkg_map, sct_map, "mld_id", "0", UCI_TYPE_STRING);
			}
		}

		/* remove from wireless */
		sct_wifi = config_get_section(ctx, pkg_wifi, "wifi-iface", "ifname", bk->name);
		if (sct_wifi) {
			struct uci_option *option;
			struct uci_ptr ptr = {0};

			option = uci_lookup_option(ctx, sct_wifi, "mld");
			if (option) {
				ptr.p = pkg_wifi;
				ptr.s = sct_wifi;
				ptr.o = option;
				uci_delete(ctx, &ptr);
				uci_save(ctx, pkg_wifi);
			}
		}
	}

	uci_commit(ctx, &pkg_map, false);
	uci_commit(ctx, &pkg_wifi, false);
	uci_unload(ctx, pkg_map);
	uci_unload(ctx, pkg_wifi);
	uci_free_context(ctx);

	return 0;
}

struct netif_apcfg *agent_config_get_ap_by_ssid(struct agent_config *cfg,
					    char *ssid)
{
	struct netif_apcfg *ap = NULL;

	list_for_each_entry(ap, &cfg->aplist, list) {
		if (!strcmp(ssid, ap->ssid))
			return ap;
	}

	return NULL;
}

struct netif_apcfg *agent_config_get_ap_by_ssid_on_band(struct agent_config *cfg,
					    enum wifi_band band,
					    char *ssid)
{
	struct netif_apcfg *ap = NULL;

	list_for_each_entry(ap, &cfg->aplist, list) {
		if (band == ap->band && !strcmp(ssid, ap->ssid))
			return ap;
	}

	return NULL;
}

static void build_mld_ifname(char *ifname, size_t size, const char *prefix,
			     unsigned int id)
{
	int num_len, prefix_len;

	num_len = snprintf(NULL, 0, "%u", id);
	prefix_len = (int)size - 1 - num_len;
	if (prefix_len < 0)
		prefix_len = 0;
	snprintf(ifname, size, "%.*s%u", prefix_len, prefix, id);
}

int uci_apply_agent_bsta_config(struct agent_config *cfg, uint8_t id, char *ssid,
			       char *key, enum wifi_band band)
{
	struct netif_bkcfg *bk = NULL;
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	char ifname[16] = {0};
	bool updated = false;
	char idstr[4] = {0};
	int rc = -1;

	if (id == 0) {
		warn("%s: invalid mld-id:%d\n", __func__, id);
		return -1;
	}

	dbg("Applying map-agent BSTA configuration:\n");
	dbg("  - ID              : %d\n", id);
	dbg("  - SSID            : %s\n", ssid);
	dbg("  - NETWORK_KEY     : %s\n", key);

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

	build_mld_ifname(ifname, sizeof(ifname), cfg->mld_sta_prefix, id - 1);
	snprintf(idstr, sizeof(idstr), "%d", id);
	section = config_get_section(ctx, pkg, "bsta", "ifname", ifname);
	if (!section) {
		section = config_add_section(ctx, pkg, UCI_AGENT, "bsta", NULL, "ifname", ifname);
		if (!section) {
			warn("%s: failed to add bsta section for ifname:%s\n", __func__, ifname);
			goto out;
		}
	}

	bk = get_netif_bkcfg_by_name(cfg, ifname);
	if (!bk) {
		bk = create_backhaul_iface_config(cfg, ifname);
		if (!bk) {
			warn("%s: OOM!\n", __func__);
			return -1;
		}
	}

	set_value(ctx, pkg, section, "onboarded", "1", UCI_TYPE_STRING);
	bk->onboarded = 1;
	set_value(ctx, pkg, section, "priority", "0", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "device", "all", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "enabled", "1", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "mld_id", idstr, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "is_mld", "1", UCI_TYPE_STRING);

	if (band && !(band & bk->band)) {
		const char *bandstr = band_to_str(band);

		if (bandstr) {
			bk->band &= (~BAND_UNKNOWN);

			dbg("%s: setting value band:%d\n", __func__, band);
			set_value(ctx, pkg, section, "band", bandstr, UCI_TYPE_LIST);
			bk->band |= band;
			updated = true;
		}
	}

	if (ssid && strlen(ssid)) {
		dbg("%s: setting value ssid:%s\n", __func__, ssid);
		set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
		strncpy((char *) bk->ssid, ssid, 32);
		updated = true;
	}

	if (key && strlen(key)) {
		dbg("%s: setting value key:%s\n", __func__, key);
		set_value(ctx, pkg, section, "key", key, UCI_TYPE_STRING);
		strncpy((char *) bk->key, key, 64);
		updated = true;
	}

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

	rc = 0;
out:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return rc;
}

int uci_apply_agent_mld_config(struct agent_config *cfg, uint8_t id, char *ssid,
			       char *key, enum mld_type type, uint16_t vlanid,
			       enum wifi_band band)
{
	struct mld_credential *mld = NULL;
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	char typestr[16] = {0};
	char ifname[16] = {0};
	char idstr[4] = {0};
	int rc = -1;
	const char *prefix;

	if (id == 0) {
		warn("%s: invalid mld-id:%d\n", __func__, id);
		return -1;
	}

	if (type == MLD_TYPE_BACKHAUL_BSS)
		strcpy(typestr, "backhaul");
	else if (type == MLD_TYPE_FRONTHAUL_BSS)
		strcpy(typestr, "fronthaul");
	else if (type == MLD_TYPE_COMBINED_BSS)
		strcpy(typestr, "combined");
	else if (type == MLD_TYPE_BACKHAUL_STA)
		strcpy(typestr, "station");
	else {
		warn("%s: invalid MLD type\n", __func__);
		return -1;
	}


	warn("Applying map-agent MLD configuration:\n");
	warn("  - ID              : %d\n", id);
	warn("  - SSID            : %s\n", ssid);
	warn("  - NETWORK_KEY     : %s\n", key);
	warn("  - TYPE            : %s\n", typestr);
	warn("  - VLANID          : %d\n", vlanid);


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

	snprintf(idstr, sizeof(idstr), "%d", id);
	section = config_get_section(ctx, pkg, "mld", "id", idstr);
	if (!section) {
		section = config_add_section(ctx, pkg, UCI_AGENT, "mld", NULL, "id", idstr);
		if (!section) {
			warn("%s: failed to add mld section for id:%s\n", __func__, idstr);
			goto out;
		}
	}

	mld = agent_get_mld_credential_by_id(cfg, id);
	if (!mld) {
		mld = agent_create_mld(cfg, id);
		if (!mld) {
			warn("%s: OOM!\n", __func__);
			goto out;
		}
	} else
		mld->id = id;

	mld->type = type;

	prefix = (type == MLD_TYPE_BACKHAUL_STA) ? cfg->mld_sta_prefix : cfg->mld_ap_prefix;
	build_mld_ifname(ifname, sizeof(ifname), prefix, id - 1);
	set_value(ctx, pkg, section, "ifname", ifname, UCI_TYPE_STRING);
	strncpy(mld->ifname, ifname, sizeof(mld->ifname) - 1);

	if (band && !(band & mld->band)) {
		const char *bandstr = band_to_str(band);

		if (bandstr) {
			dbg("%s: setting value band:%d\n", __func__, band);
			set_value(ctx, pkg, section, "band", bandstr, UCI_TYPE_LIST);
			mld->band |= band;
		}
	}

	if (ssid && strlen(ssid)) {
		dbg("%s: setting value ssid:%s\n", __func__, ssid);
		set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
		strncpy((char *) mld->ssid, ssid, 32);
	}

	if (key && strlen(key)) {
		dbg("%s: setting value key:%s\n", __func__, key);
		set_value(ctx, pkg, section, "key", key, UCI_TYPE_STRING);
		strncpy((char *) mld->key, key, 64);
	}

	if (is_vid_valid(vlanid)) {
		char vlanidstr[6] = {0};

		snprintf(vlanidstr, sizeof(vlanidstr), "%d", vlanid);
		set_value(ctx, pkg, section, "vid", vlanidstr, UCI_TYPE_STRING);
		mld->vlanid = vlanid;
	}

	dbg("%s: setting value type:%s\n", __func__, typestr);
	set_value(ctx, pkg, section, "type", typestr, UCI_TYPE_STRING);


	uci_commit(ctx, &pkg, false);

	rc = 0;
out:
	uci_unload(ctx, pkg);
	uci_free_context(ctx);
	return rc;
}



int uci_apply_wireless_mld_config(struct agent_config *cfg, uint8_t id,
				  char *ssid, char *key, const char *mode)
{
	char mldname[16] = {0};
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section = NULL;
	struct uci_element *e;
	struct uci_ptr ptr = {0};
	char ifname[16] = {0};
	const char *prefix;

	if (id == 0) {
		warn("%s: invalid mld-id:%d\n", __func__, id);
		return -1;
	}

	warn("Applying wireless MLD configuration:\n");
	warn("  - ID              : %d\n", id);
	warn("  - SSID            : %s\n", ssid);
	warn("  - NETWORK_KEY     : %s\n", key);
	warn("  - MODE            : %s\n", mode);


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

	snprintf(mldname, sizeof(mldname), "mld%d", id);
	prefix = (strcmp(mode, "sta") == 0) ? cfg->mld_sta_prefix : cfg->mld_ap_prefix;
	build_mld_ifname(ifname, sizeof(ifname), prefix, id - 1);

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

		if ((strncmp(s->type, "wifi-mld", 8)))
			continue;

		if ((strncmp(s->e.name, mldname, sizeof(mldname))))
			continue;

		section = s;
		break;
	}

	if (!section) {
		ptr.p = pkg;
		ptr.section = mldname;
		ptr.value = "wifi-mld";
		ptr.option = NULL;
		uci_set(ctx, &ptr);
		if (uci_save(ctx, ptr.p) != UCI_OK)
			goto out;
		section = ptr.s;
	}

	set_value(ctx, pkg, section, "sae_pwe", "2", UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "ifname", ifname, UCI_TYPE_STRING);
	set_value(ctx, pkg, section, "mode", mode, UCI_TYPE_STRING);

	if (ssid && strlen(ssid)) {
		dbg("%s: setting value ssid:%s\n", __func__, ssid);
		set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
	}
	if (key && strlen(key)) {
		dbg("%s: setting value key:%s\n", __func__, key);
		set_value(ctx, pkg, section, "key", key, UCI_TYPE_STRING);
	}

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

	return 0;
}
#endif

#if (EASYMESH_VERSION > 2)
struct dpp_chirp *dpp_chirp_by_ifname(struct agent_config *c,
				      const char *ifname)
{
	struct dpp_chirp *p = NULL;

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

	return NULL;
}

#endif

struct netif_apcfg *get_netif_apcfg_by_name(struct agent_config *c,
							const char *name)
{
	struct netif_apcfg *p = NULL;

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

	return NULL;
}

static struct steer_policy *get_steer_policy_by_name(struct netif_apcfg *c,
							const char *name)
{
	struct steer_policy *p = NULL;

	if (!c)
		return NULL;

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

	return NULL;
}

struct agent_config_radio *get_agent_config_radio(struct agent_config *c,
							const char *device)
{
	struct agent_config_radio *p = NULL;

	list_for_each_entry(p, &c->radiolist, list) {
		if (!strcmp(device, p->name))
			return p;
#if (EASYMESH_VERSION >= 6)
		if (!strcmp(device, "all"))
			return p;
#endif
	}

	return NULL;
}

struct agent_config_radio *get_agent_config_radio_from_iface(struct agent_config *c,
							const char *ifname)
{
	struct netif_apcfg *apcfg;
	struct netif_bkcfg *bkcfg;
	char *device;

	apcfg = get_netif_apcfg_by_name(c, ifname);
	if (apcfg) {
		device = (char *)apcfg->device;
	} else {
		bkcfg = get_netif_bkcfg_by_name(c, ifname);
		if (!bkcfg)
			return NULL;

		device = (char *)bkcfg->device;
	}

	return get_agent_config_radio(c, device);
}

static bool stax_is_entry_present(struct list_head *h, uint8_t *macaddr)
{
	struct stax *s = NULL;

	list_for_each_entry(s, h, list) {
		if (!memcmp(s->macaddr, macaddr, 6))
			return true;
	}

	return false;
}

void stax_add_entry(struct list_head *h, uint8_t *macaddr)
{
	struct stax *n = NULL;

	if (stax_is_entry_present(h, macaddr))
		return;

	n = calloc(1, sizeof(struct stax));
	if (n) {
		memcpy(n->macaddr, macaddr, 6);
		list_add(&n->list, h);
	}
}

void stax_del_entry(struct list_head *h, uint8_t *macaddr)
{
	struct stax *s = NULL, *tmp;

	list_for_each_entry_safe(s, tmp, h, list) {
		if (!memcmp(s->macaddr, macaddr, 6)) {
			list_del(&s->list);
			free(s);
			return;
		}
	}
}

static int clean_steer_btm_excl(struct policy_cfg *p)
{
	struct stax *n = NULL, *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 policy_cfg *p)
{
	struct stax *n = NULL, *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_apcfg *n = NULL;
	struct steer_policy *pol = NULL;
	struct policy_cfg *c;
	struct stax *x = NULL;

	if (!cfg)
		return;

	c = cfg->pcfg;
	if (!c)
		return;

	dbg("  Steer Exclude Lists -------\n");
	list_for_each_entry(x, &c->steer_excludelist, list) {
		dbg("    mac: " MACFMT "\n", MAC2STR(x->macaddr));
	}

	x = NULL;
	dbg("  Steer BTM Exclude Lists -------\n");
	list_for_each_entry(x, &c->steer_btm_excludelist, list) {
		dbg("    mac: " MACFMT "\n", MAC2STR(x->macaddr));
	}

	list_for_each_entry(n, &cfg->aplist, 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);
			 */
		}
	}
}

/* create ap config and initialize with default values */
struct netif_apcfg *create_fronthaul_iface_config(struct agent_config *cfg,
							const char *ifname)
{
	struct netif_apcfg *new;
	struct steer_rule *r = NULL;

	if (!cfg)
		return NULL;

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

	snprintf(new->name, 16, "%s", ifname);
	new->enabled = true;
	new->steer_btm_retry_secs = STEER_BTM_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);
	}

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

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

	return new;

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


#if (EASYMESH_VERSION > 2)
/* create ap config and initialize with default values */
struct dpp_chirp *create_dpp_chirp_config(struct agent_config *cfg,
					  const char *ifname)
{
	struct dpp_chirp *new;

	if (!cfg)
		return NULL;

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

	snprintf(new->ifname, 16, "%s", ifname);
	new->band = BAND_UNKNOWN;
	new->chirp_int = 10;

	dbg("%s: %s dpp_uri->cfg = %p\n", __func__, new->ifname, new);

	list_add(&new->list, &cfg->chirplist);
	return new;
}

#endif

static void config_update_entry(struct uci_context *ctx, struct uci_package *p,
				struct uci_section *s, const char *optname,
				int add, const 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", (const char *)val);
		uci_add_list(ctx, &ptr);
	} else {
		dbg("config: del list option: %s\n", (const 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)
		return -1;

	if (uci_load(ctx, confname, &pkg) != UCI_OK) {
		dbg("config file '%s' not found!\n", confname);
		uci_free_context(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,
		const void *value, int len)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg = NULL;
	struct uci_element *e;

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

	if (uci_load(ctx, confname, &pkg) != UCI_OK) {
		dbg("config file '%s' not found!\n", confname);
		uci_free_context(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_AL_BRIDGE,
		A_NETDEV,
		A_IFPREFIX,
		A_RESEND_NUM,
#ifdef DYNBH
		A_DYNBH,
		A_ETHWAN,
#endif
		A_DYN_CNTLR_SYNC,
#ifdef AGENT_ISLAND_PREVENTION
		A_ISL_PREV,
#endif
		A_ETH_ONBOARD,
		A_FOLLOW_STA_DFS,
		A_GUEST_ISOLATION,
		A_ON_BOOT_ONLY_SCAN,
		A_LIMIT_SCAN_RESULTS,
#ifdef OPER_CHAN_CHANGE_RELAY_MCAST
		A_CH_CHAN_RELAY_MCAST,
#endif
#ifdef CHECK_PARTIAL_WIFI_RELOAD
		A_PARTIAL_WIFI_RELOAD,
#endif
#ifdef VENDOR_EXTENSION
		A_EXTENSION_PLUGIN,
#endif
		A_HIDE_BBSS,
		A_BACKHAUL_OVERRIDE,
		A_OFF_CHAN_MONITOR,
#if (EASYMESH_VERSION >= 6)
		A_MLD_AP_PREFIX,
		A_MLD_STA_PREFIX,
		A_PHYSICAL_MLD,
#endif
		A_ASSOC_CTRL_FILE,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		[A_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
		[A_DEBUG] = { .name = "debug", .type = UCI_TYPE_STRING },
		[A_PROFILE] = { .name = "profile", .type = UCI_TYPE_STRING },
		[A_BRCM_SETUP] = { .name = "brcm_setup", .type = UCI_TYPE_STRING },
		/*[A_CONFIGURED] = { .name = "configured", .type = UCI_TYPE_STRING },*/
		[A_CNTLR_MAC] = { .name = "controller_macaddr", .type = UCI_TYPE_STRING },
		[A_AL_BRIDGE] = { .name = "al_bridge", .type = UCI_TYPE_STRING },
		[A_NETDEV] = { .name = "netdev", .type = UCI_TYPE_STRING },
		[A_IFPREFIX] = { .name = "ifprefix", .type = UCI_TYPE_STRING },
		[A_RESEND_NUM] = { .name = "resend_num", .type = UCI_TYPE_STRING },
#ifdef DYNBH
		[A_DYNBH] = { .name = "dynbh", .type = UCI_TYPE_STRING },
		[A_ETHWAN] = { .name = "ethwan", .type = UCI_TYPE_STRING },
#endif
		[A_DYN_CNTLR_SYNC] = { .name = "dyn_cntlr_sync", .type = UCI_TYPE_STRING },
#ifdef AGENT_ISLAND_PREVENTION
		[A_ISL_PREV] = { .name = "island_prevention", .type = UCI_TYPE_STRING },
#endif
		[A_ETH_ONBOARD] = { .name = "eth_onboards_wifi_bhs", .type = UCI_TYPE_STRING },
		[A_FOLLOW_STA_DFS] = { .name = "ap_follow_sta_dfs", .type = UCI_TYPE_STRING },
		[A_GUEST_ISOLATION] = { .name = "guest_isolation", .type = UCI_TYPE_STRING },
		[A_ON_BOOT_ONLY_SCAN] = { .name = "scan_on_boot_only", .type = UCI_TYPE_STRING },
		[A_LIMIT_SCAN_RESULTS] = { .name = "limit_scanresults", .type = UCI_TYPE_STRING },
#ifdef OPER_CHAN_CHANGE_RELAY_MCAST
		[A_CH_CHAN_RELAY_MCAST] = { .name = "chan_ch_relay_mcast", .type = UCI_TYPE_STRING },
#endif
#ifdef CHECK_PARTIAL_WIFI_RELOAD
		[A_PARTIAL_WIFI_RELOAD] = { .name = "partial_wifi_reload", .type = UCI_TYPE_STRING },
#endif
#ifdef VENDOR_EXTENSION
		[A_EXTENSION_PLUGIN] = { .name = "extplugin", .type = UCI_TYPE_LIST },
#endif
		[A_HIDE_BBSS] = { .name = "hide_backhaul_bss", .type = UCI_TYPE_STRING },
		[A_BACKHAUL_OVERRIDE] = { .name = "backhaul_override", .type = UCI_TYPE_STRING },
		[A_OFF_CHAN_MONITOR] = { .name = "off_channel_monitor", .type = UCI_TYPE_STRING },
#if (EASYMESH_VERSION >= 6)
		[A_MLD_AP_PREFIX] = { .name = "mld_ap_prefix", .type = UCI_TYPE_STRING },
		[A_MLD_STA_PREFIX] = { .name = "mld_sta_prefix", .type = UCI_TYPE_STRING },
		[A_PHYSICAL_MLD] = { .name = "physical_mld", .type = UCI_TYPE_STRING },
#endif
		[A_ASSOC_CTRL_FILE] = { .name = "assoc_ctrl_file", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_POLICIES];
	int prefix_idx = 0;

	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]) {
		int lvl = strtol(tb[A_DEBUG]->v.string, NULL, 10);

		lvl = ((lvl <= LOGLEVEL_MAX) ? lvl : LOGLEVEL_MAX);
		if (lvl >= 0) {
			a->debug_level = lvl;
		}
	} else {
		/* debug option missing in config */
		a->debug_level = DEFAULT_LOGLEVEL;
	}

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

	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_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_IFPREFIX])
		prefix_idx = A_IFPREFIX;
	else if (tb[A_NETDEV])
		prefix_idx = A_NETDEV;

	if (prefix_idx) {
		const char *netdev = NULL;

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

	if (tb[A_RESEND_NUM]) {
		const char *val = tb[A_RESEND_NUM]->v.string;

		a->resend_num = atoi(val);
	}

	if (tb[A_DYN_CNTLR_SYNC])
		a->dyn_cntlr_sync = !!atoi(tb[A_DYN_CNTLR_SYNC]->v.string);
	else
		a->dyn_cntlr_sync = true;

#ifdef DYNBH
	if (tb[A_DYNBH])
		a->dynbh = !!atoi(tb[A_DYNBH]->v.string);
	else
		a->dynbh = true;

	if (tb[A_ETHWAN])
		strncpy(a->ethwan, tb[A_ETHWAN]->v.string, sizeof(a->ethwan) - 1);
	else
		a->ethwan[0] = '\0';
#endif

#ifdef AGENT_ISLAND_PREVENTION
	if (tb[A_ISL_PREV])
		a->island_prevention = !!atoi(tb[A_ISL_PREV]->v.string);
	else
		a->island_prevention = true;
#endif

	if (tb[A_ETH_ONBOARD])
		a->eth_onboards_wifi_bhs = !!atoi(tb[A_ETH_ONBOARD]->v.string);
	else
		a->eth_onboards_wifi_bhs = false;

	if (tb[A_FOLLOW_STA_DFS])
		a->ap_follow_sta_dfs = !!atoi(tb[A_FOLLOW_STA_DFS]->v.string);
	else
		a->ap_follow_sta_dfs = false;

	if (tb[A_GUEST_ISOLATION])
		a->guest_isolation = !!atoi(tb[A_GUEST_ISOLATION]->v.string);
	else
		a->guest_isolation = false;

	if (tb[A_ON_BOOT_ONLY_SCAN])
		a->scan_on_boot_only = !!atoi(tb[A_ON_BOOT_ONLY_SCAN]->v.string);
	else
		a->scan_on_boot_only = false;

	if (tb[A_LIMIT_SCAN_RESULTS]) {
		const char *val = tb[A_LIMIT_SCAN_RESULTS]->v.string;

		a->limit_scanresults = atoi(val);
	} else {
		a->limit_scanresults = SCANRESULTS_MAX_NUM;
	}

#ifdef OPER_CHAN_CHANGE_RELAY_MCAST
	if (tb[A_CH_CHAN_RELAY_MCAST])
		a->chan_ch_relay_mcast = !!atoi(tb[A_CH_CHAN_RELAY_MCAST]->v.string);
	else
		a->chan_ch_relay_mcast = false;
#endif

#ifdef CHECK_PARTIAL_WIFI_RELOAD
	if (tb[A_PARTIAL_WIFI_RELOAD])
		a->partial_wifi_reload = !!atoi(tb[A_PARTIAL_WIFI_RELOAD]->v.string);
	else
		a->partial_wifi_reload = false;
#endif

#ifdef VENDOR_EXTENSION
	if (tb[A_EXTENSION_PLUGIN]) {
		struct uci_element *e;
		struct agent_extension_config *n;

		uci_foreach_element(&tb[A_EXTENSION_PLUGIN]->v.list, e) {
			n = calloc(1, sizeof(*n));
			if (n) {
				strncpy(n->name, e->name, 31);
				list_add_tail(&n->list, &a->extlist);
			}
		}
	}
#endif

	if (tb[A_HIDE_BBSS]) {
		a->hide_bbss = !!atoi(tb[A_HIDE_BBSS]->v.string);
	} else
		a->hide_bbss = true;

	if (tb[A_BACKHAUL_OVERRIDE])
		a->backhaul_override = !!atoi(tb[A_BACKHAUL_OVERRIDE]->v.string);

	if (tb[A_OFF_CHAN_MONITOR])
		a->off_channel_monitor = !!atoi(tb[A_OFF_CHAN_MONITOR]->v.string);

#if (EASYMESH_VERSION >= 6)
	if (tb[A_MLD_AP_PREFIX]) {
		strncpy(a->mld_ap_prefix, tb[A_MLD_AP_PREFIX]->v.string,
				sizeof(a->mld_ap_prefix) - 1);
	} else {
		strncpy(a->mld_ap_prefix, "wlan", sizeof(a->mld_ap_prefix) - 1);
	}

	if (tb[A_MLD_STA_PREFIX]) {
		strncpy(a->mld_sta_prefix, tb[A_MLD_STA_PREFIX]->v.string,
				sizeof(a->mld_sta_prefix) - 1);
	} else {
		strncpy(a->mld_sta_prefix, "wlan", sizeof(a->mld_sta_prefix) - 1);
	}

	if (tb[A_PHYSICAL_MLD])
		a->physical_mld = !!atoi(tb[A_PHYSICAL_MLD]->v.string);
	else
		a->physical_mld = true;
#endif

	if (tb[A_ASSOC_CTRL_FILE]) {
		const char *val = tb[A_ASSOC_CTRL_FILE]->v.string;

		if (val && *val)
			strncpy(a->assoc_ctrl_file, val,
					sizeof(a->assoc_ctrl_file) - 1);
	}

	return 0;
}

static int agent_config_get_controller_select(struct agent_config *a,
				       struct uci_section *s)
{
	enum {
		CTRL_SELECT_LOCAL,
#ifdef DYNBH
#ifdef PERSIST_CONTROLLER
		CTRL_SELECT_MODE,
#endif
#endif
		CTRL_SELECT_PROBE_INT,
		CTRL_SELECT_RETRY_INT,
#ifdef AGENT_DYNAMIC_CNTRL
		CTRL_SELECT_AUTOSTART,
#endif
		NUM_CTRL_SELECT_POLICIES,
	};

	const struct uci_parse_option opts[] = {
		[CTRL_SELECT_LOCAL] = { .name = "local", .type = UCI_TYPE_STRING },
#ifdef DYNBH
#ifdef PERSIST_CONTROLLER
		[CTRL_SELECT_MODE] = { .name = "mode", .type = UCI_TYPE_STRING },
#endif
#endif
		[CTRL_SELECT_PROBE_INT] = { .name = "probe_int", .type = UCI_TYPE_STRING },
		[CTRL_SELECT_RETRY_INT] = { .name = "retry_int", .type = UCI_TYPE_STRING },
#ifdef AGENT_DYNAMIC_CNTRL
		[CTRL_SELECT_AUTOSTART] = { .name = "autostart", .type = UCI_TYPE_STRING },
#endif
	};

	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");
		free(cscfg);
		return -1;
	}
	cscfg->local = atoi(tb[CTRL_SELECT_LOCAL]->v.string);
#ifdef DYNBH
#ifdef PERSIST_CONTROLLER
	cscfg->discovery_mode = DYNAMIC_CNTLR_MODE_AUTO;
	if (tb[CTRL_SELECT_MODE]) {
		const char *mode = tb[CTRL_SELECT_MODE]->v.string;

		if (!strncmp(mode, "auto", 4)) {
			cscfg->discovery_mode = DYNAMIC_CNTLR_MODE_AUTO;
		} else if (!strncmp(mode, "disabled", 4)) {
			cscfg->discovery_mode = DYNAMIC_CNTLR_MODE_DISABLED;
		} else if (!strncmp(mode, "controller", 4)) {
			cscfg->discovery_mode = DYNAMIC_CNTLR_MODE_CONTROLLER;
		}
	}
#endif
#endif
	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);
#ifdef AGENT_DYNAMIC_CNTRL
	if (tb[CTRL_SELECT_AUTOSTART])
		cscfg->autostart = atoi(tb[CTRL_SELECT_AUTOSTART]->v.string);
#endif

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

	return 0;
}

#define DEFAULT_BH_MISS_TMO 60
#define DEFAULT_BH_RECONF_TMO (60 * 5) /* 5 minutes */
static void agent_config_get_dynamic_backhaul(struct agent_config *cfg,
				       struct uci_section *s)
{
	enum {
		DYN_BH_MISS_TMO,
		DYN_BH_RECONF_TMO,
		NUM_DYN_BH_POLICIES,
	};

	const struct uci_parse_option opts[] = {
		[DYN_BH_MISS_TMO] = { .name = "missing_bh_timer", .type = UCI_TYPE_STRING },
		[DYN_BH_RECONF_TMO] = { .name = "missing_bh_reconfig_timer", .type = UCI_TYPE_STRING },
	};

	struct uci_option *tb[NUM_DYN_BH_POLICIES];

	uci_parse_section(s, opts, NUM_DYN_BH_POLICIES, tb);

	memset(&cfg->dbhcfg, 0, sizeof(struct dyn_bh_cfg));

	if (tb[DYN_BH_MISS_TMO])
		cfg->dbhcfg.bh_miss_tmo = atoi(tb[DYN_BH_MISS_TMO]->v.string);
	else
		cfg->dbhcfg.bh_miss_tmo = DEFAULT_BH_MISS_TMO;

	if (tb[DYN_BH_RECONF_TMO])
		cfg->dbhcfg.bh_reconf_tmo = atoi(tb[DYN_BH_RECONF_TMO]->v.string);
	else
		cfg->dbhcfg.bh_reconf_tmo = DEFAULT_BH_RECONF_TMO;
}

static int agent_config_get_wifi_radio(struct agent_config *a,
				       struct uci_section *s)
{
	enum {
		WIFI_RADIO_DEVICE,
		WIFI_RADIO_BAND,
		WIFI_RADIO_ENCRYPTION,
		WIFI_RADIO_DEDICATED,
		WIFI_RADIO_STEER_POLICY,
		WIFI_RADIO_UTIL_THRESHOLD,
		WIFI_RADIO_RCPI_THRESHOLD,
		WIFI_RADIO_REPORT_RCPI_THRESHOLD,
		WIFI_RADIO_INCLUDE_STA_STATS,
		WIFI_RADIO_INCLUDE_STA_METRIC,
#if (EASYMESH_VERSION > 2)
		WIFI_RADIO_INCLUDE_WIFI6_STA_STATUS,
#endif
		WIFI_RADIO_RCPI_HYSTERESIS_MARGIN,
		WIFI_RADIO_REPORT_UTIL_THRESHOLD,
		WIFI_RADIO_IFPREFIX,
#if (EASYMESH_VERSION >= 6)
		WIFI_RADIO_PUNCT_BITMAP,
#endif
		NUM_WIFI_RADIO_POLICIES,
	};
	const struct uci_parse_option opts[] = {
		[WIFI_RADIO_DEVICE] = { .name = "device", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_ENCRYPTION] = { .name = "encryption", .type = UCI_TYPE_LIST },
		[WIFI_RADIO_DEDICATED] = { .name = "dedicated_backhaul", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_STEER_POLICY] = { .name = "steer_policy", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_UTIL_THRESHOLD] = { .name = "util_threshold", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_RCPI_THRESHOLD] = { .name = "rcpi_threshold", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_REPORT_RCPI_THRESHOLD] = { .name = "report_rcpi_threshold", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_INCLUDE_STA_STATS] = { .name = "include_sta_stats", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_INCLUDE_STA_METRIC] = { .name = "include_sta_metric", .type = UCI_TYPE_STRING },
#if (EASYMESH_VERSION > 2)
		[WIFI_RADIO_INCLUDE_WIFI6_STA_STATUS] = { .name = "include_wifi6_sta_status", .type = UCI_TYPE_STRING },
#endif
		[WIFI_RADIO_RCPI_HYSTERESIS_MARGIN] = { .name = "rcpi_hysteresis_margin", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_REPORT_UTIL_THRESHOLD] = { .name = "report_util_threshold", .type = UCI_TYPE_STRING },
		[WIFI_RADIO_IFPREFIX] = { .name = "ifprefix", .type = UCI_TYPE_STRING },
#if (EASYMESH_VERSION >= 6)
		[WIFI_RADIO_PUNCT_BITMAP] = { .name = "punct_bitmap", .type = UCI_TYPE_STRING },
#endif
	};
	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 && band != 6) {
			warn("Incorrect band '%d' in config\n", band);
			return -1;
		}
	}

	if (ifname && band) {
		uint8_t rcpi_threshold = 0;
		uint8_t report_rcpi_threshold = 0;

		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, IFNAMSIZ - 1);
		n->name[IFNAMSIZ - 1] = '\0';
		if (band == 2) {
			n->band = BAND_2;
			rcpi_threshold = 70;
			report_rcpi_threshold = 80;
		} else if (band == 5) {
			n->band = BAND_5;
			rcpi_threshold = 86;
			report_rcpi_threshold = 96;
		} else if (band == 6) {
			n->band = BAND_6;
			rcpi_threshold = 86;
			report_rcpi_threshold = 96;
		}
		else
			n->band = BAND_UNKNOWN;

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

			uci_foreach_element(&tb[WIFI_RADIO_ENCRYPTION]->v.list, xi) {
				if (!strcmp(xi->name, "sae-mixed")) {
					n->encryption |= WPS_AUTH_WPA2PSK;
					n->encryption |= WPS_AUTH_SAE;
				} else if (!strcmp(xi->name, "sae")) {
					n->encryption |= WPS_AUTH_SAE;
				} else if (!strcmp(xi->name, "psk2")) {
					n->encryption |= WPS_AUTH_WPA2PSK;
				} else if (!strcmp(xi->name, "none")) {
					n->encryption |= WPS_AUTH_OPEN;
				} else if (!strcmp(xi->name, "psk-mixed")) {
					n->encryption |= WPS_AUTH_WPAPSK;
					n->encryption |= WPS_AUTH_WPA2PSK;
				} else if (!strcmp(xi->name, "psk")) {
					n->encryption |= WPS_AUTH_WPAPSK;
				} else if (!strcmp(xi->name, "wpa")) {
					n->encryption |= WPS_AUTH_WPA;
				} else if (!strcmp(xi->name, "wpa2")) {
					n->encryption |= WPS_AUTH_WPA2;
				} else if (!strcmp(xi->name, "owe")) {
					n->encryption |= WPS_AUTH_INVALID;
				}
			}
		} else {
			n->encryption |= WPS_AUTH_WPA2PSK;
			n->encryption |= WPS_AUTH_SAE;
			n->encryption |= WPS_AUTH_OPEN;
			n->encryption |= WPS_AUTH_WPA;
			n->encryption |= WPS_AUTH_WPA2;
			n->encryption |= WPS_AUTH_WPAPSK;
			n->encryption |= WPS_AUTH_DPP;
			n->encryption |= WPS_AUTH_SAE_AKM24;
			n->encryption |= WPS_AUTH_INVALID;
		}

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

		if (tb[WIFI_RADIO_RCPI_THRESHOLD])
			n->rcpi_threshold = atoi(tb[WIFI_RADIO_RCPI_THRESHOLD]->v.string);
		else
			n->rcpi_threshold = rcpi_threshold;

		if (tb[WIFI_RADIO_REPORT_RCPI_THRESHOLD])
			n->report_rcpi_threshold =
				atoi(tb[WIFI_RADIO_REPORT_RCPI_THRESHOLD]->v.string);
		else
			n->report_rcpi_threshold = report_rcpi_threshold;

		if (tb[WIFI_RADIO_STEER_POLICY])
			n->steer_policy = atoi(tb[WIFI_RADIO_STEER_POLICY]->v.string);

		if (tb[WIFI_RADIO_UTIL_THRESHOLD])
			n->util_threshold = atoi(tb[WIFI_RADIO_UTIL_THRESHOLD]->v.string);

		if (tb[WIFI_RADIO_RCPI_HYSTERESIS_MARGIN])
			n->rcpi_hysteresis_margin =
				atoi(tb[WIFI_RADIO_RCPI_HYSTERESIS_MARGIN]->v.string);
		else
			n->rcpi_hysteresis_margin = 0;

		if (tb[WIFI_RADIO_REPORT_UTIL_THRESHOLD])
			n->report_util_threshold =
				atoi(tb[WIFI_RADIO_REPORT_UTIL_THRESHOLD]->v.string);

		if (tb[WIFI_RADIO_INCLUDE_STA_STATS])
			n->include_sta_stats =
				atoi(tb[WIFI_RADIO_INCLUDE_STA_STATS]->v.string);

		if (tb[WIFI_RADIO_INCLUDE_STA_METRIC])
			n->include_sta_metric =
				atoi(tb[WIFI_RADIO_INCLUDE_STA_METRIC]->v.string);

#if (EASYMESH_VERSION > 2)
		if (tb[WIFI_RADIO_INCLUDE_WIFI6_STA_STATUS])
			n->include_wifi6_sta_status =
				atoi(tb[WIFI_RADIO_INCLUDE_WIFI6_STA_STATUS]->v.string);
#endif
		if (tb[WIFI_RADIO_IFPREFIX]) {
			const char *ifprefix;

			ifprefix = tb[WIFI_RADIO_IFPREFIX]->v.string;
			strncpy(n->ifprefix, ifprefix, sizeof(n->ifprefix) - 1);
		}
#if (EASYMESH_VERSION >= 6)
		if (tb[WIFI_RADIO_PUNCT_BITMAP]) {
			const char *val = tb[WIFI_RADIO_PUNCT_BITMAP]->v.string;
			int len = strlen(val);

			if (len <= 4) {
				char buf[5] = "0000";

				strncpy(buf + (4 - strlen(val)), val, strlen(val));
				strtob(buf, 2, (unsigned char *)&n->punct_bitmap);
			}
		}
#endif
	}

	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_BSSID,
		BK_PRIORITY,
#if (EASYMESH_VERSION >= 6)
		BK_IS_MLD,
		BK_MLD_ID,
		BK_BAND_LIST,
#endif
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		[BK_IFNAME] = { .name = "ifname", .type = UCI_TYPE_STRING },
		[BK_DEVICE] = { .name = "device", .type = UCI_TYPE_STRING },
		[BK_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
		[BK_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
		[BK_ONBOARDED] = { .name = "onboarded", .type = UCI_TYPE_STRING },
		[BK_SSID] = { .name = "ssid", .type = UCI_TYPE_STRING },
		[BK_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
		[BK_ENCRYPTION] = { .name = "encryption", .type = UCI_TYPE_STRING },
		[BK_BSSID] = { .name = "bssid", .type = UCI_TYPE_STRING },
		[BK_PRIORITY] = { .name = "priority", .type = UCI_TYPE_STRING },
#if (EASYMESH_VERSION >= 6)
		[BK_IS_MLD] = { .name = "is_mld", .type = UCI_TYPE_STRING },
		[BK_MLD_ID] = { .name = "mld_id", .type = UCI_TYPE_STRING },
		[BK_BAND_LIST] = { .name = "band", .type = UCI_TYPE_LIST },
#endif
	};
	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 'bsta %s' config!! ignore\n",
					ifname);
		}
	} else {
		warn("No ifname in bsta 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;
		else if (band == 6)
			bk->band = BAND_6;
	}
#if (EASYMESH_VERSION >= 6)
	else if (tb[BK_BAND_LIST]) {
		struct uci_element *xi;

		dbg("band: param: \n");
		uci_foreach_element(&tb[BK_BAND_LIST]->v.list, xi) {
			int band = atoi(xi->name);
			dbg("\t%s\n", xi->name);

			bk->band &= ~BAND_UNKNOWN;

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

	if (tb[BK_IS_MLD])
		bk->is_mld_netdev = !!atoi(tb[BK_IS_MLD]->v.string);

	if (tb[BK_MLD_ID])
		bk->mld_id = atoi(tb[BK_MLD_ID]->v.string);

#endif
	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_BSSID])
		hwaddr_aton(tb[BK_BSSID]->v.string, bk->bssid);

	if (tb[BK_PRIORITY])
		bk->priority = atoi(tb[BK_PRIORITY]->v.string);
	else {
		if (bk->band == BAND_2)
			bk->priority = 2;
		else if (bk->band == BAND_5)
			bk->priority = 1;
		else if (bk->band == BAND_6)
			bk->priority = 0;
	}

#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
	if (!bk->onboarded) {
		struct agent *agnt = container_of(a, struct agent, cfg);
		struct dpp_peer *peer = agnt->own_peer;
		struct dpp_conf_req *req = &peer->e_req;
		int idx = req->num_bsta;

		if (idx <= 3) {
			char *chan_list = NULL;

			req->blist[idx].l2_netrole = DPP_NETROLE_MAP_BH_STA;
			req->blist[idx].akm = DPP_AKM_SAE_DPP;
			if (bk->band == BAND_2)
				chan_list = strdup("81/1");
			else if (bk->band == BAND_5)
				chan_list = strdup("115/36");
			else if (bk->band == BAND_6)
				chan_list = strdup("133/65");

			req->blist[idx].chan_list = chan_list;
			req->num_bsta++;
		}
	}
#endif
#endif
	return 0;
}

static int agent_config_get_ap(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		AP_IFNAME,
		AP_BAND,
		AP_STEER,
		AP_DEVICE,
		AP_BTM_RETRY,
		AP_BTM_RETRY_SECS,
		AP_ASSOC_CTRL_SECS,
		AP_SSID,
		AP_KEY,
		AP_ENCRYPTION,
		AP_ENABLED,
		AP_VID,
		AP_TYPE,
		AP_BSTA_DISALLOW,
		AP_DISALLOW_ASSOC,
#if (EASYMESH_VERSION >= 6)
		AP_MLD_ID,
		AP_HIDDEN,
		AP_BSS_INDEX,
#endif
		NUM_POLICIES,
	};
	const struct uci_parse_option opts[] = {
		[AP_IFNAME] = { .name = "ifname", .type = UCI_TYPE_STRING },
		[AP_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
		[AP_STEER] = { .name = "steer", .type = UCI_TYPE_LIST },
		[AP_DEVICE] = { .name = "device", .type = UCI_TYPE_STRING },
		[AP_BTM_RETRY] = { .name = "btm_retry", .type = UCI_TYPE_STRING },
		[AP_BTM_RETRY_SECS] = { .name = "btm_retry_secs", .type = UCI_TYPE_STRING },
		[AP_ASSOC_CTRL_SECS] = { .name = "assoc_ctrl_secs", .type = UCI_TYPE_STRING },
		[AP_SSID] = { .name = "ssid", .type = UCI_TYPE_STRING },
		[AP_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
		[AP_ENCRYPTION] = { .name = "encryption", .type = UCI_TYPE_STRING },
		[AP_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
		[AP_VID] = { .name = "vid", .type = UCI_TYPE_STRING },
		[AP_TYPE] = { .name = "type", .type = UCI_TYPE_STRING },
		[AP_BSTA_DISALLOW] = { .name = "disallow_bsta_profile", .type = UCI_TYPE_LIST },
		[AP_DISALLOW_ASSOC] = { .name = "disallow_assoc", .type = UCI_TYPE_STRING },
#if (EASYMESH_VERSION >= 6)
		[AP_MLD_ID] = { .name = "mld_id", .type = UCI_TYPE_STRING },
		[AP_HIDDEN] = { .name = "hidden", .type = UCI_TYPE_STRING },
		[AP_BSS_INDEX] = { .name = "bss_index", .type = UCI_TYPE_STRING },
#endif
	};
	struct uci_option *tb[NUM_POLICIES];
	struct netif_apcfg *ap;

	uci_parse_section(s, opts, NUM_POLICIES, tb);

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

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

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

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

		if (band == 2)
			ap->band = BAND_2;
		else if (band == 5)
			ap->band = BAND_5;
		else if (band == 6)
			ap->band = BAND_6;
	} else {
		dbg("%s: FH:%s is missing band option, discarding!\n",
		    __func__, ap->name);
		clean_ap(ap);
		return -1;
	}

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

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

			dbg("\t%s\n", xi->name);
			p = get_steer_policy_by_name(ap, 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;
		}
	}

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

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

		strncpy(ap->device, device, sizeof(ap->device) - 1);
	} else {
		dbg("%s: FH:%s is missing device option, discarding!\n",
		    __func__, ap->name);
		clean_ap(ap);
		return -1;
	}

	if (tb[AP_BTM_RETRY])
		ap->steer_btm_retry = atoi(tb[AP_BTM_RETRY]->v.string);

	if (tb[AP_BTM_RETRY_SECS])
		ap->steer_btm_retry_secs = atoi(tb[AP_BTM_RETRY_SECS]->v.string);

	if (tb[AP_ASSOC_CTRL_SECS])
		ap->assoc_control_time =
				atoi(tb[AP_ASSOC_CTRL_SECS]->v.string);

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

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

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

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

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

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

	if (tb[AP_ENABLED])
		ap->enabled = atoi(tb[AP_ENABLED]->v.string);

	if (tb[AP_VID])
		ap->vid = atoi(tb[AP_VID]->v.string);
	else {
		/* if VID is not known or unspecified, assume pvid */
		ap->vid = (a->pcfg && a->pcfg->pvid ? a->pcfg->pvid : 1);
	}

	if (tb[AP_TYPE]) {
		const char *type = tb[AP_TYPE]->v.string;

		if (!strcmp(type, "backhaul"))
			ap->multi_ap = 1;
		else if (!strcmp(type, "fronthaul"))
			ap->multi_ap = 2;
		else if (!strcmp(type, "combined"))
			ap->multi_ap = 3;
		else {
			clean_ap(ap);
			return -1;
		}
	}

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

		uci_foreach_element(&tb[AP_BSTA_DISALLOW]->v.list, xi) {
			uint8_t profile;

			profile = atoi(xi->name);
			if (profile > 2)
				continue;

			ap->bsta_disallow |= profile;
		}
	}

	if (tb[AP_DISALLOW_ASSOC])
		ap->disallow_assoc = atoi(tb[AP_DISALLOW_ASSOC]->v.string);

#if (EASYMESH_VERSION >= 6)
	if (tb[AP_MLD_ID])
		ap->mld_id = atoi(tb[AP_MLD_ID]->v.string);
	else
		ap->mld_id = 0;

	if (tb[AP_HIDDEN])
		ap->hidden = !!atoi(tb[AP_HIDDEN]->v.string);

	if (tb[AP_BSS_INDEX])
		ap->bss_index = (uint8_t)atoi(tb[AP_BSS_INDEX]->v.string);
	else
		ap->bss_index = UINT8_MAX;
#endif
	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,
		POL_EXCLUDE,
		POL_EXCLUDE_BTM,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		[POL_REPORT_INTERVAL] = { .name = "report_interval", .type = UCI_TYPE_STRING },
		[POL_PVID] = { .name = "pvid", .type = UCI_TYPE_STRING },
		[POL_PCP_DEFAULT] = { .name = "pcp_default", .type = UCI_TYPE_STRING },
		[POL_REPORT_SCAN] = { .name = "report_scan", .type = UCI_TYPE_STRING },
		[POL_REPORT_STA_ASSOCFAILS] = { .name = "report_sta_assocfails", .type = UCI_TYPE_STRING },
		[POL_REPORT_STA_ASSOCFAILS_RATE] = { .name = "report_sta_assocfails_rate", .type = UCI_TYPE_STRING },
		[POL_EXCLUDE] = { .name = "steer_exclude", .type = UCI_TYPE_LIST },
		[POL_EXCLUDE_BTM] = { .name = "steer_exclude_btm", .type = UCI_TYPE_LIST },
	};

	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;

	INIT_LIST_HEAD(&cfg->steer_excludelist);
	INIT_LIST_HEAD(&cfg->steer_btm_excludelist);

	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 (tb[POL_EXCLUDE]) {
		struct uci_element *xi;

		dbg("Steer: exclude:\n");
		uci_foreach_element(&tb[POL_EXCLUDE]->v.list, xi) {
			uint8_t macaddr[ETH_ALEN];

			dbg("\t%s\n", xi->name);
			hwaddr_aton(xi->name, macaddr);
			stax_add_entry(&cfg->steer_excludelist, macaddr);
		}
	}

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

		dbg("Steer: exclude_btm:\n");
		uci_foreach_element(&tb[POL_EXCLUDE_BTM]->v.list, xi) {
			uint8_t macaddr[ETH_ALEN];

			dbg("\t%s\n", xi->name);
			hwaddr_aton(xi->name, macaddr);
			stax_add_entry(&cfg->steer_btm_excludelist, macaddr);
		}
	}

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

	a->pcfg = cfg;

	return 0;
}

#if (EASYMESH_VERSION > 2)
static int agent_config_get_dpp(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		DPP_PERSIST_URI,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		[DPP_PERSIST_URI] = { .name = "persist_uri", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_POLICIES];

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	if (tb[DPP_PERSIST_URI])
		a->persist_uri = atoi(tb[DPP_PERSIST_URI]->v.string);

	return 0;
}

static int agent_config_get_dpp_chirp(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		DPP_IFNAME,
		DPP_DEVICE,
		DPP_BAND,
		DPP_TYPE,
		DPP_CHAN,
		DPP_CHIRP_INT,
		NUM_POLICIES
	};
	const struct uci_parse_option opts[] = {
		[DPP_IFNAME] = { .name = "ifname", .type = UCI_TYPE_STRING },
		[DPP_DEVICE] = { .name = "device", .type = UCI_TYPE_STRING },
		[DPP_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
		[DPP_TYPE] = { .name = "type", .type = UCI_TYPE_STRING },
		[DPP_CHAN] = { .name = "channel", .type = UCI_TYPE_LIST },
		[DPP_CHIRP_INT] = { .name = "chirp_interval", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_POLICIES];
	const char *ifname;
	struct dpp_chirp *uri;

/*
config dpp_chirp
        option device 'wl1'
        option band '2'
        option ifname 'wl1'
        list channel '1'
        list channel '11'
        option type 'qrcode'
*/

	uci_parse_section(s, opts, NUM_POLICIES, tb);

	if (tb[DPP_IFNAME]) {
		ifname = tb[DPP_IFNAME]->v.string;
		uri = dpp_chirp_by_ifname(a, ifname);
		if (!uri) {
			uri = create_dpp_chirp_config(a, ifname);
			if (!uri) {
				warn("%s: OOM!\n", __func__);
				return -1;
			}
		} else {
			warn("Duplicate 'dpp uri %s' config!! ignore\n",
					ifname);
		}
	} else {
		warn("No ifname in dpp uri section!\n");
		return -1;
	}

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

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

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

		if (band == 2)
			uri->band = BAND_2;
		else if (band == 5)
			uri->band = BAND_5;
		else if (band == 6)
			uri->band = BAND_6;
	} else
		goto out_error;

#if 0
	if (tb[DPP_TYPE]) {
		const char *type;

		type = tb[DPP_TYPE]->v.string;

		if (!strcmp(type, "qrcode"))
			uri->type = DPP_BOOTSTRAP_QR_CODE_WIFI;
		else if (!strcmp(type, "pkex"))
			uri->type = DPP_BOOTSTRAP_PKEX;
		else if (!strcmp(type, "nfc"))
			uri->type = DPP_BOOTSTRAP_NFC_URI;
	}
#endif

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

		if (c_int >= 0)
			uri->chirp_int = c_int;
	}

	uri->num_chan = 0;
	if (tb[DPP_CHAN]) {
		struct uci_element *xi;

		uci_foreach_element(&tb[DPP_CHAN]->v.list, xi) {
			if (uri->num_chan >= ARRAY_SIZE(uri->channel)) {
				warn("%s: at most 16 channels can be used for chirping\n", __func__);
				break;
			}
			uri->channel[uri->num_chan++] = atoi(xi->name);
		}
	}

	if (uri->num_chan == 0)
		goto out_error;

	return 0;
out_error:
	clean_dpp_uri(uri);
	return -1;
}
#endif

#if (EASYMESH_VERSION >= 6)
static int agent_config_get_mld(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		MLD_ENABLED,
		MLD_ID,
		MLD_SSID,
		MLD_KEY,
		MLD_SEC,
		MLD_VLAN,
		MLD_TYPE,
		MLD_BAND,
		MLD_IFNAME,
		NUM_MLD_ATTRS
	};
	const struct uci_parse_option opts[] = {
		[MLD_ENABLED] = { .name = "enabled", .type = UCI_TYPE_STRING },
		[MLD_ID] = { .name = "id", .type = UCI_TYPE_STRING },
		[MLD_SSID] = { .name = "ssid", .type = UCI_TYPE_STRING },
		[MLD_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
		[MLD_SEC] = { .name = "encryption", .type = UCI_TYPE_STRING },
		[MLD_VLAN] = { .name = "vid", .type = UCI_TYPE_STRING },
		[MLD_TYPE] = { .name = "type", .type = UCI_TYPE_STRING },
		[MLD_BAND] = { .name = "band", .type = UCI_TYPE_LIST },
		[MLD_IFNAME] = { .name = "ifname", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_MLD_ATTRS];
	struct mld_credential *mld;

	uci_parse_section(s, opts, NUM_MLD_ATTRS, tb);

	if (tb[MLD_ID]) {
		const char *val = tb[MLD_ID]->v.string;
		uint8_t id;

		id = atoi(val);

		mld = agent_get_mld_credential_by_id(a, id);
		if (!mld) {
			mld = agent_create_mld(a, id);
			if (!mld) {
				agnt_warn(LOG_MLO, "%s: OOM!\n", __func__);
				return -1;
			}
		} else
			a->num_mld++;
	} else {
		agnt_warn(LOG_MLO, "%s: MLD section missing ID!\n", __func__);
		return -1;
	}

	if (tb[MLD_IFNAME])
		strncpy(mld->ifname, tb[MLD_IFNAME]->v.string, sizeof(mld->ifname) - 1);

	if (tb[MLD_ENABLED]) {
		const char *val = tb[MLD_ENABLED]->v.string;

		mld->enabled = atoi(val);
	} else {
		mld->enabled = true;
	}

	if (tb[MLD_SSID])
		strncpy((char *) mld->ssid, tb[MLD_SSID]->v.string, 32);

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

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

	if (tb[MLD_KEY])
		strncpy((char *) mld->key, tb[MLD_KEY]->v.string, 64);

	if (tb[MLD_VLAN]) {
		mld->vlanid = (uint16_t) atoi(tb[MLD_VLAN]->v.string);
		if (!is_vid_valid(mld->vlanid)) {
			agnt_dbg(LOG_MLO, "%s: cred:%s vid:%d is not valid\n", __func__, mld->ssid, mld->vlanid);
			free(mld);
			return -1;
		}
	} else {
		/* if missing option vid in ap section use 1 as default */
		mld->vlanid = 1;
	}

	if (tb[MLD_TYPE]) {
		const char *type = tb[MLD_TYPE]->v.string;

		if (!strcmp(type, "backhaul"))
			mld->type = MLD_TYPE_BACKHAUL_BSS;
		else if (!strcmp(type, "fronthaul"))
			mld->type = MLD_TYPE_FRONTHAUL_BSS;
		else if (!strcmp(type, "combined"))
			mld->type = MLD_TYPE_COMBINED_BSS;
		else if (!strcmp(type, "station"))
			mld->type = MLD_TYPE_BACKHAUL_STA;
		else {
			list_del(&mld->list);
			free(mld);
			return -1;
		}
	}

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

		uci_foreach_element(&tb[MLD_BAND]->v.list, xi) {
			uint32_t band = BAND_UNKNOWN;

			band = (uint32_t)atoi(xi->name);
			mld->band |= int_to_band(band);
		}
	}

	return 0;
}

static int agent_config_get_wifi_device(struct agent_config *a,
		struct uci_section *s)
{
	enum {
		MLO,
		MLO_CAPABLE,
		NUM_DEVICE_ATTRS
	};
	const struct uci_parse_option opts[] = {
		[MLO] = { .name = "mlo", .type = UCI_TYPE_STRING },
		[MLO_CAPABLE] = { .name = "mlo_capable", .type = UCI_TYPE_STRING },
	};
	struct uci_option *tb[NUM_DEVICE_ATTRS];
	const char *device;
	struct agent_config_radio *rcfg;

	uci_parse_section(s, opts, NUM_DEVICE_ATTRS, tb);

	device = s->e.name;
	rcfg = get_agent_config_radio(a, device);
	if (!rcfg)
		return -1;

	rcfg->mlo_capable = 0;
	if (tb[MLO]) {
		const char *val = tb[MLO]->v.string;

		rcfg->mlo_capable = atoi(val);
	}

	if (tb[MLO_CAPABLE]) {
		const char *val = tb[MLO_CAPABLE]->v.string;

		rcfg->mlo_capable &= atoi(val);
	}
	return 0;

}
#endif

int agent_config_load(struct agent_config *cfg)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
	struct agent *a = container_of(cfg, struct agent, cfg);
	int i;

	for (i = 0; i < a->own_peer->e_req.num_bsta; i++) {
		if (a->own_peer->e_req.blist[i].chan_list) {
			free(a->own_peer->e_req.blist[i].chan_list);
			a->own_peer->e_req.blist[i].chan_list = NULL;
		}
	}

	a->own_peer->e_req.num_bsta = 0;
#endif
#if (EASYMESH_VERSION >= 6)
	clean_all_mlds(cfg);
	cfg->num_mld = 0;
#endif
#endif


	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, "dynamic_backhaul"))
			agent_config_get_dynamic_backhaul(cfg, s);
		else if (!strcmp(s->type, "radio"))
			agent_config_get_wifi_radio(cfg, s);
		else if (!strcmp(s->type, "ap"))
			agent_config_get_ap(cfg, s);
		else if (!strcmp(s->type, "bsta"))
			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);
#if (EASYMESH_VERSION > 2)
		else if (!strcmp(s->type, "dpp"))
			agent_config_get_dpp(cfg, s);
		else if (!strcmp(s->type, "dpp_chirp"))
			agent_config_get_dpp_chirp(cfg, s);
#endif
#if (EASYMESH_VERSION >= 6)
		else if (!strcmp(s->type, "mld"))
			agent_config_get_mld(cfg, s);
#endif
	}

	uci_unload(ctx, pkg);

#if (EASYMESH_VERSION >= 6)
	if (uci_load(ctx, "wireless", &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, "wifi-device"))
			agent_config_get_wifi_device(cfg, s);
	}

#endif

	uci_free_context(ctx);
	return 0;
}

int config_generate_radio(struct agent_config *cfg, struct uci_context *ctx,
			  const 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, "radio", NULL, "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);
	else if (band == BAND_6)
		set_value(ctx, pkg, s, "band", "6", 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,
		enum wifi_band 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, NULL, "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);
	else if (band == BAND_6)
		set_value(ctx, pkg, s, "band", "6", 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_generate_ap_agent(struct agent_config *cfg, struct uci_context *ctx,
		const char *device, const char *ifname,
		int multi_ap, enum wifi_band 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_AP_AGENT, NULL, "ifname",
			       ifname);
	if (!s) {
		uci_unload(ctx, pkg);
		return -1;
	}

	if (multi_ap == 2)
		set_value(ctx, pkg, s, "type", "fronthaul", UCI_TYPE_STRING);
	else if (multi_ap == 1)
		set_value(ctx, pkg, s, "type", "backhaul", UCI_TYPE_STRING);
	else if (multi_ap == 3)
		set_value(ctx, pkg, s, "type", "combined", UCI_TYPE_STRING);

	set_value(ctx, pkg, s, "device", device, UCI_TYPE_STRING);

	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);
	else if (band == BAND_6)
		set_value(ctx, pkg, s, "band", "6", 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,
		      const char *radio)
{
	struct uci_package *pkg;
	struct uci_section *s;
	bool found = false;
	int rv;

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

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

	uci_unload(ctx, pkg);
	return found;
}


int config_set_bsta(struct netif_bkcfg *bk, char *option, int enable)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	int ret = -1;

	/* disable mapagent bsta 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;

	if (enable)
		set_value(ctx, pkg, section, option, "1", UCI_TYPE_STRING);
	else
		set_value(ctx, pkg, section, option, "0", UCI_TYPE_STRING);
	uci_save(ctx, pkg);
	bk->enabled = !!enable;
	uci_commit(ctx, &pkg, false);

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

int config_disable_bsta(struct netif_bkcfg *bk)
{
	return config_set_bsta(bk, "enabled", 0);
}

int config_enable_bsta(struct netif_bkcfg *bk)
{
	return config_set_bsta(bk, "enabled", 1);
}

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


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

bool config_find_ap_agent(struct agent_config *cfg, struct uci_context *ctx,
				const char *ifname)
{
	struct uci_package *pkg;
	struct uci_element *e;
	bool found = false;
	int rv;

	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);
		const char *c_ifname;

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

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

		if (strncmp(ifname, c_ifname, 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;

	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		const 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_init_wsc_attributes(struct agent *a)
{
	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_IEEE1905, &pkg)) {
		uci_free_context(ctx);
		return -1;
	}

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

		if (!strcmp(s->type, "ieee1905")) {
			uint8_t default_dev_type[8] = { 0x00, 0x06, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01 }; /* default WPS oui */
			const char *manufacturer, *model_name, *device_name;
			const char *model_number, *serial_number, *device_type;

			manufacturer = uci_lookup_option_string(ctx, s, "manufacturer");
			if (manufacturer)
				strncpy(a->wscdev.manufacturer, manufacturer, 63);

			model_name = uci_lookup_option_string(ctx, s, "model_name");
			if (model_name)
				strncpy(a->wscdev.model_name, model_name, 32);

			device_name = uci_lookup_option_string(ctx, s, "device_name");
			if (device_name)
				strncpy(a->wscdev.device_name, device_name, 32);

			model_number = uci_lookup_option_string(ctx, s, "model_number");
			if (model_number)
				strncpy(a->wscdev.model_number, model_number, 32);

			serial_number = uci_lookup_option_string(ctx, s, "serial_number");
			if (serial_number)
				strncpy(a->wscdev.serial_number, serial_number, 32);

			memcpy(a->wscdev.device_type, default_dev_type, 8);
			device_type = uci_lookup_option_string(ctx, s, "device_type");
			if (device_type) {
				int ret;
				uint8_t oui[4] = {0};
				uint16_t category = 0, sub_category = 0;

				ret = sscanf(device_type, "%02hu-%02hhx%02hhx%02hhx%02hhx-%02hu",
					     &category,
					     &oui[0], &oui[1], &oui[2], &oui[3],
					     &sub_category);
				if (ret == 6) {
					buf_put_be16(&a->wscdev.device_type[0], category);
					memcpy(&a->wscdev.device_type[2], oui, 4);
					buf_put_be16(&a->wscdev.device_type[6], sub_category);
				}
			}
			break;
		}
	}

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

/* Iterate through 'wifi-device' sections in wireless config.
 * If corresponding '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 radio section.
 */
int agent_config_prepare(struct agent_config *cfg)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_element *e;
	struct blob_buf bb = {0};
	struct {
		struct {
			char device[16];
			enum wifi_band band;
		} dev_band[WIFI_DEVICE_MAX_NUM];
		int count;
	} band_map = {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;
		enum wifi_band band = BAND_UNKNOWN;

		if (!(strncmp(s->type, UCI_WL_DEVICE, strlen(UCI_WL_DEVICE)))) {
			const char *band_str;

			device = s->e.name;

			band_str = uci_lookup_option_string(ctx, s, "band");
			if (!band_str)
				continue;

			if (!strcmp(band_str, "2g")) {
				band_map.dev_band[band_map.count++].band = BAND_2;
				band = BAND_2;
			} else if (!strcmp(band_str, "5g")) {
				band_map.dev_band[band_map.count++].band = BAND_5;
				band = BAND_5;
			} else if (!strcmp(band_str, "6g")) {
				band_map.dev_band[band_map.count++].band = BAND_6;
				band = BAND_6;
			} else {
				dbg("%s: band:%s not supported, skip\n",
				    __func__, band_str);
				continue;
			}

#ifdef CONFIG_ALL_RADIOS
			if (!config_find_radio(cfg, ctx, device))
				config_generate_radio(cfg, ctx, device, band);
#endif

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

	/* radio sections need to be  */
	uci_foreach_element(&pkg->sections, e) {
		struct uci_section *s = uci_to_section(e);
		enum wifi_band band = BAND_UNKNOWN;
		int i = 0;

		const char *wifname = NULL, *wmode = NULL;
		const char *wdevice = NULL, *wmulti_ap = NULL;
		int multi_ap;

		if (!(strncmp(s->type, UCI_WLAN_IFACE, strlen(UCI_WLAN_IFACE)))) {
			wmode = uci_lookup_option_string(ctx, s, "mode");
			if (!wmode || strcmp(wmode, "ap"))
				continue;

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

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

			wmulti_ap = uci_lookup_option_string(ctx, s, "multi_ap");
			if (!wmulti_ap)
				continue;

			if (config_find_ap_agent(cfg, ctx, wifname))
				continue;

			multi_ap = atoi(wmulti_ap);

			for (i = 0; i < band_map.count; i++) {
				if (!(strncmp(band_map.dev_band[i].device, wdevice, 15))) {
					band = band_map.dev_band[i].band;
					break;
				}
			}

			dbg("%s: device = %s band = %d\n", __func__,
				wdevice, band);

			if (band == BAND_UNKNOWN)
				continue;

			config_generate_ap_agent(cfg, ctx, wdevice, wifname, multi_ap, band);
		}
	}

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

int agent_config_init(struct agent *a, struct agent_config *cfg)
{
	INIT_LIST_HEAD(&cfg->aplist);
	INIT_LIST_HEAD(&cfg->bklist);
#if (EASYMESH_VERSION > 2)
	INIT_LIST_HEAD(&cfg->chirplist);
#endif
	INIT_LIST_HEAD(&cfg->radiolist);
#if (EASYMESH_VERSION >= 6)
	INIT_LIST_HEAD(&cfg->mldlist);
#endif

#ifdef VENDOR_EXTENSION
	INIT_LIST_HEAD(&cfg->extlist);
#endif
	a->cfg.debug_level = LOGLEVEL_UNSET;

	strncpy(a->cfg.assoc_ctrl_file, ASSOC_CTRL_FILE,
		sizeof(a->cfg.assoc_ctrl_file) - 1);

	agent_config_prepare(cfg);
	agent_config_load(cfg);

	if (a->cfg.cscfg) {
		a->cntlr_select.local = a->cfg.cscfg->local;
		a->cntlr_select.probe_int = a->cfg.cscfg->probe_int;
		a->cntlr_select.retry_int = a->cfg.cscfg->retry_int;
#ifdef AGENT_DYNAMIC_CNTRL
		a->cntlr_select.autostart = a->cfg.cscfg->autostart;
#endif
		memcpy(a->cntlr_select.alid, a->cfg.cscfg->alid, 6);
	}

	if (!a->cfg.dbhcfg.bh_miss_tmo)
		a->cfg.dbhcfg.bh_miss_tmo = DEFAULT_BH_MISS_TMO;

	if (!a->cfg.dbhcfg.bh_reconf_tmo)
		a->cfg.dbhcfg.bh_reconf_tmo = DEFAULT_BH_RECONF_TMO;

	if (a->cfg.pcfg)
		a->pvid = a->cfg.pcfg->pvid;

	//memcpy(a->cntlr_almac, a->cfg.cntlr_almac, 6);

	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 = NULL, *tmp;

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

	return 0;
}

void clean_ap(struct netif_apcfg *p)
{
	list_del(&p->list);
	free(p);
}

int clean_all_ap(struct agent_config *cfg)
{
	struct netif_apcfg *p = NULL, *tmp;

	list_for_each_entry_safe(p, tmp, &cfg->aplist, list)
		clean_ap(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 = NULL, *tmp;

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

	return 0;
}

#if (EASYMESH_VERSION > 2)
int clean_dpp_uri(struct dpp_chirp *uri)
{
	list_del(&uri->list);
	free(uri);
	return 0;
}

int clean_all_dpp_uris(struct agent_config *cfg)
{
	struct dpp_chirp *p = NULL, *tmp;

	list_for_each_entry_safe(p, tmp, &cfg->chirplist, list)
		clean_dpp_uri(p);

	return 0;
}
#endif
#if (EASYMESH_VERSION >= 6)
void clean_mld_credential(struct mld_credential *mld)
{
	if (mld) {
		list_del(&mld->list);
		free(mld);
	}
}

int clean_all_mlds(struct agent_config *cfg)
{
	struct mld_credential *p = NULL, *tmp;

	if (list_empty(&cfg->mldlist))
		return 0;

	list_for_each_entry_safe(p, tmp, &cfg->mldlist, list) {
		clean_mld_credential(p);
	}

	return 0;
}

void uci_delete_mld_config(struct agent_config *cfg)
{
	struct mld_credential *mld;
	struct uci_context *ctx = NULL;
	struct uci_package *pkg_map, *pkg_wifi;

	pkg_map = uci_load_pkg(&ctx, UCI_AGENT);
	if (!pkg_map)
		return;

	if (uci_load(ctx, UCI_WIRELESS, &pkg_wifi) != UCI_OK) {
		uci_unload(ctx, pkg_map);
		uci_free_context(ctx);
		return;
	}

	list_for_each_entry(mld, &cfg->mldlist, list) {
		struct uci_element *e;
		struct uci_section *s;
		struct uci_ptr ptr = {0};
		char mldname[16] = {0};
		char idstr[4] = {0};

		snprintf(idstr, sizeof(idstr), "%d", mld->id);
		s = config_get_section(ctx, pkg_map, "mld", "id", idstr);
		if (s) {
			ptr = (struct uci_ptr){ .p = pkg_map, .s = s };
			uci_delete(ctx, &ptr);
		}

		snprintf(mldname, sizeof(mldname), "mld%d", mld->id);
		uci_foreach_element(&pkg_wifi->sections, e) {
			struct uci_section *ws = uci_to_section(e);

			if (!strcmp(ws->type, "wifi-mld") &&
			    !strcmp(ws->e.name, mldname)) {
				ptr = (struct uci_ptr){ .p = pkg_wifi, .s = ws };
				uci_delete(ctx, &ptr);
				break;
			}
		}
	}

	uci_commit(ctx, &pkg_map, false);
	uci_commit(ctx, &pkg_wifi, false);
	uci_unload(ctx, pkg_map);
	uci_unload(ctx, pkg_wifi);
	uci_free_context(ctx);
}


#endif

int agent_config_clean(struct agent_config *cfg)
{
#ifdef VENDOR_EXTENSION
	list_flush(&cfg->extlist, struct agent_extension_config, list);
#endif
#if (EASYMESH_VERSION > 2)
	clean_all_dpp_uris(cfg);
#endif
#if (EASYMESH_VERSION >= 6)
	clean_all_mlds(cfg);
#endif
	clean_all_ap(cfg);
	clean_all_bk(cfg);
	clean_all_radios(cfg);
	if (cfg->pcfg) {
		clean_steer_btm_excl(cfg->pcfg);
		clean_steer_excl(cfg->pcfg);
		free(cfg->pcfg);
	}
	if (cfg->cscfg)
		free(cfg->cscfg);

	return 0;
}

/* config_calc_name - depending on the netdev/ifprefix option in UCI
 * wl : wl0, wl0.1, wl0.2
 * wl%. : wl0, wl0.1, wl0.2
 * wlan%_ : wlan0, wlan0_1, wlan0_2
 * wlan%_% : wlan0_0, wlan0_1, wlan0_2
 *
 * ifprefix could be specified per radio, for example:
 * ra% : ra0, ra1, ra2 (2.4g)
 * rai% : rai0, rai1, rai2 (5g)
 * rax% : rax0, rax1, rax2 (6g)
 */
int config_calc_ifname(struct agent_config *cfg,
		char *device, uint8_t index, char *ifname)
{
	char fmt[IFNAMSIZ] = { 0 };
	char *posX, *posS, *posY;
	uint8_t dev_num;
	struct agent_config_radio *rcfg;
	char *ifprefix;
	bool per_radio;

	dev_num = get_device_num_from_name(device);

	rcfg = get_agent_config_radio(cfg, device);
	if (!rcfg) {
		dbg("%s: radio config not found for %s\n", __func__, device);
		return -1;
	}

	per_radio = (rcfg->ifprefix[0] != '\0');
	if (per_radio)
		ifprefix = rcfg->ifprefix;
	else
		ifprefix = cfg->netdev;

	posX = strstr(ifprefix, "%");

	if (posX) {
		strncpy(fmt, ifprefix, labs(ifprefix - posX));
		strncat(fmt, "%hhu", sizeof(fmt) - strlen(fmt) - 1);
		if (per_radio) {
			snprintf(ifname, sizeof(fmt), fmt, index); /* Flawfinder: ignore */
			return 0;
		}
	} else {
		/* legacy: option netdev 'wl' (or 'wlan') */
		strncpy(fmt, ifprefix, IFNAMSIZ - 1);
		snprintf(ifname, IFNAMSIZ, "%s", ifprefix);
		snprintf(ifname + strlen(ifname), IFNAMSIZ, "%hhu", dev_num);
		if (index > 0) {
			snprintf(ifname + strlen(ifname), IFNAMSIZ, "%s",
					cfg->brcm_setup ? "." : "_");
			snprintf(ifname + strlen(ifname), IFNAMSIZ, "%hhu", index);
		}
		return 0;
	}

	posS = get_separator(ifprefix);

	posY = strchr((posX + 1), '%');
	if (posY || index != 0) {
		if (sizeof(fmt) > strlen(fmt) + 1 && posS)
			/* add separator, e.g: '.' or '_' */
			strncat(fmt, posS, 1);
		strncat(fmt, "%hhu", sizeof(fmt) - strlen(fmt) - 1);
		snprintf(ifname, sizeof(fmt), fmt, dev_num, index); /* Flawfinder: ignore */
	} else {
		snprintf(ifname, sizeof(fmt), fmt, dev_num); /* Flawfinder: ignore */
	}

	return 0;
}

int del_value_list(struct uci_context *ctx, struct uci_package *pkg,
		struct uci_section *s, const char *option,
		enum uci_option_type type)
{

	//struct uci_element *x, *x1, *tmp, *tmp1;
	//struct uci_option *op;
	struct uci_ptr ptr = {0};

	trace("Inside %s\n", __func__);

	ptr.p = pkg;
	ptr.s = s;
	ptr.option = option;
	uci_delete(ctx, &ptr);

	return 0;
}

int wifi_set_opclass_preference(char *radio_name, uint32_t opclass_id,
	uint32_t preference, uint8_t *channel_list, int channel_num)
{
	struct uci_section *s;
	struct uci_package *pkg;
	int ret = 0, i = 0;
	char pref_str[20];
	char opclassid_str[20];
	char channel_str[200];
	struct uci_context *ctx;

	agnt_trace(LOG_CHANNEL, "Inside %s\n", __func__);

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

	if (uci_load(ctx, UCI_AGENT, &pkg) != UCI_OK) {
		err("config file 'mapagent' not found!\n");
		ret = -1;
		goto out;
	}

	snprintf(opclassid_str, 20, "%u", opclass_id);
	agnt_trace(LOG_CHANNEL, "%s: opclass [ %s] channel no. [%d]\n", __func__,
		opclassid_str, channel_num);
	s = config_add_section(ctx, pkg, UCI_AGENT, "opclass", NULL, "opclassid",
			       opclassid_str);
	if (!s) {
		ret = -1;
		goto out;
	}
	memset(channel_str, 0, sizeof(channel_str));
	snprintf(pref_str, 20, "%u", preference);

	set_value(ctx, pkg, s, "preference", pref_str, UCI_TYPE_STRING);
	set_value(ctx, pkg, s, "radio", radio_name, UCI_TYPE_STRING);
	del_value_list(ctx, pkg, s, "channel", UCI_TYPE_LIST);

	for (i = 0; i < channel_num; i++) {
		char chan_str[8] = {0};

		snprintf(chan_str, 8, "%u", channel_list[i]);
		set_value(ctx, pkg, s, "channel", chan_str, UCI_TYPE_LIST);
	}

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

struct uci_section *config_get_name_section(struct uci_context *ctx,
	struct uci_package *pkg, const char *type, const char *value)
{
	struct uci_element *e;
	struct uci_section *section;
	int ret = 0;

	trace("Inside %s\n", __func__);

	/* get the wet iface section */
	uci_foreach_element(&pkg->sections, e) {
		section = uci_to_section(e);
		if (strcmp(section->type, type))
			continue;

		ret = strcmp(section->e.name, value);
		dbg("Inside %s: section name %s value %s \n", __func__,
			section->e.name, value);
		if (ret == 0)
			return section;
	}
	return NULL;
}

int wifi_set_transmit_power(char *ifname, uint32_t tx_power)
{
	struct uci_context *ctx = NULL;
	struct uci_package *pkg;
	struct uci_section *section;
	char txpower_str[18] = {0};
	int ret = -1;

	trace("Inside %s\n", __func__);
	pkg = uci_load_pkg(&ctx, UCI_WIRELESS);
	if (!pkg)
		return ret;

	section = config_get_name_section(ctx, pkg, UCI_DEVICE, ifname);
	if (!section)
		goto out_pkg;

	if (tx_power)
		snprintf(txpower_str, 18, "%u", tx_power);

	dbg("%s: setting tx_power to %s\n",  __func__, txpower_str);

	ret = set_value(ctx, pkg, section, "txpower", txpower_str, UCI_TYPE_STRING);

	uci_commit(ctx, &pkg, false);

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

static int agent_config_get_opclass(struct  wifi_radio_element *radio,
		struct uci_section *s)
{
	enum {
		OP_CLASSID,
		OP_PREF,
		OP_RADIO,
		OP_CHANNEL,
		NUM_POLICIES
	};

	const struct uci_parse_option opts[] = {
		[OP_CLASSID] = { .name = "opclassid", .type = UCI_TYPE_STRING },
		[OP_PREF] = { .name = "preference", .type = UCI_TYPE_STRING },
		[OP_RADIO] = { .name = "radio", .type = UCI_TYPE_STRING },
		[OP_CHANNEL] = { .name = "channel", .type = UCI_TYPE_LIST },
	};

	struct uci_option *tb[NUM_POLICIES];
	struct wifi_radio_opclass_entry entry = {};
	struct wifi_radio_opclass_channel chan = {};
	int opclassid = 0;
	int pref = 0;
	int ret = 0;

	trace("Inside %s\n", __func__);

	uci_parse_section(s, opts, NUM_POLICIES, tb);

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

		ifname = tb[OP_RADIO]->v.string;
		trace("radio name [%s] ifname [%s]\n", radio->name, ifname);
		ret = strcmp(radio->name, ifname);
		if (ret != 0)
			return 0;
	}

	if (tb[OP_CLASSID])
		opclassid = atoi(tb[OP_CLASSID]->v.string);

	if (tb[OP_PREF])
		pref = atoi(tb[OP_PREF]->v.string);

	entry.id = opclassid;

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

		dbg("Channel: param: classid %d pref %d\n", opclassid, pref);
		uci_foreach_element(&tb[OP_CHANNEL]->v.list, xi) {
			uint32_t channel = 0;

			channel = atoi(xi->name);
			chan.channel = channel;
			chan.preference = pref;

			wifi_opclass_add_channel(&entry, &chan);
		}
	}

	wifi_opclass_add_entry(&radio->req_opclass, &entry);
	return 0;
}

int agent_config_opclass(struct  wifi_radio_element *radio)
{
	struct uci_context *ctx;
	struct uci_package *pkg;
	struct uci_element *e;

	trace("Inside %s\n", __func__);

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

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

	memcpy(&radio->req_opclass, &radio->opclass, sizeof(radio->opclass));
	wifi_opclass_set_preferences(&radio->req_opclass, 0x0);

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

		if (!strcmp(s->type, "opclass"))
			agent_config_get_opclass(radio, s);
	}

	uci_free_context(ctx);
	return 0;
}

void uci_apply_bss_association_mode(const char *interface_name, bool disallow_assoc)
{
	bool ret = uci_check_wifi_iface(UCI_AGENT, interface_name, UCI_AP_AGENT);

	if (!ret) {
		ret = uci_add_wireless_iface_sec(UCI_AGENT, interface_name,
						 UCI_AP_AGENT, NULL);
		if (!ret)
			return;
	}

	uci_set_wireless_interface_option(UCI_AGENT, UCI_AP_AGENT, "ifname",
					  interface_name, "disallow_assoc",
					  disallow_assoc ? "1" : "0");
}


#if (EASYMESH_VERSION >= 6)

#endif
