View | Details | Raw Unified | Return to bug 13916
Collapse All | Expand All

(-)iputils.p8/ping6.c (-2 / +2 lines)
Lines 486-492 Link Here
486
486
487
	if (1) {
487
	if (1) {
488
		int on = 1;
488
		int on = 1;
489
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
489
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_2292HOPLIMIT,
490
			       &on, sizeof(on)) == -1) {
490
			       &on, sizeof(on)) == -1) {
491
			perror ("can't receive hop limit");
491
			perror ("can't receive hop limit");
492
			exit(2);
492
			exit(2);
Lines 704-710 Link Here
704
704
705
	for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
705
	for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
706
		if (c->cmsg_level != SOL_IPV6 ||
706
		if (c->cmsg_level != SOL_IPV6 ||
707
		    c->cmsg_type != IPV6_HOPLIMIT)
707
		    c->cmsg_type != IPV6_2292HOPLIMIT)
708
			continue;
708
			continue;
709
		if (c->cmsg_len < CMSG_LEN(sizeof(int)))
709
		if (c->cmsg_len < CMSG_LEN(sizeof(int)))
710
			continue;
710
			continue;
(-)iputils.p8/ping6.c~ (+922 lines)
Line 0 Link Here
1
/*
2
 *
3
 *	Modified for AF_INET6 by Pedro Roque
4
 *
5
 *	<roque@di.fc.ul.pt>
6
 *
7
 *	Original copyright notice included bellow
8
 */
9
10
/*
11
 * Copyright (c) 1989 The Regents of the University of California.
12
 * All rights reserved.
13
 *
14
 * This code is derived from software contributed to Berkeley by
15
 * Mike Muuss.
16
 *
17
 * Redistribution and use in source and binary forms, with or without
18
 * modification, are permitted provided that the following conditions
19
 * are met:
20
 * 1. Redistributions of source code must retain the above copyright
21
 *    notice, this list of conditions and the following disclaimer.
22
 * 2. Redistributions in binary form must reproduce the above copyright
23
 *    notice, this list of conditions and the following disclaimer in the
24
 *    documentation and/or other materials provided with the distribution.
25
 * 3. All advertising materials mentioning features or use of this software
26
 *    must display the following acknowledgement:
27
 *	This product includes software developed by the University of
28
 *	California, Berkeley and its contributors.
29
 * 4. Neither the name of the University nor the names of its contributors
30
 *    may be used to endorse or promote products derived from this software
31
 *    without specific prior written permission.
32
 *
33
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43
 * SUCH DAMAGE.
44
 */
45
46
#ifndef lint
47
char copyright[] =
48
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
49
 All rights reserved.\n";
50
#endif /* not lint */
51
52
/*
53
 *			P I N G . C
54
 *
55
 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
56
 * measure round-trip-delays and packet loss across network paths.
57
 *
58
 * Author -
59
 *	Mike Muuss
60
 *	U. S. Army Ballistic Research Laboratory
61
 *	December, 1983
62
 *
63
 * Status -
64
 *	Public Domain.  Distribution Unlimited.
65
 * Bugs -
66
 *	More statistics could always be gathered.
67
 *	This program has to run SUID to ROOT to access the ICMP socket.
68
 */
69
#include "ping_common.h"
70
#include "fixfds.h"
71
#include "droppriv.h"
72
73
#include <linux/in6.h>
74
#include <linux/ipv6.h>
75
#include <linux/icmpv6.h>
76
77
#define BIT_CLEAR(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] &= ~(1U << ((nr) & 31)); } while(0)
78
#define BIT_SET(nr, addr) do { ((__u32 *)(addr))[(nr) >> 5] |= (1U << ((nr) & 31)); } while(0)
79
#define BIT_TEST(nr, addr) do { (__u32 *)(addr))[(nr) >> 5] & (1U << ((nr) & 31)); } while(0)
80
81
#define ICMPV6_FILTER_WILLPASS(type, filterp) \
82
	(BIT_TEST((type), filterp) == 0)
83
84
#define ICMPV6_FILTER_WILLBLOCK(type, filterp) \
85
	BIT_TEST((type), filterp)
86
87
#define ICMPV6_FILTER_SETPASS(type, filterp) \
88
	BIT_CLEAR((type), filterp)
89
90
#define ICMPV6_FILTER_SETBLOCK(type, filterp) \
91
	BIT_SET((type), filterp)
92
93
#define ICMPV6_FILTER_SETPASSALL(filterp) \
94
	memset(filterp, 0, sizeof(struct icmp6_filter));
95
96
#define ICMPV6_FILTER_SETBLOCKALL(filterp) \
97
	memset(filterp, 0xFF, sizeof(struct icmp6_filter));
98
99
100
#define	MAXPACKET	128000		/* max packet size */
101
102
#ifdef SO_TIMESTAMP
103
#define HAVE_SIN6_SCOPEID 1
104
#endif
105
106
107
__u32 flowlabel;
108
__u32 tclass;
109
struct cmsghdr *srcrt;
110
111
struct sockaddr_in6 whereto;	/* who to ping */
112
u_char outpack[MAXPACKET];
113
int maxpacket = sizeof(outpack);
114
115
static unsigned char cmsgbuf[4096];
116
static int cmsglen = 0;
117
118
static char * pr_addr(struct in6_addr *addr);
119
static char * pr_addr_n(struct in6_addr *addr);
120
static int pr_icmph(__u8 type, __u8 code, __u32 info);
121
static void usage(void) __attribute((noreturn));
122
123
struct sockaddr_in6 source;
124
char *device;
125
int pmtudisc=-1;
126
127
static int icmp_sock;
128
129
130
131
static struct in6_addr in6_anyaddr;
132
static __inline__ int ipv6_addr_any(struct in6_addr *addr)
133
{
134
	return (memcmp(addr, &in6_anyaddr, 16) == 0);
135
}
136
137
size_t inet6_srcrt_space(int type, int segments)
138
{
139
	if (type != 0 || segments > 24)
140
		return 0;
141
142
	return (sizeof(struct cmsghdr) + sizeof(struct rt0_hdr) +
143
		segments * sizeof(struct in6_addr));
144
}
145
146
extern struct cmsghdr *	inet6_srcrt_init(void *bp, int type)
147
{
148
	struct cmsghdr *cmsg;
149
150
	if (type)
151
		return NULL;
152
153
	memset(bp, 0, sizeof(struct cmsghdr) + sizeof(struct rt0_hdr));
154
	cmsg = (struct cmsghdr *) bp;
155
156
	cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct rt0_hdr);
157
	cmsg->cmsg_level = SOL_IPV6;
158
	cmsg->cmsg_type = IPV6_RTHDR;
159
160
	return cmsg;
161
}
162
163
int inet6_srcrt_add(struct cmsghdr *cmsg, const struct in6_addr *addr)
164
{
165
	struct rt0_hdr *hdr;
166
	
167
	hdr = (struct rt0_hdr *) CMSG_DATA(cmsg);
168
169
	cmsg->cmsg_len += sizeof(struct in6_addr);
170
	hdr->rt_hdr.hdrlen += sizeof(struct in6_addr) / 8;
171
172
	memcpy(&hdr->addr[hdr->rt_hdr.segments_left++], addr,
173
	       sizeof(struct in6_addr));
174
		
175
	return 0;
176
}
177
178
int main(int argc, char *argv[])
179
{
180
	int ch, hold, packlen;
181
	u_char *packet;
182
	char *target;
183
	struct sockaddr_in6 firsthop;
184
	int socket_errno;
185
	struct icmp6_filter filter;
186
	int err, csum_offset, sz_opt;
187
188
	fix_fds();
189
190
	icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
191
	socket_errno = errno;
192
193
	drop_priv();
194
195
	source.sin6_family = AF_INET6;
196
	memset(&firsthop, 0, sizeof(firsthop));
197
	firsthop.sin6_family = AF_INET6;
198
199
	preload = 1;
200
	while ((ch = getopt(argc, argv, COMMON_OPTSTR "F:")) != EOF) {
201
		switch(ch) {
202
		case 'F':
203
			sscanf(optarg, "%x", &flowlabel);
204
			options |= F_FLOWINFO;
205
			break;
206
		case 'Q':
207
			sscanf(optarg, "%x", &tclass);
208
			options |= F_TCLASS;
209
			break;
210
		case 'I':
211
			if (strchr(optarg, ':')) {
212
				if (inet_pton(AF_INET6, optarg, (char*)&source.sin6_addr) <= 0) {
213
					fprintf(stderr, "ping: invalid source address %s\n", optarg);
214
					exit(2);
215
				}
216
				options |= F_STRICTSOURCE;
217
			} else {
218
				device = optarg;
219
			}
220
			break;
221
		case 'M':
222
			if (strcmp(optarg, "do") == 0)
223
				pmtudisc = IPV6_PMTUDISC_DO;
224
			else if (strcmp(optarg, "dont") == 0)
225
				pmtudisc = IPV6_PMTUDISC_DONT;
226
			else if (strcmp(optarg, "want") == 0)
227
				pmtudisc = IPV6_PMTUDISC_WANT;
228
			else {
229
				fprintf(stderr, "ping: wrong value for -M: do, dont, want are valid ones.\n");
230
				exit(2);
231
			}
232
			break;
233
		case 'V':
234
			printf("ping6 utility, iputils-ss%s\n", SNAPSHOT);
235
			exit(0);
236
		COMMON_OPTIONS
237
			common_options(ch);
238
			break;
239
		default:
240
			usage();
241
		}
242
	}
243
	argc -= optind;
244
	argv += optind;
245
246
	while (argc > 1) {
247
		struct in6_addr addr;
248
249
		if (srcrt == NULL) {
250
			int space;
251
			
252
			space = inet6_srcrt_space(IPV6_SRCRT_TYPE_0, argc - 1);
253
254
			if (space == 0)	{
255
				fprintf(stderr, "srcrt_space failed\n");
256
				exit(2);
257
			}
258
			if (space + cmsglen > sizeof(cmsgbuf)) {
259
				fprintf(stderr, "no room for options\n");
260
				exit(2);
261
			}
262
263
			srcrt = (struct cmsghdr*)(cmsgbuf+cmsglen);
264
			cmsglen += CMSG_ALIGN(space);
265
			inet6_srcrt_init(srcrt, IPV6_SRCRT_TYPE_0);
266
		}
267
268
		target = *argv;
269
270
		if (inet_pton(AF_INET6, target, &addr) <= 0) {
271
			struct hostent *hp;
272
273
			hp = gethostbyname2(target, AF_INET6);
274
275
			if (hp == NULL)	{
276
				fprintf(stderr, "unknown host %s\n", target);
277
				exit(2);
278
			}
279
280
			memcpy(&addr, hp->h_addr_list[0], 16);
281
		}
282
283
		inet6_srcrt_add(srcrt, &addr);
284
		if (ipv6_addr_any(&firsthop.sin6_addr))
285
			memcpy(&firsthop.sin6_addr, &addr, 16);
286
287
		argv++;
288
		argc--;
289
	}
290
291
	if (argc != 1)
292
		usage();
293
	target = *argv;
294
295
	memset(&whereto, 0, sizeof(struct sockaddr_in6));
296
	whereto.sin6_family = AF_INET6;
297
	whereto.sin6_port = htons(IPPROTO_ICMPV6);
298
299
	if (inet_pton(AF_INET6, target, &whereto.sin6_addr) <= 0) {
300
		struct hostent *hp;
301
302
		hp = gethostbyname2(target, AF_INET6);
303
304
		if (hp == NULL) {
305
			fprintf(stderr, "unknown host\n");
306
			exit(2);
307
		}
308
		
309
		memcpy(&whereto.sin6_addr, hp->h_addr_list[0], 16);
310
	} else {
311
		options |= F_NUMERIC;
312
	}
313
	if (ipv6_addr_any(&firsthop.sin6_addr))
314
		memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16);
315
316
	hostname = target;
317
318
	if (ipv6_addr_any(&source.sin6_addr)) {
319
		int alen;
320
		int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0);
321
322
		if (probe_fd < 0) {
323
			perror("socket");
324
			exit(2);
325
		}
326
		if (device) {
327
			struct ifreq ifr;
328
			memset(&ifr, 0, sizeof(ifr));
329
			strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
330
			if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) {
331
#ifdef HAVE_SIN6_SCOPEID
332
				if ((firsthop.sin6_addr.s6_addr16[0]&htons(0xffc0)) == htons (0xfe80) ||
333
				    (firsthop.sin6_addr.s6_addr16[0]&htons(0xffff)) == htons (0xff02)) {
334
					if (ioctl(probe_fd, SIOCGIFINDEX, &ifr) < 0) {
335
						fprintf(stderr, "ping: unknown iface %s\n", device);
336
						exit(2);
337
					}
338
					firsthop.sin6_scope_id = ifr.ifr_ifindex;
339
				}
340
#endif
341
			}
342
		}
343
		firsthop.sin6_port = htons(1025);
344
		if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) {
345
			perror("connect");
346
			exit(2);
347
		}
348
		alen = sizeof(source);
349
		if (getsockname(probe_fd, (struct sockaddr*)&source, &alen) == -1) {
350
			perror("getsockname");
351
			exit(2);
352
		}
353
		source.sin6_port = 0;
354
		close(probe_fd);
355
	}
356
357
	if (icmp_sock < 0) {
358
		errno = socket_errno;
359
		perror("ping: icmp open socket");
360
		exit(2);
361
	}
362
363
	if (device) {
364
		struct ifreq ifr;
365
		struct cmsghdr *cmsg;
366
		struct in6_pktinfo *ipi;
367
				
368
		memset(&ifr, 0, sizeof(ifr));
369
		strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
370
		if (ioctl(icmp_sock, SIOCGIFINDEX, &ifr) < 0) {
371
			fprintf(stderr, "ping: unknown iface %s\n", device);
372
			exit(2);
373
		}
374
		cmsg = (struct cmsghdr*)cmsgbuf;
375
		cmsglen += CMSG_SPACE(sizeof(*ipi));
376
		cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi));
377
		cmsg->cmsg_level = SOL_IPV6;
378
		cmsg->cmsg_type = IPV6_PKTINFO;
379
				
380
		ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg);
381
		memset(ipi, 0, sizeof(*ipi));
382
		ipi->ipi6_ifindex = ifr.ifr_ifindex;
383
	}
384
385
	if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) {
386
		if (uid) {
387
			if (interval < 1000) {
388
				fprintf(stderr, "ping: multicast ping with too short interval.\n");
389
				exit(2);
390
			}
391
			if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) {
392
				fprintf(stderr, "ping: multicast ping does not fragment.\n");
393
				exit(2);
394
			}
395
		}
396
		if (pmtudisc < 0)
397
			pmtudisc = IPV6_PMTUDISC_DO;
398
	}
399
400
	if (pmtudisc >= 0) {
401
		if (setsockopt(icmp_sock, SOL_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)) == -1) {
402
			perror("ping: IPV6_MTU_DISCOVER");
403
			exit(2);
404
		}
405
	}
406
407
	if ((options&F_STRICTSOURCE) &&
408
	    bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
409
		perror("ping: bind icmp socket");
410
		exit(2);
411
	}
412
413
	if (datalen >= sizeof(struct timeval))	/* can we time transfer */
414
		timing = 1;
415
	packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */
416
	if (!(packet = (u_char *)malloc((u_int)packlen))) {
417
		fprintf(stderr, "ping: out of memory.\n");
418
		exit(2);
419
	}
420
421
	working_recverr = 1;
422
	hold = 1;
423
	if (setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) {
424
		fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
425
		working_recverr = 0;
426
	}
427
428
	/* Estimate memory eaten by single packet. It is rough estimate.
429
	 * Actually, for small datalen's it depends on kernel side a lot. */
430
	hold = datalen+8;
431
	hold += ((hold+511)/512)*(40+16+64+160);
432
	sock_setbufs(icmp_sock, hold);
433
434
	csum_offset = 2;
435
	sz_opt = sizeof(int);
436
437
	err = setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt);
438
	if (err < 0) {
439
		perror("setsockopt(RAW_CHECKSUM)");
440
		exit(2);
441
	}
442
443
	/*
444
	 *	select icmp echo reply as icmp type to receive
445
	 */
446
447
	ICMPV6_FILTER_SETBLOCKALL(&filter);
448
449
	if (!working_recverr) {
450
		ICMPV6_FILTER_SETPASS(ICMPV6_DEST_UNREACH, &filter);
451
		ICMPV6_FILTER_SETPASS(ICMPV6_PKT_TOOBIG, &filter);
452
		ICMPV6_FILTER_SETPASS(ICMPV6_TIME_EXCEED, &filter);
453
		ICMPV6_FILTER_SETPASS(ICMPV6_PARAMPROB, &filter);
454
	}
455
456
	ICMPV6_FILTER_SETPASS(ICMPV6_ECHO_REPLY, &filter);
457
458
	err = setsockopt(icmp_sock, SOL_ICMPV6, ICMPV6_FILTER, &filter,
459
			 sizeof(struct icmp6_filter));
460
461
	if (err < 0) {
462
		perror("setsockopt(ICMPV6_FILTER)");
463
		exit(2);
464
	}
465
466
	if (options & F_NOLOOP) {
467
		int loop = 0;
468
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
469
							&loop, sizeof(loop)) == -1) {
470
			perror ("can't disable multicast loopback");
471
			exit(2);
472
		}
473
	}
474
	if (options & F_TTL) {
475
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
476
			       &ttl, sizeof(ttl)) == -1) {
477
			perror ("can't set multicast hop limit");
478
			exit(2);
479
		}
480
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
481
			       &ttl, sizeof(ttl)) == -1) {
482
			perror ("can't set unicast hop limit");
483
			exit(2);
484
		}
485
	}
486
487
	if (1) {
488
		int on = 1;
489
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_HOPLIMIT,
490
			       &on, sizeof(on)) == -1) {
491
			perror ("can't receive hop limit");
492
			exit(2);
493
		}
494
	}
495
496
	if (options&F_FLOWINFO) {
497
#ifdef IPV6_FLOWLABEL_MGR
498
		char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen];
499
		struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
500
		int freq_len = sizeof(*freq);
501
		if (srcrt)
502
			freq_len = CMSG_ALIGN(sizeof(*freq)) + srcrt->cmsg_len;
503
		memset(freq, 0, sizeof(*freq));
504
		freq->flr_label = htonl(flowlabel&0xFFFFF);
505
		freq->flr_action = IPV6_FL_A_GET;
506
		freq->flr_flags = IPV6_FL_F_CREATE;
507
		freq->flr_share = IPV6_FL_S_EXCL;
508
		memcpy(&freq->flr_dst, &whereto.sin6_addr, 16);
509
		if (srcrt)
510
			memcpy(freq_buf + CMSG_ALIGN(sizeof(*freq)), srcrt, srcrt->cmsg_len);
511
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR,
512
			       freq, freq_len) == -1) {
513
			perror ("can't set flowlabel");
514
			exit(2);
515
		}
516
		flowlabel = freq->flr_label;
517
		if (srcrt) {
518
			cmsglen = (char*)srcrt - (char*)cmsgbuf;
519
			srcrt = NULL;
520
		}
521
#else
522
		fprintf(stderr, "Flow labels are not supported.\n");
523
		exit(2);
524
#endif
525
	}
526
	if (options&(F_FLOWINFO|F_TCLASS)) {
527
#ifdef IPV6_FLOWINFO_SEND
528
		int on = 1;
529
		whereto.sin6_flowinfo = flowlabel | htonl((tclass&0xFF)<<20);
530
		if (setsockopt(icmp_sock, IPPROTO_IPV6, IPV6_FLOWINFO_SEND,
531
			       &on, sizeof(on)) == -1) {
532
			perror ("can't send flowinfo");
533
			exit(2);
534
		}
535
#else
536
		fprintf(stderr, "Flowinfo is not supported.\n");
537
		exit(2);
538
#endif
539
	}
540
541
	printf("PING %s(%s) ", hostname, pr_addr(&whereto.sin6_addr));
542
	if (flowlabel)
543
		printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel));
544
	if (device || (options&F_STRICTSOURCE)) {
545
		printf("from %s %s: ",
546
		       pr_addr_n(&source.sin6_addr), device ? : "");
547
	}
548
	printf("%d data bytes\n", datalen);
549
550
	setup(icmp_sock);
551
552
	main_loop(icmp_sock, packet, packlen);
553
}
554
555
int receive_error_msg()
556
{
557
	int res;
558
	char cbuf[512];
559
	struct iovec  iov;
560
	struct msghdr msg;
561
	struct cmsghdr *cmsg;
562
	struct sock_extended_err *e;
563
	struct icmp6hdr icmph;
564
	struct sockaddr_in6 target;
565
	int net_errors = 0;
566
	int local_errors = 0;
567
	int saved_errno = errno;
568
569
	iov.iov_base = &icmph;
570
	iov.iov_len = sizeof(icmph);
571
	msg.msg_name = (void*)&target;
572
	msg.msg_namelen = sizeof(target);
573
	msg.msg_iov = &iov;
574
	msg.msg_iovlen = 1;
575
	msg.msg_flags = 0;
576
	msg.msg_control = cbuf;
577
	msg.msg_controllen = sizeof(cbuf);
578
579
	res = recvmsg(icmp_sock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT);
580
	if (res < 0)
581
		goto out;
582
583
	e = NULL;
584
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
585
		if (cmsg->cmsg_level == SOL_IPV6) {
586
			if (cmsg->cmsg_type == IPV6_RECVERR)
587
				e = (struct sock_extended_err *)CMSG_DATA(cmsg);
588
		}
589
	}
590
	if (e == NULL)
591
		abort();
592
593
	if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
594
		local_errors++;
595
		if (options & F_QUIET)
596
			goto out;
597
		if (options & F_FLOOD)
598
			write(STDOUT_FILENO, "E", 1);
599
		else if (e->ee_errno != EMSGSIZE)
600
			fprintf(stderr, "ping: local error: %s\n", strerror(e->ee_errno));
601
		else
602
			fprintf(stderr, "ping: local error: Message too long, mtu=%u\n", e->ee_info);
603
		nerrors++;
604
	} else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) {
605
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
606
607
		if (res < sizeof(icmph) ||
608
		    memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) ||
609
		    icmph.icmp6_type != ICMPV6_ECHO_REQUEST ||
610
		    icmph.icmp6_identifier != ident) {
611
			/* Not our error, not an error at all. Clear. */
612
			saved_errno = 0;
613
			goto out;
614
		}
615
616
		net_errors++;
617
		nerrors++;
618
		if (options & F_QUIET)
619
			goto out;
620
		if (options & F_FLOOD) {
621
			write(STDOUT_FILENO, "\bE", 2);
622
		} else {
623
			printf("From %s icmp_seq=%u ", pr_addr(&sin6->sin6_addr), ntohs(icmph.icmp6_sequence));
624
			pr_icmph(e->ee_type, e->ee_code, e->ee_info);
625
			putchar('\n');
626
			fflush(stdout);
627
		}
628
	}
629
630
out:
631
	errno = saved_errno;
632
	return net_errors ? : -local_errors;
633
}
634
635
/*
636
 * pinger --
637
 * 	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
638
 * will be added on by the kernel.  The ID field is our UNIX process ID,
639
 * and the sequence number is an ascending integer.  The first 8 bytes
640
 * of the data portion are used to hold a UNIX "timeval" struct in VAX
641
 * byte-order, to compute the round-trip time.
642
 */
643
int send_probe(void)
644
{
645
	struct icmp6hdr *icmph;
646
	int cc;
647
	int i;
648
649
	icmph = (struct icmp6hdr *)outpack;
650
	icmph->icmp6_type = ICMPV6_ECHO_REQUEST;
651
	icmph->icmp6_code = 0;
652
	icmph->icmp6_cksum = 0;
653
	icmph->icmp6_sequence = htons(ntransmitted+1);
654
	icmph->icmp6_identifier = ident;
655
656
	CLR((ntransmitted+1) % mx_dup_ck);
657
658
	if (timing)
659
		gettimeofday((struct timeval *)&outpack[8],
660
		    (struct timezone *)NULL);
661
662
	cc = datalen + 8;			/* skips ICMP portion */
663
664
	if (cmsglen == 0) {
665
		i = sendto(icmp_sock, (char *)outpack, cc, confirm,
666
			   (struct sockaddr *) &whereto,
667
			   sizeof(struct sockaddr_in6));
668
	} else {
669
		struct msghdr mhdr;
670
		struct iovec iov;
671
672
		iov.iov_len  = cc;
673
		iov.iov_base = outpack;
674
675
		mhdr.msg_name = &whereto;
676
		mhdr.msg_namelen = sizeof(struct sockaddr_in6);
677
		mhdr.msg_iov = &iov;
678
		mhdr.msg_iovlen = 1;
679
		mhdr.msg_control = cmsgbuf;
680
		mhdr.msg_controllen = cmsglen;
681
682
		i = sendmsg(icmp_sock, &mhdr, confirm);
683
	}
684
	confirm = 0;
685
686
	return (cc == i ? 0 : i);
687
}
688
689
/*
690
 * parse_reply --
691
 *	Print out the packet, if it came from us.  This logic is necessary
692
 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
693
 * which arrive ('tis only fair).  This permits multiple copies of this
694
 * program to be run without having intermingled output (or statistics!).
695
 */
696
int
697
parse_reply(struct msghdr *msg, int cc, void *addr, struct timeval *tv)
698
{
699
	struct sockaddr_in6 *from = addr;
700
	__u8 *buf = msg->msg_iov->iov_base;
701
	struct cmsghdr *c;
702
	struct icmp6hdr *icmph;
703
	int hops = -1;
704
705
	for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) {
706
		if (c->cmsg_level != SOL_IPV6 ||
707
		    c->cmsg_type != IPV6_HOPLIMIT)
708
			continue;
709
		if (c->cmsg_len < CMSG_LEN(sizeof(int)))
710
			continue;
711
		hops = *(int*)CMSG_DATA(c);
712
	}
713
714
715
	/* Now the ICMP part */
716
717
	icmph = (struct icmp6hdr *) buf;
718
	if (cc < 8) {
719
		if (options & F_VERBOSE)
720
			fprintf(stderr, "ping: packet too short (%d bytes)\n", cc);
721
		return 1;
722
	}
723
724
	if (icmph->icmp6_type == ICMPV6_ECHO_REPLY) {
725
		if (icmph->icmp6_identifier != ident)
726
			return 1;
727
		if (gather_statistics((__u8*)(icmph+1), cc,
728
				      ntohs(icmph->icmp6_sequence),
729
				      hops, 0, tv, pr_addr(&from->sin6_addr)))
730
			return 0;
731
	} else {
732
		int nexthdr;
733
		struct ipv6hdr *iph1 = (struct ipv6hdr*)(icmph+1);
734
		struct icmp6hdr *icmph1 = (struct icmp6hdr *)(iph1+1);
735
736
		/* We must not ever fall here. All the messages but
737
		 * echo reply are blocked by filter and error are
738
		 * received with IPV6_RECVERR. Ugly code is preserved
739
		 * however, just to remember what crap we avoided
740
		 * using RECVRERR. :-)
741
		 */
742
743
		if (cc < 8+sizeof(struct ipv6hdr)+8)
744
			return 1;
745
746
		if (memcmp(&iph1->daddr, &whereto.sin6_addr, 16))
747
			return 1;
748
749
		nexthdr = iph1->nexthdr;
750
751
		if (nexthdr == 44) {
752
			nexthdr = *(__u8*)icmph1;
753
			icmph1++;
754
		}
755
		if (nexthdr == IPPROTO_ICMPV6) {
756
			if (icmph1->icmp6_type != ICMPV6_ECHO_REQUEST ||
757
			    icmph1->icmp6_identifier != ident)
758
				return 1;
759
			acknowledge(ntohs(icmph1->icmp6_sequence));
760
			if (working_recverr)
761
				return 0;
762
			nerrors++;
763
			if (options & F_FLOOD) {
764
				write(STDOUT_FILENO, "\bE", 2);
765
				return 0;
766
			}
767
			printf("From %s: icmp_seq=%u ", pr_addr(&from->sin6_addr), ntohs(icmph1->icmp6_sequence));
768
		} else {
769
			/* We've got something other than an ECHOREPLY */
770
			if (!(options & F_VERBOSE) || uid)
771
				return 1;
772
			printf("From %s: ", pr_addr(&from->sin6_addr));
773
		}
774
		pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu));
775
	}
776
777
	if (!(options & F_FLOOD)) {
778
		if (options & F_AUDIBLE)
779
			putchar('\a');
780
		putchar('\n');
781
		fflush(stdout);
782
	}
783
	return 0;
784
}
785
786
787
int pr_icmph(__u8 type, __u8 code, __u32 info)
788
{
789
	switch(type) {
790
	case ICMPV6_DEST_UNREACH:
791
		printf("Destination unreachable: ");
792
		switch (code) {
793
		case ICMPV6_NOROUTE:
794
			printf("No route");
795
			break;
796
		case ICMPV6_ADM_PROHIBITED:
797
			printf("Administratively prohibited");
798
			break;
799
		case ICMPV6_NOT_NEIGHBOUR:
800
			printf("Not neighbour");
801
			break;
802
		case ICMPV6_ADDR_UNREACH:
803
			printf("Address unreachable");
804
			break;
805
		case ICMPV6_PORT_UNREACH:
806
			printf("Port unreachable");
807
			break;
808
		default:	
809
			printf("Unknown code %d", code);
810
			break;
811
		}
812
		break;
813
	case ICMPV6_PKT_TOOBIG:
814
		printf("Packet too big: mtu=%u", info);
815
		if (code)
816
			printf(", code=%d", code);
817
		break;
818
	case ICMPV6_TIME_EXCEED:
819
		printf("Time exceeded: ");
820
		if (code == ICMPV6_EXC_HOPLIMIT)
821
			printf("Hop limit");
822
		else if (code == ICMPV6_EXC_FRAGTIME)
823
			printf("Defragmentation failure");
824
		else
825
			printf("code %d", code);
826
		break;
827
	case ICMPV6_PARAMPROB:
828
		printf("Parameter problem: ");
829
		if (code == ICMPV6_HDR_FIELD)
830
			printf("Wrong header field ");
831
		else if (code == ICMPV6_UNK_NEXTHDR)
832
			printf("Unknown header ");
833
		else if (code == ICMPV6_UNK_OPTION)
834
			printf("Unknown option ");
835
		else
836
			printf("code %d ", code);
837
		printf ("at %u", info);
838
		break;
839
	case ICMPV6_ECHO_REQUEST:
840
		printf("Echo request");
841
		break;
842
	case ICMPV6_ECHO_REPLY:
843
		printf("Echo reply");
844
		break;
845
	case ICMPV6_MGM_QUERY:
846
		printf("MLD Query");
847
		break;
848
	case ICMPV6_MGM_REPORT:
849
		printf("MLD Report");
850
		break;
851
	case ICMPV6_MGM_REDUCTION:
852
		printf("MLD Reduction");
853
		break;
854
	default:
855
		printf("unknown icmp type");
856
		
857
	}
858
	return 0;
859
}
860
861
#include <linux/filter.h>
862
863
void install_filter(void)
864
{
865
	static int once;
866
	static struct sock_filter insns[] = {
867
		BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4),  /* Load icmp echo ident */
868
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1),  /* Ours? */
869
		BPF_STMT(BPF_RET|BPF_K, ~0U),  /* Yes, it passes. */
870
		BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0),  /* Load icmp type */
871
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMPV6_ECHO_REPLY, 1, 0), /* Echo? */
872
		BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */
873
		BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */
874
	};
875
	static struct sock_fprog filter = {
876
		sizeof insns / sizeof(insns[0]),
877
		insns
878
	};
879
880
	if (once)
881
		return;
882
	once = 1;
883
884
	/* Patch bpflet for current identifier. */
885
	insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __constant_htons(ident), 0, 1);
886
887
	if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
888
		perror("WARNING: failed to install socket filter\n");
889
}
890
891
892
/*
893
 * pr_addr --
894
 *	Return an ascii host address as a dotted quad and optionally with
895
 * a hostname.
896
 */
897
char * pr_addr(struct in6_addr *addr)
898
{
899
	struct hostent *hp = NULL;
900
901
	if (!(options&F_NUMERIC))
902
		hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6);
903
904
	return hp ? hp->h_name : pr_addr_n(addr);
905
}
906
907
char * pr_addr_n(struct in6_addr *addr)
908
{
909
	static char str[64];
910
	inet_ntop(AF_INET6, addr, str, sizeof(str));
911
	return str;
912
}
913
914
void usage(void)
915
{
916
	fprintf(stderr,
917
"Usage: ping6 [-LUdfnqrvVaA] [-c count] [-i interval] [-w deadline]\n"
918
"             [-p pattern] [-s packetsize] [-t ttl] [-I interface]\n"
919
"             [-M mtu discovery hint] [-S sndbuf]\n"
920
"             [-F flow label] [-Q traffic class] [hop1 ...] destination\n");
921
	exit(2);
922
}
(-)iputils.p8/tracepath6.c (-2 / +2 lines)
Lines 113-119 Link Here
113
		if (cmsg->cmsg_level == SOL_IPV6) {
113
		if (cmsg->cmsg_level == SOL_IPV6) {
114
			if (cmsg->cmsg_type == IPV6_RECVERR) {
114
			if (cmsg->cmsg_type == IPV6_RECVERR) {
115
				e = (struct sock_extended_err *)CMSG_DATA(cmsg);
115
				e = (struct sock_extended_err *)CMSG_DATA(cmsg);
116
			} else if (cmsg->cmsg_type == IPV6_HOPLIMIT) {
116
			} else if (cmsg->cmsg_type == IPV6_2292HOPLIMIT) {
117
				rethops = *(int*)CMSG_DATA(cmsg);
117
				rethops = *(int*)CMSG_DATA(cmsg);
118
			}
118
			}
119
		} else if (cmsg->cmsg_level == SOL_IP) {
119
		} else if (cmsg->cmsg_level == SOL_IP) {
Lines 359-365 Link Here
359
		perror("IP_RECVERR");
359
		perror("IP_RECVERR");
360
		exit(1);
360
		exit(1);
361
	}
361
	}
362
	if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on))) {
362
	if (setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on))) {
363
		perror("IPV6_HOPLIMIT");
363
		perror("IPV6_HOPLIMIT");
364
		exit(1);
364
		exit(1);
365
	}
365
	}
(-)iputils.p8/tracepath6.c~ (+406 lines)
Line 0 Link Here
1
/*
2
 * tracepath6.c
3
 *
4
 *		This program is free software; you can redistribute it and/or
5
 *		modify it under the terms of the GNU General Public License
6
 *		as published by the Free Software Foundation; either version
7
 *		2 of the License, or (at your option) any later version.
8
 *
9
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10
 */
11
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <unistd.h>
15
#include <sys/socket.h>
16
#include <netinet/in.h>
17
18
#include <linux/in6.h>
19
#include <linux/errqueue.h>
20
#include <errno.h>
21
#include <string.h>
22
#include <netdb.h>
23
#include <resolv.h>
24
#include <sys/time.h>
25
#include <sys/uio.h>
26
#include <arpa/inet.h>
27
#include "fixfds.h"
28
#include "droppriv.h"
29
30
int overhead = 48;
31
int mtu = 128000;
32
int hops_to = -1;
33
int hops_from = -1;
34
int no_resolve = 0;
35
int show_both = 0;
36
int mapped;
37
38
struct probehdr
39
{
40
	__u32 ttl;
41
	struct timeval tv;
42
};
43
44
void data_wait(int fd)
45
{
46
	fd_set fds;
47
	struct timeval tv;
48
	FD_ZERO(&fds);
49
	FD_SET(fd, &fds);
50
	tv.tv_sec = 1;
51
	tv.tv_usec = 0;
52
	select(fd+1, &fds, NULL, NULL, &tv);
53
}
54
55
int recverr(int fd, int ttl)
56
{
57
	int res;
58
	struct probehdr rcvbuf;
59
	char cbuf[512];
60
	struct iovec  iov;
61
	struct msghdr msg;
62
	struct cmsghdr *cmsg;
63
	struct sock_extended_err *e;
64
	struct timeval tv;
65
	struct timeval *rettv;
66
	int rethops;
67
	int sndhops;
68
	int progress = -1;
69
	int broken_router;
70
71
restart:
72
	memset(&rcvbuf, -1, sizeof(rcvbuf));
73
	iov.iov_base = &rcvbuf;
74
	iov.iov_len = sizeof(rcvbuf);
75
	msg.msg_name = NULL;
76
	msg.msg_namelen = 0;
77
	msg.msg_iov = &iov;
78
	msg.msg_iovlen = 1;
79
	msg.msg_flags = 0;
80
	msg.msg_control = cbuf;
81
	msg.msg_controllen = sizeof(cbuf);
82
83
	gettimeofday(&tv, NULL);
84
	res = recvmsg(fd, &msg, MSG_ERRQUEUE);
85
	if (res < 0) {
86
		if (errno == EAGAIN)
87
			return progress;
88
		goto restart;
89
	}
90
91
	progress = 2;
92
93
	rethops = -1;
94
	sndhops = -1;
95
	e = NULL;
96
	rettv = NULL;
97
	broken_router = 0;
98
	if (res == sizeof(rcvbuf)) {
99
		if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0)
100
			broken_router = 1;
101
102
		sndhops = rcvbuf.ttl;
103
		rettv = &rcvbuf.tv;
104
		if (sndhops != ttl)
105
			progress = -1;
106
	}
107
	if (sndhops>0)
108
		printf("%2d:  ", sndhops);
109
	else
110
		printf("%2d?: ", ttl);
111
112
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
113
		if (cmsg->cmsg_level == SOL_IPV6) {
114
			if (cmsg->cmsg_type == IPV6_RECVERR) {
115
				e = (struct sock_extended_err *)CMSG_DATA(cmsg);
116
			} else if (cmsg->cmsg_type == IPV6_HOPLIMIT) {
117
				rethops = *(int*)CMSG_DATA(cmsg);
118
			}
119
		} else if (cmsg->cmsg_level == SOL_IP) {
120
			if (cmsg->cmsg_type == IP_TTL) {
121
				rethops = *(__u8*)CMSG_DATA(cmsg);
122
			}
123
		}
124
	}
125
	if (e == NULL) {
126
		printf("no info\n");
127
		return 0;
128
	}
129
	if (e->ee_origin == SO_EE_ORIGIN_LOCAL)
130
		printf("%-32s ", "[LOCALHOST]");
131
	else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 ||
132
		 e->ee_origin == SO_EE_ORIGIN_ICMP) {
133
		struct hostent * h = NULL;
134
		char abuf[128];
135
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
136
		struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
137
138
		if (!no_resolve)
139
			fflush(stdout);
140
141
		if (sin->sin_family == AF_INET6) {
142
			inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
143
			if (!no_resolve)
144
				h = gethostbyaddr((char *) &sin6->sin6_addr, sizeof(sin6->sin6_addr), AF_INET6);
145
		} else {
146
			inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
147
			if (!no_resolve)
148
				h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
149
		}
150
		if (!no_resolve) {
151
			char fabuf[256];
152
			if (show_both) {
153
				if (h)
154
					snprintf(fabuf, sizeof(fabuf), "%s %s", h->h_name, abuf);
155
				else
156
					snprintf(fabuf, sizeof(fabuf), "%s", abuf);
157
			} else {
158
				snprintf(fabuf, sizeof(fabuf), "%s", h ? h->h_name : abuf);
159
			}
160
			printf("%-40s ", fabuf);
161
		} else {
162
			printf("%-32s ", abuf);
163
		}
164
	}
165
166
	if (rethops>=0) {
167
		if (rethops<=64)
168
			rethops = 65-rethops;
169
		else if (rethops<=128)
170
			rethops = 129-rethops;
171
		else
172
			rethops = 256-rethops;
173
		if (sndhops>=0 && rethops != sndhops)
174
			printf("asymm %2d ", rethops);
175
		else if (sndhops<0 && rethops != ttl)
176
			printf("asymm %2d ", rethops);
177
	}
178
179
	if (rettv) {
180
		int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
181
		printf("%3d.%3dms ", diff/1000, diff%1000);
182
		if (broken_router)
183
			printf("(This broken router returned corrupted payload) ");
184
	}
185
186
	switch (e->ee_errno) {
187
	case ETIMEDOUT:
188
		printf("\n");
189
		break;
190
	case EMSGSIZE:
191
		printf("pmtu %d\n", e->ee_info);
192
		mtu = e->ee_info;
193
		progress = 1;
194
		break;
195
	case ECONNREFUSED:
196
		printf("reached\n");
197
		hops_to = sndhops<0 ? ttl : sndhops;
198
		hops_from = rethops;
199
		return 0;
200
	case EPROTO:
201
		printf("!P\n");
202
		return 0;
203
	case EHOSTUNREACH:
204
		if ((e->ee_origin == SO_EE_ORIGIN_ICMP &&
205
		     e->ee_type == 11 &&
206
		     e->ee_code == 0) ||
207
		    (e->ee_origin == SO_EE_ORIGIN_ICMP6 &&
208
		     e->ee_type == 3 &&
209
		     e->ee_code == 0)) {
210
			printf("\n");
211
			break;
212
		}
213
		printf("!H\n");
214
		return 0;
215
	case ENETUNREACH:
216
		printf("!N\n");
217
		return 0;
218
	case EACCES:
219
		printf("!A\n");
220
		return 0;
221
	default:
222
		printf("\n");
223
		errno = e->ee_errno;
224
		perror("NET ERROR");
225
		return 0;
226
	}
227
	goto restart;
228
}
229
230
int probe_ttl(int fd, int ttl)
231
{
232
	int i;
233
	char sndbuf[mtu];
234
	struct probehdr *hdr = (struct probehdr*)sndbuf;
235
236
restart:
237
238
	for (i=0; i<10; i++) {
239
		int res;
240
241
		hdr->ttl = ttl;
242
		gettimeofday(&hdr->tv, NULL);
243
		if (send(fd, sndbuf, mtu-overhead, 0) > 0)
244
			break;
245
		res = recverr(fd, ttl);
246
		if (res==0)
247
			return 0;
248
		if (res > 0)
249
			goto restart;
250
	}
251
252
	if (i<10) {
253
		int res;
254
255
		data_wait(fd);
256
		if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
257
			printf("%2d?: reply received 8)\n", ttl);
258
			return 0;
259
		}
260
		res = recverr(fd, ttl);
261
		if (res == 1)
262
			goto restart;
263
		return res;
264
	}
265
266
	printf("%2d:  send failed\n", ttl);
267
	return 0;
268
}
269
270
static void usage(void) __attribute((noreturn));
271
272
static void usage(void)
273
{
274
	fprintf(stderr, "Usage: tracepath6 [-n] [-b] <destination>[/<port>]\n");
275
	exit(-1);
276
}
277
278
279
int main(int argc, char **argv)
280
{
281
	int fd;
282
	int on;
283
	struct sockaddr_in6 sin;
284
	int ttl;
285
	char *p;
286
	struct hostent *he;
287
	int ch;
288
289
	fix_fds();
290
	drop_priv();
291
292
	while ((ch = getopt(argc, argv, "nbh?")) != EOF) {
293
		switch(ch) {
294
		case 'n':	
295
			no_resolve = 1;
296
			break;
297
		case 'b':	
298
			show_both = 1;
299
			break;
300
		default:
301
			usage();
302
		}
303
	}
304
305
	argc -= optind;
306
	argv += optind;
307
308
	if (argc != 1)
309
		usage();
310
311
312
	fd = socket(AF_INET6, SOCK_DGRAM, 0);
313
	if (fd < 0) {
314
		perror("socket");
315
		exit(1);
316
	}
317
	sin.sin6_family = AF_INET6;
318
319
	p = strchr(argv[0], '/');
320
	if (p) {
321
		*p = 0;
322
		sin.sin6_port = htons(atoi(p+1));
323
	} else
324
		sin.sin6_port = htons(0x8000 | getpid());
325
	he = gethostbyname2(argv[0], AF_INET6);
326
	if (he == NULL) {
327
		herror("gethostbyname2");
328
		exit(1);
329
	}
330
	memcpy(&sin.sin6_addr, he->h_addr, 16);
331
332
	if (connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
333
		perror("connect");
334
		exit(1);
335
	}
336
337
	if (!sin.sin6_addr.s6_addr32[0] && !sin.sin6_addr.s6_addr32[1]
338
	    && sin.sin6_addr.s6_addr32[2] == htonl(0xFFFF)) {
339
		mtu = 65535;
340
		overhead = 28;
341
		mapped = 1;
342
	}
343
344
	on = IPV6_PMTUDISC_DO;
345
	if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on))) {
346
		perror("IPV6_MTU_DISCOVER");
347
		exit(1);
348
	}
349
	if (mapped && setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) {
350
		perror("IP_MTU_DISCOVER");
351
		exit(1);
352
	}
353
	on = 1;
354
	if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) {
355
		perror("IPV6_RECVERR");
356
		exit(1);
357
	}
358
	if (mapped && setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
359
		perror("IP_RECVERR");
360
		exit(1);
361
	}
362
	if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on))) {
363
		perror("IPV6_HOPLIMIT");
364
		exit(1);
365
	}
366
	if (mapped && setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
367
		perror("IP_RECVTTL");
368
		exit(1);
369
	}
370
371
	for (ttl=1; ttl<32; ttl++) {
372
		int res = 0;
373
		int i;
374
375
		on = ttl;
376
		if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) {
377
			perror("IPV6_UNICAST_HOPS");
378
			exit(1);
379
		}
380
		if (mapped && setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
381
			perror("IP_TTL");
382
			exit(1);
383
		}
384
385
		for (i=0; i<3; i++) {
386
			res = probe_ttl(fd, ttl);
387
			if (res == 0)
388
				goto done;
389
			if (res > 0)
390
				break;
391
		}
392
393
		if (res < 0)
394
			printf("%2d:  no reply\n", ttl);
395
	}
396
	printf("     Too many hops: pmtu %d\n", mtu);
397
398
done:
399
	printf("     Resume: pmtu %d ", mtu);
400
	if (hops_to>=0)
401
		printf("hops %d ", hops_to);
402
	if (hops_from>=0)
403
		printf("back %d ", hops_from);
404
	printf("\n");
405
	exit(0);
406
}

Return to bug 13916