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