pax_global_header00006660000000000000000000000064122266117550014521gustar00rootroot0000000000000052 comment=647431e5beca1358c26102b96966bbd2fae16696 batctl-2013.4.0/000077500000000000000000000000001222661175500132215ustar00rootroot00000000000000batctl-2013.4.0/Makefile000077500000000000000000000047521222661175500146740ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # # Copyright (C) 2006-2013 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 vis.o debugfs.o ioctl.o list-batman.o translate.o OBJ_BISECT = bisect_iv.o MANPAGE = man/batctl.8 # batctl flags and options CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD 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 # 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) $(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-2013.4.0/README000066400000000000000000000522461222661175500141120ustar00rootroot00000000000000############################################################################## # 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, MAC, bat_host-name) to the originator mac address responsible for it. Usage: batctl translate mac|bat-host|host-name|IPv4-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 ping ============ Sends a Layer 2 batman-adv ping to check round trip time and connectivity Usage: batctl ping [parameters] mac|bat-host|host-name|IPv4-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|IPv4-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 vis 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) 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 aggregation ================== display or modify the packet aggregation setting Usage: batctl aggregation|ag [0|1] 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 vis_mode ================= display or modify the status of the VIS server Usage: batctl vis_mode|vm [mode] Example: $ batctl vis_mode client batctl vis_data =============== display the VIS data in dot or JSON format Usage: batctl vis dot {-h}{--no-TT|-T} {--no-2nd|-2} {--numbers|-n} or batctl vis json {-h}{--no-TT|-T} {--no-2nd|-2} {--numbers|-n} Example: (A <- 100% -> B,B-if2 <- 50% -> C) $ batctl vis_data dot digraph { "A" -> "B" [label="1.00"] "A" -> "00:ff:f3:cc:68:ac" [label="TT"] subgraph "cluster_A" { "A" [peripheries=2] } "B" -> "A" [label="1.00"] "B-if2" -> "C" [label="2.00"] "B" -> "22:ff:f3:cc:68:ac" [label="TT"] subgraph "cluster_B" { "B" [peripheries=2] "B-if2" } "C" -> "B-if2" [label="2.00"] "C" -> "44:ff:f3:cc:68:ac" [label="TT"] subgraph "cluster_C" { "C" [peripheries=2] } } Explanation: The vis dot (or json) output is adding an entry for each link between two originator's interfaces which are being used for internal routing in batman. The labels are similar/compatible to the ETX metric, 1.0 means perfect connection (100%), 2.0 means 50%, 3.0 means 33% and so on. A host's mac address which is currently connected to the interface of a mesh node (either the mesh node itself or hosts being bridged into the mesh) is being displayed with an "TT"-label. (--no-TT omits this output) To still have the information about which interfaces belong to which mesh node a subgraph/cluster is being added. The subpgraph is being labeled with a mesh nodes primary interface mac (= Originator MAC). It also has an additional tag [peripheries=2] to make this important MAC address visible, for instance in an image. (--no-2nd omits this output) After the conversion to a png file with graphviz-tools' fdp, all interfaces of a node would be combined in a visual box (see below for details). vis-dot to png -------------- The vis dot output could then further be converted to an image of the topology graph, e.g. with the help of the graphviz-tools. The according commands could then look like this: $ batctl vis_data dot > /tmp/graph.dot $ fdp -Tpng /tmp/graph.dot > graph.png Meaning of the shapes in this image file: * Ellipses: All BATMAN-node and host interfaces can be found in here labeled with the according interface MAC-address. * Boxes: interfaces belonging to one BATMAN-node * double circled interfaces: the primary interface of a BATMAN-node (which is known to other BATMAN-nodes only, except direct neighbours) * Ellipses with a TT-arrow: mesh clients (this can be a BATMAN-node itself with its bat0 interface or computers/devices being bridged into the mesh) * Arrows with numbers: the transmit quality (in the form 1/TQ) from one BATMAN interface to another BATMAN interface batctl-2013.4.0/allocate.h000066400000000000000000000017241222661175500151620ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 _ALLOCATE_H #define _ALLOCATE_H 1 /* debug allocate wrapper to keep hash.c happy */ #include #define debugMalloc(length, tag) malloc(length) #define debugFree(mem, tag) free(mem) #endif batctl-2013.4.0/bat-hosts.c000066400000000000000000000144611222661175500152770ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "main.h" #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 - 1); 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 - 1); 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)); } 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-2013.4.0/bat-hosts.h000066400000000000000000000022351222661175500153000ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 _BAT_HOSTS_H #define _BAT_HOSTS_H 1 #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-2013.4.0/bat-hosts.sample000066400000000000000000000000311222661175500163220ustar00rootroot000000000000000:d2:58:ca:91:e8 example batctl-2013.4.0/bisect_iv.c000066400000000000000000001323441222661175500153430ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 "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); 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].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); curr_loop_magic[sizeof(curr_loop_magic) - 1] = '\0'; 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); curr_loop_magic[sizeof(curr_loop_magic) - 1] = '\0'; 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 ? " " : "| ")); new_head[sizeof(new_head) - 1] = '\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 ? '\\' : '|')); head[sizeof(head) - 1] = '\0'; 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) 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); 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-2013.4.0/bisect_iv.h000066400000000000000000000043131222661175500153420ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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" #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; }; batctl-2013.4.0/debug.c000066400000000000000000000126211222661175500144550ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 "main.h" #include "debug.h" #include "debugfs.h" #include "functions.h" const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM] = { { .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 = 1, }, { .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, }, }; 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"); } 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; float orig_timeout; float watch_interval = 1; opterr = 0; while ((optchar = getopt(argc, argv, "hnw:t:H")) != -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 '?': if (optopt == 't') fprintf(stderr, "Error - option '-t' needs a number 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; } } 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)); 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); } 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-2013.4.0/debug.h000066400000000000000000000031231222661175500144570ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 * */ #define DEBUG_BATIF_PATH_FMT "%s/batman_adv/%s" #define DEBUG_VIS_DATA "vis_data" #define DEBUG_TRANSTABLE_GLOBAL "transtable_global" #define DEBUG_LOG "log" enum batctl_debug_tables { 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); batctl-2013.4.0/debugfs.c000066400000000000000000000066511222661175500150140ustar00rootroot00000000000000/* * 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 #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) { int len; if (strlen(debugfs_mountpoint) == 0) { buffer[0] = '\0'; return -1; } len = strlen(debugfs_mountpoint) + strlen(fmt) + 1; if (len >= size) return len+1; snprintf(buffer, size-1, fmt, debugfs_mountpoint, mesh_iface); buffer[size - 1] = '\0'; return 0; } 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; strcpy(debugfs_mountpoint, *ptr); 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_found = 1; return debugfs_mountpoint; } batctl-2013.4.0/debugfs.h000066400000000000000000000024651222661175500150200ustar00rootroot00000000000000/* * 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__ #include #include #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-2013.4.0/functions.c000066400000000000000000000266521222661175500154100ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 * */ #define _GNU_SOURCE #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_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(char *dir, char *fname, 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"); } } int read_file(char *dir, 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, strlen(dir)); full_path[strlen(dir)] = '\0'; strncat(full_path, fname, sizeof(full_path) - strlen(full_path)); 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; 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; } res = EXIT_SUCCESS; out: if (fp) fclose(fp); if (read_opt & USE_BAT_HOSTS) bat_hosts_free(); return res; } int write_file(char *dir, char *fname, char *arg1, char *arg2) { int fd = 0, res = EXIT_FAILURE; char full_path[500]; ssize_t write_len; strncpy(full_path, dir, strlen(dir)); full_path[strlen(dir)] = '\0'; strncat(full_path, fname, sizeof(full_path) - strlen(full_path)); 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) 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 uint32_t resolve_ipv4(const char *asc) { int ret; struct addrinfo hints; struct addrinfo *res; struct sockaddr_in *inet4; uint32_t addr = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; ret = getaddrinfo(asc, NULL, &hints, &res); if (ret) return 0; if (res) { inet4 = (struct sockaddr_in *)res->ai_addr; addr = inet4->sin_addr.s_addr; } freeaddrinfo(res); return addr; } static void request_arp(uint32_t ipv4_addr) { struct sockaddr_in inet4; int sock; char t = 0; memset(&inet4, 0, sizeof(inet4)); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return; inet4.sin_family = AF_INET; inet4.sin_port = htons(9); inet4.sin_addr.s_addr = ipv4_addr; sendto(sock, &t, sizeof(t), 0, (const struct sockaddr *)&inet4, sizeof(inet4)); close(sock); } static struct ether_addr *resolve_mac_from_arp(uint32_t ipv4_addr) { struct ether_addr mac_empty; struct ether_addr *mac_result = NULL, *mac_tmp = NULL; struct sockaddr_in inet4; int ret; FILE *f; size_t len = 0; char *line = NULL; int skip_line = 1; size_t column; char *token, *input, *saveptr; int line_invalid; memset(&mac_empty, 0, sizeof(mac_empty)); f = fopen("/proc/net/arp", "r"); if (!f) return NULL; while (getline(&line, &len, f) != -1) { if (skip_line) { skip_line = 0; continue; } line_invalid = 0; column = 0; input = line; while ((token = strtok_r(input, " \t", &saveptr))) { input = NULL; if (column == 0) { ret = inet_pton(AF_INET, token, &inet4.sin_addr); if (ret != 1) { line_invalid = 1; break; } } if (column == 3) { mac_tmp = ether_aton(token); if (!mac_tmp || memcmp(mac_tmp, &mac_empty, sizeof(mac_empty)) == 0) { line_invalid = 1; break; } } column++; } if (column < 4) line_invalid = 1; if (line_invalid) continue; if (ipv4_addr == inet4.sin_addr.s_addr) { mac_result = mac_tmp; break; } } free(line); fclose(f); return mac_result; } static struct ether_addr *resolve_mac_from_ipv4(const char *asc) { uint32_t ipv4_addr; int retries = 5; struct ether_addr *mac_result = NULL; ipv4_addr = resolve_ipv4(asc); if (!ipv4_addr) return NULL; while (retries-- && !mac_result) { mac_result = resolve_mac_from_arp(ipv4_addr); if (!mac_result) { request_arp(ipv4_addr); usleep(200000); } } return mac_result; } struct ether_addr *resolve_mac(const char *asc) { struct ether_addr *mac_result = NULL; mac_result = ether_aton(asc); if (mac_result) goto out; mac_result = resolve_mac_from_ipv4(asc); out: return mac_result; } batctl-2013.4.0/functions.h000066400000000000000000000033661222661175500154120ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 #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(char *dir, char *path, int read_opt, float orig_timeout, float watch_interval, size_t header_lines); int write_file(char *dir, char *fname, char *arg1, char *arg2); struct ether_addr *translate_mac(char *mesh_iface, struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); 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, }; batctl-2013.4.0/hash.c000066400000000000000000000176721222661175500143250ustar00rootroot00000000000000/* * Copyright (C) 2006-2013 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 /* NULL */ #include "hash.h" #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); } /* free only the hashtable and the hash itself. */ void hash_destroy(struct hashtable_t *hash) { debugFree(hash->table, 1302); debugFree(hash, 1303); } /* 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 */ debugFree(iter, 1304); 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 index; struct element_t *bucket, *prev_bucket = NULL; index = hash->choose(data, hash->size); bucket = hash->table[index]; while (bucket != NULL) { if (hash->compare(bucket->data, data)) return -1; prev_bucket = bucket; bucket= bucket->next; } /* found the tail of the list, add new element */ bucket = debugMalloc(sizeof(struct element_t),304); if (!bucket) return -1; /* 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; } /* 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; data_save = hash_it_t->bucket->data; /* save the pointer to the 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)) { hash_it_t.first_bucket = (hash_it_t.bucket == hash->table[hash_it_t.index] ? &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++) { bucket = hash->table[i]; while (bucket != NULL) { hash_add(new_hash, bucket->data); bucket = bucket->next; } } hash_delete(hash, NULL); /* remove hash and eventual overflow buckets but not the content itself. */ 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-2013.4.0/hash.h000066400000000000000000000073721222661175500143260ustar00rootroot00000000000000/* * Copyright (C) 2006-2013 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); #endif batctl-2013.4.0/ioctl.c000066400000000000000000000060151222661175500145010ustar00rootroot00000000000000/* * Copyright (C) 2012-2013 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 "main.h" #include "ioctl.h" #include "debugfs.h" typedef unsigned long long u64; /* 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 = (caddr_t)&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(u64); 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 = (caddr_t)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 = (caddr_t) 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)); strcpy(ifr.ifr_name, mesh_iface); 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-2013.4.0/ioctl.h000066400000000000000000000014571222661175500145130ustar00rootroot00000000000000/* * Copyright (C) 2012-2013 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 * */ int ioctl_statistics_get(char *mesh_iface); batctl-2013.4.0/list-batman.c000066400000000000000000000055271222661175500156110ustar00rootroot00000000000000/* * Copyright (C) 2006-2013 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-2013.4.0/list-batman.h000066400000000000000000000101151222661175500156030ustar00rootroot00000000000000/* * Copyright (C) 2006-2013 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 /* offsetof() */ #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-2013.4.0/main.c000066400000000000000000000163461222661175500143230ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "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 "vis.h" #include "ioctl.h" #include "functions.h" #include char mesh_dfl_iface[] = "bat0"; char module_ver_path[] = "/sys/module/batman_adv/version"; 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 (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, " \tvis_data|vd [dot|JSON] \tdisplay the VIS data in dot or JSON format\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], "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], "tcpdump") == 0) || (strcmp(argv[1], "td") == 0)) { ret = tcpdump(argc - 1, argv + 1); } else if ((strcmp(argv[1], "interface") == 0) || (strcmp(argv[1], "if") == 0)) { ret = interface(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); } else if ((strcmp(argv[1], "vis_data") == 0) || (strcmp(argv[1], "vd") == 0)) { ret = vis_data(mesh_iface, argc - 1, argv + 1); } 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); #ifdef BATCTL_BISECT } else if ((strcmp(argv[1], "bisect_iv") == 0)) { ret = bisect_iv(argc - 1, argv + 1); #endif } 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-2013.4.0/main.h000066400000000000000000000023351222661175500143210ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 SOURCE_VERSION #define SOURCE_VERSION "2013.4.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 #define __packed __attribute((packed)) /* linux kernel compat */ #define BIT(nr) (1UL << (nr)) /* linux kernel compat */ extern char module_ver_path[]; batctl-2013.4.0/man/000077500000000000000000000000001222661175500137745ustar00rootroot00000000000000batctl-2013.4.0/man/batctl.8000066400000000000000000000347651222661175500153550ustar00rootroot00000000000000.\" 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" "Jan 06, 2013" "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 (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. .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 "\fBvis_mode|vm\fP [\fBclient|server\fP]\fP" If no parameter is given the current vis mode is displayed otherwise the parameter is used to set the vis mode. .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 "\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 guess your appropriate gateway class. 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: 2000 \-> gateway class 20 .RE .RS 16 examples: 5000 \-> gateway class 49 .RE .RS 25 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 "\fBvis_data|vd dot\fP [\fB\-n\fP|\fB\-\-numbers\fP][\fB\-T\fP|\fB\-\-no-TT\fP][\fB\-2\fP|\fB\-\-no-2nd\fP]" Display the visualisation data in graphviz \fBdot\fP(1) format. If "\-\-numbers" or "\-n" is given batctl will not replace the MAC addresses with bat-host names in the output. With "\-\-no-TT" or "\-T" the TT entries are not displayed, so the pure mesh topology can be seen. With "\-\-no-2nd" or "\-2" a dot cluster is not formed around primary and secondary addresses from the same device. .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 preferrably 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). List of debug tables: .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|\fBIPv4_address\fP" Translates a destination (hostname, IPv4, 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|\fBIPv4_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 address or a hostname when the IPv4 address was configured on top of the batman-adv interface of the destination device and both source and destination devices are in the same IPv4 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|\fBIPv4_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 address or a hostname when the IPv4 address was configured on top of the batman-adv interface of the destination device and both source and destination devices are in the same IPv4 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 vis 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-2013.4.0/packet.h000066400000000000000000000216121222661175500146430ustar00rootroot00000000000000/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ enum batadv_packettype { BATADV_IV_OGM = 0x01, BATADV_ICMP = 0x02, BATADV_UNICAST = 0x03, BATADV_BCAST = 0x04, BATADV_VIS = 0x05, BATADV_UNICAST_FRAG = 0x06, BATADV_TT_QUERY = 0x07, BATADV_ROAM_ADV = 0x08, BATADV_UNICAST_4ADDR = 0x09, BATADV_CODED = 0x0a, }; /** * 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 14 enum batadv_iv_flags { BATADV_NOT_BEST_NEXT_HOP = BIT(3), BATADV_PRIMARIES_FIRST_HOP = BIT(4), BATADV_VIS_SERVER = BIT(5), BATADV_DIRECTLINK = BIT(6), }; /* 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, }; /* vis defines */ enum batadv_vis_packettype { BATADV_VIS_TYPE_SERVER_SYNC = 0, BATADV_VIS_TYPE_CLIENT_UPDATE = 1, }; /* fragmentation defines */ enum batadv_unicast_frag_flags { BATADV_UNI_FRAG_HEAD = BIT(0), BATADV_UNI_FRAG_LARGETAIL = BIT(1), }; /* TT_QUERY subtypes */ #define BATADV_TT_QUERY_TYPE_MASK 0x3 enum batadv_tt_query_packettype { BATADV_TT_REQUEST = 0, BATADV_TT_RESPONSE = 1, }; /* TT_QUERY flags */ enum batadv_tt_query_flags { BATADV_TT_FULL_TABLE = BIT(2), }; /* BATADV_TT_CLIENT flags. * Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to * BIT(15) are used for local computation only */ enum batadv_tt_client_flags { BATADV_TT_CLIENT_DEL = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), BATADV_TT_CLIENT_WIFI = BIT(2), BATADV_TT_CLIENT_TEMP = BIT(3), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), }; /* 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, }; /* the destination hardware field in the ARP frame is used to * transport the claim type and the group id */ struct batadv_bla_claim_dst { uint8_t magic[3]; /* FF:43:05 */ uint8_t type; /* bla_claimframe */ __be16 group; /* group id */ }; struct batadv_header { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; /* the parent struct has to add a byte after the header to make * everything 4 bytes aligned again */ }; struct batadv_ogm_packet { struct batadv_header header; uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ __be32 seqno; uint8_t orig[ETH_ALEN]; uint8_t prev_sender[ETH_ALEN]; uint8_t gw_flags; /* flags related to gateway class */ uint8_t tq; uint8_t tt_num_changes; uint8_t ttvn; /* translation table version number */ __be16 tt_crc; } __packed; #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet) struct batadv_icmp_packet { struct batadv_header header; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; __be16 seqno; uint8_t uid; uint8_t reserved; }; #define BATADV_RR_LEN 16 /* icmp_packet_rr must start with all fields from imcp_packet * as this is assumed by code that handles ICMP packets */ struct batadv_icmp_packet_rr { struct batadv_header header; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; __be16 seqno; uint8_t uid; uint8_t rr_cur; uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; }; /* 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 { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ uint8_t 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 */ struct batadv_unicast_4addr_packet { struct batadv_unicast_packet u; uint8_t src[ETH_ALEN]; uint8_t subtype; uint8_t reserved; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; struct batadv_unicast_frag_packet { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ uint8_t dest[ETH_ALEN]; uint8_t flags; uint8_t align; uint8_t orig[ETH_ALEN]; __be16 seqno; } __packed; struct batadv_bcast_packet { struct batadv_header header; uint8_t reserved; __be32 seqno; uint8_t orig[ETH_ALEN]; /* "4 bytes boundary + 2 bytes" long to make the payload after the * following ethernet header again 4 bytes boundary aligned */ }; #pragma pack() struct batadv_vis_packet { struct batadv_header header; uint8_t vis_type; /* which type of vis-participant sent this? */ __be32 seqno; /* sequence number */ uint8_t entries; /* number of entries behind this struct */ uint8_t reserved; uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */ uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */ uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */ }; struct batadv_tt_query_packet { struct batadv_header header; /* the flag field is a combination of: * - TT_REQUEST or TT_RESPONSE * - TT_FULL_TABLE */ uint8_t flags; uint8_t dst[ETH_ALEN]; uint8_t src[ETH_ALEN]; /* the ttvn field is: * if TT_REQUEST: ttvn that triggered the * request * if TT_RESPONSE: new ttvn for the src * orig_node */ uint8_t ttvn; /* tt_data field is: * if TT_REQUEST: crc associated with the * ttvn * if TT_RESPONSE: table_size */ __be16 tt_data; } __packed; struct batadv_roam_adv_packet { struct batadv_header header; uint8_t reserved; uint8_t dst[ETH_ALEN]; uint8_t src[ETH_ALEN]; uint8_t client[ETH_ALEN]; } __packed; struct batadv_tt_change { uint8_t flags; uint8_t addr[ETH_ALEN]; } __packed; /** * struct batadv_coded_packet - network coded packet * @header: common batman packet header and ttl of first included packet * @reserved: Align following fields to 2-byte boundaries * @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 { struct batadv_header header; uint8_t first_ttvn; /* uint8_t first_dest[ETH_ALEN]; - saved in mac header destination */ uint8_t first_source[ETH_ALEN]; uint8_t first_orig_dest[ETH_ALEN]; __be32 first_crc; uint8_t second_ttl; uint8_t second_ttvn; uint8_t second_dest[ETH_ALEN]; uint8_t second_source[ETH_ALEN]; uint8_t second_orig_dest[ETH_ALEN]; __be32 second_crc; __be16 coded_len; }; #endif /* _NET_BATMAN_ADV_PACKET_H_ */ batctl-2013.4.0/ping.c000066400000000000000000000221241222661175500143230ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "main.h" #include "ping.h" #include "functions.h" #include "packet.h" #include "bat-hosts.h" #include "debugfs.h" char is_aborted = 0; 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"); } 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 = 0, 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); memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN); icmp_packet_out.header.packet_type = BATADV_ICMP; icmp_packet_out.header.version = BATADV_COMPAT_VERSION; icmp_packet_out.msg_type = BATADV_ECHO_REQUEST; icmp_packet_out.header.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.header.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.header.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) close(ping_fd); return ret; } batctl-2013.4.0/ping.h000066400000000000000000000014671222661175500143370ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 * */ int ping(char *mesh_iface, int argc, char **argv); batctl-2013.4.0/sys.c000066400000000000000000000326271222661175500142150ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 "main.h" #include "sys.h" #include "functions.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 = "vis_mode", .opt_short = "vm", .sysfs_name = "vis_mode", .params = sysfs_param_server, }, { .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, } }; 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); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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]); path_buff[PATH_BUFF_LEN - 1] = '\0'; if (!file_exists(path_buff)) { snprintf(path_buff, PATH_BUFF_LEN, SYS_IFACE_DIR, argv[i]); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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); str[sizeof(str) - 1] = '\0'; 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 optchar, res = EXIT_FAILURE; char *path_buff; 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; } } path_buff = malloc(PATH_BUFF_LEN); snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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); 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); path_buff[PATH_BUFF_LEN - 1] = '\0'; 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; } batctl-2013.4.0/sys.h000066400000000000000000000041751222661175500142170ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 * */ #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" enum batctl_settings_list { BATCTL_SETTINGS_ORIG_INTERVAL, BATCTL_SETTINGS_AP_ISOLATION, BATCTL_SETTINGS_BLA, BATCTL_SETTINGS_DAT, BATCTL_SETTINGS_VIS_MODE, BATCTL_SETTINGS_AGGREGATION, BATCTL_SETTINGS_BONDING, BATCTL_SETTINGS_FRAGMENTATION, BATCTL_SETTINGS_NETWORK_CODING, 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); batctl-2013.4.0/tcpdump.c000066400000000000000000000750321222661175500150500ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "main.h" #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 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_BATVIS | DUMP_TYPE_BATFRAG | DUMP_TYPE_BATTT | 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 vis packets\n", DUMP_TYPE_BATVIS); fprintf(stderr, " \t\t%3d - batman fragmented packets\n", DUMP_TYPE_BATFRAG); fprintf(stderr, " \t\t%3d - batman tt / roaming packets\n", DUMP_TYPE_BATTT); 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 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 *)hw_src, 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_ip(unsigned char *packet_buff, ssize_t buff_len, int time_printed) { struct iphdr *iphdr, *tmp_iphdr; struct tcphdr *tcphdr; struct udphdr *udphdr, *tmp_udphdr; struct icmphdr *icmphdr; uint16_t tcp_header_len; iphdr = (struct iphdr *)packet_buff; LEN_CHECK((size_t)buff_len, (size_t)(iphdr->ihl * 4), "IP"); if (!time_printed) print_time(); 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("IP %s > ", inet_ntoa(*(struct in_addr *)&iphdr->saddr)); switch (icmphdr->type) { case ICMP_ECHOREPLY: printf("%s: ICMP echo reply, id %hu, seq %hu, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), 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 ", inet_ntoa(*(struct in_addr *)&iphdr->daddr)); printf("%s udp port %hu unreachable, length %zu\n", inet_ntoa(*(struct in_addr *)&tmp_iphdr->daddr), ntohs(tmp_udphdr->dest), (size_t)buff_len - (iphdr->ihl * 4)); break; default: printf("%s: ICMP unreachable %hhu, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), 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", inet_ntoa(*(struct in_addr *)&iphdr->daddr), 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", inet_ntoa(*(struct in_addr *)&iphdr->daddr), (size_t)buff_len - (iphdr->ihl * 4)); break; default: printf("%s: ICMP type %hhu, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), icmphdr->type, (size_t)buff_len - (iphdr->ihl * 4)); break; } break; case IPPROTO_TCP: tcphdr = (struct tcphdr *)(packet_buff + (iphdr->ihl * 4)); tcp_header_len = tcphdr->doff * 4; LEN_CHECK((size_t)buff_len - (iphdr->ihl * 4), (size_t)tcp_header_len, "TCP"); printf("IP %s.%i > ", inet_ntoa(*(struct in_addr *)&iphdr->saddr), ntohs(tcphdr->source)); printf("%s.%i: TCP, flags [%c%c%c%c%c%c], length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), 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 - (iphdr->ihl * 4) - tcp_header_len); break; case IPPROTO_UDP: LEN_CHECK((size_t)buff_len - (iphdr->ihl * 4), sizeof(struct udphdr), "UDP"); udphdr = (struct udphdr *)(packet_buff + (iphdr->ihl * 4)); printf("IP %s.%i > ", inet_ntoa(*(struct in_addr *)&iphdr->saddr), ntohs(udphdr->source)); switch (ntohs(udphdr->dest)) { case 67: LEN_CHECK((size_t)buff_len - (iphdr->ihl * 4) - sizeof(struct udphdr), (size_t) 44, "DHCP"); printf("%s.67: BOOTP/DHCP, Request from %s, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), ether_ntoa_long((struct ether_addr *)(((char *)udphdr) + sizeof(struct udphdr) + 28)), (size_t)buff_len - (iphdr->ihl * 4) - sizeof(struct udphdr)); break; case 68: printf("%s.68: BOOTP/DHCP, Reply, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), (size_t)buff_len - (iphdr->ihl * 4) - sizeof(struct udphdr)); break; default: printf("%s.%i: UDP, length %zu\n", inet_ntoa(*(struct in_addr *)&iphdr->daddr), ntohs(udphdr->dest), (size_t)buff_len - (iphdr->ihl * 4) - sizeof(struct udphdr)); break; } break; case IPPROTO_IPV6: printf("IP6: not implemented yet\n"); 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_tt(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_tt_query_packet *tt_query_packet; char *tt_desc, *tt_data, tt_type; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_tt_query_packet), "BAT TT"); tt_query_packet = (struct batadv_tt_query_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) print_time(); switch (tt_query_packet->flags & BATADV_TT_QUERY_TYPE_MASK) { case BATADV_TT_REQUEST: tt_desc = "request"; tt_data = "crc"; tt_type = 'Q'; break; case BATADV_TT_RESPONSE: tt_desc = "response"; tt_data = "entries"; tt_type = 'P'; break; default: tt_desc = "unknown"; tt_data = "unknown"; tt_type = '?'; break; } printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)tt_query_packet->src, read_opt)); printf("%s: TT %s, ttvn %d, %s %u, ttl %2d, v %d, flags [%c%c], length %zu\n", get_name_by_macaddr((struct ether_addr *)tt_query_packet->dst, read_opt), tt_desc, tt_query_packet->ttvn, tt_data, ntohs(tt_query_packet->tt_data), tt_query_packet->header.ttl, tt_query_packet->header.version, tt_type, (tt_query_packet->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'), (size_t)buff_len - sizeof(struct ether_header)); } static void dump_batman_roam(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_roam_adv_packet *roam_adv_packet; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_roam_adv_packet), "BAT ROAM"); roam_adv_packet = (struct batadv_roam_adv_packet *)(packet_buff + sizeof(struct ether_header)); if (!time_printed) print_time(); printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)roam_adv_packet->src, read_opt)); printf("%s: ROAM, ", get_name_by_macaddr((struct ether_addr *)roam_adv_packet->dst, read_opt)); printf("client %s, ttl %2d, v %d, length %zu\n", get_name_by_macaddr((struct ether_addr *)roam_adv_packet->client, read_opt), roam_adv_packet->header.ttl, roam_adv_packet->header.version, (size_t)buff_len - sizeof(struct ether_header)); } 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; LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), 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)); printf("OGM IV via neigh %s, seq %u, tq %3d, ttvn %d, ttcrc %hu, ttl %2d, v %d, flags [%c%c%c%c%c], length %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->ttvn, ntohs(batman_ogm_packet->tt_crc), batman_ogm_packet->header.ttl, batman_ogm_packet->header.version, (batman_ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP ? 'N' : '.'), (batman_ogm_packet->flags & BATADV_DIRECTLINK ? 'D' : '.'), (batman_ogm_packet->flags & BATADV_VIS_SERVER ? 'V' : '.'), (batman_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP ? 'F' : '.'), (batman_ogm_packet->gw_flags ? 'G' : '.'), (size_t)buff_len - sizeof(struct ether_header)); } 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->header.ttl, icmp_packet->header.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->header.ttl, icmp_packet->header.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->header.ttl, icmp_packet->header.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->header.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_frag(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) { struct batadv_unicast_frag_packet *unicast_frag_packet; LEN_CHECK((size_t)buff_len - ETH_HLEN, sizeof(struct batadv_unicast_frag_packet), "BAT FRAG"); LEN_CHECK((size_t)buff_len - ETH_HLEN - sizeof(struct batadv_unicast_frag_packet), (size_t)ETH_HLEN, "BAT FRAG (unpacked)"); unicast_frag_packet = (struct batadv_unicast_frag_packet *)(packet_buff + ETH_HLEN); if (!time_printed) time_printed = print_time(); printf("BAT %s > ", get_name_by_macaddr((struct ether_addr *)unicast_frag_packet->orig, read_opt)); printf("%s: FRAG, seq %hu, ttvn %d, ttl %hhu, flags [%c%c], ", get_name_by_macaddr((struct ether_addr *)unicast_frag_packet->dest, read_opt), ntohs(unicast_frag_packet->seqno), unicast_frag_packet->ttvn, unicast_frag_packet->header.ttl, (unicast_frag_packet->flags & BATADV_UNI_FRAG_HEAD ? 'H' : '.'), (unicast_frag_packet->flags & BATADV_UNI_FRAG_LARGETAIL ? 'L' : '.')); if (unicast_frag_packet->flags & BATADV_UNI_FRAG_HEAD) parse_eth_hdr(packet_buff + ETH_HLEN + sizeof(struct batadv_unicast_frag_packet), buff_len - ETH_HLEN - sizeof(struct batadv_unicast_frag_packet), read_opt, time_printed); else printf("length %zu\n", (size_t)buff_len - ETH_HLEN - sizeof(struct batadv_unicast_frag_packet)); } 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.header.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_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->header.version != BATADV_COMPAT_VERSION)) return; switch (batman_ogm_packet->header.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_VIS: if (dump_level & DUMP_TYPE_BATVIS) fprintf(stderr, "Warning - batman vis packet received: function not implemented yet\n"); break; case BATADV_UNICAST_FRAG: if (dump_level & DUMP_TYPE_BATFRAG) dump_batman_frag(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_TT_QUERY: if (dump_level & DUMP_TYPE_BATTT) dump_batman_tt(packet_buff, buff_len, read_opt, time_printed); break; case BATADV_ROAM_ADV: if (dump_level & DUMP_TYPE_BATTT) dump_batman_roam(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; default: fprintf(stderr, "Warning - packet contains unknown batman packet type: 0x%02x\n", batman_ogm_packet->header.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)); 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); 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); 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) 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-2013.4.0/tcpdump.h000066400000000000000000000051541222661175500150530ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "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_BATVIS 16 #define DUMP_TYPE_BATFRAG 32 #define DUMP_TYPE_BATTT 64 #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); batctl-2013.4.0/traceroute.c000066400000000000000000000147311222661175500155500ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "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 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 = 0, 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; } memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN); icmp_packet_out.header.version = BATADV_COMPAT_VERSION; icmp_packet_out.header.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.header.ttl = 1; !dst_reached && icmp_packet_out.header.ttl < TTL_MAX; icmp_packet_out.header.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.header.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.header.ttl, (return_mac ? return_mac : "*")); else printf("%2hhu: %s (%s)", icmp_packet_out.header.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) close(trace_fd); return ret; } batctl-2013.4.0/traceroute.h000066400000000000000000000014751222661175500155560ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 * */ int traceroute(char *mesh_iface, int argc, char **argv); batctl-2013.4.0/translate.c000066400000000000000000000037131222661175500153660ustar00rootroot00000000000000/* * Copyright (C) 2007-2013 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 "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-2013.4.0/translate.h000066400000000000000000000014741222661175500153750ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 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 * */ int translate(char *mesh_iface, int argc, char **argv); batctl-2013.4.0/vis.c000066400000000000000000000167451222661175500142030ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Andrew Lunn * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * */ #define _GNU_SOURCE #include #include #include #include #include #include #include "main.h" #include "vis.h" #include "functions.h" #include "bat-hosts.h" #include "debug.h" #include "debugfs.h" #define TQ_MAX_VALUE 255 typedef void (*print_tq_t) (char *orig, char *from, const long tq); typedef void (*print_TT_t) (char *orig, char *from); typedef void (*print_1st_t) (char *orig); typedef void (*print_2nd_t) (char *orig, char *from); typedef void (*print_header_t) (void); typedef void (*print_footer_t) (void); struct funcs { print_tq_t print_tq; print_TT_t print_TT; print_1st_t print_1st; print_2nd_t print_2nd; print_header_t print_header; print_footer_t print_footer; }; static bool with_TT = true; static bool with_2nd = true; static bool with_names = true; static void usage(void) { fprintf(stderr, "batctl vis_data dot {-h}{--no-TT|-T} {--no-2nd|-2} {--numbers|-n}\n"); fprintf(stderr, "or\n"); fprintf(stderr, "batctl vis_data json {-h}{--no-TT|-T} {--no-2nd|-2} {--numbers|-n}\n"); } static void dot_print_tq(char *orig, char *from, const long tq) { int int_part = TQ_MAX_VALUE / tq; int frac_part = (1000 * TQ_MAX_VALUE / tq) - (int_part * 1000); printf("\t\"%s\" -> ", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\"%s\" [label=\"%d.%03d\"]\n", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)), int_part, frac_part); } static void dot_print_TT(char *orig, char *from) { printf("\t\"%s\" -> ", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\"%s\" [label=\"TT\"]\n", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0))); } static void dot_print_1st(char *orig) { printf("\tsubgraph \"cluster_%s\" {\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\t\t\"%s\" [peripheries=2]\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\t}\n"); } static void dot_print_2nd(char *orig, char *from) { printf("\tsubgraph \"cluster_%s\" {\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\t\t\"%s\" [peripheries=2]\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\t\t\"%s\"\n", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0))); printf("\t}\n"); } static void dot_print_header(void) { printf("digraph {\n"); } static void dot_print_footer(void) { printf("}\n"); } const struct funcs dot_funcs = { dot_print_tq, dot_print_TT, dot_print_1st, dot_print_2nd, dot_print_header, dot_print_footer }; static void json_print_tq(char *orig, char *from, const long tq) { int int_part = TQ_MAX_VALUE / tq; int frac_part = (1000 * TQ_MAX_VALUE / tq) - (int_part * 1000); printf("{ \"router\" : \"%s\", ", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\"neighbor\" : \"%s\", \"label\" : \"%d.%03d\" }\n", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)), int_part, frac_part); } static void json_print_TT(char *orig, char *from) { printf("{ \"router\" : \"%s\", ", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); printf("\"gateway\" : \"%s\", \"label\" : \"TT\" }\n", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0))); } static void json_print_1st(char *orig) { printf("{ \"primary\" : \"%s\" }\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); } static void json_print_2nd(char *orig, char *from) { printf("{ \"secondary\" : \"%s\", ", get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0))); printf("\"of\" : \"%s\" }\n", get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0))); } const struct funcs json_funcs = { json_print_tq, json_print_TT, json_print_1st, json_print_2nd, NULL, NULL }; static FILE *open_vis(char *mesh_iface) { 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 NULL; } debugfs_make_path(DEBUG_BATIF_PATH_FMT "/" DEBUG_VIS_DATA, mesh_iface, full_path, sizeof(full_path)); return fopen(full_path, "r"); } static int format(char *mesh_iface, const struct funcs *funcs) { size_t len = 0; char *line = NULL; char *orig, *from; char *duplet; char *line_save_ptr, *component_save_ptr; char *duplet_save_ptr; char *endptr; char *value; long tq; char *flag; FILE *fp = open_vis(mesh_iface); if (!fp) return EXIT_FAILURE; if (funcs->print_header) funcs->print_header(); while (getline(&line, &len, fp) != -1) { /* First MAC address is the originator */ orig = strtok_r(line, ",", &line_save_ptr); duplet_save_ptr = line_save_ptr; while ((duplet = strtok_r(NULL, ",", &duplet_save_ptr)) != NULL) { flag = strtok_r(duplet, " ", &component_save_ptr); if (!flag) continue; if (!strcmp(flag, "TQ")) { from = strtok_r(NULL, " ", &component_save_ptr); value = strtok_r(NULL, " ", &component_save_ptr); tq = strtoul(value, &endptr, 0); funcs->print_tq(orig, from, tq); continue; } if (!strcmp(flag, "TT")) { /* We have a TT record */ if (!with_TT) continue; from = strtok_r(NULL, " ", &component_save_ptr); funcs->print_TT(orig, from); continue; } if (!strcmp(flag, "SEC") && with_2nd) { /* We found a secondary interface MAC address. */ from = strtok_r(NULL, " ", &component_save_ptr); funcs->print_2nd(orig, from); } if (!strcmp(flag, "PRIMARY") && with_2nd) { /* We found a primary interface MAC address. */ funcs->print_1st(orig); } } } if (funcs->print_footer) funcs->print_footer(); if (line) free(line); return EXIT_SUCCESS; } int vis_data(char *mesh_iface, int argc, char *argv[]) { bool dot = false; bool json = false; int c; if (argc <= 1) { usage(); return EXIT_FAILURE; } /* Do we know the requested format? */ if (strcmp(argv[1], "dot") == 0) dot = true; if (strcmp(argv[1], "json") == 0) json = true; if (!dot && !json) { usage(); return EXIT_FAILURE; } /* Move over the output format */ argc--; argv++; while (1) { int option_index = 0; static struct option long_options[] = { {"no-TT", 0, 0, 'T'}, {"no-2nd", 0, 0, '2'}, {"numbers", 0, 0, 'n'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hT2n", long_options, &option_index); if (c == -1) break; switch (c) { case 'T': with_TT = false; break; case '2': with_2nd = false; break; case 'n': with_names = false; break; case 'h': default: usage(); return -1; } } if (with_names) bat_hosts_init(USE_BAT_HOSTS); if (dot) return format(mesh_iface, &dot_funcs); if (json) return format(mesh_iface, &json_funcs); return EXIT_FAILURE; } batctl-2013.4.0/vis.h000066400000000000000000000014621222661175500141760ustar00rootroot00000000000000/* * Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Andrew Lunn * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * */ int vis_data(char *mesh_iface, int argc, char * argv[]);