diff --git a/README.md b/README.md
index f98aa31ebb7f5637fe80f97b9eafe5a460e15079..d5bea69793de14b667669e08673a1a9d98dd7bdd 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,8 @@ config controller 'controller'
 	option debug '0'
 	option enable_sta_steer '0'
 	option enable_bsta_steer '0'
+	option use_bcn_metrics '0'
+	option use_usta_metrics '0'
 	option primary_vid '1'
 	option primary_pcp '0'
 
@@ -277,20 +279,25 @@ all agents to be reconfigured.
 
 ```
 root@iopsys:~# ubus -v list map.controller
-'map.controller' @2d8adaa3
+'map.controller' @601d80a0
 	"status":{}
 	"ap_caps":{"agent":"String"}
 	"sta_caps":{"agent":"String","sta":"String","bssid":"String"}
-	"channels":{"agent":"String"}
+	"channel_pref":{"agent":"String"}
 	"bk_steer":{"agent":"String","bssid":"String","channel":"Integer","op_class":"Integer","bksta":"String"}
 	"agent_policy":{"agent":"String","radiolist":"Array","bsslist":"Array"}
-	"channel":{"agent":"String","radio_id":"String","class_id":"Integer","channel":"Array","preference":"Integer","transmit_power":"Integer"}
+	"channel_select":{"agent":"String","radio_id":"String","class_id":"Integer","channel":"Array","preference":"Integer","transmit_power":"Integer"}
 	"reconfig_ap":{"agent":"String"}
-	"client_steering":{"agent":"String","src_bssid":"String","sta":"Array","target_bssid":"Array","steer_timeout":"Integer","btm_timeout":"Integer","steer_req_mode":"Boolean"}
+	"steer":{"agent":"String","src_bssid":"String","sta":"Array","target_bssid":"Array","steer_timeout":"Integer","btm_timeout":"Integer","steer_req_mode":"Boolean"}
 	"client_assoc_cntlr":{"agent":"String","bssid":"String","assoc_cntl_mode":"Integer","assoc_valid_timeout":"Integer","stalist":"Array"}
 	"ap_metric_query":{"agent":"String","bsslist":"Array","radiolist":"Array"}
 	"scan":{"agent":"String","radio":"Array","channel":"Array"}
 	"sta_metric_query":{"agent":"String","sta":"String"}
+	"unassoc_sta_lm_query":{"agent":"String","opclass":"Integer","metrics":"Array"}
+	"bcn_metrics_query":{"agent":"String","sta":"String","opclass":"Integer","channel":"Integer","bssid":"String","reporting_detail":"Integer","ssid":"String","channel_report":"Array","request_element":"Array"}
 	"bk_caps":{"agent":"String"}
 	"topology_query":{"agent":"String"}
+	"cac_req":{"agent":"String","radiolist":"Array"}
+	"cac_term":{"agent":"String","radiolist":"Array"}
+	"higher_layer_data":{"agent":"String","protocol":"Integer","data":"String"}
 ```
diff --git a/src/cntlr.c b/src/cntlr.c
index 3679adedac5315c7ad6342272138f42b4360897d..17cfda6fb283cfdaa85f10069420d1db0fb4eabe 100644
--- a/src/cntlr.c
+++ b/src/cntlr.c
@@ -81,7 +81,7 @@ static void cntlr_terminate(struct controller *c)
 	exit(0);
 }
 
-/* find node by macaddress */
+/* find netif by macaddress */
 struct netif_iface *find_interface_by_mac(struct controller *c,
 		struct netif_radio *r, uint8_t *hwaddr)
 {
@@ -95,7 +95,45 @@ struct netif_iface *find_interface_by_mac(struct controller *c,
 	return NULL;
 }
 
-/* find node by macaddress */
+/* find radio by ssid */
+struct netif_radio *find_radio_by_ssid(struct controller *c,
+		struct node *n, char *ssid)
+{
+	struct netif_radio *r;
+
+	list_for_each_entry(r, &n->radiolist, list) {
+
+		struct netif_iface *p;
+
+		list_for_each_entry(p, &r->iflist, list) {
+			if (!memcmp(p->ssid, ssid, 33))
+				return r;
+		}
+	}
+
+	return NULL;
+}
+
+/* find netif by ssid */
+struct netif_iface *find_interface_by_ssid(struct controller *c,
+		struct node *n, char *ssid)
+{
+	struct netif_radio *r;
+
+	list_for_each_entry(r, &n->radiolist, list) {
+
+		struct netif_iface *p;
+
+		list_for_each_entry(p, &r->iflist, list) {
+			if (!memcmp(p->ssid, ssid, 33))
+				return p;
+		}
+	}
+
+	return NULL;
+}
+
+/* find radio by node */
 struct netif_radio *find_radio_by_node(struct controller *c, struct node *n,
 		uint8_t *radio)
 {
@@ -122,7 +160,7 @@ struct node *find_node_by_mac(struct controller *c, uint8_t *mac)
 	return NULL;
 }
 
-/* find node by macaddress */
+/* find sta by macaddress */
 struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac)
 {
 	struct sta *s;
@@ -236,7 +274,6 @@ static void cntlr_bcn_metrics_parse(struct uloop_timeout *t)
 	struct bcn_metrics *best = NULL, *b;
 	struct node *n = s->fh->agent;
 	struct controller *c = n->cntlr;
-	int i;
 
 	dbg("%s %d for "MACFMT" attached to bssid " MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), MAC2STR(s->bssid));
 	dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid));
@@ -309,6 +346,7 @@ struct sta *cntlr_add_sta(struct controller *c, uint8_t *macaddr)
 		return NULL;
 
 	INIT_LIST_HEAD(&s->bcnlist);
+	INIT_LIST_HEAD(&s->unassoclist);
 	memcpy(s->macaddr, macaddr, 6);
 	list_add(&s->list, &c->stalist);
 	s->bcn_metrics_timer.cb = cntlr_bcn_metrics_parse;
@@ -342,6 +380,27 @@ static int cntlr_monitor_sta(struct controller *c, uint8_t *sta,
 }
 #endif
 
+#if 0
+static void forall_node_get_usta_metrics(struct controller *c)
+{
+	struct sta *s;
+
+	list_for_each_entry(s, &c->stalist, list) {
+		;
+		struct cmdu_buff *cmdu;
+
+		/* TODO: */
+		//cmdu = cntlr_gen_unassoc_sta_metric_query(c, s->fh->agent->alid, s->macaddr,
+		// opclass, num_metrics, metrics);
+		if (!cmdu)
+			continue;
+
+		send_cmdu(c, cmdu);
+		cmdu_free(cmdu);
+	}
+}
+#endif
+
 static void forall_node_get_bcn_metrics(struct controller *c)
 {
 	struct sta *s;
@@ -646,6 +705,16 @@ void free_bcn_metrics(struct controller *c, struct sta *s)
 	}
 }
 
+void free_usta_metrics(struct controller *c, struct sta *s)
+{
+	struct una_sta_metrics *u, *tmp;
+
+	list_for_each_entry_safe(u, tmp, &s->unassoclist, list) {
+		list_del(&u->list);
+		free(u);
+	}
+}
+
 void free_watchnode_cleanup(struct controller *c, struct watchnode *wn)
 {
 	uloop_timeout_cancel(&wn->scan_timer);
@@ -1016,6 +1085,9 @@ static void cntlr_periodic_run(struct uloop_timeout *t)
 
 	forall_node_get_bcn_metrics(c);
 
+	/* TODO: */
+	//forall_node_get_usta_metrics(c);
+
 	/* TODO: update only when a node is added or removed */
 	//forall_node_update_neighbors(c);
 
@@ -1151,6 +1223,7 @@ static void controller_subscribe_for_cmdus(struct controller *c)
 			CMDU_BEACON_METRICS_RESPONSE,
 			CMDU_AP_METRICS_RESPONSE,
 			CMDU_ASSOC_STA_LINK_METRICS_RESPONSE,
+			CMDU_UNASSOC_STA_LINK_METRIC_RESPONSE,
 			CMDU_CHANNEL_SCAN_REQUEST,
 			CMDU_CHANNEL_SCAN_REPORT,
 			CMDU_CLIENT_DISASSOCIATION_STATS,
diff --git a/src/cntlr.h b/src/cntlr.h
index 3cb30b5c748ad96b7fed28287d652f14f0ebf7d4..fc469573a7ab96148d483271ad8b08c5432365b1 100644
--- a/src/cntlr.h
+++ b/src/cntlr.h
@@ -40,6 +40,16 @@ struct bcn_metrics {
 	struct list_head list;
 };
 
+struct una_sta_metrics {
+	/* node the measurement has been done on */
+	struct node *agent;
+	uint8_t channel;
+	uint32_t time_delta;
+	uint8_t ul_rcpi;
+
+	struct list_head list;
+};
+
 /*
 {"sta_macaddr":"ec:6c:9a:52:ac:bb","num_element":1,"element":0,"op_class":115,"channel":36,"rcpi":162,"rsni":90,"bssid":"0a:10:00:00:00:03"}
 */
@@ -58,6 +68,8 @@ struct sta {
 
 	struct list_head bcnlist;
 
+	struct list_head unassoclist;
+
 	struct netif_iface *fh; /* the AP interface the sta is connected at */
 
 	enum device_type type;
@@ -166,6 +178,7 @@ struct node {
 	struct uloop_timeout refresh_timer;
 	struct ubus_event_handler evh;
 	struct agent_policy *ap;
+	uint8_t ap_cap;
 	struct list_head radiolist;        /** list of netif_radio */
 	//struct list_head stalist;
 	struct list_head list;
@@ -215,6 +228,15 @@ struct sta_channel_report {
 	uint8_t channel[128];
 };
 
+#define MAX_UNASSOC_STAMACS 10
+struct unassoc_sta_metric {
+	uint8_t channel;
+	uint8_t num_sta;
+	struct {
+		uint8_t macaddr[6];
+	} sta[MAX_UNASSOC_STAMACS];
+};
+
 struct sta_error_response {
 	uint8_t sta_mac[6];
 	uint8_t response;
@@ -224,6 +246,10 @@ struct sta_error_response {
 
 extern int start_controller(void);
 struct node *alloc_node_init(struct controller *c, uint8_t *hwaddr);
+struct netif_iface *find_interface_by_ssid(struct controller *c,
+		struct node *n, char *ssid);
+struct netif_radio *find_radio_by_ssid(struct controller *c,
+		struct node *n, char *ssid);
 struct netif_radio *find_radio_by_node(struct controller *c, struct node *n,
 		uint8_t *radio);
 struct node *find_node_by_mac(struct controller *c, uint8_t *mac);
@@ -237,4 +263,5 @@ struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac);
 struct netif_iface *cntlr_get_fbss_by_mac(struct controller *c, struct node *n,
 		uint8_t *mac);
 void free_bcn_metrics(struct controller *c, struct sta *s);
+void free_usta_metrics(struct controller *c, struct sta *s);
 #endif /* CNTLR_H */
diff --git a/src/cntlr_cmdu.c b/src/cntlr_cmdu.c
index 699499779335ef3ad70380125748dba70a75f44b..fee8257f5005f1013eeb0954a3699f364be9a69a 100644
--- a/src/cntlr_cmdu.c
+++ b/src/cntlr_cmdu.c
@@ -333,6 +333,49 @@ out:
 	return NULL;
 }
 
+struct cmdu_buff *cntlr_gen_unassoc_sta_metric_query(struct controller *c,
+		uint8_t *origin, uint8_t opclass,
+		uint8_t num_metrics, struct unassoc_sta_metric *metrics)
+{
+	int ret;
+	uint16_t mid = 0;
+	struct cmdu_buff *frm;
+	struct node *n;
+
+	/* A Multi-AP Controller shall not send an Unassociated
+	 * STA Link Metrics Query message to a Multi-AP Agent that
+	 * does not indicate support for Unassociated STA Link
+	 * Metrics in the AP Capability TLV.
+	 */
+	n = find_node_by_mac(c, origin);
+	dbg("%s %d ap_cap = 0x%02x\n", __func__, __LINE__, n->ap_cap);
+	if (!(n->ap_cap & (UNASSOC_STA_REPORTING_ONCHAN | UNASSOC_STA_REPORTING_OFFCHAN))) {
+		dbg("%s: Unassoc STA metric not supported by " MACFMT "\n",
+		    __func__, MAC2STR(origin));
+		return NULL;
+	}
+
+	frm = cmdu_alloc_simple(CMDU_UNASSOC_STA_LINK_METRIC_QUERY, &mid);
+	if (!frm) {
+		dbg("%s: -ENOMEM\n", __func__);
+		return NULL;
+	}
+
+	memcpy(frm->origin, origin, 6);
+
+	/* Unassociated STA link metrics query TLV */
+	ret = cntlr_gen_unassociated_sta_link_metrics(c, frm,
+				opclass, num_metrics, metrics);
+	if (ret)
+		goto out;
+
+	cmdu_put_eom(frm);
+	return frm;
+
+out:
+	return NULL;
+}
+
 struct cmdu_buff *cntlr_gen_bk_caps_query(struct controller *c,
 		uint8_t *origin)
 {
diff --git a/src/cntlr_cmdu.h b/src/cntlr_cmdu.h
index 16f785d442e1341b22a651609a1d97b06ac652b0..bc718bf530aa7e47437205ad01c4d1c0704d9f7d 100644
--- a/src/cntlr_cmdu.h
+++ b/src/cntlr_cmdu.h
@@ -34,6 +34,9 @@ struct cmdu_buff *cntlr_gen_policy_config_req(struct controller *c,
 		int num_bss, uint8_t *bsslist);
 struct cmdu_buff *cntlr_gen_sta_metric_query(struct controller *c,
 		uint8_t *origin, uint8_t *sta);
+struct cmdu_buff *cntlr_gen_unassoc_sta_metric_query(struct controller *c,
+		uint8_t *origin, uint8_t opclass,
+		uint8_t num_metrics, struct unassoc_sta_metric *metrics);
 struct cmdu_buff *cntlr_gen_ap_autoconfig_search(struct controller *c,
 		uint8_t profile, uint8_t band);
 struct cmdu_buff *cntlr_gen_ap_autoconfig_response(struct controller *c,
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index 40e52bc9409af00df266a7cf8822a47d71beda97..d20a2b962be8e030774f8ffbd80082659bf2b4c3 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -649,6 +649,13 @@ int handle_ap_caps_report(void *cntlr, struct cmdu_buff *cmdu)
 	if (!n)
 		return -1;
 
+	/* AP Capability TLV */
+	if (tv[0][0]) {
+		struct tlv_ap_cap *p = (struct tlv_ap_cap *)tv[0][0]->data;
+
+		dbg("%s %d AP capability is 0x%02x\n", __func__, __LINE__, p->cap);
+		n->ap_cap = p->cap;
+	}
 
 	index = 0;
 	/* Parse AP Radio Basic Capabilities TLV */
@@ -758,6 +765,109 @@ int handle_ap_metrics_response(void *cntlr, struct cmdu_buff *cmdu)
 	return 0;
 }
 
+static int cntlr_request_usta_metrics(struct controller *c,
+		struct node *n, struct sta *s)
+{
+	struct cmdu_buff *usta_cmdu = NULL;
+	struct unassoc_sta_metric metrics[1] = {};
+	struct netif_radio *r;
+	struct netif_iface *fh;
+	int j;
+
+	trace("%s: --->\n", __func__);
+
+	if (!n || !s)
+		return -1;
+
+	trace("%s %d ap_cap = 0x%02x\n", __func__, __LINE__, n->ap_cap);
+
+	if (n->ap_cap & UNASSOC_STA_REPORTING_OFFCHAN)
+		/* TODO: off channel measurements */
+		dbg("%s: Offchannel Unassoc STA metric not supported");
+
+	if (n->ap_cap & UNASSOC_STA_REPORTING_ONCHAN) {
+
+		r = find_radio_by_ssid(c, n, s->fh->ssid);
+		fh = find_interface_by_ssid(c, n, s->fh->ssid);
+
+		if (!r || !fh)
+			return -1;
+
+		for (j = 0; j < r->num_opclass; j++) {
+			metrics[0].channel = fh->channel;
+			metrics[0].num_sta = 1;
+			memcpy(metrics[0].sta[0].macaddr, s->macaddr, 6);
+
+			usta_cmdu = cntlr_gen_unassoc_sta_metric_query(c,
+					n->alid, r->opclass[j], 1, metrics);
+
+			if (usta_cmdu) {
+				send_cmdu(c, usta_cmdu);
+				cmdu_free(usta_cmdu);
+			}
+		}
+
+	} else {
+		dbg("%s: Unassoc STA metric not supported by " MACFMT "\n",
+		    __func__, MAC2STR(n->alid));
+	}
+
+	return 0;
+}
+
+static int cntlr_request_bcn_metrics(struct controller *c, struct sta *s)
+{
+	struct cmdu_buff *bcn_cmdu;
+	uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	uint8_t *opclasses, num_opclasses;
+	int i;
+
+	dbg("%s %d band = %d\n", __func__, __LINE__, s->fh->band);
+	if (s->fh->band == BAND_5) {
+		num_opclasses = 4;
+		opclasses = calloc(1, num_opclasses);
+		if (!opclasses)
+			return -1;
+
+		// 115, 118, 121, 124
+		opclasses[0] = 115;
+		opclasses[1] = 118;
+		opclasses[2] = 121;
+		opclasses[3] = 124;
+	} else if (s->fh->band == BAND_2) {
+		num_opclasses = 1;
+		opclasses = calloc(1, num_opclasses);
+		if (!opclasses)
+			return -1;
+
+		// 81
+		opclasses[0] = 81;
+	} else
+		return -1;
+
+
+	/* TODO: use one query with num_report & report instead
+	 *       of sending separate bmq for each opclass
+	 *       as in ubus call: "channel_report":[{"opclass":
+	 *       81, "channels": [1, 6, 13]}, {"opclass":82, ...
+	 */
+	for (i = 0; i < num_opclasses; i++) {
+		bcn_cmdu = cntlr_gen_beacon_metrics_query(c,
+				s->fh->agent->alid,
+				s->macaddr, opclasses[i], 0,
+				wildcard, 0, s->fh->ssid, 0,
+				NULL, 0, 0);
+		if (bcn_cmdu) {
+			send_cmdu(c, bcn_cmdu);
+			cmdu_free(bcn_cmdu);
+		}
+	}
+
+	free(opclasses);
+
+	return 0;
+}
+
 int handle_sta_link_metrics_response(void *cntlr, struct cmdu_buff *cmdu)
 {
 	int i;
@@ -805,82 +915,64 @@ int handle_sta_link_metrics_response(void *cntlr, struct cmdu_buff *cmdu)
 		}
 
 		if (s->type == IEEE1905 && s->ul_rcpi < c->cfg.rcpi_threshold) {
-			struct cmdu_buff *bcn_cmdu;
-			uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 			if (!c->cfg.enable_bsta_steer) {
-				trace("|%s:%d| rcpi below %d, but will not query for beacon \
-				       metrics to steer as 'enable_bsta_steer' not set!\n",
+				trace("|%s:%d| rcpi below %d, but will not query for any \
+				       metrics as 'enable_bsta_steer' is not set!\n",
 				       __func__, __LINE__, c->cfg.rcpi_threshold);
 				return 0;
 			}
 
-			free_bcn_metrics(c, s);
-			dbg("%s %d\n", __func__, __LINE__);
-			bcn_cmdu = cntlr_gen_beacon_metrics_query(c,
+			if (!c->cfg.use_bcn_metrics) {
+				struct cmdu_buff *bcn_cmdu;
+				uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+				free_bcn_metrics(c, s);
+				dbg("%s %d\n", __func__, __LINE__);
+				bcn_cmdu = cntlr_gen_beacon_metrics_query(c,
 					s->fh->agent->alid, s->macaddr, 0, 0,
 					wildcard, 0, s->fh->ssid, 0, NULL, 0, 0);
-			if (bcn_cmdu) {
-				send_cmdu(c, bcn_cmdu);
-				cmdu_free(bcn_cmdu);
+				if (bcn_cmdu) {
+					send_cmdu(c, bcn_cmdu);
+					cmdu_free(bcn_cmdu);
+				}
 			}
+
+			if (!c->cfg.use_usta_metrics) {
+				/* TODO: Unassociated bsta metrics (?)
+				 * free_usta_metrics(c, s);
+				 * cntlr_gen_unassoc_sta_metric_query
+				 */
+			}
+
 		} else if (s->type == NON_IEEE1905 && s->ul_rcpi < c->cfg.rcpi_threshold) {
-			struct cmdu_buff *bcn_cmdu;
-			int i;
-			uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-			uint8_t *opclasses, num_opclasses;
 
 			if (!c->cfg.enable_sta_steer) {
-				trace("|%s:%d| rcpi below %d, but will not query for beacon \
-				       metrics to steer as 'enable_sta_steer' not set!\n",
-				       __func__, __LINE__, c->cfg.rcpi_threshold);
+				trace("|%s:%d| rcpi below %d, but will not query for any \
+				      metrics as 'enable_sta_steer' is not set!\n",
+				      __func__, __LINE__, c->cfg.rcpi_threshold);
 				return 0;
 			}
 
-			dbg("%s %d band = %d\n", __func__, __LINE__, s->fh->band);
-			if (s->fh->band == BAND_5) {
-				num_opclasses = 4;
-				opclasses = calloc(1, num_opclasses);
-				if (!opclasses)
-					return -1;
-
-				// 115, 118, 121, 124
-				opclasses[0] = 115;
-				opclasses[1] = 118;
-				opclasses[2] = 121;
-				opclasses[3] = 124;
-			} else if (s->fh->band == BAND_2) {
-				num_opclasses = 1;
-				opclasses = calloc(1, num_opclasses);
-				if (!opclasses)
-					return -1;
-
-				// 81
-				opclasses[0] = 81;
-			} else
-				return -1;
+			/* Get bcn metrics */
+			if (!c->cfg.use_bcn_metrics) {
+				trace("%s %d\n", __func__, __LINE__);
 
-			free_bcn_metrics(c, s);
-			/* TODO: use one query with num_report & report instead
-			 *       of sending separate bmq for each opclass
-			 *       as in ubus call: "channel_report":[{"opclass":
-			 *       81, "channels": [1, 6, 13]}, {"opclass":82, ...
-			 */
-			for (i = 0; i < num_opclasses; i++) {
-				bcn_cmdu = cntlr_gen_beacon_metrics_query(c,
-						s->fh->agent->alid,
-						s->macaddr, opclasses[i], 0,
-						wildcard, 0, s->fh->ssid, 0,
-						NULL, 0, 0);
-				if (bcn_cmdu) {
-					send_cmdu(c, bcn_cmdu);
-					cmdu_free(bcn_cmdu);
-				}
+				free_bcn_metrics(c, s);
+				cntlr_request_bcn_metrics(c, s);
+				uloop_timeout_set(&s->bcn_metrics_timer, 2 * 1000);
 			}
 
-			free(opclasses);
-			trace("%s %d\n", __func__, __LINE__);
-			uloop_timeout_set(&s->bcn_metrics_timer, 2 * 1000);
+			/* Get usta metrics for each agent in mesh */
+			if (!c->cfg.use_usta_metrics) {
+				struct node *no;
+
+				trace("%s %d\n", __func__, __LINE__);
+				free_usta_metrics(c, s);
+				list_for_each_entry(no, &c->nodelist, list) {
+					cntlr_request_usta_metrics(c, no, s);
+				}
+			}
 		}
 		trace("%s %d\n", __func__, __LINE__);
 	}
@@ -889,13 +981,6 @@ trace("%s %d\n", __func__, __LINE__);
 	return 0;
 }
 
-int handle_unassoc_sta_link_metrics_response(void *cntlr,
-		struct cmdu_buff *cmdu)
-{
-	trace("%s: --->\n", __func__);
-	return 0;
-}
-
 int cntlr_send_1905_acknowledge(void *cntlr,
 	struct cmdu_buff *rx_cmdu,
 	struct sta_error_response *sta_resp, uint32_t sta_count)
@@ -916,126 +1001,183 @@ int cntlr_send_1905_acknowledge(void *cntlr,
 	return 0;
 }
 
-int cntlr_check_sta_steer(struct controller *c, struct sta *s)
+struct una_sta_metrics *cntlr_find_usta_metric(struct controller *c,
+		struct sta *s, uint8_t *mac)
 {
-	struct bcn_metrics *best = NULL, *b;
+	struct una_sta_metrics *u;
+
+	dbg("%s %d\n", __func__, __LINE__);
+	list_for_each_entry(u, &s->unassoclist, list) {
+		dbg("%s %d mac "MACFMT" alid " MACFMT"\n", __func__, __LINE__,
+				MAC2STR(u->agent->alid), MAC2STR(mac));
+		if (!memcmp(u->agent->alid, mac, 6))
+			return u;
+	}
+
+	return NULL;
+}
+
+#define USTA_STEER_UL_RCPI_DELTA 10
+
+/* use unassociated STA measurements to steer */
+void cntlr_check_usta_steer(struct controller *c, struct sta *s)
+{
+	struct una_sta_metrics *best = NULL, *u;
 	struct node *n = s->fh->agent;
+	struct netif_iface *best_fh;
 
-	dbg("%s %d for "MACFMT" attached to bssid " MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), MAC2STR(s->bssid));
-	dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid));
+	dbg("%s %d\n", __func__, __LINE__);
+
+	if (!c->cfg.use_usta_metrics) {
+		dbg("%s %d Will not use unassociated STA metrics \
+		    data to steer\n", __func__, __LINE__);
+		return;
+	}
+
+	dbg("%s %d for "MACFMT" attached to bssid " MACFMT " node = " \
+	    MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr),
+	    MAC2STR(s->bssid), MAC2STR(n->alid));
+
+	list_for_each_entry(u, &s->unassoclist, list) {
+		dbg("%s %d check usta node "MACFMT"\n",
+		    __func__, __LINE__, MAC2STR(u->agent->alid));
 
-	//for (i = 0; i < s->num_bcn_metrics; i++) {
-	list_for_each_entry(b, &s->bcnlist, list) {
-		dbg("%s %d bcn "MACFMT" \n", __func__, __LINE__,
-				MAC2STR(b->bssid));
 		if (!best) {
-			best = b;
+			best = u;
 			continue;
 		}
 
-		dbg("%s %d best rcpi %u this rcpi %u\n", __func__, __LINE__,
-				best->rcpi, b->rcpi);
+		dbg("%s %d best ul_rcpi %u this ul_rcpi %u\n", __func__, __LINE__,
+				best->ul_rcpi, u->ul_rcpi);
 
-		if ((best->rcpi - b->rcpi) > 10) {
-			dbg("%s %d new best bcn "MACFMT" with rcpi %d\n",
+		if ((best->ul_rcpi - u->ul_rcpi) > USTA_STEER_UL_RCPI_DELTA) {
+			dbg("%s %d new best usta node "MACFMT" with ul_rcpi %d\n",
 					__func__, __LINE__,
-					MAC2STR(b->bssid),
-					b->rcpi);
-			best = b;
+					MAC2STR(u->agent->alid),
+					u->ul_rcpi);
+			best = u;
 		}
 	}
 
 	if (!best) {
 		dbg("%s %d\n", __func__, __LINE__);
-		return 0;
+		return;
 	}
 
-	//if (!hwaddr_is_zero(best->bssid) &&  memcmp(best->bssid, s->bssid, 6)) {
+	/* Get appropriate netif on best node based on current ssid */
+	best_fh = find_interface_by_ssid(c, best->agent, s->fh->ssid);
+
+	if (best_fh && !hwaddr_is_zero(best_fh->bssid)
+			&& memcmp(best_fh->bssid, s->bssid, 6)) {
+
 		struct cmdu_buff *cmdu;
 
-		if (!c->cfg.enable_sta_steer) {
+		if ((s->type == IEEE1905 && !c->cfg.enable_bsta_steer) ||
+		    (s->type == NON_IEEE1905 && !c->cfg.enable_sta_steer)) {
 			trace("|%s:%d| better bssid found, but will not steer "MACFMT",\
-			       because the 'enable_sta_steer' is not set!\n",
+			       because the 'enable_(b)sta_steer' is not set!\n",
 			       __func__, __LINE__, MAC2STR(s->macaddr));
-			return 0;
+			return;
 		}
 
-		dbg("%s %d new bssid, wow! try to steer "MACFMT " " MACFMT " " MACFMT "\n", __func__,
-				__LINE__, MAC2STR(s->macaddr), MAC2STR(best->bssid), MAC2STR(s->bssid));
+		dbg("%s %d better bssid found! try to steer " MACFMT " \
+		    from " MACFMT " to " MACFMT "\n",
+		    __func__, __LINE__,
+		    MAC2STR(s->macaddr), MAC2STR(s->bssid), MAC2STR(best_fh->bssid));
+
+		if (s->type == IEEE1905)
+			cmdu = cntlr_gen_backhaul_steer_request(c, s->agent->alid,
+					s->macaddr, best_fh->bssid, 0, 0);
+		else
+			cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid,
+					s->bssid, 0,
+					1, (uint8_t (*)[6])s->macaddr,
+					1, (uint8_t (*)[6])best_fh->bssid,
+					1);
 
-
-		cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid,
-						s->bssid, 0,
-						1, (uint8_t (*)[6])s->macaddr,
-						1, (uint8_t (*)[6])best->bssid,
-						1);
 		if (cmdu) {
 			send_cmdu(c, cmdu);
 			cmdu_free(cmdu);
 		}
-	//}
+	}
 	dbg("%s %d\n", __func__, __LINE__);
-
-	return 0;
 }
 
-int cntlr_check_bsta_steer(struct controller *c, struct sta *s)
+int handle_unassoc_sta_link_metrics_response(void *cntlr,
+		struct cmdu_buff *cmdu)
 {
-	struct bcn_metrics *best = NULL, *b;
-	struct node *n;
+	struct controller *c = (struct controller *) cntlr;
+	struct tlv_policy a_policy[] = {
+		[0] = { .type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE, .present = TLV_PRESENT_ONE },
+	};
+	struct tlv *tv[1][16] = {0};
+	int i = 0;
+	int ret = 0;
 
-	dbg("%s %d for "MACFMT"\n", __func__, __LINE__, MAC2STR(s->macaddr));
+	trace("%s: --->\n", __func__);
 
-	n = find_node_by_mac(c, s->agent->alid);
-	if (!n)
+	if (!cntlr || !cmdu)
 		return -1;
 
-	dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid));
+	/* If a Multi-AP Controller receives an Unassociated STA
+	 * Link Metrics Response message, then it shall respond
+	 * within one second with a 1905 Ack message.
+	 */
+	cntlr_send_1905_acknowledge(cntlr, cmdu, NULL, 0);
 
-	list_for_each_entry(b, &s->bcnlist, list) {
-		dbg("%s %d bcn "MACFMT" \n", __func__, __LINE__, MAC2STR(b->bssid));
-		if (!best) {
-			best = b;
-			continue;
-		}
-		dbg("%s %d best rcpi %u this rcpi %u\n", __func__, __LINE__,
-				best->rcpi, b->rcpi);
-		if ((best->rcpi - b->rcpi) > 10) {
-			dbg("%s %d new best bcn "MACFMT" with rcpi %d\n",
-					__func__, __LINE__,
-					MAC2STR(b->bssid),
-					b->rcpi);
-			best = b;
-		}
-	}
+	ret = cmdu_parse_tlvs(cmdu, tv, a_policy, 1);
 
-	if (!best) {
-		dbg("%s %d\n", __func__, __LINE__);
-		return 0;
-	}
+	if (ret || !tv[0][0])
+		return -1;
 
-	if (memcmp(best->bssid, s->bssid, 6)) {
-		if (!c->cfg.enable_bsta_steer) {
-			trace("|%s:%d| new bssid, but will not try to steer "MACFMT",\
-			       because the 'enable_bsta_steer' is not set!\n",
-			       __func__, __LINE__, MAC2STR(s->macaddr));
-			return 0;
-		}
-		struct cmdu_buff *cmdu;
 
-		dbg("%s %d new bssid, wow! try to steer "MACFMT"\n", __func__, __LINE__, MAC2STR(s->macaddr));
-		cmdu = cntlr_gen_backhaul_steer_request(c, s->agent->alid,
-				s->macaddr, best->bssid, 0, 0);
-		if (!cmdu)
-			return -1;
+	if (tv[0][0]) {
+		int offset = 0;
+		uint8_t *tv_data = (uint8_t *)tv[0][0]->data;
+		struct tlv_unassoc_sta_link_metrics_resp *resp =
+			(struct tlv_unassoc_sta_link_metrics_resp  *)tv_data;
+
+		offset = sizeof(*resp);
+		for (i = 0; i < resp->num_sta; i++) {
+			struct una_sta_metrics *u;
+			struct unassoc_sta_link_metrics_sta *b =
+				(struct unassoc_sta_link_metrics_sta *)&tv_data[offset];
+			struct sta *s = cntlr_find_sta(c, b->macaddr);
+
+			if (!s) {
+				dbg("|%s:%d| unassociated STA "MACFMT" not found!\n",
+				__func__, __LINE__, MAC2STR(b->macaddr));
+				continue;
+			}
 
-		dbg("%s %d\n", __func__, __LINE__);
-		send_cmdu(c, cmdu);
-		cmdu_free(cmdu);
+			u = cntlr_find_usta_metric(c, s, cmdu->origin); /* alid */
+			if (!u) {
+				u = calloc(1, sizeof(*u));
+				if (!u)
+					continue;
+
+				list_add(&u->list, &s->unassoclist);
+				u->agent = find_node_by_mac(c, cmdu->origin);
+				dbg("%s %d\n", __func__, __LINE__);
+			}
+
+			u->channel = b->channel;
+			u->time_delta = BUF_GET_BE32(b->time_delta);
+			u->ul_rcpi = b->ul_rcpi;
+
+			trace("\t\tu->agent.alid: " MACFMT "\n", MAC2STR(u->agent->alid));
+			trace("\t\tu->channel: %d\n", u->channel);
+			trace("\t\tu->time_delta: %lu\n",BUF_GET_BE32(u->time_delta));
+			trace("\t\tu->ul_rcpi: %d\n", u->ul_rcpi);
+
+			offset += sizeof(*b);
+
+			/* Steer if (b)STA would benefit from it */
+			cntlr_check_usta_steer(c, s);
+		}
 	}
-	dbg("%s %d\n", __func__, __LINE__);
 
-	return 0;
+	return ret;
 }
 
 struct bcn_metrics *cntlr_find_sta_bcn_metric(struct controller *c,
@@ -1175,10 +1317,6 @@ dbg("%s %d\n", __func__, __LINE__);
 		dbg("%s %d\n", __func__, __LINE__);
 	}
 dbg("%s %d\n", __func__, __LINE__);
-//	if (s->type == IEEE1905)
-//		cntlr_check_bsta_steer(c, s);
-//	else
-//		cntlr_check_sta_steer(c, s);
 
 	return ret;
 }
diff --git a/src/cntlr_map_debug.c b/src/cntlr_map_debug.c
index 06d9d3f82e3f0902307eb9015c552ce04496ac99..d979f6569871c10222615db9e33176cb764d63fe 100644
--- a/src/cntlr_map_debug.c
+++ b/src/cntlr_map_debug.c
@@ -919,6 +919,41 @@ int debug_unassoc_sta_link_metrics_response(void *cntlr,
 		struct cmdu_buff *cmdu)
 {
 	trace("%s: --->\n", __func__);
+
+	int i;
+	int offset = 0;
+	struct tlv *tv[1][16];
+	struct tlv_policy sta_policy[] = {
+		[0] = { .type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE, .present = TLV_PRESENT_ONE },
+	};
+
+	trace("parsing unassociated sta link metric response |" \
+		   MACFMT "\n", MAC2STR(cmdu->origin));
+	cmdu_parse_tlvs(cmdu, tv, sta_policy, 1);
+
+	if (tv[0][0]) {
+		uint8_t *tv_data = (uint8_t *)tv[0][0]->data;
+		struct tlv_unassoc_sta_link_metrics_resp *p =
+			(struct tlv_unassoc_sta_link_metrics_resp *)tv_data;
+
+		trace("MAP_TLV_UNASSOCIATED_STA_LINK_METRICS:\n");
+		trace("\topclass: %d\n", p->opclass);
+		trace("\tnum_sta: %d\n", p->num_sta);
+
+		offset = sizeof(*p);
+		for (i = 0; i < p->num_sta; i++) {
+			struct unassoc_sta_link_metrics_sta *b =
+				(struct unassoc_sta_link_metrics_sta *)&tv_data[offset];
+
+			trace("\t\tmacaddr: " MACFMT "\n", MAC2STR(b->macaddr));
+			trace("\t\tchannel: %d\n", b->channel);
+			trace("\t\ttime_delta: %lu\n",
+					BUF_GET_BE32(b->time_delta));
+			trace("\t\tul_rcpi: %d\n", b->ul_rcpi);
+			offset += sizeof(*b);
+		}
+	}
+
 	return 0;
 }
 
diff --git a/src/cntlr_tlv.c b/src/cntlr_tlv.c
index b715cd9d41eecce45d2e6370824d7e15f69202ba..e9eb1e9af2905bd0cde51737ea1df146769d9854 100644
--- a/src/cntlr_tlv.c
+++ b/src/cntlr_tlv.c
@@ -937,6 +937,54 @@ int cntlr_gen_sta_mac(struct controller *c,
 	return 0;
 }
 
+int cntlr_gen_unassociated_sta_link_metrics(struct controller *c,
+		struct cmdu_buff *frm, uint8_t opclass,
+		uint8_t num_metrics, struct unassoc_sta_metric *metrics)
+{
+	int ret, i, j, num_sta;
+	struct tlv *t;
+	struct tlv_unassoc_sta_link_metrics_query *data;
+
+	t = cmdu_reserve_tlv(frm, 512);
+	if (!t)
+		return -1;
+
+	t->type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_QUERY;
+	t->len = sizeof(struct tlv_unassoc_sta_link_metrics_query);
+
+	data = (struct tlv_unassoc_sta_link_metrics_query *) t->data;
+	data->opclass = opclass;
+	data->num_channel = num_metrics;
+
+	for (i = 0; i < num_metrics; i++) {
+		t->len += 2; /* two bytes: channel & num_sta */
+
+		data->ch[i].channel = metrics[i].channel;
+		num_sta = metrics[i].num_sta;
+
+		if (num_sta > MAX_UNASSOC_STAMACS) {
+			dbg("%s: error: num_sta (%d) greater than %d\n",
+				__func__, num_sta, MAX_UNASSOC_STAMACS);
+			num_sta = MAX_UNASSOC_STAMACS;
+		}
+
+		t->len += (num_sta * 6); /* six bytes: macaddr */
+
+		data->ch[i].num_sta = num_sta;
+		for (j = 0; j < num_sta; j++)
+			memcpy(data->ch[i].sta[j].macaddr,
+			       metrics[i].sta[j].macaddr, 6);
+	}
+
+	ret = cmdu_put_tlv(frm, t);
+	if (ret) {
+		dbg("%s: error: cmdu_put_tlv()\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
 int cntlr_gen_searched_role(struct controller *c, struct cmdu_buff *frm,
 		uint8_t role)
 {
diff --git a/src/cntlr_tlv.h b/src/cntlr_tlv.h
index 0bb12de1a1c9d639a5fac1ada698bfc06268a375..ed517a6ee6899170505eea9dc2f934a12f10e5a4 100644
--- a/src/cntlr_tlv.h
+++ b/src/cntlr_tlv.h
@@ -83,6 +83,9 @@ int cntlr_gen_ap_metric_query(struct controller *c,
 		struct cmdu_buff *frm, uint8_t num_bss, uint8_t *bsslist);
 int cntlr_gen_sta_mac(struct controller *c,
 		struct cmdu_buff *frm, uint8_t *sta);
+int cntlr_gen_unassociated_sta_link_metrics(struct controller *c,
+		struct cmdu_buff *frm, uint8_t opclass,
+		uint8_t num_metrics, struct unassoc_sta_metric *metrics);
 int cntlr_gen_searched_role(struct controller *c, struct cmdu_buff *frm,
 		uint8_t role);
 int cntlr_gen_autoconf_freq_band(struct controller *c, struct cmdu_buff *frm,
diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c
index a86b684cc91fae9a22893a50dd5b0492673f705a..c9e0cd9d3c67883a1e0fb4b784696bf1494fa9fa 100644
--- a/src/cntlr_ubus.c
+++ b/src/cntlr_ubus.c
@@ -232,6 +232,22 @@ static const struct blobmsg_policy sta_metric_query_params[__STA_METRIC_QUERY_MA
 	[STA_METRIC_QUERY_STA] = { .name = "sta", .type = BLOBMSG_TYPE_STRING },
 };
 
+enum {
+	UNASSOC_STA_LM_QUERY_AGENT,
+	UNASSOC_STA_LM_QUERY_OPCLASS,
+	UNASSOC_STA_LM_QUERY_METRICS,
+	__UNASSOC_STA_LM_QUERY_MAX,
+};
+
+static const struct blobmsg_policy unassoc_sta_lm_query_params[__UNASSOC_STA_LM_QUERY_MAX] = {
+	[UNASSOC_STA_LM_QUERY_AGENT] = { .name = "agent",
+			.type = BLOBMSG_TYPE_STRING },
+	[UNASSOC_STA_LM_QUERY_OPCLASS] = { .name = "opclass",
+			.type = BLOBMSG_TYPE_INT32 },
+	[UNASSOC_STA_LM_QUERY_METRICS] = { .name = "metrics",
+			.type = BLOBMSG_TYPE_ARRAY },
+};
+
 enum {
 	BK_CAPS_POLICY_AGENT,
 	__BK_CAPS_POLICY_MAX,
@@ -1648,6 +1664,157 @@ static int cntlr_sta_metric_query(struct ubus_context *ctx,
 	return UBUS_STATUS_OK;
 }
 
+static int cntlr_unassoc_sta_lm_query(struct ubus_context *ctx,
+		struct ubus_object *obj, struct ubus_request_data *req,
+		const char *method, struct blob_attr *msg)
+{
+	struct cmdu_buff *cmdu;
+	char mac_str[18];
+	uint8_t agent_mac[6] = { 0 };
+	struct blob_attr *tb[__UNASSOC_STA_LM_QUERY_MAX];
+	struct controller *c = container_of(obj, struct controller, obj);
+	uint8_t opclass = 0;
+	int num_metrics = 0;
+	struct unassoc_sta_metric *metrics = NULL;
+	int ret = UBUS_STATUS_OK;
+
+	blobmsg_parse(unassoc_sta_lm_query_params, __UNASSOC_STA_LM_QUERY_MAX, tb,
+			blob_data(msg), blob_len(msg));
+
+	if (!tb[UNASSOC_STA_LM_QUERY_AGENT]) {
+		fprintf(stderr, "Unassociated STA link metric query: provide Agent" \
+				"address in format aa:bb:cc:dd:ee:ff\n");
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	memset(mac_str, 0, sizeof(mac_str));
+	strncpy(mac_str, blobmsg_data(tb[UNASSOC_STA_LM_QUERY_AGENT]),
+			sizeof(mac_str) - 1);
+	if (!hwaddr_aton(mac_str, agent_mac))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	if (tb[UNASSOC_STA_LM_QUERY_OPCLASS])
+		opclass = (int) blobmsg_get_u32(
+				tb[UNASSOC_STA_LM_QUERY_OPCLASS]);
+
+	if (!opclass) {
+		fprintf(stderr, "unassoc_sta_lm_query: missing opclass\n");
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	/* Example ubus call:
+	 * ubus call map.controller unassoc_sta_lm_query '{"agent":
+	 * "44:d4:37:42:47:b9", "opclass":81, "metrics":
+	 * [{"channel":11, "stamacs": ["44:d4:37:42:3a:c6", "44:d4:37:42:47:be"]}]}'
+	 *
+	 * ubus call map.controller unassoc_sta_lm_query '{"agent":
+	 * "44:d4:37:42:47:b9", "opclass":128,
+	 * "metrics":[{"channel":36,"stamacs: ["e0:d4:e8:79:c4:ef"]}]}'
+	 */
+
+	if (tb[UNASSOC_STA_LM_QUERY_METRICS]) {
+		struct blob_attr *cur;
+		static const struct blobmsg_policy supp_attrs[2] = {
+				[0] = { .name = "channel",
+						.type = BLOBMSG_TYPE_INT32 },
+				[1] = { .name = "stamacs",
+						.type = BLOBMSG_TYPE_ARRAY },
+		};
+		int rem, i = 0;
+
+		num_metrics = blobmsg_check_array(tb[UNASSOC_STA_LM_QUERY_METRICS],
+				BLOBMSG_TYPE_TABLE);
+		if (!num_metrics) {
+			fprintf(stderr, "unassoc_sta_lm_query: missing metrics\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+
+		/* TODO: consider dynamic allocation for number of STAs on the list */
+		metrics = calloc(num_metrics, sizeof(struct unassoc_sta_metric));
+		if (!metrics) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		blobmsg_for_each_attr(cur, tb[UNASSOC_STA_LM_QUERY_METRICS], rem) {
+			int remm, j = 0;
+			struct blob_attr *data[2], *attr;
+			char mac[18];
+
+			blobmsg_parse(supp_attrs, 2, data, blobmsg_data(cur),
+					blobmsg_data_len(cur));
+
+			if (!data[0] || !data[1])
+				continue;
+
+			metrics[i].channel = (uint8_t) blobmsg_get_u32(data[0]);
+			metrics[i].num_sta = blobmsg_check_array(
+					data[1], BLOBMSG_TYPE_STRING);
+
+			if (!metrics[i].channel) {
+				fprintf(stderr, "unassoc_sta_lm_query: missing channel \
+						for metrics [%d]\n", i);
+				ret = UBUS_STATUS_INVALID_ARGUMENT;
+				goto out;
+			}
+
+			if (!metrics[i].num_sta) {
+				fprintf(stderr, "unassoc_sta_lm_query: no stations for \
+						channel %d\n", metrics[i].channel);
+				ret = UBUS_STATUS_INVALID_ARGUMENT;
+				goto out;
+			}
+
+			if (metrics[i].num_sta > MAX_UNASSOC_STAMACS) {
+				fprintf(stderr, "unassoc_sta_lm_query: max 10 stations \
+						allowed per channel!\n");
+				ret = UBUS_STATUS_INVALID_ARGUMENT;
+				goto out;
+			}
+
+			/* Iterate through all metrics of given channel */
+			blobmsg_for_each_attr(attr, data[1], remm) {
+				if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING)
+					continue;
+
+				/* STA list */
+				strncpy(mac, blobmsg_get_string(attr), sizeof(mac) - 1);
+				hwaddr_aton(mac, metrics[i].sta[j].macaddr);
+
+				j++;
+			}
+
+			if (metrics[i].num_sta != j) {
+				dbg("%s(): invalid metric [%d]!\n", __func__, i);
+				ret = UBUS_STATUS_INVALID_ARGUMENT;
+				goto out;
+			}
+
+			i++;
+		}
+
+		if (num_metrics != i) {
+			dbg("%s(): invalid metrics!\n", __func__);
+			ret = UBUS_STATUS_INVALID_ARGUMENT;
+			goto out;
+		}
+	}
+
+	cmdu = cntlr_gen_unassoc_sta_metric_query(c, agent_mac,
+				opclass, num_metrics, metrics);
+	if (!cmdu)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	send_cmdu(c, cmdu);
+	cmdu_free(cmdu);
+
+out:
+	if (metrics)
+		free(metrics);
+
+	return ret;
+}
+
 int cntlr_bcn_metrics_query(struct ubus_context *ctx, struct ubus_object *obj,
 			struct ubus_request_data *req, const char *method,
 			struct blob_attr *msg)
@@ -2184,6 +2351,8 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 		UBUS_METHOD("scan", cntlr_scan, scan_policy_params),
 		UBUS_METHOD("sta_metric_query", cntlr_sta_metric_query,
 				sta_metric_query_params),
+		UBUS_METHOD("unassoc_sta_lm_query", cntlr_unassoc_sta_lm_query,
+				unassoc_sta_lm_query_params),
 		UBUS_METHOD("bcn_metrics_query", cntlr_bcn_metrics_query,
 				bcn_metrics_query_params),
 		UBUS_METHOD("bk_caps", cntlr_bk_caps,
diff --git a/src/config.c b/src/config.c
index 4ea5a3fbd7547ed4c8a80490218a366d86b7ce9b..51f056702a270c9c640a817db2086104baef37a3 100644
--- a/src/config.c
+++ b/src/config.c
@@ -364,6 +364,8 @@ void cntlr_config_dump(struct controller_config *c)
 	dbg("Registrar @2Ghz: %d\n", c->has_registrar_2g);
 	dbg("Enable STA steer: %d\n", c->enable_sta_steer);
 	dbg("Enable BSTA steer: %d\n", c->enable_bsta_steer);
+	dbg("Use bcn metrics to steer: %d\n", c->use_bcn_metrics);
+	dbg("Use uSTA metrics to steer: %d\n", c->use_usta_metrics);
 
 	dbg("Credentials\n");
 	list_for_each_entry(cred, &c->aplist, list) {
@@ -424,6 +426,8 @@ static int cntlr_config_get_base(struct controller_config *c,
 		CNTLR_DEBUG,
 		CNTLR_ENABLE_STA_STEER,
 		CNTLR_ENABLE_BSTA_STEER,
+		CNTLR_USE_BCN_METRICS,
+		CNTLR_USE_USTA_METRICS,
 		CNTLR_RCPI_STEER_THRESHOLD,
 		CNTLR_PVID,
 		CNTLR_PCP_DEFAULT,
@@ -435,6 +439,8 @@ static int cntlr_config_get_base(struct controller_config *c,
 		{ .name = "debug", .type = UCI_TYPE_STRING },
 		{ .name = "enable_sta_steer", .type = UCI_TYPE_STRING },
 		{ .name = "enable_bsta_steer", .type = UCI_TYPE_STRING },
+		{ .name = "use_bcn_metrics", .type = UCI_TYPE_STRING },
+		{ .name = "use_usta_metrics", .type = UCI_TYPE_STRING },
 		{ .name = "rcpi_threshold", .type = UCI_TYPE_STRING },
 		{ .name = "primary_vid", .type = UCI_TYPE_STRING },
 		{ .name = "primary_pcp", .type = UCI_TYPE_STRING },
@@ -476,6 +482,18 @@ static int cntlr_config_get_base(struct controller_config *c,
 		c->enable_bsta_steer = atoi(val) == 1 ? true : false;
 	}
 
+	if (tb[CNTLR_USE_BCN_METRICS]) {
+		const char *val = tb[CNTLR_USE_BCN_METRICS]->v.string;
+
+		c->use_bcn_metrics = atoi(val) == 1 ? true : false;
+	}
+
+	if (tb[CNTLR_USE_USTA_METRICS]) {
+		const char *val = tb[CNTLR_USE_USTA_METRICS]->v.string;
+
+		c->use_usta_metrics = atoi(val) == 1 ? true : false;
+	}
+
 	if (tb[CNTLR_RCPI_STEER_THRESHOLD]) {
 		const char *val = tb[CNTLR_RCPI_STEER_THRESHOLD]->v.string;
 
diff --git a/src/config.h b/src/config.h
index fab9d1c020168d55aa621563005994cf3339ab09..42028f1ac53b712ae42b00ff83b02b66991e4428 100644
--- a/src/config.h
+++ b/src/config.h
@@ -126,6 +126,8 @@ struct controller_config {
 	int debug_level;
 	bool enable_sta_steer;
 	bool enable_bsta_steer;
+	bool use_bcn_metrics;
+	bool use_usta_metrics;
 	int rcpi_threshold;
 	int num_bss;
 	int num_vlans;