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..3f08557c058534fdd266d7334366778b2e7236b3
--- /dev/null
+++ b/src/backhaul_topology.c
@@ -0,0 +1,231 @@
+
+#include "backhaul_topology.h"
+#include "backhaul_topology_dbg.h"
+#include "utils/debug.h"
+#include "utils/utils.h"
+
+#include <easy/utils.h>
+#include <libubox/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static bool is_wifi(uint16_t media_type)
+{
+	return (media_type >> 8) == 1;
+}
+
+static struct bh_topology_dev *
+find_bh_topology_device_priv(const uint8_t *al_macaddr,
+			     const 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) {
+		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr))
+			return bh_topo_dev;
+	}
+
+	return NULL;
+}
+
+struct bh_topology_dev *
+find_bh_topology_device(const uint8_t *al_macaddr,
+			const struct bh_topology_dev_list *bh_topo_dev_list)
+{
+	return find_bh_topology_device_priv(al_macaddr, &bh_topo_dev_list->list_head);
+}
+
+struct bh_topology_dev *add_bh_topology_device(const uint8_t *al_macaddr,
+					       struct bh_topology_dev_list *bh_topo_dev_list)
+{
+	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, &bh_topo_dev_list->list_head);
+
+	++bh_topo_dev_list->bh_topo_devs_number;
+	bh_topo_dev_list->bh_topology_updated = false;
+
+	return bh_topo_dev;
+}
+
+void remove_bh_topology_device(const uint8_t *al_macaddr,
+			       struct bh_topology_dev_list *bh_topo_dev_list)
+{
+	struct bh_topology_dev *bh_topo_dev = NULL, *bh_topo_dev_tmp;
+
+	list_for_each_entry_safe(bh_topo_dev, bh_topo_dev_tmp, &bh_topo_dev_list->list_head,
+				 list_element) {
+		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr)) {
+			list_del(&bh_topo_dev->list_element);
+			free(bh_topo_dev);
+
+			--bh_topo_dev_list->bh_topo_devs_number;
+			bh_topo_dev_list->bh_topology_updated = false;
+		}
+	}
+}
+
+void remove_all_bh_topology_devices(struct bh_topology_dev_list *bh_topo_dev_list)
+{
+	/* cppcheck-suppress uninitvar */
+	list_flush(&bh_topo_dev_list->list_head, struct bh_topology_dev, list_element);
+
+	bh_topo_dev_list->bh_topo_devs_number = 0;
+	bh_topo_dev_list->bh_topology_updated = false;
+}
+
+void clear_bh_topology_device_info(struct bh_topology_dev *bh_topo_dev)
+{
+	bh_topo_dev->number_of_interfaces = 0;
+	memset(bh_topo_dev->ifaces, 0, sizeof(bh_topo_dev->ifaces));
+	bh_topo_dev->bh_info =
+		(struct backhaul_info){ UNKNOWN_TREE_LEVEL, 0, NULL, NULL, NULL };
+}
+
+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_priv(
+				&parent_iface->neighbors_al_macs[j][0], bh_topo_dev_list);
+
+			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 set_backhaul_tree_relations(const uint8_t *ctrl_al_macaddr,
+				 struct bh_topology_dev_list *bh_topo_dev_list)
+{
+	struct bh_topology_dev *ctrl_dev = NULL;
+	bool new_level_discovered = false;
+	int8_t current_level;
+
+	clear_backhaul_tree_relations(&bh_topo_dev_list->list_head);
+
+	/* Mark controller as root node in BH tree (level_in_tree = 0) */
+	ctrl_dev = find_bh_topology_device(ctrl_al_macaddr, bh_topo_dev_list);
+	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, &bh_topo_dev_list->list_head);
+		++current_level;
+	} while (new_level_discovered);
+
+	bh_topo_dev_list->bh_topology_updated = true;
+
+	info("%s: Backhaul topology tree built. BH topo devs num: %d, cntrl: " MACFMT "\n",
+		__func__,  bh_topo_dev_list->bh_topo_devs_number, MAC2STR(ctrl_al_macaddr));
+
+	dbg_dump_bh_topo_devs(&bh_topo_dev_list->list_head, INDENT_LVL_0);
+}
diff --git a/src/backhaul_topology.h b/src/backhaul_topology.h
new file mode 100644
index 0000000000000000000000000000000000000000..404bd5d05764cf05ac52e822177bbdb0fc794b1d
--- /dev/null
+++ b/src/backhaul_topology.h
@@ -0,0 +1,81 @@
+#ifndef BACKHAUL_TOPOLOGY_H
+#define BACKHAUL_TOPOLOGY_H
+
+#include <libubox/list.h>
+#include <stdint.h>
+
+#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];
+};
+
+struct bh_topology_dev;
+#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.
+ *
+ *  When bh_info is populated the list of bh_topology_dev can be traversed
+ *  and for each device bh link defined as below can be read.
+ *
+ *  TR-181 BH link definition:
+ *  BHDevALMac/BHLocalMac <-LinkType -< LocalMac (EM device)
+ */
+struct bh_topology_dev {
+	struct list_head list_element;
+
+	uint8_t al_macaddr[6];
+	uint8_t number_of_interfaces;
+	struct local_iface ifaces[IFACE_MAX_NUM];
+
+	struct backhaul_info bh_info;
+};
+
+/** Used to store all discovered easy mesh devices and model tree topology. */
+struct bh_topology_dev_list {
+	struct list_head list_head; /* list of struct bh_topology_dev */
+	uint32_t bh_topo_devs_number;
+	bool bh_topology_updated;
+};
+
+struct bh_topology_dev *find_bh_topology_device(const uint8_t *al_macaddr,
+						const struct bh_topology_dev_list *bh_topo_dev_list);
+
+struct bh_topology_dev *add_bh_topology_device(const uint8_t *al_macaddr,
+					       struct bh_topology_dev_list *bh_topo_dev_list);
+
+void remove_bh_topology_device(const uint8_t *al_macaddr,
+			       struct bh_topology_dev_list *bh_topo_dev_list);
+
+void remove_all_bh_topology_devices(struct bh_topology_dev_list *bh_topo_dev_list);
+
+/**
+ * Clear all information about device interfaces, neighbors and its position in
+ * backhaul topolgy tree.
+ */
+void clear_bh_topology_device_info(struct bh_topology_dev *bh_topo_dev);
+
+/**
+ * Builds backhaul topolgy 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).
+ */
+void set_backhaul_tree_relations(const uint8_t *ctrl_al_macaddr,
+				 struct bh_topology_dev_list *bh_topo_dev_list);
+
+#endif /* BACKHAUL_TOPOLOGY_H */
diff --git a/src/backhaul_topology_dbg.c b/src/backhaul_topology_dbg.c
new file mode 100644
index 0000000000000000000000000000000000000000..35a8a5a4ee056469c31358260bc95b5adf53ddc7
--- /dev/null
+++ b/src/backhaul_topology_dbg.c
@@ -0,0 +1,149 @@
+#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);
+	}
+
+	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);
+
+	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