diff --git a/src/Makefile b/src/Makefile
index 87a5794dc4524c22dce6e7805e8f1f3b5395e33e..5023b73e0cfdd227eed05c6e766d6b71b397e94c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,6 +11,8 @@ OBJS = \
 	wifi_opclass.o \
 	allsta.o \
 	allmac.o \
+	backhaul_topology.o \
+	backhaul_topology_dbg.o \
 	cntlr_ubus.o \
 	cntlr_ubus_dbg.o \
 	cntlr.o \
diff --git a/src/backhaul_topology.c b/src/backhaul_topology.c
new file mode 100644
index 0000000000000000000000000000000000000000..f5a5b6284d88963008c4f3391fab5862da326f6f
--- /dev/null
+++ b/src/backhaul_topology.c
@@ -0,0 +1,441 @@
+
+#include "backhaul_topology.h"
+#include "backhaul_topology_dbg.h"
+#include "utils/debug.h"
+#include "utils/utils.h"
+
+#include <bufutil.h>
+#include <easy/utils.h>
+#include <1905_tlvs.h>
+
+#include <libubox/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Used to store all discovered easy mesh devices and model tree topology. */
+struct bh_topology_data {
+	struct list_head dev_list; /* list of struct bh_topology_dev */
+	uint32_t bh_topo_devs_number;
+	bool bh_topology_valid;
+};
+
+static struct bh_topology_data priv_bh_topo_data;
+
+
+static bool is_wifi(uint16_t media_type)
+{
+	return (media_type >> 8) == 1;
+}
+
+void init_bh_topology(void)
+{
+	INIT_LIST_HEAD(&priv_bh_topo_data.dev_list);
+	priv_bh_topo_data.bh_topo_devs_number = 0;
+	priv_bh_topo_data.bh_topology_valid = false;
+}
+
+void free_bh_topology(void)
+{
+	remove_all_bh_topology_devices();
+}
+
+bool is_bh_topology_valid(void)
+{
+	return priv_bh_topo_data.bh_topology_valid;
+}
+
+uint32_t bh_topology_devs_number(void)
+{
+	return priv_bh_topo_data.bh_topo_devs_number;
+}
+
+struct bh_topology_dev *find_bh_topology_device(const uint8_t *al_macaddr)
+{
+	struct bh_topology_dev *bh_topo_dev = NULL;
+
+	list_for_each_entry(bh_topo_dev, &priv_bh_topo_data.dev_list, list_element) {
+		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr))
+			return bh_topo_dev;
+	}
+
+	return NULL;
+}
+
+struct bh_topology_dev *add_bh_topology_device(const uint8_t *al_macaddr)
+{
+	struct bh_topology_dev *bh_topo_dev =
+		calloc(1, sizeof(struct bh_topology_dev));
+
+	if (!bh_topo_dev)
+		return NULL;
+
+	memcpy(bh_topo_dev->al_macaddr, al_macaddr, 6);
+	bh_topo_dev->bh_info.level_in_tree = UNKNOWN_TREE_LEVEL;
+
+	list_add(&bh_topo_dev->list_element, &priv_bh_topo_data.dev_list);
+
+	++priv_bh_topo_data.bh_topo_devs_number;
+	priv_bh_topo_data.bh_topology_valid = false;
+
+	return bh_topo_dev;
+}
+
+void remove_bh_topology_device(const uint8_t *al_macaddr)
+{
+	struct bh_topology_dev *bh_topo_dev = NULL, *bh_topo_dev_tmp;
+
+	list_for_each_entry_safe(bh_topo_dev, bh_topo_dev_tmp,
+				 &priv_bh_topo_data.dev_list, list_element) {
+
+		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr)) {
+			list_del(&bh_topo_dev->list_element);
+			free(bh_topo_dev);
+
+			--priv_bh_topo_data.bh_topo_devs_number;
+			priv_bh_topo_data.bh_topology_valid = false;
+		}
+	}
+}
+
+void remove_all_bh_topology_devices(void)
+{
+	/* cppcheck-suppress uninitvar */
+	list_flush(&priv_bh_topo_data.dev_list, struct bh_topology_dev, list_element);
+
+	priv_bh_topo_data.bh_topo_devs_number = 0;
+	priv_bh_topo_data.bh_topology_valid = false;
+}
+
+bool has_interface_info_changed(const struct tlv_device_info *tlv_dev_info,
+				const struct bh_topology_dev *bh_topo_dev)
+{
+	const uint8_t *tlv_iface_p = (uint8_t *)tlv_dev_info->interface;
+	size_t tlv_iface_offset;
+	int i;
+
+	if (bh_topo_dev->number_of_interfaces != tlv_dev_info->num_interface)
+		return true;
+
+	tlv_iface_offset = 0;
+	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
+		const struct local_interface *interface =
+			(struct local_interface *)(tlv_iface_p + tlv_iface_offset);
+
+		if (!hwaddr_equal(bh_topo_dev->ifaces[i].macaddr, interface->macaddr))
+			return true;
+
+		tlv_iface_offset += sizeof(struct local_interface) +
+				    interface->sizeof_mediainfo;
+	}
+
+	return false;
+}
+
+void copy_interface_info_from_tlv(const struct tlv_device_info *tlv_dev_info,
+				  struct bh_topology_dev *bh_topo_dev)
+{
+	const uint8_t *tlv_iface_p = (uint8_t *)tlv_dev_info->interface;
+	size_t tlv_iface_offset;
+	int i;
+
+	priv_bh_topo_data.bh_topology_valid = false;
+
+	/* Clear device old iface data, including neighbors info */
+	memset(bh_topo_dev->ifaces, 0, sizeof(bh_topo_dev->ifaces));
+
+	bh_topo_dev->num_of_ifaces_with_neighbors = 0;
+	bh_topo_dev->number_of_interfaces = tlv_dev_info->num_interface;
+
+	if (tlv_dev_info->num_interface > IFACE_MAX_NUM) {
+		err("%s: Currently max %d interfaces supported!\n", __func__, IFACE_MAX_NUM);
+		bh_topo_dev->number_of_interfaces = IFACE_MAX_NUM;
+	}
+
+	tlv_iface_offset = 0;
+	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
+		const struct local_interface *interface =
+			(struct local_interface *)(tlv_iface_p + tlv_iface_offset);
+
+		memcpy(bh_topo_dev->ifaces[i].macaddr, interface->macaddr, 6);
+		bh_topo_dev->ifaces[i].media_type =
+			BUF_GET_BE16(interface->mediatype);
+
+		tlv_iface_offset += sizeof(struct local_interface) +
+				    interface->sizeof_mediainfo;
+	}
+}
+
+static bool are_neighbor_lists_the_same(const struct local_iface *iface,
+					const struct tlv_1905neighbor *tlv_1905neighbor,
+					uint16_t tlv_length)
+{
+	const uint8_t tlv_neighbors_num =
+		(tlv_length - sizeof(tlv_1905neighbor->local_macaddr)) /
+		sizeof(struct i1905_neighbor);
+	uint8_t i, j;
+
+	if (iface->number_of_neighbors != tlv_neighbors_num)
+		return false;
+
+	for (i = 0; i < tlv_neighbors_num; ++i) {
+		bool found = false;
+
+		for (j = 0; j < iface->number_of_neighbors; ++j) {
+			if (hwaddr_equal(tlv_1905neighbor->nbr[i].aladdr,
+					 &iface->neighbors_al_macs[j][0])) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found)
+			return false;
+	}
+
+	return true;
+}
+
+bool has_neighbor_info_changed(const struct tlv_1905neighbor **neighbor_tlvs,
+			       const uint16_t *tlv_lengths,
+			       uint8_t tlv_number,
+			       const struct bh_topology_dev *bh_topo_dev)
+{
+	uint8_t i;
+
+	/* Every TLV provides neighbors for single local interface. */
+	if (bh_topo_dev->num_of_ifaces_with_neighbors != tlv_number)
+		return true;
+
+	for (i = 0; i < tlv_number; ++i) {
+		const struct tlv_1905neighbor *neighbor_tlv = neighbor_tlvs[i];
+		uint8_t j;
+		bool found = false;
+
+		for (j = 0; j < bh_topo_dev->number_of_interfaces; ++j) {
+			bool same_mac = hwaddr_equal(bh_topo_dev->ifaces[j].macaddr,
+					 neighbor_tlv->local_macaddr);
+
+			if (same_mac && are_neighbor_lists_the_same(
+						&bh_topo_dev->ifaces[j],
+						neighbor_tlv, tlv_lengths[i])) {
+
+				found = true;
+				break;
+			}
+		}
+
+		if (!found)
+			return true;
+	}
+
+	return false;
+
+}
+
+void copy_neighbor_info_from_tlvs(const struct tlv_1905neighbor **neighbor_tlvs,
+				  const uint16_t *tlv_lengths,
+				  uint8_t tlv_number,
+				  struct bh_topology_dev *bh_topo_dev)
+{
+	uint8_t i;
+
+	priv_bh_topo_data.bh_topology_valid = false;
+	bh_topo_dev->num_of_ifaces_with_neighbors = tlv_number;
+
+	/* Clear device old/previous neighbors info */
+	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
+		if (bh_topo_dev->ifaces[i].number_of_neighbors != 0) {
+			bh_topo_dev->ifaces[i].number_of_neighbors = 0;
+			memset(bh_topo_dev->ifaces[i].neighbors_al_macs, 0,
+			       sizeof(bh_topo_dev->ifaces[i].neighbors_al_macs));
+		}
+
+	}
+
+	for (i = 0; i < tlv_number; ++i) {
+		const struct tlv_1905neighbor *neighbor_tlv = neighbor_tlvs[i];
+		const uint8_t neighbors_num =
+			(tlv_lengths[i] - sizeof(neighbor_tlv->local_macaddr)) /
+			sizeof(struct i1905_neighbor);
+		struct local_iface *local_iface = NULL;
+		uint8_t j;
+
+		for (j = 0; j < bh_topo_dev->number_of_interfaces; ++j) {
+			/*
+			 * Because different local physical interfaces can use the same MAC
+			 * address, find a one with matching MAC and no neighbors added yet.
+			 */
+			if (hwaddr_equal(bh_topo_dev->ifaces[j].macaddr,
+						neighbor_tlv->local_macaddr) &&
+				bh_topo_dev->ifaces[j].number_of_neighbors == 0) {
+
+				local_iface = &bh_topo_dev->ifaces[j];
+				break;
+			}
+		}
+
+		if (local_iface) {
+			uint8_t k;
+
+			dbg("%s: adding neighbors for local_iface[%u].macaddr: " MACFMT "\n",
+				__func__, j, MAC2STR(local_iface->macaddr));
+
+			local_iface->number_of_neighbors = neighbors_num;
+
+			for (k = 0; k < neighbors_num && k < NEIGHBORS_MAX_NUM; ++k) {
+				dbg("%s:   neighbor[%u].aladdr: " MACFMT "\n",
+					__func__, k, MAC2STR(neighbor_tlv->nbr[k].aladdr));
+
+				memcpy(&local_iface->neighbors_al_macs[k][0],
+					neighbor_tlv->nbr[k].aladdr, 6);
+			}
+
+		} else {
+			err("%s: no iface found, adding neighbors failed for local_macaddr: " MACFMT "\n",
+				__func__, MAC2STR(neighbor_tlv->local_macaddr));
+		}
+	}
+
+}
+
+
+static void clear_backhaul_tree_relations(struct list_head *bh_topo_dev_list)
+{
+	struct bh_topology_dev *bh_topo_dev = NULL;
+
+	list_for_each_entry(bh_topo_dev, bh_topo_dev_list, list_element) {
+		bh_topo_dev->bh_info =
+			(struct backhaul_info){ UNKNOWN_TREE_LEVEL, 0, NULL, NULL, NULL };
+	}
+}
+
+static const struct local_iface *
+find_neighbor_local_iface(const struct bh_topology_dev *next_level_neighbor,
+			  const uint8_t *parent_al_macaddr)
+{
+	int i, j;
+
+	for (i = 0; i < next_level_neighbor->number_of_interfaces; ++i) {
+		const struct local_iface *iface = &next_level_neighbor->ifaces[i];
+
+		for (j = 0; j < iface->number_of_neighbors; ++j) {
+			if (hwaddr_equal(&iface->neighbors_al_macs[j][0], parent_al_macaddr))
+				return iface;
+		}
+	}
+
+	return NULL;
+}
+
+static bool set_device_child_neighbors(const struct bh_topology_dev *parent_device,
+				       struct list_head *bh_topo_dev_list)
+{
+	int i, j;
+	const int8_t child_level = parent_device->bh_info.level_in_tree + 1;
+	bool has_child_neighbor = false;
+
+	for (i = 0; i < parent_device->number_of_interfaces; ++i) {
+		const struct local_iface *parent_iface = &parent_device->ifaces[i];
+
+		for (j = 0; j < parent_iface->number_of_neighbors; ++j) {
+
+			struct bh_topology_dev *child_neighbor_dev =
+				find_bh_topology_device(&parent_iface->neighbors_al_macs[j][0]);
+
+			if (child_neighbor_dev &&
+			    child_neighbor_dev->bh_info.level_in_tree == UNKNOWN_TREE_LEVEL) {
+				const struct local_iface *child_iface;
+
+				child_neighbor_dev->bh_info.parent_in_tree = parent_device;
+				child_neighbor_dev->bh_info.parent_iface = parent_iface;
+				child_neighbor_dev->bh_info.level_in_tree = child_level;
+				child_neighbor_dev->bh_info.wifi_hops_from_root =
+					parent_device->bh_info.wifi_hops_from_root;
+				if (is_wifi(parent_iface->media_type))
+					child_neighbor_dev->bh_info.wifi_hops_from_root++;
+
+				child_iface = find_neighbor_local_iface(child_neighbor_dev,
+									parent_device->al_macaddr);
+
+				if (!child_iface) {
+					warn("BH:%s: child doens't know about parent neighbor.\n", __func__);
+				}
+
+				child_neighbor_dev->bh_info.own_iface = child_iface;
+
+				if (child_iface && (child_iface->media_type != parent_iface->media_type)) {
+					warn("BH:%s: child and parent iface media type differs, parent: %s, child: %s\n",
+					     i1905_media_type_to_str(parent_iface->media_type),
+					     i1905_media_type_to_str(child_iface->media_type),
+					     __func__);
+				}
+
+				dbg("BH: %s New parent-child link set:\n", __func__);
+				dbg_dump_bh_topo_link(child_neighbor_dev, INDENT_LVL_0);
+
+				has_child_neighbor = true;
+			}
+		}
+	}
+
+	return has_child_neighbor;
+}
+
+static bool set_next_level_neighbors(int8_t current_level,
+				     struct list_head *bh_topo_dev_list)
+{
+	struct bh_topology_dev *bh_topo_dev = NULL;
+	bool new_level_discovered = false;
+
+	list_for_each_entry(bh_topo_dev, bh_topo_dev_list, list_element) {
+		if (bh_topo_dev->bh_info.level_in_tree == current_level) {
+			bool has_next_level =
+				set_device_child_neighbors(bh_topo_dev, bh_topo_dev_list);
+
+			new_level_discovered = new_level_discovered || has_next_level;
+		}
+	}
+
+	dbg("BH: %s: current_level: %d, new_level_discovered: %d\n",
+	    __func__, current_level, new_level_discovered);
+
+	return new_level_discovered;
+}
+
+void build_bh_topology_tree(const uint8_t *ctrl_al_macaddr)
+{
+	struct bh_topology_dev *ctrl_dev = NULL;
+	bool new_level_discovered = false;
+	int8_t current_level;
+
+	dbg("BH: %s\n", __func__);
+
+	clear_backhaul_tree_relations(&priv_bh_topo_data.dev_list);
+
+	/* Mark controller as root node in BH tree (level_in_tree = 0) */
+	ctrl_dev = find_bh_topology_device(ctrl_al_macaddr);
+	if (!ctrl_dev) {
+		err("%s: cannot find controller (root ndode) and build tree.\n",
+			__func__);
+		return;
+	}
+	ctrl_dev->bh_info.level_in_tree = 0;
+	ctrl_dev->bh_info.wifi_hops_from_root = 0;
+
+	current_level = ctrl_dev->bh_info.level_in_tree;
+
+	do {
+		new_level_discovered =
+			set_next_level_neighbors(current_level, &priv_bh_topo_data.dev_list);
+		++current_level;
+	} while (new_level_discovered);
+
+	priv_bh_topo_data.bh_topology_valid = true;
+
+	info("%s: Backhaul topology tree built. BH topo devs num: %d, cntrl: " MACFMT "\n",
+		__func__,  priv_bh_topo_data.bh_topo_devs_number, MAC2STR(ctrl_al_macaddr));
+
+	dbg_dump_bh_topo_devs(&priv_bh_topo_data.dev_list, INDENT_LVL_0);
+}
diff --git a/src/backhaul_topology.h b/src/backhaul_topology.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbad280f855fc2f8cca6f24ee461ce0e2b6fcc46
--- /dev/null
+++ b/src/backhaul_topology.h
@@ -0,0 +1,122 @@
+#ifndef BACKHAUL_TOPOLOGY_H
+#define BACKHAUL_TOPOLOGY_H
+
+#include <libubox/list.h>
+#include <stdint.h>
+
+struct tlv_device_info;
+struct tlv_1905neighbor;
+struct bh_topology_dev;
+
+#define IFACE_MAX_NUM 16
+#define NEIGHBORS_MAX_NUM 16
+
+struct local_iface {
+	uint8_t macaddr[6];
+	uint16_t media_type;
+	uint8_t number_of_neighbors;
+	uint8_t neighbors_al_macs[NEIGHBORS_MAX_NUM][6];
+};
+
+ /* When populated BH link defined as in TR-181:
+  * BHDevALMac/BHLocalMac() <-LinkType -< LocalMac
+  * can be read. Above corresponds to:
+  * parent_in_tree/parent_iface <--own_iface->media_type-< own_iface
+  */
+#define UNKNOWN_TREE_LEVEL -1
+struct backhaul_info {
+	int8_t level_in_tree;		/** total number of BH links from root node */
+	uint8_t wifi_hops_from_root;	/** number of Wi-Fi BH links from root node */
+	const struct bh_topology_dev *parent_in_tree;
+	const struct local_iface *parent_iface;
+	const struct local_iface *own_iface;
+};
+
+/** Represents single device and its relation to neighbors in BH tree. */
+struct bh_topology_dev {
+	struct list_head list_element;
+
+	uint8_t al_macaddr[6];
+	uint8_t number_of_interfaces;
+	uint8_t num_of_ifaces_with_neighbors;
+	struct local_iface ifaces[IFACE_MAX_NUM];
+	struct backhaul_info bh_info;
+};
+
+/** Initializes internal structures.
+ *  Has to be called once before any other functions are called.
+ */
+void init_bh_topology(void);
+
+/** Releases all data and internal structures. */
+void free_bh_topology(void);
+
+/** Returns tree if backhaul topology tree was already build and is still valid.
+ *
+ *  It's undefined behaviour to read interface, neighbor and backhaul info
+ *  of any bh_topology_dev when this function returns false.
+ */
+bool is_bh_topology_valid(void);
+
+/** Number of devices in backhaul topology tree. */
+uint32_t bh_topology_devs_number(void);
+
+/** Returns backhaul topology device if exists in the model, NULL otherwise. */
+struct bh_topology_dev *find_bh_topology_device(const uint8_t *al_macaddr);
+
+/** Adds backhaul topology device to the model, invalidates backhaul tree. */
+struct bh_topology_dev *add_bh_topology_device(const uint8_t *al_macaddr);
+
+/** Removes backhaul topology device from the model, invalidates backhaul tree. */
+void remove_bh_topology_device(const uint8_t *al_macaddr);
+
+/** Removes all backhaul topology devices from the model, invalidates backhaul tree. */
+void remove_all_bh_topology_devices(void);
+
+/**
+ * Returns true if interface information provided with tlv_device_info differs
+ * from data already stored in bh_topology_dev (model).
+ * To reduce time complexity, it's assumed interfaces received in TLV are always ordered
+ * the same way. Also only number of interfaces and their MAC addresses are compared.
+ */
+bool has_interface_info_changed(const struct tlv_device_info *tlv_dev_info,
+				const struct bh_topology_dev *bh_topo_dev);
+/**
+ * Copies interface details from tlv_device_info into bh_topology_dev.
+ * Invalidates backhaul tree.
+ */
+void copy_interface_info_from_tlv(const struct tlv_device_info *tlv_dev_info,
+				  struct bh_topology_dev *bh_topo_dev);
+
+/**
+ * Returns true if neighbor information provided with 1905 neighbor TLVs differs
+ * from data already stored in bh_topology_dev (model).
+ */
+bool has_neighbor_info_changed(const struct tlv_1905neighbor **neighbor_tlvs,
+			       const uint16_t *tlv_lengths,
+			       uint8_t tlv_number,
+			       const struct bh_topology_dev *bh_topo_dev);
+
+/**
+ * Copies neighbors details from 1905 neighbor TLVs into bh_topology_dev.
+ * Invalidates backhaul tree.
+ */
+void copy_neighbor_info_from_tlvs(const struct tlv_1905neighbor **neighbor_tlvs,
+				  const uint16_t *tlv_lengths,
+				  uint8_t tlv_number,
+				  struct bh_topology_dev *bh_topo_dev);
+
+/**
+ * Builds backhaul topology tree from unordered list of bh_topology_dev structs
+ * by setting backhaul_info fields.
+ *
+ * Controller is always root node in tree. Edges between tree nodes represent
+ * BH links, parent nodes provides BH links to child node - each child node
+ * can have only one parent (be connected over one BH link).
+ *
+ * After calling it, is_bh_topology_valid() will return true, until next
+ * input data change (device add/remove, interface or neighbor info change.)
+ */
+void build_bh_topology_tree(const uint8_t *ctrl_al_macaddr);
+
+#endif /* BACKHAUL_TOPOLOGY_H */
diff --git a/src/backhaul_topology_dbg.c b/src/backhaul_topology_dbg.c
new file mode 100644
index 0000000000000000000000000000000000000000..dcd7e2b08702b1a20c0897616742752a2ca7678e
--- /dev/null
+++ b/src/backhaul_topology_dbg.c
@@ -0,0 +1,151 @@
+#include "backhaul_topology_dbg.h"
+#include "backhaul_topology.h"
+#include "utils/debug.h"
+
+#include <easy/utils.h>
+#include <libubox/list.h>
+
+#define BH_TOPO_PREFIX "BH:"
+
+static const char *get_prefix(enum dbg_bh_topo_indent_lvl indent_level)
+{
+	switch (indent_level) {
+	case INDENT_LVL_0: return BH_TOPO_PREFIX;
+	case INDENT_LVL_1: return BH_TOPO_PREFIX"  ";
+	case INDENT_LVL_2: return BH_TOPO_PREFIX"    ";
+	case INDENT_LVL_3: return BH_TOPO_PREFIX"      ";
+	default:           return BH_TOPO_PREFIX"        ";
+	}
+}
+
+const char *i1905_media_type_to_str(uint16_t media_type)
+{
+	switch (media_type >> 8) {
+	case 0:  return "Ethernet";
+	case 1:  return "Wi-Fi";
+	default: return "Other";
+	}
+}
+
+void dbg_dump_bh_topo_backhaul_info(const struct backhaul_info *bh_info,
+				    enum dbg_bh_topo_indent_lvl indent_lvl)
+{
+	const char *pref = get_prefix(indent_lvl);
+
+	dbg("%s backhaul_info:\n", pref);
+
+	if (bh_info->level_in_tree == UNKNOWN_TREE_LEVEL) {
+		dbg("%s .level_in_tree: %s\n", pref, "UNKNOWN_TREE_LEVEL");
+	} else {
+		dbg("%s .level_in_tree: %d\n", pref, bh_info->level_in_tree);
+		dbg("%s .wifi_hops_from_root: %d\n", pref, bh_info->wifi_hops_from_root);
+	}
+
+	if (bh_info->parent_in_tree) {
+		dbg("%s .parent_in_tree: " MACFMT "\n", pref,
+		    MAC2STR(bh_info->parent_in_tree->al_macaddr));
+	}
+
+	if (bh_info->parent_iface) {
+		dbg("%s .parent_iface: " MACFMT "\n", pref,
+		    MAC2STR(bh_info->parent_iface->macaddr));
+	}
+
+	if (bh_info->own_iface) {
+		dbg("%s .own_iface: " MACFMT "\n", pref,
+		    MAC2STR(bh_info->own_iface->macaddr));
+	}
+}
+
+void dbg_dump_bh_topo_local_iface(const struct local_iface *local_iface,
+				  enum dbg_bh_topo_indent_lvl indent_lvl)
+{
+	const char *pref = get_prefix(indent_lvl);
+	int i;
+
+	dbg("%s local_iface:\n", pref);
+	dbg("%s .macaddr: " MACFMT "\n", pref, MAC2STR(local_iface->macaddr));
+	dbg("%s .media_type: %s\n", pref,
+	    i1905_media_type_to_str(local_iface->media_type));
+	dbg("%s .number_of_neighbors: %u\n", pref, local_iface->number_of_neighbors);
+
+	for (i = 0; i < local_iface->number_of_neighbors; ++i) {
+		dbg("%s .neigh_al_mac[%d]: " MACFMT "\n",
+			pref, i, MAC2STR(&local_iface->neighbors_al_macs[i][0]));
+	}
+}
+
+void dbg_dump_bh_topo_dev(const struct bh_topology_dev *bh_topo_dev,
+			  enum dbg_bh_topo_indent_lvl indent_lvl)
+{
+	const char *pref = get_prefix(indent_lvl);
+	int i;
+
+	dbg("%s bh_topology_dev:\n", pref);
+	dbg("%s .al_macaddr: " MACFMT "\n", pref, MAC2STR(bh_topo_dev->al_macaddr));
+	dbg("%s .number_of_interfaces: %d\n", pref, bh_topo_dev->number_of_interfaces);
+	dbg("%s .num_of_ifaces_with_neighbors: %d\n", pref, bh_topo_dev->num_of_ifaces_with_neighbors);
+
+	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
+		dbg("%s .iface[%d]:\n", pref, i);
+		dbg_dump_bh_topo_local_iface(&bh_topo_dev->ifaces[i], indent_lvl + 1);
+	}
+
+	dbg("%s .bh_info:\n", pref);
+	dbg_dump_bh_topo_backhaul_info(&bh_topo_dev->bh_info, indent_lvl + 1);
+
+}
+
+void dbg_dump_bh_topo_devs(const struct list_head *bh_topo_dev_list,
+			   enum dbg_bh_topo_indent_lvl indent_lvl)
+{
+	const char *pref = get_prefix(indent_lvl);
+	const struct bh_topology_dev *bh_topo_dev = NULL;
+	int i = 0;
+
+	dbg("%s =============== %s ===============\n", pref, __func__);
+
+	list_for_each_entry(bh_topo_dev, bh_topo_dev_list, list_element) {
+		dbg("%s bh_topo_dev[%d]:\n", pref, i++);
+		dbg_dump_bh_topo_dev(bh_topo_dev, ++indent_lvl);
+
+	}
+
+	dbg("%s ============ END %s ==============\n", pref, __func__);
+}
+
+void dbg_dump_bh_topo_link(const struct bh_topology_dev *bh_topo_dev,
+			   enum dbg_bh_topo_indent_lvl indent_lvl)
+{
+	const char *pref = get_prefix(indent_lvl);
+	const struct bh_topology_dev *parent_dev =
+		bh_topo_dev->bh_info.parent_in_tree;
+	const struct local_iface *parent_iface =
+		bh_topo_dev->bh_info.parent_iface;
+	const struct local_iface *own_iface =
+		bh_topo_dev->bh_info.own_iface;
+
+	if (bh_topo_dev->bh_info.level_in_tree == UNKNOWN_TREE_LEVEL) {
+		dbg("%s child level: %s\n", pref, "UNKNOWN_TREE_LEVEL");
+	} else {
+		dbg("%s child level: %d\n", pref, bh_topo_dev->bh_info.level_in_tree);
+	}
+
+	dbg("%s child wifi_hops_from_root: %d\n", pref, bh_topo_dev->bh_info.wifi_hops_from_root);
+	dbg("%s child almac: " MACFMT "\n", pref, MAC2STR(bh_topo_dev->al_macaddr));
+
+	if (own_iface) {
+		dbg("%s child iface: " MACFMT "\n", pref, MAC2STR(own_iface->macaddr));
+	}
+
+	if (parent_dev) {
+		dbg("%s parent level: %d\n", pref, parent_dev->bh_info.level_in_tree);
+		dbg("%s parent almac: " MACFMT "\n", pref, MAC2STR(parent_dev->al_macaddr));
+	}
+
+	if (parent_iface) {
+		dbg("%s parent iface: " MACFMT "\n", pref, MAC2STR(parent_iface->macaddr));
+		dbg("%s media: %s\n", pref, i1905_media_type_to_str(parent_iface->media_type));
+	}
+}
+
diff --git a/src/backhaul_topology_dbg.h b/src/backhaul_topology_dbg.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9b38a3482c487b7e18ee7e20e02dbdf3c3e9d26
--- /dev/null
+++ b/src/backhaul_topology_dbg.h
@@ -0,0 +1,35 @@
+#ifndef BACKHAUL_TOPOLOGY_DBG_H
+#define BACKHAUL_TOPOLOGY_DBG_H
+
+#include <stdint.h>
+
+struct list_head;
+struct bh_topology_dev;
+struct local_iface;
+struct backhaul_info;
+
+enum dbg_bh_topo_indent_lvl {
+	INDENT_LVL_0 = 0,
+	INDENT_LVL_1 = 1,
+	INDENT_LVL_2 = 2,
+	INDENT_LVL_3 = 3,
+};
+
+const char *i1905_media_type_to_str(uint16_t media_type);
+
+void dbg_dump_bh_topo_backhaul_info(const struct backhaul_info *bh_info,
+				    enum dbg_bh_topo_indent_lvl indent_lvl);
+
+void dbg_dump_bh_topo_local_iface(const struct local_iface *local_iface,
+				  enum dbg_bh_topo_indent_lvl indent_lvl);
+
+void dbg_dump_bh_topo_dev(const struct bh_topology_dev *bh_topo_dev,
+			  enum dbg_bh_topo_indent_lvl indent_lvl);
+
+void dbg_dump_bh_topo_devs(const struct list_head *bh_topo_dev_list,
+			   enum dbg_bh_topo_indent_lvl indent_lvl);
+
+void dbg_dump_bh_topo_link(const struct bh_topology_dev *bh_topo_dev,
+			   enum dbg_bh_topo_indent_lvl indent_lvl);
+
+#endif
diff --git a/src/cntlr.c b/src/cntlr.c
index 1992b6d82ce269cfa2aef00f77c37ef7a6867fd3..09daacc0ad0247611588065129dbbcaa4dbcecab 100644
--- a/src/cntlr.c
+++ b/src/cntlr.c
@@ -44,6 +44,7 @@
 #include "cntlr.h"
 #include "allsta.h"
 #include "allmac.h"
+#include "backhaul_topology.h"
 #include "cntlr_ubus.h"
 #include "cntlr_ubus_dbg.h"
 #include "cntlr_map.h"
@@ -2008,6 +2009,7 @@ void run_controller(void)
 #endif
 	as_init_table(&c->as_table);
 	allmac_init_table(&c->mac_table);
+	init_bh_topology();
 
 	cmdu_ackq_init(&c->cmdu_ack_q);
 	c->cmdu_ack_q.timeout_cb = cntlr_ackq_timeout_cb;
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index 20df08c9d284f30938cf62228461da58e0200afd..7acde1d60fb42766843e818471374437a9d1855a 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -52,6 +52,7 @@
 #include "utils/utils.h"
 #include "utils/debug.h"
 #include "utils/liblist.h"
+#include "backhaul_topology.h"
 #include "config.h"
 #include "cntlr.h"
 #include "cntlr_ubus.h"
@@ -301,9 +302,14 @@ static int topology_response_vext(struct controller *c, struct node *n, struct t
 
 int handle_topology_response(void *cntlr, struct cmdu_buff *cmdu, struct node *n)
 {
-	trace("%s: --->\n", __func__);
 	struct controller *c = (struct controller *) cntlr;
 	struct tlv *tv[12][16] = {0};
+	struct bh_topology_dev *bh_topo_dev = NULL;
+	const struct tlv_1905neighbor *neighbor_tlvs[16] = { NULL };
+	uint8_t neigh_tlv_cnt = 0;
+	uint16_t tlv_lengths[16] = { 0 };
+
+	trace("%s: --->\n", __func__);
 
 	cntlr_set_link_profile(c, n, cmdu);
 
@@ -313,6 +319,71 @@ int handle_topology_response(void *cntlr, struct cmdu_buff *cmdu, struct node *n
 		return -1;
 	}
 
+	/* Device Information Type TLV */
+	if (tv[0][0]) {
+		const struct tlv_device_info *tlv_dev_info =
+			(struct tlv_device_info *)tv[0][0]->data;
+		bool new_bh_topo_dev = false;
+
+		dbg("%s: Device Info TLV from " MACFMT "\n", __func__,
+			MAC2STR(cmdu->origin));
+
+		bh_topo_dev =
+			find_bh_topology_device(tlv_dev_info->aladdr);
+
+		if (!bh_topo_dev) {
+			dbg("%s: New bh_topology_device\n", __func__);
+			bh_topo_dev = add_bh_topology_device(
+				tlv_dev_info->aladdr);
+
+			if (!bh_topo_dev) {
+				err("%s: Error in memory alloc\n",
+					__func__);
+				return -1;
+			}
+
+			new_bh_topo_dev = true;
+		}
+
+		if (new_bh_topo_dev ||
+			has_interface_info_changed(tlv_dev_info,
+						bh_topo_dev)) {
+			dbg("%s: New interface info for bh_topology_device\n",
+				__func__);
+
+			/* Copy new info and invalidate the model. */
+			copy_interface_info_from_tlv(tlv_dev_info,
+							bh_topo_dev);
+		}
+	}
+
+	if (!bh_topo_dev) {
+		err("%s: 1905 dev.info is missing. Logical error in\n",
+			__func__);
+		return -1;
+	}
+
+	/* 1905.1 neighbor device TLVs */
+	neigh_tlv_cnt = 0;
+	while (tv[3][neigh_tlv_cnt] && (neigh_tlv_cnt < 16)) {
+		neighbor_tlvs[neigh_tlv_cnt] =
+			(struct tlv_1905neighbor *)tv[3][neigh_tlv_cnt]->data;
+
+		tlv_lengths[neigh_tlv_cnt] =
+			tlv_length(tv[3][neigh_tlv_cnt]);
+		++neigh_tlv_cnt;
+	}
+
+	if (has_neighbor_info_changed(neighbor_tlvs, tlv_lengths,
+					neigh_tlv_cnt, bh_topo_dev)) {
+		dbg("%s: New neighbor info for bh_topology_device\n",
+			__func__);
+
+		/* Copy new info and invalidate the model. */
+		copy_neighbor_info_from_tlvs(neighbor_tlvs, tlv_lengths,
+						neigh_tlv_cnt, bh_topo_dev);
+	}
+
 	if (tv[7][0]) {
 		struct tlv_ap_oper_bss *tlv;
 		uint8_t *tv_data;
@@ -562,6 +633,11 @@ int handle_topology_response(void *cntlr, struct cmdu_buff *cmdu, struct node *n
 		cntrl_send_channel_preference_query(c, n->alid);
 	}
 
+	/* When all topology responses collected, build topology tree. */
+	if (!is_bh_topology_valid() && c->num_nodes == bh_topology_devs_number()) {
+		build_bh_topology_tree(c->almac);
+	}
+
 	return 0;
 }