diff --git a/GPL_iopsys.md b/GPL_iopsys.md new file mode 100644 index 0000000000000000000000000000000000000000..790614e9c3e22f975be10612f1eb03d4682e4ebb --- /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 0000000000000000000000000000000000000000..7d22cb99979693498ea1e66cafc7d6821ddcd16f --- /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 0000000000000000000000000000000000000000..501f0f17a9c00b9d57a645fef7b9ef738a597b52 --- /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 0000000000000000000000000000000000000000..ee0065c5061483703c8559e3f2c6c3db2bf3de88 --- /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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8968474ab22f8942591a84b6d2da7794cd057532 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 0000000000000000000000000000000000000000..54adf90332c41c9393f34496ddfa4d3c88f45716 --- /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 0000000000000000000000000000000000000000..a82fb7a757cdeae6b908ef45acf690e236471a04 --- /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 0000000000000000000000000000000000000000..1f7575afee63ac72f7cdb1a48af051fcfba2559e --- /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 0000000000000000000000000000000000000000..e79830136fe5cfe513ae2660024e8fcb771f8783 --- /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 0000000000000000000000000000000000000000..bdd2128720f48821d78cf3b6ec527f04d6c0f191 --- /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 0000000000000000000000000000000000000000..354eff7beb45c8be3c0a0466d9508a177b155448 --- /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 0000000000000000000000000000000000000000..f3238d8ed64cd682e79addbd225d4c5b818ecae3 --- /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 0000000000000000000000000000000000000000..f295300199f43246fc5987652c11738cd65d8f52 --- /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 0000000000000000000000000000000000000000..981476c35e8ef2c14025faf2fc09523ef0890767 --- /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 0000000000000000000000000000000000000000..8c9389db76d59343b73f0eb859f95d483dbf74c7 --- /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 0000000000000000000000000000000000000000..1d2a2fb633aa8aa02405bc91cb1341d9a38c8888 --- /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 0000000000000000000000000000000000000000..0d3b9ef38f2e6fba883f33cdd697895c2920b28a --- /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 0000000000000000000000000000000000000000..08f939517381c99525db4ff1a75c7a5a4a4825e0 --- /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 0000000000000000000000000000000000000000..996640f9a35cb89c53545900b77c6fe0af6c4c3d --- /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 0000000000000000000000000000000000000000..f0130fe16f473c1e75fa76f2432395d16c819aa9 --- /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 0000000000000000000000000000000000000000..ad8d21c85ef99b7221179af0c7d61c0d19f220f7 --- /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 0000000000000000000000000000000000000000..5c78bde532f4f0b0d120c06372b0a8f576d525ea --- /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 0000000000000000000000000000000000000000..49fa32f6408041b25dc7d5b7e6785088346f738d --- /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 0000000000000000000000000000000000000000..1157365c254744de10da69f03ac01091602ccc1a --- /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 0000000000000000000000000000000000000000..67f6509fc236ddf7ce34fa0403af1ae73911d5f0 --- /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 0000000000000000000000000000000000000000..e3e997e608d88d8840356c1d8ecc0958b6ce55c7 --- /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 0000000000000000000000000000000000000000..c386ce70784db4c5aef2d43f08ca83f086b14e6f --- /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 0000000000000000000000000000000000000000..af70ed5413c60a335451f27734f381c9e0d35093 --- /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 0000000000000000000000000000000000000000..27373089008d40de3f5f2527592a98f9aef98f6d --- /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