From 5995f1521bcd54ff2a9862bc65b3e14ab60dfbef Mon Sep 17 00:00:00 2001
From: vdutta <vivek.dutta@iopsys.eu>
Date: Tue, 2 Jun 2020 15:43:09 +0530
Subject: [PATCH] initial codebase

---
 GPL_iopsys.md                             |   23 +
 LICENSE                                   |  308 +++
 Makefile                                  |   22 +
 README                                    |   57 +
 README.md                                 |   99 +
 arp_table.c                               |  453 ++++
 arp_table.h                               |   77 +
 arping.c                                  |  178 ++
 dg400-deploy.sh                           |   22 +
 event.c                                   |  263 +++
 event.h                                   |   11 +
 hashmap.c                                 |  244 ++
 hashmap.h                                 |   66 +
 ieee1905/main.c                           |    7 +
 ieee1905/topomap.c                        |  256 +++
 ieee1905/topomap.h                        |   31 +
 ieee1905/topoubus.c                       |  382 ++++
 ieee1905/topoubus.h                       |   49 +
 ieee1905/topoutils.c                      |  102 +
 ieee1905/topoutils.h                      |   37 +
 list.c                                    |  117 +
 list.h                                    |   34 +
 make-and-run-topologyd-on-target-setup.sh |   49 +
 table_gen.c                               |  241 ++
 table_gen.h                               |   27 +
 topologyd.c                               | 2486 +++++++++++++++++++++
 topologyd.h                               |   86 +
 utils.c                                   |  601 +++++
 utils.h                                   |  127 ++
 29 files changed, 6455 insertions(+)
 create mode 100644 GPL_iopsys.md
 create mode 100644 LICENSE
 create mode 100644 Makefile
 create mode 100644 README
 create mode 100644 arp_table.c
 create mode 100644 arp_table.h
 create mode 100644 arping.c
 create mode 100755 dg400-deploy.sh
 create mode 100644 event.c
 create mode 100644 event.h
 create mode 100644 hashmap.c
 create mode 100644 hashmap.h
 create mode 100644 ieee1905/main.c
 create mode 100644 ieee1905/topomap.c
 create mode 100644 ieee1905/topomap.h
 create mode 100644 ieee1905/topoubus.c
 create mode 100644 ieee1905/topoubus.h
 create mode 100644 ieee1905/topoutils.c
 create mode 100644 ieee1905/topoutils.h
 create mode 100644 list.c
 create mode 100644 list.h
 create mode 100755 make-and-run-topologyd-on-target-setup.sh
 create mode 100644 table_gen.c
 create mode 100644 table_gen.h
 create mode 100644 topologyd.c
 create mode 100644 topologyd.h
 create mode 100644 utils.c
 create mode 100644 utils.h

diff --git a/GPL_iopsys.md b/GPL_iopsys.md
new file mode 100644
index 0000000..790614e
--- /dev/null
+++ b/GPL_iopsys.md
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2019 Iopsys Software Solutions AB. All rights reserved.
+ *
+ * Author: Jakob Olsson jakob.olsson@iopsys.eu
+ * Contributor: Anjan Chanda anjan.chanda@iopsys.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7d22cb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,308 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    topologyd - Network Topology Daemon
+    Copyright (C) 2019  iopsys Software Solutions AB
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..501f0f1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+EXECS		= topologyd topo1905d
+CC		= gcc
+CFLAGS		= -g -Wall
+CFLAGS		+= -I$(STAGING_DIR)/usr/include/libnl3
+LIBS		= -leasy -lwifi-5 -lubus -lubox -lpthread -lblobmsg_json -ljson-c -lnl-3 -lnl-genl-3 -lnetlink -luci -lnl-route-3
+LIBS1905	= -leasy -lubus -lubox -lpthread -lblobmsg_json -ljson-c
+OBJS		= list.o topologyd.o utils.o hashmap.o arp_table.o table_gen.o arping.o event.o
+OBJS1905	= ieee1905/main.o ieee1905/topoubus.o ieee1905/topoutils.o ieee1905/topomap.o
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+all: $(EXECS)
+
+topologyd: ${OBJS}
+	${CC} -o topologyd ${OBJS} ${LIBS} ${CFLAGS}
+
+topo1905d: ${OBJS1905}
+	${CC} -o topo1905d ${OBJS1905} ${LIBS1905} ${CFLAGS}
+
+clean:
+	rm -f *.o ieee1905/*.o $(EXECS)
diff --git a/README b/README
new file mode 100644
index 0000000..ee0065c
--- /dev/null
+++ b/README
@@ -0,0 +1,57 @@
+======= MAKE AND RUN =======
+
+adjust the paths to your iopsys build paths
+
+use the convenience script make-and-run-topologyd-on-target-setup.sh
+
+======== ALGORITHM =======
+
+The algorithm to determine the topology structure is performed after
+obtaining the bridge table for each available repeater and extender
+in the network. 
+
+-------- first pass -------
+The algorithm itself is performed in two passes. The first pass determines
+the depth of each node in the network by observing the number of times
+a node occurrs on the downlink side of each reapter and extender.
+
+first_pass(node_list)
+	foreach node in node_list
+		foreach child in node.downlink
+			child->depth++;
+
+------- second pass -------
+The second pass determines the parent of each node in the network. By
+using the depth table and identifying the downlink nodes of each node,
+the direct parent of a node can be established by iterating through all nodes
+downlink side and assigning the current node to the parent of the child node if
+there currently is no parent, or currently linked parent has a lower depth
+than current.
+
+second_pass(node_list)
+	foreach node in node_list
+		foreach child in node.downlink
+			if (!child->parent | child->parent->depth < node->depth)
+				child->parent = node;
+
+------- (optional) third pass -------
+An additional pass is also implemented currently, however this could be combined
+with the second pass. This pass determines each nodes direct child by passing
+through all nodes once and assigning their parents child.
+
+third_pass(node)
+	foreach node in node_list
+		if (node->parent)
+			node->parent->child = node
+
+
+------- issuing updates -------
+
+
+
+
+
+======== OLD JUCI PLUGIN =======
+As a sidenote, the old topology front-end JS file is here (juci repo):
+plugins/juci-network-netifd/src/widgets/overview-slider-network.js
+
diff --git a/README.md b/README.md
index e69de29..8968474 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,99 @@
+# topology
+
+ieee1905 stack provides methods which can be used by an application to build network topology with respect to a node in the network. 
+
+## Overview
+
+map-topology daemon (topo1905d), as we speak, provides two ubus methods as mentioned in the UBUS API section below to build and dump network topology map of the node under test.
+
+The daemon uses 1905 al-mac of the node as a starting point to find its immediate as well as far-1905 and non-1905 neighbors to build a complete topology map around the node.
+
+Topology is built every 60 seconds and the "dump_tree" ubus method displays the results of the last built topology within the 60 second period.
+
+To build a fresh topology, ubus method "build_tree" should be used. See Usage below
+
+map-topology uses following algorithm to build network topology using the ieee1905 stack with an assumption that the information provided by the underlying ieee1905 stack is up-to-date.
+
+At any node 'P' in the network -
+
+Intial state:
+
+Nbr(P) = {N0,..Nn}  <== immediate neighbors of P.
+
+G{P, N0,..Nn} <== graph of all known nodes at P.
+
+<u>Step 1</u>:
+
+  for all Ni in G:
+
+  Send Topology Request to Ni.
+
+<u>Step 2:</u>
+
+Read Topology Response from Ni.
+
+if Nbr(Ni) is not in G:
+
+Insert Nbr(Ni) in G
+
+<u>Step 3:</u>
+
+Repeat Step 1-2 until no new Nbr(Ni) can be inserted in G.
+
+### Ubus API
+
+This is a verbose print of all the methods provided by map-topology over ubus :
+
+````bash
+'topo1905' @fc2af0e9
+        "build_tree":{}
+        "dump_tree":{}
+````
+
+### Usage
+
+On successful registration, map-plugin broadcast an ubus event on arrival of
+multiAP specific CMDUs in below format.
+
+````bash
+root@iopsys:~# ubus call topo1905 build_tree
+{
+        "status": true
+}
+
+
+root@iopsys:~# ubus call topo1905 dump_tree
+{
+        "Nodes": [
+                {
+                        "local_mac": "02:22:07:70:ef:e6",
+                        "neighbors_nr": 0,
+                        "neighbors": [
+
+                        ],
+                        "non_1905_neighbors_nr": 0,
+                        "non_1905_neighbors": [
+
+                        ]
+                }
+        ]
+}
+````
+
+### Limitations
+
+ * Network topology includes only neighbor's mac address information as of now.
+
+### Testing
+
+- Tested only with two ieee1905 devices in the network.
+-  Tested with only one al-mac interface enabled.
+
+### Dependencies
+
+To successfully build map-topology, the following libraries are needed:
+
+| Dependency        | Link                                       			| License        |
+| ----------------- | ---------------------------------------------------------------- 	| -------------- |
+| libubox    	    | https://git.openwrt.org/project/libubox.git 			| BSD            |
+| libubus     	    | https://git.openwrt.org/project/ubus.git    			| LGPL 2.1       |
\ No newline at end of file
diff --git a/arp_table.c b/arp_table.c
new file mode 100644
index 0000000..54adf90
--- /dev/null
+++ b/arp_table.c
@@ -0,0 +1,453 @@
+#include "arp_table.h"
+
+static struct arp_entry *arptable[ARPSIZE];
+static struct ip_entry *iptable[ARPSIZE];
+static struct ifindex_entry *ifindextable[ARPSIZE];
+
+int soft_clean_entry_arp(struct arp_entry *ae)
+{
+	tp_dbg("______________________________ soft cleaning arp %s ______________________________", ae->mac_addr);
+
+	memset(ae->ifname, 0, IFNAMSIZ);
+
+	ae->ifindex = -1;
+	ae->port_no = -1;
+
+	return 0;
+}
+
+int clean_entry_arp_by_mac(char *mac_addr)
+{
+	struct arp_entry *ae;
+
+	ae = lookup_arp(mac_addr);
+	if (!ae)
+		return -1;
+
+	return clean_entry_arp(ae);
+}
+
+int clean_entry_arp(struct arp_entry *ae)
+{
+	unsigned int hashval;
+	struct age *a, *tmp;
+
+	tp_dbg("______________________________ cleaning arp entry %s ______________________________", ae->mac_addr);
+
+	hashval = hash_arp(ae->mac_addr);
+
+	EMPT();
+	if (ae->prev) // if theres a previous entry change its next entry
+		ae->prev->next = ae->next;
+	if (ae->next) { // if theres a next entry change its previous
+		if (!ae->prev)
+			arptable[hashval] = ae->next;
+		ae->next->prev = ae->prev;
+	}
+	EMPT();
+	if (!ae->next && !ae->prev) // final entry, just remove link entirely
+		arptable[hashval] = NULL;
+	if (strlen(ae->ip_addr))
+		clean_entry_ip_by_ip(ae->ip_addr);
+	EMPT();
+	//clean_entry_ifindex_by_ifindex(ae->port_no);
+
+	list_for_each_entry_safe(a, tmp, &ae->ages, list) {
+		list_del(&a->list);
+		free(a);
+	}
+
+	ae->ifindex = -1;
+	ae->port_no = -1;
+	EMPT();
+	free(ae);
+	EMPT();
+	return 0;
+}
+
+struct arp_entry *lookup_arp(const char *key)
+{
+	struct arp_entry *entry;
+	unsigned int hashval;
+
+	tp_dbg("performing a lookup for %s", key);
+	hashval = hash_arp(key);
+	//lprintf("%d\n", __LINE__);
+	entry = arptable[hashval];
+	//lprintf("%d\n", __LINE__);
+	while (entry) {
+		DBGP("key: %s mac: %s", key, entry->mac_addr);
+
+		if (strcmp(key, entry->mac_addr) == 0)
+			return entry;
+		//lprintf("%d\n", __LINE__);
+		entry = entry->next;
+	}
+	EMPT();
+	return NULL;
+}
+
+unsigned int hash_arp(const char *key)
+{
+	unsigned int hashval;
+
+	if (!key || strlen(key) < 9)
+		return -1;
+
+	key = key+9;
+	for (hashval = 0; *key != '\0'; key++) // mask it? which is best?
+		hashval = *key + (31 * hashval);
+
+	return (hashval % ARPSIZE);
+}
+
+unsigned int hash_ip(const char *key)
+{
+	unsigned int hashval;
+
+	for (hashval = 0; *key != '\0'; key++) // mask it? which is best?
+		hashval = *key + (31 * hashval);
+
+	return (hashval % ARPSIZE);
+}
+
+static struct arp_entry *create_entry_arp(char *mac)
+{
+	struct arp_entry *ae;
+	unsigned int hashval;
+
+	ae = (struct arp_entry *)calloc(1, sizeof(*ae));
+	if (!ae)
+		goto fail_malloc;
+
+	strncpy(ae->mac_addr, mac, MAC_MAX_LEN);
+
+	hashval = hash_arp(mac);
+	ae->next = arptable[hashval]; // will this work for null?
+	if (ae->next)
+		ae->next->prev = ae;
+
+	INIT_LIST_HEAD(&ae->ages);
+	arptable[hashval] = ae;
+
+fail_malloc:
+	return ae;
+}
+
+double get_bridge_age_arp(struct arp_entry *ae, int br_ifindex)
+{
+	struct age *a, *entry = NULL;
+
+	list_for_each_entry(a, &ae->ages, list) {
+		if (a->br_ifindex != br_ifindex)
+			continue;
+
+		return (double)a->ageing_timer.tv_sec + ((double)a->ageing_timer.tv_usec / 1000000);
+	}
+
+	return -1;
+}
+
+int add_bridge_age_arp(struct timeval *ageing_timer, struct arp_entry *ae, int br_ifindex)
+{
+	struct age *a;
+
+	a = calloc(1, sizeof(*a));
+	if (!a)
+		goto fail;
+
+	memcpy(&a->ageing_timer, ageing_timer, sizeof(struct timeval));
+	a->br_ifindex = br_ifindex;
+
+	list_add(&a->list, &ae->ages);
+	return 0;
+fail:
+	return -1;
+}
+
+struct arp_entry *insert_arp(char *mac)
+{
+	struct arp_entry *ae;
+
+	ae = lookup_arp(mac);
+	if (ae) { // if an entry already exists, just update to the new list, reset parents'
+		soft_clean_entry_arp(ae);
+
+		strncpy(ae->mac_addr, mac, MAC_MAX_LEN);
+
+		goto alrdy_exist;
+	}
+
+	ae = create_entry_arp(mac);
+
+alrdy_exist:
+	DBGP("created entry for %s", ae->mac_addr);
+	return ae;
+}
+
+void clear_stale_arp(void)
+{
+	struct arp_entry *ae, *next;
+	int i;
+
+	for (i = 0; i < ARPSIZE; i++) {
+		ae = arptable[i];
+		while (ae) {
+			if (!strlen(ae->ip_addr)) {
+				next = ae->next;
+				clean_entry_arp(ae);
+				ae = next;
+			} else
+				ae = ae->next;
+		}
+	}
+}
+
+/* TODO: should probably be somewhere else? */
+int blob_add_clients(struct bridge *b, struct blob_buf *bb, int idx)
+{
+	struct arp_entry *ae;
+	int i;
+	void *table;
+	double age;
+	struct ifindex_entry *ife;
+
+	tp_dbg("------ add all clients under ifindex %d for bridge %s ------", idx, b->mac_addr);
+
+	for (ife = ifindextable[idx]; ife; ife = ife->next) {
+		table = blobmsg_open_table(bb, "client");
+
+		ae = ife->ae;
+		if (!ae)
+			continue;
+
+		tp_dbg("mac: %s", ae->mac_addr);
+
+		age = get_bridge_age_arp(ae, b->ifindex);
+		blobmsg_add_double(bb, "ageing_timer", age);
+
+		blobmsg_add_string(bb, "macaddr", ae->mac_addr);
+		blobmsg_add_string(bb, "ipaddr", ae->ip_addr);
+		blobmsg_close_table(bb, table);
+	}
+
+	tp_dbg("--------------------------------------------------------------------");
+
+	return 0;
+}
+
+int clean_entry_ip_by_ip(char *ip)
+{
+	struct ip_entry *ie;
+
+	ie = lookup_ip(ip);
+	if (!ie)
+		return -1;
+
+	return clean_entry_ip(ie);
+}
+
+static struct ip_entry *create_entry_ip(struct arp_entry *ae, char *ip)
+{
+	struct ip_entry *ie;
+	unsigned int hashval;
+
+	ie = (struct ip_entry *)calloc(1, sizeof(*ie));
+	if (!ie)
+		goto fail_malloc;
+
+	strncpy(ie->ip_addr, ip, IPV4_MAX_LEN);
+
+	hashval = hash_ip(ip);
+	ie->next = iptable[hashval]; // will this work for null?
+	if (ie->next)
+		ie->next->prev = ie;
+
+	ie->prev = NULL;
+	ie->ae = ae;
+	iptable[hashval] = ie;
+
+fail_malloc:
+	return ie;
+}
+
+struct ip_entry *insert_ip(char *mac, char *ip) // could probably pass an ae over mac
+{
+	struct arp_entry *ae;
+	struct ip_entry *ie = NULL;
+	//lprintf("%d\n", __LINE__);
+	ae = lookup_arp(mac);
+	if (!ae)
+		goto fail;
+	//lprintf("%d\n", __LINE__);
+	ie = lookup_ip(ip);
+	if (ie) { // if an ae already exists, just update to the new list, reset parents'
+		ie->ae = ae;
+		goto alrdy_exist;
+	}
+	//lprintf("%d\n", __LINE__);
+	ie = create_entry_ip(ae, ip);
+	//lprintf("%d\n", __LINE__);
+alrdy_exist:
+fail:
+	return ie;
+}
+
+struct ip_entry *lookup_ip(const char *key)
+{
+	struct ip_entry *entry;
+	unsigned int hashval;
+
+	hashval = hash_ip(key);
+	entry = iptable[hashval];
+	while (entry) {
+		if (strcmp(key, entry->ip_addr) == 0)
+			return entry;
+
+		entry = entry->next;
+	}
+
+	return NULL;
+}
+
+int clean_entry_ip(struct ip_entry *ie)
+{
+	unsigned int hashval;
+
+	hashval = hash_ip(ie->ip_addr);
+
+	if (ie->prev) // if theres a previous entry change its next entry
+		ie->prev->next = ie->next;
+	if (ie->next) { // if theres a next entry change its previous
+		if (!ie->prev)
+			iptable[hashval] = ie->next;
+		ie->next->prev = ie->prev;
+	}
+	if (!ie->next && !ie->prev) // final entry, just remove link entirely
+		iptable[hashval] = NULL;
+
+	ie->ae = NULL;
+
+	free(ie);
+	return 0;
+}
+
+int soft_clean_entry_ip(struct ip_entry *ie)
+{
+	ie->ae = NULL;
+
+	return 0;
+}
+
+struct ifindex_entry *lookup_ifindex(char *mac, int ifindex)
+{
+	struct ifindex_entry *entry;
+
+	entry = ifindextable[ifindex];
+	while (entry) {
+		if (strcmp(mac, entry->mac_addr) == 0)
+			return entry;
+
+		entry = entry->next;
+	}
+
+	return NULL;
+}
+
+
+int clean_entry_ifindex_by_ifindex(int ifindex)
+{
+	struct ifindex_entry *ife;
+
+	ife = ifindextable[ifindex];
+	if (!ife) {
+		printf("%d no such ifindex entry was discovered\n", __LINE__);
+		return -1;
+	}
+
+	return clean_entry_ifindex(ife);
+}
+
+static struct ifindex_entry *create_entry_ifindex(struct arp_entry *ae, int ifindex)
+{
+	struct ifindex_entry *ife;
+
+	ife = (struct ifindex_entry *)calloc(1, sizeof(*ife));
+	if (!ife)
+		goto fail_malloc;
+
+	ife->ifindex = ifindex;
+
+	ife->next = ifindextable[ifindex];
+	if (ife->next)
+		ife->next->prev = ife;
+
+	ife->ae = ae;
+	ifindextable[ifindex] = ife;
+
+fail_malloc:
+	return ife;
+}
+
+struct ifindex_entry *insert_ifindex(char *mac, int ifindex, int br_ifindex) // could probably pass an ae over mac
+{
+	struct arp_entry *ae;
+	struct ifindex_entry *ife = NULL;
+
+	ae = lookup_arp(mac);
+	if (!ae)
+		goto out;
+
+	ife = lookup_ifindex(mac, ifindex);
+	if (ife) {
+		ife->ae = ae;
+		ife->br_ifindex = br_ifindex;
+		goto out;
+	}
+
+	ife = create_entry_ifindex(ae, ifindex);
+	ife->br_ifindex = br_ifindex;
+
+out:
+	return ife;
+}
+
+int clean_entry_ifindex(struct ifindex_entry *ife)
+{
+
+	if (ife->prev) { // if theres a previous entry change its next entry
+		ife->prev->next = ife->next;
+	}
+
+	if (ife->next) { // if theres a next entry change its previous
+		if (!ife->prev)
+			ifindextable[ife->ifindex] = ife->next;
+		ife->next->prev = ife->prev;
+	}
+
+	if (!ife->next && !ife->prev) // final entry, just remove link entirely
+		ifindextable[ife->ifindex] = NULL;
+	ife->ae = NULL;
+	free(ife);
+	return 0;
+}
+
+// potentially could be cleaned using entries from a list of some sort? to remove a lot of redundant iterations
+void clean_all_tables(void)
+{
+	int i;
+
+	tp_dbg("------ cleaning all tables ------");
+	for (i = 0; i < ARPSIZE; i++) {
+
+		while (ifindextable[i])
+			clean_entry_ifindex(ifindextable[i]);
+
+		while (arptable[i])
+			clean_entry_arp(arptable[i]);
+
+		while (iptable[i])
+			clean_entry_ip(iptable[i]);
+	}
+	tp_dbg("------ finished cleaning tables ------");
+}
diff --git a/arp_table.h b/arp_table.h
new file mode 100644
index 0000000..a82fb7a
--- /dev/null
+++ b/arp_table.h
@@ -0,0 +1,77 @@
+#ifndef ARP_H
+#define ARP_H
+
+#include "utils.h"
+#include "topologyd.h"
+#define ARPSIZE 256
+
+struct bridge;
+
+struct age {
+	int br_ifindex;
+	struct timeval ageing_timer;
+
+	struct list_head list;
+};
+
+struct arp_entry {
+	char mac_addr[18]; // a lot of this information is duplicate too
+	char ip_addr[15]; //IPV4_MAX_LEN
+	char ifname[IFNAMSIZ];
+	char name[256];
+
+	int port_no;
+	int ifindex;
+	int br_ifindex;
+
+	__u16 state;
+
+	struct list_head ages;
+
+	struct arp_entry *next; // next entry in case of colission
+	struct arp_entry *prev; // prev entry, useful for deletion of entry
+};
+
+struct ip_entry {
+	char ip_addr[15]; //IPV4_MAX_LEN
+
+	struct arp_entry *ae;
+
+	struct ip_entry *next;
+	struct ip_entry *prev;
+};
+
+struct ifindex_entry {
+	int ifindex;
+	int br_ifindex;
+	char mac_addr[18];
+
+	struct arp_entry *ae;
+
+	struct ifindex_entry *next;
+	struct ifindex_entry *prev;
+};
+
+int soft_clean_entry_arp(struct arp_entry *ae);
+int clean_entry_arp_by_mac(char *mac_addr);
+int clean_entry_arp(struct arp_entry *ae);
+struct arp_entry *lookup_arp(const char *key);
+unsigned hash_arp(const char *key);
+struct arp_entry *insert_arp(char *mac);
+void clear_stale_arp(void);
+int blob_add_clients(struct bridge *bridge, struct blob_buf *bb, int port);
+
+int clean_entry_ip_by_ip(char *ip);
+struct ip_entry *insert_ip(char *mac, char *ip);
+struct ip_entry *lookup_ip(const char *key);
+int clean_entry_ip(struct ip_entry *il);
+struct ifindex_entry *lookup_ifindex(char *mac, int ifindex);
+int clean_entry_ifindex_by_ifindex(int ifindex);
+struct ifindex_entry *insert_ifindex(char *mac, int ifindex, int br_ifindex);
+int clean_entry_ifindex(struct ifindex_entry *pl);
+
+double get_bridge_age_arp(struct arp_entry *ae, int br_ifindex);
+int add_bridge_age_arp(struct timeval *ageing_timer, struct arp_entry *ae, int br_ifindex);
+
+void clean_all_tables(void);
+#endif
diff --git a/arping.c b/arping.c
new file mode 100644
index 0000000..1f7575a
--- /dev/null
+++ b/arping.c
@@ -0,0 +1,178 @@
+/*
+ * arping -- arping tool for questd
+ *
+ * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Author: Sukru Senli sukru.senli@inteno.se
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+struct in_addr src;
+struct in_addr dst;
+struct sockaddr_ll me;
+struct sockaddr_ll he;
+int sock_fd;
+
+static int
+send_pack(struct in_addr *src_addr, struct in_addr *dst_addr, struct sockaddr_ll *ME, struct sockaddr_ll *HE)
+{
+	int err;
+	unsigned char buf[256];
+	struct arphdr *ah = (struct arphdr *) buf;
+	unsigned char *p = (unsigned char *) (ah + 1);
+
+	ah->ar_hrd = htons(ARPHRD_ETHER);
+	ah->ar_pro = htons(ETH_P_IP);
+	ah->ar_hln = ME->sll_halen;
+	ah->ar_pln = 4;
+	ah->ar_op = htons(ARPOP_REQUEST);
+
+	p = (unsigned char *)mempcpy((void *)p, (const void *)ME->sll_addr, (size_t)ah->ar_hln);
+	p = (unsigned char *)mempcpy((void *)p, (const void *)src_addr, 4);
+
+	p = (unsigned char *)mempcpy((void *)p, (const void *)HE->sll_addr, (size_t)ah->ar_hln);
+	p = (unsigned char *)mempcpy((void *)p, (const void *)dst_addr, 4);
+
+	err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+	return err;
+}
+
+static bool
+recv_pack(char *buf, int len, struct sockaddr_ll *FROM)
+{
+	struct arphdr *ah = (struct arphdr *) buf;
+	unsigned char *p = (unsigned char *) (ah + 1);
+	struct in_addr src_ip, dst_ip;
+
+	/* Filter out wild packets */
+	if (FROM->sll_pkttype != PACKET_HOST
+	 && FROM->sll_pkttype != PACKET_BROADCAST
+	 && FROM->sll_pkttype != PACKET_MULTICAST)
+		return false;
+
+	/* Only these types are recognized */
+	if (ah->ar_op != htons(ARPOP_REPLY))
+		return false;
+
+	/* ARPHRD check and this darned FDDI hack here :-( */
+	if (ah->ar_hrd != htons(FROM->sll_hatype)
+	 && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
+		return false;
+
+	/* Protocol must be IP. */
+	if (ah->ar_pro != htons(ETH_P_IP)
+	 || (ah->ar_pln != 4)
+	 || (ah->ar_hln != me.sll_halen)
+	 || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
+		return false;
+
+	(src_ip.s_addr) = *(uint32_t *)(p + ah->ar_hln);
+	(dst_ip.s_addr) = *(uint32_t *)(p + ah->ar_hln + 4 + ah->ar_hln);
+
+	if (dst.s_addr != src_ip.s_addr)
+		return false;
+
+	if ((src.s_addr != dst_ip.s_addr) || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
+		return false;
+
+	return true;
+}
+
+
+bool
+arping(const char *targetIP, const char *device, int toms)
+{
+	struct sockaddr_in saddr;
+	struct ifreq ifr;
+	int probe_fd;
+	char packet[64];
+	struct sockaddr_ll from;
+	struct timeval timeout;
+
+	sock_fd = socket(AF_PACKET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, device, IF_NAMESIZE);
+
+	if (ioctl(sock_fd, SIOCGIFINDEX, &ifr, sizeof(ifr)) < 0) {
+		close(sock_fd);
+		return false;
+	}
+
+	me.sll_family = AF_PACKET;
+	me.sll_ifindex = ifr.ifr_ifindex;
+	me.sll_protocol = htons(ETH_P_ARP);
+	bind(sock_fd, (struct sockaddr *) &me, sizeof(me));
+
+	socklen_t mlen = sizeof(me);
+	getsockname(sock_fd, (struct sockaddr *) &me, &mlen);
+
+	he = me;
+	memset(he.sll_addr, -1, he.sll_halen);
+
+	inet_pton(AF_INET, targetIP, &(dst.s_addr));
+
+	/* Get the sender IP address */
+	probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+	memset(&saddr, 0, sizeof(saddr));
+	saddr.sin_family = AF_INET;
+	socklen_t slen = sizeof(saddr);
+	saddr.sin_port = htons(1025);
+	saddr.sin_addr = dst;
+	connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+	getsockname(probe_fd, (struct sockaddr *) &saddr, &slen);
+	src = saddr.sin_addr;
+	close(probe_fd);
+
+	send_pack(&src, &dst, &me, &he);
+
+	socklen_t alen = sizeof(from);
+	bool connected = false;
+	int cc = -1;
+
+	fd_set read_fds, write_fds, except_fds;
+	FD_ZERO(&read_fds);
+	FD_ZERO(&write_fds);
+	FD_ZERO(&except_fds);
+	FD_SET(sock_fd, &read_fds);
+
+
+	timeout.tv_sec = 0;
+	timeout.tv_usec = toms * 1000;
+
+	if (select(sock_fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1)
+		cc = recvfrom(sock_fd, packet, sizeof(packet), 0, (struct sockaddr *) &from, &alen);
+
+	if (cc >= 0)
+		connected = recv_pack(packet, cc, &from);
+
+	close(sock_fd);
+
+	return connected;
+}
diff --git a/dg400-deploy.sh b/dg400-deploy.sh
new file mode 100755
index 0000000..e798301
--- /dev/null
+++ b/dg400-deploy.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+#===== MAKE AND RUN ======
+# Clean old code then copy from local git repo
+ARM_DIR=~/git/new-dg400/build_dir/target-arm_xscale_musl_eabi/topologyd-0.1
+RAMIPS_DIR=~/git/new-ex400/build_dir/target-mipsel_1004kc_musl/topologyd-0.1
+
+rm $ARM_DIR/*
+cp * $ARM_DIR/
+touch $ARM_DIR/*.c
+#1nten0tech
+#Master compile
+#cd ~/git/dg400/
+
+#./iop compile topologyd V=s && ssh root@192.168.1.1 "killall topologyd" ; scp ~/git/iop-dg400-topd/iopsys/build_dir/target-arm_xscale_musl_eabi/topologyd-0.1/topologyd root@192.168.1.1:/sbin/
+cd ~/git/new-dg400/
+./iop compile topologyd $1
+
+ssh root@192.168.1.1 "rm /sbin/topologyd"
+scp ~/git/new-dg400/build_dir/target-arm_xscale_musl_eabi/topologyd-0.1/topologyd root@192.168.1.1:/sbin
+ssh root@192.168.1.1 "/etc/init.d/log restart"
+ssh root@192.168.1.1 "/etc/init.d/topologyd stop"
diff --git a/event.c b/event.c
new file mode 100644
index 0000000..bdd2128
--- /dev/null
+++ b/event.c
@@ -0,0 +1,263 @@
+#include "event.h"
+
+/* workaround due to identical bridge macs */
+static struct vertex_table *get_entry_by_bridge_port(struct device_context *dc, char *port)
+{
+	struct bridge *b;
+	struct vertex *v;
+
+	tp_dbg();
+
+	b = get_bridge_by_port_idx(&dc->bridges, if_nametoindex(port));
+	if (!b)
+		goto fail;
+
+	tp_dbg();
+
+	v = get_vertex_by_mac_bridge(dc, b, b->mac_addr);
+	if (!v)
+		goto fail;
+
+	tp_dbg();
+
+	return v->vt;
+fail:
+	return NULL;
+}
+/* end workaround */
+
+enum {
+	ID,
+	PATH,
+	__ADD_RM_MAX
+};
+
+const struct blobmsg_policy add_rm_policy[__ADD_RM_MAX] = {
+	[ID] = {.name = "id", .type = BLOBMSG_TYPE_INT64}, // 32 or 64?
+	[PATH] = {.name = "path", .type = BLOBMSG_TYPE_STRING}
+};
+
+static void stale_branch(struct device_context *dc, const char *obj)
+{
+	char *mac;
+	struct vertex_table *t;
+	struct bridge *b;
+
+	mac = ip_to_mac(obj);
+	if (!mac) {
+		printf("%s %d no mac found for %s\n", __func__, __LINE__, obj);
+		goto out;
+	}
+
+	t = lookup(mac);
+	if (!t) {
+		printf("%s %d no vertex_table found for %s\n", __func__, __LINE__, mac);
+		goto out;
+	}
+
+	if (t->stale) {
+		printf("%s %d %s is already stale\n", __func__, __LINE__, mac);
+		goto out; // dont need to poll?
+	}
+
+	printf("%s %d marked %s as stale\n", __func__, __LINE__, mac);
+
+	mark_stale_children(t, NULL, NULL);
+
+	b = get_bridge_by_idx(&dc->bridges, t->v->br_ifindex);
+	if (!b) // TODO: what to do on failure
+		goto out;
+
+	t->ageing_timer = get_ageing_timer(dc, mac, b->ifindex);
+	if (t->ageing_timer < 0) {
+		/* if it is gone from this bridge, may show (back) up at another one,
+		expected to have lower ageing timer than time passed at that point. */
+		t->ageing_timer = 0;
+
+		clean_entry(t, b->ifindex);
+		goto out;
+	}
+
+	gettimeofday(&t->tv_start, NULL);
+	list_add(&t->list, &dc->stale_nodes);
+	t->stale = true;
+
+out:
+	return;
+}
+
+void add_rm_handler(struct device_context *dc, struct ubus_event_handler *ev,
+				const char *type, struct blob_attr *msg)
+{
+	struct blob_attr *tb[__ADD_RM_MAX];
+	int rv;
+	char *obj;
+
+	printf("%s %d %s\n", __func__, __LINE__, type);
+
+	rv = blobmsg_parse(add_rm_policy, __ADD_RM_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
+	if (rv < 0) {
+		printf("problem parsing msg with blobmsg_parse!\n");
+		goto fail;
+	}
+
+	obj = (char *)blobmsg_get_string(tb[PATH]);
+	if (!obj)
+		goto fail;
+
+	if (!strstr(obj, "topology"))
+		goto fail;
+
+	strtok(obj, "/");
+	printf("%s %d %s\n", __func__, __LINE__, type);
+	if (strstr(type, "remove"))
+		stale_branch(dc, obj);
+
+	if (dc->root) {
+		printf("%s %d POLL CAUSED BY: %s %s %s\n", __func__, __LINE__, __func__, type, obj);
+		schedule_stale_poll(dc, 1000);
+		//poll_tree();
+	}
+fail:
+	return;
+}
+
+enum {
+	PORT,
+	LINK,
+	__SWITCH_MAX
+};
+
+const struct blobmsg_policy switch_policy[__SWITCH_MAX] = {
+	[PORT] = {.name = "port", .type = BLOBMSG_TYPE_STRING},
+	[LINK] = {.name = "link", .type = BLOBMSG_TYPE_STRING}
+};
+
+void switch_handler(struct device_context *dc, struct ubus_event_handler *ev,
+				const char *type, struct blob_attr *msg)
+{
+	int rv;
+	struct blob_attr *tb[__SWITCH_MAX];
+	char *link, *port;
+	char ifname[IFNAMSIZ] = {0};
+	struct blob_buf bb = {0};
+	struct vertex_table *t;
+
+	tp_dbg("%s\n", type);
+
+	blob_buf_init(&bb, 0);
+
+	rv = blobmsg_parse(switch_policy, __SWITCH_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
+	if (rv < 0) {
+		printf("problem parsing msg with blobmsg_parse!\n");
+		goto fail;
+	}
+
+	tp_dbg("%s\n", type);
+
+	port = (char *)blobmsg_get_string(tb[PORT]);
+	if (!port)
+		goto fail;
+
+	link = (char *)blobmsg_get_string(tb[LINK]);
+	if (!link)
+		goto fail;
+
+	if (!dc->root) {
+		tp_dbg();
+		network_event(dc, &bb, link, port);
+	} else {
+		/* workaround due to identical bridge macs */
+		t = get_entry_by_bridge_port(dc, port);
+		if (!t)
+			goto fail;
+		/* end workaround */
+		tp_dbg("t->mac: %s\n", t->mac_addr);
+
+		if (strncmp(link, "down", 4) == 0) { // if a port goes up, a node should be added?
+			tp_dbg();
+			mark_stale_children(t, NULL, port);
+		}
+
+		schedule_stale_poll(dc, 1000);
+	}
+	tp_dbg();
+fail:
+	blob_buf_free(&bb);
+	tp_dbg();
+}
+
+enum {
+	DEV_MAC,
+	VIF,
+	ACTION,
+	__WIFI_MAX
+};
+
+const struct blobmsg_policy wifi_policy[__WIFI_MAX] = {
+	[DEV_MAC] = {.name = "macaddr", .type = BLOBMSG_TYPE_STRING},
+	[VIF] = {.name = "vif", .type = BLOBMSG_TYPE_STRING},
+	[ACTION] = {.name = "action", .type = BLOBMSG_TYPE_STRING}
+};
+
+void wifi_handler(struct device_context *dc, struct ubus_event_handler *ev,
+				const char *type, struct blob_attr *msg)
+{
+	int rv;
+	struct blob_attr *tb[__WIFI_MAX];
+	char *action, *vif, *mac;
+	char event[5] = {0};
+	struct blob_buf bb = {0}; // prepare blob buf incase this is an event at dc->root
+	struct vertex_table *t;
+
+	printf("%s %d %s\n", __func__, __LINE__, type);
+
+	blob_buf_init(&bb, 0);
+
+	rv = blobmsg_parse(wifi_policy, __WIFI_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
+	if (rv < 0) {
+		printf("problem parsing msg with blobmsg_parse!\n");
+		goto fail;
+	}
+
+	vif = (char *)blobmsg_get_string(tb[VIF]);
+	if (!vif)
+		goto fail;
+
+	action = (char *)blobmsg_get_string(tb[ACTION]);
+	if (!action)
+		goto fail;
+
+	mac = (char *)blobmsg_get_string(tb[DEV_MAC]);
+	if (!mac)
+		goto fail;
+
+	blobmsg_add_string(&bb, "dev_mac", mac);
+
+	if (strncmp("disassociated", action, 32) == 0)
+		strncpy(event, "down", 4);
+	else if (strncmp("associated", action, 32) == 0)
+		strncpy(event, "up", 2);
+
+	printf("%s %d %s\n", __func__, __LINE__, event);
+	if (!dc->root) {
+		tp_dbg();
+		network_event(dc, &bb, event, vif); // change network_event to take local mac as parameter
+	} else {
+		tp_dbg("%s", vif);
+		/* workaround*/
+		t = get_entry_by_bridge_port(dc, vif);
+		if (!t)
+			goto fail;
+		/* end of workaround */
+
+		tp_dbg("t->mac: %s\n", t->mac_addr);
+
+		prep_event_blob(dc, &bb, event, vif);
+
+		child_ev_handler(NULL, &t->listener, "topology", bb.head);
+	}
+
+fail:
+	blob_buf_free(&bb);
+}
\ No newline at end of file
diff --git a/event.h b/event.h
new file mode 100644
index 0000000..354eff7
--- /dev/null
+++ b/event.h
@@ -0,0 +1,11 @@
+#ifndef EV_H
+#define EV_H
+
+#include "topologyd.h"
+
+struct device_context;
+
+void add_rm_handler(struct device_context *dc, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
+void switch_handler(struct device_context *dc, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
+void wifi_handler(struct device_context *dc, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
+#endif
\ No newline at end of file
diff --git a/hashmap.c b/hashmap.c
new file mode 100644
index 0000000..f3238d8
--- /dev/null
+++ b/hashmap.c
@@ -0,0 +1,244 @@
+#include "hashmap.h"
+
+static struct vertex_table *brtable[HASHSIZE];
+
+int soft_clean_entry_by_mac(char *mac_addr)
+{
+	struct vertex_table *t;
+
+	DBGP("soft cleaning entry %s\n", mac_addr);
+	t = lookup(mac_addr);
+	if (!t)
+		return -1;
+
+	return soft_clean_entry(t);
+}
+
+int soft_clean_entry(struct vertex_table *t)
+{
+	if (!t)
+		return -1;
+
+	tp_dbg("soft cleaning entry %s", t->mac_addr);
+
+	delete_list(t->child_head);
+	delete_list(t->bridge_head);
+
+	t->child_head = NULL;
+	t->bridge_head = NULL;
+	t->parent = NULL;
+
+	t->v = NULL;
+
+	printf("%d we shall remain in memory! %s %d\n", __LINE__, t->mac_addr, t->stale);
+	return 0;
+}
+
+int clean_entry_by_mac(char *mac_addr, int br_ifindex)
+{
+	struct vertex_table *t;
+	unsigned int hashval;
+
+	t = lookup(mac_addr);
+	if (!t)
+		return -1;
+
+	return clean_entry(t, br_ifindex);
+}
+
+int clean_entry(struct vertex_table *t, int br_ifindex)
+{
+	unsigned int hashval;
+
+	//return 0;
+	if (!t)
+		return -1;
+
+	delete_br_data(t, br_ifindex);
+
+	if (!list_empty(&t->br_indexes))
+		return 0;
+
+	tp_dbg("hard cleaning entry %s\n", t->mac_addr);
+
+	hashval = hash(t->mac_addr);
+	if (t->prev) { // if theres a previous entry change its next entry
+		t->prev->next = t->next;
+	}
+	tp_dbg();
+	if (t->next) { // if theres a next entry change its previous
+		if (!t->prev)
+			brtable[hashval] = t->next;
+		t->next->prev = t->prev;
+	}
+	tp_dbg();
+	if (!t->next && !t->prev) // final entry, just remove link entirely
+		brtable[hashval] = NULL;
+
+	tp_dbg();
+	delete_list(t->child_head);
+	delete_list(t->bridge_head);
+	tp_dbg();
+	unregister_child_listener(t);
+	tp_dbg();
+
+	if (t->stale) {
+		tp_dbg("%s is stale, remove from stale list \n", t->mac_addr);
+		list_del(&t->list);
+	}
+	tp_dbg();
+	if (t->v) {
+		t->v->vt = NULL;
+	}
+
+	t->v = NULL;
+	t->parent = NULL;
+	free(t);
+	tp_dbg();
+	return 0;
+}
+
+struct vertex_table *lookup(const char *key)
+{
+	struct vertex_table *entry;
+	unsigned int hashval;
+
+	hashval = hash(key);
+	entry = brtable[hashval];
+	tp_dbg("Looking up entry %s", key);
+	while (entry) {
+		//printf("%d %s %s\n", __LINE__, key, entry->mac_addr);
+		if (strcmp(key, entry->mac_addr) == 0) {
+			tp_dbg("entries matched %s == %s", key, entry->mac_addr);
+			return entry;
+		}
+		//printf("%d %s %s\n", __LINE__, key, entry->mac_addr);
+
+		entry = entry->next;
+	}
+	tp_dbg("%s\n", key);
+
+	return NULL;
+}
+
+unsigned int hash(const char *key)
+{
+	unsigned int hashval;
+
+	key = key+9;
+
+	for (hashval = 0; *key != '\0'; key++)
+		hashval = *key + (31 * hashval);
+
+	return (hashval % HASHSIZE);
+}
+
+/* should probably combine create & has */
+struct bridge_data *get_br_data(struct vertex_table *t, int br_ifindex)
+{
+	struct bridge_data *bd;
+
+	list_for_each_entry(bd, &t->br_indexes, list) {
+		if (bd->br_ifindex == br_ifindex)
+			return bd;
+	}
+
+	return NULL;
+}
+
+struct bridge_data *create_br_data(struct vertex_table *t, int br_ifindex)
+{
+	struct bridge_data *bd;
+	tp_dbg();
+	bd = calloc(1, sizeof(*bd));
+	if (!bd)
+		goto fail;
+	tp_dbg();
+	bd->br_ifindex = br_ifindex;
+	list_add(&bd->list, &t->br_indexes);
+	tp_dbg();
+	tp_dbg("Added %s to table %d", t->mac_addr, br_ifindex);
+
+	return bd;
+fail:
+	return NULL;
+}
+
+int delete_br_data(struct vertex_table *t, int br_ifindex)
+{
+	struct bridge_data *bd;
+
+	bd = get_br_data(t, br_ifindex);
+	if (!bd)
+		goto fail;
+
+	list_del(&bd->list);
+	free(bd);
+
+	return 0;
+fail:
+	return -1;
+}
+
+struct vertex_table *create_entry(struct device_context *dc, struct vertex *v, struct edge *head)
+{
+	struct vertex_table *entry;
+	tp_dbg();
+	entry = (struct vertex_table *)calloc(1, sizeof(*entry));
+	if (!entry)
+		goto fail;
+
+	entry->v = v;
+	entry->bridge_head = head;
+	entry->ageing_timer = -1;
+	entry->ctx = dc;
+	v->vt = entry;
+	strncpy(entry->mac_addr, v->mac_addr, MAC_MAX_LEN);
+	INIT_LIST_HEAD(&entry->br_indexes);
+	tp_dbg();
+	if (!create_br_data(entry, v->br_ifindex))
+		goto fail_bd;
+
+	return entry;
+fail_bd:
+	free(entry);
+fail:
+	return NULL;
+}
+
+struct vertex_table *insert(struct device_context *dc, struct vertex *v, struct edge *head)
+{
+	struct vertex_table *entry;
+	unsigned int hashval;
+	tp_dbg();
+	entry = lookup(v->mac_addr);
+	if (entry) { // if an entry already exists, just update to the new list, reset parents
+		tp_dbg("an existing entry is being altered %s!\n", v->mac_addr);
+		soft_clean_entry(entry);
+
+		/* Could potentially be referenced somewhere, let periodic poll perform
+		the clean of the prior vertex *v */
+		entry->v = v;
+
+		v->vt = entry;
+		entry->bridge_head = head;
+
+		if (!get_br_data(entry, v->br_ifindex))
+			create_br_data(entry, v->br_ifindex); // No error handling, doesn't matter if doesn't get added?
+
+		goto out;
+	}
+	tp_dbg("a new entry %s is being created!\n", v->mac_addr);
+	entry = create_entry(dc, v, head);
+	if (!entry)
+		goto out;
+
+	hashval = hash(v->mac_addr);
+	entry->next = brtable[hashval]; // will this work for null?
+	if (entry->next)
+		entry->next->prev = entry;
+
+	brtable[hashval] = entry;
+out:
+	return entry;
+}
diff --git a/hashmap.h b/hashmap.h
new file mode 100644
index 0000000..f295300
--- /dev/null
+++ b/hashmap.h
@@ -0,0 +1,66 @@
+#ifndef HASH_H
+#define HASH_H
+
+#include "list.h"
+
+#define HASHSIZE 256
+
+/* TODO: better name? */
+struct bridge_data {
+	int br_ifindex;
+
+	struct list_head list;
+};
+
+struct vertex_table {
+	/* identifier */
+	char mac_addr[18];
+
+	/* client status' */
+	bool stale;
+	bool fake_mac;
+
+	/* timers for when entry goes stale */
+	double ageing_timer;
+	struct timeval tv_start;
+
+	/* links to vertexes */
+	struct vertex *v;
+	struct vertex *parent;
+
+	/* all the downport information */
+	struct edge *child_head;
+	struct edge *bridge_head;
+
+	/* in case it is a masquerading entry, always referr to the real entry */
+	struct vertex_table *fake;
+	struct vertex_table *real;
+
+	/* next entry in case of colission */
+	struct vertex_table *next;
+
+	/* prev entry, useful for deletion of entry */
+	struct vertex_table *prev;
+
+	bool active_listener;
+	struct ubus_event_handler listener;
+
+	struct list_head list;
+	struct list_head br_indexes;
+
+	struct device_context *ctx;
+};
+
+int soft_clean_entry_by_mac(char *mac_addr);
+int soft_clean_entry(struct vertex_table *t);
+int clean_entry_by_mac(char *mac_addr, int br_ifindex);
+int clean_entry(struct vertex_table *t, int br_ifindex);
+struct vertex_table *lookup(const char *key);
+unsigned hash(const char *key);
+struct vertex_table *insert(struct device_context *dc, struct vertex *v, struct edge *head);
+struct vertex_table *create_entry(struct device_context *dc, struct vertex *v, struct edge *head);
+int increment_depth(struct vertex *v);
+struct bridge_data *get_br_data(struct vertex_table *t, int br_ifindex);
+struct bridge_data *create_br_data(struct vertex_table *t, int br_ifindex);
+int delete_br_data(struct vertex_table *t, int br_ifindex);
+#endif
diff --git a/ieee1905/main.c b/ieee1905/main.c
new file mode 100644
index 0000000..981476c
--- /dev/null
+++ b/ieee1905/main.c
@@ -0,0 +1,7 @@
+#include "topoubus.h"
+
+int main(int argc, char * argv[])
+{
+	init_topology();
+	return 0;
+}
diff --git a/ieee1905/topomap.c b/ieee1905/topomap.c
new file mode 100644
index 0000000..8c9389d
--- /dev/null
+++ b/ieee1905/topomap.c
@@ -0,0 +1,256 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "topomap.h"
+#include "topoutils.h"
+
+static struct topo_response tr;
+static struct topo1905 topotree;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;
+static int	       resp_available  = 0;
+
+void update_topo_response(struct topo_response *topo_res)
+{
+	memset (&tr, 0, sizeof(struct topo_response));
+
+	tr.nbr_num = (*topo_res).nbr_num;
+	memcpy(&tr.nbr_macs, &topo_res->nbr_macs, sizeof(tr.nbr_macs));
+
+	tr.non1905_nbr_num = (*topo_res).non1905_nbr_num;
+	memcpy(&tr.non1905_nbr_macs, &topo_res->non1905_nbr_macs, sizeof(tr.non1905_nbr_macs));
+
+	if (tr.nbr_num || tr.non1905_nbr_num) {
+		pthread_mutex_lock(&mutex);
+		resp_available = 1;
+		pthread_cond_signal(&cond);
+		pthread_mutex_unlock(&mutex);
+	}
+}
+
+static int topomap_node_add(uint8_t *node_macaddr)
+{
+	uint8_t hwaddr_zero[6] = { 0 };
+	struct node1905 *p;
+	int i;
+
+	if (topotree.num_nodes >= NODE_MAX_NUM)
+		return -1;
+
+	/* insert at the first empty slot */
+	for (i = 0; i < NODE_MAX_NUM; i++) {
+		p = &topotree.node[i];
+		if (!memcmp(p->hwaddr, hwaddr_zero, 6)) {
+			memcpy(p->hwaddr, node_macaddr, 6);
+			topotree.num_nodes++;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static struct node1905 *topomap_node_find(uint8_t *node_macaddr)
+{
+	int i;
+
+	for (i = 0; i < NODE_MAX_NUM; i++) {
+		if (!memcmp(topotree.node[i].hwaddr, node_macaddr, 6))
+			return &topotree.node[i];
+	}
+
+	return NULL;
+}
+
+static bool topomap_node_neighbors_add(uint8_t *node_macaddr,
+				       uint8_t *nbrs_macaddr, int nbrs_num,
+				       uint8_t *non1905_nbrs_macaddr, int non1905_nbrs_num)
+{
+	struct node1905 *p = topomap_node_find(node_macaddr);
+
+	if (!p)
+		return false;
+
+	if (nbrs_num <= NEIGHBOR_MAX_NUM) {
+		p->nbr_num = nbrs_num;
+		memcpy(p->nbrlist, nbrs_macaddr, nbrs_num * 6);
+	}
+
+	if (non1905_nbrs_num <= NEIGHBOR_MAX_NUM) {
+		p->non1905_nbr_num = non1905_nbrs_num;
+		memcpy(p->non1905_nbrlist, non1905_nbrs_macaddr, non1905_nbrs_num * 6);
+	}
+
+	return true;
+}
+
+/* Build full topology tree using a dumb but effective algo.
+ *
+ * Assumption: all nodes keep up-to-date list of their immediate neighbors.
+ *
+ * At any node 'P' in the network -
+ *
+ * Intial state:
+ *		Nbr(P) = {N0,..Nn}  <== immediate neighbors of P.
+ *		G{P, N0,..Nn} <== graph of all known nodes at P.
+ *
+ * Step 1:
+ *		for all Ni in G:
+ *			Send Topology Request to Ni.
+ *
+ * Step 2:
+ *		Read Topology Response from Ni.
+ *		if Nbr(Ni) is not in G:
+ *			Insert Nbr(Ni) in G
+ *
+ * Step 3:
+ *		Repeat Step 1-2 until no new Nbr(Ni) can be inserted in G.
+ *
+ */
+void *topomap_build_thread(void *arg)
+{
+	int ret, idx = 0;
+	int rc;
+	char al_interfaces[NAME_LEN] = "\0";
+	struct al_info *info = NULL;
+	uint8_t node_macaddr[6];
+	struct timespec ts = {0, 0};
+
+	info = get_al_info();
+
+	memcpy(node_macaddr, info->al_mac, 6);
+	strcpy(al_interfaces, info->al_interfaces);
+
+	free(info);
+	if (hwaddr_is_zero(node_macaddr)) {
+		ERROR(" node mac address is empty \n");
+		return NULL;
+	}
+
+	memset (&topotree, 0, sizeof(struct topo1905));
+	ret = topomap_node_add(node_macaddr);
+	if (ret) {
+		ERROR(" Failed to add node \n");
+		return NULL;
+	}
+
+	INFO("\n======Building Topology Tree======\n");
+	while (idx < NODE_MAX_NUM) {
+		uint8_t nbr_macs[NEIGHBOR_MAX_NUM * 6] = {0};
+		uint8_t non1905_nbr_macs[NEIGHBOR_MAX_NUM * 6] = {0};
+
+			idx++;
+			if (hwaddr_is_zero(topotree.node[idx - 1].hwaddr)) {
+				continue;
+			}
+
+		memcpy(node_macaddr, topotree.node[idx - 1].hwaddr, 6);
+		ret = ieee1905_get_neighbors(node_macaddr, al_interfaces);
+		if (ret) {
+			ERROR("ieee1905_get_neighbors failed \n");
+			return NULL;
+		}
+
+		pthread_mutex_lock(&mutex);
+		clock_gettime(CLOCK_REALTIME, &ts);
+		ts.tv_sec += MAX_TIME_WAIT;
+		while (0 == resp_available) {
+			rc = pthread_cond_timedwait(&cond, &mutex, &ts);
+			if (rc == ETIMEDOUT) {
+				INFO("\n======TIMEDOUT======\n");
+				break;
+			}
+		}
+
+		pthread_mutex_unlock(&mutex);
+
+		if (tr.nbr_num || tr.non1905_nbr_num) {
+			DEBUG("topology response available \n");
+			for (int i = 0; i < tr.nbr_num; i++) {
+				memcpy(&nbr_macs[i * 6], tr.nbr_macs[i], 6);
+			}
+
+			for (int i = 0; i < tr.non1905_nbr_num; i++) {
+				memcpy(&non1905_nbr_macs[i * 6], tr.non1905_nbr_macs[i], 6);
+			}
+
+			ret = topomap_node_neighbors_add(node_macaddr, nbr_macs, tr.nbr_num,
+							 non1905_nbr_macs, tr.non1905_nbr_num);
+			if (!ret) {
+				ERROR("Failed to add neighbors \n");
+				memset (&tr, 0, sizeof(struct topo_response));
+				pthread_mutex_lock(&mutex);
+				resp_available = 0;
+				pthread_mutex_unlock(&mutex);
+				return NULL;
+			}
+
+			for (int i = 0; i < tr.nbr_num; i++) {
+				uint8_t *n_mac = &nbr_macs[i * 6];
+
+				if (!topomap_node_find(n_mac)) {
+					topomap_node_add(n_mac);
+				}
+			}
+
+			memset (&tr, 0, sizeof(struct topo_response));
+
+			pthread_mutex_lock(&mutex);
+			resp_available = 0;
+			pthread_mutex_unlock(&mutex);
+		}
+
+	}
+	INFO("\n====== Done Building Topology Tree======\n");
+	pthread_exit(&ret);
+}
+
+void dump_topology_tree(struct blob_buf *bb)
+{
+	struct node1905 *p;
+	int i, j, k;
+	void *nodes_array, *table, *nbr_table, *nbr_array, *non1905_array = NULL;
+	nodes_array = blobmsg_open_array(bb, "Nodes");
+	for (i = 0; i < NODE_MAX_NUM; i++) {
+		p = &topotree.node[i];
+		if (hwaddr_is_zero(p->hwaddr))
+			break;
+
+		table = blobmsg_open_table(bb, NULL);
+
+		char local_mac[18] = { 0x0 };
+		hwaddr_ntoa(p->hwaddr, local_mac);
+
+		blobmsg_add_string(bb, "local_mac", local_mac);
+		blobmsg_add_u32(bb, "neighbors_nr", p->nbr_num);
+
+		nbr_array = blobmsg_open_array(bb, "neighbors");
+		for (j = 0; j < p->nbr_num; j++) {
+			char nbr_mac[18] = { 0x0 };
+			hwaddr_ntoa(&p->nbrlist[j * 6], nbr_mac);
+			nbr_table = blobmsg_open_table(bb, NULL);
+			blobmsg_add_string(bb, "mac_addr", nbr_mac);
+			blobmsg_close_table(bb, nbr_table);
+
+		}
+		blobmsg_close_array(bb, nbr_array);
+
+		blobmsg_add_u32(bb, "non_1905_neighbors_nr", p->non1905_nbr_num);
+		non1905_array = blobmsg_open_array(bb, "non_1905_neighbors");
+		for (k = 0; k < p->non1905_nbr_num; k++) {
+			char non1905_mac[18] = { 0x0 };
+			hwaddr_ntoa(&p->non1905_nbrlist[k * 6], non1905_mac);
+			nbr_table = blobmsg_open_table(bb, NULL);
+			blobmsg_add_string(bb, "mac_addr", non1905_mac);
+			blobmsg_close_table(bb, nbr_table);
+		}
+		blobmsg_close_array(bb, non1905_array);
+		blobmsg_close_table(bb, table);
+	}
+	blobmsg_close_array(bb, nodes_array);
+}
diff --git a/ieee1905/topomap.h b/ieee1905/topomap.h
new file mode 100644
index 0000000..1d2a2fb
--- /dev/null
+++ b/ieee1905/topomap.h
@@ -0,0 +1,31 @@
+#include "topoubus.h"
+#include <libubox/blobmsg.h>
+
+#ifndef TOPOMAP_H
+#define TOPOMAP_H
+
+#define NODE_MAX_NUM		32
+#define NEIGHBOR_MAX_NUM	32
+#define MAX_TIME_WAIT		25
+
+struct node1905 {
+	uint8_t hwaddr[6];
+	uint32_t nbr_num;
+	uint32_t non1905_nbr_num;
+	uint8_t nbrlist[NEIGHBOR_MAX_NUM * 6];
+	uint8_t non1905_nbrlist[NEIGHBOR_MAX_NUM * 6];
+};
+
+struct topo1905 {
+	uint32_t state;
+	int32_t num_nodes;
+	struct node1905 node[NODE_MAX_NUM];
+};
+
+// Topology build thread function. For internal use.
+void *topomap_build_thread(void *arg);
+
+void dump_topology_tree(struct blob_buf *bb);
+void update_topo_response(struct topo_response *topo_res);
+
+#endif /* TOPOMAP_H */
diff --git a/ieee1905/topoubus.c b/ieee1905/topoubus.c
new file mode 100644
index 0000000..0d3b9ef
--- /dev/null
+++ b/ieee1905/topoubus.c
@@ -0,0 +1,382 @@
+#include "topoubus.h"
+#include "topomap.h"
+#include "topoutils.h"
+
+#include <stdbool.h>
+#include <pthread.h>
+#include <json-c/json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/uloop.h>
+#include <libubus.h>
+
+
+static struct mid_list midlist;
+static struct ubus_context *ectx = NULL;
+static struct ubus_event_handler listener;
+static struct uloop_timeout topo_scheduler;
+pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+
+static void topology_schedule();
+
+static void _insertmid(uint32_t mid)
+{
+	int i;
+	for (i = 0; i<MAX_MID; i++) {
+		if (midlist.midval[i] == 0) {
+
+			midlist.midval[i] = mid;
+			midlist.midcount++;
+			break;
+		}
+	}
+}
+
+static void _deletemid(uint32_t mid)
+{
+	int i;
+	for (i = 0; i<MAX_MID; i++) {
+		if (midlist.midval[i] == mid) {
+			midlist.midval[i] = 0;
+			midlist.midcount--;
+			break;
+		}
+	}
+}
+
+static bool _findmid(uint32_t mid)
+{
+	int i;
+	for (i = 0; i<MAX_MID; i++) {
+		if (midlist.midval[i] == mid) {
+			return true;
+		}
+	}
+	return false;
+}
+
+static bool _operatemid(uint32_t mid, mid_op op)
+{
+	bool ret = true;
+	pthread_mutex_lock(&m);
+	if (op == INSERT) {
+		_insertmid(mid);
+	} else if (op == DELETE) {
+		_deletemid(mid);
+	} else if (op == FIND) {
+		if (!_findmid(mid))
+			ret = false;
+	} else {
+		ERROR("Unknown mid operation");
+		ret = false;
+	}
+	pthread_mutex_unlock(&m);
+	return ret;
+}
+
+struct al_info *get_al_info()
+{
+	char *al_macstr		  = NULL;
+	int enabled = 0;
+
+	struct al_info *info = (struct al_info *)malloc(sizeof(struct al_info));
+
+
+	if (!info) {
+		return NULL;
+	}
+
+	memset(info, 0, sizeof(struct al_info));
+	json_object *jconf = json_object_from_file(JSON_CONF);
+	if(!jconf) {
+		ERROR("Parsing of json failed");
+		return NULL;
+	}
+
+	json_object *meshconf;
+	if(!json_object_object_get_ex(jconf, JSON_AL_SEG, &meshconf)) {
+		ERROR("Parsing of meshconf failed");
+		json_safe_put(jconf);
+		return NULL;
+	}
+	enabled = _json_obj_get_int(meshconf, "enabled");
+	if(!enabled) {
+		ERROR("AL-Agent is not enabled");
+		json_safe_put(meshconf);
+		json_safe_put(jconf);
+		return NULL;
+	}
+
+	al_macstr = _json_obj_get_string(meshconf, "mac");
+	uint8_t almac[6];
+	char *al_interfaces = NULL;
+	asciiToMac(al_macstr, almac);
+	al_interfaces = _json_obj_get_string(meshconf, "interfaces");
+	strcpy(info->al_interfaces, al_interfaces);
+	memcpy(info->al_mac, almac, 6);
+
+	return (info);
+}
+
+static void get_1905_topo_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	char *json_str;
+	int status = 0;
+	uint32_t mid = 0;
+
+	if (!msg)
+		return;
+
+	json_str = blobmsg_format_json(msg, true);
+	if (!json_str)
+		return;
+
+	json_object *jobj = json_tokener_parse(json_str);
+	if (!jobj) {
+		free(json_str);
+		return;
+	}
+
+	status = _json_obj_get_int(jobj, "status");
+
+	if (status) {
+		mid = _json_obj_get_int(jobj, "mid");
+		_operatemid(mid, INSERT);
+	}
+}
+
+int get_interface_neighbors(const char *interface_name, uint8_t *dmac)
+{
+	int ret = 0;
+	unsigned int id;
+	uint8_t macaddr[6];
+	char macstr[18] = {0x0};
+	struct ubus_context *ctx = NULL;
+	struct blob_buf bb;
+
+	DEBUG("interface_name %s \n", interface_name);
+
+	ctx = ubus_connect(NULL);
+	if (!ctx) {
+		ERROR("ubus connect failed\n");
+		return -1;
+	}
+	char objpath[NAME_LEN] = "ieee1905.al.";
+	strcat(objpath, interface_name);
+
+	if (UBUS_STATUS_OK != ubus_lookup_id(ctx, objpath, &id)) {
+		ERROR("lookup Object %s failed\n", objpath);
+		return -1;
+	}
+
+	memcpy(macaddr, dmac, 6);
+	macToAscii(macaddr, macstr);
+
+	memset(&bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+	blobmsg_add_string(&bb, "remote_mac", macstr);
+
+	ret = ubus_invoke(ctx, id, GET_TOPOLOGY, bb.head, get_1905_topo_cb, NULL, 2 * 1000);
+	if (ret) {
+		ERROR("Failed to get ieee1905 topology\n");
+	}
+
+	blob_buf_free(&bb);
+	ubus_free(ctx);
+	return ret;
+}
+
+int ieee1905_get_neighbors(uint8_t *dmac, char *al_interfaces)
+{
+	int ret = 0;
+	char* interface_name;
+	char *save_ptr;
+	char aux[NAME_LEN] = "\0";
+	strcpy(aux, al_interfaces);
+
+	interface_name = strtok_r(aux, ",", &save_ptr);
+	if (NULL != interface_name)
+		ret = get_interface_neighbors(interface_name, dmac);
+
+	while (NULL != (interface_name = strtok_r(NULL, ",", &save_ptr))) {
+		ret = get_interface_neighbors(interface_name, dmac);
+	}
+
+	return ret;
+}
+
+void ieee1905_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg)
+{
+	char *str;
+	struct json_object *jmsg, *tlv_array;
+	int i, j, len, len_nbr, len_non_nbr = 0;
+	uint32_t mid = 0;
+	struct topo_response res;
+
+	if (!msg)
+		return;
+
+	memset(&res, 0, sizeof(struct topo_response));
+
+	str = blobmsg_format_json(msg, true);
+	jmsg = json_tokener_parse(str);
+	if (!jmsg) {
+		free(str);
+		return;
+	}
+	if (!json_object_is_type(jmsg, json_type_object))
+		return;
+
+	mid = _json_obj_get_int(jmsg, "message_id");
+	if (_operatemid(mid, FIND)) {
+		DEBUG("found mid \n");
+		if (!json_object_object_get_ex(jmsg, "TLVs", &tlv_array)) {
+			ERROR("Parsing of JSON response failed\n");
+			return;
+		}
+		len = json_object_array_length(tlv_array);
+		for (i = 0; i < len; i++) {
+			char *tlv_type, *nbr_mac, *non_nbr_mac;
+			struct json_object *tlv_obj;
+			struct json_object *nbr_array, *nbr_obj, *non_nbr_array, *non_nbr_obj;
+			tlv_obj = json_object_array_get_idx(tlv_array, i);
+
+			tlv_type = _json_obj_get_string(tlv_obj, "tlv_type");
+			if (0 == strcmp(tlv_type, "TLV_TYPE_NEIGHBOR_DEVICE_LIST")) {
+				res.nbr_num = _json_obj_get_int(tlv_obj, "neighbors_nr");
+				if (res.nbr_num > 0) {
+					json_object_object_get_ex(tlv_obj, "neighbors", &nbr_array);
+					len_nbr = json_object_array_length(nbr_array);
+					for (j = 0; j < len_nbr; j++) {
+						nbr_obj = json_object_array_get_idx(nbr_array, j);
+						nbr_mac = _json_obj_get_string(nbr_obj, "mac_address");
+						asciiToMac(nbr_mac, res.nbr_macs[j]);
+						free(nbr_mac);
+					}
+				}
+			}
+			if (0 == strcmp(tlv_type, "TLV_TYPE_NON_1905_NEIGHBOR_DEVICE_LIST")) {
+				res.non1905_nbr_num = _json_obj_get_int(tlv_obj, "non_1905_neighbors_nr");
+				if (res.non1905_nbr_num > 0) {
+					json_object_object_get_ex(tlv_obj, "neighbors", &non_nbr_array);
+					len_non_nbr = json_object_array_length(non_nbr_array);
+
+					for (int k = 0; k < len_non_nbr; k++) {
+						non_nbr_obj = json_object_array_get_idx(non_nbr_array, k);
+						non_nbr_mac = _json_obj_get_string(non_nbr_obj, "mac_address");
+						asciiToMac(non_nbr_mac, res.non1905_nbr_macs[k]);
+						free(non_nbr_mac);
+					}
+				}
+			}
+		}
+			if (res.nbr_num || res.non1905_nbr_num) {
+				DEBUG("updating topology response \n");
+				update_topo_response(&res);
+			}
+			_operatemid(mid, DELETE);
+	}
+
+	free(str);
+}
+
+static int start_topology_thread()
+{
+	pthread_t topo_thread_id;
+
+	if(0 != pthread_create(&topo_thread_id, NULL, topomap_build_thread, NULL)) {
+		ERROR("Topology build thread creation failed!!!");
+		return -1;
+	}
+	return 0;
+}
+
+static void topology_schedule_cb(struct uloop_timeout *timeout)
+{
+	DEBUG("Building topology tree every 60s \n");
+	start_topology_thread();
+	topology_schedule();
+}
+
+static void topology_schedule()
+{
+	topo_scheduler.cb = topology_schedule_cb;
+	uloop_timeout_set(&topo_scheduler, TOPOLOGY_TIMEOUT);
+}
+
+static int topo1905_build_tree(struct ubus_context *ectx, struct ubus_object *obj __attribute__ ((unused)),					  struct ubus_request_data *req, const char *method __attribute__ ((unused)),
+				       struct blob_attr *msg __attribute__ ((unused)))
+{
+	struct blob_buf bb;
+
+	memset(&bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	start_topology_thread();
+
+	blobmsg_add_u8(&bb, "status", true);
+	ubus_send_reply(ectx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+static int topo1905_dump_tree(struct ubus_context *ectx, struct ubus_object *obj __attribute__ ((unused)),					 struct ubus_request_data *req, const char *method __attribute__ ((unused)),
+				       struct blob_attr *msg __attribute__ ((unused)))
+{
+	struct blob_buf bb;
+
+	memset(&bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&bb, 0);
+
+	dump_topology_tree(&bb);
+
+	ubus_send_reply(ectx, req, bb.head);
+	blob_buf_free(&bb);
+	return 0;
+}
+
+static const struct ubus_method topo1905_methods[] = {
+	UBUS_METHOD_NOARG("build_tree", topo1905_build_tree),
+	UBUS_METHOD_NOARG("dump_tree", topo1905_dump_tree),
+};
+
+static struct ubus_object_type topo1905_object_type =
+	UBUS_OBJECT_TYPE("topo1905", topo1905_methods);
+
+static struct ubus_object topo1905_object = {
+	.name = "topo1905",
+	.type = &topo1905_object_type,
+	.methods = topo1905_methods,
+	.n_methods = ARRAY_SIZE(topo1905_methods),
+};
+
+int init_topology()
+{
+
+	uloop_init();
+	ectx = ubus_connect(NULL);
+	if (!ectx) {
+		ERROR("ubus connect failed\n");
+		return -1;
+	}
+
+	ubus_add_uloop(ectx);
+	listener.cb = ieee1905_event_handler;
+	if (0 != ubus_register_event_handler(ectx, &listener, IEEE1905_EVENT)) {
+		ERROR("ieee1905 event register failed!\n");
+		return -1;
+	}
+
+	int ret = ubus_add_object(ectx, &topo1905_object);
+	if (ret)
+		fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
+
+	memset(&midlist, 0, sizeof(struct mid_list));
+
+	topology_schedule();
+	uloop_run();
+	ubus_free(ectx);
+	uloop_done();
+
+	return 0;
+}
diff --git a/ieee1905/topoubus.h b/ieee1905/topoubus.h
new file mode 100644
index 0000000..08f9395
--- /dev/null
+++ b/ieee1905/topoubus.h
@@ -0,0 +1,49 @@
+#ifndef TOPOUBUS_H
+#define TOPOUBUS_H
+
+#include <libubus.h>
+
+#define IEEE1905_EVENT "ieee1905.event"
+#define GET_TOPOLOGY "topology"
+#define JSON_CONF "/tmp/ieee1905.config"
+#define JSON_AL_SEG "al"
+#define TOPOLOGY_TIMEOUT 60000   // 60 sec
+
+#define MAX_RESPONSE_NUM 32
+#define TLV_NAME_LEN 128
+#define MAX_NBRS 32
+#define NAME_LEN 64
+#define MAX_MID 32
+
+typedef enum {
+	INSERT,
+	DELETE,
+	FIND,
+	UNKNOWN
+} mid_op;
+
+struct mid_list
+{
+	uint8_t midcount;
+	uint32_t midval[MAX_MID];
+};
+
+struct al_info
+{
+	uint8_t al_mac[6];
+	char al_interfaces[NAME_LEN];
+};
+
+struct topo_response
+{
+	uint8_t nbr_num;
+	uint8_t non1905_nbr_num;
+	uint8_t nbr_macs[MAX_NBRS][6];
+	uint8_t non1905_nbr_macs[MAX_NBRS][6];
+};
+
+int init_topology();
+struct al_info *get_al_info();
+int ieee1905_get_neighbors(uint8_t *dmac, char *al_interfaces);
+
+#endif
diff --git a/ieee1905/topoutils.c b/ieee1905/topoutils.c
new file mode 100644
index 0000000..996640f
--- /dev/null
+++ b/ieee1905/topoutils.c
@@ -0,0 +1,102 @@
+#include "topoutils.h"
+
+char* _json_obj_get_string(json_object *j, const char *key)
+{
+	if(j == NULL)
+		return NULL;
+
+	json_object *t;
+	if(json_object_object_get_ex(j, key, &t)) {
+		char *str = strdup(json_object_get_string(t));
+		return(str);
+	}
+	DEBUG("|%s| not found", key);
+	return NULL;
+}
+
+int _json_obj_get_int(json_object *j, const char *key)
+{
+	int ret = 0;
+	if(j == NULL)
+		return 0;
+
+	json_object *t;
+	if(json_object_object_get_ex(j, key, &t)) {
+		ret = json_object_get_int(t);
+	}
+	return(ret);
+}
+
+void json_safe_put(json_object *j) {
+	if(j != NULL) {
+		json_object_put(j);
+	}
+	j = NULL;
+}
+
+// Convert a character to lower case
+//
+char asciiToLowCase (char c)
+{
+	if (c >= 'a' && c <= 'z') {
+		return c;
+	}
+	else if (c >= 'A' && c <= 'Z') {
+		return c + ('a' - 'A');
+	}
+	else {
+		return c;
+	}
+}
+
+// Convert a MAC string representation (example: "0a:fa:41:a3:ff:40") into a
+// six bytes array (example: {0x0a, 0xfa, 0x41, 0xa3, 0xff, 0x40})
+//
+void asciiToMac(const char *str, uint8_t *addr)
+{
+	int i = 0;
+
+	if (NULL == str) {
+		addr[0] = 0x00;
+		addr[1] = 0x00;
+		addr[2] = 0x00;
+		addr[3] = 0x00;
+		addr[4] = 0x00;
+		addr[5] = 0x00;
+
+		return;
+	}
+
+	while (0x00 != *str && i < 6) {
+		uint8_t byte = 0;
+
+		while (0x00 != *str && ':' != *str) {
+			char low;
+
+			byte <<= 4;
+			low    = asciiToLowCase (*str);
+
+			if (low >= 'a') {
+				byte |= low - 'a' + 10;
+			}
+			else {
+				byte |= low - '0';
+			}
+			str++;
+		}
+
+		addr[i] = byte;
+		i++;
+
+		if (*str == 0) {
+			break;
+		}
+
+		str++;
+	  }
+}
+
+void macToAscii(uint8_t *addr, char *mac)
+{
+	snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
diff --git a/ieee1905/topoutils.h b/ieee1905/topoutils.h
new file mode 100644
index 0000000..f0130fe
--- /dev/null
+++ b/ieee1905/topoutils.h
@@ -0,0 +1,37 @@
+#ifndef TOPOUTILS_H
+#define TOPOUTILS_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <json-c/json.h>
+
+#include <easy/easy.h>
+
+void asciiToMac (const char *str, uint8_t *addr);
+void macToAscii(uint8_t *, char *);
+char* _json_obj_get_string(json_object *j, const char *key);
+int _json_obj_get_int(json_object *j, const char *key);
+void json_safe_put(json_object *j);
+void print_info(const char *format, ...);
+
+#define DEBUG_ENABLED 0
+#define DEBUG(fmt, args...) \
+do { \
+	if (DEBUG_ENABLED) \
+		syslog(LOG_DEBUG, "[%s:%d] " fmt, __func__, __LINE__, ##args); \
+} while (0)
+
+#define INFO(fmt, args...) \
+do { \
+	syslog(LOG_INFO, "[%s:%d] " fmt, __func__, __LINE__, ##args); \
+} while (0)
+
+#define ERROR(fmt, args...) \
+do { \
+	syslog(LOG_ERR, "[%s:%d] " fmt, __func__, __LINE__, ##args); \
+} while (0)
+
+#endif
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..ad8d21c
--- /dev/null
+++ b/list.c
@@ -0,0 +1,117 @@
+#include "list.h"
+
+int enqueue(struct edge **head, struct edge *elem)
+{
+	if (!*head) {
+		*head = elem;
+
+		goto done;
+	}
+
+	if (search_list(*head, elem->mac_addr))
+		goto done;
+
+	elem->next = *head;
+	(*head)->prev = elem;
+	*head = elem;
+
+done:
+	return 0;
+}
+
+struct edge *copy_edge(struct edge *e)
+{
+	struct edge *cpy;
+
+	cpy = calloc(1, sizeof(struct edge));
+	if (!cpy)
+		goto fail;
+
+	strncpy(cpy->mac_addr, e->mac_addr, MAC_MAX_LEN);
+
+	if (strlen(e->downlink_type))
+		strncpy(cpy->downlink_type, e->downlink_type, LINK_TYPE_SIZE);
+
+	if (strlen(e->downlink_port))
+		strncpy(cpy->downlink_port, e->downlink_port, IF_NAMESIZE);
+
+	cpy->downlink_port_no = e->downlink_port_no;
+
+	cpy->prev = NULL;
+	cpy->next = NULL;
+
+	return cpy;
+fail:
+	return NULL;
+}
+
+struct edge *create_node(const char *mac_addr, const char *downlink_type, const char *downlink_port, int downlink_port_no)
+{
+	struct edge *e;
+
+	//lprintf("%d creating edge for %s with downlink_type %s %s %d\n", __LINE__, mac_addr, downlink_type, downlink_port, downlink_port_no);
+
+	e = calloc(1, sizeof(struct edge));
+	if (!e)
+		goto fail;
+
+	strncpy(e->mac_addr, mac_addr, MAC_MAX_LEN);
+
+	if (downlink_type)
+		strncpy(e->downlink_type, downlink_type, LINK_TYPE_SIZE);
+
+	if (downlink_port)
+		strncpy(e->downlink_port, downlink_port, IF_NAMESIZE);
+
+	e->downlink_port_no = downlink_port_no;
+
+	e->prev = NULL;
+	e->next = NULL;
+
+	return e;
+fail:
+	return NULL;
+}
+
+struct edge *search_list(struct edge *head, char *mac_addr)
+{
+	struct edge *e = head;
+
+	while (e) {
+		if (strcmp(e->mac_addr, mac_addr) == 0)
+			return e;
+
+		e = e->next;
+	}
+	return NULL;
+}
+
+void delete_list(struct edge *head)
+{
+	struct edge *e, *tmp;
+
+	e = head;
+	while (e) {
+		tmp = e;
+		e = e->next;
+
+		delete_edge(tmp);
+	}
+}
+
+void delete_edge(struct edge *e)
+{
+	free(e);
+}
+
+void print_list(struct edge *head)
+{
+	struct edge *e = head;
+	int i = 0;
+
+	while (e) {
+		tp_dbg("edge %d mac addr - %s available on downlink_type %s", i, e->mac_addr, e->downlink_type);
+		e = e->next;
+		i++;
+	}
+}
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..5c78bde
--- /dev/null
+++ b/list.h
@@ -0,0 +1,34 @@
+#ifndef LL_H
+#define LL_H
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <json-c/json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/uloop.h>
+#include <libubox/list.h>
+#include <libubus.h>
+
+#include "topologyd.h"
+
+struct edge {
+	struct edge *next;
+	struct edge *prev;
+
+	char downlink_port[IF_NAMESIZE];
+	int downlink_port_no;
+	char downlink_type[9]; //LINK_TYPE_SIZE
+	char mac_addr[18]; // this should probably be vertex table rather than mac_addr
+	double age;
+};
+
+int enqueue(struct edge **head, struct edge *elem);
+struct edge *copy_edge(struct edge *e);
+struct edge *create_node(const char *mac_addr, const char *downlink_type, const char *downlink_port, int downlink_port_no);
+struct edge *search_list(struct edge *head, char *mac_addr);
+void delete_list(struct edge *head);
+void delete_edge(struct edge *e);
+void print_list(struct edge *head);
+#endif
diff --git a/make-and-run-topologyd-on-target-setup.sh b/make-and-run-topologyd-on-target-setup.sh
new file mode 100755
index 0000000..49fa32f
--- /dev/null
+++ b/make-and-run-topologyd-on-target-setup.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+#===== MAKE AND RUN ======
+# Clean old code then copy from local git repo
+ARM_DIR=~/git/new-dg400/build_dir/target-arm_xscale_musl_eabi/topologyd-0.1
+RAMIPS_DIR=~/git/new-ex400/build_dir/target-mipsel_1004kc_musl/topologyd-0.1
+
+rm $ARM_DIR/*
+cp * $ARM_DIR
+
+rm $RAMIPS_DIR/*
+cp * $RAMIPS_DIR
+
+#Master compile
+#cd ~/git/dg400/
+
+#./iop compile topologyd V=s && ssh root@192.168.1.1 "killall topologyd" ; scp ~/git/iop-dg400-topd/iopsys/build_dir/target-arm_xscale_musl_eabi/topologyd-0.1/topologyd root@192.168.1.1:/sbin/
+cd ~/git/new-dg400/
+./iop compile topologyd $1
+
+scp ~/git/new-dg400/bin/packages/arm_xscale/iopsys/topologyd_0.1_arm_xscale.ipk root@192.168.1.1:/tmp/
+ssh root@192.168.1.1 "/etc/init.d/log restart"
+ssh root@192.168.1.1 "opkg --force-reinstall install /tmp/topologyd_0.1_arm_xscale.ipk"
+
+#scp ~/git/dg400/bin/packages/arm_xscale/feed_inteno_packages/topologyd_0.1_arm_xscale.ipk root@192.168.2.1:/tmp/
+#ssh root@192.168.2.1 "/etc/init.d/log restart"
+#ssh root@192.168.2.1 "opkg --force-reinstall install /tmp/topologyd_0.1_arm_xscale.ipk"
+
+# Extenders/Repeaters compile
+cd ~/git/new-ex400
+
+EXT1_IP="192.168.1.120"
+EXT2_IP="192.168.1.143"
+#EXT2_IP="192.168.1.142"
+#EXT3_IP="192.168.1.146"
+EXT3_IP="192.168.10.248"
+./iop compile topologyd $1
+scp ~/git/new-ex400/bin/packages/mipsel_1004kc/iopsys/topologyd_0.1_mipsel_1004kc.ipk root@$EXT1_IP:/tmp/
+scp ~/git/new-ex400/bin/packages/mipsel_1004kc/iopsys/topologyd_0.1_mipsel_1004kc.ipk root@$EXT2_IP:/tmp/
+scp ~/git/new-ex400/bin/packages/mipsel_1004kc/iopsys/topologyd_0.1_mipsel_1004kc.ipk root@$EXT3_IP:/tmp/
+
+ssh root@$EXT1_IP "opkg --force-reinstall install /tmp/topologyd_0.1_mipsel_1004kc.ipk" & ssh root@$EXT2_IP "opkg --force-reinstall install /tmp/topologyd_0.1_mipsel_1004kc.ipk" & ssh root@$EXT3_IP "opkg --force-reinstall install /tmp/topologyd_0.1_mipsel_1004kc.ipk"
+##&& ssh root@$EXT1_IP "killall topologyd" ; ssh root@$EXT1_IP rm /sbin/topologyd; ssh root@$EXT2_IP "killall topologyd" ; ssh root@$EXT2_IP rm /sbin/topologyd ; ssh root@$EXT3_IP "killall topologyd" ; ssh root@$EXT3_IP rm /sbin/topologyd; scp ~/git/ex400/iop-cc/build_dir/target-mipsel_1004kc_musl/topologyd-0.1/topologyd root@$EXT1_IP:/sbin/ && scp ~/git/ex400/iop-cc/build_dir/target-mipsel_1004kc_musl/topologyd-0.1/topologyd root@$EXT2_IP:/sbin && scp ~/git/ex400/iop-cc/build_dir/target-mipsel_1004kc_musl/topologyd-0.1/topologyd root@$EXT3_IP:/sbin
+
+#Start topologyd
+#ssh root@192.168.1.1 "\$(topologyd) &"
+#ssh root@$EXT1_IP "\$(topologyd) &"
+#ssh root@$EXT2_IP "\$(topologyd) &"
+#ssh root@$EXT3_IP "\$(topologyd) &"
diff --git a/table_gen.c b/table_gen.c
new file mode 100644
index 0000000..1157365
--- /dev/null
+++ b/table_gen.c
@@ -0,0 +1,241 @@
+#include "table_gen.h"
+
+//struct port ports[256]; // number 256 chosen because thats what was used in br_make_porT_list
+
+int br_port_ioctl32(char *bridge, int br_socket_fd, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3)
+{
+	unsigned long args[4];
+	struct ifreq ifr;
+
+	args[0] = arg0;
+	args[1] = arg1;
+	args[2] = arg2;
+	args[3] = arg3;
+
+	memcpy(ifr.ifr_name, bridge, IFNAMSIZ);
+	((unsigned long *)(&ifr.ifr_data))[0] = (unsigned long)args;
+
+	return ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
+}
+
+/* Concious decision to sacriface some time complexity by arranging the ports
+	are ifindex rather than port_no. port_no is not frequently used and
+	are usually between 1 to 6, meaning fast to iterate by for loop */
+int br_make_port_list(struct bridge *b)
+{
+	int i, idx;
+	int ifindices[256];
+	int br_socket_fd;
+
+	br_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (br_socket_fd < 0)
+		return -1;
+
+	if (br_port_ioctl32(b->ifname, br_socket_fd, BRCTL_GET_PORT_LIST, (unsigned long)ifindices, 0, 0) < 0)
+		goto fail;
+
+	for (i = 0; i < 256; i++) {
+		if (!ifindices[i])
+			continue;
+
+		/*
+		ports[i].index = i;
+		ports[i].ifindex = ifindices[i];
+		*/
+
+		idx = ifindices[i];
+
+		/* ports are indexed by their ifindex */
+		b->ports[idx].index = i;
+		b->ports[idx].ifindex = ifindices[i];
+		b->ports[idx].br_ifindex = b->ifindex;
+		if_indextoname(ifindices[i], b->ports[idx].ifname);
+		DBGP("%s belongs under bridge %s with portno %d and ifindex %d", b->ports[idx].ifname, b->ifname, i, ifindices[i]);
+		//printf("%d port %s can be found at port_no %d and ifindex %d\n", __LINE__, if_indextoname(ports[idx].ifindex, ifname), ports[idx].index, ports[idx].ifindex); //[idx]
+	}
+
+fail:
+	close(br_socket_fd);
+	return 0;
+}
+
+int br_device_ioctl32(char *bridge, int br_socket_fd, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3)
+{
+	unsigned long args[4];
+	struct ifreq ifr;
+	//lprintf("%d\n", __LINE__);
+	args[0] = arg0;
+	args[1] = arg1;
+	args[2] = arg2;
+	args[3] = arg3;
+	//lprintf("%d\n", __LINE__);
+	memcpy(ifr.ifr_name, bridge, IFNAMSIZ);
+	((unsigned long *)(&ifr.ifr_data))[0] = (unsigned long)args;
+	//lprintf("%d\n", __LINE__);
+	return ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
+}
+
+void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
+{
+	unsigned long long tvusec;
+
+	tvusec = (1000000ULL * jiffies) / HZ;
+	tv->tv_sec = tvusec / 1000000;
+	tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
+}
+
+void __copy_fdb(struct fdb_entry *ent, struct __fdb_entry *f)
+{
+	//lprintf("%d\n", __LINE__);
+	memcpy(ent->mac_addr, f->mac_addr, 6);
+	ent->port_no = f->port_no;
+	ent->is_local = f->is_local;
+	__jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value);
+}
+
+int br_read_fdb(char *bridge, struct fdb_entry *fdbs, int offset, int num)
+{
+	struct __fdb_entry f[num];
+	int i;
+	int numread = -1;
+	int br_socket_fd;
+
+	br_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (br_socket_fd < 0)
+		goto fail;
+
+	numread = br_device_ioctl32(bridge, br_socket_fd, BRCTL_GET_FDB_ENTRIES, (unsigned long)f, num, offset);
+	for (i = 0; i < numread; i++)
+		__copy_fdb(fdbs + i, f + i);
+
+fail:
+	close(br_socket_fd);
+	return numread;
+}
+
+struct port *get_port_by_idx(struct list_head *bridge, int idx)
+{
+	struct bridge *b;
+
+	list_for_each_entry(b, bridge, list) {
+		if (b->ports[idx].index == 0 && b->ports[idx].ifindex == 0)
+			continue;
+
+		return &b->ports[idx];
+	}
+
+	return NULL;
+}
+
+struct port *get_port_by_no(struct port *ports, int port_no)
+{
+	struct port *p;
+	int i;
+
+	/* realistically ports are numbered between 1-6, but 256 max */
+	for (i = 0; i < 256; i++) {
+		p = &ports[i];
+
+		if (p->index == port_no)
+			return p;
+	}
+
+	return NULL;
+}
+
+int compare_fdbs(const void *_f0, const void *_f1)
+{
+	const struct fdb_entry *f0 = _f0;
+	const struct fdb_entry *f1 = _f1;
+
+	return memcmp(f0->mac_addr, f1->mac_addr, 6);
+}
+
+void br_cmd_showmacs(struct bridge *b)
+{
+	struct fdb_entry fdb[1024];
+	struct fdb_entry *ent;
+	char mac[MAC_MAX_LEN];
+	int offset;
+	struct arp_entry *ae;
+	char port_name[16] = {0};
+	struct port *p;
+
+	tp_dbg("%s\n", b->ifname);
+
+	//lprintf("%d\n", __LINE__);
+	//lprintf("%d port no\tmac addr\t\tis local?\tageing timer\n", __LINE__);
+
+	offset = 0;
+	while (1) {
+		int i;
+		int num;
+		char test[16];
+		tp_dbg();
+		num = br_read_fdb(b->ifname, fdb, offset, 1024);
+		if (num <= 0)
+			break;
+		tp_dbg("%s\n", b->ifname);
+		qsort(fdb, num, sizeof(struct fdb_entry), compare_fdbs);
+
+		for (i = 0; i < num; i++) {
+			ent = fdb + i;
+			if (ent->is_local)
+				continue;
+			tp_dbg("%s\n", b->ifname);
+			snprintf(mac, MAC_MAX_LEN, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t",
+					 ent->mac_addr[0], ent->mac_addr[1], ent->mac_addr[2],
+					 ent->mac_addr[3], ent->mac_addr[4], ent->mac_addr[5]);
+			tp_dbg("%s found under bridge %s\n", mac, b->ifname);
+			ae = insert_arp(mac);
+			if (!ae)
+				continue;
+
+			ae->port_no = ent->port_no;
+			p = get_port_by_no(b->ports, ent->port_no);
+			if (!p) {
+				tp_dbg("no valid port for %s?\n", ae->mac_addr);
+				continue;
+			}
+			tp_dbg("%d %d %d %s %s\n", ent->port_no, p->index, p->ifindex, p->ifname, b->ifname);
+			br_show_timer(&ent->ageing_timer_value, test);
+			add_bridge_age_arp(&ent->ageing_timer_value, ae, b->ifindex);
+			ae->ifindex = p->ifindex;
+			ae->br_ifindex = b->ifindex;
+			insert_ifindex(mac, p->ifindex, b->ifindex);
+			if (!if_indextoname(ae->ifindex, port_name)) { // could clean it right away here
+				tp_dbg("no valid portname for %s?\n", ae->mac_addr);
+				continue;
+			}
+
+			strncpy(ae->ifname, port_name, IF_NAMESIZE);
+
+			tp_dbg("%s: %s %s %d %d\n", b->ifname, ae->mac_addr, ae->ifname, ae->ifindex, ae->port_no);
+			// what happens here?
+		}
+
+		offset += num;
+	}
+	tp_dbg();
+}
+
+int generate_neightable(struct list_head *bridges)
+{
+	char ifname[16];
+	int rv;
+	struct bridge *b;
+
+	rv = get_bridges(bridges);
+
+	list_for_each_entry(b, bridges, list) {
+		if_indextoname(b->ifindex, ifname);
+
+		tp_dbg("%d %s\n", b->ifindex, ifname);
+
+		br_make_port_list(b);
+		br_cmd_showmacs(b);
+		tp_dbg("tables are finished from here\n");
+	}
+
+	return rv;
+}
diff --git a/table_gen.h b/table_gen.h
new file mode 100644
index 0000000..67f6509
--- /dev/null
+++ b/table_gen.h
@@ -0,0 +1,27 @@
+#ifndef TABLE_H
+#define TABLE_H
+
+#include "utils.h"
+
+struct bridge;
+struct port;
+
+struct fdb_entry {
+	u_int8_t mac_addr[6];
+	u_int16_t port_no;
+	unsigned char is_local;
+	struct timeval ageing_timer_value;
+};
+
+void __dump_fdb_entry(struct fdb_entry *f);
+int br_device_ioctl32(char *bridge, int br_socket_fd, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3);
+void __copy_fdb(struct fdb_entry *ent, struct __fdb_entry *f);
+int br_read_fdb(char *bridge, struct fdb_entry *fdbs, int offset, int num);
+void br_cmd_showmacs(struct bridge *b);
+int br_port_ioctl32(char *bridge, int br_socket_fd, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3);
+int br_make_port_list(struct bridge *b);
+int generate_neightable(struct list_head *bridges);
+struct port *get_port_by_idx(struct list_head *list, int idx);
+struct port *get_port_by_no(struct port *ports, int no);
+
+#endif
diff --git a/topologyd.c b/topologyd.c
new file mode 100644
index 0000000..e3e997e
--- /dev/null
+++ b/topologyd.c
@@ -0,0 +1,2486 @@
+#include "topologyd.h"
+
+bool is_wifi(char *if_name);
+int prepare_dump_blob(struct device_context *dc, struct blob_buf *bb);
+struct vertex *create_vertex(const char *mac_addr);
+static void parse_dump(struct ubus_request *req, int type, struct blob_attr *msg);
+static int invoke_client(struct device_context *dc, const char *client);
+static int first_pass(struct device_context *dc);
+static int second_pass(struct device_context *dc);
+void update_child_list(struct vertex *v);
+void third_pass(struct device_context *dc);
+void tree_to_blob(struct blob_buf *bb, struct vertex_table *t, struct edge *parent_edge, struct vertex_table *root);
+void print_tree(struct device_context *dc, struct ubus_context *ctx, struct ubus_request_data *req);
+int register_child_listener(struct device_context *dc, char *ip);
+static int get_neightable(struct device_context *dc);
+int msg_handler(struct device_context *ctx, const struct sockaddr_nl *nl, struct nlmsghdr *msg);
+static void poll_stale_nodes(struct uloop_timeout *timeout);
+
+#define AGEING_MARGIN 12
+#define AGEING_PING_MARGIN 10
+
+struct vertex *get_vertex_by_mac_bridge(struct device_context *dc, struct bridge *b, char *mac)
+{
+	struct vertex *v;
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		if (strncmp(v->mac_addr, mac, MAC_MAX_LEN) != 0)
+			continue;
+
+		if (v->br_ifindex != b->ifindex)
+			continue;
+
+		return v;
+	}
+
+	return NULL;
+}
+
+struct vertex *get_vertex_by_ip(struct device_context *dc, char *ip)
+{
+	struct vertex *v;
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		if (strncmp(v->ip_addr, ip, IPV4_MAX_LEN) != 0)
+			continue;
+
+		return v;
+	}
+
+	return NULL;
+}
+
+static void parse_stas(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	struct priv {
+		struct bridge *b;
+		struct blob_buf *bb;
+	};
+	struct priv *priv_data = (struct priv *)req->priv;
+	struct bridge *b = priv_data->b;
+	struct blob_buf *bb = priv_data->bb;
+	const char *mac;
+	char *json_str;
+	struct arp_entry *ae;
+	//struct blob_buf *bb = (struct blob_buf *)req->priv;
+	struct json_object *json_msg, *arr, *elem;
+	void *table;
+	int i;
+	double age;
+	char buffer[128];
+
+	json_str = blobmsg_format_json(msg, true);
+	if (!json_str)
+		return;
+
+	json_msg = json_tokener_parse(json_str);
+	if (!json_msg)
+		goto out_str;
+
+	if (!json_object_is_type(json_msg, json_type_object))
+		goto out_json;
+
+	arr = json_get_array(json_msg, "assoclist");
+	if (!arr)
+		goto fail_key;
+
+	for (i = 0; i < json_object_array_length(arr); i++) {
+		elem = json_object_array_get_idx(arr, i);
+		if (!elem)
+			continue;
+
+		mac = json_get_string(elem, "macaddr");
+		if (!mac)
+			continue;
+
+		ae = lookup_arp(mac);
+		if (!ae)
+			continue;
+		if (!strlen(ae->ip_addr))
+			continue;
+		table = blobmsg_open_table(bb, "client");
+		printf("%s %d %s\n", __func__, __LINE__, ae->mac_addr);
+		blobmsg_add_string(bb, "macaddr", ae->mac_addr);
+		if (ae->ip_addr) {
+			printf("%s %d %s\n", __func__, __LINE__, ae->ip_addr);
+			blobmsg_add_string(bb, "ipaddr", ae->ip_addr);
+		}
+		printf("%d\n", __LINE__);
+		blobmsg_add_u32(bb, "rssi", json_get_int(elem, "rssi"));
+
+		/* new addition */
+		age = get_bridge_age_arp(ae, b->ifindex);
+		blobmsg_add_double(bb, "ageing_timer", age);
+
+		printf("%s %d %lf\n", __func__, __LINE__, age);
+		blobmsg_close_table(bb, table);
+	}
+
+fail_key:
+out_json:
+	json_object_put(json_msg);
+out_str:
+	free(json_str);
+}
+
+int get_wl_stas(struct ubus_context *ctx, struct bridge *b, char *ifname, struct blob_buf *bb)
+{
+	int rv;
+	uint32_t obj_id;
+	struct blob_buf tmp_bb = {0};
+	struct {
+		struct bridge *b;
+		struct blob_buf *bb;
+	} priv_data;
+
+	ubus_lookup_id(ctx, "wifix", &obj_id);
+	if (!obj_id)
+		goto fail;
+
+	rv = blob_buf_init(&tmp_bb, 0);
+	if (rv) {
+		printf("blob_buf_init error\n");
+		goto fail;
+	}
+
+	blobmsg_add_string(&tmp_bb, "vif", ifname);
+
+	priv_data.bb = bb;
+	priv_data.b = b;
+	rv = ubus_invoke(ctx, obj_id, "assoclist", tmp_bb.head, parse_stas, &priv_data, 1500);
+	if (rv)
+		printf("ubus_invoke error code %d\n", rv);
+
+	blob_buf_free(&tmp_bb);
+fail:
+	return rv;
+}
+
+void blob_add_by_port(struct device_context *dc, struct bridge *b, struct blob_buf *bb, struct port *p)
+{
+	void *clients, *port_table;
+	char ifname[16] = {0};
+	bool wireless;
+	char uplink_port[IF_NAMESIZE] = {0};
+
+	printf("%s %d %d\n", __func__, __LINE__, p->ifindex);
+
+	if (!if_indextoname(p->ifindex, ifname))
+		goto fail;
+
+	wireless = is_wifi(ifname);
+	printf("%d\n", __LINE__);
+	port_table = blobmsg_open_table(bb, "port");
+	printf("%d\n", __LINE__);
+	blobmsg_add_string(bb, "port_name", ifname);
+	printf("%s %d %s\n", __func__, __LINE__, ifname);
+
+	blobmsg_add_string(bb, "type", (wireless ? "wireless" : "ethernet"));
+	blobmsg_add_u16(bb, "port_no", p->index);
+
+	printf("%d\n", __LINE__);
+
+	clients = blobmsg_open_array(bb, "clients");
+	EMPT();
+	if (!get_uplink_port(&dc->bridges, uplink_port))
+		goto fail_uplink;
+	EMPT();
+	if (!wireless || !strncmp(ifname, uplink_port, IFNAMSIZ)) {
+		EMPT();
+		blob_add_clients(b, bb, p->ifindex);
+	} else {
+		EMPT();
+		get_wl_stas(dc->ctx, b, ifname, bb);
+	}
+
+	EMPT();
+fail_uplink:
+	blobmsg_close_array(bb, clients);
+	blobmsg_close_table(bb, port_table);
+fail:
+	return;
+}
+
+static int get_brtables(struct device_context *dc, struct blob_buf *bb, struct bridge *bridge)
+{
+	void *conn_type;
+	char uplink_port[IF_NAMESIZE];
+	int rv = -1, i, idx;
+	struct port *p, *up = NULL;
+	char ifname[IFNAMSIZ];
+
+	tp_dbg("Preparing brtables llllllllllllllllllll\n");
+	if (!get_uplink_port(&dc->bridges, uplink_port))
+		goto fail;
+
+	idx = if_nametoindex(uplink_port);
+
+	tp_dbg("uplink %s found under bridge %s\n", uplink_port, bridge->ifname);
+	if (!dc->root) {
+
+		conn_type = blobmsg_open_array(bb, "up");
+		/* WAY FUTURE TODO: extenders can have multiple bridges too */
+		up = &bridge->ports[if_nametoindex(uplink_port)];
+		if (up->index == 0)
+			goto fail;
+
+		tp_dbg("adding port %d, ifindex %d, ifname %d\n", up->index, up->ifindex, if_nametoindex(uplink_port));
+
+		blob_add_by_port(dc, bridge, bb, up);
+
+		blobmsg_close_array(bb, conn_type);
+	}
+
+	conn_type = blobmsg_open_array(bb, "down");
+
+	for (i = 0; i < 256; i++) {
+		p = &bridge->ports[i];
+		if (p->index == 0 || p->ifindex == 0)
+			continue;
+		tp_dbg("%d %d %s\n", idx, p->ifindex, if_indextoname(p->ifindex, ifname));
+		if (idx == p->ifindex)
+			continue;
+
+		blob_add_by_port(dc, bridge, bb, p);
+	}
+
+
+	blobmsg_close_array(bb, conn_type);
+	rv = 0;
+	EMPT();
+fail:
+	return rv;
+}
+
+static char *get_local_ip(char *bridge)
+{
+	int fd, rv;
+	struct ifreq ifr;
+
+	printf("%s %d %s\n", __func__, __LINE__, bridge);
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+	ifr.ifr_addr.sa_family = AF_INET;
+
+	strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+
+	rv = ioctl(fd, SIOCGIFADDR, &ifr);
+	if (rv < 0)
+		goto fail_ioctl;
+
+	close(fd);
+	return inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
+fail_ioctl:
+	close(fd);
+	return NULL;
+}
+
+/* TODO: move to utils */
+char *get_local_mac(const char *bridge, char *mac)
+{
+	int fd, rv;
+	struct ifreq ifr;
+	unsigned char *buffer;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+	ifr.ifr_addr.sa_family = AF_INET;
+
+	strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+
+	rv = ioctl(fd, SIOCGIFHWADDR, &ifr);
+	if (rv < 0)
+		goto fail_ioctl;
+
+	buffer = (unsigned char *)ifr.ifr_hwaddr.sa_data;
+	snprintf(mac, MAC_MAX_LEN, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
+
+	close(fd);
+	return mac;
+fail_ioctl:
+	close(fd);
+	return NULL;
+}
+
+static int dump_info(struct ubus_context *ctx, struct ubus_object *obj,
+					 struct ubus_request_data *req, const char *method,
+					 struct blob_attr *msg)
+{
+	struct blob_buf bb = {0};
+	int rv;
+	struct device_context *dc = container_of(obj, struct device_context, topology_object);
+
+	clean_all_tables();
+	dc->len = generate_neightable(&dc->bridges);
+	get_neightable(dc);
+	blob_buf_init(&bb, 0);
+	rv = prepare_dump_blob(dc, &bb);
+	if (rv < 0) {
+		printf("%d\n", __LINE__);
+		blob_buf_free(&bb);
+		goto fail;
+	}
+	printf("%d\n", __LINE__);
+	ubus_send_reply(ctx, req, bb.head);
+	printf("%d\n", __LINE__);
+	blob_buf_free(&bb);
+fail:
+	printf("%d\n", __LINE__);
+	return 0;
+}
+
+int prepare_dump_blob(struct device_context *dc, struct blob_buf *bb)
+{
+	char bridge[16] = {0}, local_mac[MAC_MAX_LEN] = {0};
+	char *ip;
+	int rv;
+	struct bridge *b;
+	void *table;
+
+	list_for_each_entry(b, &dc->bridges, list) {
+		if (!if_indextoname(b->ifindex, bridge))
+			goto fail;
+
+		ip = get_local_ip(bridge);
+		tp_dbg("preparing dump for bridge %s\n", bridge);
+
+		if (!get_local_mac(bridge, local_mac))
+			goto fail;
+
+		table = blobmsg_open_table(bb, b->ifname);
+		blobmsg_add_string(bb, "macaddr", local_mac);
+		if (ip)
+			blobmsg_add_string(bb, "ipaddr", ip);
+
+		rv = get_brtables(dc, bb, b);
+		if (rv < 0)
+			goto fail;
+		blobmsg_close_table(bb, table);
+	}
+
+	return 0;
+fail:
+	return -1;
+}
+
+struct vertex *create_vertex(const char *mac_addr)
+{
+	struct vertex *v;
+
+	v = calloc(1, sizeof(*v));
+	if (!v)
+		goto fail_vertex;
+
+	v->age = -1;
+	strncpy(v->mac_addr, mac_addr, MAC_MAX_LEN);
+	tp_dbg("mem_leak_dbg ------ created %s", mac_addr);
+	printf("%s %d vertex was created for %s!\n", __func__, __LINE__, v->mac_addr);
+	return v;
+fail_vertex:
+	return NULL;
+}
+
+static void parse_uplink(struct json_object *obj, struct vertex *v)
+{
+	struct json_object *up, *port_entry;
+	const char *uplink_port;
+	int port_no, i;
+
+	up = json_get_array(obj, "up");
+	if (!up)
+		return;
+
+	for (i = 0; i < json_object_array_length(up); i++) {
+		port_entry = json_object_array_get_idx(up, i);
+
+		port_no = json_get_int(port_entry, "port_no");
+		v->port_no = port_no;
+
+		printf("%s\n", json_object_get_string(port_entry));
+		uplink_port = json_get_string(port_entry, "port_name");
+		if (!uplink_port)
+			continue;
+
+		strncpy(v->uplink_port, uplink_port, IFNAMSIZ);
+	}
+}
+
+static int update_vertex(struct vertex *v, double age, const char *ip, bool local)
+{
+	if (age >= v->age && v->age > 0)
+		return -1;
+
+	tp_dbg("replaced ageing timer of %s with %lf, %d %s\n", v->mac_addr, age, local, ip);
+	v->age = age;
+	if (local)
+		strncpy(v->ip_addr, ip, IPV4_MAX_LEN);
+
+	return 0;
+}
+
+static int parse_downlink(struct device_context *dc, struct json_object *obj,
+		struct vertex *v, struct edge **br_head, bool local)
+{
+	struct json_object *down, *port_entry, *clients, *elem;
+	const char *c_mac, *c_type, *c_ip, *c_port_name; /* client information */
+	struct edge *br_entry;
+	struct vertex *child;
+	struct vertex_table *t;
+	double age;
+	int i, j, rv;
+
+	down = json_get_array(obj, "down");
+	if (!down)
+		return -1;
+
+	for (i = 0; i < json_object_array_length(down); i++) {
+		port_entry = json_object_array_get_idx(down, i);
+
+		clients = json_get_array(port_entry, "clients");
+		if (!clients)
+			continue;
+		for (j = 0; j < json_object_array_length(clients); j++) {
+			elem = json_object_array_get_idx(clients, j);
+			tp_dbg("%s has an edge to %s\n", v->mac_addr, json_object_get_string(elem));
+			c_mac = json_get_string(elem, "macaddr");
+			if (!c_mac)
+				continue;
+
+			c_type = json_get_string(port_entry, "type");
+			if (!c_type)
+				continue;
+
+			c_port_name = json_get_string(port_entry, "port_name");
+			if (!c_port_name)
+				continue;
+
+			c_ip = json_get_string(elem, "ipaddr");
+			if (!c_ip)
+				continue;
+
+			br_entry = create_node(c_mac, c_type, c_port_name, json_get_int(port_entry, "port_no"));
+			if (!br_entry)
+				continue;
+
+			age = json_get_double(elem, "ageing_timer");
+			br_entry->age = age;
+			tp_dbg("%s age set to %lf in %s edges", br_entry->mac_addr, age, v->mac_addr);
+
+			if (enqueue(br_head, br_entry))
+				delete_edge(br_entry);
+
+			t = lookup(c_mac);
+			if (t && t->v) {
+				rv = update_vertex(t->v, age, c_ip, local);
+				if (!rv)
+					t->v->br_ifindex = v->br_ifindex;
+				if (!get_br_data(t, v->br_ifindex))
+					create_br_data(t, v->br_ifindex);
+				continue;
+			}
+
+			child = create_vertex(c_mac);
+			if (!child)
+				continue;
+
+			printf("%s %d created vertex for %s\n", __func__, __LINE__, child->mac_addr);
+			child->endpoint = true;
+			list_add(&child->list, &dc->endpoints);
+
+			update_vertex(child, age, c_ip, local);
+
+			child->br_ifindex = v->br_ifindex;
+
+			t = insert(dc, child, NULL);
+		}
+	}
+
+	return 0;
+}
+
+static void parse_dump(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	struct device_context *dc = (struct device_context *)req->priv;
+	char *json_str;
+	const char *mac, *ip;
+	struct json_object *json_msg;
+	struct vertex *v;
+	struct vertex_table *t;
+	struct edge *br_head = NULL;
+	bool local = !req->ctx; /* whether this was a local bridge */
+	int rv;
+
+	tp_dbg("%d local", local);
+
+	json_str = blobmsg_format_json(msg, true);
+	if (!json_str)
+		return;
+
+	json_msg = json_tokener_parse(json_str);
+	if (!json_msg)
+		goto out_str;
+
+	if (!json_object_is_type(json_msg, json_type_object))
+		goto out_json;
+
+	json_object_object_foreach(json_msg, key, val) {
+		br_head = NULL;
+		tp_dbg("------ bridge %s -------", key);
+
+		mac = json_get_string(val, "macaddr");
+		if (!mac)
+			continue;
+		printf("%s %d create node for %s\n", __func__, __LINE__, mac);
+		t = lookup(mac);
+		if (t && t->v) {
+			tp_dbg("%s %d %s\n", __func__, __LINE__, mac);
+			v = t->v;
+			/* v was previously found as endpoint, but is in fact a vertex */
+			list_del(&t->v->list);
+		} else {
+			v = create_vertex(mac);
+			if (!v)
+				continue;
+			v->br_ifindex = if_nametoindex(key);
+			tp_dbg("br_ifindex set to %d for %s, interface %s\n", v->br_ifindex, mac, key);
+		}
+
+		ip = json_get_string(val, "ipaddr");
+		if (ip)
+			strncpy(v->ip_addr, ip, IPV4_MAX_LEN);
+
+		parse_uplink(val, v);
+
+		v->endpoint = false;
+		list_add(&v->list, &dc->vertexes);
+
+		rv = parse_downlink(dc, val, v, &br_head, local);
+		if (rv)
+			continue;
+
+		if (local) {
+			/* Workaround because bridge macs are not unique */
+			struct bridge *b = get_bridge_by_name(&dc->bridges, key);
+			if (!b) { // Shouldn't end up needing this, but incase
+				insert(dc, v, br_head);
+				continue;
+			}
+			v->bridge_entry = true;
+			t = create_entry(dc, v, br_head);
+			if (!t)
+				continue;
+			tp_dbg("---------------- bridge %s ----------------\n", b->ifname);
+			tp_dbg("---------------- root %s ----------------\n", t->mac_addr);
+			tp_dbg("---------------- table ----------------\n");
+			print_list(br_head);
+			tp_dbg("---------------------------------------\n");
+			b->root = t;
+		} else {
+			/* The standard way of doing it */
+			t = insert(dc, v, br_head);
+			if (!t)
+				continue;
+
+			/* we got a response so obviously not stale - unstale if it is */
+			if (t->stale) {
+				t->stale = false;
+				list_del(&t->list);
+			}
+		}
+	}
+
+	EMPT();
+	tp_dbg("%s", v->mac_addr);
+	tp_dbg("%s %d", t->v->mac_addr, t->stale);
+out_json:
+	json_object_put(json_msg);
+out_str:
+	free(json_str);
+}
+
+static int invoke_client(struct device_context *dc, const char *client)
+{
+	struct blob_buf buf = {0};
+	int rv;
+	uint32_t obj_id;
+
+	printf("%s %d client %s\n", __func__, __LINE__, client);
+	ubus_lookup_id(dc->ctx, client, &obj_id);
+	printf("%d\n", __LINE__);
+	if (!obj_id) {
+		printf("%d\n", __LINE__);
+		printf("%s: ubus_lookup_id error\n", __func__);
+		goto fail;
+	}
+	printf("%d\n", __LINE__);
+	printf("obj_id %u\n", obj_id);
+
+	rv = blob_buf_init(&buf, 0);
+	if (rv)	{
+		printf("blob_buf_init error\n");
+		goto fail;
+	}
+
+	rv = ubus_invoke(dc->ctx, obj_id, "dump_info", buf.head, parse_dump, dc, 1250);
+	if (rv)
+		printf("ubus_invoke error code %d\n", rv);
+
+	blob_buf_free(&buf);
+fail:
+	return rv;
+}
+
+void delete_vertex(struct vertex *v)
+{
+	tp_dbg("%s %d deleting mac addr %s\n", __func__, __LINE__, v->mac_addr);
+	if (v->bridge_entry) {
+		tp_dbg("mem_leak_dbg ------ deleting %s with a hard", v->mac_addr);
+		tp_dbg("%s was a root entry, cleaning its vertex_table entry\n");
+		clean_entry(v->vt, v->br_ifindex);
+	} else {
+		tp_dbg("mem_leak_dbg ------ deleting %s with a soft", v->mac_addr);
+		tp_dbg("soft cleaning %s", v->mac_addr);
+		soft_clean_entry(v->vt);
+	}
+
+	free(v);
+}
+
+void delete_vertex_list(struct list_head *list)
+{
+	struct vertex *v, *tmp;
+
+	tp_dbg("mem_leak_dbg --------- Deleting all vertexes ---------");
+	tp_dbg("mem_leak_dbg -------------------------------------------------");
+
+	list_for_each_entry_safe(v, tmp, list, list) {
+		list_del(&v->list);
+		delete_vertex(v);
+	}
+
+	tp_dbg("mem_leak_dbg -------------------------------------------------");
+}
+
+void dispatch_notify(struct uloop_timeout *t)
+{
+	struct device_context *dc = container_of(t, struct device_context, dispatch_scheduler);
+	struct blob_buf bb = {0};
+
+	dc->dispatch_counter = 0;
+	blob_buf_init(&bb, 0);
+	ubus_send_event(dc->ctx, "topology", bb.head);
+	blob_buf_free(&bb);
+}
+
+void schedule_update_notify(struct device_context *dc, int timeout)
+{
+	if (dc->dispatch_counter < 3) {
+		dc->dispatch_counter++;
+		uloop_timeout_set(&dc->dispatch_scheduler, timeout * dc->dispatch_counter);
+		printf("%s %d dispatch coming through in %dms\n", __func__, __LINE__, timeout);
+	}
+}
+
+int update_event(struct device_context *dc)
+{
+	printf("%d sending an event to ubus\n", __LINE__);
+
+	schedule_update_notify(dc, 250);
+	//dispatch_notify(&dc->dispatch_scheduler);
+	return 0;
+}
+
+int network_event(struct device_context *dc, struct blob_buf *bb, char *event, char *port)
+{
+	const char *port_name;
+	char br_ifname[IFNAMSIZ] = {0},
+			ifname[IFNAMSIZ] = {0},
+			local_mac[MAC_MAX_LEN] = {0};
+	struct port *p;
+	int ifindex;
+
+	tp_dbg("%s %s", event, port);
+
+	if (isdigits(port)) {
+		//p = get_port_by_no(atoi(port));
+		port_name = portno_to_ifname(atoi(port), ifname);
+		tp_dbg("%s", ifname);
+		ifindex = if_nametoindex(port_name);
+	} else {
+		port_name = port;
+		ifindex = if_nametoindex(port_name);
+		tp_dbg("%d", ifindex);
+	}
+	tp_dbg();
+	p = get_port_by_idx(&dc->bridges, ifindex);
+	if (!p)
+		goto fail;
+
+	tp_dbg();
+	if (!if_indextoname(p->br_ifindex, br_ifname))
+		goto fail;
+	tp_dbg();
+	if (!get_local_mac(br_ifname, local_mac))
+		goto fail;
+
+	tp_dbg("%s %s %s\n", local_mac, event, port_name);
+	blobmsg_add_string(bb, "source", local_mac);
+	blobmsg_add_string(bb, "event", event);
+	blobmsg_add_string(bb, "port", port_name);
+	ubus_send_event(dc->ctx, "topology", bb->head);
+
+	tp_dbg();
+	return 0;
+fail:
+	return -1;
+}
+
+static void uobjx_lookup(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
+{
+	struct device_context *dc = (struct device_context *) priv;
+	const char *token = "topology";
+	char *p; //*mac
+	char ip[128]; //ifname[IFNAMSIZ] = {0}
+	//struct vertex_table *t;
+
+	p = strstr(obj->path, token);
+	if (p) {
+		printf("%s %d %s\n", __func__, __LINE__, obj->path);
+		memset(ip, 0, sizeof(ip));
+		snprintf(ip, p - obj->path, "%s", obj->path);
+		if (!strlen(ip))
+			goto fail;
+
+		printf("ip prefix =  %s\n", ip);
+		/*
+		mac = ip_to_mac(ip); // trick to help us prevent timeouts?
+		if (!mac)
+			goto fail;
+		printf("%s %d mac to find an entry for %s\n", __func__, __LINE__, mac);
+		t = lookup(mac);
+		if (t) {
+			printf("%d\n", __LINE__);
+			if (t->stale) {
+				printf("%s %d %s has gone stale!\n", __func__, __LINE__, ip);
+				if_indextoname((list_first_entry(&device_ctx->bridges, struct bridge, list)[0]).ifindex, ifname);
+				if (!arping(ip, ifname, 1000)) {
+					printf("%s %d arping 0 %s %s is indeed stale!\n", __func__, __LINE__, t->mac_addr, ip);
+					goto fail;
+				}
+				t->stale = false;
+				list_del(&t->list);
+			}
+		}
+		*/
+		tp_dbg();
+		/*
+		if_indextoname((list_first_entry(&device_ctx->bridges, struct bridge, list)[0]).ifindex, ifname);
+		if (!arping(ip, ifname, 1250)) { // one way to prevent timeouts...
+			printf("%s %d arping 0 %s %s is indeed stale!\n", __func__, __LINE__, ifname, ip);
+			goto fail;
+		}
+		*/
+		invoke_client(dc, obj->path);
+		tp_dbg();
+
+		register_child_listener(dc, ip);
+	}
+
+fail:
+	return;
+}
+
+static int get_nodes(struct device_context *dc)
+{
+	ubus_lookup(dc->ctx, NULL, uobjx_lookup, dc);
+
+	return 0;
+}
+
+void poll_clients(struct device_context *dc)
+{
+	struct blob_buf bb = {0};
+	int rv;
+	struct vertex_table *t;
+
+	tp_dbg("initating poll clients\n");
+
+	list_for_each_entry(t, &dc->stale_nodes, list)
+		tp_dbg("currently noted stale node: %s\n", t->mac_addr);
+
+	dc->root_entry = false;
+	if (!list_empty(&dc->vertexes)) {
+		tp_dbg("removing all vertexes\n");
+		delete_vertex_list(&dc->vertexes);
+	}
+	if (!list_empty(&dc->endpoints)) {
+		tp_dbg("removing all endpoints\n");
+		delete_vertex_list(&dc->endpoints);
+	}
+
+	clean_all_tables();
+	dc->len = generate_neightable(&dc->bridges);
+	get_neightable(dc);
+
+	blob_buf_init(&bb, 0);
+	tp_dbg();
+	rv = prepare_dump_blob(dc, &bb);
+	if (rv < 0) {
+		blob_buf_free(&bb);
+		tp_dbg("failed at dump_blob!\n");
+		goto fail_blob;
+	}
+	tp_dbg();
+
+	struct ubus_request req = {
+		.priv = dc
+	};
+	parse_dump(&req, 0, bb.head);
+	tp_dbg();
+
+	get_nodes(dc);
+
+	blob_buf_free(&bb);
+	update_event(dc);
+fail_blob:
+	return;
+}
+
+static int gather_info(struct ubus_context *ctx, struct ubus_object *obj,
+					   struct ubus_request_data *req, const char *method,
+					   struct blob_attr *msg)
+{
+	struct device_context *dc = container_of(obj, struct device_context, topology_object);
+
+	poll_stale_nodes(&dc->stale_scheduler);
+	//poll_clients();
+	//network_event();
+	update_event(dc);
+	return 0;
+}
+
+// calculate depth of each node
+static int first_pass(struct device_context *dc)
+{
+	struct vertex_table *t, *child;
+	struct vertex *v;
+	struct edge *e;
+
+	printf("%d initating first pass\n", __LINE__);
+
+	if (list_empty(&dc->vertexes))
+		goto fail;
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		printf("%s %d incrementing %s brtable next\n", __func__, __LINE__, v->mac_addr);
+		/*t = lookup(v->mac_addr);
+		if (!t) {
+			printf("%d no hash table found\n", __LINE__);
+			continue;
+		}*/
+		if (!v->vt)
+			continue;
+		t = v->vt;
+		tp_dbg();
+		for (e = t->bridge_head; e; e = e->next) {
+			tp_dbg("%s %d incrementing depth of %s\n", __func__, __LINE__, e->mac_addr);
+			child = lookup(e->mac_addr);
+			if (!child)
+				continue;
+
+			if (child->stale)
+				continue;
+
+			tp_dbg();
+			child->v->depth++;
+		}
+	}
+
+	return 0;
+fail:
+	return -1;
+}
+
+double get_age(struct vertex_table *parent, char *child_mac)
+{
+	struct edge *e;
+
+	for (e = parent->bridge_head; e; e = e->next) {
+		if (strncmp(child_mac, e->mac_addr, MAC_MAX_LEN))
+			continue;
+
+		return e->age;
+	}
+
+	return -1;
+}
+
+// find parent of each node
+static int second_pass(struct device_context *dc)
+{
+	struct vertex_table *t, *child;
+	struct vertex *v;
+	struct edge *e;
+	double age;
+
+	tp_dbg("---------------- initating second pass ----------------\n");
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		tp_dbg("------------ checking bridge table of %s ------------\n", v->mac_addr);
+
+		t = v->vt;
+		if (!t)
+			continue;
+
+		for (e = t->bridge_head; e; e = e->next) {
+
+			tp_dbg("--- %s found in the bridge table of %s under port %s ---", e->mac_addr, v->mac_addr, e->downlink_port);
+			child = lookup(e->mac_addr);
+			if (!child || child->stale || !child->v)
+				continue;
+
+			if (!child->parent) {
+				if ((e->age > child->v->age + AGEING_MARGIN) && strncmp(e->downlink_type, "ethernet", strlen("ethernet") + 1) == 0) { // adding +5 fixes the corner case of broadcom though...
+					tp_dbg("parent %s is too old to be set to %s parent, e age %lf v age %lf", v->mac_addr, child->mac_addr, e->age, child->v->age);
+					continue;
+				}
+				tp_dbg("parent of %s is set to %s %lf %lf", child->mac_addr, v->mac_addr, e->age, child->v->age);
+				child->parent = v;
+				strncpy(child->v->uplink_type, e->downlink_type, LINK_TYPE_SIZE);
+				tp_dbg("setting uplink_type of %s to %s", child->v->uplink_type, e->downlink_type);
+				continue;
+			}
+
+			tp_dbg("v->depth: %d, c->p->depth, c->v->depth %d %d", v->depth, child->parent->depth, child->v->depth);
+			age = get_age(child->parent->vt, e->mac_addr);
+			if (age < 0)
+				continue;
+			tp_dbg("%lf %lf %s %s %lf %lf",  e->age, child->v->age, v->mac_addr, e->mac_addr, e->age, age);
+			if (e->age > child->v->age + AGEING_MARGIN && strncmp(e->downlink_type, "ethernet", strlen("ethernet") + 1) == 0)
+				continue;
+
+			if (v->depth < child->parent->depth)
+				continue;
+
+			tp_dbg(" --- parent of %s is updated to --- %s ---\n", child->mac_addr, v->mac_addr);
+			child->parent = v;
+			strncpy(child->v->uplink_type, e->downlink_type, LINK_TYPE_SIZE);
+		}
+	}
+
+	tp_dbg("------------------ finished second pass ------------------\n");
+	return 0;
+}
+
+void update_child_list(struct vertex *v)
+{
+	struct vertex_table *t, *parent;
+	struct edge *e, *br_entry;
+
+	tp_dbg("Adding %s to a child list\n", v->mac_addr);
+	t = v->vt;
+	if (!t) {
+		tp_dbg("no entry found for node %s\n", v->mac_addr);
+		goto fail;
+	}
+
+	print_list(t->bridge_head);
+	if (!t->parent)
+		goto fail;
+
+	tp_dbg();
+	parent = t->parent->vt;
+	if (!parent) {
+		tp_dbg("no parent entry found of node %s\n", t->mac_addr);
+		goto fail;
+	}
+
+	tp_dbg("link found from: %s to %s\n", parent->v->mac_addr, v->mac_addr);
+	print_list(parent->bridge_head);
+	br_entry = search_list(parent->bridge_head, v->mac_addr); // if this could be avoided we'd lower time comp a bit
+	if (!br_entry)
+		goto fail;
+
+	e = copy_edge(br_entry);
+	if (!e)
+		goto fail;
+
+	tp_dbg("%s\n", e->mac_addr);
+
+	if (enqueue(&parent->child_head, e))
+		delete_edge(e);
+
+fail:
+	tp_dbg();
+	return;
+}
+
+struct vertex_table *masquerading(struct device_context *dc, struct vertex *v)
+{
+	struct vertex *endpt;
+	char *mac, tmp_mac[MAC_MAX_LEN] = {0};
+	int first_octet = 0;
+
+	mac = v->mac_addr;
+
+	strncpy(tmp_mac, v->mac_addr, MAC_MAX_LEN);
+	first_octet = atoi(strtok(tmp_mac, "/"));
+	if (!(first_octet & 2))
+		goto fail;
+
+	list_for_each_entry(endpt, &dc->endpoints, list) {
+		if (!strncmp(endpt->mac_addr, v->mac_addr, MAC_MAX_LEN))
+			continue;
+		if (strncmp(v->mac_addr + 9, endpt->mac_addr + 9, 8))
+			continue;
+
+		list_for_each_entry(v, &dc->vertexes, list) {
+			if (strncmp(mac, v->mac_addr, 8))
+				continue;
+
+			return lookup(endpt->mac_addr);
+		}
+	}
+
+fail:
+	return NULL;
+}
+
+// create a direct child list of each node
+void third_pass(struct device_context *dc)
+{
+	struct vertex *v;
+	struct vertex_table *t;
+
+	printf("%d initating third pass\n", __LINE__);
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		DBGP("%s", v->mac_addr);
+		update_child_list(v);
+	}
+
+	list_for_each_entry(v, &dc->endpoints, list) {
+		tp_dbg("%s\n", v->mac_addr);
+		t = lookup(v->mac_addr);
+		if (!t || t->fake_mac)
+			continue;
+
+		/* crude solution for masquerading mac till better one found */
+		t->real = masquerading(dc, v);
+		if (t->real) {
+			t->real->fake = t;
+			t->fake_mac = true;
+			continue;
+		}
+
+		update_child_list(v);
+	}
+
+	printf("%d all passes are complete!\n", __LINE__);
+}
+
+void tree_to_blob(struct blob_buf *bb, struct vertex_table *t, struct edge *parent_edge, struct vertex_table *root)
+{
+	struct device_context *dc = t->ctx;
+	void *connected_devs, *port;
+	struct edge *e;
+	struct vertex_table *child;
+	struct arp_entry *ae;
+	int rssi, speed, rv;
+	//struct eth_link eth = {0};
+
+	//if (!parent_edge)
+
+
+	/* TODO: probably want to include thisin vertex */
+	ae = lookup_arp(t->mac_addr);
+	if(ae && strlen(ae->name))
+		blobmsg_add_string(bb, "name", ae->name);
+
+	if (t && strlen(t->v->ip_addr))
+		blobmsg_add_string(bb, "ipaddr", t->v->ip_addr);
+	blobmsg_add_string(bb, "macaddr", t->v->mac_addr);
+	printf("%d\n", __LINE__);
+
+	 if (parent_edge) {
+		printf("%s %d %s %s\n", __func__, __LINE__, t->mac_addr, t->parent->mac_addr);
+		if (strlen(t->v->uplink_port))
+			blobmsg_add_string(bb, "uplink_port", t->v->uplink_port);
+		blobmsg_add_string(bb, "interface_type", parent_edge->downlink_type);
+		EMPT();
+		if (!strncmp(parent_edge->downlink_type, "wireless", 16)) {
+			EMPT();
+			if (t->parent == root->v) // ugly, better way? move check to utils?
+				rv = get_rssi(dc->ctx, parent_edge->downlink_port, NULL, t->mac_addr, &rssi);
+			else
+				rv = get_rssi(dc->ctx, parent_edge->downlink_port, t->parent->ip_addr, t->mac_addr, &rssi);
+			if (!rv)
+				blobmsg_add_u32(bb, "rssi", rssi);
+			EMPT();
+			if (t->parent == root->v) // ugly, better way? move check to utils?
+				rv = get_speed(dc->ctx, parent_edge->downlink_port, NULL, &speed);
+			else
+				rv = get_speed(dc->ctx, parent_edge->downlink_port, t->parent->ip_addr, &speed);
+			if (!rv)
+				blobmsg_add_u32(bb, "speed", speed);
+		}
+		EMPT();
+		/*
+		else if (!strncmp(parent_edge->downlink_type, "ethernet", 16)) {
+			link.port = if_nametoindex(e->downlink_port);
+			if (!eth_get_link_settings(e->downlink_port, &link))
+				printf("%s %d %d\n", __func__, __LINE__, link.speed);
+		}
+		*/
+	} else
+		blobmsg_add_string(bb, "device_type", "bridge");
+
+	printf("%d\n", __LINE__);
+
+	if (t->v->endpoint) {
+		blobmsg_add_string(bb, "device_type", "endpoint");
+		goto done;
+	}
+	printf("%d\n", __LINE__);
+
+	if (parent_edge)
+		blobmsg_add_string(bb, "device_type", (!strncmp(parent_edge->downlink_type, "wireless", 16) ? "repeater" : "extender"));
+	connected_devs = blobmsg_open_table(bb, "connected_devices");
+	printf("%d\n", __LINE__);
+	DBGP("%s", t->v->mac_addr);
+	for (e = t->child_head; e; e = e->next) {
+		printf("%s %d %s\n", __func__, __LINE__, e->mac_addr);
+		child = lookup(e->mac_addr);
+		if (!child)
+			continue;
+		printf("%d\n", __LINE__);
+		port = blobmsg_open_table(bb, e->downlink_port);
+
+		tree_to_blob(bb, child, e, root);
+		printf("%d\n", __LINE__);
+		blobmsg_close_table(bb, port);
+	}
+	blobmsg_close_table(bb, connected_devs);
+
+done:
+	return;
+}
+
+void print_tree(struct device_context *dc, struct ubus_context *ctx, struct ubus_request_data *req)
+{
+	struct blob_buf bb = {0};
+	void *table;
+	struct bridge *b;
+
+	if (list_empty(&dc->bridges))
+		return;
+
+	printf("%d\n", __LINE__);
+	blob_buf_init(&bb, 0);
+	printf("%d\n", __LINE__);
+
+	list_for_each_entry(b, &dc->bridges, list) {
+		if (!b->root) {
+			DBGP("%s has no root", b->ifname);
+			continue;
+		}
+		table = blobmsg_open_table(&bb, b->ifname);
+		DBGP("first root name %s, mac %s, ip %s", b->ifname, b->root->mac_addr, b->root->v->ip_addr);
+		tree_to_blob(&bb, b->root, NULL, b->root);
+		blobmsg_close_table(&bb, table);
+	}
+	printf("%d\n", __LINE__);
+	ubus_send_reply(ctx, req, bb.head);
+	printf("%d\n", __LINE__);
+	blob_buf_free(&bb);
+	printf("%d\n", __LINE__);
+}
+
+// could probably be generalized into one func and then use wrapper to call it from each list
+static int edges_to_blob(struct device_context *dc, struct blob_buf *bb)
+{
+	void *container, *inner_container, *addr_container;
+	struct edge *e;
+	struct vertex_table *t, *child;
+	struct vertex *v;
+	char node_type[32];
+
+	container = blobmsg_open_array(bb, "nodes");
+	list_for_each_entry(v, &dc->vertexes, list) {
+		t = v->vt;
+		if (!t)
+			continue;
+		printf("%d\n", __LINE__);
+
+		if (t->stale)
+			continue;
+
+		inner_container = blobmsg_open_table(bb, "node");
+		blobmsg_add_string(bb, "macaddr", v->mac_addr);
+		printf("%d\n", __LINE__);
+		blobmsg_add_string(bb, "ipaddr", v->ip_addr);
+		blobmsg_add_u16(bb, "depth", v->depth);
+		printf("%s %d %s\n", __func__, __LINE__, v->uplink_type);
+		if (strlen(v->uplink_type)) {
+			snprintf(node_type, 31, "%s", (!strncmp(v->uplink_type, "ethernet", 32) ? "extender" : "repeater"));
+			blobmsg_add_string(bb, "node_type", node_type);
+			blobmsg_add_string(bb, "connection_type", v->uplink_type);
+		} else
+			blobmsg_add_string(bb, "node_type", "root");
+		printf("%d\n", __LINE__);
+		blobmsg_close_table(bb, inner_container);
+	}
+
+	list_for_each_entry(v, &dc->endpoints, list) {
+		t = v->vt;
+		if (!t)
+			continue;
+		printf("%d\n", __LINE__);
+
+		if (t->stale)
+			continue;
+
+		inner_container = blobmsg_open_table(bb, "node");
+		blobmsg_add_string(bb, "macaddr", v->mac_addr);
+		printf("%s %d %s\n", __func__, __LINE__, v->mac_addr);
+		if (!strlen(v->ip_addr))
+			blobmsg_add_string(bb, "ipaddr", "");
+		blobmsg_add_string(bb, "ipaddr", v->ip_addr);
+		blobmsg_add_u16(bb, "depth", v->depth);
+		blobmsg_add_string(bb, "node_type", "endpoint");
+		blobmsg_add_string(bb, "connection_type", v->uplink_type);
+		printf("%d\n", __LINE__);
+		blobmsg_close_table(bb, inner_container);
+	}
+	blobmsg_close_array(bb, container);
+
+	printf("%d\n", __LINE__);
+	container = blobmsg_open_array(bb, "edges");
+	list_for_each_entry(v, &dc->vertexes, list) {
+		t = v->vt;//lookup(v->mac_addr);
+		if (!t)
+			continue;
+		printf("%d\n", __LINE__);
+
+		if (t->stale)
+			continue;
+		printf("%d\n", __LINE__);
+		for (e = t->child_head; e; e = e->next) {
+			printf("%d\n", __LINE__);
+			printf("%s %d %s %s\n", __func__, __LINE__, e->mac_addr, e->downlink_type);
+			child = lookup(e->mac_addr);
+			if (!child)
+				continue;
+
+			inner_container = blobmsg_open_table(bb, "edge");
+
+			addr_container = blobmsg_open_table(bb, "from");
+			blobmsg_add_string(bb, "macaddr", v->mac_addr);
+			blobmsg_add_string(bb, "ipaddr", v->ip_addr);
+			blobmsg_add_string(bb, "port", e->downlink_port);
+			blobmsg_add_u16(bb, "port_no", e->downlink_port_no);
+			blobmsg_close_table(bb, addr_container);
+
+			addr_container = blobmsg_open_table(bb, "to");
+			blobmsg_add_string(bb, "macaddr", child->v->mac_addr);
+			blobmsg_add_string(bb, "ipaddr", child->v->ip_addr);
+			if (strlen(child->v->uplink_port)) {
+				blobmsg_add_string(bb, "port", child->v->uplink_port);
+				blobmsg_add_u16(bb, "port_no", child->v->port_no);
+			}
+			blobmsg_close_table(bb, addr_container);
+
+			blobmsg_add_string(bb, "type", e->downlink_type);
+			blobmsg_close_table(bb, inner_container);
+		}
+	}
+	printf("%d\n", __LINE__);
+
+	blobmsg_close_array(bb, container);
+	return 0;
+}
+
+static int tree(struct ubus_context *ctx, struct ubus_object *obj,
+					   struct ubus_request_data *req, const char *method,
+					   struct blob_attr *msg)
+{
+	struct device_context *dc = container_of(obj, struct device_context, topology_object);
+	struct blob_buf bb = {0};
+	int rv = 0;
+
+	printf("%d\n", __LINE__);
+	if (!dc->root_entry)
+		goto fail_root;
+
+	blob_buf_init(&bb, 0);
+
+	rv = edges_to_blob(dc, &bb);
+	if (rv < 0)
+		goto fail_edges;
+
+	ubus_send_reply(ctx, req, bb.head);
+
+fail_edges:
+	blob_buf_free(&bb);
+fail_root:
+	return rv;
+}
+
+void poll_tree(struct device_context *dc)
+{
+	struct vertex_table *t;
+	struct vertex *v;
+	int rv;
+	struct blob_buf bb = {0};
+
+	printf("%d\n", __LINE__);
+	if (dc->root_entry)
+		goto print;
+
+	rv = first_pass(dc);
+	if (rv < 0)
+		goto fail;
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		tp_dbg("%s", v->mac_addr);
+		t = v->vt;//lookup(v->mac_addr);
+		if (!t) {
+			memset(&bb, 0, sizeof(struct blob_buf));
+			blob_buf_init(&bb, 0);
+			blobmsg_add_string(&bb, "event", "SOMETHING THAT NEVER SHOULD HAPPEND HAPPENED! 1111");
+			blobmsg_add_string(&bb, "for", v->mac_addr);
+			ubus_send_event(dc->ctx, "topology", bb.head);
+			blob_buf_free(&bb);
+			continue; // dont know how or why this would ever happen? seems like it does though?
+		}
+		printf("%s %d %s\n", __func__, __LINE__, t->mac_addr);
+		printf("%s %d %s\n", __func__, __LINE__, t->v->mac_addr);
+		printf("%s %d mac - %s, depth - %d\n", __func__, __LINE__, v->mac_addr, t->v->depth);
+	}
+	list_for_each_entry(v, &dc->endpoints, list) {
+		tp_dbg("%s", v->mac_addr);
+		t = v->vt;//lookup(v->mac_addr);
+		if (!t) {
+			tp_dbg();
+			memset(&bb, 0, sizeof(struct blob_buf));
+			blob_buf_init(&bb, 0);
+			blobmsg_add_string(&bb, "event", "SOMETHING THAT NEVER SHOULD HAPPEND HAPPENED! 2222");
+			blobmsg_add_string(&bb, "for", v->mac_addr);
+			ubus_send_event(dc->ctx, "topology", bb.head);
+			blob_buf_free(&bb);
+			continue; // dont know how or why this would ever happen? seems like it does though?
+		}
+		tp_dbg("%s", t->mac_addr);
+		tp_dbg("%s", t->v->mac_addr);
+		printf("%s %d mac - %s, depth - %d\n", __func__, __LINE__, v->mac_addr, t->v->depth);
+	}
+	tp_dbg();
+	second_pass(dc);
+	third_pass(dc);
+fail:
+print:
+	return;
+}
+
+int re_link_bridge(struct device_context *dc, struct bridge *b)
+{
+	struct vertex *v;
+
+	list_for_each_entry(v, &dc->vertexes, list) {
+		if (strncmp(b->mac_addr, v->mac_addr, MAC_MAX_LEN))
+			continue;
+
+		if (v->br_ifindex != b->ifindex)
+			continue;
+
+		if (!v->bridge_entry)
+			continue;
+
+		b->root = v->vt;
+		return 0;
+	}
+
+	return -1;
+}
+
+int re_link_bridges(struct device_context* dc)
+{
+	struct bridge *b;
+
+	list_for_each_entry(b, &dc->bridges, list) {
+		re_link_bridge(dc, b);
+	}
+
+	return 0;
+}
+
+static int build_tree(struct ubus_context *ctx, struct ubus_object *obj,
+					   struct ubus_request_data *req, const char *method,
+					   struct blob_attr *msg)
+{
+	struct device_context *dc = container_of(obj, struct device_context, topology_object);
+
+	/*if (!device_ctx->root_entry)
+		poll_tree();*/
+
+	if (!list_first_entry(&dc->bridges, struct bridge, list)->root) {
+		re_link_bridges(dc);
+	}
+
+	print_tree(dc, ctx, req);
+
+	return 0;
+}
+
+void event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg)
+{
+	int i;
+	struct ev_handler {
+		const char *type;
+		void (*handler)(struct device_context *device_ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *data);
+	} evs[] = {
+		{"wifi.sta", wifi_handler},
+		{"ubus.object.add", add_rm_handler},
+		{"ubus.object.remove", add_rm_handler},
+		{"switch", switch_handler}
+	};
+	struct device_context *dc = container_of(ev, struct device_context, ubus_ev);
+
+	for (i = 0; i < sizeof(evs)/sizeof(evs[0]); i++) {
+		if (!strcmp(type, evs[i].type)) {
+			evs[i].handler(dc, ev, type, msg);
+			break;
+		}
+	}
+}
+
+int prep_event_blob(struct device_context *dc, struct blob_buf *bb, char *event, char *port)
+{
+	char *port_name;
+	char ifname[IFNAMSIZ] = {0}, local_mac[MAC_MAX_LEN] = {0};
+	struct port *p;
+	int ifindex;
+
+	if (isdigits(port)) {
+		if (!portno_to_ifname(atoi(port), ifname))
+			goto fail;
+	} else {
+		port_name = port;
+		ifindex = if_nametoindex(port_name);
+		printf("%d", ifindex);
+		p = get_port_by_idx(&dc->bridges, ifindex);
+		if (!if_indextoname(p->br_ifindex, ifname))
+			goto fail;
+	}
+	printf("%s %d %s\n", __func__, __LINE__, port_name);
+
+	if (!get_local_mac(ifname, local_mac))
+		goto fail;
+	printf("%d\n", __LINE__);
+
+	blobmsg_add_string(bb, "source", local_mac);
+	blobmsg_add_string(bb, "event", event);
+	blobmsg_add_string(bb, "port", port_name);
+	printf("%d\n", __LINE__);
+
+	printf("%d\n", __LINE__);
+
+	return 0;
+fail:
+	return -1;
+}
+
+double get_ageing_timer(struct device_context *dc, char *mac, int br_ifindex)
+{
+	char timer[16];
+	struct arp_entry *ae;
+	double time_stamp;
+
+	printf("%d\n", __LINE__);
+
+	clean_all_tables();
+	dc->len = generate_neightable(&dc->bridges);
+	get_neightable(dc);
+	ae = lookup_arp(mac);
+	if (!ae)
+		goto fail;
+
+	/* TODO: review? true? */
+	if (ae->port_no < 0) // the entry was not found in bridge table this time
+		goto fail_old_entry;
+
+	time_stamp = get_bridge_age_arp(ae, br_ifindex);
+
+	return time_stamp;
+fail_old_entry:
+	printf("%s %d entry %s no longer available in bridge tabke\n", __func__, __LINE__, mac);
+fail:
+	return -1;
+}
+
+static int ping_node(struct device_context *dc, char *ip, int br_ifindex)
+{
+	struct bridge *b;
+
+	tp_dbg();
+
+	b = get_bridge_by_idx(&dc->bridges, br_ifindex);
+	if (!b) {
+		tp_dbg("bridge not found for %s\n", ip);
+		return -1;
+	}
+
+	return arping(ip, b->ifname, 1000);
+}
+
+static void poll_stale_nodes(struct uloop_timeout *timeout)
+{
+	struct device_context *dc = container_of(timeout, struct device_context, stale_scheduler);
+	struct vertex_table *t, *tmp;
+	struct timeval tv_end;
+	double ageing_timer, passed_time;
+	struct arp_entry *ae;
+	char *mac;
+	struct bridge *b;
+	char ifname[IFNAMSIZ] = {0};
+	int rv, ifindex;
+
+	printf("%d initaiting poll\n", __LINE__);
+	list_for_each_entry_safe(t, tmp, &dc->stale_nodes, list) {
+		printf("%s %d %s\n", __func__, __LINE__, t->mac_addr);
+		/* if it is a masquerading address we want to make our judgements based
+		upon what we can observe from the root, but apply it to the real entry */
+		if (t->fake)
+			mac = t->fake->mac_addr;
+		else
+			mac = t->mac_addr;
+		/*if (t->fake)
+			ae = lookup_arp(t->fake->mac_addr);
+		else
+			ae = lookup_arp(t->mac_addr);*/
+		ae = lookup_arp(mac);
+		if (!ae)
+			continue;
+		tp_dbg("t->mac: %s, ae->mac: %s, mac %s", t->mac_addr, ae->mac_addr, mac);
+		/*if (t->fake)
+			ageing_timer = get_ageing_timer(t->fake->mac_addr);
+		else
+			ageing_timer = get_ageing_timer(t->mac_addr);*/
+
+
+		tp_dbg("mac %s - port %s", t->mac_addr, t->v->uplink_port);
+		b = get_bridge_by_idx(&dc->bridges, t->v->br_ifindex);
+		if (!b)
+			continue;
+
+		ageing_timer = get_ageing_timer(dc, mac, b->ifindex);
+		if (ageing_timer < 0) { // save ourselves a ping i guess?
+			/* this guy should no longer be in the table.. */
+			/* need to clean both entries if we discover masquerader is missing */
+			clean_entry(t, b->ifindex);
+			if (t->fake)
+				clean_entry_by_mac(t->fake->mac_addr, b->ifindex);
+			continue;
+		}
+
+		gettimeofday(&tv_end, NULL);
+		passed_time = elapsed_time(t->tv_start, tv_end);
+
+		tp_dbg("ageing timer %f, stored ageing_timer %f, elapsed time %f for node %s and a expected ageing timer of %f\n", ageing_timer, t->ageing_timer, passed_time, t->mac_addr, (t->ageing_timer + passed_time));
+		tp_dbg("attempting to ping mac: %s ip: %s %d %s\n", mac, ae->ip_addr, ae->ifindex, ae->ifname);
+
+		// arbitrarily picked 6, however must be larger than 5 due to broadcom delayed packet
+
+		if (ageing_timer > (t->ageing_timer + passed_time - AGEING_PING_MARGIN))  {
+			if (ae->ip_addr)
+				rv = ping_node(dc, ae->ip_addr, t->v->br_ifindex);
+				tp_dbg("result from arping %d -- mac:%s - ip: %s - ifname: %s", rv, mac, ae->ip_addr, ifname);
+				if (rv != 1)
+					continue;
+			else {
+				tp_dbg("No IP for node %s", ae->mac_addr);
+				continue;
+			}
+		}
+
+		tp_dbg();
+		t->stale = false;
+		tp_dbg("attempting to remove %s from stale list\n", t->mac_addr);
+		list_del(&t->list);
+		tp_dbg();
+		tp_dbg("unstaling %s\n", t->mac_addr);
+	}
+
+	if (dc->root) {
+		printf("%d POLL CAUSED BY: heartbeat\n", __LINE__);
+		schedule_stale_poll(dc, 60000);
+		poll_clients(dc);
+		poll_tree(dc);
+	}
+	tp_dbg("%d %s!\n", list_empty(&dc->stale_nodes), list_empty(&dc->stale_nodes) ? "no stale nodes remaining!" : "some stale nodes still here!");
+}
+
+enum {
+	DEV_MAC,
+	SRC,
+	EVENT,
+	PORT_NAME,
+	__TOPOLOGY_MAX
+};
+
+const struct blobmsg_policy topology_policy[__TOPOLOGY_MAX] = {
+	[DEV_MAC] = {.name = "dev_mac", .type = BLOBMSG_TYPE_STRING},
+	[SRC] = {.name = "source", .type = BLOBMSG_TYPE_STRING},
+	[EVENT] = {.name = "event", .type = BLOBMSG_TYPE_STRING},
+	[PORT_NAME] = {.name = "port", .type = BLOBMSG_TYPE_STRING}
+};
+
+void schedule_stale_poll(struct device_context *dc, int timeout)
+{
+	tp_dbg("poll coming through in %dms\n", timeout);
+	dc->stale_scheduler.cb = poll_stale_nodes;
+	tp_dbg();
+	uloop_timeout_set(&dc->stale_scheduler, timeout);
+	tp_dbg();
+}
+
+/*	optional parameter dev_mac if only want to stale one particular node,
+	optional parameter port if only want to stale nodes under one port */
+void mark_stale_children(struct vertex_table *t, char *dev_mac, char *port)
+{
+	struct edge *e;
+	struct vertex_table *child;
+	struct bridge *b;
+
+	tp_dbg("%s", t->mac_addr);
+	print_list(t->bridge_head);
+	// If the event noted a port going down
+
+	b = get_bridge_by_idx(&t->ctx->bridges, t->v->br_ifindex);
+	if (!b)
+		return;
+
+	for (e = t->bridge_head; e; e = e->next) {
+		tp_dbg();
+		tp_dbg("%s %s\n", dev_mac, e->mac_addr);
+		if (dev_mac && (strncmp(e->mac_addr, dev_mac, MAC_MAX_LEN) != 0))
+			continue;
+		tp_dbg();
+
+		child = lookup(e->mac_addr);
+		if (!child)
+			continue;
+		tp_dbg();
+
+		/*
+		if (child->fake_mac)
+			child = child->real;
+		*/
+
+		if (child->stale)
+			continue;
+
+		tp_dbg();
+		tp_dbg("%s %s\n", port, e->downlink_port);
+		if (port && strncmp(port, e->downlink_port, IFNAMSIZ) != 0)
+			continue;
+		tp_dbg();
+
+		tp_dbg("%s has gone stale!\n", child->mac_addr);
+
+		child->stale = true;
+		if (child->fake)
+			child->fake->stale = true;
+		gettimeofday(&child->tv_start, NULL);
+		list_add(&child->list, &child->ctx->stale_nodes);
+
+		child->ageing_timer = get_ageing_timer(child->ctx, e->mac_addr, b->ifindex);
+		if (child->ageing_timer < 0) {
+			tp_dbg("%s gone from brctl, full clean\n", e->mac_addr);
+			child->ageing_timer = 0;
+			clean_entry(child, b->ifindex);
+
+			tp_dbg();
+			continue;
+		}
+	}
+}
+
+void child_ev_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg)
+{
+	struct vertex_table *t = container_of(ev, struct vertex_table, listener);
+	struct device_context *dc = t->ctx;
+	struct blob_attr *tb[__TOPOLOGY_MAX];
+	int rv, ifindex;
+	char *src, *event, *port, *dev_mac;
+	struct vertex_table *child;
+	struct bridge *b;
+
+	tp_dbg("%s\n", type);
+
+	rv = blobmsg_parse(topology_policy, __TOPOLOGY_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
+	if (rv < 0) {
+		printf("problem parsing msg with blobmsg_parse!\n");
+		goto fail;
+	}
+
+	event = (char *)blobmsg_get_string(tb[EVENT]);
+	if (!event)
+		goto fail;
+	else if (!strncmp("poll", event, 4)) {
+		printf("%d registered POLL\n", __LINE__);
+		schedule_stale_poll(dc, 1000);
+		return;
+	}
+
+	tp_dbg("event: %s\n", event);
+
+	dev_mac = (char *)blobmsg_get_string(tb[DEV_MAC]);
+
+	tp_dbg("dev_mac: %s\n", dev_mac);
+	src = (char *)blobmsg_get_string(tb[SRC]);
+	if (!src)
+		goto fail;
+
+	tp_dbg("%s\n", src);
+
+	/* work around because identical mac bridges, wont work*/
+	/*v = get_vertex_by_mac(dc, src);
+	if (!v)
+		goto fail;
+	//t = lookup(src);
+	t = v->vt;
+	if (!t)
+		goto fail;*/
+	tp_dbg("source t %s", t->mac_addr);
+	tp_dbg("___________table____________");
+	print_list(t->bridge_head);
+	tp_dbg("____________________________");
+	port = blobmsg_get_string(tb[PORT_NAME]);
+	if (!port)
+		goto fail;
+
+	tp_dbg("port %s", port);
+
+	ifindex = if_nametoindex(port);
+
+	tp_dbg("t->v->br_ifindex %d, %s", t->v->br_ifindex, t->v->mac_addr);
+
+	b = get_bridge_by_idx(&dc->bridges, t->v->br_ifindex);
+	if (!b)
+		goto fail;
+
+	tp_dbg("POLL CAUSED BY: %s %s %s\n", __func__, type, event);
+
+	schedule_stale_poll(dc, 1000); // hack because of lost event (if swap from one wifi ssid to another instantly within the network an up event (iirc) will get lost)
+
+	if (strncmp(event, "up", 2) == 0) { //always trust wifi.sta events
+		if (!is_wifi(port))
+			goto fail;
+
+		child = lookup(dev_mac);
+		if (!child)
+			goto fail;
+
+		if (!child->stale)
+			goto fail;
+
+		tp_dbg("%s has returned!\n", child->mac_addr);
+
+		child->stale = false;
+		list_del(&child->list);
+	} else if (strncmp(event, "down", 4) == 0) { // if a port goes up, a node should be added?
+		tp_dbg();
+		if (!is_wifi(port))
+			mark_stale_children(t, dev_mac, port);
+		else {
+			child = lookup(dev_mac);
+			if (!child)
+				goto fail;
+
+			if (child->stale)
+				goto fail;
+
+			tp_dbg("%s has gone stale!\n", child->mac_addr);
+			if (child->fake) {
+				tp_dbg("%s has a masquerading address! %s\n", child->mac_addr, child->fake->mac_addr);
+				child->ageing_timer = get_ageing_timer(child->ctx, child->fake->mac_addr, b->ifindex);
+				tp_dbg("%lf\n", child->ageing_timer);
+			} else
+				child->ageing_timer = get_ageing_timer(child->ctx, child->mac_addr, b->ifindex);
+			if (child->ageing_timer < 0) {
+				tp_dbg("%s gone from brctl, full clean\n", child->mac_addr);
+
+				child->ageing_timer = 0;
+
+				clean_entry(child, b->ifindex);
+
+				goto fail;
+			}
+
+			child->stale = true;
+			child->fake = NULL;
+			gettimeofday(&child->tv_start, NULL);
+			list_add(&child->list, &dc->stale_nodes);
+		}
+	}
+
+fail:
+	return;
+}
+
+int init_event_listener(struct device_context *dc)
+{
+	dc->ubus_ev.cb = event_handler;
+	ubus_register_event_handler(dc->ctx, &dc->ubus_ev, "ubus.object.add");
+	ubus_register_event_handler(dc->ctx, &dc->ubus_ev, "ubus.object.remove");
+	ubus_register_event_handler(dc->ctx, &dc->ubus_ev, "wifi.sta");
+	ubus_register_event_handler(dc->ctx, &dc->ubus_ev, "switch");
+
+	printf("event handler registered\n");
+	return 0;
+}
+
+int unregister_child_listener(struct vertex_table *t)
+{
+	int rv;
+
+	rv = ubus_unregister_event_handler(t->ctx->ctx, &t->listener);
+	if (rv) {
+		printf("an error occurred unregistering our event handler, code %d\n", rv);
+		goto fail;
+	}
+
+	t->active_listener = false;
+	printf("%s %d listener for node %s has been unregistered %d\n", __func__, __LINE__, t->mac_addr, t->active_listener);
+
+	return 0;
+fail:
+	return -1;
+}
+
+// every child needs their own listener variable for us to be able to unregister it
+int register_child_listener(struct device_context *dc, char *ip)
+{
+	int rv;
+	uint32_t obj_id;
+	struct vertex_table *t;
+	char obj[64] = {0};
+	char *mac;
+
+	if (!dc->root)
+		return -1;
+
+	tp_dbg("%s\n", ip);
+	/* = get_vertex_by_ip(ip); // perhaps ip to mac could be more efficient for this?
+	if (!v)
+		goto fail;*/
+	mac = ip_to_mac(ip);
+	if (!mac)
+		goto fail;
+	tp_dbg("%s\n", mac);
+	t = lookup(mac);
+	if (!t)
+		goto fail;
+
+	tp_dbg("%s %d\n", t->mac_addr, t->active_listener);
+	snprintf(obj, 64, "%s/topology", ip);
+	if (t->active_listener)
+		return 0;
+
+	//memset(&t->listener, 0, sizeof(t->listener));
+	t->listener.cb = child_ev_handler;
+	tp_dbg("%s\n", obj);
+
+	// perform a lookup at that object and see if it actually exists or not
+	rv = ubus_lookup_id(dc->ctx, obj, &obj_id);
+	if (rv)
+		tp_dbg("something about the ubus lookup failed! error code %d\n", rv);
+
+	if (obj_id <= 0) {
+		tp_dbg("the object ID turned out to be corrupt/invalid! %d\n", obj_id);
+		goto fail;
+	}
+
+	tp_dbg("object ID seems valid! %d\n", obj_id);
+
+	rv = ubus_register_event_handler(dc->ctx, &(t->listener), obj);
+	if (rv) {
+		printf("an error occurred registering our event handler, code %d\n", rv);
+		goto fail;
+	}
+	t->active_listener = true;
+	tp_dbg("listener for node %s has been registered, pattern: %s\n", t->mac_addr, obj);
+
+	return 0;
+fail:
+	return -1;
+}
+
+int open_rtnl_sock(struct device_context *dc)
+{
+	int rv;
+	unsigned int groups = 0;
+
+	groups |= nl_mgrp(RTNLGRP_LINK);
+	groups |= nl_mgrp(RTNLGRP_NEIGH);
+
+	rv = rtnl_open(&dc->rth_socket, groups); // 5 for RTNL_NEIGH & RTNL_LINK & RTNLGRP_IPV4_IFADDR
+	if (rv < 0)
+		goto fail;
+
+	return dc->rth_socket.fd;
+fail:
+	return -1;
+}
+
+/* inspired by parse_arp */
+int msg_handler(struct device_context *dc, const struct sockaddr_nl *nl, struct nlmsghdr *msg)
+{
+	char b1[64] = {0};
+	struct ndmsg *r;
+	struct ifinfomsg *ifm;
+	struct rtattr *tb[NDA_MAX + 1];
+	struct rtattr *tb_ifl[IFLA_MAX + 1];
+	char ip[IPV4_MAX_LEN] = {0};
+	struct vertex_table *t;
+	const char *p;
+	tp_dbg();
+
+	r = NLMSG_DATA(msg);
+
+	switch (msg->nlmsg_type) {
+	case RTM_NEWLINK:
+		if (!dc->root)
+			break;
+
+		ifm = NLMSG_DATA(msg);
+		parse_rtattr(tb_ifl, IFLA_MAX, IFLA_RTA(ifm), msg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifm)));
+		/*
+		if (tb_ifl[IFLA_ADDRESS]) {
+			tp_dbg("LINK");
+			char buffer[64];
+			tp_dbg("LINK");
+			p = RTA_DATA(tb_ifl[IFLA_ADDRESS]);
+
+			snprintf(buffer, 64, "%02x:%02x:%02x:%02x:%02x:%02x", p[0] & 0xff, p[1] & 0xff, p[2] & 0xff, p[3] & 0xff, p[4] & 0xff, p[5] & 0xff);
+			tp_dbg("LINK %s", buffer);
+		}
+		*/
+		if (tb_ifl[IFLA_IFNAME]) {
+			p = rta_getattr_str(tb_ifl[IFLA_IFNAME]);
+			tp_dbg("RTM_NEWLINK ifname %s", p);
+
+			/* how do we distinguish a bridge? let alone it being one that is
+			created by this event */
+			if (strncmp("br-", p, 3) == 0)
+				schedule_stale_poll(dc, 1000);
+		}
+		break;
+	case RTM_DELLINK:
+		if (!dc->root)
+				break;
+		schedule_stale_poll(dc, 1000);
+		/*
+		ifm = NLMSG_DATA(msg);
+		parse_rtattr(tb_ifl, IFLA_MAX, IFLA_RTA(ifm), msg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifm)));
+		if (tb_ifl[IFLA_IFNAME]) {
+			p = rta_getattr_str(tb_ifl[IFLA_IFNAME]);
+			tp_dbg("RTM_NEWLINK ifname %s", p);
+		}
+		*/
+		break;
+	case RTM_NEWNEIGH:
+		parse_rtattr(tb, NDA_MAX, NDA_RTA(r), msg->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+		if (tb[NDA_LLADDR]) {
+			if (RTA_PAYLOAD(tb[NDA_LLADDR]) == 6) {
+				memset(b1, 0, 64);
+
+				p = rta_getattr_str(tb[NDA_LLADDR]);
+				snprintf(b1, 63, "%02x:%02x:%02x:%02x:%02x:%02x", p[0] & 0xff, p[1] & 0xff, p[2] & 0xff, p[3] & 0xff, p[4] & 0xff, p[5] & 0xff);
+			} else
+				break;
+		} else
+			break;
+
+		if (tb[NDA_DST]) {
+			inet_ntop(AF_INET, RTA_DATA(tb[NDA_DST]), ip, IPV4_MAX_LEN);
+			if (!strncmp(ip, "254", 3) || !strncmp(ip, "32", 2) || !strncmp(ip, "172", 3))
+				break;
+		}
+
+		if (dc->root) {
+			t = lookup(b1);
+			if (t && t->stale) {
+				//if (r->ndm_state & NUD_REACHABLE) { // needs to be nud reachable, might be a stale ip address?
+					printf("%s %d POLL CAUSED BY: RTM_NEWNEIGH msghandler NUD_REACHABLE %s\n", __func__, __LINE__, b1);
+					schedule_stale_poll(dc, 1000);
+				//}
+			}
+
+			if (!t) {
+				printf("%s %d %s was not previously found in table, lets poll!\n", __func__, __LINE__, b1);
+				printf("%s %d POLL CAUSED BY: RTM_NEWNEIGH msghandler new neighbor %s\n", __func__, __LINE__, b1);
+
+				schedule_stale_poll(dc, 1000);
+				tp_dbg();
+				poll_tree(dc);
+				tp_dbg();
+			}
+		} else {
+			struct arp_entry *ae;
+
+			ae = lookup_arp(b1);
+			if (r->ndm_state & NUD_REACHABLE || (ae && !(ae->state & NUD_REACHABLE) && (r->ndm_state & NUD_NOARP))) {
+				struct blob_buf bb = {0};
+
+				tp_dbg("%d SENDING POLL FOR: RTM_NEWNEIGH\n", __LINE__);
+				blob_buf_init(&bb, 0);
+				blobmsg_add_string(&bb, "event", "poll");
+				ubus_send_event(dc->ctx, "topology", bb.head);
+				blob_buf_free(&bb);
+			}
+		}
+		break;
+	case RTM_DELNEIGH: // duplicate code below
+		if (r->ndm_family != AF_BRIDGE)
+			return 0;
+
+		//printf("%d %s: RTM_DELNEIGH PART back with a vengeance\n", __LINE__, __func__);
+/*
+		if ((list_first_entry(&dc->bridges, struct bridge, list)[0]).ifindex != r->ndm_ifindex)
+			return 0;
+*/
+		parse_rtattr(tb, NDA_MAX, NDA_RTA(r), msg->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+		if (tb[NDA_LLADDR]) {
+			if (RTA_PAYLOAD(tb[NDA_LLADDR]) == 6) {
+				memset(b1, 0, 64);
+				char *p = RTA_DATA(tb[NDA_LLADDR]);
+
+				snprintf(b1, 64, "%02x:%02x:%02x:%02x:%02x:%02x", p[0] & 0xff, p[1] & 0xff, p[2] & 0xff, p[3] & 0xff, p[4] & 0xff, p[5] & 0xff);
+			}
+		}
+		//printf("%s %d: RTM_DELNEIGH %s\n", __func__, __LINE__, b1);
+
+		t = lookup(b1);
+		if (t) { // shouldnt be necessary for clean_entry...
+			struct bridge *b;
+			b = get_bridge_by_port_idx(&dc->bridges, r->ndm_ifindex);
+			if (b)
+				clean_entry(t, b->ifindex); // how many should we clean?
+		}
+		if (dc->root) {
+			printf("%s %d POLL CAUSED BY: RTM_DELNEIGH msghandler removed neighbor %s\n", __func__, __LINE__, b1);
+			schedule_stale_poll(dc, 1000); // shouldnt poll clients be good enough here?
+			poll_tree(dc);
+		} else {
+			struct blob_buf bb = {0};
+
+			printf("%d SENDING POLL FOR: RTM_DELNEIGH\n", __LINE__);
+
+			blob_buf_init(&bb, 0);
+			blobmsg_add_string(&bb, "event", "poll");
+			ubus_send_event(dc->ctx, "topology", bb.head);
+			blob_buf_free(&bb);
+		}
+
+		break;
+	case NLMSG_ERROR:
+		//printf("%d %s: NLMSG_ERROR\n", __LINE__, __func__);
+		break;
+	default:
+		//printf("%s: Unknown netlink nlmsg_type %d\n", __func__, msg->nlmsg_type);
+		break;
+	}
+
+	return 0;
+}
+
+void dump_bin(void *buf, int len)
+{
+	char *ptr;
+	int i;
+
+	ptr = (char *)buf;
+
+	for (i = 0; i < len; i++)
+		printf("%02x ", ptr[i] & 0xff);
+
+	printf("\n\n");
+}
+
+/* mostly taken from rtnl_listen */
+static void nl_msg_parser_cb(struct uloop_fd *ufd, unsigned int events)
+{
+	struct device_context *dc = container_of(ufd, struct device_context, kevent);
+	int status;
+	struct nlmsghdr *h;
+	struct sockaddr_nl nladdr;
+	struct iovec iov;
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	char buf[8192];
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+	nladdr.nl_pid = 0;
+	nladdr.nl_groups = 0;
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+	status = recvmsg(dc->rth_socket.fd, &msg, 0);
+
+	if (status < 0) {
+		if (errno == EINTR || errno == EAGAIN)
+			return;
+		fprintf(stderr, "netlink receive error %s (%d)\n",
+			strerror(errno), errno);
+		if (errno == ENOBUFS)
+			return;
+		return;
+	}
+
+	if (status == 0) {
+		fprintf(stderr, "EOF on netlink\n");
+		return;
+	}
+
+	if (msg.msg_namelen != sizeof(nladdr)) {
+		fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+		return;
+	}
+
+	for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
+		int err;
+		int len = h->nlmsg_len;
+		int l = len - sizeof(*h);
+
+		if (l < 0 || len > status) {
+			if (msg.msg_flags & MSG_TRUNC) {
+				fprintf(stderr, "Truncated message\n");
+				return;
+			}
+			fprintf(stderr, "!!!malformed message: len=%d\n", len);
+			return;
+		}
+
+		err = msg_handler(dc, &nladdr, h);
+		if (err < 0)
+			return;
+
+		status -= NLMSG_ALIGN(len);
+		h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
+	}
+
+	if (msg.msg_flags & MSG_TRUNC) {
+		fprintf(stderr, "Message truncated\n");
+		return;
+	}
+
+	if (status) {
+		fprintf(stderr, "!!!Remnant of size %d\n", status);
+		return;
+	}
+}
+
+static int register_kevent_to_uloop(int fd, struct uloop_fd *kevent)
+{
+	int rv;
+
+	kevent->cb = nl_msg_parser_cb;
+	kevent->fd = fd;
+
+	rv = uloop_fd_add(kevent, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+	if (rv != 0) {
+		printf("fail to add kevent to uloop %d\n", rv);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return rv;
+}
+
+bool valid_addr(char *buf, int len, unsigned int mask)
+{
+	char *ptr;
+	int i;
+	bool rv = false;
+
+	ptr = (char *)buf;
+
+	for (i = 0; i < len; i++) {
+		printf("%02x ", ptr[i] & mask);
+		if (ptr[i] & mask) {
+			printf("value to mask %02x, mask %02x turned to true\n", ptr[i], mask);
+			rv = true;
+		}
+		mask = mask >> 8;
+	}
+	printf("%s  %d %d\n", __func__, __LINE__, rv);
+	return rv;
+}
+
+int parse_arp(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	struct ndmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[NDA_MAX + 1];
+	char mac[64] = {0};
+	char *ptr;
+	char ip[IPV4_MAX_LEN] = {0};
+	struct arp_entry *ae;
+
+	//dump_bin(n, n->nlmsg_len);
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
+		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
+				n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+	if (tb[NDA_LLADDR]) {
+		if (RTA_PAYLOAD(tb[NDA_LLADDR]) == 6) {
+			ptr = RTA_DATA(tb[NDA_LLADDR]);
+			snprintf(mac, 64, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0] & 0xff, ptr[1] & 0xff, ptr[2] & 0xff, ptr[3] & 0xff, ptr[4] & 0xff, ptr[5] & 0xff);
+		} else
+			goto fail;
+	} else
+		goto fail;
+
+	if (tb[NDA_DST]) {
+		ptr = RTA_DATA(tb[NDA_DST]);
+		/*
+		if (ptr & 0xFE000000 || ptr & 0x20000000 || ptr & 0xAC000000)
+			goto fail;
+		*/
+		inet_ntop(AF_INET, ptr, ip, IPV4_MAX_LEN);
+		if (!strncmp(ip, "254", 3) || !strncmp(ip, "32", 2) || !strncmp(ip, "172", 3)) {
+			//printf("%s %d threw out %s\n", __func__, __LINE__, ip);
+			goto fail;
+		}
+	} else
+		goto fail;
+
+
+	tp_dbg("at this point we have gathered %s %s %d %d, lets start filtering", mac, ip, r->ndm_ifindex, r->ndm_state);
+
+	if (r->ndm_family != AF_INET && r->ndm_family != AF_BRIDGE)
+		return 0;
+
+	/* can filter on bridge if required */
+
+	// these mental gymnastic if cases seem to work in most cases, but not fool proof? can be given an incorrect ip of ANOTHER interface?
+	if (!(r->ndm_state & (NUD_REACHABLE | NUD_NOARP) || !(r->ndm_state == NUD_NONE))) {
+	//if (!(r->ndm_state & NUD_REACHABLE)) {
+		//printf%s ("%d ERROR incorrect nud? %s %s %d %d\n", __func__, __LINE__, mac, ip, r->ndm_state, r->ndm_flags);
+		return 0;
+	}
+
+	ae = lookup_arp(mac);
+	if (!ae) {
+		tp_dbg("ERROR: no arp entry found for %s %s", mac, ip);
+		goto fail;
+	}
+
+	if (!(r->ndm_state & (NUD_REACHABLE)) && ae->state & NUD_REACHABLE) {
+		//printf%s ("%d ERROR recorded state is already reachable! %s %s %d %d\n", __func__, __LINE__, mac, ip, r->ndm_state, r->ndm_flags);
+		goto fail;
+	}
+
+	strncpy(ae->ip_addr, ip, IPV4_MAX_LEN);
+
+	ae->state = r->ndm_state;
+
+	if (!insert_ip(mac, ip))
+		tp_dbg("ERROR: when inserting ip link for %s %s?", mac, ip);
+
+	/*
+	could do this if it was nud reachable i guess?
+	struct vertex_table *t;
+	t = lookup(ae->mac_addr);
+	if (t && t->stale) {
+		t->stale = false;
+		list_del(&t->list);
+	}
+	*/
+
+	tp_dbg("%s %s %d %s %d %d\n", ae->mac_addr, ae->ip_addr, ae->ifindex, ae->ifname, ae->state, r->ndm_family);
+fail:
+	return 0;
+}
+
+static void get_dhcp(struct device_context *dc)
+{
+	FILE *fp;
+	char *expiration, *mac, *ip, *name;
+	char line[1024];
+	struct arp_entry *ae;
+
+	tp_dbg();
+
+	fp = fopen("/tmp/dhcp.leases", "r");
+	if (!fp) {
+		perror("fopen");
+		return;
+	}
+
+	tp_dbg();
+
+	while ((fgets(line, 1024, fp)) != NULL) {
+		tp_dbg();
+		remove_newline(line);
+		tp_dbg();
+
+		expiration = strtok(line, " ");
+		mac = strtok(NULL, " ");
+		ip = strtok(NULL, " ");
+		name = strtok(NULL, " ");
+		tp_dbg();
+
+		ae = lookup_arp(mac);
+		if (!ae)
+			continue;
+		tp_dbg();
+
+		strncpy(ae->name, name, 255);
+		ae->name[255] = '\0';
+
+		/*strncpy(ae->ip_addr, ip, IPV4_MAX_LEN - 1);
+		ae->ip_addr[IPV4_MAX_LEN - 1] = '\0';
+		strncpy(ae->name, ip, IPV4_MAX_LEN - 1);
+		ae->ip_addr[IPV4_MAX_LEN - 1] = '\0';
+		if (!insert_ip(mac, ip))
+			tp_dbg("ERROR: when inserting ip link for %s %s?", mac, ip);*/
+		tp_dbg();
+	}
+
+	fclose(fp);
+	return;
+}
+
+static int get_neightable(struct device_context * dc)
+{
+	struct rtnl_handle rth = {.fd = -1};
+	struct {
+		struct nlmsghdr n;
+		struct ndmsg ndm;
+		char buf[256];
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
+		.ndm.ndm_family = AF_UNSPEC, // only want addresses which has ipv4 address configured (?)
+	};
+	int msg_size = sizeof(struct ndmsg);
+	int rv;
+
+	rv = rtnl_open(&rth, 0);
+	if (rv < 0)
+		goto fail;
+
+	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &req.ndm, msg_size) < 0) {
+		perror("Cannot send dump request");
+		goto fail_close;
+	}
+
+	if (rtnl_dump_filter(&rth, parse_arp, stdout) < 0) {
+		fprintf(stderr, "%d Dump terminated\n", __LINE__);
+		goto fail_close;
+	}
+	tp_dbg("finished parsing neigh table");
+
+	get_dhcp(dc);
+
+	rtnl_close(&rth);
+	return 0;
+fail_close:
+	rtnl_close(&rth);
+fail:
+	return -1;
+}
+
+struct ubus_method topology_object_methods[] = {
+	UBUS_METHOD_NOARG("dump_info", dump_info),
+	UBUS_METHOD_NOARG("gather_info", gather_info),
+	UBUS_METHOD_NOARG("build_tree", build_tree),
+	UBUS_METHOD_NOARG("tree", tree)};
+
+struct ubus_object_type topology_object_type =
+	UBUS_OBJECT_TYPE("topology", topology_object_methods);
+
+int main(int argc, char **argv)
+{
+	int rv, fd;
+	struct device_context *device_ctx;
+
+	device_ctx = calloc(1, sizeof(*device_ctx));
+	if (!device_ctx) {
+		perror("calloc");
+		goto fail;
+	}
+
+	INIT_LIST_HEAD(&device_ctx->vertexes);
+	INIT_LIST_HEAD(&device_ctx->endpoints);
+	INIT_LIST_HEAD(&device_ctx->stale_nodes);
+
+	device_ctx->br_ifindex = -1;
+
+	device_ctx->root = parse_args(argc, argv);
+	tp_dbg("root %d\n", device_ctx->root);
+	uloop_init();
+	device_ctx->dispatch_scheduler.cb = dispatch_notify;
+	device_ctx->ctx = ubus_connect(NULL);
+	ubus_add_uloop(device_ctx->ctx);
+
+	init_event_listener(device_ctx);
+
+	device_ctx->topology_object = (struct ubus_object) {
+		.name = "topology",
+		.type = &topology_object_type,
+		.methods = topology_object_methods,
+		.n_methods = ARRAY_SIZE(topology_object_methods),
+	};
+
+	fd = open_rtnl_sock(device_ctx);
+	if (fd < 0)
+		goto out_ctx;
+
+	rv = register_kevent_to_uloop(fd, &device_ctx->kevent);
+	if (rv != 0) {
+		printf("Error: %d, fail to register kernel event to uloop\n", rv);
+		goto out_ctx;
+	}
+
+	//rv = get_bridges((list_first_entry(&device_ctx->bridges, struct bridge, list)[0]).ifindex); // what to do on error here?
+	INIT_LIST_HEAD(&device_ctx->bridges);
+	device_ctx->len = generate_neightable(&device_ctx->bridges);
+	if (device_ctx->len <= 0)
+		goto out_ctx;
+
+	get_neightable(device_ctx);
+
+	//clear_stale_arp();
+	tp_dbg();
+	ubus_add_object(device_ctx->ctx, &device_ctx->topology_object);
+	tp_dbg();
+	schedule_stale_poll(device_ctx, 60000);
+	tp_dbg();
+	uloop_run();
+
+out_ctx:
+	ubus_free(device_ctx->ctx);
+	free(device_ctx);
+fail:
+	return 0;
+}
diff --git a/topologyd.h b/topologyd.h
new file mode 100644
index 0000000..c386ce7
--- /dev/null
+++ b/topologyd.h
@@ -0,0 +1,86 @@
+#ifndef TOP_H
+#define TOP_H
+
+#include "utils.h"
+#include "event.h"
+
+struct vertex_table;
+
+struct device_context {
+	struct ubus_context *ctx;
+	struct ubus_event_handler ubus_ev; /** for local events */
+	struct uloop_timeout stale_scheduler;
+	struct uloop_timeout dispatch_scheduler;
+	struct ubus_object topology_object;
+
+	bool *root_entry;
+	bool root;
+	int br_ifindex;
+	int dispatch_counter;
+	struct rtnl_handle rth_socket;
+
+	int len;
+	// TODO: Let these maintain a list of vertex_table entries rather than vertex entries?
+	struct list_head endpoints;
+	struct list_head vertexes;
+	struct list_head stale_nodes;
+	struct list_head bridges;
+
+	struct uloop_fd kevent;
+};
+
+struct port {
+	int index; // portno
+	int ifindex;
+	int br_ifindex;
+
+	char ifname[IF_NAMESIZE];
+};
+
+/* TODO: use binary format for mac addresses, make a MAC2FMT script */
+// this struct can probably be removed entirely and replaced by hashmap entry for all intents and purposes
+struct vertex {
+	char mac_addr[18]; //MAC_MAX_LEN
+	char ip_addr[15]; // should be using IPV4_MAX_LEN
+	char uplink_port[IF_NAMESIZE];
+	char uplink_type[9]; //LINK_TYPE_SIZE
+
+	int port_no;
+	int depth;
+
+	double age;
+	int br_ifindex;
+
+	bool endpoint;
+	bool bridge_entry;
+	struct vertex_table *vt;
+
+	struct list_head list;
+	struct list_head stack;
+};
+
+struct bridge {
+	int ifindex;
+
+	struct port ports[256];
+
+	/* TODO: use binary format for mac addresses, make a MAC2FMT script */
+	char mac_addr[18];
+	char ifname[IFNAMSIZ];
+
+	struct vertex_table *root;
+
+	struct list_head list;
+};
+
+int unregister_child_listener(struct vertex_table *t);
+void delete_vertex(struct vertex *v);
+char *get_local_mac(const char *bridge, char *mac);
+int prep_event_blob(struct device_context *dc, struct blob_buf *bb, char *event, char *port);
+int network_event(struct device_context *dc, struct blob_buf *bb, char *event, char *port);
+void child_ev_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
+double get_ageing_timer(struct device_context *dc, char *mac, int br_ifindex);
+void mark_stale_children(struct vertex_table *t, char *dev_mac, char *port);
+void schedule_stale_poll(struct device_context *dc, int timeout);
+struct vertex *get_vertex_by_mac_bridge(struct device_context *dc, struct bridge *b, char *mac);
+#endif
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..af70ed5
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,601 @@
+// will leak memory for now
+#include "utils.h"
+
+struct option longopts[] = {
+	{"root", no_argument, NULL, 'r'},
+	{0, 0, 0, 0}
+};
+
+bool parse_args(int argc, char **argv)
+{
+	int c;
+	bool root = false;
+
+	printf("%d\n", __LINE__);
+	while ((c = getopt_long(argc, argv, "r", longopts, NULL)) != -1) {
+		printf("%d\n", __LINE__);
+		switch (c) {
+		case 'r':
+			printf("%d root flag found, setting to true\n", __LINE__);
+			root = true;
+		break;
+		case '?':
+			printf("%d\n", __LINE__);
+			printf("%s: option '-%c' is not valid.\n", argv[0], optopt);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return root;
+}
+
+// could be combined with parse_link if u provde some priv struct
+static void parse_sta(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	char *json_str;
+	int *rssi = (int *)req->priv;
+	struct json_object *json_msg, *arr;
+
+
+	json_str = blobmsg_format_json(msg, true);
+	if (!json_str)
+		return;
+
+	json_msg = json_tokener_parse(json_str);
+	if (!json_msg)
+		goto out_str;
+
+	if (!json_object_is_type(json_msg, json_type_object))
+		goto out_json;
+	arr = json_get_array(json_msg, "stas");
+	if (!arr)
+		goto fail_key;
+
+	arr = json_object_array_get_idx(arr, 0);
+	if (!arr)
+		goto fail_key;
+
+	*rssi = json_get_int(arr, "rssi");
+
+fail_key:
+out_json:
+	json_object_put(json_msg);
+out_str:
+	free(json_str);
+}
+
+static void parse_link(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	char *json_str;
+	int *speed = (int *)req->priv;
+	struct json_object *json_msg;
+
+	json_str = blobmsg_format_json(msg, true);
+	if (!json_str)
+		return;
+
+	json_msg = json_tokener_parse(json_str);
+	if (!json_msg)
+		goto out_str;
+
+	if (!json_object_is_type(json_msg, json_type_object))
+		goto out_json;
+
+	*speed = json_get_int(json_msg, "rate");
+
+out_json:
+	json_object_put(json_msg);
+out_str:
+	free(json_str);
+}
+
+uint32_t get_obj_id(struct ubus_context *ctx, char *ip_addr, char *buf)
+{
+	uint32_t obj_id;
+
+	if (ip_addr)
+		snprintf(buf, 31, "%s/wifix", ip_addr);
+	else
+		snprintf(buf, 31, "wifix");
+
+	ubus_lookup_id(ctx, buf, &obj_id);
+
+	return obj_id;
+}
+
+int get_speed(struct ubus_context *ctx, char *ifname, char *ip_addr, int *speed)
+{
+	struct blob_buf bb = {0};
+	char buf[32]; // IPV4+/wifix LENGTH
+	int rv;
+	uint32_t obj_id;
+
+	obj_id = get_obj_id(ctx, ip_addr, buf);
+	if (!obj_id)
+		goto fail;
+
+	rv = blob_buf_init(&bb, 0);
+	if (rv) {
+		printf("blob_buf_init error\n");
+		goto fail;
+	}
+
+	blobmsg_add_string(&bb, "vif", ifname);
+
+	rv = ubus_invoke(ctx, obj_id, "status", bb.head, parse_link, speed, 1500);
+	if (rv)
+		printf("ubus_invoke error code %d\n", rv);
+
+	blob_buf_free(&bb);
+fail:
+	return rv;
+}
+
+int get_rssi(struct ubus_context *ctx, char *ifname, char *ip_addr, char *cli_mac, int *rssi)
+{
+	struct blob_buf bb = {0};
+	char buf[32]; // IPV4+/wifix LENGTH
+	int rv;
+	uint32_t obj_id;
+
+	obj_id = get_obj_id(ctx, ip_addr, buf);
+	if (!obj_id)
+		goto fail;
+
+	rv = blob_buf_init(&bb, 0);
+	if (rv) {
+		printf("blob_buf_init error\n");
+		goto fail;
+	}
+
+	blobmsg_add_string(&bb, "vif", ifname);
+	blobmsg_add_string(&bb, "sta", cli_mac);
+
+	rv = ubus_invoke(ctx, obj_id, "stas", bb.head, parse_sta, rssi, 1500);
+	if (rv)
+		printf("ubus_invoke error code %d\n", rv);
+
+	blob_buf_free(&bb);
+fail:
+	return rv;
+}
+
+void br_show_timer(struct timeval *tv, char *buffer)
+{
+	printf("%d %4i.%.2i\n", __LINE__, (int)tv->tv_sec, (int)tv->tv_usec / 10000);
+	snprintf(buffer, 16, "%4i.%.2i", (int)tv->tv_sec, (int)tv->tv_usec / 10000);
+	printf("%s %d %s\n", __func__, __LINE__, buffer);
+}
+
+char *show_config_entry(char *package, char *section, char *option, char *buffer)
+{
+	struct uci_context *c;
+	struct uci_ptr ptr;
+	struct uci_package *pkg;
+
+	c = uci_alloc_context();
+	if (!c)
+		goto fail;
+
+	uci_load(c, package, &pkg);
+	memset(&ptr, 0, sizeof(struct uci_ptr));
+
+	ptr.package = package;
+	ptr.section = section;
+	ptr.option = option;
+
+	if (uci_lookup_ptr(pkg->ctx, &ptr, NULL, true) != UCI_OK) {
+		uci_perror(c, "get_config_entry Error");
+		goto fail;
+	}
+
+	strncpy(buffer, ptr.o->v.string, IF_NAMESIZE);
+
+	uci_free_context(c);
+	return buffer;
+fail:
+	return NULL;
+}
+
+// should be replaced by UCI C library
+char *get_uplink_port(struct list_head *list, char *buffer)
+{
+	char uplink_ports[1024];
+	char *uplink_port;
+	int idx;
+	struct port *p;
+
+	if (!show_config_entry("ports", "WAN", "ifname", buffer))
+		goto fail;
+	idx = if_nametoindex(buffer);
+	if (!idx)
+		goto check_network;
+
+	DBGP("%s %d", buffer, idx);
+	p =	get_port_by_idx(list, idx);
+	if (p)
+		goto done; // success
+
+check_network:
+	if (!show_config_entry("network", "wan", "ifname", uplink_ports))
+		goto fail;
+
+	uplink_port = strtok(uplink_ports, " ");
+	if (!uplink_port)
+		goto fail;
+
+	strncpy(buffer, uplink_port, IF_NAMESIZE);
+	// check if buffer is part of LAN, perhaps netlink on AF_BRIDGES? check config ports LANx ?
+	printf("%d %s\n", __LINE__, uplink_ports);
+
+done:
+	return buffer;
+fail:
+	return NULL;
+}
+
+char *get_bridge(int *bridges, char *buffer)
+{
+	char *ifname = NULL;
+
+	ifname = if_indextoname(bridges[0], buffer);
+	if (ifname)
+		goto done;
+
+/*	if (get_bridges(bridges) < 0)
+		goto done;
+
+	ifname = if_indextoname(bridges[0], buffer);*/
+done:
+	return ifname;
+}
+
+void remove_newline(char *input)
+{
+	char *pos;
+
+	pos = strchr(input, '\n');
+	if (!pos)
+		return;
+
+	*pos = '\0';
+}
+
+char *ip_to_mac(const char *ip)
+{
+	struct ip_entry *ie;
+
+	printf("%d looking up %s\n", __LINE__, ip);
+	ie = lookup_ip(ip);
+	if (!ie)
+		return NULL;
+	printf("%d found %s\n", __LINE__, ie->ae->mac_addr);
+	return ie->ae->mac_addr;
+}
+
+char *mac_to_ip(char *mac)
+{
+	struct arp_entry *ae;
+
+	printf("%d looking up %s\n", __LINE__, mac);
+	ae = lookup_arp(mac);
+	if (!ae)
+		return NULL;
+	printf("%d found %s\n", __LINE__, ae->ip_addr);
+	return ae->ip_addr;
+}
+
+double json_get_double(struct json_object *obj, char *key)
+{
+	struct json_object *container = NULL;
+
+	json_object_object_get_ex(obj, key, &container);
+
+	return json_object_get_double(container);
+}
+
+int json_get_int(struct json_object *obj, char *key)
+{
+	struct json_object *container = NULL;
+
+	json_object_object_get_ex(obj, key, &container);
+
+	return json_object_get_int(container);
+}
+
+const char *json_get_string(struct json_object *obj, char *key)
+{
+	struct json_object *container = NULL;
+
+	json_object_object_get_ex(obj, key, &container);
+
+	return json_object_get_string(container);
+}
+
+struct json_object *json_get_array(struct json_object *obj, char *key)
+{
+	struct json_object *container = NULL;
+
+	json_object_object_get_ex(obj, key, &container);
+
+	return container;
+}
+
+struct json_object *json_get_object(struct json_object *obj, char *key)
+{
+	struct json_object *container = NULL;
+
+	json_object_object_get_ex(obj, key, &container);
+
+	return container;
+}
+
+void string_to_lower(char *str)
+{
+	int i;
+
+	for (i = 0; str[i]; i++)
+		str[i] = tolower(str[i]);
+}
+
+double elapsed_time(struct timeval tv_start, struct timeval tv_end)
+{
+	double ms, s;
+
+	ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000.0;	// sec to ms
+	ms += (tv_end.tv_usec - tv_start.tv_usec) / 1000.0; // us to ms
+
+	s = ms/1000;
+
+	return roundf(s * 100) / 100;
+}
+
+static int br_ioctl(unsigned long arg0, unsigned long arg1, unsigned long arg2)
+{
+	unsigned long arg[3];
+	int rv = -1;
+	int br_socket_fd;
+
+	br_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (br_socket_fd < 0)
+		goto fail;
+
+	arg[0] = arg0;
+	arg[1] = arg1;
+	arg[2] = arg2;
+
+	rv = ioctl(br_socket_fd, SIOCGIFBR, arg);
+
+fail:
+	close(br_socket_fd);
+	return rv;
+}
+
+int get_bridges(struct list_head *list)
+{
+	int num, i;
+	int ifindices[32];
+	struct bridge *b, *tmp;
+
+	list_for_each_entry_safe(b, tmp, list, list) {
+		list_del(&b->list);
+		free(b);
+	}
+
+	num = br_ioctl(BRCTL_GET_BRIDGES, (unsigned long)ifindices, 32);
+	if (num < 0)
+		goto fail;
+
+	for (i = 0; i < num; i++) { // could be replaced with memcpy?
+		tp_dbg("%d", i);
+		b = calloc(1, sizeof(*b));
+		if (!b)
+			continue;
+
+		b->ifindex = ifindices[i];
+		if (!if_indextoname(ifindices[i], b->ifname)) {
+			free(b);
+			continue;
+		}
+
+		tp_dbg("%d %d", b->ifindex, ifindices[i]);
+		if (!get_local_mac(b->ifname, b->mac_addr)) {
+			free(b);
+			continue;
+		}
+		list_add(&b->list, list);
+	}
+
+	return num;
+fail:
+	return -1;
+}
+
+bool is_wifi(char *if_name)
+{
+	if (strncmp(if_name, "wl", 2) == 0)
+		goto yes;
+	if (strncmp(if_name, "ra", 2) == 0)
+		goto yes;
+	if (strncmp(if_name, "apclii", 6) == 0)
+		goto yes;
+
+	return false;
+yes:
+	return true;
+}
+
+int isdigits(const char *s)
+{
+	while (*s) {
+		if (!isdigit(*s++))
+			return 0;
+	}
+
+	return 1;
+}
+
+const char *vlan_to_device(struct uci_context *ctx, struct uci_package *package, const char *vlan, char *buffer)
+{
+	struct uci_section *section;
+	struct uci_element *se;
+	const char *vid, *ifname;
+
+	uci_foreach_element(&package->sections, se) {
+		section = uci_to_section(se);
+
+		if (strcmp(section->type, "device") != 0)
+			continue;
+
+		vid = uci_lookup_option_string(ctx, section, "vid");
+		if (!vid)
+			continue;
+
+		if (strlen(vid) && strncmp(vid, vlan, 1))
+			continue;
+
+		ifname = uci_lookup_option_string(ctx, section, "name");
+		if (!ifname)
+			continue;
+
+		strncpy(buffer, ifname, IFNAMSIZ);
+		return buffer;
+	}
+
+	return NULL;
+}
+
+const char *portno_to_ifname(int portno, char *buffer)
+{
+	struct uci_context *ctx;
+	struct uci_package *package;
+	struct uci_section *section;
+	struct uci_element *se;
+	const char *ports, *vlan, *ifname = NULL;
+	char portno_s[16] = {0};
+	int rv;
+
+	ctx = uci_alloc_context();
+	if (!ctx)
+		goto out;
+
+	rv = uci_load(ctx, "network", &package);
+	if (rv != UCI_OK)
+		goto out_ctx;
+
+	uci_foreach_element(&package->sections, se) {
+		section = uci_to_section(se);
+
+		if (strcmp(section->type, "switch_vlan") != 0)
+			continue;
+
+		ports = uci_lookup_option_string(ctx, section, "ports");
+		strncpy(portno_s, ports, 16);
+		strtok(portno_s, " ");
+		if (strlen(portno_s) && atoi(portno_s) != portno)
+			continue;
+
+		vlan = uci_lookup_option_string(ctx, section, "vlan");
+		if (!vlan)
+			break;
+
+		ifname = vlan_to_device(ctx, package, vlan, buffer);
+		if (!ifname)
+			continue;
+
+		break;
+	}
+
+	uci_unload(ctx, package);
+out_ctx:
+	uci_free_context(ctx);
+out:
+	return ifname;
+}
+
+void log_message(int level, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	//if (syslogging && level >= 0)
+//		vsyslog(syslog_level[level], fmt, args);
+	vprintf(fmt, args);
+/*
+	if (outfile)
+		vfprintf(outfile, fmt, args);
+
+	if (usefifo && ffd)
+		vdprintf(ffd, fmt, args);
+*/
+	va_end(args);
+}
+
+void dump(unsigned char *buf, int len, char *label)
+{
+	int i;
+
+	if (label)
+		printf("---- %s ----", label);
+
+	for (i = 0; i < len; i++) {
+		if (!(i % 4))
+			printf("  ");
+		if (!(i % 16))
+			printf("\n ");
+		printf("%02x ", buf[i] & 0xff);
+	}
+
+	if (label)
+		printf("\n--------------\n");
+}
+
+struct bridge *get_bridge_by_port_idx(struct list_head *bridges, int ifindex)
+{
+	struct bridge *b;
+	struct port *p;
+	int i;
+	char ifname[IFNAMSIZ];
+
+	list_for_each_entry(b, bridges, list) {
+		for (i = 0; i < 256; i++) {
+			p = &b->ports[i];
+			if (p->index == 0 || p->ifindex == 0)
+				continue;
+			tp_dbg("%d %d %s\n", ifindex, p->ifindex, if_indextoname(p->ifindex, ifname));
+			if (ifindex != p->ifindex)
+				continue;
+
+			return b;
+		}
+	}
+
+	return NULL;
+}
+
+struct bridge *get_bridge_by_idx(struct list_head *bridge, int index)
+{
+	struct bridge *b;
+
+	list_for_each_entry(b, bridge, list) {
+		DBGP("%d %d", b->ifindex, index);
+		if (b->ifindex == index)
+			return b;
+	}
+
+	return NULL;
+}
+
+struct bridge *get_bridge_by_name(struct list_head *bridge, char *ifname)
+{
+	struct bridge *b;
+
+	list_for_each_entry(b, bridge, list) {
+		if (!strncmp(b->ifname, ifname, IFNAMSIZ))
+			return b;
+	}
+
+	return NULL;
+}
\ No newline at end of file
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..2737308
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,127 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include <json-c/json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/uloop.h>
+#include <libubus.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <errno.h>
+
+#include <netlink/route/rtnl.h>
+#include <linux/if_bridge.h>
+
+#include <libnetlink.h>
+#include <math.h>
+#include <net/if_arp.h>
+#include <linux/rtnetlink.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <uci.h>
+#include <easy/easy.h>
+#include <wifi.h>
+//#include <ethernet.h>
+
+#include <getopt.h>
+
+#include <syslog.h>
+
+#include "hashmap.h"
+#include "list.h"
+#include "arp_table.h"
+#include "table_gen.h"
+#include "topologyd.h"
+
+struct bridge;
+
+/* The following if from times(3) man page.  It may need to be changed */ // taken from brctl
+#ifndef HZ
+#ifndef CLK_TCK
+#define HZ 100.0
+#else /* CLK_TCK */
+#define HZ ((double)CLK_TCK)
+#endif
+#endif
+
+#define LINK_TYPE_SIZE 9
+#define MAC_MAX_LEN 18
+#define IPV4_MAX_LEN 15 // these are probably available in limits.h?
+
+#define DEBUG 1
+
+#define DEBUG 1
+
+#ifdef DEBUG
+#define DBGP(text, ...)                                                             \
+	do                                                                              \
+	{                                                                               \
+		printf("fnc: %s, row: %d " text "\n", __func__, __LINE__, __VA_ARGS__); \
+	} while (false)
+#define EMPT()                                                     \
+	do                                                             \
+	{                                                              \
+		printf("fnc: %s, row: %d\n", __func__, __LINE__); \
+	} while (false)
+#else
+#define DBGP(text, ...) \
+	do                  \
+	{                   \
+	} while (false)
+
+#define EMPT() \
+	do                  \
+	{                   \
+	} while (false)
+#endif
+
+#define tp_dbg(fmt, ...) dbg("%s %d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define dbg(...) log_message(3, __VA_ARGS__)
+
+extern bool arping(const char *targetIP, const char *device, int toms);
+int get_speed(struct ubus_context *ctx, char *ifname, char *ip_addr, int *speed);
+int get_rssi(struct ubus_context *ctx, char *ifname, char *ip_addr, char *cli_mac, int *rssi);
+bool parse_args(int argc, char **argv);
+void br_show_timer(struct timeval *tv, char *buffer);
+char *show_config_entry(char *package, char *section, char *option, char *buffer);
+char *get_uplink_port(struct list_head *list, char *buffer);
+char *get_bridge(int *ifindex, char *buffer);
+void remove_newline(char *input);
+char *ip_to_mac(const char *ip);
+char *mac_to_ip(char *mac);
+int get_port_no(char *bridge, char *if_name);
+double json_get_double(struct json_object *obj, char *key);
+int json_get_int(struct json_object *obj, char *key);
+const char *json_get_string(struct json_object *obj, char *key);
+struct json_object *json_get_array(struct json_object *obj, char *key);
+struct json_object *json_get_object(struct json_object *obj, char *key);
+void string_to_lower(char *str);
+double elapsed_time(struct timeval tv_start, struct timeval tv_end);
+int get_bridges(struct list_head *list);
+bool is_wifi(char *if_name);
+int isdigits(const char *s);
+const char *vlan_to_device(struct uci_context *ctx, struct uci_package *package, const char *vlan, char *buffer);
+const char *portno_to_ifname(int portno, char *buffer);
+void log_message(int level, const char *fmt, ...);
+struct bridge *get_bridge_by_port_idx(struct list_head *bridge, int ifindex);
+struct bridge *get_bridge_by_idx(struct list_head *bridge, int index);
+struct bridge *get_bridge_by_name(struct list_head *bridge, char *ifname);
+static inline __u32 nl_mgrp(__u32 group)
+{
+	if (group > 31) {
+		fprintf(stderr, "Use setsockopt for this group %d\n", group);
+		exit(-1);
+	}
+	return group ? (1 << (group - 1)) : 0;
+}
+#endif
-- 
GitLab