/* SPDX-License-Identifier: BSD-3-Clause */
/*
 * topology.c - build full network topology of the 1905 nodes.
 *
 * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 * See LICENSE file for license related information.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>


#include <json-c/json.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>

#include <easy/easy.h>
#include <wifiutils.h>

#include "util.h"
#include "timer.h"
#include "cmdu.h"
#include "1905_tlvs.h"
#include "i1905_extension.h"



#define IEEE1905_OBJECT_TOPOLOGY	"ieee1905.topology"

static int topology_init(void **priv, struct i1905_context *ieee1905);
static int topology_exit(void *priv);


struct getter_context {
	atimer_t t;
	uint32_t tmo;
	int retry;
	uint8_t state;
	uint16_t request;
	void *module;
	void *node;
};

struct neighbor {
	time_t tsp;
	atimer_t ageout;
	struct list_head list;
	uint8_t macaddr[6];		/* 1905-al address */
	uint8_t ifmacaddr[6];		/* neighbor's interface */
	uint8_t local_ifmacaddr[6];	/* interface in the 'node' device */
	bool has_bridge;
	void *node;			/* 'node' to which this is neighbor */
};

struct non1905_device {
	struct list_head list;
	uint8_t macaddr[6];
	uint8_t ifmacaddr[6];
	uint8_t local_ifmacaddr[6];
	void *node;
};

struct node_interface {
	uint8_t macaddr[6];
	uint16_t mediatype;
	uint8_t mediainfo[64];
	struct list_head list;
};

struct node_bridge_tuple {
	uint8_t num_macs;
	uint8_t *macaddrs;	/* array of interface macaddress */
	struct list_head list;
};

struct node_interface_ipv4 {
	uint8_t macaddr[6];
	uint8_t type;
	struct in_addr addr;
	struct in_addr dhcpserver;
	struct list_head list;
};

struct node_interface_ipv6 {
	uint8_t macaddr[6];
	uint8_t type;
	struct in6_addr addr;
	struct in6_addr origin;
	struct list_head list;
};

struct node {
	struct getter_context *gt;
	int probed;				/* for selfnode, always = 1 */
	time_t tsp;
	uint8_t macaddr[6];			/* AL address */
	uint8_t version;			/* 1905 profile version */
	uint8_t regband;			/* bitmap of IEEE80211_FREQUENCY_BAND_* */
	char name[65];
	char manufacturer[65];
	char model[65];
	char *url;

	int num_ifs;
	int num_neighbor;
	int num_non1905_neighbor;
	int num_brtuple;
	int num_ipv4;
	int num_ipv6;
	struct list_head list;			/* next in probelist */
	struct list_head iflist;		/* list of struct node_interface */
	struct list_head nbrlist;		/* list of struct neighbor */
	struct list_head non1905_nbrlist;	/* list of struct non1905_device */
	struct list_head brlist;		/* list of struct node_bridge_tuple */
	struct list_head ipv4list;		/* list of struct node_interface_ipv4 */
	struct list_head ipv6list;		/* list of struct node_interface_ipv6 */
};

struct net {
	int run;
	int num_nodes;
	struct list_head probelist;		/* list of struct node */
};

struct topology_private {
	void *module;
	uint16_t types[16];
	int num_types;
	int paused;
	atimer_t t;
	struct net *network;
	uint8_t alid[6];
	struct i1905_context ieee1905;
	struct ubus_context *ctx;
	struct ubus_object obj;
};

static const char *version_type2str(uint8_t v)
{
	if (v == PROFILE_1905_1)
		return "1905.1";
	else if (v == PROFILE_1905_1A)
		return "1905.1a";

	return "Unknown";
}

static const char *media_type2str(uint16_t m)
{
	switch (m) {
	case 0x0000: return "IEEE_802_3U_FAST_ETHERNET";
	case 0x0001: return "IEEE_802_3AB_GIGABIT_ETHERNET";
#ifdef ETH_MEDIA_EXTENSION
	case 0x0002: return "IEEE_802_3BZ_2_5G_ETHERNET";
	case 0x0003: return "IEEE_802_3BZ_5G_ETHERNET";
	case 0x0004: return "IEEE_802_3AE_10G_ETHERNET";
#endif
	case 0x0100: return "IEEE_802_11B_2_4_GHZ";
	case 0x0101: return "IEEE_802_11G_2_4_GHZ";
	case 0x0102: return "IEEE_802_11A_5_GHZ";
	case 0x0103: return "IEEE_802_11N_2_4_GHZ";
	case 0x0104: return "IEEE_802_11N_5_GHZ";
	case 0x0105: return "IEEE_802_11AC_5_GHZ";
	case 0x0106: return "IEEE_802_11AD_60_GHZ";
	case 0x0107: return "IEEE_802_11AF";
	case 0x0108: return "IEEE_802_11AX";
	case 0x0109: return "IEEE_802_11BE";
	case 0x0200: return "IEEE_1901_WAVELET";
	case 0x0201: return "IEEE_1901_FFT";
	case 0x0300: return "IEEE_MOCA_V1_1";
	default:
		     return "Unknown";
	}
}

static const char *role_type2str(uint8_t r)
{
	if (r == IEEE80211_ROLE_AP)
		return "ap";
	else if (r == IEEE80211_ROLE_STA)
		return "sta";
	else if (r == IEEE80211_ROLE_P2P_CLIENT)
		return "p2p_client";
	else if (r == IEEE80211_ROLE_P2P_GO)
		return "p2p_go";
	else if (r == IEEE80211_ROLE_AD_PCP)
		return "pcp";

	return "Unknown";
}

#define print_list(l,h,type)								\
do {											\
	type *__n;									\
	printf("%s { ", l);								\
	list_for_each_entry(__n, h, list) {						\
		printf(MACFMT", ", MAC2STR(__n->macaddr)); /* Flawfinder: ignore */	\
	}										\
	printf(" }\n");									\
} while(0)


static int topology_free_getter(struct node *dev);
static void topology_free_node(struct node *n);

int topology_send_cmdu(struct topology_private *priv, struct cmdu_buff *cmdu,
		       uint8_t *dst)
{
	uint16_t type = cmdu_get_type(cmdu);
	uint16_t mid = cmdu_get_mid(cmdu);

	return ieee1905_send_cmdu(&priv->ieee1905, dst, NULL, type, &mid,
				  cmdu->data, cmdu->datalen);
}

int topology_query(struct topology_private *priv, uint16_t cmdu_type,
		   struct node *dev, uint8_t *dst)
{
	uint32_t resp_timeout = 2; /* in seconds */
	struct getter_context *gt = dev->gt;
	struct cmdu_buff *req;
	uint16_t mid = 0;
	int ret = 0;


	req = cmdu_alloc_simple(cmdu_type, &mid);
	if (!req) {
		fprintf(stderr, "%s: -ENOMEM\n", __func__);
		return -1;
	}

	switch (cmdu_type) {
	case CMDU_TYPE_TOPOLOGY_QUERY:
		break;
	default:
		fprintf(stderr, "%s: Unexpected cmdu 0x%04x\n", __func__, cmdu_type);
		break;
	}

	cmdu_put_eom(req);
	ret = topology_send_cmdu(priv, req, dst);
	if (!ret) {
		gt->retry = 0;
		gt->tmo = resp_timeout * 1000;
		timer_set(&gt->t, gt->tmo);
	}

	cmdu_free(req);
	return ret;
}

int topology_hl_query(struct topology_private *priv, uint8_t *dst)
{
	struct cmdu_buff *req;
	uint16_t mid = 0;

	req = cmdu_alloc_simple(CMDU_TYPE_HIGHER_LAYER_QUERY, &mid);
	if (req) {
		cmdu_put_eom(req);
		topology_send_cmdu(priv, req, dst);
		cmdu_free(req);
	}

	return 0;
}

int topology_del_node_from_network(struct topology_private *priv, struct node *r)
{
	struct node *n = NULL, *tmp1;

	list_for_each_entry_safe(n, tmp1, &priv->network->probelist, list) {
		if (!memcmp(n->macaddr, r->macaddr, 6)) {
			list_del(&n->list);
			topology_free_getter(n);
			topology_free_node(n);
			priv->network->num_nodes--;

			return 0;
		}
	}

	return 0;
}

void getter_timer_cb(atimer_t *t)
{
	struct getter_context *gt = container_of(t, struct getter_context, t);
	struct topology_private *p = (struct topology_private *)gt->module;
	struct node *dev = (struct node *)gt->node;
	int ret;


	ret = topology_query(p, gt->request, dev, dev->macaddr);
	if (ret) {
		if (gt->retry++ < 1) {
			timer_set(&gt->t, 500);
			return;
		}

		/* no topo-response from this node; remove it */
		topology_del_node_from_network(p, dev);
	}
}

int topology_alloc_getter(struct topology_private *p, struct node *dev)
{
	dev->gt = calloc(1, sizeof(struct getter_context));
	if (!dev->gt)
		return -1;

	timer_init(&dev->gt->t, getter_timer_cb);
	dev->gt->module = p;
	dev->gt->node = dev;

	return 0;
}

int topology_free_getter(struct node *dev)
{
	if (!dev->gt)
		return 0;

	timer_del(&dev->gt->t);
	dev->gt->node = NULL;
	dev->gt->module = NULL;
	free(dev->gt);
	dev->gt = NULL;

	return 0;
}

int topology_sched_getter(struct topology_private *p, struct node *n,
			  uint16_t cmdutype, uint32_t after_ms)
{
	n->gt->request = cmdutype;
	return timer_set(&n->gt->t, after_ms);
}

struct node *topology_lookup_node(uint8_t *macaddr, struct list_head *list)
{
	struct node *n = NULL;

	list_for_each_entry(n, list, list) {
		if (!memcmp(n->macaddr, macaddr, 6)) {
			return n;
		}
	}

	return NULL;
}

struct node_interface *topology_lookup_interface(uint8_t *macaddr, struct list_head *list)
{
	struct node_interface *i = NULL;

	list_for_each_entry(i, list, list) {
		if (!memcmp(i->macaddr, macaddr, 6)) {
			return i;
		}
	}

	return NULL;
}

struct node *topology_alloc_node(uint8_t *macaddr)
{
	struct node *n;

	if (!macaddr || hwaddr_is_zero(macaddr))
		return NULL;

	n = calloc(1, sizeof(*n));
	if (!n)
		return NULL;

	memcpy(n->macaddr, macaddr, 6);
	INIT_LIST_HEAD(&n->iflist);
	INIT_LIST_HEAD(&n->nbrlist);
	INIT_LIST_HEAD(&n->non1905_nbrlist);
	INIT_LIST_HEAD(&n->brlist);
	INIT_LIST_HEAD(&n->ipv4list);
	INIT_LIST_HEAD(&n->ipv6list);
	return n;
}

void topology_free_interfaces_of_node(struct node *n)
{
	struct node_interface *ifs = NULL, *tmp;

	if (!n)
		return;

	list_for_each_entry_safe(ifs, tmp, &n->iflist, list) {
		list_del(&ifs->list);
		free(ifs);
	}

	n->num_ifs = 0;
}

void topology_free_ipaddrs_of_node(struct node *n)
{
	if (!n)
		return;

	list_flush(&n->ipv4list, struct node_interface_ipv4, list);
	n->num_ipv4 = 0;

	list_flush(&n->ipv6list, struct node_interface_ipv6, list);
	n->num_ipv6 = 0;
}

static void topology_free_neighbor(struct neighbor *n)
{
	timer_del(&n->ageout);
	n->node = NULL;
	free(n);
}

void topology_free_neighbors_of_node(struct node *n)
{
	struct neighbor *d = NULL, *tmp;

	if (!n)
		return;

	list_for_each_entry_safe(d, tmp, &n->nbrlist, list) {
		list_del(&d->list);
		topology_free_neighbor(d);
	}

	n->num_neighbor = 0;
}

void topology_free_non1905_neighbors_of_node(struct node *n)
{
	struct non1905_device *d = NULL, *tmp;

	if (!n)
		return;

	list_for_each_entry_safe(d, tmp, &n->non1905_nbrlist, list) {
		list_del(&d->list);
		d->node = NULL;
		free(d);
	}

	n->num_non1905_neighbor = 0;
}

void topology_free_brtuples_of_node(struct node *n)
{
	if (n && n->num_brtuple) {
		list_flush(&n->brlist, struct node_bridge_tuple, list);
		n->num_brtuple = 0;
	}
}

void topology_free_node(struct node *n)
{
	if (n) {
		topology_free_non1905_neighbors_of_node(n);
		topology_free_neighbors_of_node(n);
		topology_free_brtuples_of_node(n);
		topology_free_ipaddrs_of_node(n);
		topology_free_interfaces_of_node(n);
		free(n);
	}
}

struct neighbor *topology_lookup_neighbor(uint8_t *macaddr, struct list_head *list)
{
	struct neighbor *n = NULL;

	list_for_each_entry(n, list, list) {
		if (!memcmp(n->macaddr, macaddr, 6)) {
			return n;
		}
	}

	return NULL;
}

void ageout_neighbor_cb(atimer_t *t)
{
	struct neighbor *n = container_of(t, struct neighbor, ageout);
	struct node *nn = n->node;

	list_del(&n->list);
	nn->num_neighbor--;
	topology_free_neighbor(n);
}

struct neighbor *topology_alloc_neighbor(uint8_t *macaddr)
{
	struct neighbor *n;

	if (!macaddr || hwaddr_is_zero(macaddr))
		return NULL;

	n = calloc(1, sizeof(*n));
	if (!n)
		return NULL;

	memcpy(n->macaddr, macaddr, 6);
	timer_init(&n->ageout, ageout_neighbor_cb);
	time(&n->tsp);

	return n;
}

struct non1905_device *topology_alloc_non1905_neighbor(uint8_t *macaddr)
{
	struct non1905_device *n;

	if (!macaddr || hwaddr_is_zero(macaddr))
		return NULL;

	n = calloc(1, sizeof(*n));
	if (!n)
		return NULL;

	memcpy(n->macaddr, macaddr, 6);
	return n;
}

struct net *topology_alloc_net(void)
{
	struct net *n = calloc(1, sizeof(*n));

	if (!n)
		return NULL;

	INIT_LIST_HEAD(&n->probelist);
	n->num_nodes = 0;
	n->run = 1;

	return n;
}

void topology_free_net(struct topology_private *priv)
{
	struct node *n = NULL, *tmp_n;

	if (priv->network->num_nodes == 0 || list_empty(&priv->network->probelist))
		return;

	list_for_each_entry_safe(n, tmp_n, &priv->network->probelist, list) {
		list_del(&n->list);
		topology_free_getter(n);
		topology_free_node(n);
		priv->network->num_nodes--;
	}
}

struct topology_private *alloc_topology(void)
{
	struct topology_private *p;

	p = calloc(1, sizeof(*p));
	if (!p)
		return NULL;

	p->network = topology_alloc_net();
	if (!p->network) {
		free(p);
		return NULL;
	}

	return p;
}

int topology_update_interfaces_for_selfnode(struct topology_private *priv)
{
	uint8_t buf[1024] = {0};
	uint8_t *newbuf = NULL;
	struct node *selfnode;
	uint8_t *bufptr = buf;
	size_t sz = 1024;
	int ret;
	int i;


	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	topology_free_interfaces_of_node(selfnode);

	ret = ieee1905_get_local_interfaces(&priv->ieee1905, buf, &sz);
	if (ret && errno == -E2BIG) {
		newbuf = calloc(1, sz);
		if (!newbuf)
			return -1;

		ret = ieee1905_get_local_interfaces(&priv->ieee1905, newbuf, &sz);
		if (ret) {
			fprintf(stderr, "%s: Failed to get local interface list\n", __func__);
			free(newbuf);
			return -1;
		}
		bufptr = newbuf;
	}

	selfnode->num_ifs = sz / sizeof(struct i1905_local_interface);

	for (i = 0; i < selfnode->num_ifs; i++) {
		struct i1905_local_interface *e;
		struct node_interface *lif;

		lif = calloc(1, sizeof(*lif));
		if (!lif) {
			fprintf(stderr, "-ENOMEM: calloc failed\n");
			continue;
		}

		e = (struct i1905_local_interface *)bufptr + i;
		memcpy(lif->macaddr, e->if_macaddr, 6);
		lif->mediatype = e->mediatype;
		memcpy(lif->mediainfo, e->mediainfo, sizeof(e->mediainfo));


		list_add_tail(&lif->list, &selfnode->iflist);
	}

	free(newbuf);
	return 0;
}

int topology_update_ipaddrs_for_selfnode(struct topology_private *priv)
{
	uint8_t buf[1024] = {0};
	struct node *selfnode;
	size_t offset = 0;
	size_t sz = 1024;
	int ret;
	int i;


	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	topology_free_ipaddrs_of_node(selfnode);

	ret = ieee1905_get_interface_ipaddrs(&priv->ieee1905, buf, &sz);
	if (ret || !sz)
		return -1;

	while (offset < sz) {
		struct i1905_interface_ipaddress *ip =
			(struct i1905_interface_ipaddress *)(buf + offset);
		struct node_interface_ipv4 *ip4;
		struct node_interface_ipv6 *ip6;

		for (i = 0; i < ip->num; i++) {
			if (ip->addr[i].family == AF_INET) {
				ip4 = calloc(1, sizeof(*ip4));
				if (ip4) {
					memcpy(ip4->macaddr, ip->if_macaddr, 6);
					memcpy(&ip4->addr, &ip->addr[i].addr.ip4, sizeof(struct in_addr));
					list_add_tail(&ip4->list, &selfnode->ipv4list);
					selfnode->num_ipv4++;
				}
			} else if (ip->addr[i].family == AF_INET6) {
				ip6 = calloc(1, sizeof(*ip6));
				if (ip6) {
					memcpy(ip6->macaddr, ip->if_macaddr, 6);
					memcpy(&ip6->addr, &ip->addr[i].addr.ip6, sizeof(struct in6_addr));
					list_add_tail(&ip6->list, &selfnode->ipv6list);
					selfnode->num_ipv6++;
				}
			}
		}
		offset += sizeof(struct i1905_interface_ipaddress) +
				ip->num * sizeof(struct ip_address);
	}

	return 0;
}

int topology_update_brtuples_for_selfnode(struct topology_private *priv)
{
	uint8_t buf[1024] = {0};
	struct node *selfnode;
	size_t offset = 0;
	size_t sz = 1024;
	int ret;


	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	topology_free_brtuples_of_node(selfnode);

	ret = ieee1905_get_bridge_tuples(&priv->ieee1905, buf, &sz);
	if (ret || !sz)
		return -1;

	while (offset < sz) {
		struct i1905_brtuple *br = (struct i1905_brtuple *)(buf + offset);
		struct node_bridge_tuple *bri;

		bri = calloc(1, sizeof(*bri) + 6 * br->num);
		if (!bri)
			return -1;

		bri->macaddrs = (uint8_t *)(bri + 1);
		bri->num_macs = br->num;
		memcpy(bri->macaddrs, br->macaddrs, 6 * br->num);

		list_add_tail(&bri->list, &selfnode->brlist);
		selfnode->num_brtuple++;
		offset += sizeof(struct i1905_brtuple) + 6 * br->num;
	}

	return 0;
}

int topology_update_misc_for_selfnode(struct topology_private *priv)
{
	struct i1905_device_ident *misc;
	uint8_t buf[1024] = {0};
	struct node *selfnode;
	size_t sz = 1024;
	int ret;

	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	selfnode->regband = 0;
	selfnode->version = 0;
	memset(selfnode->name, 0, sizeof(selfnode->name));
	memset(selfnode->manufacturer, 0, sizeof(selfnode->manufacturer));
	memset(selfnode->model, 0, sizeof(selfnode->model));
	if (selfnode->url) {
		free(selfnode->url);
		selfnode->url = NULL;
	}

	ret = ieee1905_get_selfdevice_misc_info(&priv->ieee1905, buf, &sz);
	if (ret && errno == -E2BIG) {
		fprintf(stderr, "%s: Failed to get selfdevice misc info\n", __func__);
		return -1;
	}

	misc = (struct i1905_device_ident *)buf;
	selfnode->regband = misc->regband;
	selfnode->version = misc->version;
	memcpy(selfnode->name, misc->name, 64);
	memcpy(selfnode->manufacturer, misc->manufacturer, 64);
	memcpy(selfnode->model, misc->model, 64);
	selfnode->url = strdup(misc->url);

	return 0;
}

int topology_alloc_root_node(struct topology_private *priv, uint8_t *aladdr)
{
	struct node *rn;

	rn = topology_alloc_node(aladdr);
	if (!rn)
		return -1;

	rn->probed = 1;
	time(&rn->tsp);
	list_add_tail(&rn->list, &priv->network->probelist);
	priv->network->num_nodes++;

	topology_update_interfaces_for_selfnode(priv);
	topology_update_ipaddrs_for_selfnode(priv);
	topology_update_misc_for_selfnode(priv);
	topology_update_brtuples_for_selfnode(priv);

	return 0;
}

int topology_net_add_nodes(struct topology_private *priv, struct node *p)
{
	struct net *network = priv->network;
	struct neighbor *e = NULL;
	struct node *n = NULL;
	int num_nodes = network->num_nodes;

	list_for_each_entry(e, &p->nbrlist, list) {
		int in_probelist = 0;

		list_for_each_entry(n, &network->probelist, list) {
			if (!memcmp(e->macaddr, n->macaddr, 6)) {
				in_probelist = 1;
				break;
			}
		}

		if (!in_probelist) {
			struct node *q = topology_alloc_node(e->macaddr);

			if (q) {
				topology_alloc_getter(priv, q);
				list_add_tail(&q->list, &network->probelist);
				network->num_nodes++;
			}
		}
	}

	if (num_nodes != network->num_nodes)
		return 1;

	return 0;
}

int topology_probe_nodes(struct topology_private *priv)
{
	struct node *n = NULL;
	int ret = 0;
	time_t now;

	time(&now);
	list_for_each_entry(n, &priv->network->probelist, list) {
		if (n->probed)
			continue;

		if (difftime(now, n->tsp) < 30)
			continue;

		ret |= topology_sched_getter(priv, n, CMDU_TYPE_TOPOLOGY_QUERY, 1000);
	}

	return ret;
}

int topology_hl_query_nodes(struct topology_private *priv)
{
	struct node *n = NULL;

	list_for_each_entry(n, &priv->network->probelist, list) {
		if (n->probed)
			continue;

		if (hwaddr_is_zero(n->macaddr))
			continue;

		topology_hl_query(priv, n->macaddr);
	}

	return 0;
}

#if 0
int topology_update_nodes_info(void *arg)
{
	struct topology_private *p = (struct topology_private *)arg;
	struct node *n = NULL;
	int ret = 0;
	struct list_head *nodelist = &p->network->probelist.next;

	list_for_each_entry(n, nodelist, list) {
		uint8_t buf[1024] = {0};
		size_t sz = sizeof(buf);

		ret |= ieee1905_get_neighbor_info(p, n->macaddr, buf, &sz);
	}

	return ret;
}
#endif

uint8_t *topology_get_link_macaddr_for_nbr(struct topology_private *priv,
					   struct neighbor *f, struct node *t)
{
	struct node *n = topology_lookup_node(f->macaddr, &priv->network->probelist);
	struct neighbor *d;

	if (!n)
		return NULL;

	list_for_each_entry(d, &n->nbrlist, list) {
	       if (!memcmp(d->macaddr, t->macaddr, 6))
			return d->local_ifmacaddr;
	}

	return NULL;
}

int topology_add_nbrlist_for_node(struct topology_private *priv,
				  struct node *n, uint8_t *n_local_macaddr,
				  uint8_t *macaddrs, uint8_t *has_br, int num)
{
	int i;

	for (i = 0; i < num; i++) {
		struct neighbor *e = topology_alloc_neighbor(&macaddrs[i*6]);
		uint8_t *r_ifmacaddr = NULL;

		if (!e) {
			fprintf(stderr, "%s: -ENOMEM\n", __func__);
			return -1;
		}

		memcpy(e->local_ifmacaddr, n_local_macaddr, 6);
		r_ifmacaddr = topology_get_link_macaddr_for_nbr(priv, e, n);
		if (r_ifmacaddr)
			memcpy(e->ifmacaddr, r_ifmacaddr, 6);

		e->has_bridge = has_br[i];
		e->node = n;
		timer_set(&e->ageout, 62000);
		list_add_tail(&e->list, &n->nbrlist);
		n->num_neighbor++;
	}

	return 0;
}

int topology_add_non1905_nbrlist_for_node(struct node *n,
					  uint8_t *xnbr_local_macaddr,
					  uint8_t *macaddrs, int num)
{
	int i;

	for (i = 0; i < num; i++) {
		struct non1905_device *e = topology_alloc_non1905_neighbor(&macaddrs[i*6]);
		if (!e) {
			fprintf(stderr, "%s: -ENOMEM\n", __func__);
			return -1;
		}

		memcpy(e->local_ifmacaddr, xnbr_local_macaddr, 6);
		e->node = n;
		list_add_tail(&e->list, &n->non1905_nbrlist);
		n->num_non1905_neighbor++;
	}

	return 0;
}

int topology_update_non1905_nbrlist_for_selfnode(struct topology_private *priv)
{
	struct i1905_non1905_ifneighbor *ent;
	struct node *selfnode;
	size_t sz = 1024;
	uint8_t buf[sz];
	int pos = 0;
	int ret;


	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	topology_free_non1905_neighbors_of_node(selfnode);

	ret = ieee1905_get_non1905_neighbors(&priv->ieee1905, buf, &sz);
	if (ret && errno == -E2BIG) {
		fprintf(stderr, "%s: required bufsize = %zu\n", __func__, sz);	//TODO
		return -1;
	}

	while (pos < sz) {
		ent = (struct i1905_non1905_ifneighbor *)&buf[pos];
		ret = topology_add_non1905_nbrlist_for_node(selfnode,
							    ent->if_macaddr,
							    &ent->non1905_macaddr[0],
							    ent->num_non1905);
		if (ret)
			break;

		pos += sizeof(struct i1905_non1905_ifneighbor) + ent->num_non1905 * 6;
	}

	return 0;
}

int topology_update_brtuples_for_node(struct node *n, struct tlv *t)
{
	struct tlv_device_bridge_caps *brcaps =
			(struct tlv_device_bridge_caps *)t->data;
	int i;

	for (i = 0; i < brcaps->num_tuples; i++) {
		struct node_bridge_tuple *br = NULL;
		int num_macaddrs = brcaps->tuple[i].num_macaddrs;

		if (num_macaddrs == 0)
			continue;

		br = calloc(1, sizeof(*br) + 6 * num_macaddrs);
		if (!br)
			return -1;

		br->macaddrs = (uint8_t *)(br + 1);
		br->num_macs = num_macaddrs;
		memcpy(br->macaddrs, brcaps->tuple[i].addr, 6 * num_macaddrs);
		list_add_tail(&br->list, &n->brlist);
		n->num_brtuple++;
	}

	return 0;
}

int topology_process_topology_response(struct topology_private *priv, struct cmdu_buff *cmdu)
{
	struct tlv_policy pol[] = {
		[0] = { .type = TLV_TYPE_DEVICE_INFORMATION_TYPE, .present = TLV_PRESENT_ONE },
		[1] = { .type = TLV_TYPE_NEIGHBOR_DEVICE_LIST, .present = TLV_PRESENT_OPTIONAL_MORE },
		[2] = { .type = TLV_TYPE_NON_1905_NEIGHBOR_DEVICE_LIST, .present = TLV_PRESENT_OPTIONAL_MORE },
		[3] = { .type = TLV_TYPE_DEVICE_BRIDGING_CAPABILITIES, .present = TLV_PRESENT_OPTIONAL_MORE },
	};
	struct tlv_device_info *devinfo;
	struct tlv *tv[4][TLV_MAXNUM];
	uint8_t aladdr[6] = {0};
	struct node *e;
	uint8_t *ptr;
	int offset;
	int ret;
	int i;


	/* fprintf(stderr, "%s: rx-ifname = '%s'\n", __func__, cmdu->dev_ifname); */
	ret = cmdu_parse_tlvs(cmdu, tv, pol, 4);
	if (ret)
		return -1;

	if (!tv[0][0]) {
		fprintf(stderr, "Error! missing TLV DEVICE_INFORMATION_TYPE\n");
		return -1;
	}

	devinfo = (struct tlv_device_info *)tv[0][0]->data;
	ptr = (uint8_t *)devinfo;
	offset = sizeof(struct tlv_device_info);

	if (hwaddr_is_zero(devinfo->aladdr)) {
		fprintf(stderr, "%s: Discard topology response from aladdr = 00:00..\n", __func__);
		return -1;
	}

	if (hwaddr_equal(priv->alid, devinfo->aladdr)) {
		fprintf(stderr, "%s: Ignore topology response from self\n", __func__);
		return 0;
	}

	memcpy(aladdr, devinfo->aladdr, 6);
	if (devinfo->num_interface > 1 && !tv[3][0]) {
		fprintf(stderr,
			"%s: Discard topology response missing TLV DEVICE_BRIDGING_CAPABILITIES\n", __func__);
		return -1;
	}

	e = topology_lookup_node(aladdr, &priv->network->probelist);
	if (e) {
		topology_free_interfaces_of_node(e);
		topology_free_neighbors_of_node(e);
		topology_free_non1905_neighbors_of_node(e);
		topology_free_brtuples_of_node(e);
	} else {
		e = topology_alloc_node(aladdr);
		if (!e) {
			fprintf(stderr, "%s: -ENOMEM\n", __func__);
			return -1;
		}

		topology_alloc_getter(priv, e);
		list_add_tail(&e->list, &priv->network->probelist);
		priv->network->num_nodes++;
	}

	/* update interface list */
	for (i = 0; i < devinfo->num_interface; i++) {
		struct local_interface *tif;
		struct node_interface *ifs;

		ptr += offset;
		tif = (struct local_interface *)ptr;

		ifs = calloc(1, sizeof(*ifs));
		if (!ifs) {
			fprintf(stderr, "-ENOMEM\n");
			continue;
		}

		memcpy(ifs->macaddr, tif->macaddr, 6);
		ifs->mediatype = buf_get_be16((uint8_t *)&tif->mediatype);
		if (tif->sizeof_mediainfo > 0 && tif->sizeof_mediainfo <= 32)
			memcpy(ifs->mediainfo, tif->mediainfo, tif->sizeof_mediainfo);

		list_add_tail(&ifs->list, &e->iflist);
		e->num_ifs++;
		offset = sizeof(struct local_interface) + tif->sizeof_mediainfo;
	}

	/* update neighbor list */
	if (tv[1][0]) {
		int num = 0;

		while (num < TLV_MAXNUM && tv[1][num]) {
			struct tlv *t = tv[1][num];
			struct tlv_1905neighbor *nbr = (struct tlv_1905neighbor *)t->data;
			uint8_t node_local_macaddr[6] = {0};
			uint16_t rlen = tlv_length(t);
			uint8_t nbrlist[rlen];
			uint8_t has_br[rlen];
			int n = 0;


			memcpy(node_local_macaddr, nbr->local_macaddr, 6);
			rlen -= 6;

			memset(nbrlist, 0, rlen);
			memset(has_br, 0, rlen);
			while (rlen >= sizeof(struct i1905_neighbor)) {
				memcpy(&nbrlist[n * 6], nbr->nbr[n].aladdr, 6);
				has_br[n] = nbr->nbr[n].has_bridge;
				rlen -= sizeof(struct i1905_neighbor);
				n++;
			}

			ret = topology_add_nbrlist_for_node(priv,
							    e,
							    node_local_macaddr,
							    nbrlist,
							    has_br,
							    n);
			num++;
		}

#if 1	//debug
	char label[64] = {0};

	sprintf(label, "%s " MACFMT " =  ", "NEIGHBOR-LIST of", MAC2STR(e->macaddr)); /* Flawfinder: ignore */
	print_list(label, &e->nbrlist, struct neighbor);
#endif
	}

	/* update non-1905 neighbor list */
	if (tv[2][0]) {
		int num = 0;

		while (num < TLV_MAXNUM && tv[2][num]) {
			struct tlv *t = tv[2][num];
			struct tlv_non1905_neighbor *xnbr =
					(struct tlv_non1905_neighbor *)t->data;
			uint8_t node_local_macaddr[6] = {0};
			uint16_t rlen = tlv_length(t);
			uint8_t xnbrlist[rlen];
			int n = 0;


			memcpy(node_local_macaddr, xnbr->local_macaddr, 6);
			rlen -= 6;

			memset(xnbrlist, 0, rlen);
			while (rlen >= sizeof(struct non1905_neighbor)) {
				memcpy(&xnbrlist[n * 6], xnbr->non1905_nbr[n].macaddr, 6);
				rlen -= sizeof(struct non1905_neighbor);
				n++;
			}

			ret = topology_add_non1905_nbrlist_for_node(e,
								    node_local_macaddr,
								    xnbrlist,
								    n);
			num++;
		}

#if 1	//debug
	char label2[64] = {0};

	sprintf(label2, "%s " MACFMT " =  ", "NON-1905 NEIGHBOR-LIST of", MAC2STR(e->macaddr)); /* Flawfinder: ignore */
	print_list(label2, &e->non1905_nbrlist, struct non1905_device);
#endif
	}

	/* update bridge tuples */
	if (tv[3][0]) {
		int num = 0;

		while (num < TLV_MAXNUM && tv[3][num]) {
			ret = topology_update_brtuples_for_node(e, tv[3][num]);
			if (ret)
				break;
			num++;
		}
	}

	time(&e->tsp);
	priv->network->run = topology_net_add_nodes(priv, e);

	return ret;
}

int topology_update_device_ident_for_node(struct node *n, struct tlv *t)
{
	struct tlv_device_identification *ident =
				(struct tlv_device_identification *)t->data;

	memset(n->name, 0, sizeof(n->name));
	memcpy(n->name, ident->name, 64);

	memset(n->manufacturer, 0, sizeof(n->manufacturer));
	memcpy(n->manufacturer, ident->manufacturer, 64);

	memset(n->model, 0, sizeof(n->model));
	memcpy(n->model, ident->model, 64);

	return 0;
}

int topology_update_ctrl_url_for_node(struct node *n, struct tlv *t)
{
	struct tlv_control_url *ctrl;
	int url_len;

	if (n && n->url) {
		free(n->url);
		n->url = NULL;
	}

	if (!t)
		return 0;

	ctrl = (struct tlv_control_url *)t->data;
	url_len = tlv_length(t);
	if (url_len > 0) {
		char *url;

		url = calloc(url_len + 1, sizeof(char));
		if (!url)
			return -1;

		memcpy(url, ctrl->url, url_len);
		n->url = url;
	}

	return 0;
}

int topology_update_ipv4addrs_for_node(struct node *n, struct tlv *t)
{
	struct node_interface_ipv4 *ip4;
	struct tlv_ipv4 *ip;
	uint8_t *ptr;
	int offset;
	int i, j;


	list_flush(&n->ipv4list, struct node_interface_ipv4, list);
	n->num_ipv4 = 0;

	if (!t)
		return 0;

	ip = (struct tlv_ipv4 *)t->data;
	offset = sizeof(struct tlv_ipv4);
	ptr = t->data;

	for (i = 0; i < ip->num_interfaces; i++) {
		struct ipv4_interface *tif;

		ptr += offset;
		tif = (struct ipv4_interface *)ptr;
		offset = sizeof(struct ipv4_interface);

		for (j = 0; j < tif->num_ipv4; j++) {
			struct ipv4_entry *e;

			ptr += offset;
			e = (struct ipv4_entry *)ptr;

			ip4 = calloc(1, sizeof(*ip4));
			if (!ip4)
				return -1;

			memcpy(ip4->macaddr, tif->macaddr, 6);
			memcpy(&ip4->addr, e->address, sizeof(struct in_addr));
			ip4->type = e->type;
			memcpy(&ip4->dhcpserver, e->dhcpserver, sizeof(struct in_addr));
			list_add_tail(&ip4->list, &n->ipv4list);
			n->num_ipv4++;
			offset = sizeof(struct ipv4_entry);
		}
	}

	return 0;
}

int topology_update_ipv6addrs_for_node(struct node *n, struct tlv *t)
{
	struct node_interface_ipv6 *ip6;
	struct tlv_ipv6 *ip;
	uint8_t *ptr;
	int offset;
	int i, j;


	list_flush(&n->ipv6list, struct node_interface_ipv6, list);
	n->num_ipv6 = 0;

	if (!t)
		return 0;

	ip = (struct tlv_ipv6 *)t->data;
	offset = sizeof(struct tlv_ipv6);
	ptr = t->data;

	for (i = 0; i < ip->num_interfaces; i++) {
		struct ipv6_interface *tif;

		ptr += offset;
		tif = (struct ipv6_interface *)ptr;
		offset = sizeof(struct ipv6_interface);

		for (j = 0; j < tif->num_ipv6; j++) {
			struct ipv6_entry *e;

			ptr += offset;
			e = (struct ipv6_entry *)ptr;

			ip6 = calloc(1, sizeof(*ip6));
			if (!ip6)
				return -1;

			memcpy(ip6->macaddr, tif->macaddr, 6);
			memcpy(&ip6->addr, e->address, sizeof(struct in6_addr));
			ip6->type = e->type;
			memcpy(&ip6->origin, e->origin, sizeof(struct in6_addr));
			list_add_tail(&ip6->list, &n->ipv6list);
			n->num_ipv6++;
			offset = sizeof(struct ipv6_entry);
		}
	}

	return 0;
}

int topology_process_hl_response(struct topology_private *priv, struct cmdu_buff *cmdu)
{
	struct tlv_policy pol[] = {
		[0] = { .type = TLV_TYPE_AL_MAC_ADDRESS_TYPE, .present = TLV_PRESENT_ONE },
		[1] = { .type = TLV_TYPE_1905_PROFILE_VERSION, .present = TLV_PRESENT_ONE },
		[2] = { .type = TLV_TYPE_DEVICE_IDENTIFICATION, .present = TLV_PRESENT_ONE },
		[3] = { .type = TLV_TYPE_CONTROL_URL, .present = TLV_PRESENT_OPTIONAL_ONE},
		[4] = { .type = TLV_TYPE_IPV4, .present = TLV_PRESENT_OPTIONAL_ONE},
		[5] = { .type = TLV_TYPE_IPV6, .present = TLV_PRESENT_OPTIONAL_ONE},
	};
	struct tlv_1905_profile *profile;
	struct tlv *tv[6][TLV_MAXNUM];
	uint8_t aladdr[6] = {0};
	struct node *e;
	int ret;


	ret = cmdu_parse_tlvs(cmdu, tv, pol, 6);
	if (ret)
		return -1;

	memcpy(aladdr, tv[0][0]->data, tlv_length(tv[0][0]));
	if (hwaddr_is_zero(aladdr)) {
		fprintf(stderr, "%s: Discard HL Response from aladdr = 0\n", __func__);
		return -1;
	}

	e = topology_lookup_node(aladdr, &priv->network->probelist);
	if (!e)
		return 0;

	profile = (struct tlv_1905_profile *)tv[1][0]->data;
	e->version = profile->version;

	topology_update_device_ident_for_node(e, tv[2][0]);
	topology_update_ctrl_url_for_node(e, tv[3][0]);
	topology_update_ipv4addrs_for_node(e, tv[4][0]);
	topology_update_ipv6addrs_for_node(e, tv[5][0]);

	return 0;
}

int topology_update_nbrlist_for_selfnode(struct node *self, uint8_t *ifmacaddr,
					 uint8_t *nbr_aladdr, uint8_t *nbr_ifmacaddr)
{
	struct neighbor *d, *tmp;
	struct neighbor *e;


	list_for_each_entry_safe(d, tmp, &self->nbrlist, list) {
	       if (!memcmp(d->macaddr, nbr_aladdr, 6)) {
			/* update link's macaddresses */
			memcpy(d->local_ifmacaddr, ifmacaddr, 6);
			memcpy(d->ifmacaddr, nbr_ifmacaddr, 6);
			timer_set(&d->ageout, 62000);

			return 0;
	       }
	}

	e = topology_alloc_neighbor(nbr_aladdr);
	if (!e) {
		fprintf(stderr, "%s: -ENOMEM\n", __func__);
		return -1;
	}

	memcpy(e->local_ifmacaddr, ifmacaddr, 6);
	memcpy(e->ifmacaddr, nbr_ifmacaddr, 6);
	e->node = self;
	list_add_tail(&e->list, &self->nbrlist);
	self->num_neighbor++;
	timer_set(&e->ageout, 62000);

	return 0;
}

int topology_process_topology_discovery(struct topology_private *priv, struct cmdu_buff *cmdu)
{
	struct tlv_policy pol[] = {
		[0] = { .type = TLV_TYPE_AL_MAC_ADDRESS_TYPE, .present = TLV_PRESENT_ONE },
		[1] = { .type = TLV_TYPE_MAC_ADDRESS_TYPE, .present = TLV_PRESENT_ONE },
	};
	struct tlv *tv[2][TLV_MAXNUM];
	struct node *e, *selfnode;
	uint8_t ifmacaddr[6] = {0};
	uint8_t aladdr[6] = {0};
	int ret;


	ret = cmdu_parse_tlvs(cmdu, tv, pol, 2);
	if (ret) {
		fprintf(stdout, "%s: Error! cmdu_parse_tlvs()\n", __func__);
		return -1;
	}

	if (!tv[0][0] || !tv[1][0]) {
		fprintf(stdout, "%s: Error! missing TLV(s) in topology discovery\n", __func__);
		return -1;
	}

	memcpy(aladdr, tv[0][0]->data, tlv_length(tv[0][0]));
	memcpy(ifmacaddr, tv[1][0]->data, tlv_length(tv[1][0]));
	if (hwaddr_is_zero(aladdr)) {
		fprintf(stdout, "%s: Invalid topology discovery: aladdr = " MACFMT "\n",
		    __func__, MAC2STR(aladdr));
		return -1;
	}

	/* getting topology-discovery from selfdevice through 'lo' */
	if (!memcmp(priv->alid, aladdr, 6)) {
		fprintf(stdout, "%s: Ignore topology discovery from self\n", __func__);
		return 0;
	}

	fprintf(stdout, "%s: RECEIVED Topology Discovery from " MACFMT " on = '%s'\n", __func__,
		MAC2STR(aladdr), cmdu->dev_ifname);

	selfnode = list_first_entry(&priv->network->probelist, struct node, list);
	ret = topology_update_nbrlist_for_selfnode(selfnode, cmdu->dev_macaddr,
						   aladdr, ifmacaddr);

	e = topology_lookup_node(aladdr, &priv->network->probelist);
	if (!e) {
		fprintf(stdout, "%s: ADDING " MACFMT " to probelist\n", __func__, MAC2STR(aladdr));
		e = topology_alloc_node(aladdr);
		if (!e) {
			fprintf(stderr, "%s: -ENOMEM\n", __func__);
			return -1;
		}

		topology_alloc_getter(priv, e);
		list_add_tail(&e->list, &priv->network->probelist);
		priv->network->num_nodes++;
	}

	time(&e->tsp);
	return ret;
}

void refresh_timer_cb(atimer_t *t)
{
	struct topology_private *p = container_of(t, struct topology_private, t);

	topology_update_interfaces_for_selfnode(p);
	topology_update_ipaddrs_for_selfnode(p);
	topology_update_non1905_nbrlist_for_selfnode(p);
	topology_update_misc_for_selfnode(p);
	topology_update_brtuples_for_selfnode(p);

	topology_probe_nodes(p);
	topology_hl_query_nodes(p);

	timer_set(t, 5000);
}

int topology_recv_cmdu(void *priv, struct cmdu_buff *frm)
{
	struct topology_private *p = (struct topology_private *)priv;

	if (!p->ctx || !frm)
		return -1;

	if (!p->paused) {
		uint16_t cmdutype = cmdu_get_type(frm);

		switch (cmdutype) {
		case CMDU_TYPE_TOPOLOGY_DISCOVERY:
			topology_process_topology_discovery(p, frm);
			break;
		case CMDU_TYPE_TOPOLOGY_RESPONSE:
			topology_process_topology_response(p, frm);
			break;
		case CMDU_TYPE_HIGHER_LAYER_RESPONSE:
			topology_process_hl_response(p, frm);
			break;
		default:
			break;
		}
	}

	return CMDU_OK;
}

int topology_start(void *priv)
{
	struct topology_private *p = (struct topology_private *)priv;

	p->paused = 0;
	timer_set(&p->t, 1000);

	return 0;
}

int topology_stop(void *priv)
{
	struct topology_private *p = (struct topology_private *)priv;

	timer_del(&p->t);
	p->paused = 1;

	return 0;
}

struct i1905_cmdu_extension i1905_topology_sub[] = {
	{ .type = CMDU_TYPE_TOPOLOGY_DISCOVERY },
	{ .type = CMDU_TYPE_TOPOLOGY_RESPONSE },
	{ .type = CMDU_TYPE_HIGHER_LAYER_RESPONSE },
};

extern struct i1905_extmodule topology;
struct i1905_extmodule topology = {
	.id = "\x10\x20\x30\x40",
	.name = "topology",
	.init = topology_init,
	.exit = topology_exit,
	.start = topology_start,
	.stop = topology_stop,
	.ext = i1905_topology_sub,
	.num_ext = sizeof(i1905_topology_sub)/sizeof(i1905_topology_sub[0]),
	.process_cmdu = topology_recv_cmdu,
};

int topology_dump(struct ubus_context *ctx, struct ubus_object *obj,
			struct ubus_request_data *req, const char *method,
			struct blob_attr *msg)
{
	struct topology_private *p = container_of(obj, struct topology_private, obj);
	struct non1905_device *non = NULL;
	void *a, *aa, *aaa, *aaaa;
	struct blob_buf bb = {0};
	struct neighbor *nbr = NULL;
	struct node *n = NULL;


	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	blobmsg_add_u32(&bb, "num_nodes", p->network->num_nodes);
	a = blobmsg_open_array(&bb, "nodes");
	list_for_each_entry(n, &p->network->probelist, list) {
		char n_macstr[18] = {0};
		char nbr_macstr[18] = {0};
		struct node_interface *ifs = NULL;
		struct node_interface_ipv4 *ipv4 = NULL;
		struct node_interface_ipv6 *ipv6 = NULL;
		struct node_bridge_tuple *br = NULL;
		void *tt;

		aa = blobmsg_open_table(&bb, "");
		hwaddr_ntoa(n->macaddr, n_macstr);
		blobmsg_add_string(&bb, "ieee1905id", n_macstr);
		blobmsg_add_string(&bb, "version", version_type2str(n->version));
		blobmsg_add_string(&bb, "name", n->name);
		blobmsg_add_string(&bb, "manufacturer", n->manufacturer);
		blobmsg_add_string(&bb, "model", n->model);
		blobmsg_add_string(&bb, "url", n->url ? n->url : "");

		blobmsg_add_u32(&bb, "num_ipv4", n->num_ipv4);
		blobmsg_add_u32(&bb, "num_ipv6", n->num_ipv6);
		blobmsg_add_u32(&bb, "num_interface", n->num_ifs);
		blobmsg_add_u32(&bb, "num_neighbor_1905", n->num_neighbor);
		blobmsg_add_u32(&bb, "num_neighbor_non1905", n->num_non1905_neighbor);
		blobmsg_add_u32(&bb, "num_bridge_tuple", n->num_brtuple);

		aaa = blobmsg_open_array(&bb, "ipv4_address");
		list_for_each_entry(ipv4, &n->ipv4list, list) {
			char ipv4_ifmacstr[18] = {0};
			char ipbuf[256] = {0};

			tt = blobmsg_open_table(&bb, "");
			hwaddr_ntoa(ipv4->macaddr, ipv4_ifmacstr);
			inet_ntop(AF_INET, &ipv4->addr, ipbuf, sizeof(ipbuf));
			blobmsg_add_string(&bb, "macaddress", ipv4_ifmacstr);
			blobmsg_add_string(&bb, "ip", ipbuf);
			blobmsg_close_table(&bb, tt);
		}
		blobmsg_close_array(&bb, aaa);

		aaa = blobmsg_open_array(&bb, "ipv6_address");
		list_for_each_entry(ipv6, &n->ipv6list, list) {
			char ipv6_ifmacstr[18] = {0};
			char ipbuf[256] = {0};

			tt = blobmsg_open_table(&bb, "");
			hwaddr_ntoa(ipv6->macaddr, ipv6_ifmacstr);
			inet_ntop(AF_INET6, &ipv6->addr, ipbuf, sizeof(ipbuf));
			blobmsg_add_string(&bb, "macaddress", ipv6_ifmacstr);
			blobmsg_add_string(&bb, "ip", ipbuf);
			blobmsg_close_table(&bb, tt);
		}
		blobmsg_close_array(&bb, aaa);

		aaa = blobmsg_open_array(&bb, "interface");
		list_for_each_entry(ifs, &n->iflist, list) {
			char lif_macstr[18] = {0};

			aaaa = blobmsg_open_table(&bb, "");
			hwaddr_ntoa(ifs->macaddr, lif_macstr);

			blobmsg_add_string(&bb, "macaddress", lif_macstr);
			blobmsg_add_string(&bb, "media", media_type2str(ifs->mediatype));
			blobmsg_add_string(&bb, "power", "on");	//TODO
			if (ifs->mediatype > MEDIA_TYPE_IEEE_802_11B_2_4_GHZ &&
			    ifs->mediatype < MEDIA_TYPE_IEEE_1901_WAVELET) {
				struct ieee80211_info *winfo;
				char ifs_bssidstr[18] = {0};

				winfo = (struct ieee80211_info *)ifs->mediainfo;
				hwaddr_ntoa(winfo->bssid, ifs_bssidstr);
				blobmsg_add_string(&bb, "bssid", ifs_bssidstr);
				blobmsg_add_string(&bb, "role", role_type2str(winfo->role));
				blobmsg_add_u32(&bb, "bandwidth", winfo->ap_bandwidth < 20 ?
						wifi_bw_enum2MHz(winfo->ap_bandwidth) : winfo->ap_bandwidth);
				blobmsg_add_u32(&bb, "freq_seg0_idx", winfo->ap_channel_seg0_idx);
				blobmsg_add_u32(&bb, "freq_seg1_idx", winfo->ap_channel_seg1_idx);
			}
			blobmsg_close_table(&bb, aaaa);
		}
		blobmsg_close_array(&bb, aaa);

		aaa = blobmsg_open_array(&bb, "non1905_neighbors");
		list_for_each_entry(non, &n->non1905_nbrlist, list) {
			char xnbr_lifmacstr[18] = {0};

			aaaa = blobmsg_open_table(&bb, "");
			hwaddr_ntoa(non->macaddr, nbr_macstr);
			hwaddr_ntoa(non->local_ifmacaddr, xnbr_lifmacstr);

			blobmsg_add_string(&bb, "macaddress", nbr_macstr);
			blobmsg_add_string(&bb, "via", xnbr_lifmacstr);
			blobmsg_close_table(&bb, aaaa);
		}
		blobmsg_close_array(&bb, aaa);

		aaa = blobmsg_open_array(&bb, "ieee1905_neighbors");
		list_for_each_entry(nbr, &n->nbrlist, list) {
			char n_ifmacstr[18] = {0};
			char r_ifmacstr[18] = {0};

			aaaa = blobmsg_open_table(&bb, "");
			hwaddr_ntoa(nbr->macaddr, nbr_macstr);
			hwaddr_ntoa(nbr->local_ifmacaddr, n_ifmacstr);
			hwaddr_ntoa(nbr->ifmacaddr, r_ifmacstr);

			blobmsg_add_string(&bb, "macaddress", nbr_macstr);
			blobmsg_add_string(&bb, "via", n_ifmacstr);
			blobmsg_add_string(&bb, "remote_via", r_ifmacstr);
			blobmsg_add_u8(&bb, "has_bridge", nbr->has_bridge ? true : false);
			blobmsg_close_table(&bb, aaaa);
		}
		blobmsg_close_array(&bb, aaa);

		aaa = blobmsg_open_array(&bb, "bridge_tuples");
		list_for_each_entry(br, &n->brlist, list) {
			int i;

			tt = blobmsg_open_table(&bb, "");
			aaaa = blobmsg_open_array(&bb, "tuple");
			for (i = 0; i < br->num_macs; i++) {
				char macstr[18] = {0};

				hwaddr_ntoa(&br->macaddrs[i*6], macstr);
				blobmsg_add_string(&bb, "", macstr);
			}
			blobmsg_close_array(&bb, aaaa);
			blobmsg_close_table(&bb, tt);
		}
		blobmsg_close_array(&bb, aaa);

		blobmsg_close_table(&bb, aa);
	}
	blobmsg_close_array(&bb, a);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return 0;
}

void topology_create_dotfile(struct net *root)
{
	struct node *p = NULL;
	struct neighbor *n;
	FILE *f;
	char fstr[] = "digraph G {\n"
			"concentrate=true\n";

	f = fopen("/tmp/topo.dot", "w+");
	if (!f) {
		printf("failed to open file topo.dot\n");
		return;
	}

	fprintf(f, "%s", fstr);
	if (root->num_nodes == 1) {
		p = list_first_entry(&root->probelist, struct node, list);
		fprintf(f, "\t\"" MACFMT "\"\n", MAC2STR(p->macaddr));
	} else {
		list_for_each_entry(p, &root->probelist, list) {
			list_for_each_entry(n, &p->nbrlist, list) {
				fprintf(f, "\t\"" MACFMT "\"-> " "\"" MACFMT "\"[taillabel=\"" MACFMT "\"];\n",
					MAC2STR(p->macaddr), MAC2STR(n->macaddr),
					MAC2STR(n->local_ifmacaddr));
			}
		}
	}

	fprintf(f, "}\n");
	fclose(f);
}

int topology_dot(struct ubus_context *ctx, struct ubus_object *obj,
			struct ubus_request_data *req, const char *method,
			struct blob_attr *msg)
{
	struct topology_private *p = container_of(obj, struct topology_private, obj);

	topology_create_dotfile(p->network);
	return 0;
}

int topology_publish_object(void *priv, const char *objname)
{
	struct ubus_object *obj;
	struct ubus_object_type *obj_type;
	struct ubus_method *obj_methods;
	struct ubus_method m[2] = {
		UBUS_METHOD_NOARG("dump", topology_dump),
		UBUS_METHOD_NOARG("dot", topology_dot),
	};
	int num_methods = ARRAY_SIZE(m);
	struct topology_private *p = (struct topology_private *)priv;
	int ret;


	obj = &p->obj;
	memset(obj, 0, sizeof(*obj));
	obj_type = calloc(1, sizeof(struct ubus_object_type));
	if (!obj_type)
		return -1;

	obj_methods = calloc(num_methods, sizeof(struct ubus_method));
	if (!obj_methods) {
		free(obj_type);
		return -1;
	}

	obj->name = strdup(objname);
	memcpy(obj_methods, m, num_methods * sizeof(struct ubus_method));
	obj->methods = obj_methods;
	obj->n_methods = num_methods;

	obj_type->name = obj->name;
	obj_type->n_methods = obj->n_methods;
	obj_type->methods = obj->methods;
	obj->type = obj_type;

	ret = ubus_add_object(p->ctx, obj);
	if (ret) {
		fprintf(stderr, "Failed to add '%s' (err = %s)\n",
			objname, ubus_strerror(ret));

		free(obj_methods);
		free(obj_type);

		return ret;
	}

	fprintf(stderr, "Added UBUS object '%s'\n", objname);

	return 0;
}

void topology_remove_object(void *priv)
{
	struct topology_private *p = (struct topology_private *)priv;

	if (p && p->ctx) {
		if (p->obj.id != -1) {
			ubus_remove_object(p->ctx, &p->obj);
			free(p->obj.type);
			free((void *)p->obj.methods);
			free((void *)p->obj.name);
		}
	}
}

int topology_init(void **priv, struct i1905_context *ieee1905)
{
	struct topology_private *p;
	uint8_t aladdr[6] = {0};
	int ret;


	p = calloc(1, sizeof(struct topology_private));
	if (!p)
		return -1;

	*priv = p;
	p->module = &topology;
	if (ieee1905) {
		p->ctx = ieee1905->bus;
		memcpy(&p->ieee1905, ieee1905, sizeof(struct i1905_context));
	}

	timer_init(&p->t, refresh_timer_cb);
	ret = ieee1905_get_alid(ieee1905, aladdr);
	if (ret)
		return -1;

	fprintf(stderr, "%s: 1905 ALID: " MACFMT "\n", __func__, MAC2STR(aladdr));
	memcpy(p->alid, aladdr, 6);
	p->network = topology_alloc_net();
	if (!p->network)
		return -1;

	topology_alloc_root_node(p, aladdr);
	topology_publish_object(p, IEEE1905_OBJECT_TOPOLOGY);
	timer_set(&p->t, 1000);

	return 0;
}

static int topology_exit(void *priv)
{
	struct topology_private *p = (struct topology_private *)priv;

	if (p) {
		timer_del(&p->t);
		topology_free_net(p);
		topology_remove_object(p);
		free(p);
	}

	return 0;
}
