ping.c 8.25 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
3
 *
4
 * Andreas Langer <an.langer@gmx.de>, Marek Lindner <mareklindner@neomailbox.ch>
5
 *
6
 * License-Filename: LICENSES/preferred/GPL-2.0
Andreas Langer's avatar
Andreas Langer committed
7 8
 */

9 10


Andreas Langer's avatar
Andreas Langer committed
11 12 13 14 15
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
16
#include <signal.h>
17
#include <fcntl.h>
18
#include <string.h>
19
#include <math.h>
20 21 22 23
#include <stddef.h>
#include <stdint.h>
#include <sys/select.h>
#include <sys/time.h>
24
#include <netinet/if_ether.h>
25

26
#include "batadv_packet.h"
27
#include "main.h"
28
#include "functions.h"
29
#include "bat-hosts.h"
30
#include "icmp_helper.h"
31

32

33
static volatile sig_atomic_t is_aborted = 0;
34

35

36
static void ping_usage(void)
37
{
38 39 40 41 42 43 44 45
	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");
Andreas Langer's avatar
Andreas Langer committed
46 47
}

48
static void sig_handler(int sig)
49 50 51 52 53 54 55 56
{
	switch (sig) {
	case SIGINT:
	case SIGTERM:
		is_aborted = 1;
		break;
	default:
		break;
57 58 59
	}
}

60
static int ping(struct state *state, int argc, char **argv)
61
{
62
	struct batadv_icmp_packet_rr icmp_packet_out, icmp_packet_in;
63
	struct timeval tv;
64 65
	struct ether_addr *dst_mac = NULL, *rr_mac = NULL;
	struct bat_host *bat_host, *rr_host;
66
	ssize_t read_len;
67
	int ret = EXIT_FAILURE, res, optchar, found_args = 1;
68
	int loop_count = -1, loop_interval = 0, timeout = 1, rr = 0, i;
69
	unsigned int seq_counter = 0, packets_out = 0, packets_in = 0, packets_loss;
70
	char *dst_string, *mac_string, *rr_string;
71
	double time_delta;
72
	float min = 0.0, max = 0.0, avg = 0.0, mdev = 0.0;
73
	uint8_t last_rr_cur = 0, last_rr[BATADV_RR_LEN][ETH_ALEN];
74
	size_t packet_len;
75
	int disable_translate_mac = 0;
76

77
	while ((optchar = getopt(argc, argv, "hc:i:t:RT")) != -1) {
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		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;
100 101 102 103
		case 'R':
			rr = 1;
			found_args++;
			break;
104 105 106 107
		case 'T':
			disable_translate_mac = 1;
			found_args += 1;
			break;
108 109 110
		default:
			ping_usage();
			return EXIT_FAILURE;
111 112 113
		}
	}

114
	if (argc <= found_args) {
115
		fprintf(stderr, "Error - target mac address or bat-host name not specified\n");
116 117
		ping_usage();
		return EXIT_FAILURE;
118 119
	}

120 121
	check_root_or_die("batctl ping");

122
	dst_string = argv[found_args];
123
	bat_hosts_init(0);
124
	bat_host = bat_hosts_find_by_name(dst_string);
Andreas Langer's avatar
Andreas Langer committed
125

126 127
	if (bat_host)
		dst_mac = &bat_host->mac_addr;
128

129
	if (!dst_mac) {
130
		dst_mac = resolve_mac(dst_string);
Andreas Langer's avatar
Andreas Langer committed
131

132
		if (!dst_mac) {
133
			fprintf(stderr, "Error - mac address of the ping destination could not be resolved and is not a bat-host name: %s\n", dst_string);
134
			goto out;
Andreas Langer's avatar
Andreas Langer committed
135
		}
136
	}
Andreas Langer's avatar
Andreas Langer committed
137

138
	if (!disable_translate_mac)
139
		dst_mac = translate_mac(state->mesh_iface, dst_mac);
140

141
	mac_string = ether_ntoa_long(dst_mac);
142 143
	signal(SIGINT, sig_handler);
	signal(SIGTERM, sig_handler);
144

145
	icmp_interfaces_init();
146
	packet_len = sizeof(struct batadv_icmp_packet);
147

148
	memset(&icmp_packet_out, 0, sizeof(icmp_packet_out));
149 150 151 152 153
	memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN);
	icmp_packet_out.packet_type = BATADV_ICMP;
	icmp_packet_out.version = BATADV_COMPAT_VERSION;
	icmp_packet_out.msg_type = BATADV_ECHO_REQUEST;
	icmp_packet_out.ttl = 50;
154
	icmp_packet_out.seqno = 0;
Andreas Langer's avatar
Andreas Langer committed
155

156
	if (rr) {
157
		packet_len = sizeof(struct batadv_icmp_packet_rr);
158
		icmp_packet_out.rr_cur = 1;
159 160
		memset(&icmp_packet_out.rr, 0, BATADV_RR_LEN * ETH_ALEN);
		memset(last_rr, 0, BATADV_RR_LEN * ETH_ALEN);
161 162
	} else {
		((struct batadv_icmp_packet *)&icmp_packet_out)->reserved = 0;
163 164
	}

165
	printf("PING %s (%s) %zu(%zu) bytes of data\n", dst_string, mac_string,
166
		packet_len, packet_len + 28);
Andreas Langer's avatar
Andreas Langer committed
167

168
	while (!is_aborted) {
169 170 171
		tv.tv_sec = timeout;
		tv.tv_usec = 0;

172 173
		if (loop_count == 0)
			break;
174

175 176
		if (loop_count > 0)
			loop_count--;
Andreas Langer's avatar
Andreas Langer committed
177

178
		icmp_packet_out.seqno = htons(++seq_counter);
179

180
		res = icmp_interface_write(state->mesh_iface,
181 182 183
					   (struct batadv_icmp_header *)&icmp_packet_out,
					   packet_len);
		if (res < 0) {
184
			fprintf(stderr, "Error - can't send icmp packet: %s\n", strerror(-res));
185
			goto sleep;
186 187
		}

188
read_packet:
189
		start_timer();
190

191 192
		read_len = icmp_interface_read((struct batadv_icmp_header *)&icmp_packet_in,
					       packet_len, &tv);
Andreas Langer's avatar
Andreas Langer committed
193

194 195
		if (is_aborted)
			break;
196

197
		packets_out++;
198

199
		if (read_len == 0) {
200
			printf("Reply from host %s timed out\n", dst_string);
201 202
			goto sleep;
		}
203

204
		if (read_len < 0) {
205
			fprintf(stderr, "Error - can't receive icmp packets: %s\n", strerror(-read_len));
206
			goto sleep;
Andreas Langer's avatar
Andreas Langer committed
207 208
		}

209
		if ((size_t)read_len < packet_len) {
210
			printf("Warning - dropping received packet as it is smaller than expected (%zu): %zd\n",
211
				packet_len, read_len);
212
			goto sleep;
213
		}
214

215 216 217 218
		/* after receiving an unexpected seqno we keep waiting for our answer */
		if (htons(seq_counter) != icmp_packet_in.seqno)
			goto read_packet;

219
		switch (icmp_packet_in.msg_type) {
220
		case BATADV_ECHO_REPLY:
221
			time_delta = end_timer();
222
			printf("%zd bytes from %s icmp_seq=%hu ttl=%d time=%.2f ms",
223 224
					read_len, dst_string,
					ntohs(icmp_packet_in.seqno),
225
					icmp_packet_in.ttl,
226
					time_delta);
Andreas Langer's avatar
Andreas Langer committed
227

228
			if (read_len == sizeof(struct batadv_icmp_packet_rr)) {
229
				if (last_rr_cur == icmp_packet_in.rr_cur
230
					&& !memcmp(last_rr, icmp_packet_in.rr, BATADV_RR_LEN * ETH_ALEN)) {
231 232 233 234 235 236

					printf("\t(same route)");

				} else {
					printf("\nRR: ");

237
					for (i = 0; i < BATADV_RR_LEN
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
						&& 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;
253
					memcpy(last_rr, icmp_packet_in.rr, BATADV_RR_LEN * ETH_ALEN);
254 255 256 257 258
				}
			}

			printf("\n");

259
			if ((time_delta < min) || (min == 0.0))
260 261 262 263
				min = time_delta;
			if (time_delta > max)
				max = time_delta;
			avg += time_delta;
264
			mdev += time_delta * time_delta;
265
			packets_in++;
266
			break;
267
		case BATADV_DESTINATION_UNREACHABLE:
268
			printf("From %s: Destination Host Unreachable (icmp_seq %hu)\n", dst_string, ntohs(icmp_packet_in.seqno));
269
			break;
270
		case BATADV_TTL_EXCEEDED:
271
			printf("From %s: Time to live exceeded (icmp_seq %hu)\n", dst_string, ntohs(icmp_packet_in.seqno));
272
			break;
273
		case BATADV_PARAMETER_PROBLEM:
274
			fprintf(stderr, "Error - the batman adv kernel module version (%d) differs from ours (%d)\n",
275
				icmp_packet_in.version, BATADV_COMPAT_VERSION);
276
			printf("Please make sure to use compatible versions!\n");
277 278
			goto out;
		default:
279
			printf("Unknown message type %d len %zd received\n",
280
			       icmp_packet_in.msg_type, read_len);
Andreas Langer's avatar
Andreas Langer committed
281 282
			break;
		}
283

284
sleep:
285 286 287
		if (loop_interval > 0)
			sleep(loop_interval);
		else if ((tv.tv_sec != 0) || (tv.tv_usec != 0))
288
			select(0, NULL, NULL, NULL, &tv);
Andreas Langer's avatar
Andreas Langer committed
289
	}
290

291 292 293 294 295
	if (packets_out == 0)
		packets_loss = 0;
	else
		packets_loss = ((packets_out - packets_in) * 100) / packets_out;

296 297 298 299 300 301 302 303 304 305 306 307 308
	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;
	}

309
	printf("--- %s ping statistics ---\n", dst_string);
310
	printf("%u packets transmitted, %u received, %u%% packet loss\n",
311
		packets_out, packets_in, packets_loss);
312
	printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n",
313
		min, avg, max, mdev);
314

315 316 317 318
	if (packets_in)
		ret = EXIT_SUCCESS;
	else
		ret = EXIT_NOSUCCESS;
319 320

out:
321
	icmp_interfaces_clean();
322 323
	bat_hosts_free();
	return ret;
Andreas Langer's avatar
Andreas Langer committed
324
}
325

326
COMMAND(SUBCOMMAND_MIF, ping, "p", COMMAND_FLAG_MESH_IFACE, NULL,
327
	"<destination>     \tping another batman adv host via layer 2");