pax_global_header00006660000000000000000000000064126432332370014517gustar00rootroot0000000000000052 comment=1e7f0e548a0555348e72a78f609526338321a77b batctl-2016.0/000077500000000000000000000000001264323323700130605ustar00rootroot00000000000000batctl-2016.0/Makefile000077500000000000000000000061241264323323700145260ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # # Copyright (C) 2006-2016 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 # # 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 = main.o bat-hosts.o functions.o sys.o debug.o ping.o traceroute.o tcpdump.o hash.o debugfs.o ioctl.o list-batman.o 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 # 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) # 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-2016.0/README000066400000000000000000000515351264323323700137510ustar00rootroot00000000000000############################################################################## # 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 simliar 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) 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 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-2016.0/allocate.h000066400000000000000000000017451264323323700150240ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #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-2016.0/bat-hosts.c000066400000000000000000000146341264323323700151400ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #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) fprintf(stderr, "Error - could not allocate memory: %s\n", strerror(errno)); 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; strncpy(confdir, homedir, CONF_DIR_LEN); confdir[CONF_DIR_LEN - 1] = '\0'; strncat(confdir, &bat_hosts_path[i][1], CONF_DIR_LEN - strlen(confdir) - 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-2016.0/bat-hosts.h000066400000000000000000000022551264323323700151410ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #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-2016.0/bat-hosts.sample000066400000000000000000000000311264323323700161610ustar00rootroot000000000000000:d2:58:ca:91:e8 example batctl-2016.0/bisect_iv.c000066400000000000000000001323641264323323700152040ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #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_FIRST(bat_node->orig_event_list); INIT_LIST_HEAD_FIRST(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->list); INIT_LIST_HEAD_FIRST(orig_event->event_list); INIT_LIST_HEAD_FIRST(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((struct list_head *)&orig_event->event_list, &seqno_event->list, &orig_event->event_list); free(seqno_event); } list_for_each_entry_safe(rt_hist, rt_hist_tmp, &orig_event->rt_hist_list, list) { list_del((struct list_head *)&orig_event->rt_hist_list, &rt_hist->list, &orig_event->rt_hist_list); free(rt_hist); } list_del((struct list_head *)&bat_node->orig_event_list, &orig_event->list, &bat_node->orig_event_list); free(orig_event); } list_for_each_entry_safe(rt_table, rt_table_tmp, &bat_node->rt_table_list, list) { list_del((struct list_head *)&bat_node->rt_table_list, &rt_table->list, &bat_node->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; } INIT_LIST_HEAD(&rt_table->list); rt_table->num_entries = 1; INIT_LIST_HEAD(&rt_hist->list); 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; } INIT_LIST_HEAD(&seqno_event->list); 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; } INIT_LIST_HEAD(&seqno_event->list); 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_first *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; } INIT_LIST_HEAD(&seqno_trace->list); 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_first *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 > ((struct seqno_trace *)trace_list->prev)->seqno)) list_add_tail(&seqno_trace->list, trace_list); else if (seqno_event->seqno < ((struct seqno_trace *)trace_list->next)->seqno) list_add_before((struct list_head *)trace_list, trace_list->next, &seqno_trace->list); else list_add_before(&seqno_trace_prev->list, &seqno_trace_tmp->list, &seqno_trace->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_first 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_FIRST(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((struct list_head *)&trace_list, &seqno_trace->list, &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-2016.0/bisect_iv.h000066400000000000000000000044211264323323700152010ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #ifndef _BATCTL_BISECT_IV_H #define _BATCTL_BISECT_IV_H #include "list-batman.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_first orig_event_list; struct list_head_first 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_first event_list; struct list_head_first 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-2016.0/debug.c000066400000000000000000000204131264323323700143120ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #include #include #include #include "debug.h" #include "debugfs.h" #include "functions.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, }, { .opt_long = "originators", .opt_short = "o", .debugfs_name = "originators", .header_lines = 2, }, { .opt_long = "gateways", .opt_short = "gwl", .debugfs_name = "gateways", .header_lines = 1, }, { .opt_long = "translocal", .opt_short = "tl", .debugfs_name = "transtable_local", .header_lines = 2, }, { .opt_long = "transglobal", .opt_short = "tg", .debugfs_name = "transtable_global", .header_lines = 2, }, { .opt_long = "claimtable", .opt_short = "cl", .debugfs_name = "bla_claim_table", .header_lines = 2, }, { .opt_long = "backbonetable", .opt_short = "bbt", .debugfs_name = "bla_backbone_table", .header_lines = 2, }, { .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, }, }; 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; opterr = 0; 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; } } 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 (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 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; 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; } } 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-2016.0/debug.h000066400000000000000000000034371264323323700143260ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #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_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; }; 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 print_routing_algos(void); int print_vis_info(char *mesh_iface); #endif batctl-2016.0/debugfs.c000066400000000000000000000066351264323323700146550ustar00rootroot00000000000000/* * 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 * */ #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_known_mountpoints[] = { "/sys/kernel/debug/", "/debug/", NULL, }; /* construct a full path to a debugfs element */ int debugfs_make_path(const char *fmt, 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 */ 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) { fprintf(stderr, "Error - can't open /proc/mounts for read: %s\n", strerror(errno)); 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 */ 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-2016.0/debugfs.h000066400000000000000000000024111264323323700146460ustar00rootroot00000000000000/* * 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 * */ #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 const char *debugfs_find_mountpoint(void); extern int debugfs_valid_mountpoint(const char *debugfs); extern int debugfs_valid_entry(const char *path); extern char *debugfs_mount(const char *mountpoint); extern int debugfs_make_path(const char *fmt, char *mesh_iface, char *buffer, int size); #endif /* __DEBUGFS_H__ */ batctl-2016.0/functions.c000066400000000000000000000437631264323323700152510ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #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" static struct timeval 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, NULL, }; void start_timer(void) { gettimeofday(&start_time, NULL); } double end_timer(void) { struct timeval end_time, diff; gettimeofday(&end_time, NULL); diff.tv_sec = end_time.tv_sec - start_time.tv_sec; diff.tv_usec = end_time.tv_usec - start_time.tv_usec; if (diff.tv_usec < 0) { diff.tv_sec--; diff.tv_usec += 1000000; } return (((double)diff.tv_sec * 1000) + ((double)diff.tv_usec / 1000)); } 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); strncpy(full_path, dir, sizeof(full_path)); full_path[sizeof(full_path) - 1] = '\0'; strncat(full_path, fname, sizeof(full_path) - strlen(full_path) - 1); 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; strncpy(full_path, dir, sizeof(full_path)); full_path[sizeof(full_path) - 1] = '\0'; strncat(full_path, fname, sizeof(full_path) - strlen(full_path) - 1); 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; } struct ether_addr *translate_mac(char *mesh_iface, struct ether_addr *mac) { enum { tg_start, tg_mac, tg_via, tg_originator, } pos; char full_path[MAX_PATH+1]; char *debugfs_mnt; static struct ether_addr in_mac; struct ether_addr *mac_result, *mac_tmp; FILE *f = NULL; size_t len = 0; char *line = NULL; char *input, *saveptr, *token; int line_invalid; memcpy(&in_mac, mac, sizeof(in_mac)); mac_result = &in_mac; debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) goto out; debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" DEBUG_TRANSTABLE_GLOBAL, mesh_iface, full_path, sizeof(full_path)); f = fopen(full_path, "r"); if (!f) goto out; 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, &in_mac, sizeof(in_mac)) != 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 { mac_result = mac_tmp; goto out; } break; } if (line_invalid) break; } } out: if (f) fclose(f); free(line); 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 (!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; } batctl-2016.0/functions.h000066400000000000000000000037341264323323700152500ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #ifndef _BATCTL_FUNCTIONS_H #define _BATCTL_FUNCTIONS_H #include #include #define ETH_STR_LEN 17 #define BATMAN_ADV_TAG "batman-adv:" /* 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(char *mesh_iface, struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); int vlan_get_link(const char *ifname, char **parent); 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-2016.0/hash.c000066400000000000000000000211721264323323700141520ustar00rootroot00000000000000/* * Copyright (C) 2006-2016 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 * */ #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); 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-2016.0/hash.h000066400000000000000000000075031264323323700141610ustar00rootroot00000000000000/* * Copyright (C) 2006-2016 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 * */ #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-2016.0/ioctl.c000066400000000000000000000060741264323323700143450ustar00rootroot00000000000000/* * Copyright (C) 2012-2016 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ioctl.h" /* 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) { fprintf(stderr, "Error - can't open driver information: %s\n", strerror(errno)); 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) { fprintf(stderr, "Error - can't get stats strings information: %s\n", strerror(errno)); goto out; } stats->cmd = ETHTOOL_GSTATS; stats->n_stats = n_stats; ifr->ifr_data = (void *) stats; err = ioctl(fd, SIOCETHTOOL, ifr); if (err < 0) { fprintf(stderr, "Error - can't get stats information: %s\n", strerror(errno)); 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) { fprintf(stderr, "Error - can't open socket: %s\n", strerror(errno)); goto out; } ret = statistics_custom_get(fd, &ifr); out: if (fd >= 0) close(fd); return ret; } batctl-2016.0/ioctl.h000066400000000000000000000015541264323323700143500ustar00rootroot00000000000000/* * Copyright (C) 2012-2016 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 * */ #ifndef _BATCTL_IOCTL_H #define _BATCTL_IOCTL_H int ioctl_statistics_get(char *mesh_iface); #endif batctl-2016.0/list-batman.c000066400000000000000000000055301264323323700154420ustar00rootroot00000000000000/* * Copyright (C) 2006-2016 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 * */ #include "list-batman.h" /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the next entries already! */ static void __list_add( struct list_head *new, struct list_head *prev, struct list_head *next ) { new->next = next; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ void list_add( struct list_head *new, struct list_head_first *head ) { __list_add( new, (struct list_head *)head, head->next ); if ( head->prev == (struct list_head *)head ) head->prev = new; } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ void list_add_tail( struct list_head *new, struct list_head_first *head ) { __list_add( new, head->prev, (struct list_head *)head ); head->prev = new; } void list_add_before( struct list_head *prev_node, struct list_head *next_node, struct list_head *new_node ) { prev_node->next = new_node; new_node->next = next_node; } /* * Delete a list entry by making the next entries * point to each other. * * This is only for internal list manipulation where we know * the next entries already! */ static void __list_del( struct list_head *prev, struct list_head *next ) { prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is in an undefined state. */ void list_del( struct list_head *prev_entry, struct list_head *entry, struct list_head_first *head ) { if ( head->prev == entry ) head->prev = prev_entry; __list_del( prev_entry, entry->next ); entry->next = (void *) 0; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ int list_empty( struct list_head_first *head ) { return head->next == (struct list_head *)head; } batctl-2016.0/list-batman.h000066400000000000000000000100471264323323700154460ustar00rootroot00000000000000/* * Copyright (C) 2006-2016 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 * */ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* * XXX: Resolve conflict between this file and on BSD systems. */ #ifdef LIST_HEAD #undef LIST_HEAD #endif /* * Simple linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next; }; struct list_head_first { struct list_head *next, *prev; }; void list_add( struct list_head *new, struct list_head_first *head ); void list_add_tail( struct list_head *new, struct list_head_first *head ); void list_add_before( struct list_head *prev_node, struct list_head *next_node, struct list_head *new_node ); void list_del( struct list_head *prev_entry, struct list_head *entry, struct list_head_first *head ); int list_empty( struct list_head_first *head ); #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); \ } while (0) #define INIT_LIST_HEAD_FIRST(ptr) \ ptr.next = (struct list_head *)&ptr; ptr.prev = (struct list_head *)&ptr; \ /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (struct list_head *)(head); \ pos = pos->next) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (struct list_head *)(head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (struct list_head *)(head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (struct list_head *)(head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) #endif batctl-2016.0/main.c000066400000000000000000000171441264323323700141570ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #include #include #include #include #include "main.h" #include "sys.h" #include "debug.h" #include "ping.h" #include "translate.h" #include "traceroute.h" #include "tcpdump.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"); 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); } /* TODO: remove this generic check here and move it into the individual functions */ /* check if user is root */ if ((strncmp(argv[1], "bisect", strlen("bisect")) != 0) && ((getuid()) || (getgid()))) { fprintf(stderr, "Error - you must be root to run '%s' !\n", argv[0]); exit(EXIT_FAILURE); } 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], "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-2016.0/main.h000066400000000000000000000033271264323323700141620ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #ifndef _BATCTL_MAIN_H #define _BATCTL_MAIN_H #include #ifndef SOURCE_VERSION #define SOURCE_VERSION "2016.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 __packed __attribute((packed)) /* linux kernel compat */ #define BIT(nr) (1UL << (nr)) /* linux kernel compat */ typedef uint8_t u8; /* linux kernel compat */ typedef uint16_t u16; /* 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-2016.0/man/000077500000000000000000000000001264323323700136335ustar00rootroot00000000000000batctl-2016.0/man/batctl.8000066400000000000000000000371611264323323700152050ustar00rootroot00000000000000.\" 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. .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 [\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. .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 '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 criterias 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 .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. .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-2016.0/packet.h000066400000000000000000000435651264323323700145150ustar00rootroot00000000000000/* Copyright (C) 2007-2016 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 _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ #include #include /** * 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_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, /* 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 = BIT(0), BATADV_PRIMARIES_FIRST_HOP = BIT(1), BATADV_DIRECTLINK = BIT(2), }; /* ICMP message types */ enum batadv_icmp_packettype { BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3, BATADV_ECHO_REQUEST = 8, BATADV_TTL_EXCEEDED = 11, BATADV_PARAMETER_PROBLEM = 12, }; /** * 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 = BIT(0), BATADV_MCAST_WANT_ALL_IPV4 = BIT(1), BATADV_MCAST_WANT_ALL_IPV6 = BIT(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 = BIT(0), BATADV_TT_REQUEST = BIT(1), BATADV_TT_RESPONSE = BIT(2), BATADV_TT_FULL_TABLE = BIT(4), }; /** * enum batadv_tt_client_flags - TT client specific flags * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table * @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_WIFI: this client is connected through a wifi interface. * This information is used by the "AP Isolation" feature * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This * information is used by the Extended Isolation feature * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table * @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has * not been announced yet * @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_TEMP: this global client has been detected to be part of * the network but no nnode has already announced it * * 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 = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), BATADV_TT_CLIENT_WIFI = BIT(4), BATADV_TT_CLIENT_ISOLA = BIT(5), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), BATADV_TT_CLIENT_TEMP = BIT(11), }; /** * 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 = BIT(15), }; /* claim frame types for the bridge loop avoidance */ enum batadv_bla_claimframe { BATADV_CLAIM_TYPE_CLAIM = 0x00, BATADV_CLAIM_TYPE_UNCLAIM = 0x01, BATADV_CLAIM_TYPE_ANNOUNCE = 0x02, BATADV_CLAIM_TYPE_REQUEST = 0x03, }; /** * 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_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; }; #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 * @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 reserved:4; #elif defined(__LITTLE_ENDIAN_BITFIELD) u8 reserved:4; 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 - 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 /* _NET_BATMAN_ADV_PACKET_H_ */ batctl-2016.0/ping.c000066400000000000000000000224101264323323700141600ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "ping.h" #include "functions.h" #include "packet.h" #include "bat-hosts.h" #include "debugfs.h" char 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; fd_set read_socket; int ret = EXIT_FAILURE, ping_fd = -1, 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; char icmp_socket[MAX_PATH+1]; 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; } 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; } debugfs_make_path(SOCKET_PATH_FMT, mesh_iface, icmp_socket, sizeof(icmp_socket)); ping_fd = open(icmp_socket, O_RDWR); if (ping_fd < 0) { fprintf(stderr, "Error - can't open a connection to the batman adv kernel module via the socket '%s': %s\n", icmp_socket, strerror(errno)); printf("Check whether the module is loaded and active.\n"); goto out; } 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); if (write(ping_fd, (char *)&icmp_packet_out, packet_len) < 0) { fprintf(stderr, "Error - can't write to batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno)); goto sleep; } read_packet: start_timer(); FD_ZERO(&read_socket); FD_SET(ping_fd, &read_socket); res = select(ping_fd + 1, &read_socket, NULL, NULL, &tv); if (is_aborted) break; packets_out++; if (res == 0) { printf("Reply from host %s timed out\n", dst_string); goto sleep; } if (res < 0) goto sleep; read_len = read(ping_fd, (char *)&icmp_packet_in, packet_len); if (read_len < 0) { fprintf(stderr, "Error - can't read from batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno)); 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: bat_hosts_free(); if (ping_fd >= 0) close(ping_fd); return ret; } batctl-2016.0/ping.h000066400000000000000000000015611264323323700141710ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #ifndef _BATCTL_PING_H #define _BATCTL_PING_H int ping(char *mesh_iface, int argc, char **argv); #endif batctl-2016.0/sys.c000066400000000000000000000417241264323323700140520ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #include #include #include #include #include #include #include "main.h" #include "sys.h" #include "functions.h" #include "debug.h" #define PATH_BUFF_LEN 200 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 interface_usage(void) { fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); } static int print_interfaces(char *mesh_iface) { DIR *iface_base_dir; struct dirent *iface_dir; char *path_buff; int res; if (!file_exists(module_ver_path)) { fprintf(stderr, "Error - batman-adv module has not been loaded\n"); goto err; } path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); goto err; } 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 err_buff; } while ((iface_dir = readdir(iface_base_dir)) != NULL) { snprintf(path_buff, PATH_BUFF_LEN, SYS_MESH_IFACE_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 (strcmp(line_ptr, "none") == 0) goto free_line; if (strcmp(line_ptr, mesh_iface) != 0) goto free_line; free(line_ptr); line_ptr = NULL; snprintf(path_buff, PATH_BUFF_LEN, SYS_IFACE_STATUS_FMT, iface_dir->d_name); res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); if (res != EXIT_SUCCESS) { fprintf(stderr, "\n"); continue; } printf("%s: %s", iface_dir->d_name, line_ptr); free_line: free(line_ptr); line_ptr = NULL; } free(path_buff); closedir(iface_base_dir); return EXIT_SUCCESS; err_buff: free(path_buff); err: return EXIT_FAILURE; } int interface(char *mesh_iface, int argc, char **argv) { char *path_buff; int i, res, optchar; while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { case 'h': interface_usage(); return EXIT_SUCCESS; default: interface_usage(); return EXIT_FAILURE; } } if (argc == 1) return print_interfaces(mesh_iface); if ((strcmp(argv[1], "add") != 0) && (strcmp(argv[1], "a") != 0) && (strcmp(argv[1], "del") != 0) && (strcmp(argv[1], "d") != 0)) { fprintf(stderr, "Error - unknown argument specified: %s\n", argv[1]); interface_usage(); goto err; } if (argc == 2) { fprintf(stderr, "Error - missing interface name(s) after '%s'\n", argv[1]); interface_usage(); goto err; } path_buff = malloc(PATH_BUFF_LEN); if (!path_buff) { fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); goto err; } for (i = 2; i < argc; i++) { snprintf(path_buff, PATH_BUFF_LEN, SYS_MESH_IFACE_FMT, argv[i]); if (!file_exists(path_buff)) { snprintf(path_buff, PATH_BUFF_LEN, SYS_IFACE_DIR, argv[i]); if (!file_exists(path_buff)) { fprintf(stderr, "Error - interface does not exist: %s\n", argv[i]); continue; } if (!file_exists(module_ver_path)) { fprintf(stderr, "Error - batman-adv module has not been loaded\n"); goto err_buff; } fprintf(stderr, "Error - interface type not supported by batman-adv: %s\n", argv[i]); continue; } if (argv[1][0] == 'a') res = write_file("", path_buff, mesh_iface, NULL); else res = write_file("", path_buff, "none", NULL); if (res != EXIT_SUCCESS) goto err_buff; } free(path_buff); return EXIT_SUCCESS; err_buff: free(path_buff); err: return EXIT_FAILURE; } 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"); } 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); snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); if (argc != 1) { 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 { 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"); 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); 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; } 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); 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; } 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; } } 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; } 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; } batctl-2016.0/sys.h000066400000000000000000000051661264323323700140570ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #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 interface(char *mesh_iface, int argc, char **argv); 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); int check_mesh_iface(char *mesh_iface); int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface); #endif batctl-2016.0/tcpdump.c000066400000000000000000001103421264323323700147010ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #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 "tcpdump.h" #include "packet.h" #include "bat-hosts.h" #include "functions.h" #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_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 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); printf("%02d:%02d:%02d.%06ld ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec); 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, 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_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 batadv_tvlv_hdr *tvlv_hdr; struct ether_header *ether_header; struct ether_addr *src, *dst; batctl_tvlv_parser_t parser; ssize_t check_len, tvlv_len, len; uint8_t *ptr; check_len = (size_t)buff_len - sizeof(struct ether_header); LEN_CHECK(check_len, sizeof(*tvlv_packet), "BAT 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); ptr = (uint8_t *)(tvlv_packet + 1); 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 UCAST 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 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; 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; struct batadv_tvlv_hdr *tvlv_hdr; ssize_t tvlv_len, len, check_len; batctl_tvlv_parser_t parser; uint8_t *ptr; 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)"); ptr = (uint8_t *)(batman_ogm_packet + 1); 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 IV OGM 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_icmp(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_icmp_packet *icmp_packet; 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)); 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; 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_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); } int tcpdump(int argc, char **argv) { struct ifreq req; struct timeval tv; struct dump_if *dump_if, *dump_if_tmp; struct list_head_first 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; } bat_hosts_init(read_opt); /* init interfaces list */ INIT_LIST_HEAD_FIRST(dump_if_list); FD_ZERO(&wait_sockets); while (argc > found_args) { dump_if = malloc(sizeof(struct dump_if)); memset(dump_if, 0, sizeof(struct dump_if)); dump_if->raw_sock = -1; INIT_LIST_HEAD(&dump_if->list); dump_if->dev = argv[found_args]; if (strlen(dump_if->dev) > IFNAMSIZ - 1) { fprintf(stderr, "Error - interface name too long: %s\n", dump_if->dev); goto out; } dump_if->raw_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (dump_if->raw_sock < 0) { fprintf(stderr, "Error - can't create raw socket: %s\n", strerror(errno)); goto out; } 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) { fprintf(stderr, "Error - can't create raw socket (SIOCGIFHWADDR): %s\n", strerror(errno)); close(dump_if->raw_sock); goto out; } 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 out; } 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) { fprintf(stderr, "Error - can't create raw socket (SIOCGIFINDEX): %s\n", strerror(errno)); close(dump_if->raw_sock); goto out; } 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) { fprintf(stderr, "Error - can't bind raw socket: %s\n", strerror(errno)); close(dump_if->raw_sock); 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 (1) { 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) { fprintf(stderr, "Error - can't select on raw socket: %s\n", strerror(errno)); 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((struct list_head *)&dump_if_list, &dump_if->list, &dump_if_list); free(dump_if); } bat_hosts_free(); return ret; } batctl-2016.0/tcpdump.h000066400000000000000000000053311264323323700147070ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #ifndef _BATCTL_TCPDUMP_H #define _BATCTL_TCPDUMP_H #include #include #include #include #include "main.h" #include "list-batman.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_BATICMP 2 #define DUMP_TYPE_BATUCAST 4 #define DUMP_TYPE_BATBCAST 8 #define DUMP_TYPE_BATUTVLV 16 #define DUMP_TYPE_BATFRAG 32 #define DUMP_TYPE_NONBAT 128 #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-2016.0/traceroute.c000066400000000000000000000151761264323323700154130ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "traceroute.h" #include "functions.h" #include "packet.h" #include "bat-hosts.h" #include "debugfs.h" #define TTL_MAX 50 #define NUM_PACKETS 3 static void traceroute_usage(void) { fprintf(stderr, "Usage: batctl [options] traceroute [parameters] mac|bat-host|host_name|IPv4_address \n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); fprintf(stderr, " \t -n don't convert addresses to bat-host names\n"); fprintf(stderr, " \t -T don't try to translate mac to originator address\n"); } int traceroute(char *mesh_iface, int argc, char **argv) { struct batadv_icmp_packet icmp_packet_out, icmp_packet_in; struct bat_host *bat_host; struct ether_addr *dst_mac = NULL; struct timeval tv; fd_set read_socket; ssize_t read_len; char *dst_string, *mac_string, *return_mac, dst_reached = 0; int ret = EXIT_FAILURE, res, trace_fd = -1, i; int found_args = 1, optchar, seq_counter = 0, read_opt = USE_BAT_HOSTS; double time_delta[NUM_PACKETS]; char *debugfs_mnt; char icmp_socket[MAX_PATH+1]; int disable_translate_mac = 0; while ((optchar = getopt(argc, argv, "hnT")) != -1) { switch (optchar) { case 'h': traceroute_usage(); return EXIT_SUCCESS; case 'n': read_opt &= ~USE_BAT_HOSTS; found_args += 1; break; case 'T': disable_translate_mac = 1; found_args += 1; break; default: traceroute_usage(); return EXIT_FAILURE; } } if (argc <= found_args) { fprintf(stderr, "Error - target mac address or bat-host name not specified\n"); traceroute_usage(); return EXIT_FAILURE; } dst_string = argv[found_args]; bat_hosts_init(read_opt); 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); debugfs_mnt = debugfs_mount(NULL); if (!debugfs_mnt) { fprintf(stderr, "Error - can't mount or find debugfs\n"); goto out; } debugfs_make_path(SOCKET_PATH_FMT, mesh_iface, icmp_socket, sizeof(icmp_socket)); trace_fd = open(icmp_socket, O_RDWR); if (trace_fd < 0) { fprintf(stderr, "Error - can't open a connection to the batman adv kernel module via the socket '%s': %s\n", icmp_socket, strerror(errno)); fprintf(stderr, "Check whether the module is loaded and active.\n"); goto out; } memset(&icmp_packet_out, 0, sizeof(icmp_packet_out)); memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN); icmp_packet_out.version = BATADV_COMPAT_VERSION; icmp_packet_out.packet_type = BATADV_ICMP; icmp_packet_out.msg_type = BATADV_ECHO_REQUEST; icmp_packet_out.seqno = 0; icmp_packet_out.reserved = 0; printf("traceroute to %s (%s), %d hops max, %zu byte packets\n", dst_string, mac_string, TTL_MAX, sizeof(icmp_packet_out)); for (icmp_packet_out.ttl = 1; !dst_reached && icmp_packet_out.ttl < TTL_MAX; icmp_packet_out.ttl++) { return_mac = NULL; bat_host = NULL; for (i = 0; i < NUM_PACKETS; i++) { icmp_packet_out.seqno = htons(++seq_counter); time_delta[i] = 0.0; if (write(trace_fd, (char *)&icmp_packet_out, sizeof(icmp_packet_out)) < 0) { fprintf(stderr, "Error - can't write to batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno)); continue; } read_packet: start_timer(); tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO(&read_socket); FD_SET(trace_fd, &read_socket); res = select(trace_fd + 1, &read_socket, NULL, NULL, &tv); if (res <= 0) continue; read_len = read(trace_fd, (char *)&icmp_packet_in, sizeof(icmp_packet_in)); if (read_len < 0) { fprintf(stderr, "Error - can't read from batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno)); continue; } if ((size_t)read_len < sizeof(icmp_packet_in)) { printf("Warning - dropping received packet as it is smaller than expected (%zu): %zd\n", sizeof(icmp_packet_in), read_len); continue; } /* 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: dst_reached = 1; /* fall through */ case BATADV_TTL_EXCEEDED: time_delta[i] = end_timer(); if (!return_mac) { return_mac = ether_ntoa_long((struct ether_addr *)&icmp_packet_in.orig); if (read_opt & USE_BAT_HOSTS) bat_host = bat_hosts_find_by_mac((char *)&icmp_packet_in.orig); } break; case BATADV_DESTINATION_UNREACHABLE: printf("%s: Destination Host Unreachable\n", dst_string); goto out; 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); fprintf(stderr, "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; } } if (!bat_host) printf("%2hhu: %s", icmp_packet_out.ttl, (return_mac ? return_mac : "*")); else printf("%2hhu: %s (%s)", icmp_packet_out.ttl, bat_host->name, return_mac); for (i = 0; i < NUM_PACKETS; i++) { if (time_delta[i]) printf(" %.3f ms", time_delta[i]); else printf(" *"); } printf("\n"); } ret = EXIT_SUCCESS; out: bat_hosts_free(); if (trace_fd >= 0) close(trace_fd); return ret; } batctl-2016.0/traceroute.h000066400000000000000000000016031264323323700154060ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #ifndef _BATCTL_TRACEROUTE_H #define _BATCTL_TRACEROUTE_H int traceroute(char *mesh_iface, int argc, char **argv); #endif batctl-2016.0/translate.c000066400000000000000000000036701264323323700152270ustar00rootroot00000000000000/* * Copyright (C) 2007-2016 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 * */ #include #include #include "main.h" #include "translate.h" #include "functions.h" #include "bat-hosts.h" static void translate_usage(void) { fprintf(stderr, "Usage: batctl [options] translate mac|bat-host|host_name|IPv4_address\n"); } int translate(char *mesh_iface, int argc, char **argv) { struct ether_addr *dst_mac = NULL; struct bat_host *bat_host; int ret = EXIT_FAILURE; char *dst_string, *mac_string; if (argc <= 1) { fprintf(stderr, "Error - destination not specified\n"); translate_usage(); return EXIT_FAILURE; } dst_string = argv[1]; 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; } } dst_mac = translate_mac(mesh_iface, dst_mac); if (dst_mac) { mac_string = ether_ntoa_long(dst_mac); printf("%s\n", mac_string); ret = EXIT_SUCCESS; } else { ret = EXIT_NOSUCCESS; } out: bat_hosts_free(); return ret; } batctl-2016.0/translate.h000066400000000000000000000016001264323323700152230ustar00rootroot00000000000000/* * Copyright (C) 2009-2016 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 * */ #ifndef _BATCTL_TRANSLATE_H #define _BATCTL_TRANSLATE_H int translate(char *mesh_iface, int argc, char **argv); #endif