diff --git a/src/Makefile b/src/Makefile
index 5cb37d52ce6aa9143c5a9b18baf5dfac471d1ef6..623ad30c4257c78b064a48fd8587f47a25019482 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -20,8 +20,8 @@ CNTLR_OBJS = \
 	core/cntlr_ubus.o \
 	core/cntlr.o \
 	core/cntlr_map.o \
+	core/config.o \
 	core/main.o
-	#core/config.o \
 
 LIBS = -lubus -lubox -ljson-c -lblobmsg_json -luci -pthread
 LIBS += -rdynamic -ldl
diff --git a/src/controller.conf b/src/controller.conf
index f46ae102d9d97600b8f6a6152c29c36be181f931..c7fe7b4e36c1f6cb62a42533e86fc5537ba170d9 100644
--- a/src/controller.conf
+++ b/src/controller.conf
@@ -1,6 +1,6 @@
 config wificntlr
 	option enabled '1'
-	list registrar '5 2'           #bands on which wps registrar supported
+	option registrar '5 2'	    #bands on which wps registrar supported
 
 config fh-credentials
 	option band '5'
diff --git a/src/core/config.c b/src/core/config.c
index bbcbf746141a57a48b28b79d71b7f6cd19123380..1ce3210024f2bcb3c430e60313bebb634c09a18f 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -9,7 +9,11 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
@@ -26,792 +30,315 @@
 #include "debug.h"
 #include "utils.h"
 #include "config.h"
-//#include "steer_rules.h"
 #include "comm.h"
 #include "msgqueue.h"
 #include "worker.h"
 #include "cntlr.h"
+#include "config.h"
 
-//TODO: remove
-struct stax{
-	char macstring[32];	/* ':' separated mac address string */
-	struct list_head list;
-};
-////////////
-
-static struct netif_fhcfg *get_netif_fhcfg_by_name(struct cntlr_config *c,
-							const char *name)
+void cntlr_config_dump(struct controller_config *c)
 {
-	struct netif_fhcfg *p;
-
-	list_for_each_entry(p, &c->fhlist, list) {
-		if (!strcmp(name, p->name))
-			return p;
+	int i;
+
+	dbg("Controller config ---------\n");
+	dbg("Enabled: %d\n", c->enabled);
+	dbg("Registrar @5Ghz: %d\n", c->has_registrar_5g);
+	dbg("Registrar @2Ghz: %d\n", c->has_registrar_2g);
+
+	dbg("Credentials: Fronthaul\n");
+	for (i = 0; i < c->num_fh; i++) {
+		dbg("  Band    : %d\n", c->fh[i].band);
+		dbg("  Security: 0x%x\n", c->fh[i].sec);
+		dbg("  Key     : \n");
+		dbg("  ssid    : %s\n", c->fh[i].ssid);
+		dbg("  vlan    : %d\n\n", c->fh[i].vlanid);
 	}
 
-	return NULL;
-}
-
-static struct steer_policy *get_steer_policy_by_name(struct netif_fhcfg *c,
-							const char *name)
-{
-	struct steer_policy *p;
-
-	if (!c)
-		return NULL;
-
-	list_for_each_entry(p, &c->steer_policylist, list) {
-		if (!strcmp(name, p->name))
-			return p;
+	dbg("Credentials: Backhaul\n");
+	for (i = 0; i < c->num_bk; i++) {
+		dbg("  Band    : %d\n", c->bk[i].band);
+		dbg("  Security: 0x%x\n", c->bk[i].sec);
+		dbg("  Key     : \n");
+		dbg("  ssid    : %s\n", c->bk[i].ssid);
+		dbg("  vlan    : %d\n\n", c->bk[i].vlanid);
 	}
 
-	return NULL;
+	dbg("Agents policy: Default\n");
+	dbg("  Id                    : " MACFMT "\n", MAC2STR(c->apolicy.id));
+	dbg("  Steer-policy          : %d\n", c->apolicy.policy);
+	dbg("  Util-threshold        : %d\n", c->apolicy.util_threshold);
+	dbg("  RCPI-threshold        : %d\n", c->apolicy.rcpi_threshold);
+	dbg("  Report scan           : %d\n", c->apolicy.report_scan);
+	dbg("  Report assocfails     : %d\n", c->apolicy.report_sta_assocfails);
+	dbg("  Report metric         : %d\n", c->apolicy.report_metric_periodic);
+	dbg("  Report RCPI-thresh    : %d\n", c->apolicy.report_rcpi_threshold);
+	dbg("  Report Util-thresh    : %d\n", c->apolicy.report_util_threshold);
+	dbg("  Include STA stats     : %d\n", c->apolicy.include_sta_stats);
+	dbg("  Include STA metric    : %d\n", c->apolicy.include_sta_metric);
+	dbg("  Primary VLAN ID       : %d\n", c->apolicy.pvid);
+	dbg("  PCP Default           : %d\n", c->apolicy.pcp_default);
+	dbg("  Disallow bSTA P1      : %d\n", c->apolicy.disallow_bsta_p1);
+	dbg("  Disallow bSTA P2      : %d\n", c->apolicy.disallow_bsta_p2);
+
+	dbg("---------------------------\n");
 }
 
-void stax_add_entry(struct list_head *h, char *sta_macstr)
+int cntlr_config_defaults(struct controller *cntlr, struct controller_config *c)
 {
-	struct stax *n;
+	memset(c, 0, sizeof(*c));
+	INIT_LIST_HEAD(&c->policylist);
 
-	n = calloc(1, sizeof(struct stax));
-	if (n) {
-		snprintf(n->macstring, 18, "%s", sta_macstr);
-		list_add(&n->list, h);
-	}
+	return 0;
 }
 
-void stax_del_entry(struct list_head *h, char *sta_macstr)
+static int cntlr_config_get_base(struct controller_config *c,
+						struct uci_section *s)
 {
-	struct stax *s, *tmp;
+	enum {
+		CNTLR_ENABLED,
+		CNTLR_REGISTRAR,
+		NUM_CNTLR_ATTRS,
+	};
+	const struct uci_parse_option opts[] = {
+		{ .name = "enabled", .type = UCI_TYPE_STRING },
+		{ .name = "registrar", .type = UCI_TYPE_STRING },
+	};
+	struct uci_option *tb[NUM_CNTLR_ATTRS];
 
-	list_for_each_entry_safe(s, tmp, h, list) {
-		if (!strncmp(s->macstring, sta_macstr, sizeof(s->macstring))) {
-			list_del(&s->list);
-			free(s);
-			return;
-		}
-	}
-}
 
-void cntlr_config_dump(struct cntlr_config *cfg)
-{
-	struct netif_fhcfg *n;
-	struct steer_policy *pol;
-	struct stax *x;
-
-	if (!cfg)
-		return;
-
-	list_for_each_entry(n, &cfg->fhlist, list) {
-		dbg("name: %s\n", n->name);
-		dbg("  enabled  : %s\n", n->enabled ? "true" : "false");
-		dbg("  assocctrl: %s\n", n->assoc_control ? "true" : "false");
-
-		dbg("  Policies -------\n");
-		list_for_each_entry(pol, &n->steer_policylist, list) {
-			dbg("    name: %s\n", pol->name);
-			dbg("    enabled  : %s\n", pol->enabled ? "true" : "false");
-			/* if (pol->dump_config)
-				pol->dump_config(pol, pol->policy); */
-		}
+	uci_parse_section(s, opts, NUM_CNTLR_ATTRS, tb);
 
-		dbg("  Steer Exclude Lists -------\n");
-		list_for_each_entry(x, &n->steer_excludelist, list) {
-			dbg("    mac: %s\n", x->macstring);
-		}
+	if (tb[CNTLR_ENABLED]) {
+		const char *val = tb[CNTLR_ENABLED]->v.string;
 
-		dbg("  Steer BTM Exclude Lists -------\n");
-		list_for_each_entry(x, &n->steer_btm_excludelist, list) {
-			dbg("    mac: %s\n", x->macstring);
-		}
-
-		dbg("  Assoc Ctrl Lists -------\n");
-		list_for_each_entry(x, &n->assoc_ctrllist, list) {
-			dbg("    mac: %s\n", x->macstring);
-		}
+		c->enabled = atoi(val) == 1 ? true : false;
 	}
-}
 
-int cntlr_config_defaults(struct cntlr *a, struct cntlr_config *cfg)
-{
-	struct list_head *p, *tmp;
-	struct netif_fh *ifptr;
-	struct netif_fhcfg *new;
-
-	if (!cfg)
-		return -1;
+	if (tb[CNTLR_REGISTRAR]) {
+		const char *val = tb[CNTLR_REGISTRAR]->v.string;
 
-	cfg->enabled = false;
-	cfg->runfreq = AGENT_RUN_AUTO;
-
-	INIT_LIST_HEAD(&cfg->fhlist);
-	INIT_LIST_HEAD(&cfg->bklist);
-	list_for_each_entry(ifptr, &a->fhlist, list) {
-		/* struct list_head pollist; */
-		struct steer_rule *r;
-
-		new = calloc(1, sizeof(struct netif_fhcfg));
-		if (!new) {
-			warn("OOM! config\n");
-			goto err_alloc;
-		}
-
-		snprintf(new->name, 16, "%s", ifptr->name);
-		new->enabled = false;
-		new->steer_btm_retry = STEER_BTM_RETRY;
-		new->steer_btm_retry_secs = STEER_BTM_RETRY_INT;
-		new->fallback_legacy = STEER_LEGACY_FALLBACK_INT;
-		new->steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
-		new->steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
-		new->assoc_control_time = ASSOC_CONTROL_INT;
-		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_alloc_policy;
-
-			snprintf(pol->name, 16, "%s", r->name);
-			pol->enabled = false;
-			if (r->init_config)
-				r->init_config(r, &pol->policy);
-			list_add(&pol->list, &new->steer_policylist);
-		}
-
-		INIT_LIST_HEAD(&new->steer_excludelist);
-		INIT_LIST_HEAD(&new->steer_btm_excludelist);
-		INIT_LIST_HEAD(&new->assoc_ctrllist);
-
-		ifptr->cfg = new;
-		dbg("%s: %s netif_fh->cfg = %p\n", __func__, ifptr->name, ifptr->cfg);
-		list_add(&new->list, &cfg->fhlist);
+		c->has_registrar_5g = !strstr(val, "5") ? false : true;
+		c->has_registrar_2g = !strstr(val, "2") ? false : true;
 	}
 
 	return 0;
-
-err_alloc_policy:
-	/* TODO */
-err_alloc:
-	list_for_each_safe(p, tmp, &cfg->fhlist) {
-		list_del(p);
-		free(p);
-	}
-
-	return -1;
 }
 
-/* create fh-iface config and initialize with default values */
-struct netif_fhcfg *create_fronthaul_iface_config(struct cntlr_config *cfg,
-							const char *ifname)
+static int cntlr_config_get_credentials(struct controller_config *c,
+						struct uci_section *s)
 {
-	struct netif_fhcfg *new;
-	struct steer_rule *r;
-
-	if (!cfg)
-		return NULL;
+	enum {
+		CRED_BAND,
+		CRED_SSID,
+		CRED_SEC,
+		CRED_KEY,
+		CRED_VLAN,
+		NUM_CREDS,
+	};
+	const struct uci_parse_option opts[] = {
+		[CRED_BAND] = { .name = "band", .type = UCI_TYPE_STRING },
+		[CRED_SSID] = { .name = "ssid", .type = UCI_TYPE_STRING },
+		[CRED_SEC] = { .name = "encryption", .type = UCI_TYPE_STRING },
+		[CRED_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
+		[CRED_VLAN] = { .name = "vlan", .type = UCI_TYPE_STRING },
+	};
+	struct uci_option *tb[NUM_CREDS];
+	struct iface_credential *cred;
+
+
+	if (!strcmp(s->type, "fh-credentials")) {
+		if (c->num_fh >= 2)
+			return -1;
+
+		cred = &c->fh[c->num_fh++];
+	} else {
+		if (c->num_bk >= 2)
+			return -1;
 
-	new = calloc(1, sizeof(struct netif_fhcfg));
-	if (!new) {
-		warn("OOM! config\n");
-		return NULL;
+		cred = &c->bk[c->num_bk++];
 	}
 
-	snprintf(new->name, 16, "%s", ifname);
-	new->enabled = true;
-	new->fallback_legacy = STEER_LEGACY_FALLBACK_INT;
-	new->steer_btm_retry_secs = STEER_BTM_RETRY_INT;
-	new->steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
-	new->steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
-	new->assoc_control_time = ASSOC_CONTROL_INT;
-	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;
-		}
+	uci_parse_section(s, opts, NUM_CREDS, tb);
 
-		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);
+	if (tb[CRED_BAND]) {
+		if (atoi(tb[CRED_BAND]->v.string) == 5)
+			cred->band = WIFI_BAND_5;
+		else if (atoi(tb[CRED_BAND]->v.string) == 2)
+			cred->band = WIFI_BAND_2;
 	}
 
-	INIT_LIST_HEAD(&new->steer_excludelist);
-	INIT_LIST_HEAD(&new->steer_btm_excludelist);
-	INIT_LIST_HEAD(&new->assoc_ctrllist);
+	if (tb[CRED_SSID])
+		memcpy(cred->ssid, tb[CRED_SSID]->v.string, 32);
 
-	/* f->cfg = new; */
-	dbg("%s: %s netif_fh->cfg = %p\n", __func__, new->name, new);
+	if (tb[CRED_SEC]) {
+		const char *sec = tb[CRED_SEC]->v.string;
+
+		if (!strncmp(sec, "psk3", 4))
+			cred->sec = WIFI_SECURITY_WPA3PSK;
+		else if (!strncmp(sec, "psk2", 4))
+			cred->sec = WIFI_SECURITY_WPA2PSK;
+		else if (!strncmp(sec, "psk", 3))
+			cred->sec = WIFI_SECURITY_WPAPSK;
+
+		//TODO: ciphers (if any)
+	}
 
-	list_add(&new->list, &cfg->fhlist);
+	if (tb[CRED_KEY])
+		memcpy(cred->key, tb[CRED_KEY]->v.string, 63);
 
-	return new;
+	if (tb[CRED_VLAN])
+		cred->vlanid = atoi(tb[CRED_VLAN]->v.string);
 
-err_oom:
-	list_flush(&new->steer_policylist, struct steer_policy, list);
-	free(new);
-	return NULL;
+	return 0;
 }
 
-int cntlr_config_reload(const char *confname, struct cntlr_config *cfg)
+static int cntlr_config_get_agent_policy(struct controller_config *c,
+						struct uci_section *s)
 {
-	struct uci_context *ctx = NULL;
-	struct uci_package *pkg = NULL;
-	struct uci_element *e;
-
-	ctx = uci_alloc_context();
-	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
-		err("config file '%s' not found!\n", confname);
-		uci_free_context(ctx);
-		return -1;
+	enum {
+		POL_ID,
+		POL_STEER,
+		POL_UTIL_TH,
+		POL_RCPI_TH,
+		POL_RPT_SCAN,
+		POL_RPT_ASSOC_FAILS,
+		POL_RPT_METRIC_PERIODIC,
+		POL_RPT_RCPI_TH,
+		POL_RPT_UTIL_TH,
+		POL_INC_STA_STATS,
+		POL_INC_STA_METRIC,
+		POL_PVID,
+		POL_PCP_DEFAULT,
+		POL_DISALLOW_BSTA_P1,
+		POL_DISALLOW_BSTA_P2,
+		NUM_POLICIES,
+	};
+	const struct uci_parse_option opts[] = {
+		{ .name = "id", .type = UCI_TYPE_STRING },
+		{ .name = "steer_policy", .type = UCI_TYPE_STRING },
+		{ .name = "util_threshold", .type = UCI_TYPE_STRING },
+		{ .name = "rcpi_threshold", .type = UCI_TYPE_STRING },
+		{ .name = "report_scan", .type = UCI_TYPE_STRING },
+		{ .name = "report_sta_assocfails", .type = UCI_TYPE_STRING },
+		{ .name = "report_metric_periodic", .type = UCI_TYPE_STRING },
+		{ .name = "report_rcpi_threshold", .type = UCI_TYPE_STRING },
+		{ .name = "report_util_threshold", .type = UCI_TYPE_STRING },
+		{ .name = "include_sta_stats", .type = UCI_TYPE_STRING },
+		{ .name = "include_sta_metric", .type = UCI_TYPE_STRING },
+		{ .name = "pvid", .type = UCI_TYPE_STRING },
+		{ .name = "pcp_default", .type = UCI_TYPE_STRING },
+		{ .name = "disallow_bsta_p1", .type = UCI_TYPE_STRING },
+		{ .name = "disallow_bsta_p2", .type = UCI_TYPE_STRING },
+	};
+	struct uci_option *tb[NUM_POLICIES];
+
+	uci_parse_section(s, opts, NUM_POLICIES, tb);
+
+
+	if (tb[POL_ID]) {
+		const char *val = tb[POL_ID]->v.string;
+
+		hwaddr_aton(val, c->apolicy.id);
 	}
 
-	uci_foreach_element(&pkg->sections, e) {
-		struct uci_section *s = uci_to_section(e);
+	if (tb[POL_STEER])
+		c->apolicy.policy = atoi(tb[POL_STEER]->v.string);
 
-		if (!strcmp(s->type, "wificntlr")) {
-			struct uci_element *x;
-			struct uci_option *op;
-			bool enabled = false;
-
-			uci_foreach_element(&s->options, x) {
-				op = uci_to_option(x);
-				if (!strncmp(x->name, "enabled", 7) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("wificntlr: enabled = %d\n", atoi(op->v.string));
-					enabled = atoi(op->v.string) == 1 ?
-							true : false;
-					cfg->enabled = enabled;
-				} else if (!strncmp(x->name, "runfreq", 7) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("wificntlr: speed = %s\n", op->v.string);
-					if (!strncasecmp(op->v.string, "auto", 4))
-						cfg->runfreq = AGENT_RUN_AUTO;
-					else if (!strncasecmp(op->v.string, "low", 3))
-						cfg->runfreq = AGENT_RUN_LOW;
-					else if (!strncasecmp(op->v.string, "high", 4))
-						cfg->runfreq = AGENT_RUN_HIGH;
-					else if (!strncasecmp(op->v.string, "off", 3))
-						cfg->runfreq = AGENT_RUN_OFF;
-				} /* else if (!strncmp(x->name, "ifname", 6) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("wificntlr: ifname: ");
-					uci_foreach_element(&op->v.list, xi) {
-						dbg("%s ", xi->name);
-					}
-					dbg("\n");
-				} */
-			}
-		} else if (!strcmp(s->type, "fh-iface")) {
-			struct uci_element *x;
-			struct uci_option *op;
-			bool disabled = false;
-			unsigned int steer_btm_retry = STEER_BTM_RETRY;
-			unsigned int steer_btm_retry_secs = STEER_BTM_RETRY_INT;
-			unsigned int legacy = STEER_LEGACY_FALLBACK_INT;
-			unsigned int steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
-			unsigned int steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
-			unsigned int assoc_ctrl_secs = ASSOC_CONTROL_INT;
-			struct netif_fhcfg *ifcfg = NULL;
-
-			uci_foreach_element(&s->options, x) {
-				op = uci_to_option(x);
-				if (!strncmp(x->name, "disabled", 8) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("wificntlr: disabled = %d\n", atoi(op->v.string));
-					disabled = atoi(op->v.string) == 1 ?
-								true : false;
-				} else if (!strncmp(x->name, "ifname", 6) &&
-						op->type == UCI_TYPE_STRING) {
-					/* struct uci_element *xi; */
-
-					ifcfg = get_netif_fhcfg_by_name(cfg, op->v.string);
-					if (!ifcfg) {
-						ifcfg = create_fronthaul_iface_config(cfg, op->v.string);
-						if (!ifcfg) {
-							warn("%s: OOM!\n", __func__);
-							break;
-						}
-					} else {
-						warn("Duplicate 'fh-iface %s' config!! ignore\n",
-								op->v.string);
-					}
-
-					list_flush(&ifcfg->steer_excludelist, struct stax, list);
-					list_flush(&ifcfg->steer_btm_excludelist, struct stax, list);
-					list_flush(&ifcfg->assoc_ctrllist, struct stax, list);
-
-				} else if (!strncmp(x->name, "btm_retry", 9) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: btm_retry = %d\n",
-						atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						steer_btm_retry = atoi(op->v.string);
-				} else if (!strncmp(x->name, "btm_retry_secs", 14) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: btm_retry_secs = %d\n",
-						atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						steer_btm_retry_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "fallback_legacy", 15) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: legacy = %d\n",
-							atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						legacy = atoi(op->v.string);
-				} else if (!strncmp(x->name, "steer_legacy_reassoc_secs", 25) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: steer_legacy_rassoc_secs = %d\n",
-						atoi(op->v.string));
-					steer_legacy_reassoc_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "steer_legacy_retry_secs", 23) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: steer_legacy_retry_secs = %d\n",
-						atoi(op->v.string));
-					steer_legacy_retry_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "assoc_ctrl", 10) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: assoc_ctlr = %d\n",
-							atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						assoc_ctrl_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "steer", 5) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: param: ");
-					uci_foreach_element(&op->v.list, xi) {
-						struct steer_policy *p = NULL;
-						struct steer_rule *r;
-
-						dbg("%s ", xi->name);
-						p = get_steer_policy_by_name(ifcfg, xi->name);
-						if (!p) {
-							/* TODO? */
-							dbg("TODO!! steer before ifname\n");
-							continue;
-						}
-						p->enabled = true;
-						r = get_steer_rule_by_name(xi->name);
-						if (r)
-							r->enabled = true;
-					}
-					dbg("\n");
-				} else if (!strncmp(x->name, "exclude_btm", 11) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: exclude_btm: ");
-					uci_foreach_element(&op->v.list, xi) {
-						dbg("%s ", xi->name);
-						stax_add_entry(&ifcfg->steer_btm_excludelist, xi->name);
-					}
-					dbg("\n");
-				} else if (!strncmp(x->name, "exclude", 7) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: exclude: ");
-					uci_foreach_element(&op->v.list, xi) {
-						dbg("%s ", xi->name);
-						stax_add_entry(&ifcfg->steer_excludelist, xi->name);
-					}
-					dbg("\n");
-				}
-			}
-			if (ifcfg) {
-				ifcfg->enabled = !disabled;
-				ifcfg->steer_btm_retry = steer_btm_retry;
-				ifcfg->steer_btm_retry_secs = steer_btm_retry_secs;
-				if (legacy) {
-					/* wait atleast these many secs before
-					 * falling to legacy steering.
-					 */
-					ifcfg->fallback_legacy = 1 +
-						steer_btm_retry * steer_btm_retry_secs +
-						legacy;
-				}
-				ifcfg->steer_legacy_reassoc_secs = steer_legacy_reassoc_secs;
-				ifcfg->steer_legacy_retry_secs = steer_legacy_retry_secs;
-				ifcfg->assoc_control_time = assoc_ctrl_secs;
-			}
-		}
-#if 0
-		else if (!strcmp(s->type, "steer")) {
-			struct uci_element *x, *tmp;
-			struct uci_option *op;
-			bool enabled = false;
-			unsigned int legacy = STEER_LEGACY_FALLBACK_INT;
-			unsigned int steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
-			unsigned int steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
-			unsigned int steer_btm_retry = STEER_BTM_RETRY;
-			unsigned int steer_btm_retry_secs = STEER_BTM_RETRY_INT;
-			struct netif_fhcfg *ifcfg = NULL;
-
-			uci_foreach_element_safe(&s->options, tmp, x) {
-				op = uci_to_option(x);
-				if (!strncmp(x->name, "enabled", 7) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: enabled = %d\n", atoi(op->v.string));
-					enabled = atoi(op->v.string) == 1 ?
-							true : false;
-				} else if (!strncmp(x->name, "btm_retry", 9) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: btm_retry = %d\n",
-						atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						steer_btm_retry = atoi(op->v.string);
-				} else if (!strncmp(x->name, "btm_retry_secs", 14) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: btm_retry_secs = %d\n",
-						atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						steer_btm_retry_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "fallback_legacy", 15) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: legacy = %d\n",
-							atoi(op->v.string));
-					if (atoi(op->v.string) >= 0)
-						legacy = atoi(op->v.string);
-				} else if (!strncmp(x->name, "steer_legacy_reassoc_secs", 25) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: steer_legacy_rassoc_secs = %d\n",
-						atoi(op->v.string));
-					steer_legacy_reassoc_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "steer_legacy_retry_secs", 23) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: steer_legacy_retry_secs = %d\n",
-						atoi(op->v.string));
-					steer_legacy_retry_secs = atoi(op->v.string);
-				} else if (!strncmp(x->name, "ifname", 6) &&
-						op->type == UCI_TYPE_STRING) {
-					dbg("Steer: ifname = %s\n", op->v.string);
-					ifcfg = get_netif_fhcfg_by_name(cfg, op->v.string);
-					if (!ifcfg) {
-						warn("stale config for '%s'\n", op->v.string);
-						break;
-					}
-
-					list_flush(&ifcfg->steer_excludelist, struct stax, list);
-					list_flush(&ifcfg->steer_btm_excludelist, struct stax, list);
-					list_flush(&ifcfg->assoc_ctrllist, struct stax, list);
-				} else if (!strncmp(x->name, "param", 5) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: param: ");
-					uci_foreach_element(&op->v.list, xi) {
-						struct steer_policy *p = NULL;
-						struct steer_rule *r;
-
-						dbg("%s ", xi->name);
-						p = get_steer_policy_by_name(ifcfg, xi->name);
-						if (!p) {
-							dbg("TODO ?");
-							continue;
-						}
-						p->enabled = enabled;
-						r = get_steer_rule_by_name(xi->name);
-						if (r)
-							r->enabled = enabled;
-					}
-					dbg("\n");
-				} else if (!strncmp(x->name, "exclude_btm", 11) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: exclude_btm: ");
-					uci_foreach_element(&op->v.list, xi) {
-						dbg("%s ", xi->name);
-						stax_add_entry(&ifcfg->steer_btm_excludelist, xi->name);
-					}
-					dbg("\n");
-				} else if (!strncmp(x->name, "exclude", 7) &&
-						op->type == UCI_TYPE_LIST) {
-					struct uci_element *xi;
-
-					dbg("Steer: exclude: ");
-					uci_foreach_element(&op->v.list, xi) {
-						dbg("%s ", xi->name);
-						stax_add_entry(&ifcfg->steer_excludelist, xi->name);
-					}
-					dbg("\n");
-				}
-			}
-
-			if (ifcfg) {
-				ifcfg->enabled = enabled;
-				ifcfg->steer_btm_retry = steer_btm_retry;
-				ifcfg->steer_btm_retry_secs = steer_btm_retry_secs;
-				if (legacy) {
-					/* wait atleast these many secs before
-					 * falling to legacy steering.
-					 */
-					ifcfg->fallback_legacy = 1 +
-						steer_btm_retry * steer_btm_retry_secs +
-						legacy;
-				}
-				ifcfg->steer_legacy_reassoc_secs = steer_legacy_reassoc_secs;
-				ifcfg->steer_legacy_retry_secs = steer_legacy_retry_secs;
-			}
-		}
-#endif
-		else if (!strcmp(s->type, "steer-param")) {
-			/* For every steer-param name, find matching rule
-			 * module. If one is available, then the rule module
-			 * takes care of handling the get/set config for this
-			 * parameter specific optins.
-			 */
-			struct steer_rule *r;
-
-			dbg("Steer-param: %s\n", s->e.name);
-			r = get_steer_rule_by_name(s->e.name);
-			if (r) {
-				dbg("Rule to handle steer-param '%s' available\n", s->e.name);
-				r->config(r, cfg, s);
-			}
-		} else if (!strncmp(s->type, "wifi-iface", 10)) {
-			struct uci_element *x;
-			int mob_id = 500;
-
-			uci_foreach_element(&s->options, x) {
-				struct uci_option *op;
-
-				op = uci_to_option(x);
-				if (!strncmp(x->name, "mobility_domain", 7) &&
-						op->type == UCI_TYPE_STRING)
-					mob_id = atoi(op->v.string);
-			}
-
-			dbg("wireless: mobility_domain = %d\n", mob_id);
-			memcpy(cfg->mobility_domain, &mob_id, 2);
-			dbg("wireless: mobility_domain hex = %02x:%02x\n",
-					cfg->mobility_domain[0],
-					cfg->mobility_domain[1]);
-
-			break;
-		}
-	}
+	if (tb[POL_UTIL_TH])
+		c->apolicy.util_threshold = atoi(tb[POL_UTIL_TH]->v.string);
 
-	uci_unload(ctx, pkg);
-	uci_free_context(ctx);
-	return 0;
-}
+	if (tb[POL_RCPI_TH])
+		c->apolicy.rcpi_threshold = atoi(tb[POL_RCPI_TH]->v.string);
 
-static void config_update_entry(struct uci_context *ctx, struct uci_package *p,
-				struct uci_section *s, const char *optname,
-				int add, void *val, int len)
-{
-	struct uci_ptr ptr;
-
-	memset(&ptr, 0, sizeof(struct uci_ptr));
-	ptr.p = p;
-	ptr.s = s;
-	ptr.package = p->e.name;
-	ptr.section = s->e.name;
-	ptr.option = optname;
-	ptr.target = UCI_TYPE_OPTION;
-	ptr.flags |= UCI_LOOKUP_EXTENDED;
-	ptr.value = (char *)val;
-
-	if (add) {
-		dbg("config: add list option: %s\n", (char *)val);
-		uci_add_list(ctx, &ptr);
-	} else {
-		dbg("config: del list option: %s\n", (char *)val);
-		uci_del_list(ctx, &ptr);
+	if (tb[POL_RPT_SCAN]) {
+		c->apolicy.report_scan =
+			atoi(tb[POL_RPT_SCAN]->v.string) == 1 ? true : false;
 	}
-	uci_commit(ctx, &p, false);
-}
 
-int config_update(const char *confname, struct cntlr_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;
+	if (tb[POL_RPT_ASSOC_FAILS]) {
+		c->apolicy.report_sta_assocfails =
+			atoi(tb[POL_RPT_ASSOC_FAILS]->v.string) == 1 ?
+								true : false;
+	}
 
-	ctx = uci_alloc_context();
-	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
-		dbg("config file '%s' not found!\n", confname);
-		free(ctx);
-		return -1;
+	if (tb[POL_RPT_METRIC_PERIODIC]) {
+		c->apolicy.report_metric_periodic =
+				atoi(tb[POL_RPT_METRIC_PERIODIC]->v.string);
 	}
 
-	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);
+	if (tb[POL_RPT_RCPI_TH]) {
+		c->apolicy.report_rcpi_threshold =
+				atoi(tb[POL_RPT_RCPI_TH]->v.string);
+	}
 
-		goto out_exit;
+	if (tb[POL_RPT_UTIL_TH]) {
+		c->apolicy.report_util_threshold =
+				atoi(tb[POL_RPT_UTIL_TH]->v.string);
 	}
-out_exit:
-	uci_free_context(ctx);
-	return 0;
-}
 
-int config_update2(const char *confname, struct cntlr_config *cfg,
-		const char *section_type,
-		const char *match_option,
-		const char *match_option_value,
-		const char *option,
-		int add,
-		void *value, int len)
-{
-	struct uci_context *ctx = NULL;
-	struct uci_package *pkg = NULL;
-	struct uci_element *e;
+	if (tb[POL_INC_STA_STATS]) {
+		c->apolicy.include_sta_stats =
+			atoi(tb[POL_INC_STA_STATS]->v.string) == 1 ?
+								true : false;
+	}
 
-	ctx = uci_alloc_context();
-	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
-		dbg("config file '%s' not found!\n", confname);
-		free(ctx);
-		return -1;
+	if (tb[POL_INC_STA_METRIC]) {
+		c->apolicy.include_sta_metric =
+			atoi(tb[POL_INC_STA_METRIC]->v.string) == 1 ?
+								true : false;
 	}
 
-	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 (tb[POL_PVID])
+		c->apolicy.pvid = atoi(tb[POL_PVID]->v.string);
 
-		if (strcmp(s->type, section_type))
-			continue;
+	if (tb[POL_PCP_DEFAULT])
+		c->apolicy.pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);
 
-		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;
+	if (tb[POL_DISALLOW_BSTA_P1]) {
+		c->apolicy.disallow_bsta_p1 =
+			atoi(tb[POL_DISALLOW_BSTA_P1]->v.string) == 1 ?
+								true : false;
 	}
-out_exit:
-	uci_free_context(ctx);
-	return 0;
-}
 
+	if (tb[POL_DISALLOW_BSTA_P2]) {
+		c->apolicy.disallow_bsta_p2 =
+			atoi(tb[POL_DISALLOW_BSTA_P2]->v.string) == 1 ?
+								true : false;
+	}
 
-#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)
+	return 0;
+}
 
-void register_config(struct config *c)
+int cntlr_config_reload(struct controller_config *cfg)
 {
-	static struct uci_context *ctx;
-	static struct uci_package *pkg;
+	struct uci_context *ctx;
+	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, "wificntlr", &pkg))
+	if (!ctx)
 		return -1;
 
+	if (uci_load(ctx, "controller", &pkg)) {
+		uci_free_context(ctx);
+		return -1;
+	}
+
 	uci_foreach_element(&pkg->sections, e) {
 		struct uci_section *s = uci_to_section(e);
-		const char *option_val;
-
-		if (strcmp(s->type, "wificntlr"))
-			continue;
 
-		option_val = uci_lookup_option_string(ctx, s, name);
-		if (option_val)
-			sprintf(val, "%s", option_val);
-		else
-			ret = -1;
+		if (!strcmp(s->type, "wificntlr")) {
+			cntlr_config_get_base(cfg, s);
+		} else if (!strcmp(s->type, "fh-credentials") ||
+				!strcmp(s->type, "bk-credentials")) {
+			cntlr_config_get_credentials(cfg, s);
+		} else if (!strcmp(s->type, "agent-policy")) {
+			cntlr_config_get_agent_policy(cfg, s);
+		}
 	}
-	uci_free_context(ctx);
-	return ret;
 
+	uci_free_context(ctx);
+	return 0;
 }
-#endif
diff --git a/src/core/config.h b/src/core/config.h
index e9c6a9b93c330adc88143f3bc51c2dc0944a9af9..d8270878f362057d76358755595e91a9ff02f974 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -11,6 +11,7 @@
 #define CONFIG_H
 
 
+// TODO: use wifi.h definitions??
 enum wifi_band {
 	WIFI_BAND_NONE,
 	WIFI_BAND_2       = 1 << 0,        /**< 0x1 for 2.4Ghz band. */
@@ -19,13 +20,28 @@ enum wifi_band {
 	WIFI_BAND_6       = 1 << 3,        /**< 0x8 for 6Ghz */
 };
 
+// TODO: use wifi.h definitions??
+enum wifi_security {
+	WIFI_SECURITY_NONE,
+	WIFI_SECURITY_WEP64,
+	WIFI_SECURITY_WEP128,
+	WIFI_SECURITY_WPAPSK,
+	WIFI_SECURITY_WPA2PSK,
+	WIFI_SECURITY_WPA3PSK,
+	WIFI_SECURITY_WPA3PSK_T,
+	WIFI_SECURITY_WPA,
+	WIFI_SECURITY_WPA2,
+	WIFI_SECURITY_WPA3,
+};
+
+
 struct iface_credential {
-	uint8_t band;
+	enum wifi_band band;
+	enum wifi_security sec;
+	uint8_t key[64];
 	uint8_t bssid[6];
 	uint8_t ssid[33];
-	char encryption[64];
-	uint8_t key[64];
-	uint16_t vlan_id;
+	uint16_t vlanid;
 };
 
 enum agent_steer_policy {
@@ -35,7 +51,7 @@ enum agent_steer_policy {
 };
 
 struct agent_policy {
-	uint8_t alid[6];                  /* ieee1905 AL macaddress */
+	uint8_t id[6];                    /* ieee1905 AL macaddress */
 	enum agent_steer_policy policy;   /* 0, 1, 2 - see MultiAP specs */
 	uint8_t util_threshold;           /* utilization as in BSS load IE */
 	uint8_t rcpi_threshold;           /* 0 - 220 */
@@ -52,35 +68,29 @@ struct agent_policy {
 	bool disallow_bsta_p2;            /* disallow profile2 bSTA link */
 
 	struct list_head list;            /* link to next policy */
+
+	/* custom policies follow */
+	struct list_head steer_exlist;	  /* exclude stas from steering */
+	struct list_head btmsteer_exlist; /* exclude stas from BTM steering */
 };
 
 struct controller_config {
 	bool enabled;
-	uint8_t wps_supp_bands;           /* WPS registrar supported bands */
-
-	struct iface_credential fh5;
-	struct iface_credential fh2;
-
-	struct iface_credential bk5;
-	struct iface_credential bk2;
-
-	struct list_head policylist;      /* list of struct agent_policy */
+	bool has_registrar_5g;
+	bool has_registrar_2g;
+	int num_fh;
+	int num_bk;
+	struct iface_credential fh[2];
+	struct iface_credential bk[2];
+	struct agent_policy apolicy;
+
+	struct list_head policylist;      /* custom list of agent_policy */
 };
 
 struct controller;
 
-int cntlr_config_reload(const char *confname, struct controller_config *cfg);
+int cntlr_config_reload(struct controller_config *cfg);
 int cntlr_config_defaults(struct controller *c, struct controller_config *cfg);
 void cntlr_config_dump(struct controller_config *cfg);
 
-int config_update(const char *confname, struct controller_config *cfg,
-			const char *section, const char *option, int add,
-			void *value, int len);
-
-
-int config_update2(const char *confname, struct controller_config *cfg,
-			const char *section_type,
-			const char *match_option, const char *match_option_value,
-			const char *option, int add, void *value, int len);
-
 #endif
diff --git a/src/utils/utils.h b/src/utils/utils.h
index 6b00df0f734eecd8dba6463f76019660f8d97dca..465032f3b6d109489149d96a404048d7cd9f827b 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -20,6 +20,14 @@
 
 #define hwaddr_hash(a)	(a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5])
 
+#ifndef MACFMT
+#define MACFMT	"%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+#ifndef MAC2STR
+#define MAC2STR(_m)	(_m)[0], (_m)[1], (_m)[2], (_m)[3], (_m)[4], (_m)[5]
+#endif
+
 
 bool match_oui0(unsigned char *oui, unsigned char *hwaddr, int ouis);
 unsigned char *hwaddr_aton(const char *macstr, unsigned char *mac);