diff --git a/src/core/cntlr_cmdu_generator.c b/src/core/cntlr_cmdu_generator.c
index da8aa6747e2bc28809635737484759dcf2c1f1f8..da0562564fc56af2d836da87411231bdb27e8618 100644
--- a/src/core/cntlr_cmdu_generator.c
+++ b/src/core/cntlr_cmdu_generator.c
@@ -534,3 +534,25 @@ fail_cmdu:
 	free(cmdu);
 	return NULL;
 }
+
+struct cmdu_cstruct *cntlr_gen_topology_query(struct controller *c,
+					      uint8_t *origin, char *intf_name)
+{
+	struct cmdu_cstruct *cmdu;
+
+	cmdu = calloc(1, sizeof(struct cmdu_cstruct));
+	if (!cmdu) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return NULL;
+	}
+
+	cmdu_defaults(c, cmdu, origin, intf_name, CMDU_TYPE_TOPOLOGY_QUERY);
+
+	cmdu->num_tlvs = 0;
+
+	return cmdu;
+
+fail_cmdu:
+	map_free_cmdu(cmdu);
+	return NULL;
+}
diff --git a/src/core/cntlr_map_debug.c b/src/core/cntlr_map_debug.c
index 847d061fc2eb2efba64ed9b2719f44bd6bfe8410..fad2834d5f40d39e76e3a24de8a0c80f048263ab 100644
--- a/src/core/cntlr_map_debug.c
+++ b/src/core/cntlr_map_debug.c
@@ -40,7 +40,173 @@ int debug_topology_notification(void *cntlr, struct cmdu_cstruct *cmdu)
 
 int debug_topology_response(void *cntlr, struct cmdu_cstruct *cmdu)
 {
+	uint8_t *tlv;
+	int i, j, k;
+
 	trace("%s: --->\n", __func__);
+	trace("parsing topology response |" MACFMT "|CMDU: %s\n",
+			MAC2STR(cmdu->origin),
+			map_stringify_cmdu_type(cmdu->message_type));
+
+	for (i = 0; i < cmdu->num_tlvs; i++) {
+		tlv = (uint8_t *)cmdu->tlvs[i];
+		trace("TLV: %s\n", map_stringify_tlv_type(*tlv));
+		switch (*tlv) {
+		case TLV_TYPE_DEVICE_INFORMATION_TYPE:
+			{
+				struct tlv_device_info *p =
+					(struct tlv_device_info *)tlv;
+
+				trace("\tal_mac_address: " MACFMT "\n",
+				      MAC2STR(p->al_mac_address));
+				trace("\tlocal_interfaces_nr: %d\n",
+				      p->local_interfaces_nr);
+				for (j = 0; j < p->local_interfaces_nr; j++) {
+					trace("\t\tmac_address: " MACFMT "\n",
+					      MAC2STR(p->local_interfaces[j].mac_address));
+					trace("\t\tmedia_type: 0x%04hx\n",
+					      p->local_interfaces[j].media_type);
+					trace("\t\tmedia_specific_data_size: %d\n",
+					      p->local_interfaces[j].media_specific_data_size);
+					if ((p->local_interfaces[j].media_type & 0xff00) == 0x0100) {
+						/* IEEE 802.11 devices */
+						trace("\t\tnetwork_membership: " MACFMT "\n",
+						      MAC2STR(p->local_interfaces[j].media_specific_data.ieee80211.network_membership));
+						trace("\t\trole: ");
+						print_bits(p->local_interfaces[j].media_specific_data.ieee80211.role,
+							   4, 4);
+						trace("\t\tap_channel_band: 0x%02hx\n",
+						      p->local_interfaces[j].media_specific_data.ieee80211.ap_channel_band);
+						trace("\t\tap_ch_freq_1: 0x%02hx\n",
+						      p->local_interfaces[j].media_specific_data.ieee80211.ap_ch_freq_1);
+						trace("\t\tap_ch_freq_2: 0x%02hx\n",
+						      p->local_interfaces[j].media_specific_data.ieee80211.ap_ch_freq_2);
+					}
+				}
+				break;
+			}
+		case TLV_TYPE_DEVICE_BRIDGING_CAPABILITIES:
+			{
+				struct tlv_device_bridge_cap *p =
+					(struct tlv_device_bridge_cap *)tlv;
+
+				trace("\tbridging_tuples_nr: %d\n",
+				      p->bridging_tuples_nr);
+				for (j = 0; j < p->bridging_tuples_nr; j++) {
+					trace("\t\tbridging_tuple_macs_nr: %d\n",
+					      p->bridging_tuples[j].bridging_tuple_macs_nr);
+					for (k = 0; k < p->bridging_tuples[j].bridging_tuple_macs_nr; k++) {
+						trace("\t\t\tmac_address: " MACFMT "\n",
+						      MAC2STR(p->bridging_tuples[j].bridging_tuple_macs[k].mac_address));
+					}
+				}
+				break;
+			}
+		case TLV_TYPE_NON_1905_NEIGHBOR_DEVICE_LIST:
+			{
+				struct tlv_non1905_neighbor *p =
+					(struct tlv_non1905_neighbor *)tlv;
+
+				trace("\tlocal_mac_address: " MACFMT "\n",
+				      MAC2STR(p->local_mac_address));
+				trace("\tnon_1905_neighbors_nr: %d\n",
+				      p->non_1905_neighbors_nr);
+				for (j = 0; j < p->non_1905_neighbors_nr; j++) {
+					trace("\t\tmac_address: " MACFMT "\n",
+					      MAC2STR(p->non_1905_neighbors[j].mac_address));
+				}
+				break;
+			}
+		case TLV_TYPE_NEIGHBOR_DEVICE_LIST:
+			{
+				struct tlv_neighbor_device *p =
+					(struct tlv_neighbor_device *)tlv;
+
+				trace("\tlocal_mac_address: " MACFMT "\n",
+				      MAC2STR(p->local_mac_address));
+				trace("\tneighbors_nr: %d\n",
+				      p->neighbors_nr);
+				for (j = 0; j < p->neighbors_nr; j++) {
+					trace("\t\tmac_address: " MACFMT "\n",
+					      MAC2STR(p->neighbors[j].mac_address));
+					trace("\t\tbridge_flag: %s\n",
+					      (p->neighbors[j].bridge_flag ? "true" : "false"));
+				}
+				break;
+			}
+		case MAP_TLV_SUPPORTED_SERVICE:
+			{
+				struct tlv_supp_service *p =
+					(struct tlv_supp_service *)tlv;
+
+				trace("\tsupported_services_list: %d\n",
+				      p->supported_services_list);
+				for (j = 0; j < p->supported_services_list; j++) {
+					trace("\t\tservice: %s\n",
+					      (p->supported_services[j].service ?
+					       "Multi-AP Agent" : "Multi-AP Controller"));
+				}
+				break;
+			}
+		case MAP_TLV_AP_OPERATIONAL_BSS:
+			{
+				struct tlv_ap_oper_bss *p =
+					(struct tlv_ap_oper_bss *)tlv;
+
+				trace("\tradios_nr: %d\n", p->radios_nr);
+				for (j = 0; j < p->radios_nr; j++) {
+					trace("\t\tradio_id: " MACFMT "\n",
+					      MAC2STR(p->radio[j].radio_id));
+					trace("\t\tbss_nr: %d\n",
+					      p->radio[j].bss_nr);
+					for (k = 0; k < p->radio[j].bss_nr; k++) {
+						trace("\t\t\tbssid: " MACFMT "\n",
+						      MAC2STR(p->radio[j].bss[k].bssid));
+						trace("\t\t\tssid_len: %d\n",
+						      p->radio[j].bss[k].ssid_len);
+						trace("\t\t\tssid: %.*s\n",
+						      p->radio[j].bss[k].ssid_len,
+						      p->radio[j].bss[k].ssid);
+					}
+				}
+				break;
+			}
+		case MAP_TLV_ASSOCIATED_CLIENTS:
+			{
+				struct tlv_assoc_client *p =
+					(struct tlv_assoc_client *)tlv;
+
+				trace("\tbss_nr: %d\n", p->bss_nr);
+				for (j = 0; j < p->bss_nr; j++) {
+					trace("\t\tbssid: " MACFMT "\n",
+					      MAC2STR(p->bss[j].bssid));
+					trace("\t\tassoc_clients_nr: %d\n",
+					      p->bss[j].assoc_clients_nr);
+					for (k = 0; k < p->bss[j].assoc_clients_nr; k++) {
+						trace("\t\t\tclient_addr: " MACFMT "\n",
+						      MAC2STR(p->bss[j].clients[k].client_addr));
+						trace("\t\t\tuptime: 0x%04x\n",
+						      p->bss[j].clients[k].uptime);
+					}
+				}
+				break;
+			}
+		case MAP_TLV_MULTIAP_PROFILE:
+			{
+				struct tlv_map_profile *p =
+					(struct tlv_map_profile *)tlv;
+
+				trace("\tprofile: 0x%02hx\n", p->profile);
+				break;
+			}
+		default:
+			{
+				trace("unknown TLV\n");
+				break;
+			}
+		}
+		trace("\n");
+	}
 	return 0;
 }
 
diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c
index 01e5f8d69a4be6985baf68fca1eadd8ba29b55a3..c79d9e54fe59c54e714ef5121e268f1cadedce15 100644
--- a/src/core/cntlr_ubus.c
+++ b/src/core/cntlr_ubus.c
@@ -37,6 +37,7 @@
 #include "cntlr_tlv_generator.h"
 #include "cntlr_cmdu_generator.h"
 
+#define MULTICAST_ADDR_STR "01:80:c2:00:00:13"
 
 enum {
 	AP_POLICY_AGENT,
@@ -245,6 +246,15 @@ static const struct blobmsg_policy bk_caps_policy_params[__BK_CAPS_POLICY_MAX] =
 	[BK_CAPS_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
 };
 
+enum {
+	TOPOLOGY_QUERY_AGENT,
+	__TOPOLOGY_QUERY_MAX,
+};
+
+static const struct blobmsg_policy topology_query_params[__TOPOLOGY_QUERY_MAX] = {
+	[TOPOLOGY_QUERY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
+};
+
 void send_cmdu_cb(struct ubus_request *req,
 				int type, struct blob_attr *msg)
 {
@@ -1744,6 +1754,39 @@ static int cntlr_bk_caps(struct ubus_context *ctx, struct ubus_object *obj,
 	return UBUS_STATUS_OK;
 }
 
+static int cntlr_topology_query(struct ubus_context *ctx, struct ubus_object *obj,
+				struct ubus_request_data *req, const char *method,
+				struct blob_attr *msg)
+{
+	trace("%s:--->\n", __func__);
+	struct blob_attr *tb[__TOPOLOGY_QUERY_MAX];
+	struct controller *c = container_of(obj, struct controller, obj);
+	char agent[18] = { 0 };
+	uint8_t agent_mac[6] = { 0 };
+	struct cmdu_cstruct *cmdu_data;
+
+	blobmsg_parse(topology_query_params, __TOPOLOGY_QUERY_MAX, tb,
+			blob_data(msg), blob_len(msg));
+
+	if (tb[TOPOLOGY_QUERY_AGENT]) {
+		strncpy(agent, blobmsg_data(tb[TOPOLOGY_QUERY_AGENT]),
+			sizeof(agent) - 1);
+	} else {
+		strncpy(agent, MULTICAST_ADDR_STR, sizeof(agent) - 1);
+	}
+	if (!hwaddr_aton(agent, agent_mac))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	cmdu_data = cntlr_gen_topology_query(c, agent_mac, NULL);
+	if (!cmdu_data)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	send_cmdu(c, cmdu_data);
+	map_free_cmdu(cmdu_data);
+
+	return UBUS_STATUS_OK;
+}
+
 void cntlr_notify_event(struct controller *c, void *ev_type, void *ev_data)
 {
 	struct blob_buf b;
@@ -1762,7 +1805,7 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 	struct ubus_object *obj;
 	struct ubus_object_type *obj_type;
 	struct ubus_method *obj_methods;
-	struct ubus_method m[14] = {
+	struct ubus_method m[15] = {
 		UBUS_METHOD_NOARG("status", cntlr_status),
 		UBUS_METHOD("ap_caps", cntlr_ap_caps,
 				ap_caps_policy_params),
@@ -1788,6 +1831,8 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 				sta_metric_query_params),
 		UBUS_METHOD("bk_caps", cntlr_bk_caps,
 				bk_caps_policy_params),
+		UBUS_METHOD("topology_query", cntlr_topology_query,
+			    topology_query_params),
 		/*
 		UBUS_METHOD("teardown_ap", cntlr_teardown_ap,
 				config_policy_params),