pax_global_header00006660000000000000000000000064132452161530014514gustar00rootroot0000000000000052 comment=6916f52fe82f992d3e6b42ea67b9f9185fa9cdbf batctl-2018.0/000077500000000000000000000000001324521615300130575ustar00rootroot00000000000000batctl-2018.0/CHANGELOG.rst000066400000000000000000000033331324521615300151020ustar00rootroot00000000000000.. SPDX-License-Identifier: GPL-2.0 2018.0 (2018-02-26) =================== * synchronization of batman-adv netlink and packet headers * mark licenses clearer, change batman-adv UAPI header from ISC to MIT * coding style cleanups and refactoring 2017.4 (2017-12-05) =================== * synchronization of batman-adv netlink header * coding style cleanups and refactoring * documentation cleanup * bugs squashed: - improve error handling for libnl related errors - add checks for various allocation errors 2017.3 (2017-09-28) =================== * bugs squashed: - Fix error messages on traceroute send failures 2017.2 (2017-06-28) =================== * coding style cleanups and refactoring 2017.1 (2017-05-23) ==================== * (no changes) 2017.0 (2017-02-28) =================== * remove root check for read-only sysfs and rtnl functionality * coding style cleanups * bugs squashed: - fix check for root priviliges when started under modified effective uid 2016.5 (2016-12-15) =================== * reimplement traceroute/ping commands in userspace without debugfs * switch interface manipulation from (legacy) sysfs to rtnetlink * coding style cleanups 2016.4 (2016-10-27) =================== * integrate support for batman-adv netlink * coding style cleanups * documentation updates * bugs squashed: - fix endless loop in TP meter on some platforms - fix build errors caused by name conflicts 2016.3 (2016-09-01) =================== * synchronize common headers with batman-adv * support multicast logging and debug table * split tcpdump OGM packet filter in OGM and OGMv2 filter * add infrastructure to communicate with batadv netlink family * integrate command to control new kernel throughput meter batctl-2018.0/LICENSES/000077500000000000000000000000001324521615300142645ustar00rootroot00000000000000batctl-2018.0/LICENSES/preferred/000077500000000000000000000000001324521615300162425ustar00rootroot00000000000000batctl-2018.0/LICENSES/preferred/GPL-2.0000066400000000000000000000443061324521615300171130ustar00rootroot00000000000000Valid-License-Identifier: GPL-2.0 Valid-License-Identifier: GPL-2.0+ SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ License-Text: 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. Copyright (C) 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. batctl-2018.0/LICENSES/preferred/MIT000066400000000000000000000025341324521615300166220ustar00rootroot00000000000000Valid-License-Identifier: MIT SPDX-URL: https://spdx.org/licenses/MIT.html Usage-Guide: To use the MIT License put the following SPDX tag/value pair into a comment according to the placement guidelines in the licensing rules documentation: SPDX-License-Identifier: MIT License-Text: MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. batctl-2018.0/Makefile000077500000000000000000000074471324521615300145360ustar00rootroot00000000000000#!/usr/bin/make -f # SPDX-License-Identifier: GPL-2.0 # -*- makefile -*- # # Copyright (C) 2006-2018 B.A.T.M.A.N. contributors # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License 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 Street, Fifth Floor, Boston, MA # 02110-1301, USA # # License-Filename: LICENSES/preferred/GPL-2.0 # changing the CONFIG_* line to 'y' enables the related feature # batctl advanced debugging tool bisect: export CONFIG_BATCTL_BISECT=n # batctl build BINARY_NAME = batctl OBJ += bat-hosts.o OBJ += debugfs.o OBJ += debug.o OBJ += functions.o OBJ += genl.o OBJ += hash.o OBJ += icmp_helper.o OBJ += interface.o OBJ += ioctl.o OBJ += main.o OBJ += netlink.o OBJ += ping.o OBJ += sys.o OBJ += tcpdump.o OBJ += tp_meter.o OBJ += traceroute.o OBJ += translate.o OBJ_BISECT = bisect_iv.o MANPAGE = man/batctl.8 # batctl flags and options CFLAGS += -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP CPPFLAGS += -D_GNU_SOURCE LDLIBS += -lm -lrt # disable verbose output ifneq ($(findstring $(MAKEFLAGS),s),s) ifndef V Q_CC = @echo ' ' CC $@; Q_LD = @echo ' ' LD $@; export Q_CC export Q_LD endif endif ifeq ($(origin PKG_CONFIG), undefined) PKG_CONFIG = pkg-config ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),) $(error $(PKG_CONFIG) not found) endif endif ifeq ($(origin LIBNL_CFLAGS) $(origin LIBNL_LDLIBS), undefined undefined) LIBNL_NAME ?= libnl-3.0 ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_NAME) 2>/dev/null),) $(error No $(LIBNL_NAME) development libraries found!) endif LIBNL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_NAME)) LIBNL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBNL_NAME)) endif CFLAGS += $(LIBNL_CFLAGS) LDLIBS += $(LIBNL_LDLIBS) ifeq ($(origin LIBNL_GENL_CFLAGS) $(origin LIBNL_GENL_LDLIBS), undefined undefined) LIBNL_GENL_NAME ?= libnl-genl-3.0 ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_GENL_NAME) 2>/dev/null),) $(error No $(LIBNL_GENL_NAME) development libraries found!) endif LIBNL_GENL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_GENL_NAME)) LIBNL_GENL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBNL_GENL_NAME)) endif CFLAGS += $(LIBNL_GENL_CFLAGS) LDLIBS += $(LIBNL_GENL_LDLIBS) # standard build tools ifeq ($(CONFIG_BATCTL_BISECT),y) OBJ += $(OBJ_BISECT) CPPFLAGS += -DBATCTL_BISECT endif CC ?= gcc RM ?= rm -f INSTALL ?= install MKDIR ?= mkdir -p COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) # standard install paths PREFIX = /usr/local SBINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man # try to generate revision REVISION= $(shell if [ -d .git ]; then \ echo $$(git describe --always --dirty --match "v*" |sed 's/^v//' 2> /dev/null || echo "[unknown]"); \ fi) ifneq ($(REVISION),) CPPFLAGS += -DSOURCE_VERSION=\"$(REVISION)\" endif # default target all: $(BINARY_NAME) # standard build rules .SUFFIXES: .o .c .c.o: $(COMPILE.c) -o $@ $< $(BINARY_NAME): $(OBJ) $(LINK.o) $^ $(LDLIBS) -o $@ clean: $(RM) $(BINARY_NAME) $(OBJ) $(OBJ_BISECT) $(DEP) install: $(BINARY_NAME) $(MKDIR) $(DESTDIR)$(SBINDIR) $(MKDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 0755 $(BINARY_NAME) $(DESTDIR)$(SBINDIR) $(INSTALL) -m 0644 $(MANPAGE) $(DESTDIR)$(MANDIR)/man8 # load dependencies DEP = $(OBJ:.o=.d) $(OBJ_BISECT:.o=.d) -include $(DEP) .PHONY: all clean install batctl-2018.0/README.rst000066400000000000000000000604111324521615300145500ustar00rootroot00000000000000.. SPDX-License-Identifier: GPL-2.0 ========================================================== batctl - B.A.T.M.A.N. advanced control and management tool ========================================================== Introduction ============ Why do I need batctl ? B.A.T.M.A.N. advanced operates on layer 2 and thus all hosts participating in the virtual switch are completely transparent for all protocols above layer 2. Therefore the common diagnosis tools do not work as expected. To overcome these problems batctl was created. At the moment batctl contains ping, traceroute, tcpdump and interfaces to the kernel module settings. How does it work ? ================== batctl uses the debugfs/batman_adv/bat0/socket device provided by the B.A.T.M.A.N. advanced kernel module to inject custom icmp packets into the data flow. That's why ping and traceroute work almost like their IP based counterparts. Tcpdump was designed because B.A.T.M.A.N. advanced encapsulates all traffic within batman packets, so that the normal tcpdump would not recognize the packets. The bat-hosts file ================== This file is similar to the /etc/hosts file. You can write one MAC address and one host name per line. batctl will analyze the file to find the matching MAC address to your provided host name. Host names are much easier to remember than MAC addresses. ;) batctl statistics ================= The batman-adv kernel module maintains a number of traffic counters which are exported to user space. With batctl these counters can be easily retrieved. The output may vary depending on which features have been compiled into the kernel module. For example, if the distributed arp table (short: dat) wasn't selected as an option at compile time its counters won't be shown. Each module subsystem has its own counters which are indicated by their prefixes: mgmt: mesh protocol counters tt: translation table counters dat: distributed arp table counters All counters without a prefix concern payload (pure user data) traffic. Usage:: batctl statistics Example:: $ batctl statistics tx: 14 tx_bytes: 1316 tx_errors: 0 rx: 14 rx_bytes: 1316 forward: 0 forward_bytes: 0 mgmt_tx: 18 mgmt_tx_bytes: 762 mgmt_rx: 17 mgmt_rx_bytes: 1020 tt_request_tx: 0 tt_request_rx: 0 tt_response_tx: 0 tt_response_rx: 0 tt_roam_adv_tx: 0 tt_roam_adv_rx: 0 dat_request_tx: 0 dat_request_rx: 0 dat_reply_tx: 1 dat_reply_rx: 0 batctl translate ================ Translates a destination (hostname, IPv4, IPv6, MAC, bat_host-name) to the originator mac address responsible for it. Usage:: batctl translate mac|bat-host|host-name|IP-address Example:: $ batctl translate www.google.de 02:ca:fe:af:fe:01 $ batctl translate 02:ca:fe:af:fe:01 02:ca:fe:af:fe:01 $ batctl translate 192.168.1.2 02:ca:fe:af:fe:05 $ batctl translate fe:fe:00:00:09:01 02:ca:fe:af:fe:05 $ batctl translate 2001::1 02:ca:fe:af:fe:05 batctl ping =========== Sends a Layer 2 batman-adv ping to check round trip time and connectivity Usage:: batctl ping [parameters] mac|bat-host|host-name|IP-address parameters: -c ping packet count -h print this help -i interval in seconds -t timeout in seconds -T don't try to translate mac to originator address -R record route Example:: $ batctl ping fe:fe:00:00:09:01 PING fe:fe:00:00:09:01 (fe:fe:00:00:09:01) 19(47) bytes of data 19 bytes from fe:fe:00:00:09:01 icmp_seq=1 ttl=43 time=8.74 ms 19 bytes from fe:fe:00:00:09:01 icmp_seq=2 ttl=43 time=7.48 ms 19 bytes from fe:fe:00:00:09:01 icmp_seq=3 ttl=43 time=8.23 ms ^C--- fe:fe:00:00:09:01 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss rtt min/avg/max/mdev = 7.476/8.151/8.743/1.267 ms batctl traceroute ================= Traceroute sends 3 packets to each hop, awaits the answers and prints out the response times. Usage:: batctl traceroute [parameters] mac|bat-host|host-name|IP-address Example:: $ batctl traceroute fe:fe:00:00:09:01 traceroute to fe:fe:00:00:09:01 (fe:fe:00:00:09:01), 50 hops max, 19 byte packets 1: fe:fe:00:00:02:01 4.932 ms 2.338 ms 1.333 ms 2: fe:fe:00:00:03:01 6.860 ms 1.579 ms 1.260 ms 3: fe:fe:00:00:04:01 2.342 ms 1.547 ms 1.655 ms 4: fe:fe:00:00:05:01 2.906 ms 2.211 ms 2.253 ms 5: fe:fe:00:00:06:01 3.577 ms 2.687 ms 3.088 ms 6: fe:fe:00:00:07:01 4.217 ms 5.741 ms 3.551 ms 7: fe:fe:00:00:08:01 5.017 ms 5.547 ms 4.294 ms 8: fe:fe:00:00:09:01 5.730 ms 4.970 ms 6.437 ms batctl tcpdump ============== tcpdump layer 2 and/or layer 3 traffic on the given interface Usage:: batctl tcpdump [parameters] interface [interface] parameters: -c compat filter - only display packets matching own compat version (14) -h print this help -n don't convert addresses to bat-host names -p dump specific packet type -x dump all packet types except specified packet types: 1 - batman ogm packets 2 - batman icmp packets 4 - batman unicast packets 8 - batman broadcast packets 16 - batman unicast tvlv packets 32 - batman fragmented packets 64 - batman tt / roaming packets 128 - non batman packets 129 - batman ogm & non batman packets tcpdump supports standard interfaces as well as raw wifi interfaces running in monitor mode. Example output for tcpdump:: $ batctl tcpdump mesh0 01:51:42.401188 BAT kansas: OGM via neigh kansas, seqno 6718, tq 255, ttl 50, v 9, flags [..I], length 28 01:51:42.489735 BAT kansas: OGM via neigh wyoming, seqno 6718, tq 245, ttl 49, v 9, flags [.D.], length 28 01:51:42.510330 BAT wyoming: OGM via neigh wyoming, seqno 6721, tq 255, ttl 50, v 9, flags [..I], length 28 01:51:42.601092 BAT wyoming: OGM via neigh kansas, seqno 6721, tq 245, ttl 49, v 9, flags [.D.], length 28 01:51:43.361076 BAT kansas > wyoming: ICMP echo request, id 0, seq 1, ttl 1, v 9, length 19 01:51:43.365347 BAT wyoming > kansas: ICMP echo reply, id 0, seq 1, ttl 50, v 9, length 19 01:51:43.372224 BAT kansas > wyoming: ICMP echo request, id 0, seq 2, ttl 1, v 9, length 19 01:51:43.376506 BAT wyoming > kansas: ICMP echo reply, id 0, seq 2, ttl 50, v 9, length 19 01:51:43.381250 BAT kansas: OGM via neigh kansas, seqno 6719, tq 255, ttl 50, v 9, flags [..I], length 28 01:51:43.386281 BAT kansas > wyoming: ICMP echo request, id 0, seq 3, ttl 1, v 9, length 19 01:51:43.387910 BAT wyoming > kansas: ICMP echo reply, id 0, seq 3, ttl 50, v 9, length 19 01:51:43.479503 BAT kansas: OGM via neigh wyoming, seqno 6719, tq 245, ttl 49, v 9, flags [.D.], length 28 01:51:43.509899 BAT wyoming: OGM via neigh wyoming, seqno 6722, tq 255, ttl 50, v 9, flags [..I], length 28 01:51:43.600999 BAT wyoming: OGM via neigh kansas, seqno 6722, tq 245, ttl 49, v 9, flags [.D.], length 28 01:51:44.381064 BAT kansas: OGM via neigh kansas, seqno 6720, tq 255, ttl 50, v 9, flags [..I], length 28 batctl bisect_iv ================ Analyzes the B.A.T.M.A.N. IV logfiles to build a small internal database of all sent sequence numbers and routing table changes. This database can be used to search for routing loops (default action), to trace OGMs of a host (use "-t" to specify the mac address or bat-host name) throughout the network or to display routing tables of the nodes (use "-r" to specify the mac address or bat-host name). You can name a specific sequence number or a range using the "-s" option to limit the output's range. Furthermore you can filter the output by specifying an originator (use "-o" to specify the mac address or bat-host name) to only see data connected to this originator. If "-n" was given batctl will not replace the mac addresses with bat-host names in the output. Usage:: batctl bisect_iv [parameters] .. parameters: -h print this help -l run a loop detection of given mac address or bat-host (default) -n don't convert addresses to bat-host names -r print routing tables of given mac address or bat-host -s seqno range to limit the output -t trace seqnos of given mac address or bat-host Examples:: $ batctl bisect_iv log/* -l uml3 Analyzing routing tables of originator: uml3 [all sequence numbers] Checking host: uml3 Path towards uml7 (seqno 9 via neigh uml5): -> uml5 -> uml6 Path towards uml7 (seqno 10 via neigh uml4): -> uml4 -> uml5 -> uml6 Path towards uml6 (seqno 4 via neigh uml4): -> uml4 Path towards uml8 (seqno 12 via neigh uml4): -> uml4 -> uml5 -> uml6 -> uml7 Path towards uml8 (seqno 203 via neigh uml4): -> uml4 -> uml6 -> uml7 Path towards uml8 (seqno 391 via neigh uml2): -> uml2 -> uml3 -> uml2 aborted due to loop! Path towards uml8 (seqno 396 via neigh uml4): -> uml4 -> uml6 -> uml7 Path towards uml9 (seqno 10 via neigh uml5): -> uml5 -> uml6 -> uml7 -> uml9. Path towards uml9 (seqno 10 via neigh uml4): -> uml4 -> uml5 -> uml6 -> uml7 -> uml9. Path towards uml9 (seqno 11 via neigh uml4): -> uml4 -> uml6 -> uml7 -> uml8 -> uml9. Path towards uml9 (seqno 12 via neigh uml4): -> uml4 -> uml5 -> uml6 -> uml7 -> uml8 -> uml9. Path towards uml9 (seqno 21 via neigh uml5): -> uml5 -> uml6 -> uml7 -> uml8 -> uml9. Path towards uml9 (seqno 22 via neigh uml4): -> uml4 -> uml5 -> uml6 -> uml7 -> uml8 -> uml9. $ ./batctl bisect_iv -t uml3 log/* Sequence number flow of originator: uml3 [all sequence numbers] [...] +=> uml3 (seqno 19) |- uml2 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] | |- uml3 [tq: 154, ttl: 49, neigh: uml2, prev_sender: uml3] | \- uml1 [tq: 154, ttl: 49, neigh: uml2, prev_sender: uml3] | |- uml3 [tq: 51, ttl: 48, neigh: uml1, prev_sender: uml2] | \- uml2 [tq: 51, ttl: 48, neigh: uml1, prev_sender: uml2] |- uml5 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] | |- uml6 [tq: 33, ttl: 48, neigh: uml5, prev_sender: uml3] | | |- uml5 [tq: 11, ttl: 47, neigh: uml6, prev_sender: uml5] | | |- uml7 [tq: 11, ttl: 47, neigh: uml6, prev_sender: uml5] | | | |- uml8 [tq: 3, ttl: 46, neigh: uml7, prev_sender: uml6] | | | | |- uml6 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | | |- uml9 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | | \- uml7 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | |- uml6 [tq: 3, ttl: 46, neigh: uml7, prev_sender: uml6] | | | |- uml9 [tq: 3, ttl: 46, neigh: uml7, prev_sender: uml6] | | | \- uml5 [tq: 3, ttl: 46, neigh: uml7, prev_sender: uml6] | | \- uml4 [tq: 11, ttl: 47, neigh: uml6, prev_sender: uml5] | |- uml7 [tq: 33, ttl: 48, neigh: uml5, prev_sender: uml3] | \- uml4 [tq: 33, ttl: 48, neigh: uml5, prev_sender: uml3] \- uml4 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] |- uml3 [tq: 106, ttl: 49, neigh: uml4, prev_sender: uml3] |- uml6 [tq: 106, ttl: 49, neigh: uml4, prev_sender: uml3] |- uml2 [tq: 106, ttl: 49, neigh: uml4, prev_sender: uml3] \- uml5 [tq: 106, ttl: 49, neigh: uml4, prev_sender: uml3] +=> uml3 (seqno 20) |- uml2 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] | |- uml3 [tq: 160, ttl: 49, neigh: uml2, prev_sender: uml3] | |- uml1 [tq: 160, ttl: 49, neigh: uml2, prev_sender: uml3] | \- uml4 [tq: 160, ttl: 49, neigh: uml2, prev_sender: uml3] |- uml5 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] | |- uml3 [tq: 43, ttl: 48, neigh: uml5, prev_sender: uml3] | |- uml6 [tq: 43, ttl: 48, neigh: uml5, prev_sender: uml3] | | |- uml8 [tq: 16, ttl: 47, neigh: uml6, prev_sender: uml5] | | |- uml5 [tq: 16, ttl: 47, neigh: uml6, prev_sender: uml5] | | |- uml7 [tq: 16, ttl: 47, neigh: uml6, prev_sender: uml5] | | | |- uml8 [tq: 5, ttl: 46, neigh: uml7, prev_sender: uml6] | | | | |- uml6 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | | |- uml9 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | | \- uml7 [tq: 0, ttl: 45, neigh: uml8, prev_sender: uml7] | | | \- uml6 [tq: 5, ttl: 46, neigh: uml7, prev_sender: uml6] | | \- uml4 [tq: 16, ttl: 47, neigh: uml6, prev_sender: uml5] | \- uml4 [tq: 43, ttl: 48, neigh: uml5, prev_sender: uml3] |- uml1 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] | \- uml2 [tq: 49, ttl: 48, neigh: uml1, prev_sender: uml3] \- uml4 [tq: 255, ttl: 50, neigh: uml3, prev_sender: uml3] |- uml3 [tq: 114, ttl: 49, neigh: uml4, prev_sender: uml3] |- uml6 [tq: 114, ttl: 49, neigh: uml4, prev_sender: uml3] |- uml2 [tq: 114, ttl: 49, neigh: uml4, prev_sender: uml3] \- uml5 [tq: 114, ttl: 49, neigh: uml4, prev_sender: uml3] [...] batctl originators ================== Check the Originators table Usage:: batctl originators|o Example:: $ batctl originators [B.A.T.M.A.N. adv 2011.4.0, MainIF/MAC: eth0/fe:fe:00:00:01:01 (bat0)] Originator last-seen (#/255) Nexthop [outgoingIF]: Potential nexthops ... fe:fe:00:00:08:01 0.820s (194) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 65) fe:fe:00:00:02:01 (194) fe:fe:00:00:03:01 0.980s (245) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 81) fe:fe:00:00:02:01 (245) fe:fe:00:00:05:01 0.140s (221) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 76) fe:fe:00:00:02:01 (221) fe:fe:00:00:04:01 0.010s (235) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:02:01 (235) fe:fe:00:00:03:01 ( 81) fe:fe:00:00:09:01 0.830s (187) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 63) fe:fe:00:00:02:01 (187) fe:fe:00:00:06:01 0.830s (213) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 71) fe:fe:00:00:02:01 (213) fe:fe:00:00:02:01 0.240s (255) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 81) fe:fe:00:00:02:01 (255) fe:fe:00:00:07:01 0.670s (200) fe:fe:00:00:02:01 [ eth0]: fe:fe:00:00:03:01 ( 68) fe:fe:00:00:02:01 (200) Since 2014.1.0, each batman interface has an individual originator table as well which is only used for routing. These table explain to which neighbor a packet is forwarded when the packet is received on the specified interface. Example:: $ batctl originators -i eth0 [B.A.T.M.A.N. adv master-b82b9b2, IF/MAC: eth0/fe:f0:00:00:02:01 (bat0 BATMAN_IV)] Originator last-seen (#/255) Nexthop [outgoingIF]: Potential nexthops ... fe:f1:00:00:03:01 0.170s (255) fe:f1:00:00:03:01 [ eth1]: fe:f1:00:00:03:01 (255) fe:f1:00:00:01:01 0.510s (253) fe:f1:00:00:01:01 [ eth1]: fe:f1:00:00:01:01 (253) fe:f0:00:00:05:01 0.660s (222) fe:f1:00:00:03:01 [ eth1]: fe:f0:00:00:03:01 (198) fe:f1:00:00:03:01 (222) fe:f0:00:00:03:01 0.560s (252) fe:f1:00:00:03:01 [ eth1]: fe:f1:00:00:03:01 (252) fe:f0:00:00:03:01 (240) fe:f0:00:00:04:01 0.250s (240) fe:f1:00:00:03:01 [ eth1]: fe:f1:00:00:03:01 (240) fe:f0:00:00:03:01 (211) fe:f0:00:00:01:01 0.850s (255) fe:f1:00:00:01:01 [ eth1]: fe:f1:00:00:01:01 (255) fe:f0:00:00:01:01 (238) $ batctl originators -i eth1 [B.A.T.M.A.N. adv master-b82b9b2, IF/MAC: eth1/fe:f1:00:00:02:01 (bat0 BATMAN_IV)] Originator last-seen (#/255) Nexthop [outgoingIF]: Potential nexthops ... fe:f1:00:00:03:01 0.880s (240) fe:f1:00:00:03:01 [ eth1]: fe:f1:00:00:03:01 (240) fe:f1:00:00:01:01 0.250s (239) fe:f1:00:00:01:01 [ eth1]: fe:f1:00:00:01:01 (239) fe:f0:00:00:05:01 0.340s (211) fe:f1:00:00:03:01 [ eth1]: fe:f0:00:00:03:01 (210) fe:f1:00:00:03:01 (211) fe:f0:00:00:03:01 0.260s (253) fe:f0:00:00:03:01 [ eth0]: fe:f1:00:00:03:01 (240) fe:f0:00:00:03:01 (253) fe:f0:00:00:04:01 0.010s (225) fe:f0:00:00:03:01 [ eth0]: fe:f1:00:00:03:01 (224) fe:f0:00:00:03:01 (225) fe:f0:00:00:01:01 0.510s (255) fe:f0:00:00:01:01 [ eth0]: fe:f1:00:00:01:01 (240) fe:f0:00:00:01:01 (255) batctl interface ================ display or modify the interface settings Usage:: batctl interface|if [add|del iface(s)] Example:: $ batctl interface eth0: active batctl interval =============== display or modify the originator interval in ms Usage:: batctl orig_interval|it [interval] Example:: $ batctl interval 1000 batctl log ========== read the log produced by the kernel module Usage:: batctl log|l Example:: $ batctl log [ 400] Received BATMAN packet via NB: fe:fe:00:00:02:01 IF: eth0 [fe:fe:00:00:01:01] (from OG: fe:fe:00:00:01:01 via prev OG: fe:fe:00:00:01:01 seqno 670, tq 245, TTL 49, V 8, IDF 1) [ 400] Drop packet: originator packet from myself (via neighbour) [ 400] Received BATMAN packet via NB: fe:fe:00:00:02:01 IF: eth0 [fe:fe:00:00:01:01] (from OG: fe:fe:00:00:02:01 via prev OG: fe:fe:00:00:02:01 seqno 545, tq 255, TTL 50, V 8, IDF 0) [ 400] updating last_seqno: old 544, new 545 [ 400] bidirectional: orig = fe:fe:00:00:02:01 neigh = fe:fe:00:00:02:01 => own_bcast = 64, real recv = 64, local tq: 255, asym_penalty: 255, total tq: 255 [ 400] update_originator(): Searching and updating originator entry of received packet [ 400] Updating existing last-hop neighbour of originator [...] batctl loglevel =============== display or modify the log level Usage:: batctl loglevel|ll [level] Example:: $ batctl loglevel [x] all debug output disabled (none) [ ] messages related to routing / flooding / broadcasting (batman) [ ] messages related to route added / changed / deleted (routes) [ ] messages related to translation table operations (tt) [ ] messages related to bridge loop avoidance (bla) [ ] messages related to arp snooping and distributed arp table (dat) [ ] messages related to network coding (nc) [ ] messages related to multicast (mcast) batctl nc_nodes =============== display the neighbor nodes considered for network coded packets Usage:: batctl nc_nodes|nn Example:: Node: fe:fe:00:0a:01:01 Ingoing: fe:fe:00:0a:01:01 fe:fe:00:0a:02:01 Outgoing: fe:fe:00:0a:01:01 fe:fe:00:0a:02:01 Where Node: is the neighbor Ingoing: is the neighbors this neighbor can hear packets from Outgoing: is the neighbors that can hear packets from this neighbor batctl network_coding ===================== display or modify the network coding setting Usage:: batctl network_coding|nc [0|1] Note that network coding requires a working promiscuous mode on all interfaces. batctl multicast_mode ===================== display or modify the multicast mode setting Usage:: batctl multicast_mode|mm [0|1] batctl mcast_flags ================== display local and remote multicast flags Usage:: batctl mcast_flags|mf Example:: Multicast flags (own flags: [U46]) * Bridged [U] U * No IGMP/MLD Querier [4/6]: ./. * Shadowing IGMP/MLD Querier [4/6]: 4/6 ------------------------------------------- Originator Flags 02:04:64:a4:39:c1 [U..] 02:04:64:a4:39:c2 [U..] 02:04:64:a4:39:c3 [...] where Originator: the MAC address of the originating (primary interface) batman-adv node Flags: multicast flags of the according node U: wants all unsnoopable multicast traffic, meaning other nodes need to always forward any multicast traffic destined to ff02::1 or 224.0.0.0/24 to it 4: wants all IPv4 multicast traffic, meaning other nodes need to always forward any IPv4 multicast traffic to it 6: wants all IPv6 multicast traffic, meaning other nodes need to always forward any IPv6 multicast traffic to it If a node does not have multicast optimizations available (e.g. old batman-adv version or optimizations not compiled in), therefore not announcing any multicast tvlv/flags, a '-' will be displayed instead of '[...]'. batctl aggregation ================== display or modify the packet aggregation setting Usage:: batctl aggregation|ag [0|1] batctl isolation_mark ===================== display or modify the isolation mark. This value is used by Extended Isolation feature. Usage:: batctl isolation_mark|mark $value[/0x$mask] * Example 1: ``batctl mark 0x00000001/0xffffffff`` * Example 2: ``batctl mark 0x00040000/0xffff0000`` * Example 3: ``batctl mark 16`` * Example 4: ``batctl mark 0x0f`` batctl translocal ================= display the local translation table Usage:: batctl translocal|tl Example:: $ batctl translocal Locally retrieved addresses (from bat0) announced via TT (TTVN: 1): * fe:fe:00:00:01:01 [RPNXW] In particular, RPNXW are flags which have the following meanings: R/Roaming: this client moved to another node but it is still kept for consistency reasons until the next OGM is sent. P/noPurge: this client represents the local soft interface and will never be deleted. N/New: this client has recently been added but is not advertised in the mesh until the next OGM is sent (for consistency reasons). X/delete: this client has to be removed for some reason, but it is still kept for consistency reasons until the next OGM is sent. W/Wireless: this client is connected to the node through a wireless device. If any of the flags is not enabled, a '.' will substitute its symbol. batctl transglobal ================== display the global translation table Usage:: batctl transglobal|tg Example:: Globally announced TT entries received via the mesh bat0 Client (TTVN) Originator (Curr TTVN) Flags * fe:fe:00:00:01:01 ( 12) via fe:fe:00:00:01:02 ( 50) [RXW] where TTVN: is the translation-table-version-number which introduced this client Curr TTVN: is the translation-table-version-number currently advertised by the originator serving this client (different clients advertised by the same originator have the same Curr TTVN) Flags that mean: R/Roaming: this client moved to another node but it is still kept for consistency reasons until the next OGM is sent. X/delete: this client has to be removed for some reason, but it is still kept for consistency reasons until the next OGM is sent. W/Wireless: this client is connected to the node through a wireless device. If any of the flags is not enabled, a '.' will substitute its symbol. batctl dat_cache ================= display the local D.A.T. cache Usage:: batctl dat_cache|dc Example:: Distributed ARP Table (bat0): IPv4 MAC last-seen * 172.100.0.1 b6:9b:d0:ea:b1:13 0:00 where IPv4: is the IP address of a client in the mesh network MAC: is the MAC address associated to that IP last-seen: is the amount of time since last refresh of this entry batctl and network name spaces ============================== The batman-adv kernel module is netns aware. Mesh instances can be created in name spaces, and interfaces in that name space added to the mesh. The mesh interface cannot be moved between name spaces, as is typical for virtual interfaces. The following example creates two network namespaces, and uses veth pairs to connect them together into a mesh of three nodes:: EMU1="ip netns exec emu1" EMU2="ip netns exec emu2" ip netns add emu1 ip netns add emu2 ip link add emu1-veth1 type veth peer name emu2-veth1 ip link set emu1-veth1 netns emu1 ip link set emu2-veth1 netns emu2 $EMU1 ip link set emu1-veth1 name veth1 $EMU2 ip link set emu2-veth1 name veth1 $EMU1 ip link set veth1 up $EMU2 ip link set veth1 up ip link add emu1-veth2 type veth peer name veth2 ip link set emu1-veth2 netns emu1 $EMU1 ip link set emu1-veth2 name veth2 $EMU1 ip link set veth2 up ip link set veth2 up $EMU1 batctl if add veth1 $EMU1 batctl if add veth2 $EMU1 ip link set bat0 up $EMU2 batctl if add veth1 $EMU2 ip link set bat0 up batctl if add veth2 ip link set bat0 up alfred and batadv-vis can also be used with name spaces. In this example, only netns has been used, so there are no filesystem name spaces. Hence the unix domain socket used by alfred needs to be given a unique name per instance:: ($EMU1 alfred -m -i bat0 -u /var/run/emu1-alfred.soc) & ($EMU2 alfred -m -i bat0 -u /var/run/emu2-alfred.soc) & alfred -m -i bat0 & ($EMU1 batadv-vis -s -u /var/run/emu1-alfred.soc) & ($EMU2 batadv-vis -s -u /var/run/emu2-alfred.soc) & batadv-vis -s & batctl-2018.0/allocate.h000066400000000000000000000020711324521615300150140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_ALLOCATE_H #define _BATCTL_ALLOCATE_H /* debug allocate wrapper to keep hash.c happy */ #include #define debugMalloc(length, tag) malloc(length) #define debugFree(mem, tag) free(mem) #endif batctl-2018.0/bat-hosts.c000066400000000000000000000145701324521615300151360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include "bat-hosts.h" #include "hash.h" #include "functions.h" static struct hashtable_t *host_hash = NULL; const char *bat_hosts_path[3] = {"/etc/bat-hosts", "~/bat-hosts", "bat-hosts"}; static int compare_mac(void *data1, void *data2) { return (memcmp(data1, data2, sizeof(struct ether_addr)) == 0 ? 1 : 0); } static int choose_mac(void *data, int32_t size) { unsigned char *key= data; uint32_t hash = 0, m_size = sizeof(struct ether_addr); size_t i; for (i = 0; i < m_size; i++) { hash += key[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return (hash % size); } static void parse_hosts_file(struct hashtable_t **hash, const char path[], int read_opt) { FILE *fd; char *line_ptr = NULL; char name[HOST_NAME_MAX_LEN], mac_str[18]; struct ether_addr *mac_addr; struct bat_host *bat_host; struct hashtable_t *swaphash; size_t len = 0; name[0] = mac_str[0] = '\0'; fd = fopen(path, "r"); if (!fd) return; while (getline(&line_ptr, &len, fd) != -1) { /* ignore empty lines and comments */ if ((line_ptr[0] == '\n') || (line_ptr[0] == '#')) continue; if (sscanf(line_ptr, "%17[^ \t]%49s\n", mac_str, name) != 2) { if (read_opt & USE_BAT_HOSTS) fprintf(stderr, "Warning - unrecognized bat-host definition: %s", line_ptr); continue; } mac_addr = ether_aton(mac_str); if (!mac_addr) { if (read_opt & USE_BAT_HOSTS) fprintf(stderr, "Warning - invalid mac address in '%s' detected: %s\n", path, mac_str); continue; } bat_host = bat_hosts_find_by_mac((char *)mac_addr); /* mac entry already exists - we found a new name for it */ if (bat_host) { /* if the mac addresses and the names are the same we can safely ignore the entry */ if (strcmp(bat_host->name, name) == 0) continue; if (read_opt & USE_BAT_HOSTS) fprintf(stderr, "Warning - mac already known (changing name from '%s' to '%s'): %s\n", bat_host->name, name, mac_str); strncpy(bat_host->name, name, HOST_NAME_MAX_LEN); bat_host->name[HOST_NAME_MAX_LEN - 1] = '\0'; continue; } bat_host = bat_hosts_find_by_name(name); /* name entry already exists - we found a new mac address for it */ if (bat_host) { if (read_opt & USE_BAT_HOSTS) fprintf(stderr, "Warning - name already known (changing mac from '%s' to '%s'): %s\n", ether_ntoa(&bat_host->mac_addr), mac_str, name); hash_remove(*hash, bat_host); free(bat_host); } bat_host = malloc(sizeof(struct bat_host)); if (!bat_host) { if (read_opt & USE_BAT_HOSTS) perror("Error - could not allocate memory"); goto out; } memcpy(&bat_host->mac_addr, mac_addr, sizeof(struct ether_addr)); strncpy(bat_host->name, name, HOST_NAME_MAX_LEN); bat_host->name[HOST_NAME_MAX_LEN - 1] = '\0'; hash_add(*hash, bat_host); if ((*hash)->elements * 4 > (*hash)->size) { swaphash = hash_resize((*hash), (*hash)->size * 2); if (swaphash) *hash = swaphash; else if (read_opt & USE_BAT_HOSTS) fprintf(stderr, "Warning - couldn't resize bat hosts hash table\n"); } } out: if (fd) fclose(fd); if (line_ptr) free(line_ptr); return; } void bat_hosts_init(int read_opt) { unsigned int i, j, parse; char confdir[CONF_DIR_LEN]; char *homedir; size_t locations = sizeof(bat_hosts_path) / sizeof(char *); char *normalized; /*** * realpath could allocate the memory for us but some embedded libc * implementations seem to expect a buffer as second argument */ normalized = malloc(locations * PATH_MAX); if (!normalized) { if (read_opt & USE_BAT_HOSTS) printf("Warning - could not get memory for bat-hosts file parsing\n"); return; } memset(normalized, 0, locations * PATH_MAX); host_hash = hash_new(64, compare_mac, choose_mac); if (!host_hash) { if (read_opt & USE_BAT_HOSTS) printf("Warning - could not create bat hosts hash table\n"); goto out; } homedir = getenv("HOME"); for (i = 0; i < locations; i++) { strcpy(confdir, ""); if (strlen(bat_hosts_path[i]) >= 2 && bat_hosts_path[i][0] == '~' && bat_hosts_path[i][1] == '/') { if (!homedir) continue; snprintf(confdir, CONF_DIR_LEN, "%s%s", homedir, &bat_hosts_path[i][1]); } else { strncpy(confdir, bat_hosts_path[i], CONF_DIR_LEN); confdir[CONF_DIR_LEN - 1] = '\0'; } if (!realpath(confdir, normalized + (i * PATH_MAX))) continue; /* check for duplicates: don't parse the same file twice */ parse = 1; for (j = 0; j < i; j++) { if (strncmp(normalized + (i * PATH_MAX), normalized + (j * PATH_MAX), CONF_DIR_LEN) == 0) { parse = 0; break; } } if (parse) parse_hosts_file(&host_hash, normalized + (i * PATH_MAX), read_opt); } out: free(normalized); } struct bat_host *bat_hosts_find_by_name(char *name) { struct hash_it_t *hashit = NULL; struct bat_host *bat_host = NULL, *tmp_bat_host; if (!host_hash) return NULL; while (NULL != (hashit = hash_iterate(host_hash, hashit))) { tmp_bat_host = (struct bat_host *)hashit->bucket->data; if (strncmp(tmp_bat_host->name, name, HOST_NAME_MAX_LEN - 1) == 0) bat_host = tmp_bat_host; } return bat_host; } struct bat_host *bat_hosts_find_by_mac(char *mac) { if (!host_hash) return NULL; return (struct bat_host *)hash_find(host_hash, mac); } static void bat_host_free(void *data) { free(data); } void bat_hosts_free(void) { if (host_hash) hash_delete(host_hash, bat_host_free); } batctl-2018.0/bat-hosts.h000066400000000000000000000024011324521615300151310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_BAT_HOSTS_H #define _BATCTL_BAT_HOSTS_H #include #define HOST_NAME_MAX_LEN 50 #define CONF_DIR_LEN 256 struct bat_host { struct ether_addr mac_addr; char name[HOST_NAME_MAX_LEN]; } __attribute__((packed)); void bat_hosts_init(int read_opt); struct bat_host *bat_hosts_find_by_name(char *name); struct bat_host *bat_hosts_find_by_mac(char *mac); void bat_hosts_free(void); #endif batctl-2018.0/bat-hosts.sample000066400000000000000000000001541324521615300161660ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0 # License-Filename: LICENSES/preferred/GPL-2.0 0:d2:58:ca:91:e8 example batctl-2018.0/batadv_packet.h000066400000000000000000000505061324521615300160260ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) */ /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 . */ #ifndef _UAPI_LINUX_BATADV_PACKET_H_ #define _UAPI_LINUX_BATADV_PACKET_H_ #include #include #include /** * batadv_tp_is_error() - Check throughput meter return code for error * @n: throughput meter return code * * Return: 0 when not error was detected, != 0 otherwise */ #define batadv_tp_is_error(n) ((__u8)(n) > 127 ? 1 : 0) /** * enum batadv_packettype - types for batman-adv encapsulated packets * @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV * @BATADV_BCAST: broadcast packets carrying broadcast payload * @BATADV_CODED: network coded packets * @BATADV_ELP: echo location packets for B.A.T.M.A.N. V * @BATADV_OGM2: originator messages for B.A.T.M.A.N. V * * @BATADV_UNICAST: unicast packets carrying unicast payload traffic * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original * payload packet * @BATADV_UNICAST_4ADDR: unicast packet including the originator address of * the sender * @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute * @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers */ enum batadv_packettype { /* 0x00 - 0x3f: local packets or special rules for handling */ BATADV_IV_OGM = 0x00, BATADV_BCAST = 0x01, BATADV_CODED = 0x02, BATADV_ELP = 0x03, BATADV_OGM2 = 0x04, /* 0x40 - 0x7f: unicast */ #define BATADV_UNICAST_MIN 0x40 BATADV_UNICAST = 0x40, BATADV_UNICAST_FRAG = 0x41, BATADV_UNICAST_4ADDR = 0x42, BATADV_ICMP = 0x43, BATADV_UNICAST_TVLV = 0x44, #define BATADV_UNICAST_MAX 0x7f /* 0x80 - 0xff: reserved */ }; /** * enum batadv_subtype - packet subtype for unicast4addr * @BATADV_P_DATA: user payload * @BATADV_P_DAT_DHT_GET: DHT request message * @BATADV_P_DAT_DHT_PUT: DHT store message * @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT */ enum batadv_subtype { BATADV_P_DATA = 0x01, BATADV_P_DAT_DHT_GET = 0x02, BATADV_P_DAT_DHT_PUT = 0x03, BATADV_P_DAT_CACHE_REPLY = 0x04, }; /* this file is included by batctl which needs these defines */ #define BATADV_COMPAT_VERSION 15 /** * enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets * @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was * previously received from someone else than the best neighbor. * @BATADV_PRIMARIES_FIRST_HOP: flag unused. * @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a * one hop neighbor on the interface where it was originally received. */ enum batadv_iv_flags { BATADV_NOT_BEST_NEXT_HOP = 1UL << 0, BATADV_PRIMARIES_FIRST_HOP = 1UL << 1, BATADV_DIRECTLINK = 1UL << 2, }; /** * enum batadv_icmp_packettype - ICMP message types * @BATADV_ECHO_REPLY: success reply to BATADV_ECHO_REQUEST * @BATADV_DESTINATION_UNREACHABLE: failure when route to destination not found * @BATADV_ECHO_REQUEST: request BATADV_ECHO_REPLY from destination * @BATADV_TTL_EXCEEDED: error after BATADV_ECHO_REQUEST traversed too many hops * @BATADV_PARAMETER_PROBLEM: return code for malformed messages * @BATADV_TP: throughput meter packet */ enum batadv_icmp_packettype { BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3, BATADV_ECHO_REQUEST = 8, BATADV_TTL_EXCEEDED = 11, BATADV_PARAMETER_PROBLEM = 12, BATADV_TP = 15, }; /** * enum batadv_mcast_flags - flags for multicast capabilities and settings * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for * 224.0.0.0/24 or ff02::1 * @BATADV_MCAST_WANT_ALL_IPV4: we want all IPv4 multicast packets * @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets */ enum batadv_mcast_flags { BATADV_MCAST_WANT_ALL_UNSNOOPABLES = 1UL << 0, BATADV_MCAST_WANT_ALL_IPV4 = 1UL << 1, BATADV_MCAST_WANT_ALL_IPV6 = 1UL << 2, }; /* tt data subtypes */ #define BATADV_TT_DATA_TYPE_MASK 0x0F /** * enum batadv_tt_data_flags - flags for tt data tvlv * @BATADV_TT_OGM_DIFF: TT diff propagated through OGM * @BATADV_TT_REQUEST: TT request message * @BATADV_TT_RESPONSE: TT response message * @BATADV_TT_FULL_TABLE: contains full table to replace existing table */ enum batadv_tt_data_flags { BATADV_TT_OGM_DIFF = 1UL << 0, BATADV_TT_REQUEST = 1UL << 1, BATADV_TT_RESPONSE = 1UL << 2, BATADV_TT_FULL_TABLE = 1UL << 4, }; /** * enum batadv_vlan_flags - flags for the four MSB of any vlan ID field * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not */ enum batadv_vlan_flags { BATADV_VLAN_HAS_TAG = 1UL << 15, }; /** * enum batadv_bla_claimframe - claim frame types for the bridge loop avoidance * @BATADV_CLAIM_TYPE_CLAIM: claim of a client mac address * @BATADV_CLAIM_TYPE_UNCLAIM: unclaim of a client mac address * @BATADV_CLAIM_TYPE_ANNOUNCE: announcement of backbone with current crc * @BATADV_CLAIM_TYPE_REQUEST: request of full claim table * @BATADV_CLAIM_TYPE_LOOPDETECT: mesh-traversing loop detect packet */ enum batadv_bla_claimframe { BATADV_CLAIM_TYPE_CLAIM = 0x00, BATADV_CLAIM_TYPE_UNCLAIM = 0x01, BATADV_CLAIM_TYPE_ANNOUNCE = 0x02, BATADV_CLAIM_TYPE_REQUEST = 0x03, BATADV_CLAIM_TYPE_LOOPDETECT = 0x04, }; /** * enum batadv_tvlv_type - tvlv type definitions * @BATADV_TVLV_GW: gateway tvlv * @BATADV_TVLV_DAT: distributed arp table tvlv * @BATADV_TVLV_NC: network coding tvlv * @BATADV_TVLV_TT: translation table tvlv * @BATADV_TVLV_ROAM: roaming advertisement tvlv * @BATADV_TVLV_MCAST: multicast capability tvlv */ enum batadv_tvlv_type { BATADV_TVLV_GW = 0x01, BATADV_TVLV_DAT = 0x02, BATADV_TVLV_NC = 0x03, BATADV_TVLV_TT = 0x04, BATADV_TVLV_ROAM = 0x05, BATADV_TVLV_MCAST = 0x06, }; #pragma pack(2) /* the destination hardware field in the ARP frame is used to * transport the claim type and the group id */ struct batadv_bla_claim_dst { __u8 magic[3]; /* FF:43:05 */ __u8 type; /* bla_claimframe */ __be16 group; /* group id */ }; #pragma pack() /** * struct batadv_ogm_packet - ogm (routing protocol) packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @flags: contains routing relevant flags - see enum batadv_iv_flags * @seqno: sequence identification * @orig: address of the source node * @prev_sender: address of the previous sender * @reserved: reserved byte for alignment * @tq: transmission quality * @tvlv_len: length of tvlv data following the ogm header */ struct batadv_ogm_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 flags; __be32 seqno; __u8 orig[ETH_ALEN]; __u8 prev_sender[ETH_ALEN]; __u8 reserved; __u8 tq; __be16 tvlv_len; /* __packed is not needed as the struct size is divisible by 4, * and the largest data type in this struct has a size of 4. */ }; #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet) /** * struct batadv_ogm2_packet - ogm2 (routing protocol) packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the general header * @ttl: time to live for this packet, part of the general header * @flags: reseved for routing relevant flags - currently always 0 * @seqno: sequence number * @orig: originator mac address * @tvlv_len: length of the appended tvlv buffer (in bytes) * @throughput: the currently flooded path throughput */ struct batadv_ogm2_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 flags; __be32 seqno; __u8 orig[ETH_ALEN]; __be16 tvlv_len; __be32 throughput; /* __packed is not needed as the struct size is divisible by 4, * and the largest data type in this struct has a size of 4. */ }; #define BATADV_OGM2_HLEN sizeof(struct batadv_ogm2_packet) /** * struct batadv_elp_packet - elp (neighbor discovery) packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @orig: originator mac address * @seqno: sequence number * @elp_interval: currently used ELP sending interval in ms */ struct batadv_elp_packet { __u8 packet_type; __u8 version; __u8 orig[ETH_ALEN]; __be32 seqno; __be32 elp_interval; }; #define BATADV_ELP_HLEN sizeof(struct batadv_elp_packet) /** * struct batadv_icmp_header - common members among all the ICMP packets * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @align: not used - useful for alignment purposes only * * This structure is used for ICMP packets parsing only and it is never sent * over the wire. The alignment field at the end is there to ensure that * members are padded the same way as they are in real packets. */ struct batadv_icmp_header { __u8 packet_type; __u8 version; __u8 ttl; __u8 msg_type; /* see ICMP message types above */ __u8 dst[ETH_ALEN]; __u8 orig[ETH_ALEN]; __u8 uid; __u8 align[3]; }; /** * struct batadv_icmp_packet - ICMP packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @reserved: not used - useful for alignment * @seqno: ICMP sequence number */ struct batadv_icmp_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 msg_type; /* see ICMP message types above */ __u8 dst[ETH_ALEN]; __u8 orig[ETH_ALEN]; __u8 uid; __u8 reserved; __be16 seqno; }; /** * struct batadv_icmp_tp_packet - ICMP TP Meter packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @subtype: TP packet subtype (see batadv_icmp_tp_subtype) * @session: TP session identifier * @seqno: the TP sequence number * @timestamp: time when the packet has been sent. This value is filled in a * TP_MSG and echoed back in the next TP_ACK so that the sender can compute the * RTT. Since it is read only by the host which wrote it, there is no need to * store it using network order */ struct batadv_icmp_tp_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 msg_type; /* see ICMP message types above */ __u8 dst[ETH_ALEN]; __u8 orig[ETH_ALEN]; __u8 uid; __u8 subtype; __u8 session[2]; __be32 seqno; __be32 timestamp; }; /** * enum batadv_icmp_tp_subtype - ICMP TP Meter packet subtypes * @BATADV_TP_MSG: Msg from sender to receiver * @BATADV_TP_ACK: acknowledgment from receiver to sender */ enum batadv_icmp_tp_subtype { BATADV_TP_MSG = 0, BATADV_TP_ACK, }; #define BATADV_RR_LEN 16 /** * struct batadv_icmp_packet_rr - ICMP RouteRecord packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @msg_type: ICMP packet type * @dst: address of the destination node * @orig: address of the source node * @uid: local ICMP socket identifier * @rr_cur: number of entries the rr array * @seqno: ICMP sequence number * @rr: route record array */ struct batadv_icmp_packet_rr { __u8 packet_type; __u8 version; __u8 ttl; __u8 msg_type; /* see ICMP message types above */ __u8 dst[ETH_ALEN]; __u8 orig[ETH_ALEN]; __u8 uid; __u8 rr_cur; __be16 seqno; __u8 rr[BATADV_RR_LEN][ETH_ALEN]; }; #define BATADV_ICMP_MAX_PACKET_SIZE sizeof(struct batadv_icmp_packet_rr) /* All packet headers in front of an ethernet header have to be completely * divisible by 2 but not by 4 to make the payload after the ethernet * header again 4 bytes boundary aligned. * * A packing of 2 is necessary to avoid extra padding at the end of the struct * caused by a structure member which is larger than two bytes. Otherwise * the structure would not fulfill the previously mentioned rule to avoid the * misalignment of the payload after the ethernet header. It may also lead to * leakage of information when the padding it not initialized before sending. */ #pragma pack(2) /** * struct batadv_unicast_packet - unicast packet for network payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @ttvn: translation table version number * @dest: originator destination of the unicast packet */ struct batadv_unicast_packet { __u8 packet_type; __u8 version; __u8 ttl; __u8 ttvn; /* destination translation table version number */ __u8 dest[ETH_ALEN]; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_unicast_4addr_packet - extended unicast packet * @u: common unicast packet header * @src: address of the source * @subtype: packet subtype * @reserved: reserved byte for alignment */ struct batadv_unicast_4addr_packet { struct batadv_unicast_packet u; __u8 src[ETH_ALEN]; __u8 subtype; __u8 reserved; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_frag_packet - fragmented packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @dest: final destination used when routing fragments * @orig: originator of the fragment used when merging the packet * @no: fragment number within this sequence * @priority: priority of frame, from ToS IP precedence or 802.1p * @reserved: reserved byte for alignment * @seqno: sequence identification * @total_size: size of the merged packet */ struct batadv_frag_packet { __u8 packet_type; __u8 version; /* batman version field */ __u8 ttl; #if defined(__BIG_ENDIAN_BITFIELD) __u8 no:4; __u8 priority:3; __u8 reserved:1; #elif defined(__LITTLE_ENDIAN_BITFIELD) __u8 reserved:1; __u8 priority:3; __u8 no:4; #else #error "unknown bitfield endianness" #endif __u8 dest[ETH_ALEN]; __u8 orig[ETH_ALEN]; __be16 seqno; __be16 total_size; }; /** * struct batadv_bcast_packet - broadcast packet for network payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @reserved: reserved byte for alignment * @seqno: sequence identification * @orig: originator of the broadcast packet */ struct batadv_bcast_packet { __u8 packet_type; __u8 version; /* batman version field */ __u8 ttl; __u8 reserved; __be32 seqno; __u8 orig[ETH_ALEN]; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; /** * struct batadv_coded_packet - network coded packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @first_source: original source of first included packet * @first_orig_dest: original destinal of first included packet * @first_crc: checksum of first included packet * @first_ttvn: tt-version number of first included packet * @second_ttl: ttl of second packet * @second_dest: second receiver of this coded packet * @second_source: original source of second included packet * @second_orig_dest: original destination of second included packet * @second_crc: checksum of second included packet * @second_ttvn: tt version number of second included packet * @coded_len: length of network coded part of the payload */ struct batadv_coded_packet { __u8 packet_type; __u8 version; /* batman version field */ __u8 ttl; __u8 first_ttvn; /* __u8 first_dest[ETH_ALEN]; - saved in mac header destination */ __u8 first_source[ETH_ALEN]; __u8 first_orig_dest[ETH_ALEN]; __be32 first_crc; __u8 second_ttl; __u8 second_ttvn; __u8 second_dest[ETH_ALEN]; __u8 second_source[ETH_ALEN]; __u8 second_orig_dest[ETH_ALEN]; __be32 second_crc; __be16 coded_len; }; #pragma pack() /** * struct batadv_unicast_tvlv_packet - generic unicast packet with tvlv payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header * @ttl: time to live for this packet, part of the genereal header * @reserved: reserved field (for packet alignment) * @src: address of the source * @dst: address of the destination * @tvlv_len: length of tvlv data following the unicast tvlv header * @align: 2 bytes to align the header to a 4 byte boundary */ struct batadv_unicast_tvlv_packet { __u8 packet_type; __u8 version; /* batman version field */ __u8 ttl; __u8 reserved; __u8 dst[ETH_ALEN]; __u8 src[ETH_ALEN]; __be16 tvlv_len; __u16 align; }; /** * struct batadv_tvlv_hdr - base tvlv header struct * @type: tvlv container type (see batadv_tvlv_type) * @version: tvlv container version * @len: tvlv container length */ struct batadv_tvlv_hdr { __u8 type; __u8 version; __be16 len; }; /** * struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv * container * @bandwidth_down: advertised uplink download bandwidth * @bandwidth_up: advertised uplink upload bandwidth */ struct batadv_tvlv_gateway_data { __be32 bandwidth_down; __be32 bandwidth_up; }; /** * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container * @flags: translation table flags (see batadv_tt_data_flags) * @ttvn: translation table version number * @num_vlan: number of announced VLANs. In the TVLV this struct is followed by * one batadv_tvlv_tt_vlan_data object per announced vlan */ struct batadv_tvlv_tt_data { __u8 flags; __u8 ttvn; __be16 num_vlan; }; /** * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through * the tt tvlv container * @crc: crc32 checksum of the entries belonging to this vlan * @vid: vlan identifier * @reserved: unused, useful for alignment purposes */ struct batadv_tvlv_tt_vlan_data { __be32 crc; __be16 vid; __u16 reserved; }; /** * struct batadv_tvlv_tt_change - translation table diff data * @flags: status indicators concerning the non-mesh client (see * batadv_tt_client_flags) * @reserved: reserved field - useful for alignment purposes only * @addr: mac address of non-mesh client that triggered this tt change * @vid: VLAN identifier */ struct batadv_tvlv_tt_change { __u8 flags; __u8 reserved[3]; __u8 addr[ETH_ALEN]; __be16 vid; }; /** * struct batadv_tvlv_roam_adv - roaming advertisement * @client: mac address of roaming client * @vid: VLAN identifier */ struct batadv_tvlv_roam_adv { __u8 client[ETH_ALEN]; __be16 vid; }; /** * struct batadv_tvlv_mcast_data - payload of a multicast tvlv * @flags: multicast flags announced by the orig node * @reserved: reserved field */ struct batadv_tvlv_mcast_data { __u8 flags; __u8 reserved[3]; }; #endif /* _UAPI_LINUX_BATADV_PACKET_H_ */ batctl-2018.0/batman_adv.h000066400000000000000000000231601324521615300153260ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* Copyright (C) 2016-2018 B.A.T.M.A.N. contributors: * * Matthias Schiffer * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _UAPI_LINUX_BATMAN_ADV_H_ #define _UAPI_LINUX_BATMAN_ADV_H_ #define BATADV_NL_NAME "batadv" #define BATADV_NL_MCAST_GROUP_TPMETER "tpmeter" /** * enum batadv_tt_client_flags - TT client specific flags * * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire. * Bits from 8 to 15 are called _local flags_ because they are used for local * computations only. * * Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with * the other nodes in the network. To achieve this goal these flags are included * in the TT CRC computation. */ enum batadv_tt_client_flags { /** * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table */ BATADV_TT_CLIENT_DEL = (1 << 0), /** * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and * the new update telling its new real location has not been * received/sent yet */ BATADV_TT_CLIENT_ROAM = (1 << 1), /** * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi * interface. This information is used by the "AP Isolation" feature */ BATADV_TT_CLIENT_WIFI = (1 << 4), /** * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This * information is used by the Extended Isolation feature */ BATADV_TT_CLIENT_ISOLA = (1 << 5), /** * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from * the table */ BATADV_TT_CLIENT_NOPURGE = (1 << 8), /** * @BATADV_TT_CLIENT_NEW: this client has been added to the local table * but has not been announced yet */ BATADV_TT_CLIENT_NEW = (1 << 9), /** * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it * is kept in the table for one more originator interval for consistency * purposes */ BATADV_TT_CLIENT_PENDING = (1 << 10), /** * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be * part of the network but no nnode has already announced it */ BATADV_TT_CLIENT_TEMP = (1 << 11), }; /** * enum batadv_nl_attrs - batman-adv netlink attributes */ enum batadv_nl_attrs { /** * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors */ BATADV_ATTR_UNSPEC, /** * @BATADV_ATTR_VERSION: batman-adv version string */ BATADV_ATTR_VERSION, /** * @BATADV_ATTR_ALGO_NAME: name of routing algorithm */ BATADV_ATTR_ALGO_NAME, /** * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface */ BATADV_ATTR_MESH_IFINDEX, /** * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface */ BATADV_ATTR_MESH_IFNAME, /** * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface */ BATADV_ATTR_MESH_ADDRESS, /** * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface */ BATADV_ATTR_HARD_IFINDEX, /** * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface */ BATADV_ATTR_HARD_IFNAME, /** * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv * interface */ BATADV_ATTR_HARD_ADDRESS, /** * @BATADV_ATTR_ORIG_ADDRESS: originator mac address */ BATADV_ATTR_ORIG_ADDRESS, /** * @BATADV_ATTR_TPMETER_RESULT: result of run (see * batadv_tp_meter_status) */ BATADV_ATTR_TPMETER_RESULT, /** * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took */ BATADV_ATTR_TPMETER_TEST_TIME, /** * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run */ BATADV_ATTR_TPMETER_BYTES, /** * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session */ BATADV_ATTR_TPMETER_COOKIE, /** * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment */ BATADV_ATTR_PAD, /** * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active */ BATADV_ATTR_ACTIVE, /** * @BATADV_ATTR_TT_ADDRESS: Client MAC address */ BATADV_ATTR_TT_ADDRESS, /** * @BATADV_ATTR_TT_TTVN: Translation table version */ BATADV_ATTR_TT_TTVN, /** * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version */ BATADV_ATTR_TT_LAST_TTVN, /** * @BATADV_ATTR_TT_CRC32: CRC32 over translation table */ BATADV_ATTR_TT_CRC32, /** * @BATADV_ATTR_TT_VID: VLAN ID */ BATADV_ATTR_TT_VID, /** * @BATADV_ATTR_TT_FLAGS: Translation table client flags */ BATADV_ATTR_TT_FLAGS, /** * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best */ BATADV_ATTR_FLAG_BEST, /** * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen */ BATADV_ATTR_LAST_SEEN_MSECS, /** * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address */ BATADV_ATTR_NEIGH_ADDRESS, /** * @BATADV_ATTR_TQ: TQ to neighbour */ BATADV_ATTR_TQ, /** * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour */ BATADV_ATTR_THROUGHPUT, /** * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth */ BATADV_ATTR_BANDWIDTH_UP, /** * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth */ BATADV_ATTR_BANDWIDTH_DOWN, /** * @BATADV_ATTR_ROUTER: Gateway router MAC address */ BATADV_ATTR_ROUTER, /** * @BATADV_ATTR_BLA_OWN: Flag indicating own originator */ BATADV_ATTR_BLA_OWN, /** * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address */ BATADV_ATTR_BLA_ADDRESS, /** * @BATADV_ATTR_BLA_VID: BLA VLAN ID */ BATADV_ATTR_BLA_VID, /** * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address */ BATADV_ATTR_BLA_BACKBONE, /** * @BATADV_ATTR_BLA_CRC: BLA CRC */ BATADV_ATTR_BLA_CRC, /* add attributes above here, update the policy in netlink.c */ /** * @__BATADV_ATTR_AFTER_LAST: internal use */ __BATADV_ATTR_AFTER_LAST, /** * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available */ NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST, /** * @BATADV_ATTR_MAX: highest attribute number currently defined */ BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1 }; /** * enum batadv_nl_commands - supported batman-adv netlink commands */ enum batadv_nl_commands { /** * @BATADV_CMD_UNSPEC: unspecified command to catch errors */ BATADV_CMD_UNSPEC, /** * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv * device */ BATADV_CMD_GET_MESH_INFO, /** * @BATADV_CMD_TP_METER: Start a tp meter session */ BATADV_CMD_TP_METER, /** * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session */ BATADV_CMD_TP_METER_CANCEL, /** * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms. */ BATADV_CMD_GET_ROUTING_ALGOS, /** * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces */ BATADV_CMD_GET_HARDIFS, /** * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations */ BATADV_CMD_GET_TRANSTABLE_LOCAL, /** * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations */ BATADV_CMD_GET_TRANSTABLE_GLOBAL, /** * @BATADV_CMD_GET_ORIGINATORS: Query list of originators */ BATADV_CMD_GET_ORIGINATORS, /** * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours */ BATADV_CMD_GET_NEIGHBORS, /** * @BATADV_CMD_GET_GATEWAYS: Query list of gateways */ BATADV_CMD_GET_GATEWAYS, /** * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims */ BATADV_CMD_GET_BLA_CLAIM, /** * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance * backbones */ BATADV_CMD_GET_BLA_BACKBONE, /* add new commands above here */ /** * @__BATADV_CMD_AFTER_LAST: internal use */ __BATADV_CMD_AFTER_LAST, /** * @BATADV_CMD_MAX: highest used command number */ BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1 }; /** * enum batadv_tp_meter_reason - reason of a tp meter test run stop */ enum batadv_tp_meter_reason { /** * @BATADV_TP_REASON_COMPLETE: sender finished tp run */ BATADV_TP_REASON_COMPLETE = 3, /** * @BATADV_TP_REASON_CANCEL: sender was stopped during run */ BATADV_TP_REASON_CANCEL = 4, /* error status >= 128 */ /** * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or * didn't answer */ BATADV_TP_REASON_DST_UNREACHABLE = 128, /** * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit */ BATADV_TP_REASON_RESEND_LIMIT = 129, /** * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node * already ongoing */ BATADV_TP_REASON_ALREADY_ONGOING = 130, /** * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory */ BATADV_TP_REASON_MEMORY_ERROR = 131, /** * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface */ BATADV_TP_REASON_CANT_SEND = 132, /** * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions */ BATADV_TP_REASON_TOO_MANY = 133, }; #endif /* _UAPI_LINUX_BATMAN_ADV_H_ */ batctl-2018.0/bisect_iv.c000066400000000000000000001313061324521615300151760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include "bisect_iv.h" #include "bat-hosts.h" #include "hash.h" #include "functions.h" static struct hashtable_t *node_hash = NULL; static struct bat_node *curr_bat_node = NULL; static void bisect_iv_usage(void) { fprintf(stderr, "Usage: batctl bisect_iv [parameters] .. \n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -l run a loop detection of given mac address or bat-host (default)\n"); fprintf(stderr, " \t -n don't convert addresses to bat-host names\n"); fprintf(stderr, " \t -o only display orig events that affect given mac address or bat-host\n"); fprintf(stderr, " \t -r print routing tables of given mac address or bat-host\n"); fprintf(stderr, " \t -s seqno range to limit the output\n"); fprintf(stderr, " \t -t trace seqnos of given mac address or bat-host\n"); } static int compare_name(void *data1, void *data2) { return (memcmp(data1, data2, NAME_LEN) == 0 ? 1 : 0); } static int choose_name(void *data, int32_t size) { unsigned char *key= data; uint32_t hash = 0, m_size = NAME_LEN - 1; size_t i; for (i = 0; i < m_size; i++) { hash += key[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return (hash % size); } static struct bat_node *node_get(char *name) { struct bat_node *bat_node; if (!name) return NULL; bat_node = (struct bat_node *)hash_find(node_hash, name); if (bat_node) goto out; bat_node = malloc(sizeof(struct bat_node)); if (!bat_node) { fprintf(stderr, "Could not allocate memory for data structure (out of mem?) - skipping"); return NULL; } strncpy(bat_node->name, name, NAME_LEN); bat_node->name[NAME_LEN - 1] = '\0'; INIT_LIST_HEAD(&bat_node->orig_event_list); INIT_LIST_HEAD(&bat_node->rt_table_list); memset(bat_node->loop_magic, 0, sizeof(bat_node->loop_magic)); memset(bat_node->loop_magic2, 0, sizeof(bat_node->loop_magic2)); hash_add(node_hash, bat_node); out: return bat_node; } static struct orig_event *orig_event_new(struct bat_node *bat_node, struct bat_node *orig_node) { struct orig_event *orig_event; orig_event = malloc(sizeof(struct orig_event)); if (!orig_event) { fprintf(stderr, "Could not allocate memory for orig event structure (out of mem?) - skipping"); return NULL; } INIT_LIST_HEAD(&orig_event->event_list); INIT_LIST_HEAD(&orig_event->rt_hist_list); orig_event->orig_node = orig_node; list_add_tail(&orig_event->list, &bat_node->orig_event_list); return orig_event; } static struct orig_event *orig_event_get_by_name(struct bat_node *bat_node, char *orig) { struct bat_node *orig_node; struct orig_event *orig_event; if (!bat_node) return NULL; list_for_each_entry(orig_event, &bat_node->orig_event_list, list) { if (compare_name(orig_event->orig_node->name, orig)) return orig_event; } orig_node = node_get(orig); if (!orig_node) return NULL; return orig_event_new(bat_node, orig_node); } static struct orig_event *orig_event_get_by_ptr(struct bat_node *bat_node, struct bat_node *orig_node) { struct orig_event *orig_event; if (!bat_node) return NULL; list_for_each_entry(orig_event, &bat_node->orig_event_list, list) { if (orig_event->orig_node == orig_node) return orig_event; } return orig_event_new(bat_node, orig_node); } static void node_free(void *data) { struct orig_event *orig_event, *orig_event_tmp; struct seqno_event *seqno_event, *seqno_event_tmp; struct rt_table *rt_table, *rt_table_tmp; struct rt_hist *rt_hist, *rt_hist_tmp; struct bat_node *bat_node = (struct bat_node *)data; list_for_each_entry_safe(orig_event, orig_event_tmp, &bat_node->orig_event_list, list) { list_for_each_entry_safe(seqno_event, seqno_event_tmp, &orig_event->event_list, list) { list_del(&seqno_event->list); free(seqno_event); } list_for_each_entry_safe(rt_hist, rt_hist_tmp, &orig_event->rt_hist_list, list) { list_del(&rt_hist->list); free(rt_hist); } list_del(&orig_event->list); free(orig_event); } list_for_each_entry_safe(rt_table, rt_table_tmp, &bat_node->rt_table_list, list) { list_del(&rt_table->list); free(rt_table->entries); free(rt_table); } free(bat_node); } static int routing_table_new(char *orig, char *next_hop, char *old_next_hop, char rt_flag) { struct bat_node *next_hop_node; struct orig_event *orig_event; struct seqno_event *seqno_event; struct rt_table *rt_table, *prev_rt_table = NULL; struct rt_hist *rt_hist; int i, j = -1; if (!curr_bat_node) { fprintf(stderr, "Routing table change without preceding OGM - skipping"); goto err; } if (!orig) { fprintf(stderr, "Invalid originator found - skipping"); goto err; } if ((rt_flag != RT_FLAG_DELETE) && (!next_hop)) { fprintf(stderr, "Invalid next hop found - skipping"); goto err; } if ((rt_flag == RT_FLAG_UPDATE) && (!old_next_hop)) { fprintf(stderr, "Invalid old next hop found - skipping"); goto err; } next_hop_node = node_get(next_hop); if ((rt_flag != RT_FLAG_DELETE) && (!next_hop_node)) goto err; orig_event = orig_event_get_by_name(curr_bat_node, orig); if (!orig_event) goto err; if (list_empty(&orig_event->event_list)) { fprintf(stderr, "Routing table change without any preceding OGM of that originator - skipping"); goto err; } if (!compare_name(((struct seqno_event *)(orig_event->event_list.prev))->orig->name, orig)) { fprintf(stderr, "Routing table change does not match with last received OGM - skipping"); goto err; } rt_table = malloc(sizeof(struct rt_table)); if (!rt_table) { fprintf(stderr, "Could not allocate memory for routing table (out of mem?) - skipping"); goto err; } rt_hist = malloc(sizeof(struct rt_hist)); if (!rt_hist) { fprintf(stderr, "Could not allocate memory for routing history (out of mem?) - skipping"); goto table_free; } rt_table->num_entries = 1; rt_hist->prev_rt_hist = NULL; rt_hist->next_hop = next_hop_node; rt_hist->flags = rt_flag; memset(rt_hist->loop_magic, 0, sizeof(rt_hist->loop_magic)); if (!(list_empty(&orig_event->rt_hist_list))) rt_hist->prev_rt_hist = (struct rt_hist *)(orig_event->rt_hist_list.prev); if (!(list_empty(&curr_bat_node->rt_table_list))) prev_rt_table = (struct rt_table *)(curr_bat_node->rt_table_list.prev); switch (rt_flag) { case RT_FLAG_ADD: if (prev_rt_table) rt_table->num_entries = prev_rt_table->num_entries + 1; break; case RT_FLAG_UPDATE: if (prev_rt_table) { rt_table->num_entries = prev_rt_table->num_entries + 1; /* if we had that route already we just change the entry */ for (i = 0; i < prev_rt_table->num_entries; i++) { if (compare_name(orig, prev_rt_table->entries[i].orig)) { rt_table->num_entries = prev_rt_table->num_entries; break; } } } break; case RT_FLAG_DELETE: if (prev_rt_table) { rt_table->num_entries = prev_rt_table->num_entries + 1; /* if we had that route already we just change the entry */ for (i = 0; i < prev_rt_table->num_entries; i++) { if (compare_name(orig, prev_rt_table->entries[i].orig)) { rt_table->num_entries = prev_rt_table->num_entries; break; } } if (rt_table->num_entries != prev_rt_table->num_entries) { fprintf(stderr, "Found a delete entry of orig '%s' but no existing record - skipping", orig); goto rt_hist_free; } /** * we need to create a special seqno event as a timer instead * of an OGM triggered that event */ seqno_event = malloc(sizeof(struct seqno_event)); if (!seqno_event) { fprintf(stderr, "Could not allocate memory for delete seqno event (out of mem?) - skipping"); goto rt_hist_free; } seqno_event->orig = node_get(orig); seqno_event->neigh = NULL; seqno_event->prev_sender = NULL; seqno_event->seqno = -1; seqno_event->tq = -1; seqno_event->ttl = -1; seqno_event->rt_hist = NULL; list_add_tail(&seqno_event->list, &orig_event->event_list); } break; default: fprintf(stderr, "Unknown rt_flag received: %i - skipping", rt_flag); goto rt_hist_free; } rt_table->entries = malloc(sizeof(struct rt_entry) * rt_table->num_entries); if (!rt_table->entries) { fprintf(stderr, "Could not allocate memory for routing table entries (out of mem?) - skipping"); goto rt_hist_free; } if (prev_rt_table) { for (i = 0; i < prev_rt_table->num_entries; i++) { /* if we have a previously deleted item don't copy it over */ if (prev_rt_table->entries[i].flags == RT_FLAG_DELETE) { rt_table->num_entries--; continue; } /** * if we delete one item the entries are not in sync anymore, * therefore we need to counters: one for the old and one for * the new routing table */ j++; memcpy((char *)&rt_table->entries[j], (char *)&prev_rt_table->entries[i], sizeof(struct rt_entry)); if (compare_name(orig, rt_table->entries[j].orig)) { if (rt_flag != RT_FLAG_DELETE) rt_table->entries[j].next_hop = next_hop_node; rt_table->entries[j].flags = rt_flag; continue; } rt_table->entries[j].flags = 0; } } if ((rt_table->num_entries == 1) || (rt_table->num_entries != j + 1)) { i = rt_table->num_entries; strncpy(rt_table->entries[i - 1].orig, orig, NAME_LEN); rt_table->entries[i - 1].orig[NAME_LEN - 1] = '\0'; rt_table->entries[i - 1].next_hop = next_hop_node; rt_table->entries[i - 1].flags = rt_flag; } rt_table->rt_hist = rt_hist; rt_hist->seqno_event = (struct seqno_event *)(orig_event->event_list.prev); rt_hist->seqno_event->rt_hist = rt_hist; rt_hist->rt_table = rt_table; list_add_tail(&rt_table->list, &curr_bat_node->rt_table_list); list_add_tail(&rt_hist->list, &orig_event->rt_hist_list); return 1; rt_hist_free: free(rt_hist); table_free: free(rt_table); err: return 0; } static int seqno_event_new(char *iface_addr, char *orig, char *prev_sender, char *neigh, long long seqno, int tq, int ttl) { struct bat_node *orig_node, *neigh_node, *prev_sender_node; struct orig_event *orig_event; struct seqno_event *seqno_event; if (!iface_addr) { fprintf(stderr, "Invalid interface address found - skipping"); goto err; } if (!orig) { fprintf(stderr, "Invalid originator found - skipping"); goto err; } if (!neigh) { fprintf(stderr, "Invalid neighbor found - skipping"); goto err; } if ((seqno < 0) || (seqno > UINT32_MAX)) { fprintf(stderr, "Invalid sequence number found (%lli) - skipping", seqno); goto err; } if ((tq < 0) || (tq > UINT8_MAX)) { fprintf(stderr, "Invalid tq value found (%i) - skipping", tq); goto err; } if ((ttl < 0) || (ttl > UINT8_MAX)) { fprintf(stderr, "Invalid ttl value found (%i) - skipping", ttl); goto err; } curr_bat_node = node_get(iface_addr); if (!curr_bat_node) goto err; orig_node = node_get(orig); if (!orig_node) goto err; neigh_node = node_get(neigh); if (!neigh_node) goto err; prev_sender_node = node_get(prev_sender); if (!prev_sender_node) goto err; orig_event = orig_event_get_by_ptr(curr_bat_node, orig_node); if (!orig_event) goto err; seqno_event = malloc(sizeof(struct seqno_event)); if (!seqno_event) { fprintf(stderr, "Could not allocate memory for seqno event (out of mem?) - skipping"); goto err; } seqno_event->orig = orig_node; seqno_event->neigh = neigh_node; seqno_event->prev_sender = prev_sender_node; seqno_event->seqno = seqno; seqno_event->tq = tq; seqno_event->ttl = ttl; seqno_event->rt_hist = NULL; list_add_tail(&seqno_event->list, &orig_event->event_list); return 1; err: return 0; } static int parse_log_file(char *file_path) { FILE *fd; char line_buff[MAX_LINE], *start_ptr, *start_ptr_safe, *tok_ptr; char *neigh, *iface_addr, *orig, *prev_sender, rt_flag; int line_count = 0, tq, ttl, i, res, max; long long seqno; fd = fopen(file_path, "r"); if (!fd) { fprintf(stderr, "Error - could not open file '%s': %s\n", file_path, strerror(errno)); return 0; } while (fgets(line_buff, sizeof(line_buff), fd) != NULL) { /* ignore the timestamp at the beginning of each line */ start_ptr = line_buff + 13; line_count++; if (strstr(start_ptr, "Received BATMAN packet via NB")) { strtok_r(start_ptr, " ", &start_ptr_safe); neigh = iface_addr = orig = prev_sender = NULL; seqno = tq = ttl = -1; for (i = 0; i < 21; i++) { tok_ptr = strtok_r(NULL, " ", &start_ptr_safe); if (!tok_ptr) break; switch (i) { case 4: neigh = tok_ptr; neigh[strlen(neigh) - 1] = 0; break; case 7: iface_addr = tok_ptr + 1; iface_addr[strlen(iface_addr) - 1] = 0; break; case 10: orig = tok_ptr; orig[strlen(orig) - 1] = 0; break; case 14: prev_sender = tok_ptr; prev_sender[strlen(prev_sender) - 1] = 0; break; case 16: seqno = strtoll(tok_ptr, NULL, 10); break; case 18: tq = strtol(tok_ptr, NULL, 10); break; case 20: ttl = strtol(tok_ptr, NULL, 10); break; } } if (ttl == -1) { fprintf(stderr, "Broken 'received packet' line found - skipping [file: %s, line: %i]\n", file_path, line_count); continue; } // fprintf(stderr, "received packet (line %i): neigh: '%s', iface_addr: '%s', orig: '%s', prev_sender: '%s', seqno: %i, tq: %i, ttl: %i\n", line_count, neigh, iface_addr, orig, prev_sender, seqno, tq, ttl); res = seqno_event_new(iface_addr, orig, prev_sender, neigh, seqno, tq, ttl); if (res < 1) fprintf(stderr, " [file: %s, line: %i]\n", file_path, line_count); } else if (strstr(start_ptr, "Adding route towards") || strstr(start_ptr, "Changing route towards") || strstr(start_ptr, "Deleting route towards")) { rt_flag = RT_FLAG_UPDATE; max = 12; if (strstr(start_ptr, "Adding route towards")) { rt_flag = RT_FLAG_ADD; max = 5; } else if (strstr(start_ptr, "Deleting route towards")) { rt_flag = RT_FLAG_DELETE; max = 3; } strtok_r(start_ptr, " ", &start_ptr_safe); orig = neigh = prev_sender = NULL; for (i = 0; i < max; i++) { tok_ptr = strtok_r(NULL, " ", &start_ptr_safe); if (!tok_ptr) break; switch (i) { case 2: orig = tok_ptr; if (rt_flag == RT_FLAG_DELETE) orig[strlen(orig) - 1] = 0; break; case 4: if (rt_flag == RT_FLAG_ADD) { neigh = tok_ptr; neigh[strlen(neigh) - 2] = 0; } break; case 5: neigh = tok_ptr; break; case 9: prev_sender = tok_ptr; prev_sender[strlen(prev_sender) - 2] = 0; break; } } // printf("route (file: %s, line %i): orig: '%s', neigh: '%s', prev_sender: '%s'\n", // file_path, line_count, orig, neigh, prev_sender); if (((rt_flag == RT_FLAG_ADD) && (!neigh)) || ((rt_flag == RT_FLAG_UPDATE) && (!prev_sender)) || ((rt_flag == RT_FLAG_DELETE) && (!orig))) { fprintf(stderr, "Broken '%s route' line found - skipping [file: %s, line: %i]\n", (rt_flag == RT_FLAG_UPDATE ? "changing" : (rt_flag == RT_FLAG_ADD ? "adding" : "deleting")), file_path, line_count); continue; } res = routing_table_new(orig, neigh, prev_sender, rt_flag); if (res < 1) fprintf(stderr, " [file: %s, line: %i]\n", file_path, line_count); } } // printf("File '%s' parsed (lines: %i)\n", file_path, line_count); fclose(fd); curr_bat_node = NULL; return 1; } static struct rt_hist *get_rt_hist_by_seqno(struct orig_event *orig_event, long long seqno) { struct seqno_event *seqno_event; struct rt_hist *rt_hist = NULL; list_for_each_entry(seqno_event, &orig_event->event_list, list) { if (seqno_event->seqno > seqno) break; if (seqno_event->rt_hist) rt_hist = seqno_event->rt_hist; } return rt_hist; } static struct rt_hist *get_rt_hist_by_node_seqno(struct bat_node *bat_node, struct bat_node *orig_node, long long seqno) { struct orig_event *orig_event; struct rt_hist *rt_hist; orig_event = orig_event_get_by_ptr(bat_node, orig_node); if (!orig_event) return NULL; rt_hist = get_rt_hist_by_seqno(orig_event, seqno); return rt_hist; } static int print_rt_path_at_seqno(struct bat_node *src_node, struct bat_node *dst_node, struct bat_node *next_hop, long long seqno, long long seqno_rand, int read_opt) { struct bat_node *next_hop_tmp; struct orig_event *orig_event; struct rt_hist *rt_hist; char curr_loop_magic[LOOP_MAGIC_LEN]; snprintf(curr_loop_magic, sizeof(curr_loop_magic), "%s%s%lli%lli", src_node->name, dst_node->name, seqno, seqno_rand); printf("Path towards %s (seqno %lli ", get_name_by_macstr(dst_node->name, read_opt), seqno); printf("via neigh %s):", get_name_by_macstr(next_hop->name, read_opt)); next_hop_tmp = next_hop; while (1) { printf(" -> %s%s", get_name_by_macstr(next_hop_tmp->name, read_opt), (dst_node == next_hop_tmp ? "." : "")); /* destination reached */ if (dst_node == next_hop_tmp) break; orig_event = orig_event_get_by_ptr(next_hop_tmp, dst_node); if (!orig_event) goto out; /* no more data - path seems[tm] fine */ if (list_empty(&orig_event->event_list)) goto out; /* same here */ if (list_empty(&orig_event->rt_hist_list)) goto out; /* we are running in a loop */ if (memcmp(curr_loop_magic, next_hop_tmp->loop_magic, LOOP_MAGIC_LEN) == 0) { printf(" aborted due to loop!"); goto out; } memcpy(next_hop_tmp->loop_magic, curr_loop_magic, sizeof(next_hop_tmp->loop_magic)); rt_hist = get_rt_hist_by_seqno(orig_event, seqno); /* no more routing data - what can we do ? */ if (!rt_hist) break; next_hop_tmp = rt_hist->next_hop; } out: printf("\n"); return 1; } static int find_rt_table_change(struct bat_node *src_node, struct bat_node *dst_node, struct bat_node *curr_node, long long seqno_min, long long seqno_max, long long seqno_rand, int read_opt) { struct orig_event *orig_event; struct rt_hist *rt_hist, *rt_hist_tmp; char curr_loop_magic[LOOP_MAGIC_LEN], loop_check = 0; int res; long long seqno_tmp, seqno_min_tmp = seqno_min; /* printf("%i: curr_node: %s ", bla, get_name_by_macstr(curr_node->name, read_opt)); printf("dst_node: %s [%i - %i]\n", get_name_by_macstr(dst_node->name, read_opt), seqno_min, seqno_max); */ /* recursion ends here */ if (curr_node == dst_node) { rt_hist = get_rt_hist_by_node_seqno(src_node, dst_node, seqno_max); if (rt_hist) print_rt_path_at_seqno(src_node, dst_node, rt_hist->next_hop, seqno_max, seqno_rand, read_opt); return 0; } snprintf(curr_loop_magic, sizeof(curr_loop_magic), "%s%s%lli%lli", src_node->name, dst_node->name, seqno_min_tmp, seqno_rand); orig_event = orig_event_get_by_ptr(curr_node, dst_node); if (!orig_event) goto out; list_for_each_entry(rt_hist, &orig_event->rt_hist_list, list) { /* special seqno that indicates an originator timeout */ if (rt_hist->seqno_event->seqno == -1) { printf("Woot - originator timeout ??\n"); continue; } if ((seqno_min_tmp != -1) && (rt_hist->seqno_event->seqno < seqno_min_tmp)) continue; if ((seqno_max != -1) && (rt_hist->seqno_event->seqno >= seqno_max)) continue; /* we are running in a loop */ if (memcmp(curr_loop_magic, rt_hist->loop_magic, LOOP_MAGIC_LEN) == 0) { rt_hist_tmp = get_rt_hist_by_node_seqno(src_node, dst_node, rt_hist->seqno_event->seqno); if (rt_hist_tmp) print_rt_path_at_seqno(src_node, dst_node, rt_hist_tmp->next_hop, rt_hist->seqno_event->seqno, seqno_rand, read_opt); goto loop; } memcpy(rt_hist->loop_magic, curr_loop_magic, sizeof(rt_hist->loop_magic)); loop_check = 1; /* printf("validate route after change (seqno %i) at node: %s\n", rt_hist->seqno_event->seqno, get_name_by_macstr(curr_node->name, read_opt)); */ res = find_rt_table_change(src_node, dst_node, rt_hist->next_hop, seqno_min_tmp, rt_hist->seqno_event->seqno, seqno_rand, read_opt); seqno_min_tmp = rt_hist->seqno_event->seqno + 1; /* find_rt_table_change() did not run into a loop and printed the path */ if (res == 0) continue; /** * retrieve routing table towards dst at that point and * print the routing path **/ rt_hist_tmp = get_rt_hist_by_node_seqno(src_node, dst_node, rt_hist->seqno_event->seqno); if (!rt_hist_tmp) continue; print_rt_path_at_seqno(src_node, dst_node, rt_hist_tmp->next_hop, rt_hist->seqno_event->seqno, seqno_rand, read_opt); } /** * if we have no routing table changes within the seqno range * the loop detection above won't be triggered **/ if (!loop_check) { if (memcmp(curr_loop_magic, curr_node->loop_magic2, LOOP_MAGIC_LEN) == 0) { rt_hist_tmp = get_rt_hist_by_node_seqno(src_node, dst_node, seqno_min); if (rt_hist_tmp) print_rt_path_at_seqno(src_node, dst_node, rt_hist_tmp->next_hop, seqno_min, seqno_rand, read_opt); /* no need to print the path twice */ if (seqno_min == seqno_max) goto out; else goto loop; } memcpy(curr_node->loop_magic2, curr_loop_magic, sizeof(curr_node->loop_magic2)); } seqno_tmp = seqno_max - 1; if (seqno_min == seqno_max) seqno_tmp = seqno_max; rt_hist = get_rt_hist_by_seqno(orig_event, seqno_tmp); if (rt_hist) return find_rt_table_change(src_node, dst_node, rt_hist->next_hop, seqno_min_tmp, seqno_max, seqno_rand, read_opt); out: return -1; loop: return -2; } static void loop_detection(char *loop_orig, long long seqno_min, long long seqno_max, char *filter_orig, int read_opt) { struct bat_node *bat_node; struct orig_event *orig_event; struct hash_it_t *hashit = NULL; struct rt_hist *rt_hist, *prev_rt_hist; long long last_seqno = -1, seqno_count = 0; int res; char check_orig[NAME_LEN]; printf("\nAnalyzing routing tables "); /* if no option was given loop_orig is empty */ memset(check_orig, 0, NAME_LEN); if (!compare_name(loop_orig, check_orig)) printf("of originator: %s ", get_name_by_macstr(loop_orig, read_opt)); if ((seqno_min == -1) && (seqno_max == -1)) printf("[all sequence numbers]"); else if (seqno_min == seqno_max) printf("[sequence number: %lli]", seqno_min); else printf("[sequence number range: %lli-%lli]", seqno_min, seqno_max); if (!compare_name(filter_orig, check_orig)) printf(" [filter originator: %s]", get_name_by_macstr(filter_orig, read_opt)); printf("\n"); while (NULL != (hashit = hash_iterate(node_hash, hashit))) { bat_node = hashit->bucket->data; if (!compare_name(loop_orig, check_orig) && !compare_name(loop_orig, bat_node->name)) continue; printf("\nChecking host: %s\n", get_name_by_macstr(bat_node->name, read_opt)); list_for_each_entry(orig_event, &bat_node->orig_event_list, list) { if (bat_node == orig_event->orig_node) continue; if (!compare_name(filter_orig, check_orig) && !compare_name(filter_orig, orig_event->orig_node->name)) continue; /* we might have no log file from this node */ if (list_empty(&orig_event->event_list)) { fprintf(stderr, "No seqno data of originator '%s' - skipping\n", get_name_by_macstr(orig_event->orig_node->name, read_opt)); continue; } /* or routing tables */ if (list_empty(&orig_event->rt_hist_list)) { fprintf(stderr, "No routing history of originator '%s' - skipping\n", get_name_by_macstr(orig_event->orig_node->name, read_opt)); continue; } list_for_each_entry(rt_hist, &orig_event->rt_hist_list, list) { /* special seqno that indicates an originator timeout */ if (rt_hist->seqno_event->seqno == -1) continue; if ((seqno_min != -1) && (rt_hist->seqno_event->seqno < seqno_min)) continue; if ((seqno_max != -1) && (rt_hist->seqno_event->seqno > seqno_max)) continue; /** * sometime we change the routing table more than once * with the same seqno */ if (last_seqno == rt_hist->seqno_event->seqno) seqno_count++; else seqno_count = 0; last_seqno = rt_hist->seqno_event->seqno; if (rt_hist->flags == RT_FLAG_DELETE) { printf("Path towards %s deleted (originator timeout)\n", get_name_by_macstr(rt_hist->seqno_event->orig->name, read_opt)); continue; } prev_rt_hist = rt_hist->prev_rt_hist; if ((prev_rt_hist) && (rt_hist->seqno_event->seqno != prev_rt_hist->seqno_event->seqno)) { if (rt_hist->seqno_event->seqno < prev_rt_hist->seqno_event->seqno) { fprintf(stderr, "Smaller seqno (%lli) than previously received seqno (%lli) of orig %s triggered routing table change - skipping recursive check\n", rt_hist->seqno_event->seqno, prev_rt_hist->seqno_event->seqno, get_name_by_macstr(rt_hist->seqno_event->orig->name, read_opt)); goto validate_path; } if (rt_hist->seqno_event->seqno == prev_rt_hist->seqno_event->seqno + 1) goto validate_path; /* printf("\n=> checking orig %s in seqno range of: %i - %i ", get_name_by_macstr(rt_hist->seqno_event->orig->name, read_opt), prev_rt_hist->seqno_event->seqno + 1, rt_hist->seqno_event->seqno); printf("(prev nexthop: %s)\n", get_name_by_macstr(prev_rt_hist->next_hop->name, read_opt)); */ res = find_rt_table_change(bat_node, rt_hist->seqno_event->orig, prev_rt_hist->next_hop, prev_rt_hist->seqno_event->seqno + 1, rt_hist->seqno_event->seqno, seqno_count, read_opt); if (res != -2) continue; } validate_path: print_rt_path_at_seqno(bat_node, rt_hist->seqno_event->orig, rt_hist->next_hop, rt_hist->seqno_event->seqno, seqno_count, read_opt); } } } } static void seqno_trace_print_neigh(struct seqno_trace_neigh *seqno_trace_neigh, struct seqno_event *seqno_event_parent, int num_sisters, char *head, int read_opt) { char new_head[MAX_LINE]; int i; printf("%s%s- %s [tq: %i, ttl: %i", head, (strlen(head) == 1 ? "" : num_sisters == 0 ? "\\" : "|"), get_name_by_macstr(seqno_trace_neigh->bat_node->name, read_opt), seqno_trace_neigh->seqno_event->tq, seqno_trace_neigh->seqno_event->ttl); printf(", neigh: %s", get_name_by_macstr(seqno_trace_neigh->seqno_event->neigh->name, read_opt)); printf(", prev_sender: %s]", get_name_by_macstr(seqno_trace_neigh->seqno_event->prev_sender->name, read_opt)); if ((seqno_event_parent) && (seqno_trace_neigh->seqno_event->tq > seqno_event_parent->tq)) printf(" TQ UP!\n"); else printf("\n"); for (i = 0; i < seqno_trace_neigh->num_neighbors; i++) { snprintf(new_head, sizeof(new_head), "%s%s", (strlen(head) > 1 ? head : num_sisters == 0 ? " " : head), (strlen(head) == 1 ? " " : num_sisters == 0 ? " " : "| ")); seqno_trace_print_neigh(seqno_trace_neigh->seqno_trace_neigh[i], seqno_trace_neigh->seqno_event, seqno_trace_neigh->num_neighbors - i - 1, new_head, read_opt); } } static void seqno_trace_print(struct list_head *trace_list, char *trace_orig, long long seqno_min, long long seqno_max, char *filter_orig, int read_opt) { struct seqno_trace *seqno_trace; char head[MAX_LINE], check_orig[NAME_LEN]; int i; /* if no option was given filter_orig is empty */ memset(check_orig, 0, NAME_LEN); printf("Sequence number flow of originator: %s ", get_name_by_macstr(trace_orig, read_opt)); if ((seqno_min == -1) && (seqno_max == -1)) printf("[all sequence numbers]"); else if (seqno_min == seqno_max) printf("[sequence number: %lli]", seqno_min); else printf("[sequence number range: %lli-%lli]", seqno_min, seqno_max); if (!compare_name(filter_orig, check_orig)) printf(" [filter originator: %s]", get_name_by_macstr(filter_orig, read_opt)); printf("\n"); list_for_each_entry(seqno_trace, trace_list, list) { if (!seqno_trace->print) continue; printf("+=> %s (seqno %lli)\n", get_name_by_macstr(trace_orig, read_opt), seqno_trace->seqno); for (i = 0; i < seqno_trace->seqno_trace_neigh.num_neighbors; i++) { snprintf(head, sizeof(head), "%c", (seqno_trace->seqno_trace_neigh.num_neighbors == i + 1 ? '\\' : '|')); seqno_trace_print_neigh(seqno_trace->seqno_trace_neigh.seqno_trace_neigh[i], NULL, seqno_trace->seqno_trace_neigh.num_neighbors - i - 1, head, read_opt); } printf("\n"); } } static int _seqno_trace_neigh_add(struct seqno_trace_neigh *seqno_trace_mom, struct seqno_trace_neigh *seqno_trace_child) { struct seqno_trace_neigh **data_ptr; data_ptr = malloc((seqno_trace_mom->num_neighbors + 1) * sizeof(struct seqno_trace_neigh *)); if (!data_ptr) return 0; if (seqno_trace_mom->num_neighbors > 0) { memcpy(data_ptr, seqno_trace_mom->seqno_trace_neigh, seqno_trace_mom->num_neighbors * sizeof(struct seqno_trace_neigh *)); free(seqno_trace_mom->seqno_trace_neigh); } seqno_trace_mom->num_neighbors++; seqno_trace_mom->seqno_trace_neigh = data_ptr; seqno_trace_mom->seqno_trace_neigh[seqno_trace_mom->num_neighbors - 1] = seqno_trace_child; return 1; } static struct seqno_trace_neigh *seqno_trace_neigh_add(struct seqno_trace_neigh *seqno_trace_neigh, struct bat_node *bat_node, struct seqno_event *seqno_event) { struct seqno_trace_neigh *seqno_trace_neigh_new; int res; seqno_trace_neigh_new = malloc(sizeof(struct seqno_trace_neigh)); if (!seqno_trace_neigh_new) goto err; seqno_trace_neigh_new->bat_node = bat_node; seqno_trace_neigh_new->seqno_event = seqno_event; seqno_trace_neigh_new->num_neighbors = 0; res = _seqno_trace_neigh_add(seqno_trace_neigh, seqno_trace_neigh_new); if (res < 1) goto free_neigh; return seqno_trace_neigh_new; free_neigh: free(seqno_trace_neigh_new); err: return NULL; } static struct seqno_trace_neigh *seqno_trace_find_neigh(struct bat_node *neigh, struct bat_node *prev_sender, struct seqno_trace_neigh *seqno_trace_neigh) { struct seqno_trace_neigh *seqno_trace_neigh_tmp, *seqno_trace_neigh_ret; int i; for (i = 0; i < seqno_trace_neigh->num_neighbors; i++) { seqno_trace_neigh_tmp = seqno_trace_neigh->seqno_trace_neigh[i]; if ((neigh == seqno_trace_neigh_tmp->bat_node) && (prev_sender == seqno_trace_neigh_tmp->seqno_event->neigh)) return seqno_trace_neigh_tmp; seqno_trace_neigh_ret = seqno_trace_find_neigh(neigh, prev_sender, seqno_trace_neigh_tmp); if (seqno_trace_neigh_ret) return seqno_trace_neigh_ret; } return NULL; } static void seqno_trace_neigh_free(struct seqno_trace_neigh *seqno_trace_neigh) { int i; for (i = 0; i < seqno_trace_neigh->num_neighbors; i++) seqno_trace_neigh_free(seqno_trace_neigh->seqno_trace_neigh[i]); if (seqno_trace_neigh->num_neighbors > 0) free(seqno_trace_neigh->seqno_trace_neigh); free(seqno_trace_neigh); } static int seqno_trace_fix_leaf(struct seqno_trace_neigh *seqno_trace_mom, struct seqno_trace_neigh *seqno_trace_old_mom, struct seqno_trace_neigh *seqno_trace_child) { struct seqno_trace_neigh **data_ptr, *seqno_trace_neigh; int i, j = 0; data_ptr = malloc((seqno_trace_old_mom->num_neighbors - 1) * sizeof(struct seqno_trace_neigh *)); if (!data_ptr) return 0; /* copy all children except the child that is going to move */ for (i = 0; i < seqno_trace_old_mom->num_neighbors; i++) { seqno_trace_neigh = seqno_trace_old_mom->seqno_trace_neigh[i]; if (seqno_trace_neigh != seqno_trace_child) { data_ptr[j] = seqno_trace_neigh; j++; } } seqno_trace_old_mom->num_neighbors--; free(seqno_trace_old_mom->seqno_trace_neigh); seqno_trace_old_mom->seqno_trace_neigh = data_ptr; return _seqno_trace_neigh_add(seqno_trace_mom, seqno_trace_child); } static int seqno_trace_check_leaves(struct seqno_trace *seqno_trace, struct seqno_trace_neigh *seqno_trace_neigh_new) { struct seqno_trace_neigh *seqno_trace_neigh_tmp; int i, res; for (i = 0; i < seqno_trace->seqno_trace_neigh.num_neighbors; i++) { seqno_trace_neigh_tmp = seqno_trace->seqno_trace_neigh.seqno_trace_neigh[i]; if ((seqno_trace_neigh_tmp->seqno_event->neigh == seqno_trace_neigh_new->bat_node) && (seqno_trace_neigh_tmp->seqno_event->prev_sender == seqno_trace_neigh_new->seqno_event->neigh)) { res = seqno_trace_fix_leaf(seqno_trace_neigh_new, &seqno_trace->seqno_trace_neigh, seqno_trace_neigh_tmp); if (res < 1) return res; /* restart checking procedure because we just changed the array we are working on */ return seqno_trace_check_leaves(seqno_trace, seqno_trace_neigh_new); } } return 1; } static struct seqno_trace *seqno_trace_new(struct seqno_event *seqno_event) { struct seqno_trace *seqno_trace; seqno_trace = malloc(sizeof(struct seqno_trace)); if (!seqno_trace) { fprintf(stderr, "Could not allocate memory for seqno tracing data (out of mem?)\n"); return NULL; } seqno_trace->seqno = seqno_event->seqno; seqno_trace->print = 0; seqno_trace->seqno_trace_neigh.num_neighbors = 0; return seqno_trace; } static void seqno_trace_free(struct seqno_trace *seqno_trace) { int i; for (i = 0; i < seqno_trace->seqno_trace_neigh.num_neighbors; i++) seqno_trace_neigh_free(seqno_trace->seqno_trace_neigh.seqno_trace_neigh[i]); free(seqno_trace); } static int seqno_trace_add(struct list_head *trace_list, struct bat_node *bat_node, struct seqno_event *seqno_event, char print_trace) { struct seqno_trace *seqno_trace = NULL, *seqno_trace_tmp = NULL, *seqno_trace_prev = NULL; struct seqno_trace_neigh *seqno_trace_neigh; list_for_each_entry(seqno_trace_tmp, trace_list, list) { if (seqno_trace_tmp->seqno == seqno_event->seqno) { seqno_trace = seqno_trace_tmp; break; } if (seqno_trace_tmp->seqno > seqno_event->seqno) break; seqno_trace_prev = seqno_trace_tmp; } if (!seqno_trace) { seqno_trace = seqno_trace_new(seqno_event); if (!seqno_trace) goto err; if ((list_empty(trace_list)) || (seqno_event->seqno > list_last_entry(trace_list, struct seqno_trace, list)->seqno)) list_add_tail(&seqno_trace->list, trace_list); else if (seqno_event->seqno < list_first_entry(trace_list, struct seqno_trace, list)->seqno) list_add(&seqno_trace->list, trace_list); else list_add_behind(&seqno_trace->list, &seqno_trace_prev->list); } if (print_trace) seqno_trace->print = print_trace; seqno_trace_neigh = seqno_trace_find_neigh(seqno_event->neigh, seqno_event->prev_sender, &seqno_trace->seqno_trace_neigh); /* no neighbor found to hook up to - adding new root node */ if (!seqno_trace_neigh) seqno_trace_neigh = seqno_trace_neigh_add(&seqno_trace->seqno_trace_neigh, bat_node, seqno_event); else seqno_trace_neigh = seqno_trace_neigh_add(seqno_trace_neigh, bat_node, seqno_event); if (seqno_trace_neigh) seqno_trace_check_leaves(seqno_trace, seqno_trace_neigh); return 1; err: return 0; } static void trace_seqnos(char *trace_orig, long long seqno_min, long long seqno_max, char *filter_orig, int read_opt) { struct bat_node *bat_node; struct orig_event *orig_event; struct seqno_event *seqno_event; struct hash_it_t *hashit = NULL; struct list_head trace_list; struct seqno_trace *seqno_trace, *seqno_trace_tmp; char check_orig[NAME_LEN], print_trace; int res; /* if no option was given filter_orig is empty */ memset(check_orig, 0, NAME_LEN); INIT_LIST_HEAD(&trace_list); while (NULL != (hashit = hash_iterate(node_hash, hashit))) { bat_node = hashit->bucket->data; list_for_each_entry(orig_event, &bat_node->orig_event_list, list) { /* we might have no log file from this node */ if (list_empty(&orig_event->event_list)) continue; list_for_each_entry(seqno_event, &orig_event->event_list, list) { /* special seqno that indicates an originator timeout */ if (seqno_event->seqno == -1) continue; if (!compare_name(trace_orig, seqno_event->orig->name)) continue; if ((seqno_min != -1) && (seqno_event->seqno < seqno_min)) continue; if ((seqno_max != -1) && (seqno_event->seqno > seqno_max)) continue; /* if no filter option was given all seqno traces are to be printed */ print_trace = compare_name(filter_orig, check_orig); if (!compare_name(filter_orig, check_orig) && compare_name(filter_orig, bat_node->name)) print_trace = 1; res = seqno_trace_add(&trace_list, bat_node, seqno_event, print_trace); if (res < 1) { hash_iterate_free(hashit); goto out; } } } } seqno_trace_print(&trace_list, trace_orig, seqno_min, seqno_max, filter_orig, read_opt); out: list_for_each_entry_safe(seqno_trace, seqno_trace_tmp, &trace_list, list) { list_del(&seqno_trace->list); seqno_trace_free(seqno_trace); } return; } static void print_rt_tables(char *rt_orig, long long seqno_min, long long seqno_max, char *filter_orig, int read_opt) { struct bat_node *bat_node; struct rt_table *rt_table; struct seqno_event *seqno_event; char check_orig[NAME_LEN]; int i; /* if no option was given filter_orig is empty */ memset(check_orig, 0, NAME_LEN); printf("Routing tables of originator: %s ", get_name_by_macstr(rt_orig, read_opt)); if ((seqno_min == -1) && (seqno_max == -1)) printf("[all sequence numbers]"); else if (seqno_min == seqno_max) printf("[sequence number: %lli]", seqno_min); else printf("[sequence number range: %lli-%lli]", seqno_min, seqno_max); if (!compare_name(filter_orig, check_orig)) printf(" [filter originator: %s]", get_name_by_macstr(filter_orig, read_opt)); printf("\n"); bat_node = node_get(rt_orig); if (!bat_node) goto out; /* we might have no log file from this node */ if (list_empty(&bat_node->rt_table_list)) goto out; list_for_each_entry(rt_table, &bat_node->rt_table_list, list) { seqno_event = rt_table->rt_hist->seqno_event; if (!compare_name(filter_orig, check_orig) && !compare_name(filter_orig, seqno_event->orig->name)) continue; if ((seqno_min != -1) && (seqno_event->seqno < seqno_min)) continue; if ((seqno_max != -1) && (seqno_event->seqno > seqno_max)) continue; if (seqno_event->seqno > -1) { printf("rt change triggered by OGM from: %s (tq: %i, ttl: %i, seqno %lli", get_name_by_macstr(seqno_event->orig->name, read_opt), seqno_event->tq, seqno_event->ttl, seqno_event->seqno); printf(", neigh: %s", get_name_by_macstr(seqno_event->neigh->name, read_opt)); printf(", prev_sender: %s)\n", get_name_by_macstr(seqno_event->prev_sender->name, read_opt)); } else { printf("rt change triggered by originator timeout: \n"); } for (i = 0; i < rt_table->num_entries; i++) { printf("%s %s via next hop", (rt_table->entries[i].flags ? " *" : " "), get_name_by_macstr(rt_table->entries[i].orig, read_opt)); printf(" %s", get_name_by_macstr(rt_table->entries[i].next_hop->name, read_opt)); switch (rt_table->entries[i].flags) { case RT_FLAG_ADD: printf(" (route added)\n"); break; case RT_FLAG_UPDATE: printf(" (next hop changed)\n"); break; case RT_FLAG_DELETE: printf(" (route deleted)\n"); break; default: printf("\n"); break; } } printf("\n"); } out: return; } static int get_orig_addr(char *orig_name, char *orig_addr) { struct bat_host *bat_host; struct ether_addr *orig_mac; char *orig_name_tmp = orig_name; bat_host = bat_hosts_find_by_name(orig_name_tmp); if (bat_host) { orig_name_tmp = ether_ntoa_long((struct ether_addr *)&bat_host->mac_addr); goto copy_name; } orig_mac = ether_aton(orig_name_tmp); if (!orig_mac) { fprintf(stderr, "Error - the originator is not a mac address or bat-host name: %s\n", orig_name); goto err; } /** * convert the given mac address to the long format to * make sure we can find it */ orig_name_tmp = ether_ntoa_long(orig_mac); copy_name: strncpy(orig_addr, orig_name_tmp, NAME_LEN); orig_addr[NAME_LEN - 1] = '\0'; return 1; err: return 0; } int bisect_iv(int argc, char **argv) { int ret = EXIT_FAILURE, res, optchar, found_args = 1; int read_opt = USE_BAT_HOSTS, num_parsed_files; long long tmp_seqno, seqno_max = -1, seqno_min = -1; char *trace_orig_ptr = NULL, *rt_orig_ptr = NULL, *loop_orig_ptr = NULL; char orig[NAME_LEN], filter_orig[NAME_LEN], *dash_ptr, *filter_orig_ptr = NULL; memset(orig, 0, NAME_LEN); memset(filter_orig, 0, NAME_LEN); while ((optchar = getopt(argc, argv, "hl:no:r:s:t:")) != -1) { switch (optchar) { case 'h': bisect_iv_usage(); return EXIT_SUCCESS; case 'l': loop_orig_ptr = optarg; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 'n': read_opt &= ~USE_BAT_HOSTS; found_args += 1; break; case 'o': filter_orig_ptr = optarg; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 'r': rt_orig_ptr = optarg; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 's': dash_ptr = strchr(optarg, '-'); if (dash_ptr) *dash_ptr = 0; tmp_seqno = strtol(optarg, NULL , 10); if ((tmp_seqno >= 0) && (tmp_seqno <= UINT32_MAX)) seqno_min = tmp_seqno; else fprintf(stderr, "Warning - given sequence number is out of range: %lli\n", tmp_seqno); if (dash_ptr) { tmp_seqno = strtol(dash_ptr + 1, NULL , 10); if ((tmp_seqno >= 0) && (tmp_seqno <= UINT32_MAX)) seqno_max = tmp_seqno; else fprintf(stderr, "Warning - given sequence number is out of range: %lli\n", tmp_seqno); *dash_ptr = '-'; } found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 't': trace_orig_ptr = optarg; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; default: bisect_iv_usage(); return EXIT_FAILURE; } } if (argc <= found_args + 1) { fprintf(stderr, "Error - need at least 2 log files to compare\n"); bisect_iv_usage(); goto err; } node_hash = hash_new(64, compare_name, choose_name); if (!node_hash) { fprintf(stderr, "Error - could not create node hash table\n"); goto err; } bat_hosts_init(read_opt); num_parsed_files = 0; if ((rt_orig_ptr) && (trace_orig_ptr)) { fprintf(stderr, "Error - the 'print routing table' option can't be used together with the 'trace seqno' option\n"); goto err; } else if ((loop_orig_ptr) && (trace_orig_ptr)) { fprintf(stderr, "Error - the 'loop detection' option can't be used together with the 'trace seqno' option\n"); goto err; } else if ((loop_orig_ptr) && (rt_orig_ptr)) { fprintf(stderr, "Error - the 'loop detection' option can't be used together with the 'print routing table' option\n"); goto err; } else if (rt_orig_ptr) { res = get_orig_addr(rt_orig_ptr, orig); if (res < 1) goto err; } else if (trace_orig_ptr) { res = get_orig_addr(trace_orig_ptr, orig); if (res < 1) goto err; } else if (loop_orig_ptr) { res = get_orig_addr(loop_orig_ptr, orig); if (res < 1) goto err; } /* we search a specific seqno - no range */ if ((seqno_min > 0) && (seqno_max == -1)) seqno_max = seqno_min; if (seqno_min > seqno_max) { fprintf(stderr, "Error - the sequence range minimum (%lli) should be smaller than the maximum (%lli)\n", seqno_min, seqno_max); goto err; } if (filter_orig_ptr) { res = get_orig_addr(filter_orig_ptr, filter_orig); if (res < 1) goto err; } while (argc > found_args) { res = parse_log_file(argv[found_args]); if (res > 0) num_parsed_files++; found_args++; } if (num_parsed_files < 2) { fprintf(stderr, "Error - need at least 2 log files to compare\n"); goto err; } if (trace_orig_ptr) trace_seqnos(orig, seqno_min, seqno_max, filter_orig, read_opt); else if (rt_orig_ptr) print_rt_tables(orig, seqno_min, seqno_max, filter_orig, read_opt); else loop_detection(orig, seqno_min, seqno_max, filter_orig, read_opt); ret = EXIT_SUCCESS; err: if (node_hash) hash_delete(node_hash, node_free); bat_hosts_free(); return ret; } batctl-2018.0/bisect_iv.h000066400000000000000000000045061324521615300152040ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_BISECT_IV_H #define _BATCTL_BISECT_IV_H #include "list.h" #define NAME_LEN 18 #define MAX_LINE 256 #define LOOP_MAGIC_LEN ((2 * NAME_LEN) + (2 * sizeof(int)) - 2) #define RT_FLAG_ADD 1 #define RT_FLAG_UPDATE 2 #define RT_FLAG_DELETE 3 int bisect_iv(int argc, char **argv); struct bat_node { char name[NAME_LEN]; struct list_head orig_event_list; struct list_head rt_table_list; char loop_magic[LOOP_MAGIC_LEN]; char loop_magic2[LOOP_MAGIC_LEN]; }; struct orig_event { struct list_head list; struct bat_node *orig_node; struct list_head event_list; struct list_head rt_hist_list; }; struct rt_table { struct list_head list; int num_entries; struct rt_entry *entries; struct rt_hist *rt_hist; }; struct rt_hist { struct list_head list; struct rt_table *rt_table; struct rt_hist *prev_rt_hist; struct seqno_event *seqno_event; struct bat_node *next_hop; char flags; char loop_magic[LOOP_MAGIC_LEN]; }; struct rt_entry { char orig[NAME_LEN]; struct bat_node *next_hop; char flags; }; struct seqno_event { struct list_head list; struct bat_node *orig; struct bat_node *neigh; struct bat_node *prev_sender; long long seqno; int tq; int ttl; struct rt_hist *rt_hist; }; struct seqno_trace_neigh { struct bat_node *bat_node; struct seqno_event *seqno_event; int num_neighbors; struct seqno_trace_neigh **seqno_trace_neigh; }; struct seqno_trace { struct list_head list; long long seqno; char print; struct seqno_trace_neigh seqno_trace_neigh; }; #endif batctl-2018.0/debug.c000066400000000000000000000221441324521615300143140ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include "debug.h" #include "debugfs.h" #include "functions.h" #include "netlink.h" #include "sys.h" const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM] = { { .opt_long = "neighbors", .opt_short = "n", .debugfs_name = "neighbors", .header_lines = 2, .netlink_fn = netlink_print_neighbors, }, { .opt_long = "originators", .opt_short = "o", .debugfs_name = "originators", .header_lines = 2, .netlink_fn = netlink_print_originators, }, { .opt_long = "gateways", .opt_short = "gwl", .debugfs_name = "gateways", .header_lines = 1, .netlink_fn = netlink_print_gateways, }, { .opt_long = "translocal", .opt_short = "tl", .debugfs_name = "transtable_local", .header_lines = 2, .netlink_fn = netlink_print_translocal, }, { .opt_long = "transglobal", .opt_short = "tg", .debugfs_name = "transtable_global", .header_lines = 2, .netlink_fn = netlink_print_transglobal, }, { .opt_long = "claimtable", .opt_short = "cl", .debugfs_name = "bla_claim_table", .header_lines = 2, .netlink_fn = netlink_print_bla_claim, }, { .opt_long = "backbonetable", .opt_short = "bbt", .debugfs_name = "bla_backbone_table", .header_lines = 2, .netlink_fn = netlink_print_bla_backbone, }, { .opt_long = "dat_cache", .opt_short = "dc", .debugfs_name = "dat_cache", .header_lines = 2, }, { .opt_long = "nc_nodes", .opt_short = "nn", .debugfs_name = "nc_nodes", .header_lines = 0, }, { .opt_long = "mcast_flags", .opt_short = "mf", .debugfs_name = "mcast_flags", .header_lines = 6, }, }; static void debug_table_usage(int debug_table) { fprintf(stderr, "Usage: batctl [options] %s|%s [parameters]\n", batctl_debug_tables[debug_table].opt_long, batctl_debug_tables[debug_table].opt_short); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -n don't replace mac addresses with bat-host names\n"); fprintf(stderr, " \t -H don't show the header\n"); fprintf(stderr, " \t -w [interval] watch mode - refresh the table continuously\n"); if (debug_table == BATCTL_TABLE_ORIGINATORS) { fprintf(stderr, " \t -t timeout interval - don't print originators not seen for x.y seconds \n"); fprintf(stderr, " \t -i [interface] - show multiif originator table for a specific interface\n"); } if (debug_table == BATCTL_TABLE_TRANSLOCAL || debug_table == BATCTL_TABLE_TRANSGLOBAL) { fprintf(stderr, " \t -u|-m print unicast or multicast mac addresses only\n"); } } int handle_debug_table(char *mesh_iface, int debug_table, int argc, char **argv) { int optchar, read_opt = USE_BAT_HOSTS; char full_path[MAX_PATH+1]; char *debugfs_mnt; char *orig_iface = NULL; float orig_timeout = 0.0f; float watch_interval = 1; int err; while ((optchar = getopt(argc, argv, "hnw:t:Humi:")) != -1) { switch (optchar) { case 'h': debug_table_usage(debug_table); return EXIT_SUCCESS; case 'n': read_opt &= ~USE_BAT_HOSTS; break; case 'w': read_opt |= CLR_CONT_READ; if (optarg[0] == '-') { optind--; break; } if (!sscanf(optarg, "%f", &watch_interval)) { fprintf(stderr, "Error - provided argument of '-%c' is not a number\n", optchar); return EXIT_FAILURE; } break; case 't': if (debug_table != BATCTL_TABLE_ORIGINATORS) { fprintf(stderr, "Error - unrecognised option '-%c'\n", optchar); debug_table_usage(debug_table); return EXIT_FAILURE; } read_opt |= NO_OLD_ORIGS; if (!sscanf(optarg, "%f", &orig_timeout)) { fprintf(stderr, "Error - provided argument of '-%c' is not a number\n", optchar); return EXIT_FAILURE; } break; case 'H': read_opt |= SKIP_HEADER; break; case 'u': if (debug_table != BATCTL_TABLE_TRANSLOCAL && debug_table != BATCTL_TABLE_TRANSGLOBAL) { fprintf(stderr, "Error - unrecognised option '-%c'\n", optchar); debug_table_usage(debug_table); return EXIT_FAILURE; } read_opt |= UNICAST_ONLY; break; case 'm': if (debug_table != BATCTL_TABLE_TRANSLOCAL && debug_table != BATCTL_TABLE_TRANSGLOBAL) { fprintf(stderr, "Error - unrecognised option '-%c'\n", optchar); debug_table_usage(debug_table); return EXIT_FAILURE; } read_opt |= MULTICAST_ONLY; break; case 'i': if (debug_table != BATCTL_TABLE_ORIGINATORS) { fprintf(stderr, "Error - unrecognised option '-%c'\n", optchar); debug_table_usage(debug_table); return EXIT_FAILURE; } if (check_mesh_iface_ownership(mesh_iface, optarg) != EXIT_SUCCESS) return EXIT_FAILURE; orig_iface = optarg; break; case '?': if (optopt == 't') { fprintf(stderr, "Error - option '-t' needs a number as argument\n"); } else if (optopt == 'i') { fprintf(stderr, "Error - option '-i' needs an interface as argument\n"); } else if (optopt == 'w') { read_opt |= CLR_CONT_READ; break; } else fprintf(stderr, "Error - unrecognised option: '-%c'\n", optopt); return EXIT_FAILURE; default: debug_table_usage(debug_table); return EXIT_FAILURE; } } check_root_or_die("batctl"); if (read_opt & UNICAST_ONLY && read_opt & MULTICAST_ONLY) { fprintf(stderr, "Error - '-u' and '-m' are exclusive options\n"); debug_table_usage(debug_table); return EXIT_FAILURE; } debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); return EXIT_FAILURE; } if (batctl_debug_tables[debug_table].netlink_fn) { err = batctl_debug_tables[debug_table].netlink_fn( mesh_iface, orig_iface, read_opt, orig_timeout, watch_interval); if (err != -EOPNOTSUPP) return err; } if (orig_iface) debugfs_make_path(DEBUG_BATIF_PATH_FMT "/", orig_iface, full_path, sizeof(full_path)); else debugfs_make_path(DEBUG_BATIF_PATH_FMT "/", mesh_iface, full_path, sizeof(full_path)); return read_file(full_path, (char *)batctl_debug_tables[debug_table].debugfs_name, read_opt, orig_timeout, watch_interval, batctl_debug_tables[debug_table].header_lines); } int debug_print_routing_algos(void) { char full_path[MAX_PATH+1]; char *debugfs_mnt; debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); return -1; } debugfs_make_path(DEBUG_BATIF_PATH_FMT, "", full_path, sizeof(full_path)); return read_file(full_path, DEBUG_ROUTING_ALGOS, 0, 0, 0, 0); } int print_vis_info(char *mesh_iface) { char full_path[MAX_PATH+1]; char *debugfs_mnt; FILE *fp; check_root_or_die("batctl vis_data"); debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); return -1; } debugfs_make_path(DEBUG_BATIF_PATH_FMT "/vis_data", mesh_iface, full_path, sizeof(full_path)); fp = fopen(full_path, "r"); if (fp) { fclose(fp); fprintf(stderr, "Error - batctl version is newer than kernel module - the kernel module still supports\n" "vis, but later versions will not. The vis functionality has been moved to the userspace\n" "daemon ''alfred''. Please either downgrade to an older (compatible) batctl version or use alfred.\n"); } else { fprintf(stderr, "Error - The installed batctl version and kernel module don't have vis support. The vis functionality\n" "has been moved to the userspace daemon ''alfred''.\n"); } return 0; } static void log_usage(void) { fprintf(stderr, "Usage: batctl [options] log [parameters]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -n don't replace mac addresses with bat-host names\n"); } int log_print(char *mesh_iface, int argc, char **argv) { int optchar, res, read_opt = USE_BAT_HOSTS | LOG_MODE; char full_path[MAX_PATH+1]; char *debugfs_mnt; while ((optchar = getopt(argc, argv, "hn")) != -1) { switch (optchar) { case 'h': log_usage(); return EXIT_SUCCESS; case 'n': read_opt &= ~USE_BAT_HOSTS; break; default: log_usage(); return EXIT_FAILURE; } } check_root_or_die("batctl log"); debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); return EXIT_FAILURE; } debugfs_make_path(DEBUG_BATIF_PATH_FMT "/", mesh_iface, full_path, sizeof(full_path)); res = read_file(full_path, DEBUG_LOG, read_opt, 0, 0, 0); return res; } batctl-2018.0/debug.h000066400000000000000000000040161324521615300143170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_DEBUG_H #define _BATCTL_DEBUG_H #include #include "main.h" #define DEBUG_BATIF_PATH_FMT "%s/batman_adv/%s" #define DEBUG_TRANSTABLE_GLOBAL "transtable_global" #define DEBUG_LOG "log" #define DEBUG_ROUTING_ALGOS "routing_algos" enum batctl_debug_tables { BATCTL_TABLE_NEIGHBORS, BATCTL_TABLE_ORIGINATORS, BATCTL_TABLE_GATEWAYS, BATCTL_TABLE_TRANSLOCAL, BATCTL_TABLE_TRANSGLOBAL, BATCTL_TABLE_BLA_CLAIMS, BATCTL_TABLE_BLA_BACKBONES, BATCTL_TABLE_DAT, BATCTL_TABLE_NETWORK_CODING_NODES, BATCTL_TABLE_MCAST_FLAGS, BATCTL_TABLE_NUM, }; struct debug_table_data { const char opt_long[OPT_LONG_MAX_LEN]; const char opt_short[OPT_SHORT_MAX_LEN]; const char debugfs_name[DEBUG_TABLE_PATH_MAX_LEN]; size_t header_lines; int (*netlink_fn)(char *mesh_iface, char *hard_iface, int read_opt, float orig_timeout, float watch_interval); }; extern const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM]; int handle_debug_table(char *mesh_iface, int debug_table, int argc, char **argv); int log_print(char *mesh_iface, int argc, char **argv); int debug_print_routing_algos(void); int print_vis_info(char *mesh_iface); #endif batctl-2018.0/debugfs.c000066400000000000000000000071121324521615300146430ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009 Clark Williams * Copyright (C) 2009 Xiao Guangrong * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "debugfs.h" #include #include #include #include #include #include #ifndef DEBUGFS_MAGIC #define DEBUGFS_MAGIC 0x64626720 #endif static int debugfs_premounted; static char debugfs_mountpoint[MAX_PATH+1]; static const char *debugfs_find_mountpoint(void); static int debugfs_valid_mountpoint(const char *debugfs); static const char *debugfs_known_mountpoints[] = { "/sys/kernel/debug/", "/debug/", NULL, }; /* construct a full path to a debugfs element */ int debugfs_make_path(const char *fmt, const char *mesh_iface, char *buffer, int size) { if (strlen(debugfs_mountpoint) == 0) { buffer[0] = '\0'; return -1; } return snprintf(buffer, size, fmt, debugfs_mountpoint, mesh_iface); } static int debugfs_found; /* find the path to the mounted debugfs */ static const char *debugfs_find_mountpoint(void) { const char **ptr; char type[100]; FILE *fp; if (debugfs_found) return (const char *)debugfs_mountpoint; ptr = debugfs_known_mountpoints; while (*ptr) { if (debugfs_valid_mountpoint(*ptr) == 0) { debugfs_found = 1; strncpy(debugfs_mountpoint, *ptr, sizeof(debugfs_mountpoint)); debugfs_mountpoint[sizeof(debugfs_mountpoint) - 1] = 0; return debugfs_mountpoint; } ptr++; } /* give up and parse /proc/mounts */ fp = fopen("/proc/mounts", "r"); if (fp == NULL) { perror("Error - can't open /proc/mounts for read"); return NULL; } while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", debugfs_mountpoint, type) == 2) { if (strcmp(type, "debugfs") == 0) break; } fclose(fp); if (strcmp(type, "debugfs") != 0) return NULL; debugfs_found = 1; return debugfs_mountpoint; } /* verify that a mountpoint is actually a debugfs instance */ static int debugfs_valid_mountpoint(const char *debugfs) { struct statfs st_fs; if (statfs(debugfs, &st_fs) < 0) return -ENOENT; else if (st_fs.f_type != (long) DEBUGFS_MAGIC) return -ENOENT; return 0; } int debugfs_valid_entry(const char *path) { struct stat st; if (stat(path, &st)) return -errno; return 0; } /* mount the debugfs somewhere if it's not mounted */ char *debugfs_mount(const char *mountpoint) { /* see if it's already mounted */ if (debugfs_find_mountpoint()) { debugfs_premounted = 1; return debugfs_mountpoint; } /* if not mounted and no argument */ if (mountpoint == NULL) mountpoint = "/sys/kernel/debug"; if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) return NULL; /* save the mountpoint */ strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); debugfs_mountpoint[sizeof(debugfs_mountpoint) - 1] = '\0'; debugfs_found = 1; return debugfs_mountpoint; } batctl-2018.0/debugfs.h000066400000000000000000000023671324521615300146570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009 Clark Williams * Copyright (C) 2009 Xiao Guangrong * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef __DEBUGFS_H__ #define __DEBUGFS_H__ #ifndef MAX_PATH # define MAX_PATH 256 #endif #ifndef STR # define _STR(x) #x # define STR(x) _STR(x) #endif extern int debugfs_valid_entry(const char *path); extern char *debugfs_mount(const char *mountpoint); extern int debugfs_make_path(const char *fmt, const char *mesh_iface, char *buffer, int size); #endif /* __DEBUGFS_H__ */ batctl-2018.0/functions.c000066400000000000000000000572201324521615300152410ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "functions.h" #include "bat-hosts.h" #include "sys.h" #include "debug.h" #include "debugfs.h" #include "netlink.h" #define PATH_BUFF_LEN 400 static struct timespec start_time; static char *host_name; char *line_ptr = NULL; const char *fs_compile_out_param[] = { SYS_LOG, SYS_LOG_LEVEL, batctl_settings[BATCTL_SETTINGS_BLA].sysfs_name, batctl_settings[BATCTL_SETTINGS_DAT].sysfs_name, batctl_settings[BATCTL_SETTINGS_NETWORK_CODING].sysfs_name, batctl_settings[BATCTL_SETTINGS_MULTICAST_MODE].sysfs_name, batctl_debug_tables[BATCTL_TABLE_BLA_CLAIMS].debugfs_name, batctl_debug_tables[BATCTL_TABLE_BLA_BACKBONES].debugfs_name, batctl_debug_tables[BATCTL_TABLE_DAT].debugfs_name, batctl_debug_tables[BATCTL_TABLE_NETWORK_CODING_NODES].debugfs_name, batctl_debug_tables[BATCTL_TABLE_MCAST_FLAGS].debugfs_name, NULL, }; void start_timer(void) { clock_gettime(CLOCK_MONOTONIC, &start_time); } double end_timer(void) { struct timespec end_time, diff; clock_gettime(CLOCK_MONOTONIC, &end_time); diff.tv_sec = end_time.tv_sec - start_time.tv_sec; diff.tv_nsec = end_time.tv_nsec - start_time.tv_nsec; if (diff.tv_nsec < 0) { diff.tv_sec--; diff.tv_nsec += 1000000000; } return (((double)diff.tv_sec * 1000) + ((double)diff.tv_nsec / 1000000)); } char *ether_ntoa_long(const struct ether_addr *addr) { static char asc[18]; sprintf(asc, "%02x:%02x:%02x:%02x:%02x:%02x", addr->ether_addr_octet[0], addr->ether_addr_octet[1], addr->ether_addr_octet[2], addr->ether_addr_octet[3], addr->ether_addr_octet[4], addr->ether_addr_octet[5]); return asc; } char *get_name_by_macaddr(struct ether_addr *mac_addr, int read_opt) { struct bat_host *bat_host = NULL; if (read_opt & USE_BAT_HOSTS) bat_host = bat_hosts_find_by_mac((char *)mac_addr); if (!bat_host) host_name = ether_ntoa_long((struct ether_addr *)mac_addr); else host_name = bat_host->name; return host_name; } char *get_name_by_macstr(char *mac_str, int read_opt) { struct ether_addr *mac_addr; mac_addr = ether_aton(mac_str); if (!mac_addr) return mac_str; return get_name_by_macaddr(mac_addr, read_opt); } int file_exists(const char *fpath) { struct stat st; return stat(fpath, &st) == 0; } static void file_open_problem_dbg(const char *dir, const char *fname, const char *full_path) { const char **ptr; struct stat st; if (strstr(dir, "/sys/")) { if (stat("/sys/", &st) != 0) { fprintf(stderr, "Error - the folder '/sys/' was not found on the system\n"); fprintf(stderr, "Please make sure that the sys filesystem is properly mounted\n"); return; } } if (!file_exists(module_ver_path)) { fprintf(stderr, "Error - batman-adv module has not been loaded\n"); return; } if (!file_exists(dir)) { fprintf(stderr, "Error - mesh has not been enabled yet\n"); fprintf(stderr, "Activate your mesh by adding interfaces to batman-adv\n"); return; } for (ptr = fs_compile_out_param; *ptr; ptr++) { if (strcmp(*ptr, fname) != 0) continue; break; } fprintf(stderr, "Error - can't open file '%s': %s\n", full_path, strerror(errno)); if (*ptr) { fprintf(stderr, "The option you called seems not to be compiled into your batman-adv kernel module.\n"); fprintf(stderr, "Consult the README if you wish to learn more about compiling options into batman-adv.\n"); } } static int str_is_mcast_addr(char *addr) { struct ether_addr *mac_addr = ether_aton(addr); return !mac_addr ? 0 : mac_addr->ether_addr_octet[0] & 0x01; } int read_file(const char *dir, const char *fname, int read_opt, float orig_timeout, float watch_interval, size_t header_lines) { struct ether_addr *mac_addr; struct bat_host *bat_host; int res = EXIT_FAILURE; float last_seen; char full_path[500], *buff_ptr, *space_ptr, extra_char; size_t len = 0; FILE *fp = NULL; size_t line; if (read_opt & USE_BAT_HOSTS) bat_hosts_init(read_opt); snprintf(full_path, sizeof(full_path), "%s%s", dir, fname); open: line = 0; fp = fopen(full_path, "r"); if (!fp) { if (!(read_opt & SILENCE_ERRORS)) file_open_problem_dbg(dir, fname, full_path); goto out; } if (read_opt & CLR_CONT_READ) /* clear screen, set cursor back to 0,0 */ printf("\033[2J\033[0;0f"); read: while (getline(&line_ptr, &len, fp) != -1) { if (line++ < header_lines && read_opt & SKIP_HEADER) continue; /* the buffer will be handled elsewhere */ if (read_opt & USE_READ_BUFF) break; /* skip timed out originators */ if (read_opt & NO_OLD_ORIGS) if (sscanf(line_ptr, "%*s %f", &last_seen) && (last_seen > orig_timeout)) continue; /* translation table: skip multicast */ if (line > header_lines && read_opt & UNICAST_ONLY && strlen(line_ptr) > strlen(" * xx:xx:xx:") && str_is_mcast_addr(line_ptr+3)) continue; /* translation table: skip unicast */ if (line > header_lines && read_opt & MULTICAST_ONLY && strlen(line_ptr) > strlen(" * xx:xx:xx:") && !str_is_mcast_addr(line_ptr+3)) continue; if (!(read_opt & USE_BAT_HOSTS)) { printf("%s", line_ptr); continue; } /* replace mac addresses with bat host names */ buff_ptr = line_ptr; while ((space_ptr = strchr(buff_ptr, ' ')) != NULL) { *space_ptr = '\0'; extra_char = '\0'; if (strlen(buff_ptr) == ETH_STR_LEN + 1) { extra_char = buff_ptr[ETH_STR_LEN]; switch (extra_char) { case ',': case ')': buff_ptr[ETH_STR_LEN] = '\0'; break; default: extra_char = '\0'; break; } } if (strlen(buff_ptr) != ETH_STR_LEN) goto print_plain_buff; mac_addr = ether_aton(buff_ptr); if (!mac_addr) goto print_plain_buff; bat_host = bat_hosts_find_by_mac((char *)mac_addr); if (!bat_host) goto print_plain_buff; if (read_opt & LOG_MODE) printf("%s", bat_host->name); else /* keep table format */ printf("%17s", bat_host->name); goto written; print_plain_buff: printf("%s", buff_ptr); written: if (extra_char != '\0') printf("%c", extra_char); printf(" "); buff_ptr = space_ptr + 1; } printf("%s", buff_ptr); } if (read_opt & CONT_READ) { usleep(1000000 * watch_interval); goto read; } if (read_opt & CLR_CONT_READ) { if (fp) fclose(fp); usleep(1000000 * watch_interval); goto open; } if (line_ptr) res = EXIT_SUCCESS; out: if (fp) fclose(fp); if (read_opt & USE_BAT_HOSTS) bat_hosts_free(); return res; } int write_file(const char *dir, const char *fname, const char *arg1, const char *arg2) { int fd = -1, res = EXIT_FAILURE; char full_path[500]; ssize_t write_len; snprintf(full_path, sizeof(full_path), "%s%s", dir, fname); fd = open(full_path, O_WRONLY); if (fd < 0) { file_open_problem_dbg(dir, fname, full_path); goto out; } if (arg2) write_len = dprintf(fd, "%s %s", arg1, arg2); else write_len = write(fd, arg1, strlen(arg1) + 1); if (write_len < 0) { fprintf(stderr, "Error - can't write to file '%s': %s\n", full_path, strerror(errno)); goto out; } res = EXIT_SUCCESS; out: if (fd >= 0) close(fd); return res; } static int translate_mac_debugfs(const char *mesh_iface, const struct ether_addr *mac, struct ether_addr *mac_out) { enum { tg_start, tg_mac, tg_via, tg_originator, } pos; char full_path[MAX_PATH+1]; char *debugfs_mnt; struct ether_addr *mac_tmp; FILE *f = NULL; size_t len = 0; char *line = NULL; char *input, *saveptr, *token; int line_invalid; bool found = false; debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) return -EOPNOTSUPP; debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" DEBUG_TRANSTABLE_GLOBAL, mesh_iface, full_path, sizeof(full_path)); f = fopen(full_path, "r"); if (!f) return -EOPNOTSUPP; while (getline(&line, &len, f) != -1) { line_invalid = 0; pos = tg_start; input = line; while ((token = strtok_r(input, " \t", &saveptr))) { input = NULL; switch (pos) { case tg_start: if (strcmp(token, "*") != 0) line_invalid = 1; else pos = tg_mac; break; case tg_mac: mac_tmp = ether_aton(token); if (!mac_tmp || memcmp(mac_tmp, mac, ETH_ALEN) != 0) line_invalid = 1; else pos = tg_via; break; case tg_via: if (strcmp(token, "via") == 0) pos = tg_originator; break; case tg_originator: mac_tmp = ether_aton(token); if (!mac_tmp) { line_invalid = 1; } else { memcpy(mac_out, mac_tmp, ETH_ALEN); found = true; goto out; } break; } if (line_invalid) break; } } out: if (f) fclose(f); free(line); if (found) return 0; else return -ENOENT; } struct ether_addr *translate_mac(const char *mesh_iface, const struct ether_addr *mac) { struct ether_addr in_mac; static struct ether_addr out_mac; struct ether_addr *mac_result; int ret; /* input mac has to be copied because it could be in the shared * ether_aton buffer */ memcpy(&in_mac, mac, sizeof(in_mac)); memcpy(&out_mac, mac, sizeof(out_mac)); mac_result = &out_mac; ret = translate_mac_netlink(mesh_iface, &in_mac, mac_result); if (ret == -EOPNOTSUPP) translate_mac_debugfs(mesh_iface, &in_mac, mac_result); return mac_result; } static int resolve_l3addr(int ai_family, const char *asc, void *l3addr) { int ret; struct addrinfo hints; struct addrinfo *res; struct sockaddr_in *inet4; struct sockaddr_in6 *inet6; memset(&hints, 0, sizeof(hints)); hints.ai_family = ai_family; ret = getaddrinfo(asc, NULL, &hints, &res); if (ret) return -EADDRNOTAVAIL; if (res) { switch (ai_family) { case AF_INET: inet4 = (struct sockaddr_in *)res->ai_addr; memcpy(l3addr, &inet4->sin_addr.s_addr, sizeof(inet4->sin_addr.s_addr)); break; case AF_INET6: inet6 = (struct sockaddr_in6 *)res->ai_addr; memcpy(l3addr, &inet6->sin6_addr.s6_addr, sizeof(inet6->sin6_addr.s6_addr)); break; default: ret = -EINVAL; } } freeaddrinfo(res); return ret; } static void request_mac_resolve(int ai_family, const void *l3addr) { const struct sockaddr *sockaddr; struct sockaddr_in inet4; struct sockaddr_in6 inet6; size_t sockaddr_len; int sock; char t = 0; sock = socket(ai_family, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return; switch (ai_family) { case AF_INET: memset(&inet4, 0, sizeof(inet4)); inet4.sin_family = ai_family; inet4.sin_port = htons(9); memcpy(&inet4.sin_addr.s_addr, l3addr, sizeof(inet4.sin_addr.s_addr)); sockaddr = (const struct sockaddr *)&inet4; sockaddr_len = sizeof(inet4); break; case AF_INET6: memset(&inet6, 0, sizeof(inet6)); inet6.sin6_family = ai_family; inet6.sin6_port = htons(9); memcpy(&inet6.sin6_addr.s6_addr, l3addr, sizeof(inet6.sin6_addr.s6_addr)); sockaddr = (const struct sockaddr *)&inet6; sockaddr_len = sizeof(inet6); break; default: close(sock); return; } sendto(sock, &t, sizeof(t), 0, sockaddr, sockaddr_len); close(sock); } struct resolve_mac_nl_arg { int ai_family; const void *l3addr; struct ether_addr *mac_result; int found; }; static struct nla_policy neigh_policy[NDA_MAX+1] = { [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, [NDA_PROBES] = { .type = NLA_U32 }, }; static int resolve_mac_from_parse(struct nl_msg *msg, void *arg) { struct nlattr *tb[NDA_MAX + 1]; struct ndmsg *nm; int ret; int l3_len; struct resolve_mac_nl_arg *nl_arg = arg; uint8_t *mac; uint8_t *l3addr; nm = nlmsg_data(nlmsg_hdr(msg)); ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*nm), tb, NDA_MAX, neigh_policy); if (ret < 0) goto err; if (nl_arg->ai_family != nm->ndm_family) goto err; switch (nl_arg->ai_family) { case AF_INET: l3_len = 4; break; case AF_INET6: l3_len = 16; break; default: l3_len = 0; } if (l3_len == 0) goto err; if (!tb[NDA_LLADDR] || !tb[NDA_DST]) goto err; if (nla_len(tb[NDA_LLADDR]) != ETH_ALEN) goto err; if (nla_len(tb[NDA_DST]) != l3_len) goto err; mac = nla_data(tb[NDA_LLADDR]); l3addr = nla_data(tb[NDA_DST]); if (memcmp(nl_arg->l3addr, l3addr, l3_len) == 0) { memcpy(nl_arg->mac_result, mac, ETH_ALEN); nl_arg->found = 1; } err: if (nl_arg->found) return NL_STOP; else return NL_OK; } static struct ether_addr *resolve_mac_from_cache(int ai_family, const void *l3addr) { struct nl_sock *sock; struct ether_addr *mac_result = NULL; static struct ether_addr mac_tmp; int ret; struct rtgenmsg gmsg = { .rtgen_family = ai_family, }; struct nl_cb *cb = NULL; struct resolve_mac_nl_arg arg = { .ai_family = ai_family, .l3addr = l3addr, .mac_result = &mac_tmp, .found = 0, }; sock = nl_socket_alloc(); if (!sock) goto err; ret = nl_connect(sock, NETLINK_ROUTE); if (ret < 0) goto err; ret = nl_send_simple(sock, RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP, &gmsg, sizeof(gmsg)); if (ret < 0) goto err; cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) goto err; nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, resolve_mac_from_parse, &arg); ret = nl_recvmsgs(sock, cb); if (ret < 0) goto err; if (arg.found) mac_result = &mac_tmp; err: if (cb) nl_cb_put(cb); if (sock) nl_socket_free(sock); return mac_result; } static struct ether_addr *resolve_mac_from_addr(int ai_family, const char *asc) { uint8_t ipv4_addr[4]; uint8_t ipv6_addr[16]; void *l3addr; int ret; int retries = 5; struct ether_addr *mac_result = NULL; switch (ai_family) { case AF_INET: l3addr = ipv4_addr; break; case AF_INET6: l3addr = ipv6_addr; break; default: return NULL; } ret = resolve_l3addr(ai_family, asc, l3addr); if (ret < 0) return NULL; while (retries-- && !mac_result) { mac_result = resolve_mac_from_cache(ai_family, l3addr); if (!mac_result) { request_mac_resolve(ai_family, l3addr); usleep(200000); } } return mac_result; } struct ether_addr *resolve_mac(const char *asc) { struct ether_addr *mac_result = NULL; static const int ai_families[] = {AF_INET, AF_INET6}; size_t i; mac_result = ether_aton(asc); if (mac_result) goto out; for (i = 0; i < sizeof(ai_families) / sizeof(*ai_families); i++) { mac_result = resolve_mac_from_addr(ai_families[i], asc); if (mac_result) goto out; } out: return mac_result; } struct vlan_get_link_nl_arg { char *iface; int vid; }; static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { [IFLA_LINKINFO] = { .type = NLA_NESTED }, [IFLA_LINK] = { .type = NLA_U32 }, }; static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { [IFLA_INFO_DATA] = { .type = NLA_NESTED }, }; static struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, }; /** * vlan_get_link_parse - parse a get_link rtnl message and extract the important * data * @msg: the reply msg * @arg: pointer to the buffer which will store the return values * * Saves the vid in arg::vid in case of success or -1 otherwise */ static int vlan_get_link_parse(struct nl_msg *msg, void *arg) { struct vlan_get_link_nl_arg *nl_arg = arg; struct nlmsghdr *n = nlmsg_hdr(msg); struct nlattr *tb[IFLA_MAX + 1]; struct nlattr *li[IFLA_INFO_MAX + 1]; struct nlattr *vi[IFLA_VLAN_MAX + 1]; int ret; int idx; if (!nlmsg_valid_hdr(n, sizeof(struct ifinfomsg))) return -NLE_MSG_TOOSHORT; ret = nlmsg_parse(n, sizeof(struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy); if (ret < 0) return ret; if (!tb[IFLA_LINK]) return -NLE_MISSING_ATTR; /* parse subattributes linkinfo */ if (!tb[IFLA_LINKINFO]) return -NLE_MISSING_ATTR; ret = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy); if (ret < 0) return ret; if (!li[IFLA_INFO_KIND]) return -NLE_MISSING_ATTR; if (strcmp(nla_data(li[IFLA_INFO_KIND]), "vlan") != 0) goto err; /* parse subattributes info_data for vlan */ if (!li[IFLA_INFO_DATA]) return -NLE_MISSING_ATTR; ret = nla_parse_nested(vi, IFLA_VLAN_MAX, li[IFLA_INFO_DATA], vlan_policy); if (ret < 0) return ret; if (!vi[IFLA_VLAN_ID]) return -NLE_MISSING_ATTR; /* get parent link name */ idx = *(int *)nla_data(tb[IFLA_LINK]); free(nl_arg->iface); nl_arg->iface = malloc(IFNAMSIZ + 1); if (!nl_arg->iface) goto err; if (!if_indextoname(idx, nl_arg->iface)) goto err; /* get the corresponding vid */ nl_arg->vid = *(int *)nla_data(vi[IFLA_VLAN_ID]); err: if (nl_arg->vid >= 0) return NL_STOP; else return NL_OK; } /** * vlan_get_link - convert a VLAN interface into its parent one * @ifname: the interface to convert * @parent: buffer where the parent interface name will be written (allocated by * this function) * * Returns the vlan identifier on success or -1 on error */ int vlan_get_link(const char *ifname, char **parent) { struct nl_sock *sock; int ret; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC, .ifi_index = if_nametoindex(ifname), }; struct nl_cb *cb = NULL; struct vlan_get_link_nl_arg arg = { .iface = NULL, .vid = -1, }; *parent = NULL; sock = nl_socket_alloc(); if (!sock) goto err; ret = nl_connect(sock, NETLINK_ROUTE); if (ret < 0) goto err; ret = nl_send_simple(sock, RTM_GETLINK, NLM_F_REQUEST, &ifinfo, sizeof(ifinfo)); if (ret < 0) goto err; cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) goto err; nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, vlan_get_link_parse, &arg); ret = nl_recvmsgs(sock, cb); if (ret < 0) goto err; *parent = arg.iface; err: if (cb) nl_cb_put(cb); if (sock) nl_socket_free(sock); return arg.vid; } int print_routing_algos(void) { int err; err = netlink_print_routing_algos(); if (err == -EOPNOTSUPP) err = debug_print_routing_algos(); return err; } int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg) { struct ifinfomsg rt_hdr = { .ifi_family = IFLA_UNSPEC, }; struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int err = 0; int ret; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; ret = nl_connect(sock, NETLINK_ROUTE); if (ret < 0) { err = -ENOMEM; goto err_free_sock; } cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { err = -ENOMEM; goto err_free_sock; } nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, func, arg); msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP); if (!msg) { err = -ENOMEM; goto err_free_cb; } ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_u32(msg, IFLA_MASTER, ifindex); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nl_send_auto_complete(sock, msg); if (ret < 0) goto err_free_msg; nl_recvmsgs(sock, cb); err_free_msg: nlmsg_free(msg); err_free_cb: nl_cb_put(cb); err_free_sock: nl_socket_free(sock); return err; } static int ack_errno_handler(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *nlerr, void *arg) { int *err = arg; *err = nlerr->error; return NL_STOP; } static int ack_wait_handler(struct nl_msg *msg __maybe_unused, void *arg __maybe_unused) { return NL_STOP; } int netlink_simple_request(struct nl_msg *msg) { struct nl_sock *sock; struct nl_cb *cb; int err = 0; int ret; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; ret = nl_connect(sock, NETLINK_ROUTE); if (ret < 0) { err = -ENOMEM; goto err_free_sock; } cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { err = -ENOMEM; goto err_free_sock; } nl_cb_err(cb, NL_CB_CUSTOM, ack_errno_handler, &err); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); ret = nl_send_auto_complete(sock, msg); if (ret < 0) goto err_free_cb; // ack_errno_handler sets err on errors err = 0; nl_recvmsgs(sock, cb); err_free_cb: nl_cb_put(cb); err_free_sock: nl_socket_free(sock); return err; } int check_mesh_iface(char *mesh_iface) { char *base_dev = NULL; char path_buff[PATH_BUFF_LEN]; int ret = -1, vid; DIR *dir; /* use the parent interface if this is a VLAN */ vid = vlan_get_link(mesh_iface, &base_dev); if (vid >= 0) snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH, base_dev, vid); else snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); /* try to open the mesh sys directory */ dir = opendir(path_buff); if (!dir) goto out; closedir(dir); ret = 0; out: if (base_dev) free(base_dev); return ret; } int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface) { char path_buff[PATH_BUFF_LEN]; int res; /* check if this device actually belongs to the mesh interface */ snprintf(path_buff, sizeof(path_buff), SYS_MESH_IFACE_FMT, hard_iface); res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); if (res != EXIT_SUCCESS) { fprintf(stderr, "Error - the directory '%s' could not be read: %s\n", path_buff, strerror(errno)); fprintf(stderr, "Is the batman-adv module loaded and sysfs mounted ?\n"); return EXIT_FAILURE; } if (line_ptr[strlen(line_ptr) - 1] == '\n') line_ptr[strlen(line_ptr) - 1] = '\0'; if (strcmp(line_ptr, mesh_iface) != 0) { fprintf(stderr, "Error - interface %s is part of batman network %s, not %s\n", hard_iface, line_ptr, mesh_iface); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int get_random_bytes_syscall(void *buf __maybe_unused, size_t buflen __maybe_unused) { #ifdef SYS_getrandom return syscall(SYS_getrandom, buf, buflen, 0); #else return -EOPNOTSUPP; #endif } static int get_random_bytes_urandom(void *buf, size_t buflen) { int fd; ssize_t r; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) return -EOPNOTSUPP; r = read(fd, buf, buflen); close(fd); if (r < 0) return -EOPNOTSUPP; if ((size_t)r != buflen) return -EOPNOTSUPP; return 0; } static int get_random_bytes_fallback(void *buf, size_t buflen) { struct timespec now; static int initialized = 0; size_t i; uint8_t *bufc = buf; /* this is not a good source for randomness */ if (!initialized) { clock_gettime(CLOCK_MONOTONIC, &now); srand(now.tv_sec ^ now.tv_nsec); initialized = 1; } for (i = 0; i < buflen; i++) bufc[i] = rand() & 0xff; return 0; } void get_random_bytes(void *buf, size_t buflen) { int ret; ret = get_random_bytes_syscall(buf, buflen); if (ret != -EOPNOTSUPP) return; ret = get_random_bytes_urandom(buf, buflen); if (ret != -EOPNOTSUPP) return; get_random_bytes_fallback(buf, buflen); } void check_root_or_die(const char *cmd) { if (geteuid() != 0) { fprintf(stderr, "Error - you must be root to run '%s' !\n", cmd); exit(EXIT_FAILURE); } } batctl-2018.0/functions.h000066400000000000000000000047651324521615300152540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_FUNCTIONS_H #define _BATCTL_FUNCTIONS_H #include #include #include #include #define ETH_STR_LEN 17 #define BATMAN_ADV_TAG "batman-adv:" #define PATH_BUFF_LEN 400 /* return time delta from start to end in milliseconds */ void start_timer(void); double end_timer(void); char *ether_ntoa_long(const struct ether_addr *addr); char *get_name_by_macaddr(struct ether_addr *mac_addr, int read_opt); char *get_name_by_macstr(char *mac_str, int read_opt); int file_exists(const char *fpath); int read_file(const char *dir, const char *path, int read_opt, float orig_timeout, float watch_interval, size_t header_lines); int write_file(const char *dir, const char *fname, const char *arg1, const char *arg2); struct ether_addr *translate_mac(const char *mesh_iface, const struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); int vlan_get_link(const char *ifname, char **parent);\ int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg); int netlink_simple_request(struct nl_msg *msg); int check_mesh_iface(char *mesh_iface); int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface); void get_random_bytes(void *buf, size_t buflen); void check_root_or_die(const char *cmd); int print_routing_algos(void); extern char *line_ptr; enum { NO_FLAGS = 0x00, CONT_READ = 0x01, CLR_CONT_READ = 0x02, USE_BAT_HOSTS = 0x04, LOG_MODE = 0x08, USE_READ_BUFF = 0x10, SILENCE_ERRORS = 0x20, NO_OLD_ORIGS = 0x40, COMPAT_FILTER = 0x80, SKIP_HEADER = 0x100, UNICAST_ONLY = 0x200, MULTICAST_ONLY = 0x400, }; #endif batctl-2018.0/genl.c000066400000000000000000000067411324521615300141600ustar00rootroot00000000000000// SPDX-License-Identifier: ISC /* * Copyright (c) 2007, 2008 Johannes Berg * Copyright (c) 2007 Andy Lutomirski * Copyright (c) 2007 Mike Kershaw * Copyright (c) 2008-2009 Luis R. Rodriguez * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This ought to be provided by libnl - but was borrowed from iw/genl.c */ #include "netlink.h" #include "main.h" #include #include #include #include #include #include #include #include "batman_adv.h" static int mcast_error_handler(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *err, void *arg) { int *ret = arg; *ret = err->error; return NL_STOP; } static int mcast_ack_handler(struct nl_msg *msg __maybe_unused, void *arg) { int *ret = arg; *ret = 0; return NL_STOP; } struct mcast_handler_args { const char *group; int id; }; static int mcast_family_handler(struct nl_msg *msg, void *arg) { struct mcast_handler_args *grp = arg; struct nlattr *tb[CTRL_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *mcgrp; int rem_mcgrp; nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[CTRL_ATTR_MCAST_GROUPS]) return NL_SKIP; nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), nla_len(mcgrp), NULL); if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) continue; if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) continue; grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); break; } return NL_SKIP; } int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group) { struct nl_msg *msg; struct nl_cb *cb; int ret, ctrlid; struct mcast_handler_args grp = { .group = group, .id = -ENOENT, }; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { ret = -ENOMEM; goto out_fail_cb; } ctrlid = genl_ctrl_resolve(sock, "nlctrl"); genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); ret = -ENOBUFS; NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); ret = nl_send_auto_complete(sock, msg); if (ret < 0) goto out; ret = 1; nl_cb_err(cb, NL_CB_CUSTOM, mcast_error_handler, &ret); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, mcast_ack_handler, &ret); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, mcast_family_handler, &grp); while (ret > 0) nl_recvmsgs(sock, cb); if (ret == 0) ret = grp.id; nla_put_failure: out: nl_cb_put(cb); out_fail_cb: nlmsg_free(msg); return ret; } batctl-2018.0/genl.h000066400000000000000000000020771324521615300141630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_GENL_H #define _BATCTL_GENL_H #include #include int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group); #endif /* _BATCTL_GENL_H */ batctl-2018.0/hash.c000066400000000000000000000213511324521615300141500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "hash.h" #include #include #include "allocate.h" /* clears the hash */ void hash_init(struct hashtable_t *hash) { int i; hash->elements = 0; for (i = 0; i < hash->size; i++) hash->table[i] = NULL; } /* remove the hash structure. if hashdata_free_cb != NULL, * this function will be called to remove the elements inside of the hash. * if you don't remove the elements, memory might be leaked. */ void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb) { struct element_t *bucket, *last_bucket; int i; for (i = 0; i < hash->size; i++) { bucket = hash->table[i]; while (bucket != NULL) { if (free_cb != NULL) free_cb(bucket->data); last_bucket = bucket; bucket = bucket->next; debugFree(last_bucket, 1301); } } hash_destroy(hash); } /* adds data to the hashtable and reuse bucket. * returns 0 on success, -1 on error */ static int hash_add_bucket(struct hashtable_t *hash, void *data, struct element_t *bucket, int check_duplicate) { int index; struct element_t *bucket_it, *prev_bucket = NULL; index = hash->choose(data, hash->size); bucket_it = hash->table[index]; while (bucket_it != NULL) { if (check_duplicate && hash->compare(bucket_it->data, data)) return -1; prev_bucket = bucket_it; bucket_it = bucket_it->next; } /* init the new bucket */ bucket->data = data; bucket->next = NULL; /* and link it */ if (prev_bucket == NULL) hash->table[index] = bucket; else prev_bucket->next = bucket; hash->elements++; return 0; } /* free only the hashtable and the hash itself. */ void hash_destroy(struct hashtable_t *hash) { debugFree(hash->table, 1302); debugFree(hash, 1303); } /* free hash_it_t pointer when stopping hash_iterate early */ void hash_iterate_free(struct hash_it_t *iter_in) { debugFree(iter_in, 1304); } /* iterate though the hash. first element is selected with iter_in NULL. * use the returned iterator to access the elements until hash_it_t returns * NULL. */ struct hash_it_t *hash_iterate(struct hashtable_t *hash, struct hash_it_t *iter_in) { struct hash_it_t *iter; if (iter_in == NULL) { iter = debugMalloc(sizeof(struct hash_it_t), 301); if (!iter) return NULL; iter->index = -1; iter->bucket = NULL; iter->prev_bucket = NULL; } else { iter = iter_in; } /* sanity checks first (if our bucket got deleted in the last * iteration): */ if (iter->bucket != NULL) { if (iter->first_bucket != NULL) { /* we're on the first element and it got removed after * the last iteration. */ if ((*iter->first_bucket) != iter->bucket) { /* there are still other elements in the list */ if ((*iter->first_bucket) != NULL) { iter->prev_bucket = NULL; iter->bucket = (*iter->first_bucket); iter->first_bucket = &hash->table[iter->index]; return iter; } else { iter->bucket = NULL; } } } else if (iter->prev_bucket != NULL) { /* we're not on the first element, and the bucket got * removed after the last iteration. The last bucket's * next pointer is not pointing to our actual bucket * anymore. Select the next. */ if (iter->prev_bucket->next != iter->bucket) iter->bucket = iter->prev_bucket; } } /* now as we are sane, select the next one if there is some */ if (iter->bucket != NULL) { if (iter->bucket->next != NULL) { iter->prev_bucket = iter->bucket; iter->bucket = iter->bucket->next; iter->first_bucket = NULL; return iter; } } /* if not returned yet, we've reached the last one on the index and * have to search forward */ iter->index++; /* go through the entries of the hash table */ while (iter->index < hash->size) { if ((hash->table[iter->index]) == NULL) { iter->index++; continue; } iter->prev_bucket = NULL; iter->bucket = hash->table[iter->index]; iter->first_bucket = &hash->table[iter->index]; return iter; /* if this table entry is not null, return it */ } /* nothing to iterate over anymore */ hash_iterate_free(iter); return NULL; } /* allocates and clears the hash */ struct hashtable_t *hash_new(int size, hashdata_compare_cb compare, hashdata_choose_cb choose) { struct hashtable_t *hash; hash = debugMalloc(sizeof(struct hashtable_t), 302); if (!hash) return NULL; hash->size = size; hash->table = debugMalloc(sizeof(struct element_t *)*size, 303); if (!hash->table) { debugFree(hash, 1305); return NULL; } hash_init(hash); hash->compare = compare; hash->choose = choose; return hash; } /* adds data to the hashtable. returns 0 on success, -1 on error */ int hash_add(struct hashtable_t *hash, void *data) { int ret; struct element_t *bucket; /* found the tail of the list, add new element */ bucket = debugMalloc(sizeof(struct element_t), 304); if (!bucket) return -1; ret = hash_add_bucket(hash, data, bucket, 1); if (ret < 0) debugFree(bucket, 1307); return ret; } /* finds data, based on the key in keydata. returns the found data on success, * or NULL on error */ void *hash_find(struct hashtable_t *hash, void *keydata) { int index; struct element_t *bucket; index = hash->choose(keydata , hash->size); bucket = hash->table[index]; while (bucket != NULL) { if (hash->compare(bucket->data, keydata)) return bucket->data; bucket = bucket->next; } return NULL; } /* remove bucket (this might be used in hash_iterate() if you already found * the bucket you want to delete and don't need the overhead to find it again * with hash_remove(). But usually, you don't want to use this function, as it * fiddles with hash-internals. */ void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t) { void *data_save; /* save the pointer to the data */ data_save = hash_it_t->bucket->data; if (hash_it_t->prev_bucket != NULL) hash_it_t->prev_bucket->next = hash_it_t->bucket->next; else if (hash_it_t->first_bucket != NULL) (*hash_it_t->first_bucket) = hash_it_t->bucket->next; debugFree(hash_it_t->bucket, 1306); hash->elements--; return data_save; } /* removes data from hash, if found. returns pointer do data on success, * so you can remove the used structure yourself, or NULL on error . * data could be the structure you use with just the key filled, * we just need the key for comparing. */ void *hash_remove(struct hashtable_t *hash, void *data) { struct hash_it_t hash_it_t; hash_it_t.index = hash->choose(data, hash->size); hash_it_t.bucket = hash->table[hash_it_t.index]; hash_it_t.prev_bucket = NULL; while (hash_it_t.bucket != NULL) { if (hash->compare(hash_it_t.bucket->data, data)) { int bucket_same; bucket_same = (hash_it_t.bucket == hash->table[hash_it_t.index]); hash_it_t.first_bucket = (bucket_same ? &hash->table[hash_it_t.index] : NULL); return hash_remove_bucket(hash, &hash_it_t); } hash_it_t.prev_bucket = hash_it_t.bucket; hash_it_t.bucket = hash_it_t.bucket->next; } return NULL; } /* resize the hash, returns the pointer to the new hash or NULL on error. * removes the old hash on success. */ struct hashtable_t *hash_resize(struct hashtable_t *hash, int size) { struct hashtable_t *new_hash; struct element_t *bucket; int i; /* initialize a new hash with the new size */ new_hash = hash_new(size, hash->compare, hash->choose); if (!new_hash) return NULL; /* copy the elements */ for (i = 0; i < hash->size; i++) { while (hash->table[i]) { bucket = hash->table[i]; hash->table[i] = bucket->next; hash_add_bucket(new_hash, bucket->data, bucket, 0); } } /* remove hash and eventual overflow buckets but not the * content itself. */ hash_delete(hash, NULL); return new_hash; } /* print the hash table for debugging */ /* void hash_debug(struct hashtable_t *hash) { int i; struct element_t *bucket; for (i = 0; i < hash->size; i++) { printf("[%d] ", i); bucket = hash->table[i]; while (bucket) { printf("-> [%10p] ", (void *)bucket); bucket = bucket->next; } printf("\n"); } printf("\n"); }*/ batctl-2018.0/hash.h000066400000000000000000000076261324521615300141660ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2006-2018 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATMAN_HASH_H #define _BATMAN_HASH_H typedef int (*hashdata_compare_cb)(void *, void *); typedef int (*hashdata_choose_cb)(void *, int); typedef void (*hashdata_free_cb)(void *); struct element_t { void *data; /* pointer to the data */ struct element_t *next; /* overflow bucket pointer */ }; struct hash_it_t { int index; struct element_t *bucket; struct element_t *prev_bucket; struct element_t **first_bucket; }; struct hashtable_t { struct element_t **table; /* the hashtable itself, with the * buckets */ int elements; /* number of elements registered */ int size; /* size of hashtable */ hashdata_compare_cb compare; /* callback to a compare function. * should compare 2 element datas for * their keys, return 0 if same and not * 0 if not same */ hashdata_choose_cb choose; /* the hashfunction, should return an * index based on the key in the data * of the first argument and the size * the second */ }; /* clears the hash */ void hash_init(struct hashtable_t *hash); /* allocates and clears the hash */ struct hashtable_t *hash_new(int size, hashdata_compare_cb compare, hashdata_choose_cb choose); /* remove bucket (this might be used in hash_iterate() if you already found * the bucket you want to delete and don't need the overhead to find it again * with hash_remove(). But usually, you don't want to use this function, as it * fiddles with hash-internals. */ void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t); /* remove the hash structure. if hashdata_free_cb != NULL, * this function will be called to remove the elements inside of the hash. * if you don't remove the elements, memory might be leaked. */ void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb); /* free only the hashtable and the hash itself. */ void hash_destroy(struct hashtable_t *hash); /* adds data to the hashtable. returns 0 on success, -1 on error */ int hash_add(struct hashtable_t *hash, void *data); /* removes data from hash, if found. returns pointer do data on success, * so you can remove the used structure yourself, or NULL on error . * data could be the structure you use with just the key filled, * we just need the key for comparing. */ void *hash_remove(struct hashtable_t *hash, void *data); /* finds data, based on the key in keydata. returns the found data on success, * or NULL on error */ void *hash_find(struct hashtable_t *hash, void *keydata); /* resize the hash, returns the pointer to the new hash or NULL on error. * removes the old hash on success */ struct hashtable_t *hash_resize(struct hashtable_t *hash, int size); /* print the hash table for debugging */ void hash_debug(struct hashtable_t *hash); /* iterate though the hash. first element is selected with iter_in NULL. * use the returned iterator to access the elements until hash_it_t * returns NULL. */ struct hash_it_t *hash_iterate(struct hashtable_t *hash, struct hash_it_t *iter_in); /* free hash_it_t pointer when stopping hash_iterate early */ void hash_iterate_free(struct hash_it_t *iter_in); #endif batctl-2018.0/icmp_helper.c000066400000000000000000000347321324521615300155230ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Marek Lindner , Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "icmp_helper.h" #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "batadv_packet.h" #include "debug.h" #include "debugfs.h" #include "functions.h" #include "list.h" #include "netlink.h" #ifndef ETH_P_BATMAN #define ETH_P_BATMAN 0x4305 #endif /* ETH_P_BATMAN */ static LIST_HEAD(interface_list); static size_t direct_reply_len; static uint8_t uid; static uint8_t primary_mac[ETH_ALEN]; static uint8_t icmp_buffer[BATADV_ICMP_MAX_PACKET_SIZE]; #define BATADV_ICMP_MIN_PACKET_SIZE sizeof(struct batadv_icmp_packet) #define BADADV_ICMP_ETH_OFFSET(member) \ (ETH_HLEN + offsetof(struct batadv_icmp_packet, member)) static struct icmp_interface *icmp_interface_find(const char *ifname) { struct icmp_interface *found = NULL; struct icmp_interface *iface; list_for_each_entry(iface, &interface_list, list) { if (strcmp(iface->name, ifname) == 0) { found = iface; break; } } return found; } static bool icmp_interfaces_is_my_mac(uint8_t dst[ETH_ALEN]) { struct icmp_interface *iface; list_for_each_entry(iface, &interface_list, list) { if (memcmp(iface->mac, dst, ETH_ALEN) == 0) return true; } return false; } void icmp_interface_destroy(struct icmp_interface *iface) { close(iface->sock); list_del(&iface->list); free(iface); } static int icmp_interface_filter(int sock, int uid) { struct sock_fprog filter; struct sock_filter accept_icmp[] = { /* load ethernet proto */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_header, ether_type)), /* jump to ret 0 when it is != 0x4305 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_P_BATMAN, 0, 14), /* load pktlen */ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* jump to ret 0 when it is < 34 */ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, ETH_HLEN + BATADV_ICMP_MIN_PACKET_SIZE, 0, 12), /* jump to ret 0 when it is > 130 */ BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, ETH_HLEN + BATADV_ICMP_MAX_PACKET_SIZE, 11, 0), /* load batman-adv type */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, BADADV_ICMP_ETH_OFFSET(packet_type)), /* jump to ret 0 when it is != BATADV_ICMP */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BATADV_ICMP, 0, 9), /* load batman-adv version */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, BADADV_ICMP_ETH_OFFSET(version)), /* jump to ret 0 when it is != 15 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BATADV_COMPAT_VERSION, 0, 7), /* load batman-adv icmp msg_type */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, BADADV_ICMP_ETH_OFFSET(msg_type)), /* accept BATADV_ECHO_REPLY or go to next check */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BATADV_ECHO_REPLY, 2, 0), /* accept BATADV_DESTINATION_UNREACHABLE or go to next check */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BATADV_DESTINATION_UNREACHABLE, 1, 0), /* accept BATADV_TTL_EXCEEDED or go to ret 0 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BATADV_TTL_EXCEEDED, 0, 3), /* load batman-adv icmp uid */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, BADADV_ICMP_ETH_OFFSET(uid)), /* jump to ret 0 when it is not our uid */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, uid, 0, 1), /* accept 130 bytes */ BPF_STMT(BPF_RET + BPF_K, ETH_HLEN + BATADV_ICMP_MAX_PACKET_SIZE), /* ret 0 -> reject packet */ BPF_STMT(BPF_RET + BPF_K, 0), }; memset(&filter, 0, sizeof(filter)); filter.len = sizeof(accept_icmp) / sizeof(*accept_icmp); filter.filter = accept_icmp; if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) return -1; return 0; } static int icmp_interface_add(const char *ifname, const uint8_t mac[ETH_ALEN]) { struct icmp_interface *iface; struct sockaddr_ll sll; struct ifreq req; int ret; iface = malloc(sizeof(*iface)); if (!iface) return -ENOMEM; iface->mark = 1; memcpy(iface->mac, mac, ETH_ALEN); strncpy(iface->name, ifname, IFNAMSIZ); iface->name[sizeof(iface->name) - 1] = '\0'; iface->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (iface->sock < 0) { perror("Error - can't create raw socket"); ret = -errno; goto free_iface; } memset(&req, 0, sizeof(struct ifreq)); strncpy(req.ifr_name, ifname, IFNAMSIZ); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; ret = ioctl(iface->sock, SIOCGIFINDEX, &req); if (ret < 0) { perror("Error - can't create raw socket (SIOCGIFINDEX)"); ret = -errno; goto close_sock; } memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_protocol = htons(ETH_P_ALL); sll.sll_pkttype = PACKET_HOST; sll.sll_ifindex = req.ifr_ifindex; ret = bind(iface->sock, (struct sockaddr *)&sll, sizeof(struct sockaddr_ll)); if (ret < 0) { perror("Error - can't bind raw socket"); ret = -errno; goto close_sock; } ret = icmp_interface_filter(iface->sock, uid); if (ret < 0) { fprintf(stderr, "Error - can't add filter to raw socket: %s\n", strerror(-ret)); goto close_sock; } list_add(&iface->list, &interface_list); return 0; close_sock: close(iface->sock); free_iface: free(iface); return ret; } int icmp_interfaces_init(void) { get_random_bytes(&uid, 1); return 0; } static struct nla_policy link_policy[IFLA_MAX + 1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MASTER] = { .type = NLA_U32 }, [IFLA_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN, }, }; struct icmp_interface_update_arg { int ifindex; }; static int icmp_interface_update_parse(struct nl_msg *msg, void *arg) { struct icmp_interface_update_arg *update_arg = arg; struct nlattr *attrs[IFLA_MAX + 1]; struct icmp_interface *iface; struct ifinfomsg *ifm; char *ifname; int ret; int master; uint8_t *mac; ifm = nlmsg_data(nlmsg_hdr(msg)); ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, link_policy); if (ret < 0) goto err; if (!attrs[IFLA_IFNAME]) goto err; if (!attrs[IFLA_MASTER]) goto err; if (!attrs[IFLA_ADDRESS]) goto err; ifname = nla_get_string(attrs[IFLA_IFNAME]); master = nla_get_u32(attrs[IFLA_MASTER]); mac = nla_data(attrs[IFLA_ADDRESS]); /* required on older kernels which don't prefilter the results */ if (master != update_arg->ifindex) goto err; /* update or add interface */ iface = icmp_interface_find(ifname); if (!iface) { icmp_interface_add(ifname, mac); goto err; } /* update */ iface->mark = 1; memcpy(iface->mac, mac, ETH_ALEN); err: return NL_OK; } static int get_nexthop_debugfs(const char *mesh_iface, struct ether_addr *mac, uint8_t nexthop[ETH_ALEN], char ifname[IF_NAMESIZE]) { char *tptr; char *temp1, *temp2; char *dest, *neigh, *iface; int lnum, tnum; struct ether_addr *mac_tmp; char path[1024]; FILE *f = NULL; size_t len = 0; char *line = NULL; char *primary_info; debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" "originators", mesh_iface, path, sizeof(path)); f = fopen(path, "r"); if (!f) return -EOPNOTSUPP; lnum = 0; while (getline(&line, &len, f) != -1) { lnum++; /* find primary mac */ if (lnum == 1) { primary_info = strstr(line, "MainIF/MAC: "); if (!primary_info) continue; primary_info += 12; temp1 = strstr(primary_info, "/"); if (!temp1) continue; temp1++; temp2 = strstr(line, " "); if (!temp2) continue; temp2[0] = '\0'; mac_tmp = ether_aton(temp1); if (!mac_tmp) continue; memcpy(primary_mac, mac_tmp, ETH_ALEN); } if (lnum < 3) continue; /* find correct neighbor */ for (tptr = line, tnum = 0;; tptr = NULL, tnum++) { tptr = strtok_r(tptr, "\t []()", &temp2); if (!tptr) break; switch (tnum) { case 0: dest = tptr; break; case 2: break; case 3: neigh = tptr; break; case 4: iface = tptr; break; default: break; } } if (tnum <= 4) continue; mac_tmp = ether_aton(dest); if (!mac_tmp || memcmp(mac_tmp, mac, ETH_ALEN) != 0) continue; mac_tmp = ether_aton(neigh); if (!mac_tmp) continue; memcpy(nexthop, mac_tmp, ETH_ALEN); strncpy(ifname, iface, IF_NAMESIZE); ifname[IF_NAMESIZE - 1] = '\0'; break; } free(line); fclose(f); return 0; } static void icmp_interface_unmark(void) { struct icmp_interface *iface; list_for_each_entry(iface, &interface_list, list) iface->mark = 0; } static void icmp_interface_sweep(void) { struct icmp_interface *iface, *safe; list_for_each_entry_safe(iface, safe, &interface_list, list) { if (iface->mark) continue; icmp_interface_destroy(iface); } } static int icmp_interface_update(const char *mesh_iface) { struct icmp_interface_update_arg update_arg; update_arg.ifindex = if_nametoindex(mesh_iface); if (!update_arg.ifindex) return -errno; /* unmark current interface - will be marked again by query */ icmp_interface_unmark(); query_rtnl_link(update_arg.ifindex, icmp_interface_update_parse, &update_arg); /* remove old interfaces */ icmp_interface_sweep(); get_primarymac_netlink(mesh_iface, primary_mac); return 0; } static int icmp_interface_send(struct batadv_icmp_header *icmp_packet, size_t packet_len, struct icmp_interface *iface, uint8_t nexthop[ETH_ALEN]) { struct ether_header header; struct iovec vector[2]; header.ether_type = htons(ETH_P_BATMAN); memcpy(header.ether_shost, iface->mac, ETH_ALEN); memcpy(header.ether_dhost, nexthop, ETH_ALEN); vector[0].iov_base = &header; vector[0].iov_len = sizeof(struct ether_header); vector[1].iov_base = icmp_packet; vector[1].iov_len = packet_len; return (int)writev(iface->sock, vector, 2); } int icmp_interface_write(const char *mesh_iface, struct batadv_icmp_header *icmp_packet, size_t len) { struct batadv_icmp_packet_rr *icmp_packet_rr; struct icmp_interface *iface; uint8_t nexthop[ETH_ALEN]; char ifname[IF_NAMESIZE]; struct ether_addr mac; size_t packet_len; int ret; if (len < sizeof(*icmp_packet)) return -EINVAL; if (len >= BATADV_ICMP_MAX_PACKET_SIZE) packet_len = BATADV_ICMP_MAX_PACKET_SIZE; else packet_len = len; if (icmp_packet->packet_type != BATADV_ICMP) return -EINVAL; if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) return -EINVAL; icmp_interface_update(mesh_iface); if (list_empty(&interface_list)) return -EFAULT; /* find best neighbor */ memcpy(&mac, icmp_packet->dst, ETH_ALEN); ret = get_nexthop_netlink(mesh_iface, &mac, nexthop, ifname); if (ret == -EOPNOTSUPP) ret = get_nexthop_debugfs(mesh_iface, &mac, nexthop, ifname); if (ret < 0) goto dst_unreachable; iface = icmp_interface_find(ifname); if (!iface) goto dst_unreachable; direct_reply_len = 0; icmp_packet->uid = uid; memcpy(icmp_packet->orig, primary_mac, ETH_ALEN); /* start RR packet */ icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_packet; if (packet_len == sizeof(*icmp_packet_rr)) memcpy(icmp_packet_rr->rr[0], iface->mac, ETH_ALEN); return icmp_interface_send(icmp_packet, packet_len, iface, nexthop); dst_unreachable: memcpy(icmp_buffer, icmp_packet, packet_len); icmp_packet = (struct batadv_icmp_header *)icmp_buffer; icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE; direct_reply_len = packet_len; return 0; } static int icmp_interface_preselect(fd_set *read_sockets) { struct icmp_interface *iface; int max = 0; FD_ZERO(read_sockets); list_for_each_entry(iface, &interface_list, list) { FD_SET(iface->sock, read_sockets); if (max <= iface->sock) max = iface->sock + 1; } return max; } static ssize_t icmp_interface_get_read_sock(fd_set *read_sockets, struct icmp_interface **piface) { struct icmp_interface *iface; int sock = -1; list_for_each_entry(iface, &interface_list, list) { if (!FD_ISSET(iface->sock, read_sockets)) continue; sock = iface->sock; *piface = iface; break; } return sock; } ssize_t icmp_interface_read(struct batadv_icmp_header *icmp_packet, size_t len, struct timeval *tv) { struct batadv_icmp_packet_rr *icmp_packet_rr; struct icmp_interface *iface; struct ether_header header; struct iovec vector[2]; fd_set read_sockets; size_t packet_len; int max_sock; ssize_t read_len; int read_sock; int res; if (len < sizeof(*icmp_packet)) return -EINVAL; if (len >= BATADV_ICMP_MAX_PACKET_SIZE) packet_len = BATADV_ICMP_MAX_PACKET_SIZE; else packet_len = len; if (direct_reply_len > 0) { memcpy(icmp_packet, icmp_buffer, packet_len); direct_reply_len = 0; return (ssize_t)packet_len; } retry: max_sock = icmp_interface_preselect(&read_sockets); res = select(max_sock, &read_sockets, NULL, NULL, tv); /* timeout, or < 0 error */ if (res <= 0) return res; read_sock = icmp_interface_get_read_sock(&read_sockets, &iface); if (read_sock < 0) return read_sock; vector[0].iov_base = &header; vector[0].iov_len = sizeof(struct ether_header); vector[1].iov_base = icmp_packet; vector[1].iov_len = packet_len; read_len = readv(read_sock, vector, 2); if (read_len < 0) return read_len; if (read_len < ETH_HLEN) goto retry; read_len -= ETH_HLEN; if (read_len < (ssize_t)sizeof(*icmp_packet)) goto retry; if (!icmp_interfaces_is_my_mac(icmp_packet->dst)) goto retry; /* end RR packet */ icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_packet; if (read_len == sizeof(*icmp_packet_rr) && icmp_packet_rr->rr_cur < BATADV_RR_LEN) { memcpy(icmp_packet_rr->rr[icmp_packet_rr->rr_cur], iface->mac, ETH_ALEN); icmp_packet_rr->rr_cur++; } return read_len; } void icmp_interfaces_clean(void) { struct icmp_interface *iface, *safe; list_for_each_entry_safe(iface, safe, &interface_list, list) icmp_interface_destroy(iface); } batctl-2018.0/icmp_helper.h000066400000000000000000000031161324521615300155200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_ICMP_HELPER_H #define _BATCTL_ICMP_HELPER_H #include "main.h" #include #include #include #include #include #include #include #include "batadv_packet.h" #include "list.h" struct timeval; struct icmp_interface { char name[IFNAMSIZ]; uint8_t mac[ETH_ALEN]; int sock; int mark; struct list_head list; }; int icmp_interfaces_init(void); int icmp_interface_write(const char *mesh_iface, struct batadv_icmp_header *icmp_packet, size_t len); void icmp_interfaces_clean(void); ssize_t icmp_interface_read(struct batadv_icmp_header *icmp_packet, size_t len, struct timeval *tv); #endif batctl-2018.0/interface.c000066400000000000000000000235501324521615300151700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "sys.h" #include "functions.h" static void interface_usage(void) { fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); fprintf(stderr, " batctl [options] interface [parameters] [create|destroy]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -M disable automatic creation/removal of batman-adv interface\n"); fprintf(stderr, " \t -h print this help\n"); } static struct nla_policy link_policy[IFLA_MAX + 1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MASTER] = { .type = NLA_U32 }, }; struct print_interfaces_rtnl_arg { int ifindex; }; static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) { struct print_interfaces_rtnl_arg *print_arg = arg; struct nlattr *attrs[IFLA_MAX + 1]; char path_buff[PATH_BUFF_LEN]; struct ifinfomsg *ifm; char *ifname; int ret; const char *status; int master; ifm = nlmsg_data(nlmsg_hdr(msg)); ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, link_policy); if (ret < 0) goto err; if (!attrs[IFLA_IFNAME]) goto err; if (!attrs[IFLA_MASTER]) goto err; ifname = nla_get_string(attrs[IFLA_IFNAME]); master = nla_get_u32(attrs[IFLA_MASTER]); /* required on older kernels which don't prefilter the results */ if (master != print_arg->ifindex) goto err; snprintf(path_buff, sizeof(path_buff), SYS_IFACE_STATUS_FMT, ifname); ret = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); if (ret != EXIT_SUCCESS) status = "\n"; else status = line_ptr; printf("%s: %s", ifname, status); free(line_ptr); line_ptr = NULL; err: return NL_OK; } static int print_interfaces(char *mesh_iface) { struct print_interfaces_rtnl_arg print_arg; if (!file_exists(module_ver_path)) { fprintf(stderr, "Error - batman-adv module has not been loaded\n"); return EXIT_FAILURE; } print_arg.ifindex = if_nametoindex(mesh_iface); if (!print_arg.ifindex) return EXIT_FAILURE; query_rtnl_link(print_arg.ifindex, print_interfaces_rtnl_parse, &print_arg); return EXIT_SUCCESS; } struct count_interfaces_rtnl_arg { int ifindex; unsigned int count; }; static int count_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) { struct count_interfaces_rtnl_arg *count_arg = arg; struct nlattr *attrs[IFLA_MAX + 1]; struct ifinfomsg *ifm; int ret; int master; ifm = nlmsg_data(nlmsg_hdr(msg)); ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, link_policy); if (ret < 0) goto err; if (!attrs[IFLA_IFNAME]) goto err; if (!attrs[IFLA_MASTER]) goto err; master = nla_get_u32(attrs[IFLA_MASTER]); /* required on older kernels which don't prefilter the results */ if (master != count_arg->ifindex) goto err; count_arg->count++; err: return NL_OK; } static unsigned int count_interfaces(char *mesh_iface) { struct count_interfaces_rtnl_arg count_arg; count_arg.count = 0; count_arg.ifindex = if_nametoindex(mesh_iface); if (!count_arg.ifindex) return 0; query_rtnl_link(count_arg.ifindex, count_interfaces_rtnl_parse, &count_arg); return count_arg.count; } static int create_interface(const char *mesh_iface) { struct ifinfomsg rt_hdr = { .ifi_family = IFLA_UNSPEC, }; struct nlattr *linkinfo; struct nl_msg *msg; int err = 0; int ret; msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); if (!msg) { return -ENOMEM; } ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } linkinfo = nla_nest_start(msg, IFLA_LINKINFO); if (!linkinfo) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_string(msg, IFLA_INFO_KIND, "batadv"); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } nla_nest_end(msg, linkinfo); err = netlink_simple_request(msg); err_free_msg: nlmsg_free(msg); return err; } static int destroy_interface(const char *mesh_iface) { struct ifinfomsg rt_hdr = { .ifi_family = IFLA_UNSPEC, }; struct nl_msg *msg; int err = 0; int ret; msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); if (!msg) { return -ENOMEM; } ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } err = netlink_simple_request(msg); err_free_msg: nlmsg_free(msg); return err; } static int set_master_interface(const char *iface, unsigned int ifmaster) { struct ifinfomsg rt_hdr = { .ifi_family = IFLA_UNSPEC, }; struct nl_msg *msg; int err = 0; int ret; msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); if (!msg) { return -ENOMEM; } ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_string(msg, IFLA_IFNAME, iface); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } ret = nla_put_u32(msg, IFLA_MASTER, ifmaster); if (ret < 0) { err = -ENOMEM; goto err_free_msg; } err = netlink_simple_request(msg); err_free_msg: nlmsg_free(msg); return err; } int interface(char *mesh_iface, int argc, char **argv) { int i, optchar; int ret; unsigned int ifindex; unsigned int ifmaster; const char *long_op; unsigned int cnt; int rest_argc; char **rest_argv; bool manual_mode = false; while ((optchar = getopt(argc, argv, "hM")) != -1) { switch (optchar) { case 'h': interface_usage(); return EXIT_SUCCESS; case 'M': manual_mode = true; break; default: interface_usage(); return EXIT_FAILURE; } } rest_argc = argc - optind; rest_argv = &argv[optind]; if (rest_argc == 0) return print_interfaces(mesh_iface); check_root_or_die("batctl interface"); if ((strcmp(rest_argv[0], "add") != 0) && (strcmp(rest_argv[0], "a") != 0) && (strcmp(rest_argv[0], "del") != 0) && (strcmp(rest_argv[0], "d") != 0) && (strcmp(rest_argv[0], "create") != 0) && (strcmp(rest_argv[0], "c") != 0) && (strcmp(rest_argv[0], "destroy") != 0) && (strcmp(rest_argv[0], "D") != 0)) { fprintf(stderr, "Error - unknown argument specified: %s\n", rest_argv[0]); interface_usage(); goto err; } if (strcmp(rest_argv[0], "destroy") == 0) rest_argv[0][0] = 'D'; switch (rest_argv[0][0]) { case 'a': case 'd': if (rest_argc == 1) { fprintf(stderr, "Error - missing interface name(s) after '%s'\n", rest_argv[0]); interface_usage(); goto err; } break; case 'c': case 'D': if (rest_argc != 1) { fprintf(stderr, "Error - extra parameter after '%s'\n", rest_argv[0]); interface_usage(); goto err; } break; default: break; } switch (rest_argv[0][0]) { case 'c': ret = create_interface(mesh_iface); if (ret < 0) { fprintf(stderr, "Error - failed to add create batman-adv interface: %s\n", strerror(-ret)); goto err; } return EXIT_SUCCESS; case 'D': ret = destroy_interface(mesh_iface); if (ret < 0) { fprintf(stderr, "Error - failed to destroy batman-adv interface: %s\n", strerror(-ret)); goto err; } return EXIT_SUCCESS; default: break; } /* get index of batman-adv interface - or try to create it */ ifmaster = if_nametoindex(mesh_iface); if (!manual_mode && !ifmaster && rest_argv[0][0] == 'a') { ret = create_interface(mesh_iface); if (ret < 0) { fprintf(stderr, "Error - failed to create batman-adv interface: %s\n", strerror(-ret)); goto err; } ifmaster = if_nametoindex(mesh_iface); } if (!ifmaster) { ret = -ENODEV; fprintf(stderr, "Error - failed to find batman-adv interface: %s\n", strerror(-ret)); goto err; } /* make sure that batman-adv is loaded or was loaded by create_interface */ if (!file_exists(module_ver_path)) { fprintf(stderr, "Error - batman-adv module has not been loaded\n"); goto err; } for (i = 1; i < rest_argc; i++) { ifindex = if_nametoindex(rest_argv[i]); if (!ifindex) { fprintf(stderr, "Error - interface does not exist: %s\n", rest_argv[i]); continue; } if (rest_argv[0][0] == 'a') ifindex = ifmaster; else ifindex = 0; ret = set_master_interface(rest_argv[i], ifindex); if (ret < 0) { if (rest_argv[0][0] == 'a') long_op = "add"; else long_op = "delete"; fprintf(stderr, "Error - failed to %s interface %s: %s\n", long_op, rest_argv[i], strerror(-ret)); goto err; } } /* check if there is no interface left and then destroy mesh_iface */ if (!manual_mode && rest_argv[0][0] == 'd') { cnt = count_interfaces(mesh_iface); if (cnt == 0) destroy_interface(mesh_iface); } return EXIT_SUCCESS; err: return EXIT_FAILURE; } batctl-2018.0/interface.h000066400000000000000000000017471324521615300152010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_INTERFACE_H #define _BATCTL_INTERFACE_H #include "main.h" int interface(char *mesh_iface, int argc, char **argv); #endif batctl-2018.0/ioctl.c000066400000000000000000000060671324521615300143460ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ioctl.h" void check_root_or_die(const char *cmd); /* code borrowed from ethtool */ static int statistics_custom_get(int fd, struct ifreq *ifr) { struct ethtool_drvinfo drvinfo; struct ethtool_gstrings *strings = NULL; struct ethtool_stats *stats = NULL; unsigned int n_stats, sz_str, sz_stats, i; int err, ret = EXIT_FAILURE; drvinfo.cmd = ETHTOOL_GDRVINFO; ifr->ifr_data = (void *)&drvinfo; err = ioctl(fd, SIOCETHTOOL, ifr); if (err < 0) { perror("Error - can't open driver information"); goto out; } n_stats = drvinfo.n_stats; if (n_stats < 1) goto success; sz_str = n_stats * ETH_GSTRING_LEN; sz_stats = n_stats * sizeof(uint64_t); strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings)); stats = calloc(1, sz_stats + sizeof(struct ethtool_stats)); if (!strings || !stats) { fprintf(stderr, "Error - out of memory\n"); goto out; } strings->cmd = ETHTOOL_GSTRINGS; strings->string_set = ETH_SS_STATS; strings->len = n_stats; ifr->ifr_data = (void *)strings; err = ioctl(fd, SIOCETHTOOL, ifr); if (err < 0) { perror("Error - can't get stats strings information"); goto out; } stats->cmd = ETHTOOL_GSTATS; stats->n_stats = n_stats; ifr->ifr_data = (void *) stats; err = ioctl(fd, SIOCETHTOOL, ifr); if (err < 0) { perror("Error - can't get stats information"); goto out; } for (i = 0; i < n_stats; i++) { printf("\t%.*s: %llu\n", ETH_GSTRING_LEN, &strings->data[i * ETH_GSTRING_LEN], stats->data[i]); } success: ret = EXIT_SUCCESS; out: free(strings); free(stats); return ret; } int ioctl_statistics_get(char *mesh_iface) { struct ifreq ifr; int fd = -1, ret = EXIT_FAILURE; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, mesh_iface, sizeof(ifr.ifr_name)); ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("Error - can't open socket"); goto out; } ret = statistics_custom_get(fd, &ifr); out: if (fd >= 0) close(fd); return ret; } batctl-2018.0/ioctl.h000066400000000000000000000017001324521615300143400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2012-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_IOCTL_H #define _BATCTL_IOCTL_H int ioctl_statistics_get(char *mesh_iface); #endif batctl-2018.0/list.h000066400000000000000000000654321324521615300142150ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* Minimal Linux-like double-linked list helper functions * * Copyright (c) 2012-2018, Sven Eckelmann * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef __LINUX_LIKE_LIST_H__ #define __LINUX_LIKE_LIST_H__ #ifdef __cplusplus extern "C" { #endif #include #if defined(__GNUC__) #define LIST_TYPEOF_USE 1 #endif #if defined(_MSC_VER) #define __inline__ __inline #endif /** * container_of() - Calculate address of object that contains address ptr * @ptr: pointer to member variable * @type: type of the structure containing ptr * @member: name of the member variable in struct @type * * Return: @type pointer of object containing ptr */ #ifndef container_of #ifdef LIST_TYPEOF_USE #define container_of(ptr, type, member) __extension__ ({ \ const __typeof__(((type *)0)->member) *__pmember = (ptr); \ (type *)((char *)__pmember - offsetof(type, member)); }) #else #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) #endif #endif /** * struct list_head - Head and node of a double-linked list * @prev: pointer to the previous node in the list * @next: pointer to the next node in the list * * The simple double-linked list consists of a head and nodes attached to * this head. Both node and head share the same struct type. The list_* * functions and macros can be used to access and modify this data structure. * * The @prev pointer of the list head points to the last list node of the * list and @next points to the first list node of the list. For an empty list, * both member variables point to the head. * * The list nodes are usually embedded in a container structure which holds the * actual data. Such an container object is called entry. The helper list_entry * can be used to calculate the object address from the address of the node. */ struct list_head { struct list_head *prev; struct list_head *next; }; /** * LIST_HEAD - Declare list head and initialize it * @head: name of the new object */ #define LIST_HEAD(head) \ struct list_head head = { &(head), &(head) } /** * INIT_LIST_HEAD() - Initialize empty list head * @head: pointer to list head * * This can also be used to initialize a unlinked list node. * * A node is usually linked inside a list, will be added to a list in * the near future or the entry containing the node will be free'd soon. * * But an unlinked node may be given to a function which uses list_del(_init) * before it ends up in a previously mentioned state. The list_del(_init) on an * initialized node is well defined and safe. But the result of a * list_del(_init) on an uninitialized node is undefined (unrelated memory is * modified, crashes, ...). */ static __inline__ void INIT_LIST_HEAD(struct list_head *head) { head->next = head; head->prev = head; } /** * list_add() - Add a list node to the beginning of the list * @node: pointer to the new node * @head: pointer to the head of the list */ static __inline__ void list_add(struct list_head *node, struct list_head *head) { struct list_head *next = head->next; next->prev = node; node->next = next; node->prev = head; head->next = node; } /** * list_add_tail() - Add a list node to the end of the list * @node: pointer to the new node * @head: pointer to the head of the list */ static __inline__ void list_add_tail(struct list_head *node, struct list_head *head) { struct list_head *prev = head->prev; prev->next = node; node->next = head; node->prev = prev; head->prev = node; } /** * list_add_before() - Add a list node before another node to the list * @new_node: pointer to the new node * @node: pointer to the reference node in the list * * WARNING this functionality is not available in the Linux list implementation */ #define list_add_before(new_node, node) \ list_add_tail(new_node, node) /** * list_add_behind() - Add a list node behind another node to the list * @new_node: pointer to the new node * @node: pointer to the reference node in the list * * WARNING this functionality is not available in the Linux list implementation */ #define list_add_behind(new_node, node) \ list_add(new_node, node) /** * list_del() - Remove a list node from the list * @node: pointer to the node * * The node is only removed from the list. Neither the memory of the removed * node nor the memory of the entry containing the node is free'd. The node * has to be handled like an uninitialized node. Accessing the next or prev * pointer of the node is not safe. * * Unlinked, initialized nodes are also uninitialized after list_del. * * LIST_POISONING can be enabled during build-time to provoke an invalid memory * access when the memory behind the next/prev pointer is used after a list_del. * This only works on systems which prohibit access to the predefined memory * addresses. */ static __inline__ void list_del(struct list_head *node) { struct list_head *next = node->next; struct list_head *prev = node->prev; next->prev = prev; prev->next = next; #ifdef LIST_POISONING node->prev = (struct list_head *)(0x00100100); node->next = (struct list_head *)(0x00200200); #endif } /** * list_del_init() - Remove a list node from the list and reinitialize it * @node: pointer to the node * * The removed node will not end up in an uninitialized state like when using * list_del. Instead the node is initialized again to the unlinked state. */ static __inline__ void list_del_init(struct list_head *node) { list_del(node); INIT_LIST_HEAD(node); } /** * list_empty() - Check if list head has no nodes attached * @head: pointer to the head of the list * * Return: 0 - list is not empty !0 - list is empty */ static __inline__ int list_empty(const struct list_head *head) { return (head->next == head); } /** * list_is_singular() - Check if list head has exactly one node attached * @head: pointer to the head of the list * * Return: 0 - list is not singular !0 -list has exactly one entry */ static __inline__ int list_is_singular(const struct list_head *head) { return (!list_empty(head) && head->prev == head->next); } /** * list_splice() - Add list nodes from a list to beginning of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the beginning of the list of @head. * It is similar to list_add but for multiple nodes. The @list head is not * modified and has to be initialized to be used as a valid list head/node * again. */ static __inline__ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *head_first = head->next; struct list_head *list_first = list->next; struct list_head *list_last = list->prev; if (list_empty(list)) return; head->next = list_first; list_first->prev = head; list_last->next = head_first; head_first->prev = list_last; } /** * list_splice_tail() - Add list nodes from a list to end of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the end of the list of @head. * It is similar to list_add_tail but for multiple nodes. The @list head is not * modified and has to be initialized to be used as a valid list head/node * again. */ static __inline__ void list_splice_tail(struct list_head *list, struct list_head *head) { struct list_head *head_last = head->prev; struct list_head *list_first = list->next; struct list_head *list_last = list->prev; if (list_empty(list)) return; head->prev = list_last; list_last->next = head; list_first->prev = head_last; head_last->next = list_first; } /** * list_splice_init() - Move list nodes from a list to beginning of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the beginning of the list of @head. * It is similar to list_add but for multiple nodes. * * The @list head will not end up in an uninitialized state like when using * list_splice. Instead the @list is initialized again to the an empty * list/unlinked state. */ static __inline__ void list_splice_init(struct list_head *list, struct list_head *head) { list_splice(list, head); INIT_LIST_HEAD(list); } /** * list_splice_tail_init() - Move list nodes from a list to end of another list * @list: pointer to the head of the list with the node entries * @head: pointer to the head of the list * * All nodes from @list are added to to the end of the list of @head. * It is similar to list_add_tail but for multiple nodes. * * The @list head will not end up in an uninitialized state like when using * list_splice. Instead the @list is initialized again to the an empty * list/unlinked state. */ static __inline__ void list_splice_tail_init(struct list_head *list, struct list_head *head) { list_splice_tail(list, head); INIT_LIST_HEAD(list); } /** * list_cut_position() - Move beginning of a list to another list * @head_to: pointer to the head of the list which receives nodes * @head_from: pointer to the head of the list * @node: pointer to the node in which defines the cutting point * * All entries from the beginning of the list @head_from to (including) the * @node is moved to @head_from. * * @head_to is replaced when @head_from is not empty. @node must be a real * list node from @head_from or the behavior is undefined. */ static __inline__ void list_cut_position(struct list_head *head_to, struct list_head *head_from, struct list_head *node) { struct list_head *head_from_first = head_from->next; if (list_empty(head_from)) return; if (head_from == node) { INIT_LIST_HEAD(head_to); return; } head_from->next = node->next; head_from->next->prev = head_from; head_to->prev = node; node->next = head_to; head_to->next = head_from_first; head_to->next->prev = head_to; } /** * list_move() - Move a list node to the beginning of the list * @node: pointer to the node * @head: pointer to the head of the list * * The @node is removed from its old position/node and add to the beginning of * @head */ static __inline__ void list_move(struct list_head *node, struct list_head *head) { list_del(node); list_add(node, head); } /** * list_move_tail() - Move a list node to the end of the list * @node: pointer to the node * @head: pointer to the head of the list * * The @node is removed from its old position/node and add to the end of @head */ static __inline__ void list_move_tail(struct list_head *node, struct list_head *head) { list_del(node); list_add_tail(node, head); } /** * list_entry() - Calculate address of entry that contains list node * @node: pointer to list node * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of entry containing node */ #define list_entry(node, type, member) container_of(node, type, member) /** * list_first_entry() - get first entry of the list * @head: pointer to the head of the list * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of first entry in list */ #define list_first_entry(head, type, member) \ list_entry((head)->next, type, member) /** * list_last_entry() - get last entry of the list * @head: pointer to the head of the list * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of last entry in list */ #define list_last_entry(head, type, member) \ list_entry((head)->prev, type, member) /** * list_for_each - iterate over list nodes * @node: list_head pointer used as iterator * @head: pointer to the head of the list * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. */ #define list_for_each(node, head) \ for (node = (head)->next; \ node != (head); \ node = node->next) /** * list_for_each_entry_t - iterate over list entries * @entry: @type pointer used as iterator * @head: pointer to the head of the list * @type: type of the entries containing the list nodes * @member: name of the list_head member variable in struct @type * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define list_for_each_entry_t(entry, head, type, member) \ for (entry = list_entry((head)->next, type, member); \ &entry->member != (head); \ entry = list_entry(entry->member.next, type, member)) /** * list_for_each_entry - iterate over list entries * @entry: pointer used as iterator * @head: pointer to the head of the list * @member: name of the list_head member variable in struct type of @entry * * The nodes and the head of the list must must be kept unmodified while * iterating through it. Any modifications to the the list will cause undefined * behavior. */ #ifdef LIST_TYPEOF_USE #define list_for_each_entry(entry, head, member) \ list_for_each_entry_t(entry, head, __typeof__(*entry), member) #endif /** * list_for_each_safe - iterate over list nodes and allow deletes * @node: list_head pointer used as iterator * @safe: list_head pointer used to store info for next entry in list * @head: pointer to the head of the list * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. */ #define list_for_each_safe(node, safe, head) \ for (node = (head)->next, safe = node->next; \ node != (head); \ node = safe, safe = node->next) /** * list_for_each_entry_safe_t - iterate over list entries and allow deletes * @entry: @type pointer used as iterator * @safe: @type pointer used to store info for next entry in list * @head: pointer to the head of the list * @type: type of the entries containing the list nodes * @member: name of the list_head member variable in struct @type * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define list_for_each_entry_safe_t(entry, safe, head, type, member) \ for (entry = list_entry((head)->next, type, member), \ safe = list_entry(entry->member.next, type, member); \ &entry->member != (head); \ entry = safe, \ safe = list_entry(safe->member.next, type, member)) /** * list_for_each_entry_safe - iterate over list entries and allow deletes * @entry: pointer used as iterator * @safe: @type pointer used to store info for next entry in list * @head: pointer to the head of the list * @member: name of the list_head member variable in struct type of @entry * * The current node (iterator) is allowed to be removed from the list. Any * other modifications to the the list will cause undefined behavior. */ #ifdef LIST_TYPEOF_USE #define list_for_each_entry_safe(entry, safe, head, member) \ list_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry), \ member) #endif /** * struct hlist_node - Node of a double-linked list with single pointer head * @next: pointer to the next node in the list * @pprev: pointer to @next of the previous node in the hlist * * The double-linked list with single pointer head consists of a head and nodes * attached to this head. The hlist_* functions and macros can be used to access * and modify this data structure. * * The @pprev pointer is used to find the previous node (or head) in the list * when doing hlist_del operations * * The hlist nodes are usually embedded in a container structure which holds the * actual data. Such an container object is called entry. The helper hlist_entry * can be used to calculate the object address from the address of the node. */ struct hlist_node { struct hlist_node *next; struct hlist_node **pprev; }; /** * struct hlist_head - Head of a double-linked list with single pointer head * @first: pointer to the first node in the hlist * * The hlist doesn't have a pointer to the last node. This makes it harder to * access or modify the tail of the list. But the single pointer to the first * entry makes it well suited for implementation of hash tables because it * cuts the size cost of the head pointers by half compared to the list_head. */ struct hlist_head { struct hlist_node *first; }; /** * HLIST_HEAD - Declare hlist head and initialize it * @head: name of the new object */ #define HLIST_HEAD(head) \ struct hlist_head head = { NULL } /** * INIT_HLIST_HEAD() - Initialize empty hlist head * @head: pointer to hlist head */ static __inline__ void INIT_HLIST_HEAD(struct hlist_head *head) { head->first = NULL; } /** * INIT_HLIST_NODE() - Initialize unhashed hlist node * @node: pointer to hlist node * * A hlist_node is usually linked inside a hlist, will be added to a hlist in * the near future or the entry containing the node will be free'd soon. * * But an unlinked node may be given to a function which uses hlist_del(_init) * before it ends up in a previously mentioned state. The hlist_del(_init) on an * initialized node is well defined and safe. But the result of a * hlist_del(_init) on an uninitialized node is undefined (unrelated memory is * modified, crashes, ...). */ static __inline__ void INIT_HLIST_NODE(struct hlist_node *node) { node->next = NULL; node->pprev = NULL; } /** * hlist_add_head() - Add a hlist node to the beginning of the hlist * @node: pointer to the new node * @head: pointer to the head of the hlist */ static __inline__ void hlist_add_head(struct hlist_node *node, struct hlist_head *head) { struct hlist_node *first = head->first; head->first = node; node->next = first; node->pprev = &head->first; if (first) first->pprev = &node->next; } /** * hlist_add_before() - Add a hlist node before another node to the hlist * @new_node: pointer to the new node * @node: pointer to the reference node in the hlist */ static __inline__ void hlist_add_before(struct hlist_node *new_node, struct hlist_node *node) { struct hlist_node **pprev = node->pprev; *pprev = new_node; new_node->next = node; new_node->pprev = pprev; node->pprev = &new_node->next; } /** * hlist_add_behind() - Add a hlist node behind another node to the hlist * @new_node: pointer to the new node * @node: pointer to the reference node in the hlist */ static __inline__ void hlist_add_behind(struct hlist_node *new_node, struct hlist_node *node) { struct hlist_node *next = node->next; node->next = new_node; new_node->pprev = &node->next; new_node->next = next; if (next) next->pprev = &new_node->next; } /** * hlist_del() - Remove a hlist node from the hlist * @node: pointer to the node * * The node is only removed from the hlist. Neither the memory of the removed * node nor the memory of the entry containing the node is free'd. The node * has to be handled like an uninitialized node. Accessing the next or pprev * pointer of the node is not safe. * * Unlinked, initialized nodes are also uninitialized after hlist_del. * * LIST_POISONING can be enabled during build-time to provoke an invalid memory * access when the memory behind the next/prev pointer is used after an * hlist_del. This only works on systems which prohibit access to the predefined * memory addresses. */ static __inline__ void hlist_del(struct hlist_node *node) { struct hlist_node *next = node->next; struct hlist_node **pprev = node->pprev; if (pprev) *pprev = next; if (next) next->pprev = pprev; #ifdef LIST_POISONING node->pprev = (struct hlist_node **)(0x00100100); node->next = (struct hlist_node *)(0x00200200); #endif } /** * hlist_del_init() - Remove a hlist node from the hlist and reinitialize it * @node: pointer to the node * * The removed node will not end up in an uninitialized state like when using * hlist_del. Instead the node is initialized again to the unlinked state. */ static __inline__ void hlist_del_init(struct hlist_node *node) { hlist_del(node); INIT_HLIST_NODE(node); } /** * hlist_empty() - Check if hlist head has no nodes attached * @head: pointer to the head of the hlist * * Return: 0 - hlist is not empty !0 - hlist is empty */ static __inline__ int hlist_empty(const struct hlist_head *head) { return !head->first; } /** * hlist_move_list() - Move hlist nodes from a hlist head new hlist head * @list: pointer to the head of the hlist with the node entries * @head: pointer to the head of the hlist * * All nodes from @list are added to to the beginning of the list of @head. * @head can be uninitialized or an empty, initialized hlist. All entries of * a non-empty hlist @head would be lost after this operation. * * The @list head will not end up in an uninitialized state. Instead the @list * is initialized again to an empty hlist. */ static __inline__ void hlist_move_list(struct hlist_head *list, struct hlist_head *head) { head->first = list->first; if (head->first) head->first->pprev = &head->first; INIT_HLIST_HEAD(list); } /** * hlist_entry() - Calculate address of entry that contains hlist node * @node: pointer to hlist node * @type: type of the entry containing the hlist node * @member: name of the hlist_node member variable in struct @type * * Return: @type pointer of entry containing node */ #define hlist_entry(node, type, member) container_of(node, type, member) /** * hlist_entry_safe() - Calculate address of entry that contains hlist node * @node: pointer to hlist node or (struct hlist_node *)NULL * @type: type of the entry containing the hlist node * @member: name of the hlist_node member variable in struct @type * * Return: @type pointer of entry containing node or NULL */ #ifdef LIST_TYPEOF_USE #define hlist_entry_safe(node, type, member) __extension__ ({ \ __typeof__(node) __node = (node); \ !__node ? NULL : hlist_entry(__node, type, member); }) #else #define hlist_entry_safe(node, type, member) \ (node) ? hlist_entry(node, type, member) : NULL #endif /** * hlist_for_each - iterate over hlist nodes * @node: hlist_node pointer used as iterator * @head: pointer to the head of the hlist * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. */ #define hlist_for_each(node, head) \ for (node = (head)->first; \ node; \ node = node->next) /** * hlist_for_each_entry_t - iterate over hlist entries * @entry: @type pointer used as iterator * @head: pointer to the head of the hlist * @type: type of the entries containing the hlist nodes * @member: name of the hlist_node member variable in struct @type * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define hlist_for_each_entry_t(entry, head, type, member) \ for (entry = hlist_entry_safe((head)->first, type, member); \ entry; \ entry = hlist_entry_safe(entry->member.next, type, member)) /** * hlist_for_each_entry - iterate over hlist entries * @entry: pointer used as iterator * @head: pointer to the head of the hlist * @member: name of the hlist_node member variable in struct type of @entry * * The nodes and the head of the hlist must must be kept unmodified while * iterating through it. Any modifications to the the hlist will cause undefined * behavior. */ #ifdef LIST_TYPEOF_USE #define hlist_for_each_entry(entry, head, member) \ hlist_for_each_entry_t(entry, head, __typeof__(*entry), member) #endif /** * hlist_for_each_safe - iterate over hlist nodes and allow deletes * @node: hlist_node pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. */ #define hlist_for_each_safe(node, safe, head) \ for (node = (head)->first; \ node && ((safe = node->next) || 1); \ node = safe) /** * hlist_for_each_entry_safe_t - iterate over hlist entries and allow deletes * @entry: @type pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * @type: type of the entries containing the hlist nodes * @member: name of the hlist_node member variable in struct @type * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. * * WARNING this functionality is not available in the Linux list implementation */ #define hlist_for_each_entry_safe_t(entry, safe, head, type, member) \ for (entry = hlist_entry_safe((head)->first, type, member); \ entry && ((safe = entry->member.next) || 1); \ entry = hlist_entry_safe(safe, type, member)) /** * hlist_for_each_entry_safe - iterate over hlist entries and allow deletes * @entry: pointer used as iterator * @safe: hlist_node pointer used to store info for next entry in hlist * @head: pointer to the head of the hlist * @member: name of the hlist_node member variable in struct type of @entry * * The current node (iterator) is allowed to be removed from the hlist. Any * other modifications to the the hlist will cause undefined behavior. */ #ifdef LIST_TYPEOF_USE #define hlist_for_each_entry_safe(entry, safe, head, member) \ hlist_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry),\ member) #endif #ifdef __cplusplus } #endif #endif /* __LINUX_LIKE_LIST_H__ */ batctl-2018.0/main.c000066400000000000000000000172371324521615300141610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include "main.h" #include "sys.h" #include "debug.h" #include "interface.h" #include "ping.h" #include "translate.h" #include "traceroute.h" #include "tcpdump.h" #include "tp_meter.h" #include "bisect_iv.h" #include "ioctl.h" #include "functions.h" char mesh_dfl_iface[] = "bat0"; char module_ver_path[] = "/sys/module/batman_adv/version"; static void print_usage(void) { int i, opt_indent; fprintf(stderr, "Usage: batctl [options] command|debug table [parameters]\n"); fprintf(stderr, "options:\n"); fprintf(stderr, " \t-m mesh interface or VLAN created on top of a mesh interface (default 'bat0')\n"); fprintf(stderr, " \t-h print this help (or 'batctl -h' for the parameter help)\n"); fprintf(stderr, " \t-v print version\n"); fprintf(stderr, "\n"); fprintf(stderr, "commands:\n"); fprintf(stderr, " \tinterface|if [add|del iface(s)]\tdisplay or modify the interface settings\n"); for (i = 0; i < BATCTL_SETTINGS_NUM; i++) { fprintf(stderr, " \t%s|%s", batctl_settings[i].opt_long, batctl_settings[i].opt_short); opt_indent = strlen(batctl_settings[i].opt_long) + strlen(batctl_settings[i].opt_short); if (batctl_settings[i].params == sysfs_param_enable) fprintf(stderr, "%*s display or modify %s setting\n", 31 - opt_indent, "[0|1]", batctl_settings[i].opt_long); else if (batctl_settings[i].params == sysfs_param_server) fprintf(stderr, "%*s display or modify %s setting\n", 41 - opt_indent, "[client|server]", batctl_settings[i].opt_long); else fprintf(stderr, " display or modify %s setting\n", batctl_settings[i].opt_long); } fprintf(stderr, " \tloglevel|ll [level] \tdisplay or modify the log level\n"); fprintf(stderr, " \tlog|l \tread the log produced by the kernel module\n"); fprintf(stderr, " \tgw_mode|gw [mode] \tdisplay or modify the gateway mode\n"); fprintf(stderr, " \trouting_algo|ra [mode] \tdisplay or modify the routing algorithm\n"); fprintf(stderr, "\n"); fprintf(stderr, "debug tables: \tdisplay the corresponding debug table\n"); for (i = 0; i < BATCTL_TABLE_NUM; i++) fprintf(stderr, " \t%s|%s\n", batctl_debug_tables[i].opt_long, batctl_debug_tables[i].opt_short); fprintf(stderr, "\n"); fprintf(stderr, " \tstatistics|s \tprint mesh statistics\n"); fprintf(stderr, " \tping|p \tping another batman adv host via layer 2\n"); fprintf(stderr, " \ttraceroute|tr \ttraceroute another batman adv host via layer 2\n"); fprintf(stderr, " \ttcpdump|td \ttcpdump layer 2 traffic on the given interface\n"); printf(" \tthroughputmeter|tp \tstart a throughput measurement\n"); fprintf(stderr, " \ttranslate|t \ttranslate a destination to the originator responsible for it\n"); #ifdef BATCTL_BISECT fprintf(stderr, " \tbisect_iv .. \tanalyze given batman iv log files for routing stability\n"); #endif } int main(int argc, char **argv) { int i, ret = EXIT_FAILURE; char *mesh_iface = mesh_dfl_iface; if ((argc > 1) && (strcmp(argv[1], "-m") == 0)) { if (argc < 3) { fprintf(stderr, "Error - the option '-m' needs a parameter\n"); goto err; } mesh_iface = argv[2]; argv += 2; argc -= 2; } if (argc < 2) { fprintf(stderr, "Error - no command specified\n"); goto err; } if (strcmp(argv[1], "-h") == 0) { print_usage(); exit(EXIT_SUCCESS); } if (strcmp(argv[1], "-v") == 0) { printf("batctl %s [batman-adv: ", SOURCE_VERSION); ret = read_file("", module_ver_path, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); if ((line_ptr) && (line_ptr[strlen(line_ptr) - 1] == '\n')) line_ptr[strlen(line_ptr) - 1] = '\0'; if (ret == EXIT_SUCCESS) printf("%s]\n", line_ptr); else printf("module not loaded]\n"); free(line_ptr); exit(EXIT_SUCCESS); } if ((strcmp(argv[1], "interface") == 0) || (strcmp(argv[1], "if") == 0)) { ret = interface(mesh_iface, argc - 1, argv + 1); } else if ((strcmp(argv[1], "tcpdump") == 0) || (strcmp(argv[1], "td") == 0)) { ret = tcpdump(argc - 1, argv + 1); #ifdef BATCTL_BISECT } else if ((strcmp(argv[1], "bisect_iv") == 0)) { ret = bisect_iv(argc - 1, argv + 1); #endif } else if ((strcmp(argv[1], "routing_algo") == 0) || (strcmp(argv[1], "ra") == 0)) { ret = handle_ra_setting(argc - 1, argv + 1); } else if (check_mesh_iface(mesh_iface) < 0) { fprintf(stderr, "Error - interface %s is not present or not a batman-adv interface\n", mesh_iface); exit(EXIT_FAILURE); } else if ((strcmp(argv[1], "ping") == 0) || (strcmp(argv[1], "p") == 0)) { ret = ping(mesh_iface, argc - 1, argv + 1); } else if ((strcmp(argv[1], "throughputmeter") == 0) || (strcmp(argv[1], "tp") == 0)) { ret = tp_meter (mesh_iface, argc -1, argv + 1); } else if ((strcmp(argv[1], "traceroute") == 0) || (strcmp(argv[1], "tr") == 0)) { ret = traceroute(mesh_iface, argc - 1, argv + 1); } else if ((strcmp(argv[1], "loglevel") == 0) || (strcmp(argv[1], "ll") == 0)) { ret = handle_loglevel(mesh_iface, argc - 1, argv + 1); } else if ((strcmp(argv[1], "log") == 0) || (strcmp(argv[1], "l") == 0)) { ret = log_print(mesh_iface, argc - 1, argv + 1); /* vis legacy support */ } else if ((strcmp(argv[1], "vis_data") == 0) || (strcmp(argv[1], "vd") == 0) || (strcmp(argv[1], "vis_mode") == 0) || (strcmp(argv[1], "vm") == 0)) { ret = print_vis_info(mesh_iface); } else if ((strcmp(argv[1], "gw_mode") == 0) || (strcmp(argv[1], "gw") == 0)) { ret = handle_gw_setting(mesh_iface, argc - 1, argv + 1); } else if ((strcmp(argv[1], "statistics") == 0) || (strcmp(argv[1], "s") == 0)) { ret = ioctl_statistics_get(mesh_iface); } else if ((strcmp(argv[1], "translate") == 0) || (strcmp(argv[1], "t") == 0)) { ret = translate(mesh_iface, argc - 1, argv + 1); } else { for (i = 0; i < BATCTL_SETTINGS_NUM; i++) { if ((strcmp(argv[1], batctl_settings[i].opt_long) != 0) && (strcmp(argv[1], batctl_settings[i].opt_short) != 0)) continue; ret = handle_sys_setting(mesh_iface, i, argc - 1, argv + 1); goto out; } for (i = 0; i < BATCTL_TABLE_NUM; i++) { if ((strcmp(argv[1], batctl_debug_tables[i].opt_long) != 0) && (strcmp(argv[1], batctl_debug_tables[i].opt_short) != 0)) continue; ret = handle_debug_table(mesh_iface, i, argc - 1, argv + 1); goto out; } fprintf(stderr, "Error - no valid command or debug table specified: %s\n", argv[1]); print_usage(); } out: return ret; err: print_usage(); exit(EXIT_FAILURE); } batctl-2018.0/main.h000066400000000000000000000032701324521615300141560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_MAIN_H #define _BATCTL_MAIN_H #include #ifndef SOURCE_VERSION #define SOURCE_VERSION "2018.0" #endif #define SOCKET_PATH_FMT "%s/batman_adv/%s/socket" #define EXIT_NOSUCCESS 2 #define OPT_LONG_MAX_LEN 25 #define OPT_SHORT_MAX_LEN 5 #define DEBUG_TABLE_PATH_MAX_LEN 20 #define SETTINGS_PATH_MAX_LEN 25 #if BYTE_ORDER == BIG_ENDIAN #define __BIG_ENDIAN_BITFIELD #elif BYTE_ORDER == LITTLE_ENDIAN #define __LITTLE_ENDIAN_BITFIELD #else #error "unknown endianess" #endif #define __maybe_unused __attribute__((unused)) #define BIT(nr) (1UL << (nr)) /* linux kernel compat */ extern char module_ver_path[]; #ifndef VLAN_VID_MASK #define VLAN_VID_MASK 0xfff #endif #define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \ (int)(vid & VLAN_VID_MASK) : -1) #endif batctl-2018.0/man/000077500000000000000000000000001324521615300136325ustar00rootroot00000000000000batctl-2018.0/man/batctl.8000066400000000000000000000425351324521615300152050ustar00rootroot00000000000000.\" SPDX-License-Identifier: GPL-2.0 .\" License-Filename: LICENSES/preferred/GPL-2.0 .\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH "BATCTL" "8" "July 17, 2015" "Linux" "B.A.T.M.A.N. Advanced Control Tool" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .\" -------------------------------------------------------------------------- .\" Process this file with .\" groff -man batctl.8 -Tutf8 .\" Retrieve format warnings with .\" man --warnings batctl.8 > /dev/null .\" -------------------------------------------------------------------------- .ad l .SH NAME batctl \- B.A.T.M.A.N. advanced control and management tool .SH SYNOPSIS .B batctl [\fIoptions\fP]\ \fIcommand\fP|\fIdebug\ table\fP\ [\fIparameters\fP] .br .SH DESCRIPTION batctl offers a convenient way to configure the batman\-adv kernel module as well as displaying debug information such as originator tables, translation tables and the debug log. In combination with a bat\-hosts file batctl allows the use of host names instead of MAC addresses. .PP B.A.T.M.A.N. advanced operates on layer 2. Thus all hosts participating in the virtual switched network are transparently connected together for all protocols above layer 2. Therefore the common diagnosis tools do not work as expected. To overcome these problems batctl contains the commands \fBping\fP, \fBtraceroute\fP, \fBtcpdump\fP which provide similar functionality to the normal \fBping\fP(1), \fBtraceroute\fP(1), \fBtcpdump\fP(1) commands, but modified to layer 2 behaviour or using the B.A.T.M.A.N. advanced protocol. For similar reasons, \fBthroughputmeter\fP, a command to test network performances, is also included. .PP .PP .SH OPTIONS .TP .I \fBoptions: \-m specify mesh interface or VLAN created on top of a mesh interface (default 'bat0') .br \-h print general batctl help .br \-v print batctl version and batman-adv version (if the module is loaded) .br .TP .I \fBcommands: .IP "\fBinterface\fP|\fBif\fP [\fB-M\fP] [\fBadd\fP|\fBdel iface(s)\fP]" If no parameter is given or the first parameter is neither "add" nor "del" the current interface settings are displayed. In order to add or delete interfaces specify "add" or "del" as first argument and append the interface names you wish to add or delete. Multiple interfaces can be specified. The "\-M" option tells batctl to not automatically create the batman-adv interface on "add" or to destroy it when "del" removed all interfaces which belonged to it. .IP "\fBinterface\fP|\fBif\fP [\fBcreate\fP|\fBdestroy\fP]" A batman-adv interface without attached interfaces can be created using "create". The parameter "destroy" can be used to free all attached interfaces and remove batman-adv interface. .br .IP "\fBorig_interval\fP|\fBit\fP [\fBinterval\fP]" If no parameter is given the current originator interval setting is displayed otherwise the parameter is used to set the originator interval. The interval is in units of milliseconds. .br .IP "\fBap_isolation\fP|\fBap\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current ap isolation setting is displayed. Otherwise the parameter is used to enable or disable ap isolation. This command can be used in conjunction with "\-m" option to target per VLAN configurations. .br .IP "\fBbridge_loop_avoidance\fP|\fBbl\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current bridge loop avoidance setting is displayed. Otherwise the parameter is used to enable or disable the bridge loop avoidance. Bridge loop avoidance support has to be enabled when compiling the module otherwise this option won't be available. .br .IP "\fBdistributed_arp_table\fP|\fBdat\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current distributed arp table setting is displayed. Otherwise the parameter is used to enable or disable the distributed arp table. .br .IP "\fBaggregation\fP|\fBag\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current aggregation setting is displayed. Otherwise the parameter is used to enable or disable OGM packet aggregation. .br .IP "\fBbonding\fP|\fBb\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current bonding mode setting is displayed. Otherwise the parameter is used to enable or disable the bonding mode. .br .IP "\fBfragmentation\fP|\fBf\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current fragmentation mode setting is displayed. Otherwise the parameter is used to enable or disable fragmentation. .br .IP "\fBnetwork_coding\fP|\fBnc\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current network coding mode setting is displayed. Otherwise the parameter is used to enable or disable network coding. .br .IP "\fBmulticast_mode\fP|\fBmm\fP [\fB0\fP|\fB1\fP]" If no parameter is given the current multicast mode setting is displayed. Otherwise the parameter is used to enable or disable multicast optimizations (i.e. disabling means always sending own multicast frames via classic flooding). .br .IP "\fBloglevel\fP|\fBll\fP [\fBlevel\fP[ \fBlevel\fP[ \fBlevel\fP]] \fB...\fP]" If no parameter is given the current log level settings are displayed otherwise the parameter(s) is/are used to set the log level. Level 'none' disables all verbose logging. Level 'batman' enables messages related to routing / flooding / broadcasting. Level 'routes' enables messages related to routes being added / changed / deleted. Level 'tt' enables messages related to translation table operations. Level 'bla' enables messages related to the bridge loop avoidance. Level 'dat' enables messages related to ARP snooping and the Distributed Arp Table. Level 'nc' enables messages related to network coding. Level 'mcast' enables messages related to multicast optimizations. Level 'all' enables all messages. The messages are sent to the batman-adv debug log. Use \fBbatctl log\fP to retrieve it. Make sure to have debugging output enabled when compiling the module otherwise the output as well as the loglevel options won't be available. .br .IP "\fBlog\fP|\fBl\fP [\fB\-n\fP]\fP" batctl will read the batman-adv debug log which has to be compiled into the kernel module. If "\-n" is given batctl will not replace the MAC addresses with bat\-host names in the output. .br .IP "\fBgw_mode|gw\fP [\fBoff\fP|\fBclient\fP|\fBserver\fP] [\fBsel_class|bandwidth\fP]\fP" If no parameter is given the current gateway mode is displayed otherwise the parameter is used to set the gateway mode. The second (optional) argument specifies the selection class (if 'client' was the first argument) or the gateway bandwidth (if 'server' was the first argument). If the node is a server this parameter is used to inform other nodes in the network about this node's internet connection bandwidth. Just enter any number (optionally followed by "kbit" or "mbit") and the batman-adv module will propagate the entered value in the mesh. Use "/" to separate the down\(hy and upload rates. You can omit the upload rate and the module will assume an upload of download / 5. .RS 17 default: 10000 \-> 10.0/2.0 MBit .RE .RS 16 examples: 5000 \-> 5.0/1.0 MBit .RE .RS 26 5000kbit 5mbit 5mbit/1024 5mbit/1024kbit 5mbit/1mbit .RE .RS 7 If the node is a gateway client the parameter will decide which criteria to consider when the batman-adv module has to choose between different internet connections announced by the aforementioned servers. .RE .RS 17 default: 20 \-> late switch (TQ 20) .RE .RS 16 examples: 1 -> fast connection .RS 16 consider the gateway's advertised throughput as well as the link quality towards the gateway and stick with the selection until the gateway disappears .RE .RE .RS 25 2 \-> stable connection .RS 7 chooses the gateway with the best link quality and sticks with it (ignore the advertised throughput) .RE 3 \-> fast switch connection .RS 7 chooses the gateway with the best link quality but switches to another gateway as soon as a better one is found .RE XX \-> late switch connection .RS 7 chooses the gateway with the best link quality but switches to another gateway as soon as a better one is found which is at least XX TQ better than the currently selected gateway (XX has to be a number between 3 and 256). .RE .RE .br .IP "\fBrouting_algo\fP|\fBra\fP [\fBalgorithm\fP]\fP" If no parameter is given the current routing algorithm configuration as well as supported routing algorithms are displayed. Otherwise the parameter is used to select the routing algorithm for the following batX interface to be created. .br .IP "\fBisolation_mark\fP|\fBmark\fP" If no parameter is given the current isolation mark value is displayed. Otherwise the parameter is used to set or unset the isolation mark used by the Extended Isolation feature. .br The input is supposed to be of the form $value/$mask, where $value can be any 32bit long integer (expressed in decimal or hex base) and $mask is a generic bitmask (expressed in hex base) that selects the bits to take into consideration from $value. It is also possible to enter the input using only $value and in this case the full bitmask is used by default. .br .br Example 1: 0x00000001/0xffffffff .br Example 2: 0x00040000/0xffff0000 .br Example 3: 16 or 0x0F .br .br .PP .I \fBdebug tables: .IP The batman-adv kernel module comes with a variety of debug tables containing various information about the state of the mesh seen by each individual node. These tables are exported via debugfs and easily accessible via batctl. You will need debugfs support compiled into your kernel and preferably have mounted the debugfs to a well-known mountpoint. If debugfs is not mounted batctl will attempt to do this step for you. All of the debug tables support the following options: .RS 10 \-w refresh the list every second or add a number to let it refresh at a custom interval in seconds (with optional decimal places) .RE .RS 10 \-n do not replace the MAC addresses with bat\-host names in the output .RE .RS 10 \-H do not show the header of the debug table .RE .RS 7 The originator table also supports the "\-t" filter option to remove all originators from the output that have not been seen for the specified amount of seconds (with optional decimal places). It furthermore supports the "\-i" parameter to specify an interface for which the originator table should be printed. If this parameter is not supplied, the default originator table is printed. The local and global translation tables also support the "\-u" and "\-m" option to only display unicast or multicast translation table announcements respectively. List of debug tables: .RS 10 \- neighbors|n .RE .RS 10 \- originators|o .RE .RS 10 \- gateways|gwl .RE .RS 10 \- translocal|tl .RE .RS 10 \- transglobal|tg .RE .RS 10 \- claimtable|cl (compile time option) .RE .RS 10 \- backbonetable|bbt (compile time option) .RE .RS 10 \- dat_cache|dc (compile time option) .RE .RS 10 \- nc_nodes|nn (compile time option) .RE .RS 10 \- mcast_flags|mf (compile time option) .RE .RE .br .IP "\fBtranslate\fP|\fBt\fP \fBMAC_address\fP|\fBbat\-host_name\fP|\fBhost_name\fP|\fBIP_address\fP" Translates a destination (hostname, IP, MAC, bat_host-name) to the originator mac address responsible for it. .br .IP "\fBstatistics\fP|\fBs\fP" Retrieve traffic counters from batman-adv kernel module. The output may vary depending on which features have been compiled into the kernel module. .br Each module subsystem has its own counters which are indicated by their prefixes: .RS 15 mgmt - mesh protocol counters .RE .RS 17 tt - translation table counters .RE .RS 7 All counters without a prefix concern payload (pure user data) traffic. .RE .br .IP "\fBping\fP|\fBp\fP [\fB\-c count\fP][\fB\-i interval\fP][\fB\-t time\fP][\fB\-R\fP][\fB\-T\fP] \fBMAC_address\fP|\fBbat\-host_name\fP|\fBhost_name\fP|\fBIP_address\fP" Layer 2 ping of a MAC address or bat\-host name. batctl will try to find the bat\-host name if the given parameter was not a MAC address. It can also try to guess the MAC address using an IPv4/IPv6 address or a hostname when the IPv4/IPv6 address was configured on top of the batman-adv interface of the destination device and both source and destination devices are in the same IP subnet. The "\-c" option tells batctl how man pings should be sent before the program exits. Without the "\-c" option batctl will continue pinging without end. Use CTRL + C to stop it. With "\-i" and "\-t" you can set the default interval between pings and the timeout time for replies, both in seconds. When run with "\-R", the route taken by the ping messages will be recorded. With "\-T" you can disable the automatic translation of a client MAC address to the originator address which is responsible for this client. .br .IP "\fBtraceroute\fP|\fBtr\fP [\fB\-n\fP][\fB\-T\fP] \fBMAC_address\fP|\fBbat\-host_name\fP|\fBhost_name\fP|\fBIP_address\fP" Layer 2 traceroute to a MAC address or bat\-host name. batctl will try to find the bat\-host name if the given parameter was not a MAC address. It can also try to guess the MAC address using an IPv4/IPv6 address or a hostname when the IPv4/IPv6 address was configured on top of the batman-adv interface of the destination device and both source and destination devices are in the same IP subnet. batctl will send 3 packets to each host and display the response time. If "\-n" is given batctl will not replace the MAC addresses with bat\-host names in the output. With "\-T" you can disable the automatic translation of a client MAC address to the originator address which is responsible for this client. .br .IP "\fBtcpdump\fP|\fBtd\fP [\fB\-c\fP][\fB\-n\fP][\fB\-p filter\fP][\fB\-x filter\fP] \fBinterface ...\fP" batctl will display all packets that are seen on the given interface(s). A variety of options to filter the output are available: To only print packets that match the compatibility number of batctl specify the "\-c" (compat filter) option. If "\-n" is given batctl will not replace the MAC addresses with bat\-host names in the output. To filter the shown packet types you can either use "\-p" (dump only specified packet types) or "\-x" (dump all packet types except specified). The following packet types are available: .RS 17 1 - batman ogm packets .RE .RS 17 2 - batman icmp packets .RE .RS 17 4 - batman unicast packets .RE .RS 17 8 - batman broadcast packets .RE .RS 16 16 - batman unicast tvlv packets .RE .RS 16 32 - batman fragmented packets .RE .RS 16 64 - batman tt / roaming packets .RE .RS 15 128 - non batman packets .RE .RS 7 Example: batctl td \-p 129 \-> only display batman ogm packets and non batman packets .RE .br .IP "\fBbisect_iv\fP [\fB\-l MAC\fP][\fB\-t MAC\fP][\fB\-r MAC\fP][\fB\-s min\fP [\fB\- max\fP]][\fB\-o MAC\fP][\fB\-n\fP] \fBlogfile1\fP [\fBlogfile2\fP ... \fBlogfileN\fP]" Analyses the B.A.T.M.A.N. IV logfiles to build a small internal database of all sent sequence numbers and routing table changes. This database can then be analyzed in a number of different ways. With "\-l" the database can be used to search for routing loops. Use "\-t" to trace OGMs of a host throughout the network. Use "\-r" to display routing tables of the nodes. The option "\-s" can be used to limit the output to a range of sequence numbers, between min and max, or to one specific sequence number, min. Furthermore using "\-o" you can filter the output to a specified originator. If "\-n" is given batctl will not replace the MAC addresses with bat\-host names in the output. .RE .br .IP "\fBthroughputmeter\fP|\fBtp\fP \fBMAC\fP" This command starts a throughput test entirely controlled by batman module in kernel space: the computational resources needed to align memory and copy data between user and kernel space that are required by other user space tools may represent a bootleneck on some low profile device. The test consist of the transfer of 14 MB of data between the two nodes. The protocol used to transfer the data is somehow similar to TCP, but simpler: some TCP features are still missing, thus protocol performances could be worst. Since a fixed amount of data is transferred the experiment duration depends on the network conditions. The experiment can be interrupted with CTRL + C. At the end of a successful experiment the throughput in KBytes per second is returned, togheter with the experiment duration in millisecond and the amount of bytes transferred. If too many packets are lost or the specified MAC address is not reachable, a message notifing the error is returned instead of the result. .RE .br .SH FILES .TP .I "\fBbat-hosts\fP" This file is similar to the /etc/hosts file. You can write one MAC address and one host name per line. batctl will search for bat-hosts in /etc, your home directory and the current directory. The found data is used to match MAC address to your provided host name or replace MAC addresses in debug output and logs. Host names are much easier to remember than MAC addresses. .SH SEE ALSO .BR ping (1), .BR traceroute (1), .BR tcpdump (1), .BR dmesg (1), .BR dot (1) .SH AUTHOR batctl was written by Andreas Langer and Marek Lindner . .PP This manual page was written by Simon Wunderlich , Marek Lindner and Andrew Lunn batctl-2018.0/netlink.c000066400000000000000000001247631324521615300147040ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner , Andrew Lunn * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "netlink.h" #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "bat-hosts.h" #include "batadv_packet.h" #include "batman_adv.h" #include "netlink.h" #include "functions.h" #include "main.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) #ifndef container_of #define container_of(ptr, type, member) __extension__ ({ \ const __typeof__(((type *)0)->member) *__pmember = (ptr); \ (type *)((char *)__pmember - offsetof(type, member)); }) #endif struct print_opts { int read_opt; float orig_timeout; float watch_interval; nl_recvmsg_msg_cb_t callback; char *remaining_header; const char *static_header; uint8_t nl_cmd; }; struct nlquery_opts { int err; }; struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [BATADV_ATTR_MESH_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [BATADV_ATTR_HARD_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_ORIG_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, [BATADV_ATTR_PAD] = { .type = NLA_UNSPEC }, [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, [BATADV_ATTR_TT_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, [BATADV_ATTR_NEIGH_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_TQ] = { .type = NLA_U8 }, [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, [BATADV_ATTR_ROUTER] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, [BATADV_ATTR_BLA_ADDRESS] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, [BATADV_ATTR_BLA_BACKBONE] = { .type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, }; static int last_err; static char algo_name_buf[256] = ""; static int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[], int num) { int i; for (i = 0; i < num; i++) if (!attrs[mandatory[i]]) return -EINVAL; return 0; } static int print_error(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *nlerr, void *arg __maybe_unused) { if (nlerr->error != -EOPNOTSUPP) fprintf(stderr, "Error received: %s\n", strerror(-nlerr->error)); last_err = nlerr->error; return NL_STOP; } static int stop_callback(struct nl_msg *msg, void *arg __maybe_unused) { struct nlmsghdr *nlh = nlmsg_hdr(msg); int *error = nlmsg_data(nlh); if (*error) fprintf(stderr, "Error received: %s\n", strerror(-*error)); return NL_STOP; } static const int info_mandatory[] = { BATADV_ATTR_MESH_IFINDEX, BATADV_ATTR_MESH_IFNAME, }; static const int info_hard_mandatory[] = { BATADV_ATTR_VERSION, BATADV_ATTR_ALGO_NAME, BATADV_ATTR_HARD_IFNAME, BATADV_ATTR_HARD_ADDRESS, }; static int info_callback(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct print_opts *opts = arg; const uint8_t *primary_mac; struct genlmsghdr *ghdr; const uint8_t *mesh_mac; const char *primary_if; const char *mesh_name; const char *version; char *extra_info = NULL; uint8_t ttvn = 0; uint16_t bla_group_id = 0; const char *algo_name; const char *extra_header; int ret; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_MESH_INFO) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, info_mandatory, ARRAY_SIZE(info_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } mesh_name = nla_get_string(attrs[BATADV_ATTR_MESH_IFNAME]); mesh_mac = nla_data(attrs[BATADV_ATTR_MESH_ADDRESS]); if (attrs[BATADV_ATTR_HARD_IFNAME]) { if (missing_mandatory_attrs(attrs, info_hard_mandatory, ARRAY_SIZE(info_hard_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } version = nla_get_string(attrs[BATADV_ATTR_VERSION]); algo_name = nla_get_string(attrs[BATADV_ATTR_ALGO_NAME]); primary_if = nla_get_string(attrs[BATADV_ATTR_HARD_IFNAME]); primary_mac = nla_data(attrs[BATADV_ATTR_HARD_ADDRESS]); snprintf(algo_name_buf, sizeof(algo_name_buf), "%s", algo_name); if (attrs[BATADV_ATTR_TT_TTVN]) ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]); if (attrs[BATADV_ATTR_BLA_CRC]) bla_group_id = nla_get_u16(attrs[BATADV_ATTR_BLA_CRC]); switch (opts->nl_cmd) { case BATADV_CMD_GET_TRANSTABLE_LOCAL: ret = asprintf(&extra_info, ", TTVN: %u", ttvn); if (ret < 0) extra_info = NULL; break; case BATADV_CMD_GET_BLA_BACKBONE: case BATADV_CMD_GET_BLA_CLAIM: ret = asprintf(&extra_info, ", group id: 0x%04x", bla_group_id); if (ret < 0) extra_info = NULL; break; default: extra_info = strdup(""); break; } if (opts->static_header) extra_header = opts->static_header; else extra_header = ""; ret = asprintf(&opts->remaining_header, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s", version, primary_if, primary_mac[0], primary_mac[1], primary_mac[2], primary_mac[3], primary_mac[4], primary_mac[5], mesh_name, mesh_mac[0], mesh_mac[1], mesh_mac[2], mesh_mac[3], mesh_mac[4], mesh_mac[5], algo_name, extra_info, extra_header); if (ret < 0) opts->remaining_header = NULL; if (extra_info) free(extra_info); } else { ret = asprintf(&opts->remaining_header, "BATMAN mesh %s disabled\n", mesh_name); if (ret < 0) opts->remaining_header = NULL; } return NL_STOP; } static char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header) { struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int family; struct print_opts opts = { .read_opt = 0, .nl_cmd = nl_cmd, .remaining_header = NULL, .static_header = header, }; sock = nl_socket_alloc(); if (!sock) return NULL; genl_connect(sock); family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { nl_socket_free(sock); return NULL; } msg = nlmsg_alloc(); if (!msg) { nl_socket_free(sock); return NULL; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, BATADV_CMD_GET_MESH_INFO, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); nl_send_auto_complete(sock, msg); nlmsg_free(msg); cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) goto err_free_sock; nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, info_callback, &opts); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); err_free_sock: nl_socket_free(sock); return opts.remaining_header; } static void netlink_print_remaining_header(struct print_opts *opts) { if (!opts->remaining_header) return; fputs(opts->remaining_header, stdout); free(opts->remaining_header); opts->remaining_header = NULL; } static int netlink_print_common_cb(struct nl_msg *msg, void *arg) { struct print_opts *opts = arg; netlink_print_remaining_header(opts); return opts->callback(msg, arg); } static const int routing_algos_mandatory[] = { BATADV_ATTR_ALGO_NAME, }; static int routing_algos_callback(struct nl_msg *msg, void *arg __maybe_unused) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct genlmsghdr *ghdr; const char *algo_name; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_ROUTING_ALGOS) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, routing_algos_mandatory, ARRAY_SIZE(routing_algos_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } algo_name = nla_get_string(attrs[BATADV_ATTR_ALGO_NAME]); printf(" * %s\n", algo_name); return NL_OK; } int netlink_print_routing_algos(void) { struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int family; struct print_opts opts = { .callback = routing_algos_callback, }; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; genl_connect(sock); family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { last_err = -EOPNOTSUPP; goto err_free_sock; } msg = nlmsg_alloc(); if (!msg) { last_err = -ENOMEM; goto err_free_sock; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, BATADV_CMD_GET_ROUTING_ALGOS, 1); nl_send_auto_complete(sock, msg); nlmsg_free(msg); opts.remaining_header = strdup("Available routing algorithms:\n"); cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { last_err = -ENOMEM; goto err_free_sock; } nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, netlink_print_common_cb, &opts); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); err_free_sock: nl_socket_free(sock); if (!last_err) netlink_print_remaining_header(&opts); return last_err; } static const int originators_mandatory[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_HARD_IFINDEX, BATADV_ATTR_LAST_SEEN_MSECS, }; static int originators_callback(struct nl_msg *msg, void *arg) { unsigned throughput_mbits, throughput_kbits; struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); int last_seen_msecs, last_seen_secs; struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; char ifname[IF_NAMESIZE]; float last_seen; uint8_t *neigh; uint8_t *orig; char c = ' '; uint8_t tq; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, originators_mandatory, ARRAY_SIZE(originators_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]), ifname)) ifname[0] = '\0'; if (attrs[BATADV_ATTR_FLAG_BEST]) c = '*'; last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); last_seen = (float)last_seen_msecs / 1000.0; last_seen_secs = last_seen_msecs / 1000; last_seen_msecs = last_seen_msecs % 1000; /* skip timed out originators */ if (opts->read_opt & NO_OLD_ORIGS) if (last_seen > opts->orig_timeout) return NL_OK; if (attrs[BATADV_ATTR_THROUGHPUT]) { throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); throughput_mbits = throughput_kbits / 1000; throughput_kbits = throughput_kbits % 1000; if (!(opts->read_opt & USE_BAT_HOSTS)) { printf(" %c %02x:%02x:%02x:%02x:%02x:%02x %4i.%03is (%9u.%1u) %02x:%02x:%02x:%02x:%02x:%02x [%10s]\n", c, orig[0], orig[1], orig[2], orig[3], orig[4], orig[5], last_seen_secs, last_seen_msecs, throughput_mbits, throughput_kbits / 100, neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5], ifname); } else { bat_host = bat_hosts_find_by_mac((char *)orig); if (bat_host) printf(" %c %17s ", c, bat_host->name); else printf(" %c %02x:%02x:%02x:%02x:%02x:%02x ", c, orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); printf("%4i.%03is (%9u.%1u) ", last_seen_secs, last_seen_msecs, throughput_mbits, throughput_kbits / 100); bat_host = bat_hosts_find_by_mac((char *)neigh); if (bat_host) printf(" %c %17s ", c, bat_host->name); else printf(" %02x:%02x:%02x:%02x:%02x:%02x ", neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5]); printf("[%10s]\n", ifname); } } if (attrs[BATADV_ATTR_TQ]) { tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); if (!(opts->read_opt & USE_BAT_HOSTS)) { printf(" %c %02x:%02x:%02x:%02x:%02x:%02x %4i.%03is (%3i) %02x:%02x:%02x:%02x:%02x:%02x [%10s]\n", c, orig[0], orig[1], orig[2], orig[3], orig[4], orig[5], last_seen_secs, last_seen_msecs, tq, neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5], ifname); } else { bat_host = bat_hosts_find_by_mac((char *)orig); if (bat_host) printf(" %c %17s ", c, bat_host->name); else printf(" %c %02x:%02x:%02x:%02x:%02x:%02x ", c, orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); printf("%4i.%03is (%3i) ", last_seen_secs, last_seen_msecs, tq); bat_host = bat_hosts_find_by_mac((char *)neigh); if (bat_host) printf("%17s ", bat_host->name); else printf("%02x:%02x:%02x:%02x:%02x:%02x ", neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5]); printf("[%10s]\n", ifname); } } return NL_OK; } static const int neighbors_mandatory[] = { BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_HARD_IFINDEX, BATADV_ATTR_LAST_SEEN_MSECS, }; static int neighbors_callback(struct nl_msg *msg, void *arg) { unsigned throughput_mbits, throughput_kbits; struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); int last_seen_msecs, last_seen_secs; struct print_opts *opts = arg; struct bat_host *bat_host; char ifname[IF_NAMESIZE]; struct genlmsghdr *ghdr; uint8_t *neigh; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_NEIGHBORS) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, neighbors_mandatory, ARRAY_SIZE(neighbors_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); bat_host = bat_hosts_find_by_mac((char *)neigh); if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]), ifname)) ifname[0] = '\0'; last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); last_seen_secs = last_seen_msecs / 1000; last_seen_msecs = last_seen_msecs % 1000; if (attrs[BATADV_ATTR_THROUGHPUT]) { throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); throughput_mbits = throughput_kbits / 1000; throughput_kbits = throughput_kbits % 1000; if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5]); else printf("%17s ", bat_host->name); printf("%4i.%03is (%9u.%1u) [%10s]\n", last_seen_secs, last_seen_msecs, throughput_mbits, throughput_kbits / 100, ifname); } else { printf(" %10s ", ifname); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", neigh[0], neigh[1], neigh[2], neigh[3], neigh[4], neigh[5]); else printf("%17s ", bat_host->name); printf("%4i.%03is\n", last_seen_secs, last_seen_msecs); } return NL_OK; } static const int transglobal_mandatory[] = { BATADV_ATTR_TT_ADDRESS, BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_TT_VID, BATADV_ATTR_TT_TTVN, BATADV_ATTR_TT_LAST_TTVN, BATADV_ATTR_TT_CRC32, BATADV_ATTR_TT_FLAGS, }; static int transglobal_callback(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; char c, r, w, i, t; uint8_t last_ttvn; uint32_t crc32; uint32_t flags; uint8_t *addr; uint8_t *orig; uint8_t ttvn; int16_t vid; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, transglobal_mandatory, ARRAY_SIZE(transglobal_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]); ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]); last_ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_LAST_TTVN]); crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]); flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01)) return NL_OK; if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01)) return NL_OK; c = ' ', r = '.', w = '.', i = '.', t = '.'; if (attrs[BATADV_ATTR_FLAG_BEST]) c = '*'; if (flags & BATADV_TT_CLIENT_ROAM) r = 'R'; if (flags & BATADV_TT_CLIENT_WIFI) w = 'W'; if (flags & BATADV_TT_CLIENT_ISOLA) i = 'I'; if (flags & BATADV_TT_CLIENT_TEMP) t = 'T'; printf(" %c ", c); bat_host = bat_hosts_find_by_mac((char *)addr); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); else printf("%17s ", bat_host->name); printf("%4i [%c%c%c%c] (%3u) ", BATADV_PRINT_VID(vid), r, w, i, t, ttvn); bat_host = bat_hosts_find_by_mac((char *)orig); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); else printf("%17s ", bat_host->name); printf("(%3u) (0x%.8x)\n", last_ttvn, crc32); return NL_OK; } static const int translocal_mandatory[] = { BATADV_ATTR_TT_ADDRESS, BATADV_ATTR_TT_VID, BATADV_ATTR_TT_CRC32, BATADV_ATTR_TT_FLAGS, }; static int translocal_callback(struct nl_msg *msg, void *arg) { int last_seen_msecs = 0, last_seen_secs = 0; struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; char r, p, n, x, w, i; uint8_t *addr; int16_t vid; uint32_t crc32; uint32_t flags; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, translocal_mandatory, ARRAY_SIZE(translocal_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]); crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]); flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); last_seen_msecs = 0, last_seen_secs = 0; if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01)) return NL_OK; if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01)) return NL_OK; r = '.', p = '.', n = '.', x = '.', w = '.', i = '.'; if (flags & BATADV_TT_CLIENT_ROAM) r = 'R'; if (flags & BATADV_TT_CLIENT_NEW) n = 'N'; if (flags & BATADV_TT_CLIENT_PENDING) x = 'X'; if (flags & BATADV_TT_CLIENT_WIFI) w = 'W'; if (flags & BATADV_TT_CLIENT_ISOLA) i = 'I'; if (flags & BATADV_TT_CLIENT_NOPURGE) { p = 'P'; } else { if (!attrs[BATADV_ATTR_LAST_SEEN_MSECS]) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } last_seen_msecs = nla_get_u32( attrs[BATADV_ATTR_LAST_SEEN_MSECS]); last_seen_secs = last_seen_msecs / 1000; last_seen_msecs = last_seen_msecs % 1000; } bat_host = bat_hosts_find_by_mac((char *)addr); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); else printf("%17s ", bat_host->name); printf("%4i [%c%c%c%c%c%c] %3u.%03u (0x%.8x)\n", BATADV_PRINT_VID(vid), r, p, n, x, w, i, last_seen_secs, last_seen_msecs, crc32); return NL_OK; } static const int gateways_mandatory[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_ROUTER, BATADV_ATTR_HARD_IFNAME, BATADV_ATTR_BANDWIDTH_DOWN, BATADV_ATTR_BANDWIDTH_UP, }; static int gateways_callback(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; const char *primary_if; uint32_t bandwidth_down; uint32_t bandwidth_up; uint32_t throughput; uint8_t *router; uint8_t *orig; char c = ' '; uint8_t tq; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, gateways_mandatory, ARRAY_SIZE(gateways_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } if (attrs[BATADV_ATTR_FLAG_BEST]) c = '*'; orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); router = nla_data(attrs[BATADV_ATTR_ROUTER]); primary_if = nla_get_string(attrs[BATADV_ATTR_HARD_IFNAME]); bandwidth_down = nla_get_u32(attrs[BATADV_ATTR_BANDWIDTH_DOWN]); bandwidth_up = nla_get_u32(attrs[BATADV_ATTR_BANDWIDTH_UP]); printf("%c ", c); bat_host = bat_hosts_find_by_mac((char *)orig); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); else printf("%17s ", bat_host->name); if (attrs[BATADV_ATTR_THROUGHPUT]) { throughput = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); printf("(%9u.%1u) ", throughput / 10, throughput % 10); } else if (attrs[BATADV_ATTR_TQ]) { tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); printf("(%3i) ", tq); } bat_host = bat_hosts_find_by_mac((char *)router); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", router[0], router[1], router[2], router[3], router[4], router[5]); else printf("%17s ", bat_host->name); printf("[%10s]: %u.%u/%u.%u MBit\n", primary_if, bandwidth_down / 10, bandwidth_down % 10, bandwidth_up / 10, bandwidth_up % 10); return NL_OK; } static const int bla_claim_mandatory[] = { BATADV_ATTR_BLA_ADDRESS, BATADV_ATTR_BLA_VID, BATADV_ATTR_BLA_BACKBONE, BATADV_ATTR_BLA_CRC, }; static int bla_claim_callback(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; uint16_t backbone_crc; uint8_t *backbone; uint8_t *client; uint16_t vid; char c = ' '; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_BLA_CLAIM) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, bla_claim_mandatory, ARRAY_SIZE(bla_claim_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } if (attrs[BATADV_ATTR_BLA_OWN]) c = '*'; client = nla_data(attrs[BATADV_ATTR_BLA_ADDRESS]); vid = nla_get_u16(attrs[BATADV_ATTR_BLA_VID]); backbone = nla_data(attrs[BATADV_ATTR_BLA_BACKBONE]); backbone_crc = nla_get_u16(attrs[BATADV_ATTR_BLA_CRC]); bat_host = bat_hosts_find_by_mac((char *)client); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", client[0], client[1], client[2], client[3], client[4], client[5]); else printf("%17s ", bat_host->name); printf("on %5d by ", BATADV_PRINT_VID(vid)); bat_host = bat_hosts_find_by_mac((char *)backbone); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", backbone[0], backbone[1], backbone[2], backbone[3], backbone[4], backbone[5]); else printf("%17s ", bat_host->name); printf("[%c] (0x%04x)\n", c, backbone_crc); return NL_OK; } static const int bla_backbone_mandatory[] = { BATADV_ATTR_BLA_VID, BATADV_ATTR_BLA_BACKBONE, BATADV_ATTR_BLA_CRC, BATADV_ATTR_LAST_SEEN_MSECS, }; static int bla_backbone_callback(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); int last_seen_msecs, last_seen_secs; struct print_opts *opts = arg; struct bat_host *bat_host; struct genlmsghdr *ghdr; uint16_t backbone_crc; uint8_t *backbone; uint16_t vid; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_BLA_BACKBONE) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); exit(1); } if (missing_mandatory_attrs(attrs, bla_backbone_mandatory, ARRAY_SIZE(bla_backbone_mandatory))) { fputs("Missing attributes from kernel\n", stderr); exit(1); } /* don't show own backbones */ if (attrs[BATADV_ATTR_BLA_OWN]) return NL_OK; vid = nla_get_u16(attrs[BATADV_ATTR_BLA_VID]); backbone = nla_data(attrs[BATADV_ATTR_BLA_BACKBONE]); backbone_crc = nla_get_u16(attrs[BATADV_ATTR_BLA_CRC]); last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); last_seen_secs = last_seen_msecs / 1000; last_seen_msecs = last_seen_msecs % 1000; bat_host = bat_hosts_find_by_mac((char *)backbone); if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) printf("%02x:%02x:%02x:%02x:%02x:%02x ", backbone[0], backbone[1], backbone[2], backbone[3], backbone[4], backbone[5]); else printf("%17s ", bat_host->name); printf("on %5d %4i.%03is (0x%04x)\n", BATADV_PRINT_VID(vid), last_seen_secs, last_seen_msecs, backbone_crc); return NL_OK; } static int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval, const char *header, uint8_t nl_cmd, nl_recvmsg_msg_cb_t callback) { struct print_opts opts = { .read_opt = read_opt, .orig_timeout = orig_timeout, .watch_interval = watch_interval, .remaining_header = NULL, .callback = callback, }; int hardifindex = 0; struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int ifindex; int family; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; genl_connect(sock); family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { last_err = -EOPNOTSUPP; goto err_free_sock; } ifindex = if_nametoindex(mesh_iface); if (!ifindex) { fprintf(stderr, "Interface %s is unknown\n", mesh_iface); last_err = -ENODEV; goto err_free_sock; } if (orig_iface) { hardifindex = if_nametoindex(orig_iface); if (!hardifindex) { fprintf(stderr, "Interface %s is unknown\n", orig_iface); last_err = -ENODEV; goto err_free_sock; } } cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { last_err = -ENOMEM; goto err_free_sock; } bat_hosts_init(read_opt); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, netlink_print_common_cb, &opts); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); do { if (read_opt & CLR_CONT_READ) /* clear screen, set cursor back to 0,0 */ printf("\033[2J\033[0;0f"); if (!(read_opt & SKIP_HEADER)) opts.remaining_header = netlink_get_info(ifindex, nl_cmd, header); msg = nlmsg_alloc(); if (!msg) continue; genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, nl_cmd, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); if (hardifindex) nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, hardifindex); nl_send_auto_complete(sock, msg); nlmsg_free(msg); last_err = 0; nl_recvmsgs(sock, cb); /* the header should still be printed when no entry was received */ if (!last_err) netlink_print_remaining_header(&opts); if (!last_err && read_opt & (CONT_READ|CLR_CONT_READ)) usleep(1000000 * watch_interval); } while (!last_err && read_opt & (CONT_READ|CLR_CONT_READ)); bat_hosts_free(); err_free_sock: nl_socket_free(sock); return last_err; } int netlink_print_originators(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { char *header = NULL; char *info_header; int ifindex; ifindex = if_nametoindex(mesh_iface); if (!ifindex) { fprintf(stderr, "Interface %s is unknown\n", mesh_iface); return -ENODEV; } /* only parse routing algorithm name */ last_err = -EINVAL; info_header = netlink_get_info(ifindex, BATADV_CMD_GET_ORIGINATORS, NULL); free(info_header); if (strlen(algo_name_buf) == 0) return last_err; if (!strcmp("BATMAN_IV", algo_name_buf)) header = " Originator last-seen (#/255) Nexthop [outgoingIF]\n"; if (!strcmp("BATMAN_V", algo_name_buf)) header = " Originator last-seen ( throughput) Nexthop [outgoingIF]\n"; if (!header) return -EINVAL; return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, header, BATADV_CMD_GET_ORIGINATORS, originators_callback); } int netlink_print_neighbors(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, "IF Neighbor last-seen\n", BATADV_CMD_GET_NEIGHBORS, neighbors_callback); } int netlink_print_transglobal(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, " Client VID Flags Last ttvn Via ttvn (CRC )\n", BATADV_CMD_GET_TRANSTABLE_GLOBAL, transglobal_callback); } int netlink_print_translocal(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, "Client VID Flags Last seen (CRC )\n", BATADV_CMD_GET_TRANSTABLE_LOCAL, translocal_callback); } int netlink_print_gateways(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { char *header = NULL; char *info_header; int ifindex; ifindex = if_nametoindex(mesh_iface); if (!ifindex) { fprintf(stderr, "Interface %s is unknown\n", mesh_iface); return -ENODEV; } /* only parse routing algorithm name */ last_err = -EINVAL; info_header = netlink_get_info(ifindex, BATADV_CMD_GET_ORIGINATORS, NULL); free(info_header); if (strlen(algo_name_buf) == 0) return last_err; if (!strcmp("BATMAN_IV", algo_name_buf)) header = " Router ( TQ) Next Hop [outgoingIf] Bandwidth\n"; if (!strcmp("BATMAN_V", algo_name_buf)) header = " Router ( throughput) Next Hop [outgoingIf] Bandwidth\n"; if (!header) return -EINVAL; return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, header, BATADV_CMD_GET_GATEWAYS, gateways_callback); } int netlink_print_bla_claim(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, "Client VID Originator [o] (CRC )\n", BATADV_CMD_GET_BLA_CLAIM, bla_claim_callback); } int netlink_print_bla_backbone(char *mesh_iface, char *orig_iface, int read_opts, float orig_timeout, float watch_interval) { return netlink_print_common(mesh_iface, orig_iface, read_opts, orig_timeout, watch_interval, "Originator VID last seen (CRC )\n", BATADV_CMD_GET_BLA_BACKBONE, bla_backbone_callback); } static int nlquery_error_cb(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *nlerr, void *arg) { struct nlquery_opts *query_opts = arg; query_opts->err = nlerr->error; return NL_STOP; } static int nlquery_stop_cb(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlquery_opts *query_opts = arg; int *error = nlmsg_data(nlh); if (*error) query_opts->err = *error; return NL_STOP; } static int netlink_query_common(const char *mesh_iface, uint8_t nl_cmd, nl_recvmsg_msg_cb_t callback, int flags, struct nlquery_opts *query_opts) { struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int ifindex; int family; int ret; query_opts->err = 0; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; ret = genl_connect(sock); if (ret < 0) { query_opts->err = ret; goto err_free_sock; } family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { query_opts->err = -EOPNOTSUPP; goto err_free_sock; } ifindex = if_nametoindex(mesh_iface); if (!ifindex) { query_opts->err = -ENODEV; goto err_free_sock; } cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { query_opts->err = -ENOMEM; goto err_free_sock; } nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, query_opts); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nlquery_stop_cb, query_opts); nl_cb_err(cb, NL_CB_CUSTOM, nlquery_error_cb, query_opts); msg = nlmsg_alloc(); if (!msg) { query_opts->err = -ENOMEM; goto err_free_cb; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags, nl_cmd, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); nl_send_auto_complete(sock, msg); nlmsg_free(msg); nl_recvmsgs(sock, cb); err_free_cb: nl_cb_put(cb); err_free_sock: nl_socket_free(sock); return query_opts->err; } static const int translate_mac_netlink_mandatory[] = { BATADV_ATTR_TT_ADDRESS, BATADV_ATTR_ORIG_ADDRESS, }; struct translate_mac_netlink_opts { struct ether_addr mac; bool found; struct nlquery_opts query_opts; }; static int translate_mac_netlink_cb(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlquery_opts *query_opts = arg; struct translate_mac_netlink_opts *opts; struct genlmsghdr *ghdr; uint8_t *addr; uint8_t *orig; opts = container_of(query_opts, struct translate_mac_netlink_opts, query_opts); if (!genlmsg_valid_hdr(nlh, 0)) return NL_OK; ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { return NL_OK; } if (missing_mandatory_attrs(attrs, translate_mac_netlink_mandatory, ARRAY_SIZE(translate_mac_netlink_mandatory))) return NL_OK; addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); if (!attrs[BATADV_ATTR_FLAG_BEST]) return NL_OK; if (memcmp(&opts->mac, addr, ETH_ALEN) != 0) return NL_OK; memcpy(&opts->mac, orig, ETH_ALEN); opts->found = true; opts->query_opts.err = 0; return NL_STOP; } int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, struct ether_addr *mac_out) { struct translate_mac_netlink_opts opts = { .found = false, .query_opts = { .err = 0, }, }; int ret; memcpy(&opts.mac, mac, ETH_ALEN); ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_TRANSTABLE_GLOBAL, translate_mac_netlink_cb, NLM_F_DUMP, &opts.query_opts); if (ret < 0) return ret; if (!opts.found) return -ENOENT; memcpy(mac_out, &opts.mac, ETH_ALEN); return 0; } static const int get_nexthop_netlink_mandatory[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_HARD_IFINDEX, }; struct get_nexthop_netlink_opts { struct ether_addr mac; uint8_t *nexthop; char *ifname; bool found; struct nlquery_opts query_opts; }; static int get_nexthop_netlink_cb(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlquery_opts *query_opts = arg; struct get_nexthop_netlink_opts *opts; struct genlmsghdr *ghdr; const uint8_t *orig; const uint8_t *neigh; uint32_t index; const char *ifname; opts = container_of(query_opts, struct get_nexthop_netlink_opts, query_opts); if (!genlmsg_valid_hdr(nlh, 0)) return NL_OK; ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { return NL_OK; } if (missing_mandatory_attrs(attrs, get_nexthop_netlink_mandatory, ARRAY_SIZE(get_nexthop_netlink_mandatory))) return NL_OK; orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); index = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); if (!attrs[BATADV_ATTR_FLAG_BEST]) return NL_OK; if (memcmp(&opts->mac, orig, ETH_ALEN) != 0) return NL_OK; /* save result */ memcpy(opts->nexthop, neigh, ETH_ALEN); ifname = if_indextoname(index, opts->ifname); if (!ifname) return NL_OK; opts->found = true; opts->query_opts.err = 0; return NL_STOP; } int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac, uint8_t *nexthop, char *ifname) { struct get_nexthop_netlink_opts opts = { .nexthop = 0, .found = false, .query_opts = { .err = 0, }, }; int ret; memcpy(&opts.mac, mac, ETH_ALEN); opts.nexthop = nexthop; opts.ifname = ifname; ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS, get_nexthop_netlink_cb, NLM_F_DUMP, &opts.query_opts); if (ret < 0) return ret; if (!opts.found) return -ENOENT; return 0; } static const int get_primarymac_netlink_mandatory[] = { BATADV_ATTR_HARD_ADDRESS, }; struct get_primarymac_netlink_opts { uint8_t *primarymac; bool found; struct nlquery_opts query_opts; }; static int get_primarymac_netlink_cb(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlquery_opts *query_opts = arg; struct get_primarymac_netlink_opts *opts; struct genlmsghdr *ghdr; const uint8_t *primary_mac; opts = container_of(query_opts, struct get_primarymac_netlink_opts, query_opts); if (!genlmsg_valid_hdr(nlh, 0)) return NL_OK; ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_GET_MESH_INFO) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { return NL_OK; } if (missing_mandatory_attrs(attrs, get_primarymac_netlink_mandatory, ARRAY_SIZE(get_primarymac_netlink_mandatory))) return NL_OK; primary_mac = nla_data(attrs[BATADV_ATTR_HARD_ADDRESS]); /* save result */ memcpy(opts->primarymac, primary_mac, ETH_ALEN); opts->found = true; opts->query_opts.err = 0; return NL_STOP; } int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac) { struct get_primarymac_netlink_opts opts = { .primarymac = 0, .found = false, .query_opts = { .err = 0, }, }; int ret; opts.primarymac = primarymac; ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_MESH_INFO, get_primarymac_netlink_cb, 0, &opts.query_opts); if (ret < 0) return ret; if (!opts.found) return -ENOENT; return 0; } batctl-2018.0/netlink.h000066400000000000000000000046651324521615300147070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner , Andrew Lunn * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_NETLINK_H #define _BATCTL_NETLINK_H #include #include struct ether_addr; int netlink_print_routing_algos(void); int netlink_print_originators(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_neighbors(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_gateways(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_transglobal(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_translocal(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_gateways(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_bla_claim(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int netlink_print_bla_backbone(char *mesh_iface, char *orig_iface, int read_opt, float orig_timeout, float watch_interval); int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, struct ether_addr *mac_out); int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac, uint8_t *nexthop, char *ifname); int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac); extern struct nla_policy batadv_netlink_policy[]; #endif /* _BATCTL_NETLINK_H */ batctl-2018.0/ping.c000066400000000000000000000216641324521615300141710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "batadv_packet.h" #include "main.h" #include "ping.h" #include "functions.h" #include "bat-hosts.h" #include "debugfs.h" #include "icmp_helper.h" static volatile sig_atomic_t is_aborted = 0; static void ping_usage(void) { fprintf(stderr, "Usage: batctl [options] ping [parameters] mac|bat-host|host_name|IPv4_address \n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -c ping packet count \n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -i interval in seconds\n"); fprintf(stderr, " \t -t timeout in seconds\n"); fprintf(stderr, " \t -R record route\n"); fprintf(stderr, " \t -T don't try to translate mac to originator address\n"); } static void sig_handler(int sig) { switch (sig) { case SIGINT: case SIGTERM: is_aborted = 1; break; default: break; } } int ping(char *mesh_iface, int argc, char **argv) { struct batadv_icmp_packet_rr icmp_packet_out, icmp_packet_in; struct timeval tv; struct ether_addr *dst_mac = NULL, *rr_mac = NULL; struct bat_host *bat_host, *rr_host; ssize_t read_len; int ret = EXIT_FAILURE, res, optchar, found_args = 1; int loop_count = -1, loop_interval = 0, timeout = 1, rr = 0, i; unsigned int seq_counter = 0, packets_out = 0, packets_in = 0, packets_loss; char *dst_string, *mac_string, *rr_string; double time_delta; float min = 0.0, max = 0.0, avg = 0.0, mdev = 0.0; uint8_t last_rr_cur = 0, last_rr[BATADV_RR_LEN][ETH_ALEN]; size_t packet_len; char *debugfs_mnt; int disable_translate_mac = 0; while ((optchar = getopt(argc, argv, "hc:i:t:RT")) != -1) { switch (optchar) { case 'c': loop_count = strtol(optarg, NULL , 10); if (loop_count < 1) loop_count = -1; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 'h': ping_usage(); return EXIT_SUCCESS; case 'i': loop_interval = strtol(optarg, NULL , 10); if (loop_interval < 1) loop_interval = 1; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 't': timeout = strtol(optarg, NULL , 10); if (timeout < 1) timeout = 1; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 'R': rr = 1; found_args++; break; case 'T': disable_translate_mac = 1; found_args += 1; break; default: ping_usage(); return EXIT_FAILURE; } } if (argc <= found_args) { fprintf(stderr, "Error - target mac address or bat-host name not specified\n"); ping_usage(); return EXIT_FAILURE; } check_root_or_die("batctl ping"); dst_string = argv[found_args]; bat_hosts_init(0); bat_host = bat_hosts_find_by_name(dst_string); if (bat_host) dst_mac = &bat_host->mac_addr; if (!dst_mac) { dst_mac = resolve_mac(dst_string); if (!dst_mac) { fprintf(stderr, "Error - mac address of the ping destination could not be resolved and is not a bat-host name: %s\n", dst_string); goto out; } } if (!disable_translate_mac) dst_mac = translate_mac(mesh_iface, dst_mac); mac_string = ether_ntoa_long(dst_mac); signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); goto out; } icmp_interfaces_init(); packet_len = sizeof(struct batadv_icmp_packet); memset(&icmp_packet_out, 0, sizeof(icmp_packet_out)); memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN); icmp_packet_out.packet_type = BATADV_ICMP; icmp_packet_out.version = BATADV_COMPAT_VERSION; icmp_packet_out.msg_type = BATADV_ECHO_REQUEST; icmp_packet_out.ttl = 50; icmp_packet_out.seqno = 0; if (rr) { packet_len = sizeof(struct batadv_icmp_packet_rr); icmp_packet_out.rr_cur = 1; memset(&icmp_packet_out.rr, 0, BATADV_RR_LEN * ETH_ALEN); memset(last_rr, 0, BATADV_RR_LEN * ETH_ALEN); } else { ((struct batadv_icmp_packet *)&icmp_packet_out)->reserved = 0; } printf("PING %s (%s) %zu(%zu) bytes of data\n", dst_string, mac_string, packet_len, packet_len + 28); while (!is_aborted) { tv.tv_sec = timeout; tv.tv_usec = 0; if (loop_count == 0) break; if (loop_count > 0) loop_count--; icmp_packet_out.seqno = htons(++seq_counter); res = icmp_interface_write(mesh_iface, (struct batadv_icmp_header *)&icmp_packet_out, packet_len); if (res < 0) { fprintf(stderr, "Error - can't send icmp packet: %s\n", strerror(-res)); goto sleep; } read_packet: start_timer(); read_len = icmp_interface_read((struct batadv_icmp_header *)&icmp_packet_in, packet_len, &tv); if (is_aborted) break; packets_out++; if (read_len == 0) { printf("Reply from host %s timed out\n", dst_string); goto sleep; } if (read_len < 0) { fprintf(stderr, "Error - can't receive icmp packets: %s\n", strerror(-read_len)); goto sleep; } if ((size_t)read_len < packet_len) { printf("Warning - dropping received packet as it is smaller than expected (%zu): %zd\n", packet_len, read_len); goto sleep; } /* after receiving an unexpected seqno we keep waiting for our answer */ if (htons(seq_counter) != icmp_packet_in.seqno) goto read_packet; switch (icmp_packet_in.msg_type) { case BATADV_ECHO_REPLY: time_delta = end_timer(); printf("%zd bytes from %s icmp_seq=%hu ttl=%d time=%.2f ms", read_len, dst_string, ntohs(icmp_packet_in.seqno), icmp_packet_in.ttl, time_delta); if (read_len == sizeof(struct batadv_icmp_packet_rr)) { if (last_rr_cur == icmp_packet_in.rr_cur && !memcmp(last_rr, icmp_packet_in.rr, BATADV_RR_LEN * ETH_ALEN)) { printf("\t(same route)"); } else { printf("\nRR: "); for (i = 0; i < BATADV_RR_LEN && i < icmp_packet_in.rr_cur; i++) { rr_mac = (struct ether_addr *)&icmp_packet_in.rr[i]; rr_host = bat_hosts_find_by_mac((char *)rr_mac); if (rr_host) rr_string = rr_host->name; else rr_string = ether_ntoa_long(rr_mac); printf("\t%s\n", rr_string); if (memcmp(rr_mac, dst_mac, ETH_ALEN) == 0) printf("\t%s\n", rr_string); } last_rr_cur = icmp_packet_in.rr_cur; memcpy(last_rr, icmp_packet_in.rr, BATADV_RR_LEN * ETH_ALEN); } } printf("\n"); if ((time_delta < min) || (min == 0.0)) min = time_delta; if (time_delta > max) max = time_delta; avg += time_delta; mdev += time_delta * time_delta; packets_in++; break; case BATADV_DESTINATION_UNREACHABLE: printf("From %s: Destination Host Unreachable (icmp_seq %hu)\n", dst_string, ntohs(icmp_packet_in.seqno)); break; case BATADV_TTL_EXCEEDED: printf("From %s: Time to live exceeded (icmp_seq %hu)\n", dst_string, ntohs(icmp_packet_in.seqno)); break; case BATADV_PARAMETER_PROBLEM: fprintf(stderr, "Error - the batman adv kernel module version (%d) differs from ours (%d)\n", icmp_packet_in.version, BATADV_COMPAT_VERSION); printf("Please make sure to use compatible versions!\n"); goto out; default: printf("Unknown message type %d len %zd received\n", icmp_packet_in.msg_type, read_len); break; } sleep: if (loop_interval > 0) sleep(loop_interval); else if ((tv.tv_sec != 0) || (tv.tv_usec != 0)) select(0, NULL, NULL, NULL, &tv); } if (packets_out == 0) packets_loss = 0; else packets_loss = ((packets_out - packets_in) * 100) / packets_out; if (packets_in) { avg /= packets_in; mdev /= packets_in; mdev = mdev - avg * avg; if (mdev > 0.0) mdev = sqrt(mdev); else mdev = 0.0; } else { avg = 0.0; mdev = 0.0; } printf("--- %s ping statistics ---\n", dst_string); printf("%u packets transmitted, %u received, %u%% packet loss\n", packets_out, packets_in, packets_loss); printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n", min, avg, max, mdev); if (packets_in) ret = EXIT_SUCCESS; else ret = EXIT_NOSUCCESS; out: icmp_interfaces_clean(); bat_hosts_free(); return ret; } batctl-2018.0/ping.h000066400000000000000000000017051324521615300141700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_PING_H #define _BATCTL_PING_H int ping(char *mesh_iface, int argc, char **argv); #endif batctl-2018.0/sys.c000066400000000000000000000320221324521615300140400ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "sys.h" #include "functions.h" #include "debug.h" const char *sysfs_param_enable[] = { "enable", "disable", "1", "0", NULL, }; const char *sysfs_param_server[] = { "off", "client", "server", NULL, }; const struct settings_data batctl_settings[BATCTL_SETTINGS_NUM] = { { .opt_long = "orig_interval", .opt_short = "it", .sysfs_name = "orig_interval", .params = NULL, }, { .opt_long = "ap_isolation", .opt_short = "ap", .sysfs_name = "ap_isolation", .params = sysfs_param_enable, }, { .opt_long = "bridge_loop_avoidance", .opt_short = "bl", .sysfs_name = "bridge_loop_avoidance", .params = sysfs_param_enable, }, { .opt_long = "distributed_arp_table", .opt_short = "dat", .sysfs_name = "distributed_arp_table", .params = sysfs_param_enable, }, { .opt_long = "aggregation", .opt_short = "ag", .sysfs_name = "aggregated_ogms", .params = sysfs_param_enable, }, { .opt_long = "bonding", .opt_short = "b", .sysfs_name = "bonding", .params = sysfs_param_enable, }, { .opt_long = "fragmentation", .opt_short = "f", .sysfs_name = "fragmentation", .params = sysfs_param_enable, }, { .opt_long = "network_coding", .opt_short = "nc", .sysfs_name = "network_coding", .params = sysfs_param_enable, }, { .opt_long = "isolation_mark", .opt_short = "mark", .sysfs_name = "isolation_mark", .params = NULL, }, { .opt_long = "multicast_mode", .opt_short = "mm", .sysfs_name = "multicast_mode", .params = sysfs_param_enable, }, }; static void log_level_usage(void) { fprintf(stderr, "Usage: batctl [options] loglevel [parameters] [level[ level[ level]]...]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, "levels:\n"); fprintf(stderr, " \t none Debug logging is disabled\n"); fprintf(stderr, " \t all Print messages from all below\n"); fprintf(stderr, " \t batman Messages related to routing / flooding / broadcasting\n"); fprintf(stderr, " \t routes Messages related to route added / changed / deleted\n"); fprintf(stderr, " \t tt Messages related to translation table operations\n"); fprintf(stderr, " \t bla Messages related to bridge loop avoidance\n"); fprintf(stderr, " \t dat Messages related to arp snooping and distributed arp table\n"); fprintf(stderr, " \t nc Messages related to network coding\n"); fprintf(stderr, " \t mcast Messages related to multicast\n"); } int handle_loglevel(char *mesh_iface, int argc, char **argv) { int optchar, res = EXIT_FAILURE; int log_level = 0; char *path_buff; char str[4]; int i; while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { case 'h': log_level_usage(); return EXIT_SUCCESS; default: log_level_usage(); return EXIT_FAILURE; } } path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); return EXIT_FAILURE; } snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); if (argc != 1) { check_root_or_die("batctl loglevel"); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "none") == 0) { log_level = 0; break; } else if (strcmp(argv[i], "all") == 0) { log_level = 63; break; } else if (strcmp(argv[i], "batman") == 0) log_level |= BIT(0); else if (strcmp(argv[i], "routes") == 0) log_level |= BIT(1); else if (strcmp(argv[i], "tt") == 0) log_level |= BIT(2); else if (strcmp(argv[i], "bla") == 0) log_level |= BIT(3); else if (strcmp(argv[i], "dat") == 0) log_level |= BIT(4); else if (strcmp(argv[i], "nc") == 0) log_level |= BIT(5); else if (strcmp(argv[i], "mcast") == 0) log_level |= BIT(6); else { log_level_usage(); goto out; } } snprintf(str, sizeof(str), "%i", log_level); res = write_file(path_buff, SYS_LOG_LEVEL, str, NULL); goto out; } res = read_file(path_buff, SYS_LOG_LEVEL, USE_READ_BUFF, 0, 0, 0); if (res != EXIT_SUCCESS) goto out; log_level = strtol(line_ptr, (char **) NULL, 10); printf("[%c] %s (%s)\n", (!log_level) ? 'x' : ' ', "all debug output disabled", "none"); printf("[%c] %s (%s)\n", (log_level & BIT(0)) ? 'x' : ' ', "messages related to routing / flooding / broadcasting", "batman"); printf("[%c] %s (%s)\n", (log_level & BIT(1)) ? 'x' : ' ', "messages related to route added / changed / deleted", "routes"); printf("[%c] %s (%s)\n", (log_level & BIT(2)) ? 'x' : ' ', "messages related to translation table operations", "tt"); printf("[%c] %s (%s)\n", (log_level & BIT(3)) ? 'x' : ' ', "messages related to bridge loop avoidance", "bla"); printf("[%c] %s (%s)\n", (log_level & BIT(4)) ? 'x' : ' ', "messages related to arp snooping and distributed arp table", "dat"); printf("[%c] %s (%s)\n", (log_level & BIT(5)) ? 'x' : ' ', "messages related to network coding", "nc"); printf("[%c] %s (%s)\n", (log_level & BIT(6)) ? 'x' : ' ', "messages related to multicast", "mcast"); out: free(path_buff); return res; } static void settings_usage(int setting) { fprintf(stderr, "Usage: batctl [options] %s|%s [parameters]", (char *)batctl_settings[setting].opt_long, (char *)batctl_settings[setting].opt_short); if (batctl_settings[setting].params == sysfs_param_enable) fprintf(stderr, " [0|1]\n"); else if (batctl_settings[setting].params == sysfs_param_server) fprintf(stderr, " [client|server]\n"); else fprintf(stderr, "\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); } int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv) { int vid, optchar, res = EXIT_FAILURE; char *path_buff, *base_dev = NULL; const char **ptr; while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { case 'h': settings_usage(setting); return EXIT_SUCCESS; default: settings_usage(setting); return EXIT_FAILURE; } } /* prepare the classic path */ path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); return EXIT_FAILURE; } snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); /* if the specified interface is a VLAN then change the path to point * to the proper "vlan%{vid}" subfolder in the sysfs tree. */ vid = vlan_get_link(mesh_iface, &base_dev); if (vid >= 0) snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH, base_dev, vid); if (argc == 1) { res = read_file(path_buff, (char *)batctl_settings[setting].sysfs_name, NO_FLAGS, 0, 0, 0); goto out; } check_root_or_die("batctl"); if (!batctl_settings[setting].params) goto write_file; ptr = batctl_settings[setting].params; while (*ptr) { if (strcmp(*ptr, argv[1]) == 0) goto write_file; ptr++; } fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]); fprintf(stderr, "The following values are allowed:\n"); ptr = batctl_settings[setting].params; while (*ptr) { fprintf(stderr, " * %s\n", *ptr); ptr++; } goto out; write_file: res = write_file(path_buff, (char *)batctl_settings[setting].sysfs_name, argv[1], argc > 2 ? argv[2] : NULL); out: free(path_buff); free(base_dev); return res; } static void gw_mode_usage(void) { fprintf(stderr, "Usage: batctl [options] gw_mode [mode] [sel_class|bandwidth]\n"); fprintf(stderr, "options:\n"); fprintf(stderr, " \t -h print this help\n"); } int handle_gw_setting(char *mesh_iface, int argc, char **argv) { int optchar, res = EXIT_FAILURE; char *path_buff, gw_mode; const char **ptr; while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { case 'h': gw_mode_usage(); return EXIT_SUCCESS; default: gw_mode_usage(); return EXIT_FAILURE; } } path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); return EXIT_FAILURE; } snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); if (argc == 1) { res = read_file(path_buff, SYS_GW_MODE, USE_READ_BUFF, 0, 0, 0); if (res != EXIT_SUCCESS) goto out; if (line_ptr[strlen(line_ptr) - 1] == '\n') line_ptr[strlen(line_ptr) - 1] = '\0'; if (strcmp(line_ptr, "client") == 0) gw_mode = GW_MODE_CLIENT; else if (strcmp(line_ptr, "server") == 0) gw_mode = GW_MODE_SERVER; else gw_mode = GW_MODE_OFF; free(line_ptr); line_ptr = NULL; switch (gw_mode) { case GW_MODE_CLIENT: res = read_file(path_buff, SYS_GW_SEL, USE_READ_BUFF, 0, 0, 0); break; case GW_MODE_SERVER: res = read_file(path_buff, SYS_GW_BW, USE_READ_BUFF, 0, 0, 0); break; default: printf("off\n"); goto out; } if (res != EXIT_SUCCESS) goto out; if (line_ptr[strlen(line_ptr) - 1] == '\n') line_ptr[strlen(line_ptr) - 1] = '\0'; switch (gw_mode) { case GW_MODE_CLIENT: printf("client (selection class: %s)\n", line_ptr); break; case GW_MODE_SERVER: printf("server (announced bw: %s)\n", line_ptr); break; default: goto out; } free(line_ptr); line_ptr = NULL; goto out; } check_root_or_die("batctl gw_mode"); if (strcmp(argv[1], "client") == 0) gw_mode = GW_MODE_CLIENT; else if (strcmp(argv[1], "server") == 0) gw_mode = GW_MODE_SERVER; else if (strcmp(argv[1], "off") == 0) gw_mode = GW_MODE_OFF; else goto opt_err; res = write_file(path_buff, SYS_GW_MODE, argv[1], NULL); if (res != EXIT_SUCCESS) goto out; if (argc == 2) goto out; switch (gw_mode) { case GW_MODE_CLIENT: res = write_file(path_buff, SYS_GW_SEL, argv[2], NULL); break; case GW_MODE_SERVER: res = write_file(path_buff, SYS_GW_BW, argv[2], NULL); break; } goto out; opt_err: fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]); fprintf(stderr, "The following values are allowed:\n"); ptr = sysfs_param_server; while (*ptr) { fprintf(stderr, " * %s\n", *ptr); ptr++; } out: free(path_buff); return res; } static void ra_mode_usage(void) { fprintf(stderr, "Usage: batctl [options] routing_algo [algorithm]\n"); fprintf(stderr, "options:\n"); fprintf(stderr, " \t -h print this help\n"); } int handle_ra_setting(int argc, char **argv) { DIR *iface_base_dir; struct dirent *iface_dir; int optchar; char *path_buff; int res = EXIT_FAILURE; int first_iface = 1; while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { case 'h': ra_mode_usage(); return EXIT_SUCCESS; default: ra_mode_usage(); return EXIT_FAILURE; } } check_root_or_die("batctl routing_algo"); if (argc == 2) { res = write_file(SYS_SELECTED_RA_PATH, "", argv[1], NULL); goto out; } path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); goto out; } iface_base_dir = opendir(SYS_IFACE_PATH); if (!iface_base_dir) { fprintf(stderr, "Error - the directory '%s' could not be read: %s\n", SYS_IFACE_PATH, strerror(errno)); fprintf(stderr, "Is the batman-adv module loaded and sysfs mounted ?\n"); goto free_buff; } while ((iface_dir = readdir(iface_base_dir)) != NULL) { snprintf(path_buff, PATH_BUFF_LEN, SYS_ROUTING_ALGO_FMT, iface_dir->d_name); res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); if (res != EXIT_SUCCESS) continue; if (line_ptr[strlen(line_ptr) - 1] == '\n') line_ptr[strlen(line_ptr) - 1] = '\0'; if (first_iface) { first_iface = 0; printf("Active routing protocol configuration:\n"); } printf(" * %s: %s\n", iface_dir->d_name, line_ptr); free(line_ptr); line_ptr = NULL; } closedir(iface_base_dir); free(path_buff); if (!first_iface) printf("\n"); res = read_file("", SYS_SELECTED_RA_PATH, USE_READ_BUFF, 0, 0, 0); if (res != EXIT_SUCCESS) return EXIT_FAILURE; printf("Selected routing algorithm (used when next batX interface is created):\n"); printf(" => %s\n", line_ptr); free(line_ptr); line_ptr = NULL; print_routing_algos(); return EXIT_SUCCESS; free_buff: free(path_buff); out: return res; } batctl-2018.0/sys.h000066400000000000000000000050461324521615300140530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2009-2018 B.A.T.M.A.N. contributors: * * Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_SYS_H #define _BATCTL_SYS_H #include "main.h" #define SYS_BATIF_PATH_FMT "/sys/class/net/%s/mesh/" #define SYS_LOG_LEVEL "log_level" #define SYS_LOG "log" #define SYS_GW_MODE "gw_mode" #define SYS_GW_SEL "gw_sel_class" #define SYS_GW_BW "gw_bandwidth" #define SYS_IFACE_PATH "/sys/class/net" #define SYS_IFACE_DIR SYS_IFACE_PATH"/%s/" #define SYS_MESH_IFACE_FMT SYS_IFACE_PATH"/%s/batman_adv/mesh_iface" #define SYS_IFACE_STATUS_FMT SYS_IFACE_PATH"/%s/batman_adv/iface_status" #define SYS_VLAN_PATH SYS_IFACE_PATH"/%s/mesh/vlan%d/" #define SYS_ROUTING_ALGO_FMT SYS_IFACE_PATH"/%s/mesh/routing_algo" #define SYS_SELECTED_RA_PATH "/sys/module/batman_adv/parameters/routing_algo" #define VLAN_ID_MAX_LEN 4 enum batctl_settings_list { BATCTL_SETTINGS_ORIG_INTERVAL, BATCTL_SETTINGS_AP_ISOLATION, BATCTL_SETTINGS_BLA, BATCTL_SETTINGS_DAT, BATCTL_SETTINGS_AGGREGATION, BATCTL_SETTINGS_BONDING, BATCTL_SETTINGS_FRAGMENTATION, BATCTL_SETTINGS_NETWORK_CODING, BATCTL_SETTINGS_ISOLATION_MARK, BATCTL_SETTINGS_MULTICAST_MODE, BATCTL_SETTINGS_NUM, }; enum gw_modes { GW_MODE_OFF, GW_MODE_CLIENT, GW_MODE_SERVER, }; struct settings_data { const char opt_long[OPT_LONG_MAX_LEN]; const char opt_short[OPT_SHORT_MAX_LEN]; const char sysfs_name[SETTINGS_PATH_MAX_LEN]; const char **params; }; extern const char *sysfs_param_enable[]; extern const char *sysfs_param_server[]; extern const struct settings_data batctl_settings[BATCTL_SETTINGS_NUM]; int handle_loglevel(char *mesh_iface, int argc, char **argv); int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv); int handle_gw_setting(char *mesh_iface, int argc, char **argv); int handle_ra_setting(int argc, char **argv); #endif batctl-2018.0/tcpdump.c000066400000000000000000001175251324521615300147120ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "batadv_packet.h" #include "tcpdump.h" #include "bat-hosts.h" #include "functions.h" #define BATADV_THROUGHPUT_MAX_VALUE 0xFFFFFFFF #ifndef ETH_P_BATMAN #define ETH_P_BATMAN 0x4305 #endif /* ETH_P_BATMAN */ #define IPV6_MIN_MTU 1280 #define LEN_CHECK(buff_len, check_len, desc) \ if ((size_t)(buff_len) < (check_len)) { \ fprintf(stderr, "Warning - dropping received %s packet as it is smaller than expected (%zu): %zu\n", \ desc, (check_len), (size_t)(buff_len)); \ return; \ } static unsigned short dump_level_all = DUMP_TYPE_BATOGM | DUMP_TYPE_BATOGM2 | DUMP_TYPE_BATELP | DUMP_TYPE_BATICMP | DUMP_TYPE_BATUCAST | DUMP_TYPE_BATBCAST | DUMP_TYPE_BATUTVLV | DUMP_TYPE_BATFRAG | DUMP_TYPE_NONBAT; static unsigned short dump_level; static void parse_eth_hdr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed); static void tcpdump_usage(void) { fprintf(stderr, "Usage: batctl tcpdump [parameters] interface [interface]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -c compat filter - only display packets matching own compat version (%i)\n", BATADV_COMPAT_VERSION); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -n don't convert addresses to bat-host names\n"); fprintf(stderr, " \t -p dump specific packet type\n"); fprintf(stderr, " \t -x dump all packet types except specified\n"); fprintf(stderr, "packet types:\n"); fprintf(stderr, " \t\t%3d - batman ogm packets\n", DUMP_TYPE_BATOGM); fprintf(stderr, " \t\t%3d - batman ogmv2 packets\n", DUMP_TYPE_BATOGM2); fprintf(stderr, " \t\t%3d - batman elp packets\n", DUMP_TYPE_BATELP); fprintf(stderr, " \t\t%3d - batman icmp packets\n", DUMP_TYPE_BATICMP); fprintf(stderr, " \t\t%3d - batman unicast packets\n", DUMP_TYPE_BATUCAST); fprintf(stderr, " \t\t%3d - batman broadcast packets\n", DUMP_TYPE_BATBCAST); fprintf(stderr, " \t\t%3d - batman fragmented packets\n", DUMP_TYPE_BATFRAG); fprintf(stderr, " \t\t%3d - batman unicast tvlv packets\n", DUMP_TYPE_BATUTVLV); fprintf(stderr, " \t\t%3d - non batman packets\n", DUMP_TYPE_NONBAT); fprintf(stderr, " \t\t%3d - batman ogm & non batman packets\n", DUMP_TYPE_BATOGM | DUMP_TYPE_NONBAT); } static int print_time(void) { struct timeval tv; struct tm *tm; gettimeofday(&tv, NULL); tm = localtime(&tv.tv_sec); if (tm) printf("%02d:%02d:%02d.%06ld ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec); else printf("00:00:00.000000 "); return 1; } static void batctl_tvlv_parse_gw_v1(void *buff, ssize_t buff_len) { struct batadv_tvlv_gateway_data *tvlv = buff; uint32_t down, up; if (buff_len != sizeof(*tvlv)) { fprintf(stderr, "Warning - dropping received %s packet as it is not the correct size (%zu): %zu\n", "TVLV GWv1", sizeof(*tvlv), buff_len); return; } down = ntohl(tvlv->bandwidth_down); up = ntohl(tvlv->bandwidth_up); printf("\tTVLV GWv1: down %d.%.1dMbps, up %d.%1dMbps\n", down / 10, down % 10, up / 10, up % 10); } static void batctl_tvlv_parse_dat_v1(void (*buff)__attribute__((unused)), ssize_t buff_len) { if (buff_len != 0) { fprintf(stderr, "Warning - dropping received %s packet as it is not the correct size (0): %zu\n", "TVLV DATv1", buff_len); return; } printf("\tTVLV DATv1: enabled\n"); } static void batctl_tvlv_parse_nc_v1(void (*buff)__attribute__((unused)), ssize_t buff_len) { if (buff_len != 0) { fprintf(stderr, "Warning - dropping received %s packet as it is not the correct size (0): %zu\n", "TVLV NCv1", buff_len); return; } printf("\tTVLV NCv1: enabled\n"); } static void batctl_tvlv_parse_tt_v1(void *buff, ssize_t buff_len) { struct batadv_tvlv_tt_data *tvlv = buff; struct batadv_tvlv_tt_vlan_data *vlan; int i; unsigned short num_vlan, num_entry; const char *type; size_t vlan_len; LEN_CHECK(buff_len, sizeof(*tvlv), "TVLV TTv1") if (tvlv->flags & BATADV_TT_OGM_DIFF) type = "OGM DIFF"; else if (tvlv->flags & BATADV_TT_REQUEST) type = "TT REQUEST"; else if (tvlv->flags & BATADV_TT_RESPONSE) type = "TT RESPONSE"; else type = "UNKNOWN"; num_vlan = ntohs(tvlv->num_vlan); vlan_len = sizeof(*tvlv) + sizeof(*vlan) * num_vlan; LEN_CHECK(buff_len, vlan_len, "TVLV TTv1 VLAN") buff_len -= vlan_len; num_entry = buff_len / sizeof(struct batadv_tvlv_tt_change); printf("\tTVLV TTv1: %s [%c] ttvn=%hhu vlan_num=%hu entry_num=%hu\n", type, tvlv->flags & BATADV_TT_FULL_TABLE ? 'F' : '.', tvlv->ttvn, num_vlan, num_entry); vlan = (struct batadv_tvlv_tt_vlan_data *)(tvlv + 1); for (i = 0; i < num_vlan; i++) { printf("\t\tVLAN ID %hd, crc %#.8x\n", BATADV_PRINT_VID(ntohs(vlan->vid)), ntohl(vlan->crc)); vlan++; } } static void batctl_tvlv_parse_roam_v1(void *buff, ssize_t buff_len) { struct batadv_tvlv_roam_adv *tvlv = buff; if (buff_len != sizeof(*tvlv)) { fprintf(stderr, "Warning - dropping received %s packet as it is not the correct size (%zu): %zu\n", "TVLV ROAMv1", sizeof(*tvlv), buff_len); return; } printf("\tTVLV ROAMv1: client %s, VLAN ID %d\n", get_name_by_macaddr((struct ether_addr *)tvlv->client, NO_FLAGS), BATADV_PRINT_VID(ntohs(tvlv->vid))); } typedef void (*batctl_tvlv_parser_t)(void *buff, ssize_t buff_len); static batctl_tvlv_parser_t tvlv_parser_get(uint8_t type, uint8_t version) { switch (type) { case BATADV_TVLV_GW: switch (version) { case 1: return batctl_tvlv_parse_gw_v1; default: return NULL; } case BATADV_TVLV_DAT: switch (version) { case 1: return batctl_tvlv_parse_dat_v1; default: return NULL; } case BATADV_TVLV_NC: switch (version) { case 1: return batctl_tvlv_parse_nc_v1; default: return NULL; } case BATADV_TVLV_TT: switch (version) { case 1: return batctl_tvlv_parse_tt_v1; default: return NULL; } case BATADV_TVLV_ROAM: switch (version) { case 1: return batctl_tvlv_parse_roam_v1; default: return NULL; } default: return NULL; } } static void dump_tvlv(unsigned char *ptr, ssize_t tvlv_len) { struct batadv_tvlv_hdr *tvlv_hdr; batctl_tvlv_parser_t parser; ssize_t len; while (tvlv_len >= (ssize_t)sizeof(*tvlv_hdr)) { tvlv_hdr = (struct batadv_tvlv_hdr *)ptr; /* data after TVLV header */ ptr = (uint8_t *)(tvlv_hdr + 1); tvlv_len -= sizeof(*tvlv_hdr); len = ntohs(tvlv_hdr->len); LEN_CHECK(tvlv_len, (size_t)len, "BAT TVLV"); parser = tvlv_parser_get(tvlv_hdr->type, tvlv_hdr->version); if (parser) parser(ptr, len); /* go to the next container */ ptr += len; tvlv_len -= len; } } static void dump_batman_ucast_tvlv(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_unicast_tvlv_packet *tvlv_packet; struct ether_header *ether_header; struct ether_addr *src, *dst; ssize_t check_len, tvlv_len; check_len = (size_t)buff_len - sizeof(struct ether_header); LEN_CHECK(check_len, sizeof(*tvlv_packet), "BAT UCAST TVLV"); check_len -= sizeof(*tvlv_packet); ether_header = (struct ether_header *)packet_buff; tvlv_packet = (struct batadv_unicast_tvlv_packet *)(ether_header + 1); LEN_CHECK(check_len, (size_t)ntohs(tvlv_packet->tvlv_len), "BAT TVLV (containers)"); if (!time_printed) time_printed = print_time(); src = (struct ether_addr *)tvlv_packet->src; printf("BAT %s > ", get_name_by_macaddr(src, read_opt)); dst = (struct ether_addr *)tvlv_packet->dst; tvlv_len = ntohs(tvlv_packet->tvlv_len); printf("%s: TVLV, len %zu, tvlv_len %zu, ttl %hhu\n", get_name_by_macaddr(dst, read_opt), buff_len - sizeof(struct ether_header), tvlv_len, tvlv_packet->ttl); dump_tvlv((uint8_t *)(tvlv_packet + 1), tvlv_len); } static int dump_bla2_claim(struct ether_header *eth_hdr, struct ether_arp *arphdr, int read_opt) { uint8_t bla_claim_magic[3] = {0xff, 0x43, 0x05}; struct batadv_bla_claim_dst *bla_dst; int arp_is_bla2_claim = 0; uint8_t *hw_src, *hw_dst; if (arphdr->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) goto out; if (arphdr->ea_hdr.ar_pro != htons(ETH_P_IP)) goto out; if (arphdr->ea_hdr.ar_hln != ETH_ALEN) goto out; if (arphdr->ea_hdr.ar_pln != 4) goto out; hw_src = arphdr->arp_sha; hw_dst = arphdr->arp_tha; bla_dst = (struct batadv_bla_claim_dst *)hw_dst; if (memcmp(bla_dst->magic, bla_claim_magic, sizeof(bla_claim_magic)) != 0) goto out; switch (bla_dst->type) { case BATADV_CLAIM_TYPE_CLAIM: printf("BLA CLAIM, backbone %s, ", get_name_by_macaddr((struct ether_addr *)hw_src, read_opt)); printf("client %s, bla group %04x\n", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_shost, read_opt), ntohs(bla_dst->group)); break; case BATADV_CLAIM_TYPE_UNCLAIM: printf("BLA UNCLAIM, backbone %s, ", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_shost, read_opt)); printf("client %s, bla group %04x\n", get_name_by_macaddr((struct ether_addr *)hw_src, read_opt), ntohs(bla_dst->group)); break; case BATADV_CLAIM_TYPE_ANNOUNCE: printf("BLA ANNOUNCE, backbone %s, bla group %04x, crc %04x\n", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_shost, read_opt), ntohs(bla_dst->group), ntohs(*((uint16_t *)(&hw_src[4])))); break; case BATADV_CLAIM_TYPE_REQUEST: printf("BLA REQUEST, src backbone %s, ", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_shost, read_opt)); printf("dst backbone %s\n", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_dhost, read_opt)); break; case BATADV_CLAIM_TYPE_LOOPDETECT: printf("BLA LOOPDETECT, src backbone %s, ", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_shost, read_opt)); printf("dst backbone %s\n", get_name_by_macaddr((struct ether_addr *)eth_hdr->ether_dhost, read_opt)); break; default: printf("BLA UNKNOWN, type %hhu\n", bla_dst->type); break; } arp_is_bla2_claim = 1; out: return arp_is_bla2_claim; } static void dump_arp(unsigned char *packet_buff, ssize_t buff_len, struct ether_header *eth_hdr, int read_opt, int time_printed) { struct ether_arp *arphdr; int arp_is_bla2_claim; LEN_CHECK((size_t)buff_len, sizeof(struct ether_arp), "ARP"); if (!time_printed) print_time(); arphdr = (struct ether_arp *)packet_buff; switch (ntohs(arphdr->arp_op)) { case ARPOP_REQUEST: printf("ARP, Request who-has %s", inet_ntoa(*(struct in_addr *)&arphdr->arp_tpa)); printf(" tell %s (%s), length %zd\n", inet_ntoa(*(struct in_addr *)&arphdr->arp_spa), ether_ntoa_long((struct ether_addr *)&arphdr->arp_sha), buff_len); break; case ARPOP_REPLY: arp_is_bla2_claim = dump_bla2_claim(eth_hdr, arphdr, read_opt); if (arp_is_bla2_claim) break; printf("ARP, Reply %s is-at %s, length %zd\n", inet_ntoa(*(struct in_addr *)&arphdr->arp_spa), ether_ntoa_long((struct ether_addr *)&arphdr->arp_sha), buff_len); break; default: printf("ARP, unknown op code: %i\n", ntohs(arphdr->arp_op)); break; } } static void dump_tcp(const char ip_string[], unsigned char *packet_buff, ssize_t buff_len, size_t ip6_header_len, char *src_addr, char *dst_addr) { uint16_t tcp_header_len; struct tcphdr *tcphdr; LEN_CHECK((size_t)buff_len - ip6_header_len, sizeof(struct tcphdr), "TCP"); tcphdr = (struct tcphdr *)(packet_buff + ip6_header_len); tcp_header_len = tcphdr->doff * 4; printf("%s %s.%i > ", ip_string, src_addr, ntohs(tcphdr->source)); printf("%s.%i: TCP, Flags [%c%c%c%c%c%c], length %zu\n", dst_addr, ntohs(tcphdr->dest), (tcphdr->fin ? 'F' : '.'), (tcphdr->syn ? 'S' : '.'), (tcphdr->rst ? 'R' : '.'), (tcphdr->psh ? 'P' : '.'), (tcphdr->ack ? 'A' : '.'), (tcphdr->urg ? 'U' : '.'), (size_t)buff_len - ip6_header_len - tcp_header_len); } static void dump_udp(const char ip_string[], unsigned char *packet_buff, ssize_t buff_len, size_t ip6_header_len, char *src_addr, char *dst_addr) { struct udphdr *udphdr; LEN_CHECK((size_t)buff_len - ip6_header_len, sizeof(struct udphdr), "UDP"); udphdr = (struct udphdr *)(packet_buff + ip6_header_len); printf("%s %s.%i > ", ip_string, src_addr, ntohs(udphdr->source)); switch (ntohs(udphdr->dest)) { case 67: LEN_CHECK((size_t)buff_len - ip6_header_len - sizeof(struct udphdr), (size_t) 44, "DHCP"); printf("%s.67: BOOTP/DHCP, Request from %s, length %zu\n", dst_addr, ether_ntoa_long((struct ether_addr *)(((char *)udphdr) + sizeof(struct udphdr) + 28)), (size_t)buff_len - ip6_header_len - sizeof(struct udphdr)); break; case 68: printf("%s.68: BOOTP/DHCP, Reply, length %zu\n", dst_addr, (size_t)buff_len - ip6_header_len - sizeof(struct udphdr)); break; default: printf("%s.%i: UDP, length %zu\n", dst_addr, ntohs(udphdr->dest), (size_t)buff_len - ip6_header_len - sizeof(struct udphdr)); break; } } static void dump_ipv6(unsigned char *packet_buff, ssize_t buff_len, int time_printed) { struct ip6_hdr *iphdr; struct icmp6_hdr *icmphdr; char ipsrc[INET6_ADDRSTRLEN], ipdst[INET6_ADDRSTRLEN]; struct nd_neighbor_solicit *nd_neigh_sol; struct nd_neighbor_advert *nd_advert; char nd_nas_target[INET6_ADDRSTRLEN]; const char ip_string[] = "IP6"; iphdr = (struct ip6_hdr *)packet_buff; LEN_CHECK((size_t)buff_len, (size_t)(sizeof(struct ip6_hdr)), ip_string); if (!time_printed) print_time(); if (!inet_ntop(AF_INET6, &iphdr->ip6_src, ipsrc, sizeof(ipsrc))) { fprintf(stderr, "Cannot decode source IPv6\n"); return; } if (!inet_ntop(AF_INET6, &iphdr->ip6_dst, ipdst, sizeof(ipdst))) { fprintf(stderr, "Cannot decode destination IPv6\n"); return; } switch (iphdr->ip6_nxt) { case IPPROTO_ICMPV6: LEN_CHECK((size_t)buff_len - (size_t)(sizeof(struct ip6_hdr)), sizeof(struct icmp6_hdr), "ICMPv6"); icmphdr = (struct icmp6_hdr *)(packet_buff + sizeof(struct ip6_hdr)); printf("%s %s > %s ", ip_string, ipsrc, ipdst); if (icmphdr->icmp6_type < ICMP6_INFOMSG_MASK && (size_t)(buff_len) > IPV6_MIN_MTU) { fprintf(stderr, "Warning - dropping received 'ICMPv6 destination unreached' packet as it is bigger than maximum allowed size (%u): %zu\n", IPV6_MIN_MTU, (size_t)(buff_len)); return; } printf("ICMP6"); switch (icmphdr->icmp6_type) { case ICMP6_DST_UNREACH: switch (icmphdr->icmp6_code) { case ICMP6_DST_UNREACH_NOROUTE: printf(", unreachable route\n"); break; case ICMP6_DST_UNREACH_ADMIN: printf(", unreachable prohibited\n"); break; case ICMP6_DST_UNREACH_ADDR: printf(", unreachable address\n"); break; case ICMP6_DST_UNREACH_BEYONDSCOPE: printf(", beyond scope\n"); break; case ICMP6_DST_UNREACH_NOPORT: printf(", unreachable port\n"); break; default: printf(", unknown unreach code (%u)\n", icmphdr->icmp6_code); } break; case ICMP6_ECHO_REQUEST: printf(" echo request, id: %d, seq: %d, length: %hu\n", icmphdr->icmp6_id, icmphdr->icmp6_seq, iphdr->ip6_plen); break; case ICMP6_ECHO_REPLY: printf(" echo reply, id: %d, seq: %d, length: %hu\n", icmphdr->icmp6_id, icmphdr->icmp6_seq, iphdr->ip6_plen); break; case ICMP6_TIME_EXCEEDED: printf(" time exceeded in-transit, length %zu\n", (size_t)buff_len - sizeof(struct icmp6_hdr)); break; case ND_NEIGHBOR_SOLICIT: nd_neigh_sol = (struct nd_neighbor_solicit *)icmphdr; inet_ntop(AF_INET6, &(nd_neigh_sol->nd_ns_target), nd_nas_target, 40); printf(" neighbor solicitation, who has %s, length %zd\n", nd_nas_target, buff_len); break; case ND_NEIGHBOR_ADVERT: nd_advert = (struct nd_neighbor_advert *)icmphdr; inet_ntop(AF_INET6, &(nd_advert->nd_na_target), nd_nas_target, 40); printf(" neighbor advertisement, tgt is %s, length %zd\n", nd_nas_target, buff_len); break; default: printf(", destination unreachable, unknown icmp6 type (%u)\n", icmphdr->icmp6_type); break; } break; case IPPROTO_TCP: dump_tcp(ip_string, packet_buff, buff_len, sizeof(struct ip6_hdr), ipsrc, ipdst); break; case IPPROTO_UDP: dump_udp(ip_string, packet_buff, buff_len, sizeof(struct ip6_hdr), ipsrc, ipdst); break; default: printf(" IPv6 unknown protocol: %i\n", iphdr->ip6_nxt); } } static void dump_ip(unsigned char *packet_buff, ssize_t buff_len, int time_printed) { char ipsrc[INET_ADDRSTRLEN], ipdst[INET_ADDRSTRLEN]; struct iphdr *iphdr, *tmp_iphdr; const char ip_string[] = "IP"; struct udphdr *tmp_udphdr; struct icmphdr *icmphdr; iphdr = (struct iphdr *)packet_buff; LEN_CHECK((size_t)buff_len, (size_t)(iphdr->ihl * 4), ip_string); if (!time_printed) print_time(); if (!inet_ntop(AF_INET, &iphdr->saddr, ipsrc, sizeof(ipsrc))) { fprintf(stderr, "Cannot decode source IP\n"); return; } if (!inet_ntop(AF_INET, &iphdr->daddr, ipdst, sizeof(ipdst))) { fprintf(stderr, "Cannot decode destination IP\n"); return; } switch (iphdr->protocol) { case IPPROTO_ICMP: LEN_CHECK((size_t)buff_len - (iphdr->ihl * 4), sizeof(struct icmphdr), "ICMP"); icmphdr = (struct icmphdr *)(packet_buff + (iphdr->ihl * 4)); printf("%s %s > ", ip_string, ipsrc); switch (icmphdr->type) { case ICMP_ECHOREPLY: printf("%s: ICMP echo reply, id %hu, seq %hu, length %zu\n", ipdst, ntohs(icmphdr->un.echo.id), ntohs(icmphdr->un.echo.sequence), (size_t)buff_len - (iphdr->ihl * 4)); break; case ICMP_DEST_UNREACH: LEN_CHECK((size_t)buff_len - (iphdr->ihl * 4) - sizeof(struct icmphdr), sizeof(struct iphdr) + 8, "ICMP DEST_UNREACH"); switch (icmphdr->code) { case ICMP_PORT_UNREACH: tmp_iphdr = (struct iphdr *)(((char *)icmphdr) + sizeof(struct icmphdr)); tmp_udphdr = (struct udphdr *)(((char *)tmp_iphdr) + (tmp_iphdr->ihl * 4)); printf("%s: ICMP ", ipdst); printf("%s udp port %hu unreachable, length %zu\n", ipdst, ntohs(tmp_udphdr->dest), (size_t)buff_len - (iphdr->ihl * 4)); break; default: printf("%s: ICMP unreachable %hhu, length %zu\n", ipdst, icmphdr->code, (size_t)buff_len - (iphdr->ihl * 4)); break; } break; case ICMP_ECHO: printf("%s: ICMP echo request, id %hu, seq %hu, length %zu\n", ipdst, ntohs(icmphdr->un.echo.id), ntohs(icmphdr->un.echo.sequence), (size_t)buff_len - (iphdr->ihl * 4)); break; case ICMP_TIME_EXCEEDED: printf("%s: ICMP time exceeded in-transit, length %zu\n", ipdst, (size_t)buff_len - (iphdr->ihl * 4)); break; default: printf("%s: ICMP type %hhu, length %zu\n", ipdst, icmphdr->type, (size_t)buff_len - (iphdr->ihl * 4)); break; } break; case IPPROTO_TCP: dump_tcp(ip_string, packet_buff, buff_len, iphdr->ihl * 4, ipsrc, ipdst); break; case IPPROTO_UDP: dump_udp(ip_string, packet_buff, buff_len, iphdr->ihl * 4, ipsrc, ipdst); break; default: printf("IP unknown protocol: %i\n", iphdr->protocol); break; } } static void dump_vlan(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct vlanhdr *vlanhdr; vlanhdr = (struct vlanhdr *)(packet_buff + sizeof(struct ether_header)); LEN_CHECK((size_t)buff_len, sizeof(struct ether_header) + sizeof(struct vlanhdr), "VLAN"); if (!time_printed) time_printed = print_time(); vlanhdr->vid = ntohs(vlanhdr->vid); printf("vlan %u, p %u, ", vlanhdr->vid, vlanhdr->vid >> 12); /* overwrite vlan tags */ memmove(packet_buff + 4, packet_buff, 2 * ETH_ALEN); parse_eth_hdr(packet_buff + 4, buff_len - 4, read_opt, time_printed); } static void dump_batman_iv_ogm(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct ether_header *ether_header; struct batadv_ogm_packet *batman_ogm_packet; ssize_t tvlv_len, check_len; check_len = (size_t)buff_len - sizeof(struct ether_header); LEN_CHECK(check_len, sizeof(struct batadv_ogm_packet), "BAT IV OGM"); ether_header = (struct ether_header *)packet_buff; batman_ogm_packet = (struct batadv_ogm_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) print_time(); printf("BAT %s: ", get_name_by_macaddr((struct ether_addr *)batman_ogm_packet->orig, read_opt)); tvlv_len = ntohs(batman_ogm_packet->tvlv_len); printf("OGM IV via neigh %s, seq %u, tq %3d, ttl %2d, v %d, flags [%c%c%c], length %zu, tvlv_len %zu\n", get_name_by_macaddr((struct ether_addr *)ether_header->ether_shost, read_opt), ntohl(batman_ogm_packet->seqno), batman_ogm_packet->tq, batman_ogm_packet->ttl, batman_ogm_packet->version, (batman_ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP ? 'N' : '.'), (batman_ogm_packet->flags & BATADV_DIRECTLINK ? 'D' : '.'), (batman_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP ? 'F' : '.'), check_len, tvlv_len); check_len -= sizeof(struct batadv_ogm_packet); LEN_CHECK(check_len, (size_t)tvlv_len, "BAT OGM TVLV (containers)"); dump_tvlv((uint8_t *)(batman_ogm_packet + 1), tvlv_len); } static void dump_batman_ogm2(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_ogm2_packet *batman_ogm2; struct ether_header *ether_header; struct ether_addr *ether_addr; ssize_t tvlv_len, check_len; uint32_t throughput; char thr_str[20]; check_len = (size_t)buff_len - sizeof(struct ether_header); LEN_CHECK(check_len, BATADV_OGM2_HLEN, "BAT OGM2"); ether_header = (struct ether_header *)packet_buff; batman_ogm2 = (struct batadv_ogm2_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) print_time(); ether_addr = (struct ether_addr *)batman_ogm2->orig; printf("BAT %s: ", get_name_by_macaddr(ether_addr, read_opt)); tvlv_len = ntohs(batman_ogm2->tvlv_len); throughput = ntohl(batman_ogm2->throughput); if (throughput == BATADV_THROUGHPUT_MAX_VALUE) snprintf(thr_str, sizeof(thr_str), "MAX"); else snprintf(thr_str, sizeof(thr_str), "%.1fMbps", (float)ntohl(batman_ogm2->throughput) / 10); ether_addr = (struct ether_addr *)ether_header->ether_shost; printf("OGM2 via neigh %s, seq %u, throughput %s, ttl %2d, v %d, length %zu, tvlv_len %zu\n", get_name_by_macaddr(ether_addr, read_opt), ntohl(batman_ogm2->seqno), thr_str, batman_ogm2->ttl, batman_ogm2->version, check_len, tvlv_len); check_len -= BATADV_OGM2_HLEN; LEN_CHECK(check_len, (size_t)tvlv_len, "BAT OGM2 TVLV (containers)"); dump_tvlv((uint8_t *)(batman_ogm2 + 1), tvlv_len); } static void dump_batman_elp(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_elp_packet *batman_elp; struct ether_header *ether_header; struct ether_addr *ether_addr; ssize_t check_len; check_len = (size_t)buff_len - sizeof(struct ether_header); LEN_CHECK(check_len, BATADV_ELP_HLEN, "BAT ELP"); ether_header = (struct ether_header *)packet_buff; batman_elp = (struct batadv_elp_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) print_time(); ether_addr = (struct ether_addr *)batman_elp->orig; printf("BAT %s: ", get_name_by_macaddr(ether_addr, read_opt)); ether_addr = (struct ether_addr *)ether_header->ether_shost; printf("ELP via iface %s, seq %u, v %d, interval %ums, length %zu\n", get_name_by_macaddr(ether_addr, read_opt), ntohl(batman_elp->seqno), batman_elp->version, ntohl(batman_elp->elp_interval), check_len); } static void dump_batman_icmp(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_icmp_packet *icmp_packet; struct batadv_icmp_tp_packet *tp; char *name; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_icmp_packet), "BAT ICMP"); icmp_packet = (struct batadv_icmp_packet *)(packet_buff + sizeof(struct ether_header)); tp = (struct batadv_icmp_tp_packet *)icmp_packet; if (!time_printed) print_time(); printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)icmp_packet->orig, read_opt)); name = get_name_by_macaddr((struct ether_addr *)icmp_packet->dst, read_opt); switch (icmp_packet->msg_type) { case BATADV_ECHO_REPLY: printf("%s: ICMP echo reply, id %hhu, seq %hu, ttl %2d, v %d, length %zu\n", name, icmp_packet->uid, ntohs(icmp_packet->seqno), icmp_packet->ttl, icmp_packet->version, (size_t)buff_len - sizeof(struct ether_header)); break; case BATADV_ECHO_REQUEST: printf("%s: ICMP echo request, id %hhu, seq %hu, ttl %2d, v %d, length %zu\n", name, icmp_packet->uid, ntohs(icmp_packet->seqno), icmp_packet->ttl, icmp_packet->version, (size_t)buff_len - sizeof(struct ether_header)); break; case BATADV_TTL_EXCEEDED: printf("%s: ICMP time exceeded in-transit, id %hhu, seq %hu, ttl %2d, v %d, length %zu\n", name, icmp_packet->uid, ntohs(icmp_packet->seqno), icmp_packet->ttl, icmp_packet->version, (size_t)buff_len - sizeof(struct ether_header)); break; case BATADV_TP: printf("%s: ICMP TP type %s (%hhu), id %hhu, seq %u, ttl %2d, v %d, length %zu\n", name, tp->subtype == BATADV_TP_MSG ? "MSG" : tp->subtype == BATADV_TP_ACK ? "ACK" : "N/A", tp->subtype, tp->uid, ntohl(tp->seqno), tp->ttl, tp->version, (size_t)buff_len - sizeof(struct ether_header)); break; default: printf("%s: ICMP type %hhu, length %zu\n", name, icmp_packet->msg_type, (size_t)buff_len - sizeof(struct ether_header)); break; } } static void dump_batman_ucast(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct ether_header *ether_header; struct batadv_unicast_packet *unicast_packet; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_unicast_packet), "BAT UCAST"); LEN_CHECK((size_t)buff_len - sizeof(struct ether_header) - sizeof(struct batadv_unicast_packet), sizeof(struct ether_header), "BAT UCAST (unpacked)"); ether_header = (struct ether_header *)packet_buff; unicast_packet = (struct batadv_unicast_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) time_printed = print_time(); printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)ether_header->ether_shost, read_opt)); printf("%s: UCAST, ttvn %d, ttl %hhu, ", get_name_by_macaddr((struct ether_addr *)unicast_packet->dest, read_opt), unicast_packet->ttvn, unicast_packet->ttl); parse_eth_hdr(packet_buff + ETH_HLEN + sizeof(struct batadv_unicast_packet), buff_len - ETH_HLEN - sizeof(struct batadv_unicast_packet), read_opt, time_printed); } static void dump_batman_bcast(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct ether_header *ether_header; struct batadv_bcast_packet *bcast_packet; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_bcast_packet), "BAT BCAST"); LEN_CHECK((size_t)buff_len - sizeof(struct ether_header) - sizeof(struct batadv_bcast_packet), sizeof(struct ether_header), "BAT BCAST (unpacked)"); ether_header = (struct ether_header *)packet_buff; bcast_packet = (struct batadv_bcast_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) time_printed = print_time(); printf("BAT %s: ", get_name_by_macaddr((struct ether_addr *)ether_header->ether_shost, read_opt)); printf("BCAST, orig %s, seq %u, ", get_name_by_macaddr((struct ether_addr *)bcast_packet->orig, read_opt), ntohl(bcast_packet->seqno)); parse_eth_hdr(packet_buff + ETH_HLEN + sizeof(struct batadv_bcast_packet), buff_len - ETH_HLEN - sizeof(struct batadv_bcast_packet), read_opt, time_printed); } static void dump_batman_4addr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct ether_header *ether_header; struct batadv_unicast_4addr_packet *unicast_4addr_packet; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_unicast_4addr_packet), "BAT 4ADDR"); LEN_CHECK((size_t)buff_len - sizeof(struct ether_header) - sizeof(struct batadv_unicast_4addr_packet), sizeof(struct ether_header), "BAT 4ADDR (unpacked)"); ether_header = (struct ether_header *)packet_buff; unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) time_printed = print_time(); printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)ether_header->ether_shost, read_opt)); printf("%s: 4ADDR, subtybe %hhu, ttvn %d, ttl %hhu, ", get_name_by_macaddr((struct ether_addr *)unicast_4addr_packet->u.dest, read_opt), unicast_4addr_packet->subtype, unicast_4addr_packet->u.ttvn, unicast_4addr_packet->u.ttl); parse_eth_hdr(packet_buff + ETH_HLEN + sizeof(struct batadv_unicast_4addr_packet), buff_len - ETH_HLEN - sizeof(struct batadv_unicast_4addr_packet), read_opt, time_printed); } static void parse_eth_hdr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_ogm_packet *batman_ogm_packet; struct ether_header *eth_hdr; eth_hdr = (struct ether_header *)packet_buff; switch (ntohs(eth_hdr->ether_type)) { case ETH_P_ARP: if ((dump_level & DUMP_TYPE_NONBAT) || (time_printed)) dump_arp(packet_buff + ETH_HLEN, buff_len - ETH_HLEN, eth_hdr, read_opt, time_printed); break; case ETH_P_IP: if ((dump_level & DUMP_TYPE_NONBAT) || (time_printed)) dump_ip(packet_buff + ETH_HLEN, buff_len - ETH_HLEN, time_printed); break; case ETH_P_IPV6: if ((dump_level & DUMP_TYPE_NONBAT) || (time_printed)) dump_ipv6(packet_buff + ETH_HLEN, buff_len - ETH_HLEN, time_printed); break; case ETH_P_8021Q: if ((dump_level & DUMP_TYPE_NONBAT) || (time_printed)) dump_vlan(packet_buff, buff_len, read_opt, time_printed); break; case ETH_P_BATMAN: batman_ogm_packet = (struct batadv_ogm_packet *)(packet_buff + ETH_HLEN); if ((read_opt & COMPAT_FILTER) && (batman_ogm_packet->version != BATADV_COMPAT_VERSION)) return; switch (batman_ogm_packet->packet_type) { case BATADV_IV_OGM: if (dump_level & DUMP_TYPE_BATOGM) dump_batman_iv_ogm(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_OGM2: if (dump_level & DUMP_TYPE_BATOGM2) dump_batman_ogm2(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_ELP: if (dump_level & DUMP_TYPE_BATELP) dump_batman_elp(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_ICMP: if (dump_level & DUMP_TYPE_BATICMP) dump_batman_icmp(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_UNICAST: if (dump_level & DUMP_TYPE_BATUCAST) dump_batman_ucast(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_BCAST: if (dump_level & DUMP_TYPE_BATBCAST) dump_batman_bcast(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_UNICAST_4ADDR: if (dump_level & DUMP_TYPE_BATUCAST) dump_batman_4addr(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_UNICAST_TVLV: if ((dump_level & DUMP_TYPE_BATUCAST) || (dump_level & DUMP_TYPE_BATUTVLV)) dump_batman_ucast_tvlv(packet_buff, buff_len, read_opt, time_printed); break; default: fprintf(stderr, "Warning - packet contains unknown batman packet type: 0x%02x\n", batman_ogm_packet->packet_type); break; } break; default: fprintf(stderr, "Warning - packet contains unknown ether type: 0x%04x\n", ntohs(eth_hdr->ether_type)); break; } } static int monitor_header_length(unsigned char *packet_buff, ssize_t buff_len, int32_t hw_type) { struct radiotap_header *radiotap_hdr; switch (hw_type) { case ARPHRD_IEEE80211_PRISM: if (buff_len <= (ssize_t)PRISM_HEADER_LEN) return -1; else return PRISM_HEADER_LEN; case ARPHRD_IEEE80211_RADIOTAP: if (buff_len <= (ssize_t)RADIOTAP_HEADER_LEN) return -1; radiotap_hdr = (struct radiotap_header*)packet_buff; if (buff_len <= radiotap_hdr->it_len) return -1; else return radiotap_hdr->it_len; } return -1; } static void parse_wifi_hdr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct ether_header *eth_hdr; struct ieee80211_hdr *wifi_hdr; unsigned char *shost, *dhost; uint16_t fc; int hdr_len; /* we assume a minimum size of 38 bytes * (802.11 data frame + LLC) * before we calculate the real size */ if (buff_len <= 38) return; wifi_hdr = (struct ieee80211_hdr *)packet_buff; fc = ntohs(wifi_hdr->frame_control); /* not carrying payload */ if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) return; /* encrypted packet */ if (fc & IEEE80211_FCTL_PROTECTED) return; shost = wifi_hdr->addr2; if (fc & IEEE80211_FCTL_FROMDS) shost = wifi_hdr->addr3; else if (fc & IEEE80211_FCTL_TODS) shost = wifi_hdr->addr4; dhost = wifi_hdr->addr1; if (fc & IEEE80211_FCTL_TODS) dhost = wifi_hdr->addr3; hdr_len = 24; if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) hdr_len = 30; if (fc & IEEE80211_STYPE_QOS_DATA) hdr_len += 2; /* LLC */ hdr_len += 8; hdr_len -= sizeof(struct ether_header); if (buff_len <= hdr_len) return; buff_len -= hdr_len; packet_buff += hdr_len; eth_hdr = (struct ether_header *)packet_buff; memmove(eth_hdr->ether_shost, shost, ETH_ALEN); memmove(eth_hdr->ether_dhost, dhost, ETH_ALEN); /* printf("parse_wifi_hdr(): ether_type: 0x%04x\n", ntohs(eth_hdr->ether_type)); printf("parse_wifi_hdr(): shost: %s\n", ether_ntoa_long((struct ether_addr *)eth_hdr->ether_shost)); printf("parse_wifi_hdr(): dhost: %s\n", ether_ntoa_long((struct ether_addr *)eth_hdr->ether_dhost)); */ parse_eth_hdr(packet_buff, buff_len, read_opt, time_printed); } static struct dump_if *create_dump_interface(char *iface) { struct dump_if *dump_if; struct ifreq req; int res; dump_if = malloc(sizeof(struct dump_if)); if (!dump_if) return NULL; memset(dump_if, 0, sizeof(struct dump_if)); dump_if->dev = iface; if (strlen(dump_if->dev) > IFNAMSIZ - 1) { fprintf(stderr, "Error - interface name too long: %s\n", dump_if->dev); goto free_dumpif; } dump_if->raw_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (dump_if->raw_sock < 0) { perror("Error - can't create raw socket"); goto free_dumpif; } memset(&req, 0, sizeof (struct ifreq)); strncpy(req.ifr_name, dump_if->dev, IFNAMSIZ); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; res = ioctl(dump_if->raw_sock, SIOCGIFHWADDR, &req); if (res < 0) { perror("Error - can't create raw socket (SIOCGIFHWADDR)"); goto close_socket; } dump_if->hw_type = req.ifr_hwaddr.sa_family; switch (dump_if->hw_type) { case ARPHRD_ETHER: case ARPHRD_IEEE80211_PRISM: case ARPHRD_IEEE80211_RADIOTAP: break; default: fprintf(stderr, "Error - interface '%s' is of unknown type: %i\n", dump_if->dev, dump_if->hw_type); goto close_socket; } memset(&req, 0, sizeof (struct ifreq)); strncpy(req.ifr_name, dump_if->dev, IFNAMSIZ); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; res = ioctl(dump_if->raw_sock, SIOCGIFINDEX, &req); if (res < 0) { perror("Error - can't create raw socket (SIOCGIFINDEX)"); goto close_socket; } dump_if->addr.sll_family = AF_PACKET; dump_if->addr.sll_protocol = htons(ETH_P_ALL); dump_if->addr.sll_ifindex = req.ifr_ifindex; res = bind(dump_if->raw_sock, (struct sockaddr *)&dump_if->addr, sizeof(struct sockaddr_ll)); if (res < 0) { perror("Error - can't bind raw socket"); goto close_socket; } return dump_if; close_socket: close(dump_if->raw_sock); free_dumpif: free(dump_if); return NULL; } static volatile sig_atomic_t is_aborted = 0; static void sig_handler(int sig) { switch (sig) { case SIGINT: case SIGTERM: is_aborted = 1; break; default: break; } } int tcpdump(int argc, char **argv) { struct timeval tv; struct dump_if *dump_if, *dump_if_tmp; struct list_head dump_if_list; fd_set wait_sockets, tmp_wait_sockets; ssize_t read_len; int ret = EXIT_FAILURE, res, optchar, found_args = 1, max_sock = 0, tmp; int read_opt = USE_BAT_HOSTS; unsigned char packet_buff[2000]; int monitor_header_len = -1; dump_level = dump_level_all; while ((optchar = getopt(argc, argv, "chnp:x:")) != -1) { switch (optchar) { case 'c': read_opt |= COMPAT_FILTER; found_args += 1; break; case 'h': tcpdump_usage(); return EXIT_SUCCESS; case 'n': read_opt &= ~USE_BAT_HOSTS; found_args += 1; break; case 'p': tmp = strtol(optarg, NULL , 10); if ((tmp > 0) && (tmp <= dump_level_all)) dump_level = tmp; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; case 'x': tmp = strtol(optarg, NULL , 10); if ((tmp > 0) && (tmp <= dump_level_all)) dump_level &= ~tmp; found_args += ((*((char*)(optarg - 1)) == optchar ) ? 1 : 2); break; default: tcpdump_usage(); return EXIT_FAILURE; } } if (argc <= found_args) { fprintf(stderr, "Error - target interface not specified\n"); tcpdump_usage(); return EXIT_FAILURE; } check_root_or_die("batctl tcpdump"); bat_hosts_init(read_opt); signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); /* init interfaces list */ INIT_LIST_HEAD(&dump_if_list); FD_ZERO(&wait_sockets); while (argc > found_args) { dump_if = create_dump_interface(argv[found_args]); if (!dump_if) goto out; if (dump_if->raw_sock > max_sock) max_sock = dump_if->raw_sock; FD_SET(dump_if->raw_sock, &wait_sockets); list_add_tail(&dump_if->list, &dump_if_list); found_args++; } while (!is_aborted) { memcpy(&tmp_wait_sockets, &wait_sockets, sizeof(fd_set)); tv.tv_sec = 1; tv.tv_usec = 0; res = select(max_sock + 1, &tmp_wait_sockets, NULL, NULL, &tv); if (res == 0) continue; if (res < 0) { perror("Error - can't select on raw socket"); continue; } list_for_each_entry(dump_if, &dump_if_list, list) { if (!FD_ISSET(dump_if->raw_sock, &tmp_wait_sockets)) continue; read_len = read(dump_if->raw_sock, packet_buff, sizeof(packet_buff)); if (read_len < 0) { fprintf(stderr, "Error - can't read from interface '%s': %s\n", dump_if->dev, strerror(errno)); continue; } if ((size_t)read_len < sizeof(struct ether_header)) { fprintf(stderr, "Warning - dropping received packet as it is smaller than expected (%zu): %zd\n", sizeof(struct ether_header), read_len); continue; } switch (dump_if->hw_type) { case ARPHRD_ETHER: parse_eth_hdr(packet_buff, read_len, read_opt, 0); break; case ARPHRD_IEEE80211_PRISM: case ARPHRD_IEEE80211_RADIOTAP: monitor_header_len = monitor_header_length(packet_buff, read_len, dump_if->hw_type); if (monitor_header_len >= 0) parse_wifi_hdr(packet_buff + monitor_header_len, read_len - monitor_header_len, read_opt, 0); break; default: /* should not happen */ break; } fflush(stdout); } } out: list_for_each_entry_safe(dump_if, dump_if_tmp, &dump_if_list, list) { if (dump_if->raw_sock >= 0) close(dump_if->raw_sock); list_del(&dump_if->list); free(dump_if); } bat_hosts_free(); return ret; } batctl-2018.0/tcpdump.h000066400000000000000000000055401324521615300147100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors: * * Andreas Langer , Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #ifndef _BATCTL_TCPDUMP_H #define _BATCTL_TCPDUMP_H #include #include #include #include #include "main.h" #include "list.h" #ifndef ARPHRD_IEEE80211_PRISM #define ARPHRD_IEEE80211_PRISM 802 #endif #ifndef ARPHRD_IEEE80211_RADIOTAP #define ARPHRD_IEEE80211_RADIOTAP 803 #endif #define DUMP_TYPE_BATOGM 1 #define DUMP_TYPE_BATOGM2 2 #define DUMP_TYPE_BATELP 4 #define DUMP_TYPE_BATICMP 8 #define DUMP_TYPE_BATUCAST 16 #define DUMP_TYPE_BATBCAST 32 #define DUMP_TYPE_BATUTVLV 64 #define DUMP_TYPE_BATFRAG 128 #define DUMP_TYPE_NONBAT 256 #define IEEE80211_FCTL_FTYPE 0x0c00 #define IEEE80211_FCTL_TODS 0x0001 #define IEEE80211_FCTL_FROMDS 0x0002 #define IEEE80211_FCTL_PROTECTED 0x0040 #define IEEE80211_FTYPE_DATA 0x0800 #define IEEE80211_STYPE_QOS_DATA 0x8000 struct dump_if { struct list_head list; char *dev; int32_t raw_sock; struct sockaddr_ll addr; int32_t hw_type; }; struct vlanhdr { unsigned short vid; u_int16_t ether_type; } __attribute__ ((packed)); struct ieee80211_hdr { u_int16_t frame_control; u_int16_t duration_id; u_int8_t addr1[ETH_ALEN]; u_int8_t addr2[ETH_ALEN]; u_int8_t addr3[ETH_ALEN]; u_int16_t seq_ctrl; u_int8_t addr4[ETH_ALEN]; } __attribute__ ((packed)); struct radiotap_header { u_int8_t it_version; u_int8_t it_pad; u_int16_t it_len; u_int32_t it_present; } __attribute__((__packed__)); struct prism_item { u_int32_t did; u_int16_t status; u_int16_t len; u_int32_t data; }; struct prism_header { u_int32_t msgcode; u_int32_t msglen; u_int8_t devname[16]; struct prism_item hosttime; struct prism_item mactime; struct prism_item channel; struct prism_item rssi; struct prism_item sq; struct prism_item signal; struct prism_item noise; struct prism_item rate; struct prism_item istx; struct prism_item frmlen; }; #define PRISM_HEADER_LEN sizeof(struct prism_header) #define RADIOTAP_HEADER_LEN sizeof(struct radiotap_header) int tcpdump(int argc, char **argv); #endif batctl-2018.0/tp_meter.c000066400000000000000000000274271324521615300150560ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2013-2018 B.A.T.M.A.N. contributors: * * Antonio Quartulli * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA * * License-Filename: LICENSES/preferred/GPL-2.0 */ #include "main.h" #include "tp_meter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bat-hosts.h" #include "batadv_packet.h" #include "batman_adv.h" #include "functions.h" #include "genl.h" #include "netlink.h" #include "debugfs.h" static struct ether_addr *dst_mac; static char *tp_mesh_iface; struct tp_result { int error; bool found; uint32_t cookie; uint8_t return_value; uint32_t test_time; uint64_t total_bytes; }; struct tp_cookie { int error; bool found; uint32_t cookie; }; static int tpmeter_nl_print_error(struct sockaddr_nl *nla __maybe_unused, struct nlmsgerr *nlerr, void *arg) { struct tp_result *result = arg; if (nlerr->error != -EOPNOTSUPP) fprintf(stderr, "Error received: %s\n", strerror(-nlerr->error)); result->error = nlerr->error; return NL_STOP; } static int tp_meter_result_callback(struct nl_msg *msg, void *arg) { struct tp_result *result = arg; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[NUM_BATADV_ATTR]; struct genlmsghdr *ghdr; uint32_t cookie; if (!genlmsg_valid_hdr(nlh, 0)) { result->error = -EINVAL; return NL_STOP; } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_TP_METER) return NL_OK; if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); result->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_COOKIE]) { result->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_RESULT]) return NL_OK; cookie = nla_get_u32(attrs[BATADV_ATTR_TPMETER_COOKIE]); if (cookie != result->cookie) return NL_OK; result->found = true; result->return_value = nla_get_u8(attrs[BATADV_ATTR_TPMETER_RESULT]); if (attrs[BATADV_ATTR_TPMETER_TEST_TIME]) result->test_time = nla_get_u32(attrs[BATADV_ATTR_TPMETER_TEST_TIME]); if (attrs[BATADV_ATTR_TPMETER_BYTES]) result->total_bytes = nla_get_u64(attrs[BATADV_ATTR_TPMETER_BYTES]); return NL_OK; } static int tp_meter_cookie_callback(struct nl_msg *msg, void *arg) { struct tp_cookie *cookie = arg; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[NUM_BATADV_ATTR]; struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, 0)) { cookie->error = -EINVAL; return NL_STOP; } ghdr = nlmsg_data(nlh); if (ghdr->cmd != BATADV_CMD_TP_METER) { cookie->error = -EINVAL; return NL_STOP; } if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), batadv_netlink_policy)) { fputs("Received invalid data from kernel.\n", stderr); cookie->error = -EINVAL; return NL_STOP; } if (!attrs[BATADV_ATTR_TPMETER_COOKIE]) { cookie->error = -EINVAL; return NL_STOP; } cookie->cookie = nla_get_u32(attrs[BATADV_ATTR_TPMETER_COOKIE]); cookie->found = true; return NL_OK; } static int tp_meter_start(char *mesh_iface, struct ether_addr *dst_mac, uint32_t time, struct tp_cookie *cookie) { struct nl_sock *sock; struct nl_msg *msg; struct nl_cb *cb; int ifindex; int family; int ret; int err = 0; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; ret = genl_connect(sock); if (ret < 0) { err = -EOPNOTSUPP; goto out; } family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { err = -EOPNOTSUPP; goto out; } ifindex = if_nametoindex(mesh_iface); if (!ifindex) { fprintf(stderr, "Interface %s is unknown\n", mesh_iface); err = -ENODEV; goto out; } cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_cookie_callback, cookie); nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, cookie); msg = nlmsg_alloc(); if (!msg) { err = -ENOMEM; goto out; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, BATADV_CMD_TP_METER, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst_mac); nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, time); nl_send_auto_complete(sock, msg); nlmsg_free(msg); nl_recvmsgs(sock, cb); nl_cb_put(cb); if (cookie->error < 0) err = cookie->error; else if (!cookie->found) err= -EINVAL; out: nl_socket_free(sock); return err; } static int no_seq_check(struct nl_msg *msg __maybe_unused, void *arg __maybe_unused) { return NL_OK; } static int tp_recv_result(struct nl_sock *sock, struct tp_result *result) { int err = 0; struct nl_cb *cb; cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_result_callback, result); nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, result); while (result->error == 0 && !result->found) nl_recvmsgs(sock, cb); nl_cb_put(cb); if (result->error < 0) err = result->error; else if (!result->found) err= -EINVAL; return err; } static int tp_meter_stop(char *mesh_iface, struct ether_addr *dst_mac) { struct nl_sock *sock; struct nl_msg *msg; int ifindex; int family; int ret; int err = 0; sock = nl_socket_alloc(); if (!sock) return -ENOMEM; ret = genl_connect(sock); if (ret < 0) { err = -EOPNOTSUPP; goto out; } family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { err = -EOPNOTSUPP; goto out; } ifindex = if_nametoindex(mesh_iface); if (!ifindex) { fprintf(stderr, "Interface %s is unknown\n", mesh_iface); err = -ENODEV; goto out; } msg = nlmsg_alloc(); if (!msg) { err = -ENOMEM; goto out; } genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, BATADV_CMD_TP_METER_CANCEL, 1); nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst_mac); nl_send_auto_complete(sock, msg); nlmsg_free(msg); out: nl_socket_free(sock); return err; } static struct nl_sock *tp_prepare_listening_sock(void) { struct nl_sock *sock; int family; int ret; int mcid; sock = nl_socket_alloc(); if (!sock) return NULL; ret = genl_connect(sock); if (ret < 0) { fprintf(stderr, "Failed to connect to generic netlink: %d\n", ret); goto err; } family = genl_ctrl_resolve(sock, BATADV_NL_NAME); if (family < 0) { fprintf(stderr, "Failed to resolve batman-adv netlink: %d\n", family); goto err; } mcid = nl_get_multicast_id(sock, BATADV_NL_NAME, BATADV_NL_MCAST_GROUP_TPMETER); if (mcid < 0) { fprintf(stderr, "Failed to resolve batman-adv tpmeter multicast group: %d\n", mcid); goto err; } ret = nl_socket_add_membership(sock, mcid); if (ret) { fprintf(stderr, "Failed to join batman-adv tpmeter multicast group: %d\n", ret); goto err; } return sock; err: nl_socket_free(sock); return NULL; } void tp_sig_handler(int sig) { switch (sig) { case SIGINT: case SIGTERM: fflush(stdout); tp_meter_stop(tp_mesh_iface, dst_mac); break; default: break; } } static void tp_meter_usage(void) { fprintf(stderr, "Usage: batctl tp [parameters] \n"); fprintf(stderr, "Parameters:\n"); fprintf(stderr, "\t -t