diff --git a/gitlab-ci/install-dependencies.sh b/gitlab-ci/install-dependencies.sh
index eaf28997e2cae200e021364fe6a82ee05fcc649d..526ab0bf344a989a2075ff8942f7b0c748a7609a 100755
--- a/gitlab-ci/install-dependencies.sh
+++ b/gitlab-ci/install-dependencies.sh
@@ -160,5 +160,104 @@ config rule-custom
 echo "
 config wificntlr
 	option enabled '1'
+	option registrar '5 2'	    #bands on which wps registrar supported
 	option debug '6'
+
+config fh-credentials 'map-net55'
+	option band '5'
+	option encryption 'psk2'
+	option key '1234567890'
+	option ssid 'map-net5'
+	option vlan '101'
+	option multi_ap '128'		# TODO: should mode 1/2/3 be added?
+
+config fh-credentials 'map-net55'
+	option band '5'
+	option encryption 'psk2'
+	option key '1234567890'
+	option ssid 'map-net55'
+	option vlan '0'               # 0 - 2 rsvd, 3 - 4094 => vlan id
+
+config bk-credentials
+	option band '5'
+	option encryption 'psk2'
+	option key '5555555555'
+	option ssid 'map-bkhaul-5'
+
+config bk-credentials
+	option band '2'
+	option encryption 'psk2'
+	option key '2222222222'
+	option ssid 'map-bkhaul-2'
+
+config agent-policy
+	option agent_id '00:22:07:7E:9C:D6'        # 1905al macaddress of agent
+	list steer_exclude '00:11:22:33:44:55'
+	list steer_exclude_btm '00:aa:bb:cc:dd:ee'
+	option steer_policy '2'                    # 0, 1, 2 - see MultiAP spec
+	option util_threshold '200'                # channel-util as in BSS load
+	option rcpi_threshold '30'                 # 0 - 220 valid range
+	option report_scan '0'                     # 0 or 1 for independent sc include_sta_metric '0'              # sta metric in AP metric resp
+	option pvid '100'                          # primary vlan id
+	option pcp_default '5'                     # default vlan pcp
+	option disallow_bsta_p1 '0'                # 0 or 1 profile20 valid range
+	option report_scan '0'                     # 0 or 1 for independent scans
+	option report_sta_assocfails '1'           # 0 or 1 - stas assoc failure
+	option report_metric_periodic '0'          # 0, or 1 - 255 in secs
+	option report_rcpi_threshold '30'                 # 0 - 220 valid range
+	option report_scan '0'                     # 0 or 1 for independent scans
+	option report_sta_assocfails '1'           # 0 or 1 - stas assoc failure
+	option report_metric_periodic '0'          # 0, or 1 - 255 in secs
+	option report_rcpi_threshold '0'           # 0, or 1 - 220
+	option report_util_threshold '0'           # 0, or  channel-util value
+	option include_sta_stats '0'               # sta stats in AP metric resp
+	option include_sta_metric '0'              # sta metric in AP metric resp
+	option pvid '100'                          # primary vlan id
+	option pcp_default '5'                     # default vlan pcp
+	option disallow_bsta_p1 '0'                # 0 or 1 profile-1 bSTA
+	option disallow_bsta_p2 '0'                # 0 or 1 profile-2 bSTA
+
+config agent-policy
+	option agent_id '00:00:00:00:00:00'
+	list steer_exclude '00:11:22:33:44:55'
+	list steer_exclude_btm '00:aa:bb:cc:dd:ee'
+	option steer_policy '2'                    # 0, 1, 2 - see MultiAP spec
+	option util_threshold '200'                # channel-util as in BSS load
+	option rcpi_threshold '30'                 # 0 - 220 valid range
+	option report_scan '0'                     # 0 or 1 for independent sc include_sta_metric '0'              # sta metric in AP metric resp
+	option pvid '100'                          # primary vlan id
+	option pcp_default '5'                     # default vlan pcp
+	option disallow_bsta_p1 '0'                # 0 or 1 profile20 valid range
+	option report_scan '0'                     # 0 or 1 for independent scans
+	option report_sta_assocfails '1'           # 0 or 1 - stas assoc failure
+	option report_metric_periodic '0'          # 0, or 1 - 255 in secs
+	option report_rcpi_threshold '30'                 # 0 - 220 valid range
+	option report_scan '0'                     # 0 or 1 for independent scans
+	option report_sta_assocfails '1'           # 0 or 1 - stas assoc failure
+	option report_metric_periodic '0'          # 0, or 1 - 255 in secs
+	option report_rcpi_threshold '0'           # 0, or 1 - 220
+	option report_util_threshold '0'           # 0, or  channel-util value
+	option include_sta_stats '0'               # sta stats in AP metric resp
+	option include_sta_metric '0'              # sta metric in AP metric resp
+	option pvid '100'                          # primary vlan id
+	option pcp_default '5'                     # default vlan pcp
+	option disallow_bsta_p1 '0'                # 0 or 1 profile-1 bSTA
+	option disallow_bsta_p2 '0'                # 0 or 1 profile-2 bSTA
+
+config agent-policy
+	option agent_id '00:22:07:6A:1C:3A'    # 1905al macaddress of agent
+	list steer_exclude '00:11:22:33:44:55'
+	list steer_exclude_btm '00:aa:bb:cc:dd:ee'
+	option steer_policy '2'                    # 0, 1, 2 - see MultiAP spec
+	option util_threshold '200'                # channel-util as in BSS load
+	option report_metric_periodic '0'          # 0, or 1 - 255 in secs
+	option report_rcpi_threshold '0'           # 0, or 1 - 220
+	option report_util_threshold '0'           # 0, or  channel-util value
+	option include_sta_stats '0'               # sta stats in 3:4d:35:d2'
+	option bss '00:22:07:11:22:33'
+
+config rule-custom
+	option action restrict
+	option sta 'd8:32:e3:4d:35:d2'
+	option bss '00:22:07:11:22:33'
 " > /etc/config/controller
diff --git a/src/Makefile b/src/Makefile
index 8f6ca42b2390fea7542ed9d9834bf228ef663774..9daac83e83af9a1b91c817a547b32eeebbd4bfb5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -20,6 +20,7 @@ CNTLR_OBJS = \
 	core/cntlr_ubus.o \
 	core/cntlr.o \
 	core/cntlr_map.o \
+	core/cntlr_tlv_generator.o \
 	core/cntlr_map_debug.o \
 	core/config.o \
 	core/main.o
@@ -27,6 +28,7 @@ CNTLR_OBJS = \
 LIBS = -lubus -lubox -ljson-c -lblobmsg_json -luci -pthread
 LIBS += -rdynamic -ldl
 LIBS += -lmaputils
+LIBS += -lwsc
 
 plugin_subdirs ?= $(wildcard plugins/*)
 plugin_sofile = $(wildcard $(d)/*.so)
diff --git a/src/controller.conf b/src/controller.conf
index 452caf3e5219f738916659df50215f0b6a0bffc7..1cb46cfb8c0af03c1d6c4ab8e82b9d0277652059 100644
--- a/src/controller.conf
+++ b/src/controller.conf
@@ -22,12 +22,22 @@ config bk-credentials
 	option encryption 'psk2'
 	option key '5555555555'
 	option ssid 'map-bkhaul-5'
+	option multi_ap '2'
+#	option multi_ap_backhaul_sta '1'                        # possible future TODO
+#	option multi_ap_backhaul_key 'multiap_key123'           # possible future TODO
+#	option multi_ap_backhaul_ssid 'MultiAP-0022077E9CD6'    # possible future TODO
+	option disallow_bsta '1'		# bitmap, 1 for disallow p1, 2 to disallow p2, 3 to disallow both (probably never applicable)
 
 config bk-credentials
 	option band '2'
 	option encryption 'psk2'
 	option key '2222222222'
 	option ssid 'map-bkhaul-2'
+	option multi_ap '2'
+#	option multi_ap_backhaul_sta '1'                        # possible future TODO
+#	option multi_ap_backhaul_key 'multiap_key123'           # possible future TODO
+#	option multi_ap_backhaul_ssid 'MultiAP-0022077E9CD6'    # possible future TODO
+	option disallow_bsta '0'                # 0 or 1 profile-1 bSTA
 
 config agent-policy
 	option agent_id '02:20:40:55:66:77'        # 1905al macaddress of agent
@@ -48,7 +58,6 @@ config agent-policy
 	option disallow_bsta_p1 '0'                # 0 or 1 profile-1 bSTA
 	option disallow_bsta_p2 '0'                # 0 or 1 profile-2 bSTA
 
-
 config agent-policy
 	option agent_id '02:20:40:aa:bb:cc'    # 1905al macaddress of agent
 	list steer_exclude '00:11:22:33:44:55'
@@ -68,7 +77,6 @@ config agent-policy
 	option disallow_bsta_p1 '0'                # 0 or 1 profile-1 bSTA
 	option disallow_bsta_p2 '0'                # 0 or 1 profile-2 bSTA
 
-
 ### do not parse following now ###
 config steer-param 'rssi'
 	option rssi_threshold '-68'
diff --git a/src/core/cntlr.c b/src/core/cntlr.c
index 73a32fa39db245834591dfa57daed78fb5d8261b..890c547b1e42c2fca97d858da945f4882b98b6e2 100644
--- a/src/core/cntlr.c
+++ b/src/core/cntlr.c
@@ -29,6 +29,9 @@
 #include <easy/easy.h>
 #include <wifi.h>	// FIXME: should not be included
 
+#include <map1905/map2.h>
+#include <map1905/maputils.h>
+
 #include "debug.h"
 #include "utils.h"
 #include "config.h"
@@ -44,6 +47,36 @@ static int update_fronthaul_bsslist(struct node *n, struct netif_fhbss *fh);
 static void node_getbssinfo(void *cntlr, void *resp, int len, void *cookie);
 static int enumerate_topology_indirect(struct controller *c);
 
+void stop_cntlr(struct controller *c)
+{
+
+	if (!c) {
+		warn("%s: (cntlr = NULL)\n", __func__);
+		exit(0);
+	}
+
+	stop_test_logging();
+	ubus_unregister_event_handler(c->ubus_ctx, &c->ieee1905_evh);
+	ubus_unregister_event_handler(c->ubus_ctx, &c->ubusx_ev);
+	cntlr_remove_object(c);
+	uloop_done();
+	cntlr_config_clean(&c->cfg);
+	comm_destroy(c->comm);
+	ubus_free(c->ubus_ctx);
+	free(c);
+}
+
+static void cntlr_terminate(struct controller *c)
+{
+	dbg("%s: called.\n", __func__);
+	stop_cntlr(c);
+	exit_alloctrace();
+	stop_logging();
+	//unlink(pidfile);
+	exit(0);
+}
+
+
 /* find node by macaddress */
 static struct node *find_node_by_mac(struct controller *c,
 		const unsigned char *mac)
@@ -1977,6 +2010,7 @@ int start_controller(void)
 	as_init_table(&c->as_table);
 	ubus_add_uloop(ctx);
 
+	cntlr_config_defaults(c, &c->cfg);
 	cntlr_config_reload(&c->cfg);
 
 	c->topology = ubus_get_topology_object(c->ubus_ctx);
@@ -2008,13 +2042,11 @@ int start_controller(void)
 	cntlr_register_module(c);
 
 	uloop_run();
-
 out_exit:
 	/* ubus_unregister_event_handler(ctx, ev); */
 	/* cntlr_remove_object(ctx); */
-	ubus_free(ctx);
 	uloop_done();
-	free(c);
+	cntlr_terminate(c);
 
 	return 0;
 }
diff --git a/src/core/cntlr.h b/src/core/cntlr.h
index 734b564f0f91f8faed1c46ce26a4ae97d26f4d91..a66bd2f97bf8f7962ab0ee79b1082784f8acbbdb 100644
--- a/src/core/cntlr.h
+++ b/src/core/cntlr.h
@@ -49,6 +49,14 @@ struct netif_link {
 	struct list_head list;
 };
 
+/* TODO - fill this structure */
+struct netif_radio {
+	char name[16];
+	unsigned char hwaddr[6];
+	bool isup;
+	struct list_head list;
+};
+
 enum nodetype {
 	NODE_WIFI_EXTENDER,
 	NODE_WIFI_REPEATER,
@@ -121,6 +129,7 @@ struct controller {
 	object_t topology;
 	int num_nodes;
 	struct list_head nodelist;
+	struct list_head radiolist;  /** TODOTODO -- list of radios */
 	struct list_head watchlist;
 	struct uloop_timeout radar_timer;
 	struct hlist_head *as_table; /** active sta hash table */
diff --git a/src/core/cntlr_map.c b/src/core/cntlr_map.c
index 75fa1c7482dd917e532965f03eb8cdf06b7610cd..d753e5cce4ebb053abd9c736574899fb9b80dece 100644
--- a/src/core/cntlr_map.c
+++ b/src/core/cntlr_map.c
@@ -30,6 +30,10 @@
 #include <easy/easy.h>
 #include <wifi.h> // TODO: remove wifi.h
 
+#include <map1905/map2.h>
+#include <map1905/maputils.h>
+#include <ieee1905/1905_tlvs.h>
+
 #include "map_module.h"
 #include "utils.h"
 #include "debug.h"
@@ -39,12 +43,10 @@
 #include "msgqueue.h"
 #include "worker.h"
 #include "cntlr.h"
-
-#include <map1905/map2.h>
-#include <map1905/maputils.h>
+#include "cntlr_ubus.h"
 
 #include "cntlr_map_debug.h"
-
+#include "cntlr_tlv_generator.h"
 
 #define for_each_tlv(e, _buf, _len)					  \
 for ((e) = (struct tlv *)(_buf);					  \
@@ -77,10 +79,58 @@ int handle_topology_response(void *cntlr, struct cmdu_cstruct *cmdu)
 	return 0;
 }
 
-int handle_ap_autoconfig_search(void *cntlr, struct cmdu_cstruct *cmdu)
+/* TODO: error handling */
+int handle_ap_autoconfig_search(void *cntlr, struct cmdu_cstruct *rec_cmdu)
 {
 	trace("%s: --->\n", __func__);
-	return 0;
+	struct controller *c = (struct controller *) cntlr;
+	uint16_t tlv_index = 0;
+	struct cmdu_cstruct *cmdu;
+	struct tlv_map_profile *p;
+	struct tlv_supp_service *p1;
+	int ret;
+
+	cmdu = (struct cmdu_cstruct *)calloc(1,
+			sizeof(struct cmdu_cstruct));
+	if (!cmdu) {
+		fprintf(stderr, "Out of memory!\n");
+		return -1;
+	}
+
+	cmdu->message_type = CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE;
+	memcpy(cmdu->origin, rec_cmdu->origin, 6);
+	cmdu->message_id = cmdu->message_id;
+	strncpy(cmdu->intf_name, rec_cmdu->intf_name,
+			sizeof(cmdu->intf_name) - 1);
+
+	p = cntlr_gen_map_profile(c, cmdu);
+	if (!p)
+		goto fail_cmdu;
+	cmdu->num_tlvs++;
+
+	p1 = cntlr_gen_supp_service(c, cmdu);
+	if (!p1)
+		goto fail_p;
+	cmdu->num_tlvs++;
+
+	cmdu->tlvs = (uint8_t **)calloc(cmdu->num_tlvs,	sizeof(uint8_t *));
+	if (!cmdu->tlvs)
+		goto fail_p1;
+
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p1;
+
+	ret = send_cmdu(c, cmdu);
+	map_free_cmdu(cmdu);
+
+	return ret;
+fail_p1:
+	map_free_tlv_cstruct((uint8_t *) p1);
+fail_p:
+	map_free_tlv_cstruct((uint8_t *) p);
+fail_cmdu:
+	free(cmdu);
+	return -1;
 }
 
 int handle_ap_autoconfig_response(void *cntlr, struct cmdu_cstruct *cmdu)
@@ -89,10 +139,108 @@ int handle_ap_autoconfig_response(void *cntlr, struct cmdu_cstruct *cmdu)
 	return 0;
 }
 
-int handle_ap_autoconfig_wsc(void *cntlr, struct cmdu_cstruct *cmdu)
+int handle_ap_autoconfig_wsc(void *cntlr, struct cmdu_cstruct *rec_cmdu)
 {
 	trace("%s: --->\n", __func__);
-	return 0;
+	struct controller *c = (struct controller *) cntlr;
+	struct tlv_default_8021q_settings *p;
+	struct tlv_traffic_sep_policy *p1;
+	struct tlv_ap_radio_identifier *p2;
+	struct tlv_wsc *p3;
+	struct cmdu_cstruct *cmdu;
+	int tlv_index = 0, ret, i;
+	uint8_t **tlvs;
+
+	cmdu = (struct cmdu_cstruct *)calloc(1, sizeof(struct cmdu_cstruct));
+	if (!cmdu) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	memcpy(cmdu->origin, rec_cmdu->origin, 6);
+	cmdu->message_type = CMDU_TYPE_AP_AUTOCONFIGURATION_WSC;
+	cmdu->message_id = rec_cmdu->message_id;
+	strncpy(cmdu->intf_name, rec_cmdu->intf_name,
+			sizeof(cmdu->intf_name) - 1);
+
+	p = cntlr_gen_8021q_settings(c, cmdu, rec_cmdu);
+	if (!p)
+		goto fail_cmdu;
+	cmdu->num_tlvs++;
+
+	p1 = cntlr_gen_traffic_sep_policy(c, cmdu);
+	if (!p1)
+		goto fail_p;
+	cmdu->num_tlvs++;
+
+	p2 = cntlr_gen_radio_identifier(c, cmdu, rec_cmdu);
+	if (!p2)
+		goto fail_p1;
+	cmdu->num_tlvs++;
+
+	cmdu->tlvs = (uint8_t **)calloc(cmdu->num_tlvs, sizeof(uint8_t *));
+	if (!cmdu->tlvs)
+		goto fail_p2;
+
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p1;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p2;
+
+	/* Generate all fBSS wsc tlvs for this BSS */
+	for (i = 0; i < c->cfg.num_fh; i++) {
+		/* Will return null if band did not match OR on failure*/
+		p3 = cntlr_gen_wsc(c, cmdu, rec_cmdu, &c->cfg.fh[i]);
+		if (!p3)
+			continue;
+		cmdu->num_tlvs++;
+
+		tlvs = (uint8_t **)realloc(cmdu->tlvs, cmdu->num_tlvs * sizeof(uint8_t *));
+		if (!tlvs) {
+			cmdu->num_tlvs--;
+			map_free_tlv_cstruct((uint8_t *) p3);
+			continue;
+		}
+
+		cmdu->tlvs = tlvs;
+		cmdu->tlvs[tlv_index++] = (uint8_t *)p3;
+	}
+
+	/* Generate all bBSS wsc tlvs for this BSS */
+	for (i = 0; i < c->cfg.num_bk; i++) {
+		/* Will return null if band did not match OR on failure*/
+		p3 = cntlr_gen_wsc(c, cmdu, rec_cmdu, &c->cfg.bk[i]);
+		if (!p3)
+			continue;
+		cmdu->num_tlvs++;
+
+		tlvs = (uint8_t **)realloc(cmdu->tlvs, cmdu->num_tlvs * sizeof(uint8_t *));
+		if (!tlvs) {
+			cmdu->num_tlvs--;
+			map_free_tlv_cstruct((uint8_t *) p3);
+			continue;
+		}
+
+		cmdu->tlvs = tlvs;
+		cmdu->tlvs[tlv_index++] = (uint8_t *)p3;
+	}
+
+	if (cmdu->num_tlvs < 4)
+		goto fail_tlvs;
+
+	ret = send_cmdu(c, cmdu);
+	map_free_cmdu(cmdu);
+	return ret;
+fail_tlvs:
+	free(cmdu->tlvs);
+fail_p2:
+	map_free_tlv_cstruct((uint8_t *) p2);
+fail_p1:
+	map_free_tlv_cstruct((uint8_t *) p1);
+fail_p:
+	map_free_tlv_cstruct((uint8_t *) p);
+fail_cmdu:
+	free(cmdu);
+	return -1;
 }
 
 
@@ -347,7 +495,7 @@ bool is_cmdu_for_us(void *module, uint16_t type)
 }
 
 int cntlr_handle_map_event(void *module, uint16_t cmdutype, uint16_t mid,
-			   char *rxif, uint8_t *src, uint8_t *tlvs, int len)
+		char *rxif, uint8_t *src, uint8_t *tlvs, int len)
 {
 	struct controller *c = (struct controller *)module;
 	const struct map_cmdu_calltable_t *f;
@@ -379,7 +527,6 @@ int cntlr_handle_map_event(void *module, uint16_t cmdutype, uint16_t mid,
 	if (verbose >= 4 && f[idx].debug)
 		f[idx].debug(c, cmdu);
 
-
 	map_free_cmdu(cmdu);
 
 	//TODO: check ret
diff --git a/src/core/cntlr_map_debug.c b/src/core/cntlr_map_debug.c
index 4d2f580af687539b949072f8ebfc751a0efc0328..919586fe9d1c2d034f7f035cd3e52ba3aed1f074 100644
--- a/src/core/cntlr_map_debug.c
+++ b/src/core/cntlr_map_debug.c
@@ -356,15 +356,22 @@ int debug_sta_caps_report(void *cntlr, struct cmdu_cstruct *cmdu)
 				char *frame;
 				int offset = 1 + 2 + 1;
 
+				trace("\tresult_code: 0x%02x\n", p->result_code);
+
+				/* if tlv is shorter or equal to the offset len,
+				 * then no frame is passed
+				 */
+				if (p->tlv_len <= offset)
+					break;
+
 				frame = calloc(1,
 						(2 * (p->tlv_len - offset)) + 1);
 				if (!frame)
-					continue;
+					break;
 
 				btostr(p->frame_body + offset,
 						p->tlv_len - offset, frame);
 
-				trace("\tresult_code: 0x%02x\n", p->result_code);
 				trace("\tframe: 0x%s\n", frame);
 				break;
 			}
diff --git a/src/core/cntlr_tlv_generator.c b/src/core/cntlr_tlv_generator.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d6cfab0e79f0ee835e0a2296c3ef3da6e7ca9d8
--- /dev/null
+++ b/src/core/cntlr_tlv_generator.c
@@ -0,0 +1,263 @@
+/*
+ * cntlr_tlv_generator.c - tlv building function
+ *
+ * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
+ *
+ * Author: jakob.olsson@iopsys.eu
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <net/if_arp.h>
+#include <pthread.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <json-c/json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/uloop.h>
+#include <libubox/ustream.h>
+#include <libubox/utils.h>
+#include <libubus.h>
+
+#include <easy/easy.h>
+#include <wifi.h> // TODO: remove wifi.h
+
+#include <map1905/map2.h>
+#include <map1905/maputils.h>
+#include <wsc.h>
+
+#include "map_module.h"
+#include "utils.h"
+#include "debug.h"
+#include "config.h"
+#include "cntlr.h"
+#include "cntlr_ubus.h"
+
+#include "cntlr_map_debug.h"
+#include "cntlr_tlv_generator.h"
+
+uint8_t *extract_tlv_by_type(struct cmdu_cstruct *cmdu, uint8_t tlv_type)
+{
+	uint8_t *tlv;
+	int i;
+
+	for (i = 0; i < cmdu->num_tlvs; i++) {
+		tlv = cmdu->tlvs[i];
+		if (*tlv == tlv_type)
+			return tlv;
+	}
+
+	return NULL;
+
+}
+
+struct tlv_default_8021q_settings *cntlr_gen_8021q_settings(struct controller *c,
+	struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu)
+{
+	struct tlv_default_8021q_settings *p;
+	struct agent_policy *a, *found = NULL;
+
+	list_for_each_entry(a, &c->cfg.policylist, list) {
+		if (!memcmp(rec_cmdu->origin, a->agent_id, 6)) {
+			found = a;
+			break;
+		}
+	}
+
+	if (!found)
+		return NULL;
+
+	p = (struct tlv_default_8021q_settings *) calloc(1, sizeof(*p));
+	if (!p)
+		return NULL;
+
+	p->tlv_type = MAP_TLV_DEFAULT_8021Q_SETTINGS;
+	p->primary_vid = a->pvid;
+	p->pcp = a->pcp_default;
+
+	return p;
+}
+
+struct tlv_traffic_sep_policy *cntlr_gen_traffic_sep_policy(struct controller *c,
+	struct cmdu_cstruct *cmdu)
+{
+	struct tlv_traffic_sep_policy *p;
+	int i;
+
+	p = (struct tlv_traffic_sep_policy *) calloc(1, sizeof(*p));
+	if (!p)
+		return NULL;
+
+	p->tlv_type = MAP_TLV_TRAFFIC_SEPARATION_POLICY;
+	p->nbr_ssid = c->cfg.num_fh;
+	p->data = calloc(p->nbr_ssid, sizeof(*(p->data)));
+	if (!p->data) {
+		map_free_tlv_cstruct((uint8_t *) p);
+		return NULL;
+	}
+
+	for (i = 0; i < p->nbr_ssid; i++) {
+		int len;
+
+		len = strlen((char *)c->cfg.fh[i].ssid);
+
+		p->data[i].ssid_len = len;
+		p->data[i].vid = c->cfg.fh[i].vlanid;
+		p->data[i].ssid = calloc(1, len + 1);
+		if (!p->data[i].ssid)
+			continue;
+
+		strncpy(p->data[i].ssid, (char *)c->cfg.fh[i].ssid, len);
+	}
+
+	return p;
+}
+
+struct tlv_wsc *cntlr_gen_wsc(struct controller *c,
+	struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu,
+	struct iface_credential *creds)
+{
+	struct tlv_ap_radio_basic_cap *ap_caps = NULL;
+	struct tlv_wsc *m2, *m1;
+	enum wifi_band band = BAND_UNKNOWN;
+	struct mdata cfg = {0};
+	int i, rv;
+
+	ap_caps = (struct tlv_ap_radio_basic_cap *) extract_tlv_by_type(rec_cmdu,
+			MAP_TLV_AP_RADIO_BASIC_CAPABILITIES);
+	if (!ap_caps)
+		return NULL;
+
+	m1 = (struct tlv_wsc *) extract_tlv_by_type(rec_cmdu,
+			TLV_TYPE_WSC);
+	if (!m1)
+		return NULL;
+
+	/* determine radio frequency */
+	for (i = 0; i < ap_caps->operating_classes_nr; i++) {
+		if (ap_caps->operating_class[i].op_class >= 81 &&
+				ap_caps->operating_class[i].op_class <= 84)
+			band = BAND_2;
+		else if (ap_caps->operating_class[i].op_class >= 112 &&
+				ap_caps->operating_class[i].op_class <= 130)
+			band = BAND_5;
+
+	}
+
+	if (creds->band != band)
+		return NULL;
+
+	m2 = (struct tlv_wsc *) calloc(1, sizeof(struct tlv_wsc));
+	if (!m2)
+		return NULL;
+
+	m2->tlv_type = TLV_TYPE_WSC;
+
+	if (creds->sec == WIFI_SECURITY_WPA3PSK)
+		cfg.input.auth_types = 0x0040;
+	else if (creds->sec == WIFI_SECURITY_WPA2PSK)
+		cfg.input.auth_types = 0x0020;
+	else if (creds->sec == WIFI_SECURITY_WPAPSK)
+		cfg.input.auth_types = 0x0002;
+	else /* default to WPA */
+		cfg.input.auth_types = 0x0008;
+
+	/* backhaul STA */
+	cfg.input.mapie |= (creds->mode == WIFI_MODE_STA ? 1 << 7 : 0);
+	/* backhaul BSS */
+	cfg.input.mapie |= (creds->multi_ap & 0x01) << 6;
+	/* fronthaul BSS */
+	cfg.input.mapie |= (creds->multi_ap & 0x02) << 4;
+	/* backhaul STA */
+	cfg.input.mapie |= (creds->disallow_bsta << 2);
+	/* teardown bit */
+	//cfg.input.mapie |= 1 << 3;
+
+	memcpy(cfg.input.ssid, creds->ssid, 32);
+	memcpy(cfg.input.network_key, creds->key, 64);
+	cfg.input.encryption_types = 0; // Will not match any
+
+	rv = wscBuildM2(m1->wsc_frame, m1->wsc_frame_size, &m2->wsc_frame,
+			&m2->wsc_frame_size, band, NULL, &cfg);
+	if (!rv)
+		err("Failed to build m2!\n");
+
+	return m2;
+}
+
+struct tlv_ap_radio_identifier *cntlr_gen_radio_identifier(struct controller *c,
+		struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu)
+{
+	struct tlv_ap_radio_identifier *p;
+	uint8_t *tlv;
+	int i;
+
+	p = (struct tlv_ap_radio_identifier *) calloc(1,
+			sizeof(struct tlv_ap_radio_identifier));
+	if (!p)
+		return NULL;
+
+	for (i = 0; i < rec_cmdu->num_tlvs; i++) {
+		tlv = (uint8_t *) rec_cmdu->tlvs[i];
+		switch (*tlv) {
+		case MAP_TLV_AP_RADIO_BASIC_CAPABILITIES: {
+			struct tlv_ap_radio_basic_cap *rec_p = (struct tlv_ap_radio_basic_cap *)tlv;
+
+			p->tlv_type = MAP_TLV_AP_RADIO_IDENTIFIER;
+			memcpy(p->radio_id, rec_p->radio_id, 6);
+			break;
+		}
+		default:
+			break;
+		}
+	}
+
+	return p;
+}
+
+struct tlv_supp_service *cntlr_gen_supp_service(struct controller *c,
+		struct cmdu_cstruct *cmdu)
+{
+	struct tlv_supp_service *p;
+	int i;
+
+	p = (struct tlv_supp_service *)calloc(1, sizeof(*p));
+	if (!p)
+		return NULL;
+
+	p->tlv_type = MAP_TLV_SUPPORTED_SERVICE;
+	p->supported_services_list = 1;
+	p->supported_services = calloc(p->supported_services_list,
+			sizeof(*p->supported_services));
+	if (!p->supported_services) {
+		map_free_tlv_cstruct((uint8_t *) p);
+		return NULL;
+	}
+
+	for (i = 0; i < p->supported_services_list; i++)
+		p->supported_services[i].service = SUPPORTED_SERVICE_MULTIAP_CONTROLLER;
+
+	return p;
+}
+
+struct tlv_map_profile *cntlr_gen_map_profile(struct controller *c,
+		struct cmdu_cstruct *cmdu)
+{
+	struct tlv_map_profile *p;
+
+	p = calloc(1, sizeof(struct tlv_map_profile));
+	if (!p)
+		return NULL;
+
+	p->tlv_type = MAP_TLV_MULTIAP_PROFILE;
+	p->profile = 0x02;
+
+	return p;
+}
diff --git a/src/core/cntlr_tlv_generator.h b/src/core/cntlr_tlv_generator.h
new file mode 100644
index 0000000000000000000000000000000000000000..399f92bc4334cccb72068480aae0efb98e48970b
--- /dev/null
+++ b/src/core/cntlr_tlv_generator.h
@@ -0,0 +1,26 @@
+/*
+ * cntlr_tlv_generator.h - tlv building function declarations
+ *
+ * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
+ *
+ * Author: jakob.olsson@iopsys.eu
+ *
+ */
+
+#ifndef CNTLR_TLV_GEN_H
+#define CNTLR_TLV_GEN_H
+
+struct tlv_default_8021q_settings *cntlr_gen_8021q_settings(struct controller *c,
+		struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu);
+struct tlv_traffic_sep_policy *cntlr_gen_traffic_sep_policy(struct controller *c,
+		struct cmdu_cstruct *cmdu);
+struct tlv_wsc *cntlr_gen_wsc(struct controller *c,
+		struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu,
+		struct iface_credential *fh);
+struct tlv_ap_radio_identifier *cntlr_gen_radio_identifier(struct controller *c,
+		struct cmdu_cstruct *cmdu, struct cmdu_cstruct *rec_cmdu);
+struct tlv_supp_service *cntlr_gen_supp_service(struct controller *c,
+		struct cmdu_cstruct *cmdu);
+struct tlv_map_profile *cntlr_gen_map_profile(struct controller *c,
+		struct cmdu_cstruct *cmdu);
+#endif
diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c
index 5659a95cd7bf36a0c6f631ce4fd2625720ab8837..1bcffc90823922a3cd49ba8dd1e3707bc8c5dc85 100644
--- a/src/core/cntlr_ubus.c
+++ b/src/core/cntlr_ubus.c
@@ -21,6 +21,8 @@
 
 #include <easy/easy.h>
 #include <wifi.h> // TODO: remove wifi.h
+#include <map1905/map2.h>
+#include <map1905/maputils.h>
 
 #include "utils.h"
 #include "debug.h"
@@ -32,9 +34,6 @@
 
 #include "map_module.h"
 
-#include <map1905/map2.h>
-#include <map1905/maputils.h>
-
 enum {
 	AP_POLICY_AGENT,
 	/* TODO: filter on cntlr side based on bssid */
@@ -71,7 +70,30 @@ static const struct blobmsg_policy channel_pref_policy_params[__CHANNEL_PREF_POL
 	[CHANNEL_PREF_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING }
 };
 
-static void send_cmdu_cb(struct ubus_request *req,
+enum {
+	CFG_POLICY_AGENT,
+	CFG_POLICY_RADIO,
+	CFG_POLICY_BSSID,
+	__CFG_POLICY_MAX,
+};
+
+static const struct blobmsg_policy config_policy_params[__CFG_POLICY_MAX] = {
+	[CFG_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
+	[CFG_POLICY_RADIO] = { .name = "radio", .type = BLOBMSG_TYPE_STRING },
+	[CFG_POLICY_BSSID] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
+};
+
+enum {
+	RECFG_POLICY_AGENT,
+	__RECFG_POLICY_MAX,
+};
+
+static const struct blobmsg_policy reconfig_policy_params[__RECFG_POLICY_MAX] = {
+	[RECFG_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
+};
+
+
+void send_cmdu_cb(struct ubus_request *req,
 				int type, struct blob_attr *msg)
 {
 	struct json_object *jobj = NULL;
@@ -103,7 +125,7 @@ static void send_cmdu_cb(struct ubus_request *req,
 	json_object_put(jobj);
 }
 
-static int send_cmdu(struct controller *c,
+int send_cmdu(struct controller *c,
 		struct cmdu_cstruct *cmdu_data)
 {
 	char *tlv_data = NULL;
@@ -112,7 +134,7 @@ static int send_cmdu(struct controller *c,
 	int copy_index;
 	struct blob_buf b = { 0 };
 	char dst_addr[18] = { 0 };
-	char tlv_str[512] = { 0 };
+	char *tlv_str = NULL;
 	uint8_t *ss = NULL;
 	uint16_t msgid = 0;
 	uint16_t len;
@@ -127,7 +149,6 @@ static int send_cmdu(struct controller *c,
 
 	blobmsg_add_u32(&b, "type", cmdu_data->message_type);
 	blobmsg_add_string(&b, "egress", cmdu_data->intf_name);
-
 	blobmsg_add_u32(&b, "mid", cmdu_data->message_id);
 
 	hwaddr_ntoa(cmdu_data->origin, dst_addr);
@@ -139,9 +160,16 @@ static int send_cmdu(struct controller *c,
 
 	if (cmdu_data->num_tlvs > 0) {
 		for (i = 0; i < cmdu_data->num_tlvs; i++) {
+			trace("CMDU type: %s\n", map_stringify_tlv_type(*cmdu_data->tlvs[i]));
+
 			len = 0;
 			ss = map_put_tlv_cstruct(cmdu_data->tlvs[i], &len);
 			if (ss) {
+				tlv_str = (char *)calloc((2 * len) + 1, sizeof(char));
+				if (!tlv_str) {
+					free(ss);
+					goto out;
+				}
 				btostr(ss, len, tlv_str);
 				tlv_str_len = 2 * len;
 				tlv_data_len += tlv_str_len;
@@ -155,17 +183,16 @@ static int send_cmdu(struct controller *c,
 
 				copy_index = tlv_data_len - tlv_str_len - 1;
 				memcpy(tlv_data + copy_index, tlv_str, tlv_str_len);
-				memset(tlv_str, 0, sizeof(tlv_str));
 				free(ss);
+				free(tlv_str);
 			}
 		}
-
 		tlv_data[tlv_data_len - 1] = '\0';
 		blobmsg_add_string(&b, "data", tlv_data);
 		if (tlv_data)
 			free(tlv_data);
-	}
 
+	}
 
 	if (ubus_lookup_id(c->ubus_ctx, "map.1905", &id)) {
 		fprintf(stderr, "[%s:%d] not present", __func__, __LINE__);
@@ -181,10 +208,10 @@ static int send_cmdu(struct controller *c,
 					__func__, __LINE__);
 		goto out;
 	}
+
 	test_cmdu(cmdu_data);
 out:
 	blob_buf_free(&b);
-
 	return ret;
 }
 
@@ -274,7 +301,7 @@ static int cntlr_status(struct ubus_context *ctx, struct ubus_object *obj,
 	return UBUS_STATUS_OK;
 }
 
-static int cntlr_ap_caps(struct ubus_context *ctx, struct ubus_object *obj,
+int cntlr_ap_caps(struct ubus_context *ctx, struct ubus_object *obj,
 			struct ubus_request_data *req, const char *method,
 			struct blob_attr *msg)
 {
@@ -310,6 +337,8 @@ static int cntlr_ap_caps(struct ubus_context *ctx, struct ubus_object *obj,
 
 	send_cmdu(c, cmdu_data);
 
+	map_free_cmdu(cmdu_data);
+
 	return 0;
 }
 
@@ -426,9 +455,219 @@ static int cntlr_sta_caps(struct ubus_context *ctx, struct ubus_object *obj,
 
 	send_cmdu(c, cmdu_data);
 
+	map_free_cmdu(cmdu_data);
+
 	return 0;
 }
 
+static int cntlr_teardown_ap(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	return 0;
+}
+
+static int cntlr_reconfig_ap(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	return 0;
+}
+
+
+/*
+ * - One AP Radio Identifier TLV (see section 17.2.3).
+ * - One or more WSC TLV (containing M2).
+ * - Zero or one Default 802.1Q Settings TLV (see section 17.2.49). [Profile-2]
+ * - Zero or one Traffic Separation Policy TLV (see section 17.2.50). [Profile-2]
+*/
+
+struct tlv_ap_radio_identifier *cntlr_gen_config_ap_tlv(struct controller *c,
+		struct cmdu_cstruct *cmdu, uint8_t *hwaddr)
+{
+	struct tlv_ap_radio_identifier *p;
+
+	p = (struct tlv_ap_radio_identifier *)calloc(1,
+			sizeof(struct tlv_ap_radio_identifier));
+	if (!p) {
+		fprintf(stderr, "failed to malloc tlv\n");
+		return NULL;
+	}
+
+	p->tlv_type = MAP_TLV_AP_RADIO_IDENTIFIER;
+	memcpy(p->radio_id, hwaddr, 6);
+
+	return p;
+}
+
+static struct netif_radio *cntlr_radio_to_bssid(struct controller *c, const char *radio)
+{
+	struct netif_radio *r;
+
+	list_for_each_entry(r, &c->radiolist, list) {
+		if (!strncmp(r->name, radio, 16))
+			return r;
+	}
+
+	return NULL;
+}
+
+static int cntlr_config_ap(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct controller *c = container_of(obj, struct controller, obj);
+	struct blob_attr *tb[__CFG_POLICY_MAX];
+	char agent[18] = {0};
+	uint8_t hwaddr[6] = {0};
+	struct cmdu_cstruct *cmdu;
+	struct agent_policy *a, *found = NULL;
+	struct tlv_ap_radio_identifier *p = NULL;
+	struct tlv_default_8021q_settings *p1;
+	struct tlv_traffic_sep_policy *p2;
+	int i, tlv_index = 0;
+
+	blobmsg_parse(config_policy_params, __CFG_POLICY_MAX, tb,
+			blob_data(msg), blob_len(msg));
+
+	if (!tb[CFG_POLICY_AGENT] ||
+			(!tb[CFG_POLICY_RADIO] && !tb[CFG_POLICY_BSSID])) {
+		fprintf(stderr, "STA Capability Query: provide BSSID " \
+				"address in format 11:22:33...\n");
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	strncpy(agent, blobmsg_data(tb[CFG_POLICY_AGENT]), sizeof(agent) - 1);
+	if (!hwaddr_aton(agent, hwaddr))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	list_for_each_entry(a, &c->cfg.policylist, list) {
+		if (!memcmp(hwaddr, a->agent_id, sizeof(hwaddr))) {
+			found = a;
+			break;
+		}
+	}
+
+	if (!found)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	cmdu = (struct cmdu_cstruct *)calloc(1, sizeof(struct cmdu_cstruct));
+	if (!cmdu) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	memcpy(cmdu->origin, hwaddr, sizeof(hwaddr));
+	cmdu->message_type = CMDU_TYPE_AP_AUTOCONFIGURATION_WSC;
+	cmdu->message_id = 1;
+
+	if (tb[CFG_POLICY_BSSID]) {
+		char bssid[18] = {0};
+		uint8_t hwaddr[6] = {0};
+
+		strncpy(bssid, blobmsg_data(tb[CFG_POLICY_BSSID]),
+				sizeof(bssid) - 1);
+		if (!hwaddr_aton(bssid, hwaddr)) {
+			free(cmdu);
+			return UBUS_STATUS_UNKNOWN_ERROR;
+		}
+		p = cntlr_gen_config_ap_tlv(c, cmdu, hwaddr);
+	} else if (tb[CFG_POLICY_RADIO]) {
+		char radio[18] = {0};
+		struct netif_radio *r;
+
+		strncpy(radio, blobmsg_data(tb[CFG_POLICY_RADIO]),
+				sizeof(radio) - 1);
+		r = cntlr_radio_to_bssid(c, radio);
+		if (!r) {
+			free(cmdu);
+			return UBUS_STATUS_UNKNOWN_ERROR;
+		}
+
+		p = cntlr_gen_config_ap_tlv(c, cmdu, r->hwaddr);
+	}
+	/*
+	else { // TODO: all radios
+		struct netif_radio *r;
+
+		list_for_each_entry(r, &c->radiolist, list) {
+			int i;
+
+			cntlr_gen_config_ap_tlv(c, cmdu, r->hwaddr);
+			send_cmdu(c, cmdu);
+
+			for (i = 0; i < cmdu->num_tlvs; i++)
+				free(cmdu->tlvs[i]);
+			free(cmdu->tlvs);
+			cmdu->num_tlvs = 0;
+		}
+	}
+	*/
+	if (!p)
+		goto fail_cmdu;
+
+	cmdu->num_tlvs++;
+
+	p1 = (struct tlv_default_8021q_settings *) calloc(1, sizeof(*p1));
+	if (!p1)
+		goto fail_p;
+
+	cmdu->num_tlvs++;
+	p1->tlv_type = MAP_TLV_DEFAULT_8021Q_SETTINGS;
+	p1->primary_vid = a->pvid;
+	p1->pcp = a->pcp_default;
+
+	p2 = (struct tlv_traffic_sep_policy *) calloc(1, sizeof(*p2));
+	if (!p2)
+		goto fail_p1;
+
+	cmdu->num_tlvs++;
+	p2->tlv_type = MAP_TLV_TRAFFIC_SEPARATION_POLICY;
+	p2->nbr_ssid = c->cfg.num_fh;
+	p2->data = calloc(p2->nbr_ssid, sizeof(*(p2->data)));
+	if (!p2->data)
+		goto fail_p2;
+
+	for (i = 0; i < p2->nbr_ssid; i++) {
+		int len;
+
+		len = strlen((char *)c->cfg.fh[i].ssid);
+
+		p2->data[i].ssid_len = len;
+		p2->data[i].vid = c->cfg.fh[i].vlanid;
+		p2->data[i].ssid = calloc(1, len + 1);
+		if (!p2->data[i].ssid)
+			continue;
+
+		strncpy(p2->data[i].ssid, (char *)c->cfg.fh[i].ssid, len);
+	}
+
+	cmdu->tlvs = (uint8_t **)calloc(cmdu->num_tlvs, sizeof(uint8_t *));
+	if (!cmdu->tlvs)
+		goto fail_p2_data;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p1;
+	cmdu->tlvs[tlv_index++] = (uint8_t *)p2;
+
+	// TODO: ff:ff:ff:ff:ff:ff = send to all agents
+
+	send_cmdu(c, cmdu);
+	map_free_cmdu(cmdu);
+	return 0;
+
+fail_p2_data:
+	free(p2->data);
+fail_p2:
+	free(p2);
+fail_p1:
+	free(p1);
+fail_p:
+	free(p);
+fail_cmdu:
+	free(cmdu);
+	return UBUS_STATUS_UNKNOWN_ERROR;
+}
+
 int cntlr_publish_object(struct controller *c, const char *objname)
 {
 	struct ubus_object *obj;
@@ -439,6 +678,14 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 		UBUS_METHOD("ap_caps", cntlr_ap_caps, ap_caps_policy_params),
 		UBUS_METHOD("sta_caps", cntlr_sta_caps, sta_caps_policy_params),
 		UBUS_METHOD("channels", cntlr_channel_pref, channel_pref_policy_params)
+		/*
+		UBUS_METHOD("teardown_ap", cntlr_teardown_ap,
+				config_policy_params),
+		UBUS_METHOD("reconfig_ap", cntlr_reconfig_ap,
+				reconfig_policy_params),
+		UBUS_METHOD("config_ap", cntlr_config_ap, config_policy_params),
+		*/
+
 	};
 	int num_methods = ARRAY_SIZE(m);
 	int ret;
@@ -482,8 +729,11 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 
 void cntlr_remove_object(struct controller *c)
 {
-	if (c->ubus_ctx && c->obj.id != OBJECT_INVALID)
+	if (c->ubus_ctx && c->obj.id != OBJECT_INVALID) {
 		ubus_remove_object(c->ubus_ctx, &c->obj);
+		free(c->obj.type);
+		free((void *)c->obj.methods);
+	}
 }
 
 int cntlr_register_module(struct controller *c)
diff --git a/src/core/cntlr_ubus.h b/src/core/cntlr_ubus.h
index 42b326a4d0466e1a0a54b20aa9f1699fed9a8628..9df1484004fe37d131dc9826f395e8b862003cfe 100644
--- a/src/core/cntlr_ubus.h
+++ b/src/core/cntlr_ubus.h
@@ -12,9 +12,8 @@
 
 extern int cntlr_publish_object(struct controller *c, const char *objname);
 extern void cntlr_remove_object(struct controller *c);
-
-
 extern int cntlr_register_module(struct controller *c);
 
-
+void send_cmdu_cb(struct ubus_request *req, int type, struct blob_attr *msg);
+int send_cmdu(struct controller *c, struct cmdu_cstruct *cmdu_data);
 #endif /* CNTLR_UBUS_H */
diff --git a/src/core/config.c b/src/core/config.c
index c996dfa82705d36ca5f43c3d38a792c2a47da137..bb72a65b3b619cd8b878fea94c0827cc0eb7bf2d 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -69,7 +69,7 @@ void cntlr_config_dump(struct controller_config *c)
 	}
 
 	dbg("Agents policy: Default\n");
-	dbg("  Id                    : " MACFMT "\n", MAC2STR(c->apolicy.id));
+	dbg("  Id                    : " MACFMT "\n", MAC2STR(c->apolicy.agent_id));
 	dbg("  Steer-policy          : %d\n", c->apolicy.policy);
 	dbg("  Util-threshold        : %d\n", c->apolicy.util_threshold);
 	dbg("  RCPI-threshold        : %d\n", c->apolicy.rcpi_threshold);
@@ -149,6 +149,8 @@ static int cntlr_config_get_credentials(struct controller_config *c,
 		CRED_SEC,
 		CRED_KEY,
 		CRED_VLAN,
+		CRED_MAP,
+		CRED_D_BSTA,
 		NUM_CREDS,
 	};
 	const struct uci_parse_option opts[] = {
@@ -157,21 +159,24 @@ static int cntlr_config_get_credentials(struct controller_config *c,
 		[CRED_SEC] = { .name = "encryption", .type = UCI_TYPE_STRING },
 		[CRED_KEY] = { .name = "key", .type = UCI_TYPE_STRING },
 		[CRED_VLAN] = { .name = "vlan", .type = UCI_TYPE_STRING },
+		[CRED_MAP] = { .name = "multi_ap", .type = UCI_TYPE_STRING },
+		[CRED_D_BSTA] = { .name = "disallow_bsta", .type = UCI_TYPE_STRING },
 	};
 	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++];
+		cred->mode = WIFI_MODE_AP;
 	} else {
 		if (c->num_bk >= 2)
 			return -1;
 
 		cred = &c->bk[c->num_bk++];
+		cred->mode = WIFI_MODE_STA;
 	}
 
 	uci_parse_section(s, opts, NUM_CREDS, tb);
@@ -184,7 +189,7 @@ static int cntlr_config_get_credentials(struct controller_config *c,
 	}
 
 	if (tb[CRED_SSID])
-		memcpy(cred->ssid, tb[CRED_SSID]->v.string, 32);
+		strncpy((char *) cred->ssid, tb[CRED_SSID]->v.string, 32);
 
 	if (tb[CRED_SEC]) {
 		const char *sec = tb[CRED_SEC]->v.string;
@@ -200,11 +205,17 @@ static int cntlr_config_get_credentials(struct controller_config *c,
 	}
 
 	if (tb[CRED_KEY])
-		memcpy(cred->key, tb[CRED_KEY]->v.string, 63);
+		strncpy((char *) cred->key, tb[CRED_KEY]->v.string, 63);
 
 	if (tb[CRED_VLAN])
 		cred->vlanid = atoi(tb[CRED_VLAN]->v.string);
 
+	if (tb[CRED_MAP])
+		cred->multi_ap = atoi(tb[CRED_MAP]->v.string);
+
+	if (tb[CRED_D_BSTA])
+		cred->disallow_bsta = atoi(tb[CRED_D_BSTA]->v.string);
+
 	return 0;
 }
 
@@ -212,7 +223,7 @@ static int cntlr_config_get_agent_policy(struct controller_config *c,
 						struct uci_section *s)
 {
 	enum {
-		POL_ID,
+		POL_AGENT_ID,
 		POL_STEER,
 		POL_UTIL_TH,
 		POL_RCPI_TH,
@@ -230,7 +241,7 @@ static int cntlr_config_get_agent_policy(struct controller_config *c,
 		NUM_POLICIES,
 	};
 	const struct uci_parse_option opts[] = {
-		{ .name = "id", .type = UCI_TYPE_STRING },
+		{ .name = "agent_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 },
@@ -247,82 +258,87 @@ static int cntlr_config_get_agent_policy(struct controller_config *c,
 		{ .name = "disallow_bsta_p2", .type = UCI_TYPE_STRING },
 	};
 	struct uci_option *tb[NUM_POLICIES];
+	struct agent_policy *a;
+
+	a = calloc(1, sizeof(*a));
+	if (!a)
+		return -1;
 
 	uci_parse_section(s, opts, NUM_POLICIES, tb);
 
-	if (tb[POL_ID]) {
-		const char *val = tb[POL_ID]->v.string;
+	if (tb[POL_AGENT_ID]) {
+		const char *val = tb[POL_AGENT_ID]->v.string;
 
-		hwaddr_aton(val, c->apolicy.id);
+		hwaddr_aton(val, a->agent_id);
 	}
 
 	if (tb[POL_STEER])
-		c->apolicy.policy = atoi(tb[POL_STEER]->v.string);
+		a->policy = atoi(tb[POL_STEER]->v.string);
 
 	if (tb[POL_UTIL_TH])
-		c->apolicy.util_threshold = atoi(tb[POL_UTIL_TH]->v.string);
+		a->util_threshold = atoi(tb[POL_UTIL_TH]->v.string);
 
 	if (tb[POL_RCPI_TH])
-		c->apolicy.rcpi_threshold = atoi(tb[POL_RCPI_TH]->v.string);
+		a->rcpi_threshold = atoi(tb[POL_RCPI_TH]->v.string);
 
 	if (tb[POL_RPT_SCAN]) {
-		c->apolicy.report_scan =
+		a->report_scan =
 			atoi(tb[POL_RPT_SCAN]->v.string) == 1 ? true : false;
 	}
 
 	if (tb[POL_RPT_ASSOC_FAILS]) {
-		c->apolicy.report_sta_assocfails =
+		a->report_sta_assocfails =
 			atoi(tb[POL_RPT_ASSOC_FAILS]->v.string) == 1 ?
 					true : false;
 	}
 
 	if (tb[POL_RPT_METRIC_PERIODIC]) {
-		c->apolicy.report_metric_periodic =
+		a->report_metric_periodic =
 				atoi(tb[POL_RPT_METRIC_PERIODIC]->v.string);
 	}
 
 	if (tb[POL_RPT_RCPI_TH]) {
-		c->apolicy.report_rcpi_threshold =
+		a->report_rcpi_threshold =
 				atoi(tb[POL_RPT_RCPI_TH]->v.string);
 	}
 
 	if (tb[POL_RPT_UTIL_TH]) {
-		c->apolicy.report_util_threshold =
+		a->report_util_threshold =
 				atoi(tb[POL_RPT_UTIL_TH]->v.string);
 	}
 
 	if (tb[POL_INC_STA_STATS]) {
-		c->apolicy.include_sta_stats =
+		a->include_sta_stats =
 			atoi(tb[POL_INC_STA_STATS]->v.string) == 1 ?
 					true : false;
 	}
 
 	if (tb[POL_INC_STA_METRIC]) {
-		c->apolicy.include_sta_metric =
+		a->include_sta_metric =
 			atoi(tb[POL_INC_STA_METRIC]->v.string) == 1 ?
 					true : false;
 	}
 
 	if (tb[POL_PVID])
-		c->apolicy.pvid = atoi(tb[POL_PVID]->v.string);
+		a->pvid = atoi(tb[POL_PVID]->v.string);
 
 	if (tb[POL_PCP_DEFAULT])
-		c->apolicy.pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);
+		a->pcp_default = atoi(tb[POL_PCP_DEFAULT]->v.string);
 
 
 	if (tb[POL_DISALLOW_BSTA_P1]) {
-		c->apolicy.disallow_bsta_p1 =
+		a->disallow_bsta_p1 =
 			atoi(tb[POL_DISALLOW_BSTA_P1]->v.string) == 1 ?
 					true : false;
 	}
 
 	if (tb[POL_DISALLOW_BSTA_P2]) {
-		c->apolicy.disallow_bsta_p2 =
+		a->disallow_bsta_p2 =
 			atoi(tb[POL_DISALLOW_BSTA_P2]->v.string) == 1 ?
 					true : false;
 	}
 
-
+	list_add(&a->list, &c->policylist);
 	return 0;
 }
 
@@ -357,3 +373,22 @@ int cntlr_config_reload(struct controller_config *cfg)
 	uci_free_context(ctx);
 	return 0;
 }
+
+int clean_agent_policies(struct controller_config *cfg)
+{
+	struct agent_policy *p, *tmp;
+
+	list_for_each_entry_safe(p, tmp, &cfg->policylist, list) {
+		list_del(&p->list);
+		free(p);
+	}
+
+	return 0;
+}
+
+int cntlr_config_clean(struct controller_config *cfg)
+{
+	clean_agent_policies(cfg);
+
+	return 0;
+}
diff --git a/src/core/config.h b/src/core/config.h
index c1b752c7ad80ba7a83b3c3921b0461a19a6217a9..792e25bd2732711cbe089a2893755c3c05b743f5 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -44,6 +44,10 @@ struct iface_credential {
 	uint8_t bssid[6];
 	uint8_t ssid[33];
 	uint16_t vlanid;
+	uint8_t multi_ap;
+	struct list_head *list;
+	enum wifi_mode mode;
+	uint8_t disallow_bsta;
 };
 
 enum agent_steer_policy {
@@ -53,7 +57,7 @@ enum agent_steer_policy {
 };
 
 struct agent_policy {
-	uint8_t id[6];                    /* ieee1905 AL macaddress */
+	uint8_t agent_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 */
@@ -95,5 +99,5 @@ struct controller;
 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 cntlr_config_clean(struct controller_config *cfg);
 #endif
diff --git a/src/utils/debug.c b/src/utils/debug.c
index 0c3e26db97a4ee6ace9fd3c99d2879f70433e4dd..b10ecb1dd45eeeea093b38e6fda3f58edff0032a 100644
--- a/src/utils/debug.c
+++ b/src/utils/debug.c
@@ -220,5 +220,6 @@ void log_cmdu(int level, void *var)
 			continue;
 
 		log_test(level, btlv, len);
+		free(btlv);
 	}
 }