1 |
|
|
/* $OpenBSD: ping6.c,v 1.146 2016/03/03 18:30:48 florian Exp $ */ |
2 |
|
|
/* $KAME: ping6.c,v 1.163 2002/10/25 02:19:06 itojun Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of the project nor the names of its contributors |
17 |
|
|
* may be used to endorse or promote products derived from this software |
18 |
|
|
* without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
21 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
24 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 |
|
|
* SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
/* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */ |
34 |
|
|
|
35 |
|
|
/* |
36 |
|
|
* Copyright (c) 1989, 1993 |
37 |
|
|
* The Regents of the University of California. All rights reserved. |
38 |
|
|
* |
39 |
|
|
* This code is derived from software contributed to Berkeley by |
40 |
|
|
* Mike Muuss. |
41 |
|
|
* |
42 |
|
|
* Redistribution and use in source and binary forms, with or without |
43 |
|
|
* modification, are permitted provided that the following conditions |
44 |
|
|
* are met: |
45 |
|
|
* 1. Redistributions of source code must retain the above copyright |
46 |
|
|
* notice, this list of conditions and the following disclaimer. |
47 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
48 |
|
|
* notice, this list of conditions and the following disclaimer in the |
49 |
|
|
* documentation and/or other materials provided with the distribution. |
50 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
51 |
|
|
* may be used to endorse or promote products derived from this software |
52 |
|
|
* without specific prior written permission. |
53 |
|
|
* |
54 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
55 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
56 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
57 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
58 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
59 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
60 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
61 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
62 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
63 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
64 |
|
|
* SUCH DAMAGE. |
65 |
|
|
*/ |
66 |
|
|
|
67 |
|
|
/* |
68 |
|
|
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, |
69 |
|
|
* measure round-trip-delays and packet loss across network paths. |
70 |
|
|
* |
71 |
|
|
* Author - |
72 |
|
|
* Mike Muuss |
73 |
|
|
* U. S. Army Ballistic Research Laboratory |
74 |
|
|
* December, 1983 |
75 |
|
|
* |
76 |
|
|
* Status - |
77 |
|
|
* Public Domain. Distribution Unlimited. |
78 |
|
|
* Bugs - |
79 |
|
|
* More statistics could always be gathered. |
80 |
|
|
* This program has to run SUID to ROOT to access the ICMP socket. |
81 |
|
|
*/ |
82 |
|
|
|
83 |
|
|
#include <sys/types.h> |
84 |
|
|
#include <sys/socket.h> |
85 |
|
|
#include <sys/time.h> |
86 |
|
|
#include <sys/uio.h> |
87 |
|
|
|
88 |
|
|
#include <netinet/in.h> |
89 |
|
|
#include <netinet/ip6.h> |
90 |
|
|
#include <netinet/icmp6.h> |
91 |
|
|
#include <netinet/ip_ah.h> |
92 |
|
|
#include <arpa/inet.h> |
93 |
|
|
#include <netdb.h> |
94 |
|
|
|
95 |
|
|
#include <ctype.h> |
96 |
|
|
#include <err.h> |
97 |
|
|
#include <errno.h> |
98 |
|
|
#include <limits.h> |
99 |
|
|
#include <math.h> |
100 |
|
|
#include <poll.h> |
101 |
|
|
#include <signal.h> |
102 |
|
|
#include <siphash.h> |
103 |
|
|
#include <stdint.h> |
104 |
|
|
#include <stdio.h> |
105 |
|
|
#include <stdlib.h> |
106 |
|
|
#include <string.h> |
107 |
|
|
#include <time.h> |
108 |
|
|
#include <unistd.h> |
109 |
|
|
|
110 |
|
|
struct tv64 { |
111 |
|
|
u_int64_t tv64_sec; |
112 |
|
|
u_int64_t tv64_nsec; |
113 |
|
|
}; |
114 |
|
|
|
115 |
|
|
struct payload { |
116 |
|
|
struct tv64 tv64; |
117 |
|
|
u_int8_t mac[SIPHASH_DIGEST_LENGTH]; |
118 |
|
|
}; |
119 |
|
|
|
120 |
|
|
#define MAXPACKETLEN 131072 |
121 |
|
|
#define IP6LEN 40 |
122 |
|
|
#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ |
123 |
|
|
#define ICMP6ECHOTMLEN sizeof(struct payload) |
124 |
|
|
#define EXTRA 256 /* for AH and various other headers. weird. */ |
125 |
|
|
#define DEFDATALEN ICMP6ECHOTMLEN |
126 |
|
|
#define MAXPAYLOAD MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN |
127 |
|
|
#define MAXWAIT_DEFAULT 10 /* secs to wait for response */ |
128 |
|
|
|
129 |
|
|
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ |
130 |
|
|
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ |
131 |
|
|
#define SET(bit) (A(bit) |= B(bit)) |
132 |
|
|
#define CLR(bit) (A(bit) &= (~B(bit))) |
133 |
|
|
#define TST(bit) (A(bit) & B(bit)) |
134 |
|
|
|
135 |
|
|
#define F_FLOOD 0x0001 |
136 |
|
|
#define F_INTERVAL 0x0002 |
137 |
|
|
#define F_PINGFILLED 0x0008 |
138 |
|
|
#define F_QUIET 0x0010 |
139 |
|
|
#define F_SO_DEBUG 0x0040 |
140 |
|
|
#define F_VERBOSE 0x0100 |
141 |
|
|
/* 0x4000 */ |
142 |
|
|
#define F_HOSTNAME 0x10000 |
143 |
|
|
#define F_AUD_RECV 0x200000 |
144 |
|
|
#define F_AUD_MISS 0x400000 |
145 |
|
|
u_int options; |
146 |
|
|
|
147 |
|
|
/* multicast options */ |
148 |
|
|
int moptions; |
149 |
|
|
#define MULTICAST_NOLOOP 0x001 |
150 |
|
|
|
151 |
|
|
#define DUMMY_PORT 10101 |
152 |
|
|
|
153 |
|
|
/* |
154 |
|
|
* MAX_DUP_CHK is the number of bits in received table, i.e. the maximum |
155 |
|
|
* number of received sequence numbers we can keep track of. Change 128 |
156 |
|
|
* to 8192 for complete accuracy... |
157 |
|
|
*/ |
158 |
|
|
#define MAX_DUP_CHK (8 * 8192) |
159 |
|
|
int mx_dup_ck = MAX_DUP_CHK; |
160 |
|
|
char rcvd_tbl[MAX_DUP_CHK / 8]; |
161 |
|
|
|
162 |
|
|
struct sockaddr_in6 dst; /* who to ping6 */ |
163 |
|
|
|
164 |
|
|
int datalen = DEFDATALEN; |
165 |
|
|
int s; /* socket file descriptor */ |
166 |
|
|
u_char outpack[MAXPACKETLEN]; |
167 |
|
|
char BSPACE = '\b'; /* characters written for flood */ |
168 |
|
|
char DOT = '.'; |
169 |
|
|
char *hostname; |
170 |
|
|
int ident; /* process id to identify our packets */ |
171 |
|
|
int hoplimit = -1; /* hoplimit */ |
172 |
|
|
|
173 |
|
|
/* counters */ |
174 |
|
|
int64_t npackets; /* max packets to transmit */ |
175 |
|
|
int64_t nreceived; /* # of packets we got back */ |
176 |
|
|
int64_t nrepeats; /* number of duplicates */ |
177 |
|
|
int64_t ntransmitted; /* sequence # for outbound packets = #sent */ |
178 |
|
|
int64_t nmissedmax = 1; /* max value of ntransmitted - nreceived - 1 */ |
179 |
|
|
struct timeval interval = {1, 0}; /* interval between packets */ |
180 |
|
|
|
181 |
|
|
/* timing */ |
182 |
|
|
int timing; /* flag to do timing */ |
183 |
|
|
unsigned int maxwait = MAXWAIT_DEFAULT; /* max seconds to wait for response */ |
184 |
|
|
double tmin = 999999999.0; /* minimum round trip time */ |
185 |
|
|
double tmax = 0.0; /* maximum round trip time */ |
186 |
|
|
double tsum = 0.0; /* sum of all times, for doing average */ |
187 |
|
|
double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ |
188 |
|
|
struct tv64 tv64_offset; /* random offset for time values */ |
189 |
|
|
SIPHASH_KEY mac_key; |
190 |
|
|
|
191 |
|
|
/* for ancillary data(advanced API) */ |
192 |
|
|
struct msghdr smsghdr; |
193 |
|
|
struct iovec smsgiov; |
194 |
|
|
|
195 |
|
|
volatile sig_atomic_t seenalrm; |
196 |
|
|
volatile sig_atomic_t seenint; |
197 |
|
|
volatile sig_atomic_t seeninfo; |
198 |
|
|
|
199 |
|
|
int main(int, char *[]); |
200 |
|
|
void fill(char *, char *); |
201 |
|
|
int get_hoplim(struct msghdr *); |
202 |
|
|
int get_pathmtu(struct msghdr *); |
203 |
|
|
struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); |
204 |
|
|
void onsignal(int); |
205 |
|
|
void retransmit(void); |
206 |
|
|
void onint(int); |
207 |
|
|
int pinger(void); |
208 |
|
|
const char *pr_addr(struct sockaddr *, socklen_t); |
209 |
|
|
void pr_icmph(struct icmp6_hdr *, u_char *); |
210 |
|
|
void pr_iph(struct ip6_hdr *); |
211 |
|
|
void pr_pack(u_char *, int, struct msghdr *); |
212 |
|
|
void pr_exthdrs(struct msghdr *); |
213 |
|
|
void pr_ip6opt(void *); |
214 |
|
|
void pr_rthdr(void *); |
215 |
|
|
void pr_retip(struct ip6_hdr *, u_char *); |
216 |
|
|
void summary(int); |
217 |
|
|
void usage(void); |
218 |
|
|
|
219 |
|
|
int |
220 |
|
|
main(int argc, char *argv[]) |
221 |
|
|
{ |
222 |
|
|
struct addrinfo *res0; |
223 |
|
|
struct itimerval itimer; |
224 |
|
|
struct sockaddr_in6 from, from6; |
225 |
|
|
struct addrinfo hints; |
226 |
|
|
int64_t preload; |
227 |
|
|
int ch, i, maxsize, packlen, optval, error; |
228 |
|
|
socklen_t maxsizelen; |
229 |
|
|
u_char *datap, *packet; |
230 |
|
|
char *e, *target; |
231 |
|
|
char *source = NULL; |
232 |
|
|
const char *errstr; |
233 |
|
|
struct cmsghdr *scmsg = NULL; |
234 |
|
|
struct in6_pktinfo *pktinfo = NULL; |
235 |
|
|
double intval; |
236 |
|
|
int mflag = 0, loop = 1; |
237 |
|
|
uid_t uid; |
238 |
|
|
u_int rtableid = 0; |
239 |
|
|
|
240 |
|
|
if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) |
241 |
|
|
err(1, "socket"); |
242 |
|
|
|
243 |
|
|
/* revoke privs */ |
244 |
|
|
uid = getuid(); |
245 |
|
|
if (setresuid(uid, uid, uid) == -1) |
246 |
|
|
err(1, "setresuid"); |
247 |
|
|
|
248 |
|
|
preload = 0; |
249 |
|
|
datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; |
250 |
|
|
while ((ch = getopt(argc, argv, |
251 |
|
|
"c:dEefHh:I:i:Ll:mNnp:qS:s:V:vw:")) != -1) { |
252 |
|
|
switch (ch) { |
253 |
|
|
case 'c': |
254 |
|
|
npackets = strtonum(optarg, 0, INT64_MAX, &errstr); |
255 |
|
|
if (errstr) |
256 |
|
|
errx(1, |
257 |
|
|
"number of packets to transmit is %s: %s", |
258 |
|
|
errstr, optarg); |
259 |
|
|
break; |
260 |
|
|
case 'd': |
261 |
|
|
options |= F_SO_DEBUG; |
262 |
|
|
break; |
263 |
|
|
case 'E': |
264 |
|
|
options |= F_AUD_MISS; |
265 |
|
|
break; |
266 |
|
|
case 'e': |
267 |
|
|
options |= F_AUD_RECV; |
268 |
|
|
break; |
269 |
|
|
case 'f': |
270 |
|
|
if (getuid()) |
271 |
|
|
errx(1, "%s", strerror(EPERM)); |
272 |
|
|
options |= F_FLOOD; |
273 |
|
|
setvbuf(stdout, NULL, _IONBF, 0); |
274 |
|
|
break; |
275 |
|
|
case 'H': |
276 |
|
|
options |= F_HOSTNAME; |
277 |
|
|
break; |
278 |
|
|
case 'h': /* hoplimit */ |
279 |
|
|
hoplimit = strtonum(optarg, 0, IPV6_MAXHLIM, &errstr); |
280 |
|
|
if (errstr) |
281 |
|
|
errx(1, "hoplimit is %s: %s", errstr, optarg); |
282 |
|
|
break; |
283 |
|
|
case 'I': |
284 |
|
|
case 'S': /* deprecated */ |
285 |
|
|
source = optarg; |
286 |
|
|
break; |
287 |
|
|
case 'i': /* wait between sending packets */ |
288 |
|
|
intval = strtod(optarg, &e); |
289 |
|
|
if (*optarg == '\0' || *e != '\0') |
290 |
|
|
errx(1, "illegal timing interval %s", optarg); |
291 |
|
|
if (intval < 1 && getuid()) { |
292 |
|
|
errx(1, "%s: only root may use interval < 1s", |
293 |
|
|
strerror(EPERM)); |
294 |
|
|
} |
295 |
|
|
interval.tv_sec = (time_t)intval; |
296 |
|
|
interval.tv_usec = |
297 |
|
|
(long)((intval - interval.tv_sec) * 1000000); |
298 |
|
|
if (interval.tv_sec < 0) |
299 |
|
|
errx(1, "illegal timing interval %s", optarg); |
300 |
|
|
/* less than 1/Hz does not make sense */ |
301 |
|
|
if (interval.tv_sec == 0 && interval.tv_usec < 10000) { |
302 |
|
|
warnx("too small interval, raised to 0.01"); |
303 |
|
|
interval.tv_usec = 10000; |
304 |
|
|
} |
305 |
|
|
options |= F_INTERVAL; |
306 |
|
|
break; |
307 |
|
|
case 'L': |
308 |
|
|
moptions |= MULTICAST_NOLOOP; |
309 |
|
|
loop = 0; |
310 |
|
|
break; |
311 |
|
|
case 'l': |
312 |
|
|
if (getuid()) |
313 |
|
|
errx(1, "%s", strerror(EPERM)); |
314 |
|
|
preload = strtonum(optarg, 1, INT64_MAX, &errstr); |
315 |
|
|
if (errstr) |
316 |
|
|
errx(1, "preload value is %s: %s", errstr, |
317 |
|
|
optarg); |
318 |
|
|
break; |
319 |
|
|
case 'm': |
320 |
|
|
mflag++; |
321 |
|
|
break; |
322 |
|
|
case 'n': |
323 |
|
|
options &= ~F_HOSTNAME; |
324 |
|
|
break; |
325 |
|
|
case 'p': /* fill buffer with user pattern */ |
326 |
|
|
options |= F_PINGFILLED; |
327 |
|
|
fill((char *)datap, optarg); |
328 |
|
|
break; |
329 |
|
|
case 'q': |
330 |
|
|
options |= F_QUIET; |
331 |
|
|
break; |
332 |
|
|
case 's': /* size of packet to send */ |
333 |
|
|
datalen = strtonum(optarg, 0, MAXPAYLOAD, &errstr); |
334 |
|
|
if (errstr) |
335 |
|
|
errx(1, "packet size is %s: %s", errstr, |
336 |
|
|
optarg); |
337 |
|
|
break; |
338 |
|
|
case 'V': |
339 |
|
|
rtableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); |
340 |
|
|
if (errstr) |
341 |
|
|
errx(1, "rtable value is %s: %s", errstr, |
342 |
|
|
optarg); |
343 |
|
|
if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rtableid, |
344 |
|
|
sizeof(rtableid)) == -1) |
345 |
|
|
err(1, "setsockopt SO_RTABLE"); |
346 |
|
|
break; |
347 |
|
|
case 'v': |
348 |
|
|
options |= F_VERBOSE; |
349 |
|
|
break; |
350 |
|
|
case 'w': |
351 |
|
|
maxwait = strtonum(optarg, 1, INT_MAX, &errstr); |
352 |
|
|
if (errstr) |
353 |
|
|
errx(1, "maxwait value is %s: %s", |
354 |
|
|
errstr, optarg); |
355 |
|
|
break; |
356 |
|
|
default: |
357 |
|
|
usage(); |
358 |
|
|
/*NOTREACHED*/ |
359 |
|
|
} |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
argc -= optind; |
363 |
|
|
argv += optind; |
364 |
|
|
|
365 |
|
|
if (argc < 1) { |
366 |
|
|
usage(); |
367 |
|
|
/*NOTREACHED*/ |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
if (argc != 1) |
371 |
|
|
usage(); |
372 |
|
|
|
373 |
|
|
target = argv[argc - 1]; |
374 |
|
|
|
375 |
|
|
/* getaddrinfo */ |
376 |
|
|
memset(&hints, 0, sizeof(struct addrinfo)); |
377 |
|
|
hints.ai_flags = AI_CANONNAME; |
378 |
|
|
hints.ai_family = AF_INET6; |
379 |
|
|
hints.ai_socktype = SOCK_RAW; |
380 |
|
|
hints.ai_protocol = IPPROTO_ICMPV6; |
381 |
|
|
|
382 |
|
|
error = getaddrinfo(target, NULL, &hints, &res0); |
383 |
|
|
if (error) |
384 |
|
|
errx(1, "host %s: %s", target, gai_strerror(error)); |
385 |
|
|
if (res0->ai_canonname) { |
386 |
|
|
if ((hostname = strdup(res0->ai_canonname)) == NULL) |
387 |
|
|
errx(1, "out of memory"); |
388 |
|
|
} else |
389 |
|
|
hostname = target; |
390 |
|
|
if (res0->ai_next && (options & F_VERBOSE)) |
391 |
|
|
warnx("host resolves to multiple addresses"); |
392 |
|
|
if (res0->ai_family != AF_INET6 || res0->ai_addrlen != sizeof(dst)) |
393 |
|
|
errx(1, "getaddrinfo failed"); |
394 |
|
|
memcpy(&dst, res0->ai_addr, sizeof(dst)); |
395 |
|
|
|
396 |
|
|
freeaddrinfo(res0); |
397 |
|
|
|
398 |
|
|
/* set the source address if specified. */ |
399 |
|
|
if (source) { |
400 |
|
|
memset(&hints, 0, sizeof(struct addrinfo)); |
401 |
|
|
hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */ |
402 |
|
|
hints.ai_family = AF_INET6; |
403 |
|
|
hints.ai_socktype = SOCK_RAW; |
404 |
|
|
hints.ai_protocol = IPPROTO_ICMPV6; |
405 |
|
|
|
406 |
|
|
error = getaddrinfo(source, NULL, &hints, &res0); |
407 |
|
|
if (error) |
408 |
|
|
errx(1, "invalid source address: %s", |
409 |
|
|
gai_strerror(error)); |
410 |
|
|
|
411 |
|
|
if (res0->ai_family != AF_INET6 || res0->ai_addrlen != |
412 |
|
|
sizeof(from6)) |
413 |
|
|
errx(1, "invalid source address"); |
414 |
|
|
memcpy(&from6, res0->ai_addr, sizeof(from6)); |
415 |
|
|
freeaddrinfo(res0); |
416 |
|
|
if (bind(s, (struct sockaddr *)&from6, sizeof(from6)) != 0) |
417 |
|
|
err(1, "bind"); |
418 |
|
|
} |
419 |
|
|
|
420 |
|
|
/* |
421 |
|
|
* let the kernel pass extension headers of incoming packets, |
422 |
|
|
* for privileged socket options |
423 |
|
|
*/ |
424 |
|
|
if ((options & F_VERBOSE) != 0) { |
425 |
|
|
int opton = 1; |
426 |
|
|
|
427 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, |
428 |
|
|
(socklen_t)sizeof(opton))) |
429 |
|
|
err(1, "setsockopt(IPV6_RECVHOPOPTS)"); |
430 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, |
431 |
|
|
(socklen_t)sizeof(opton))) |
432 |
|
|
err(1, "setsockopt(IPV6_RECVDSTOPTS)"); |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
if ((options & F_FLOOD) && (options & F_INTERVAL)) |
436 |
|
|
errx(1, "-f and -i incompatible options"); |
437 |
|
|
|
438 |
|
|
if ((options & F_FLOOD) && (options & (F_AUD_RECV | F_AUD_MISS))) |
439 |
|
|
warnx("No audible output for flood pings"); |
440 |
|
|
|
441 |
|
|
if ((moptions & MULTICAST_NOLOOP) && |
442 |
|
|
setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, |
443 |
|
|
sizeof(loop)) < 0) |
444 |
|
|
err(1, "setsockopt IP6_MULTICAST_LOOP"); |
445 |
|
|
|
446 |
|
|
if (datalen >= sizeof(struct payload)) { |
447 |
|
|
/* we can time transfer */ |
448 |
|
|
timing = 1; |
449 |
|
|
} else |
450 |
|
|
timing = 0; |
451 |
|
|
/* in F_VERBOSE case, we may get non-echoreply packets*/ |
452 |
|
|
if (options & F_VERBOSE) |
453 |
|
|
packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA; /* XXX 2048? */ |
454 |
|
|
else |
455 |
|
|
packlen = datalen + IP6LEN + ICMP6ECHOLEN + EXTRA; |
456 |
|
|
|
457 |
|
|
if (!(packet = malloc(packlen))) |
458 |
|
|
err(1, "Unable to allocate packet"); |
459 |
|
|
|
460 |
|
|
/* |
461 |
|
|
* When trying to send large packets, you must increase the |
462 |
|
|
* size of both the send and receive buffers... |
463 |
|
|
*/ |
464 |
|
|
maxsizelen = sizeof maxsize; |
465 |
|
|
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &maxsize, &maxsizelen) < 0) |
466 |
|
|
err(1, "getsockopt"); |
467 |
|
|
if (maxsize < packlen && |
468 |
|
|
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(maxsize)) < 0) |
469 |
|
|
err(1, "setsockopt"); |
470 |
|
|
|
471 |
|
|
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &maxsize, &maxsizelen) < 0) |
472 |
|
|
err(1, "getsockopt"); |
473 |
|
|
if (maxsize < packlen && |
474 |
|
|
setsockopt(s, SOL_SOCKET, SO_RCVBUF, &packlen, sizeof(maxsize)) < 0) |
475 |
|
|
err(1, "setsockopt"); |
476 |
|
|
|
477 |
|
|
if (!(options & F_PINGFILLED)) |
478 |
|
|
for (i = ICMP6ECHOLEN; i < packlen; ++i) |
479 |
|
|
*datap++ = i; |
480 |
|
|
|
481 |
|
|
ident = getpid() & 0xFFFF; |
482 |
|
|
|
483 |
|
|
optval = 1; |
484 |
|
|
|
485 |
|
|
if (options & F_SO_DEBUG) |
486 |
|
|
(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, &optval, |
487 |
|
|
(socklen_t)sizeof(optval)); |
488 |
|
|
optval = IPV6_DEFHLIM; |
489 |
|
|
if (IN6_IS_ADDR_MULTICAST(&dst.sin6_addr)) |
490 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, |
491 |
|
|
&optval, (socklen_t)sizeof(optval)) == -1) |
492 |
|
|
err(1, "IPV6_MULTICAST_HOPS"); |
493 |
|
|
if (mflag != 1) { |
494 |
|
|
optval = mflag > 1 ? 0 : 1; |
495 |
|
|
|
496 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, |
497 |
|
|
&optval, (socklen_t)sizeof(optval)) == -1) |
498 |
|
|
err(1, "setsockopt(IPV6_USE_MIN_MTU)"); |
499 |
|
|
} else { |
500 |
|
|
optval = 1; |
501 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU, |
502 |
|
|
&optval, sizeof(optval)) == -1) |
503 |
|
|
err(1, "setsockopt(IPV6_RECVPATHMTU)"); |
504 |
|
|
} |
505 |
|
|
|
506 |
|
|
|
507 |
|
|
{ |
508 |
|
|
struct icmp6_filter filt; |
509 |
|
|
if (!(options & F_VERBOSE)) { |
510 |
|
|
ICMP6_FILTER_SETBLOCKALL(&filt); |
511 |
|
|
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); |
512 |
|
|
} else { |
513 |
|
|
ICMP6_FILTER_SETPASSALL(&filt); |
514 |
|
|
} |
515 |
|
|
if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, |
516 |
|
|
(socklen_t)sizeof(filt)) < 0) |
517 |
|
|
err(1, "setsockopt(ICMP6_FILTER)"); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
/* let the kernel pass extension headers of incoming packets */ |
521 |
|
|
if ((options & F_VERBOSE) != 0) { |
522 |
|
|
int opton = 1; |
523 |
|
|
|
524 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, |
525 |
|
|
sizeof(opton))) |
526 |
|
|
err(1, "setsockopt(IPV6_RECVRTHDR)"); |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
if (hoplimit != -1) { |
530 |
|
|
/* set IP6 packet options */ |
531 |
|
|
if ((scmsg = malloc( CMSG_SPACE(sizeof(int)))) == NULL) |
532 |
|
|
errx(1, "can't allocate enough memory"); |
533 |
|
|
smsghdr.msg_control = (caddr_t)scmsg; |
534 |
|
|
smsghdr.msg_controllen = CMSG_SPACE(sizeof(int)); |
535 |
|
|
|
536 |
|
|
scmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
537 |
|
|
scmsg->cmsg_level = IPPROTO_IPV6; |
538 |
|
|
scmsg->cmsg_type = IPV6_HOPLIMIT; |
539 |
|
|
*(int *)(CMSG_DATA(scmsg)) = hoplimit; |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
if (!source && options & F_VERBOSE) { |
543 |
|
|
/* |
544 |
|
|
* get the source address. XXX since we revoked the root |
545 |
|
|
* privilege, we cannot use a raw socket for this. |
546 |
|
|
*/ |
547 |
|
|
int dummy; |
548 |
|
|
socklen_t len = sizeof(from6); |
549 |
|
|
|
550 |
|
|
if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) |
551 |
|
|
err(1, "UDP socket"); |
552 |
|
|
|
553 |
|
|
from6.sin6_family = AF_INET6; |
554 |
|
|
from6.sin6_addr = dst.sin6_addr; |
555 |
|
|
from6.sin6_port = ntohs(DUMMY_PORT); |
556 |
|
|
from6.sin6_scope_id = dst.sin6_scope_id; |
557 |
|
|
|
558 |
|
|
if (pktinfo && |
559 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, |
560 |
|
|
(void *)pktinfo, sizeof(*pktinfo))) |
561 |
|
|
err(1, "UDP setsockopt(IPV6_PKTINFO)"); |
562 |
|
|
|
563 |
|
|
if (hoplimit != -1 && |
564 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS, |
565 |
|
|
(void *)&hoplimit, sizeof(hoplimit))) |
566 |
|
|
err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)"); |
567 |
|
|
|
568 |
|
|
if (hoplimit != -1 && |
569 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, |
570 |
|
|
(void *)&hoplimit, sizeof(hoplimit))) |
571 |
|
|
err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)"); |
572 |
|
|
|
573 |
|
|
if (rtableid > 0 && |
574 |
|
|
setsockopt(dummy, SOL_SOCKET, SO_RTABLE, &rtableid, |
575 |
|
|
sizeof(rtableid)) < 0) |
576 |
|
|
err(1, "setsockopt(SO_RTABLE)"); |
577 |
|
|
|
578 |
|
|
if (connect(dummy, (struct sockaddr *)&from6, len) < 0) |
579 |
|
|
err(1, "UDP connect"); |
580 |
|
|
|
581 |
|
|
if (getsockname(dummy, (struct sockaddr *)&from6, &len) < 0) |
582 |
|
|
err(1, "getsockname"); |
583 |
|
|
|
584 |
|
|
close(dummy); |
585 |
|
|
} |
586 |
|
|
|
587 |
|
|
optval = 1; |
588 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, |
589 |
|
|
(socklen_t)sizeof(optval)) < 0) |
590 |
|
|
warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ |
591 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, |
592 |
|
|
(socklen_t)sizeof(optval)) < 0) |
593 |
|
|
warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ |
594 |
|
|
|
595 |
|
|
if (options & F_HOSTNAME) { |
596 |
|
|
if (pledge("stdio inet dns rpath cpath wpath", NULL) == -1) |
597 |
|
|
err(1, "pledge"); |
598 |
|
|
} else { |
599 |
|
|
if (pledge("stdio inet rpath cpath wpath", NULL) == -1) |
600 |
|
|
err(1, "pledge"); |
601 |
|
|
} |
602 |
|
|
|
603 |
|
|
arc4random_buf(&tv64_offset, sizeof(tv64_offset)); |
604 |
|
|
arc4random_buf(&mac_key, sizeof(mac_key)); |
605 |
|
|
|
606 |
|
|
printf("PING6 %s (", hostname); |
607 |
|
|
if (options & F_VERBOSE) |
608 |
|
|
printf("%s --> ", pr_addr((struct sockaddr *)&from6, |
609 |
|
|
sizeof(from6))); |
610 |
|
|
printf("%s): %d data bytes\n", pr_addr((struct sockaddr *)&dst, |
611 |
|
|
sizeof(dst)), datalen); |
612 |
|
|
|
613 |
|
|
while (preload--) /* Fire off them quickies. */ |
614 |
|
|
(void)pinger(); |
615 |
|
|
|
616 |
|
|
(void)signal(SIGINT, onsignal); |
617 |
|
|
(void)signal(SIGINFO, onsignal); |
618 |
|
|
|
619 |
|
|
if ((options & F_FLOOD) == 0) { |
620 |
|
|
(void)signal(SIGALRM, onsignal); |
621 |
|
|
itimer.it_interval = interval; |
622 |
|
|
itimer.it_value = interval; |
623 |
|
|
(void)setitimer(ITIMER_REAL, &itimer, NULL); |
624 |
|
|
if (ntransmitted == 0) |
625 |
|
|
retransmit(); |
626 |
|
|
} |
627 |
|
|
|
628 |
|
|
seenalrm = seenint = 0; |
629 |
|
|
seeninfo = 0; |
630 |
|
|
|
631 |
|
|
for (;;) { |
632 |
|
|
struct msghdr m; |
633 |
|
|
union { |
634 |
|
|
struct cmsghdr hdr; |
635 |
|
|
u_char buf[CMSG_SPACE(1024)]; |
636 |
|
|
} cmsgbuf; |
637 |
|
|
struct iovec iov[1]; |
638 |
|
|
struct pollfd pfd; |
639 |
|
|
ssize_t cc; |
640 |
|
|
int timeout; |
641 |
|
|
|
642 |
|
|
/* signal handling */ |
643 |
|
|
if (seenalrm) { |
644 |
|
|
retransmit(); |
645 |
|
|
seenalrm = 0; |
646 |
|
|
if (ntransmitted - nreceived - 1 > nmissedmax) { |
647 |
|
|
nmissedmax = ntransmitted - nreceived - 1; |
648 |
|
|
if (!(options & F_FLOOD) && |
649 |
|
|
(options & F_AUD_MISS)) |
650 |
|
|
(void)fputc('\a', stderr); |
651 |
|
|
} |
652 |
|
|
continue; |
653 |
|
|
} |
654 |
|
|
if (seenint) { |
655 |
|
|
onint(SIGINT); |
656 |
|
|
seenint = 0; |
657 |
|
|
continue; |
658 |
|
|
} |
659 |
|
|
if (seeninfo) { |
660 |
|
|
summary(0); |
661 |
|
|
seeninfo = 0; |
662 |
|
|
continue; |
663 |
|
|
} |
664 |
|
|
|
665 |
|
|
if (options & F_FLOOD) { |
666 |
|
|
(void)pinger(); |
667 |
|
|
timeout = 10; |
668 |
|
|
} else |
669 |
|
|
timeout = INFTIM; |
670 |
|
|
|
671 |
|
|
pfd.fd = s; |
672 |
|
|
pfd.events = POLLIN; |
673 |
|
|
|
674 |
|
|
if (poll(&pfd, 1, timeout) <= 0) |
675 |
|
|
continue; |
676 |
|
|
|
677 |
|
|
m.msg_name = &from; |
678 |
|
|
m.msg_namelen = sizeof(from); |
679 |
|
|
memset(&iov, 0, sizeof(iov)); |
680 |
|
|
iov[0].iov_base = (caddr_t)packet; |
681 |
|
|
iov[0].iov_len = packlen; |
682 |
|
|
m.msg_iov = iov; |
683 |
|
|
m.msg_iovlen = 1; |
684 |
|
|
m.msg_control = (caddr_t)&cmsgbuf.buf; |
685 |
|
|
m.msg_controllen = sizeof(cmsgbuf.buf); |
686 |
|
|
|
687 |
|
|
cc = recvmsg(s, &m, 0); |
688 |
|
|
if (cc < 0) { |
689 |
|
|
if (errno != EINTR) { |
690 |
|
|
warn("recvmsg"); |
691 |
|
|
sleep(1); |
692 |
|
|
} |
693 |
|
|
continue; |
694 |
|
|
} else if (cc == 0) { |
695 |
|
|
int mtu; |
696 |
|
|
|
697 |
|
|
/* |
698 |
|
|
* receive control messages only. Process the |
699 |
|
|
* exceptions (currently the only possibility is |
700 |
|
|
* a path MTU notification.) |
701 |
|
|
*/ |
702 |
|
|
if ((mtu = get_pathmtu(&m)) > 0) { |
703 |
|
|
if ((options & F_VERBOSE) != 0) { |
704 |
|
|
printf("new path MTU (%d) is " |
705 |
|
|
"notified\n", mtu); |
706 |
|
|
} |
707 |
|
|
} |
708 |
|
|
continue; |
709 |
|
|
} else { |
710 |
|
|
/* |
711 |
|
|
* an ICMPv6 message (probably an echoreply) arrived. |
712 |
|
|
*/ |
713 |
|
|
pr_pack(packet, cc, &m); |
714 |
|
|
} |
715 |
|
|
if (npackets && nreceived >= npackets) |
716 |
|
|
break; |
717 |
|
|
} |
718 |
|
|
summary(0); |
719 |
|
|
exit(nreceived == 0); |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
void |
723 |
|
|
onsignal(int sig) |
724 |
|
|
{ |
725 |
|
|
|
726 |
|
|
switch (sig) { |
727 |
|
|
case SIGALRM: |
728 |
|
|
seenalrm++; |
729 |
|
|
break; |
730 |
|
|
case SIGINT: |
731 |
|
|
seenint++; |
732 |
|
|
break; |
733 |
|
|
case SIGINFO: |
734 |
|
|
seeninfo++; |
735 |
|
|
break; |
736 |
|
|
} |
737 |
|
|
} |
738 |
|
|
|
739 |
|
|
/* |
740 |
|
|
* retransmit -- |
741 |
|
|
* This routine transmits another ping6. |
742 |
|
|
*/ |
743 |
|
|
void |
744 |
|
|
retransmit(void) |
745 |
|
|
{ |
746 |
|
|
struct itimerval itimer; |
747 |
|
|
|
748 |
|
|
if (pinger() == 0) |
749 |
|
|
return; |
750 |
|
|
|
751 |
|
|
/* |
752 |
|
|
* If we're not transmitting any more packets, change the timer |
753 |
|
|
* to wait two round-trip times if we've received any packets or |
754 |
|
|
* maxwait seconds if we haven't. |
755 |
|
|
*/ |
756 |
|
|
if (nreceived) { |
757 |
|
|
itimer.it_value.tv_sec = 2 * tmax / 1000; |
758 |
|
|
if (itimer.it_value.tv_sec == 0) |
759 |
|
|
itimer.it_value.tv_sec = 1; |
760 |
|
|
} else |
761 |
|
|
itimer.it_value.tv_sec = maxwait; |
762 |
|
|
itimer.it_interval.tv_sec = 0; |
763 |
|
|
itimer.it_interval.tv_usec = 0; |
764 |
|
|
itimer.it_value.tv_usec = 0; |
765 |
|
|
|
766 |
|
|
(void)signal(SIGALRM, onint); |
767 |
|
|
(void)setitimer(ITIMER_REAL, &itimer, NULL); |
768 |
|
|
} |
769 |
|
|
|
770 |
|
|
/* |
771 |
|
|
* pinger -- |
772 |
|
|
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet |
773 |
|
|
* will be added on by the kernel. The ID field is our UNIX process ID, |
774 |
|
|
* and the sequence number is an ascending integer. The first 8 bytes |
775 |
|
|
* of the data portion are used to hold a UNIX "timeval" struct in VAX |
776 |
|
|
* byte-order, to compute the round-trip time. |
777 |
|
|
*/ |
778 |
|
|
|
779 |
|
|
int |
780 |
|
|
pinger(void) |
781 |
|
|
{ |
782 |
|
|
struct icmp6_hdr *icp; |
783 |
|
|
int i, cc; |
784 |
|
|
int seq; |
785 |
|
|
|
786 |
|
|
if (npackets && ntransmitted >= npackets) |
787 |
|
|
return(-1); /* no more transmission */ |
788 |
|
|
|
789 |
|
|
icp = (struct icmp6_hdr *)outpack; |
790 |
|
|
memset(icp, 0, sizeof(*icp)); |
791 |
|
|
icp->icmp6_cksum = 0; |
792 |
|
|
seq = ntransmitted++; |
793 |
|
|
CLR(seq % mx_dup_ck); |
794 |
|
|
|
795 |
|
|
icp->icmp6_type = ICMP6_ECHO_REQUEST; |
796 |
|
|
icp->icmp6_code = 0; |
797 |
|
|
icp->icmp6_id = htons(ident); |
798 |
|
|
icp->icmp6_seq = ntohs(seq); |
799 |
|
|
if (timing) { |
800 |
|
|
SIPHASH_CTX ctx; |
801 |
|
|
struct timespec ts; |
802 |
|
|
struct payload payload; |
803 |
|
|
struct tv64 *tv64 = &payload.tv64; |
804 |
|
|
|
805 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) |
806 |
|
|
err(1, "clock_gettime(CLOCK_MONOTONIC)"); |
807 |
|
|
tv64->tv64_sec = htobe64((u_int64_t)ts.tv_sec + |
808 |
|
|
tv64_offset.tv64_sec); |
809 |
|
|
tv64->tv64_nsec = htobe64((u_int64_t)ts.tv_nsec + |
810 |
|
|
tv64_offset.tv64_nsec); |
811 |
|
|
|
812 |
|
|
SipHash24_Init(&ctx, &mac_key); |
813 |
|
|
SipHash24_Update(&ctx, tv64, sizeof(*tv64)); |
814 |
|
|
SipHash24_Update(&ctx, &ident, sizeof(ident)); |
815 |
|
|
SipHash24_Update(&ctx, |
816 |
|
|
&icp->icmp6_seq, sizeof(icp->icmp6_seq)); |
817 |
|
|
SipHash24_Final(&payload.mac, &ctx); |
818 |
|
|
|
819 |
|
|
memcpy(&outpack[ICMP6ECHOLEN], |
820 |
|
|
&payload, sizeof(payload)); |
821 |
|
|
} |
822 |
|
|
cc = ICMP6ECHOLEN + datalen; |
823 |
|
|
|
824 |
|
|
smsghdr.msg_name = &dst; |
825 |
|
|
smsghdr.msg_namelen = sizeof(dst); |
826 |
|
|
smsgiov.iov_base = (caddr_t)outpack; |
827 |
|
|
smsgiov.iov_len = cc; |
828 |
|
|
smsghdr.msg_iov = &smsgiov; |
829 |
|
|
smsghdr.msg_iovlen = 1; |
830 |
|
|
|
831 |
|
|
i = sendmsg(s, &smsghdr, 0); |
832 |
|
|
|
833 |
|
|
if (i < 0 || i != cc) { |
834 |
|
|
if (i < 0) |
835 |
|
|
warn("sendmsg"); |
836 |
|
|
(void)printf("ping6: wrote %s %d chars, ret=%d\n", |
837 |
|
|
hostname, cc, i); |
838 |
|
|
} |
839 |
|
|
if (!(options & F_QUIET) && options & F_FLOOD) |
840 |
|
|
(void)write(STDOUT_FILENO, &DOT, 1); |
841 |
|
|
|
842 |
|
|
return(0); |
843 |
|
|
} |
844 |
|
|
|
845 |
|
|
#define MINIMUM(a,b) (((a)<(b))?(a):(b)) |
846 |
|
|
|
847 |
|
|
/* |
848 |
|
|
* pr_pack -- |
849 |
|
|
* Print out the packet, if it came from us. This logic is necessary |
850 |
|
|
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets |
851 |
|
|
* which arrive ('tis only fair). This permits multiple copies of this |
852 |
|
|
* program to be run without having intermingled output (or statistics!). |
853 |
|
|
*/ |
854 |
|
|
void |
855 |
|
|
pr_pack(u_char *buf, int cc, struct msghdr *mhdr) |
856 |
|
|
{ |
857 |
|
|
#define safeputc(c) printf((isprint((c)) ? "%c" : "\\%03o"), c) |
858 |
|
|
struct icmp6_hdr *icp; |
859 |
|
|
int i; |
860 |
|
|
int hoplim; |
861 |
|
|
struct sockaddr *from; |
862 |
|
|
socklen_t fromlen; |
863 |
|
|
u_char *cp = NULL, *dp, *end = buf + cc; |
864 |
|
|
struct in6_pktinfo *pktinfo = NULL; |
865 |
|
|
struct timespec ts, tp; |
866 |
|
|
struct payload payload; |
867 |
|
|
struct tv64 *tv64; |
868 |
|
|
double triptime = 0; |
869 |
|
|
int dupflag; |
870 |
|
|
u_int16_t seq; |
871 |
|
|
|
872 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) |
873 |
|
|
err(1, "clock_gettime(CLOCK_MONOTONIC)"); |
874 |
|
|
|
875 |
|
|
if (!mhdr || !mhdr->msg_name || |
876 |
|
|
mhdr->msg_namelen != sizeof(struct sockaddr_in6) || |
877 |
|
|
((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET6) { |
878 |
|
|
if (options & F_VERBOSE) |
879 |
|
|
warnx("invalid peername"); |
880 |
|
|
return; |
881 |
|
|
} |
882 |
|
|
from = (struct sockaddr *)mhdr->msg_name; |
883 |
|
|
fromlen = mhdr->msg_namelen; |
884 |
|
|
if (cc < sizeof(struct icmp6_hdr)) { |
885 |
|
|
if (options & F_VERBOSE) |
886 |
|
|
warnx("packet too short (%d bytes) from %s", cc, |
887 |
|
|
pr_addr(from, fromlen)); |
888 |
|
|
return; |
889 |
|
|
} |
890 |
|
|
icp = (struct icmp6_hdr *)buf; |
891 |
|
|
|
892 |
|
|
if ((hoplim = get_hoplim(mhdr)) == -1) { |
893 |
|
|
warnx("failed to get receiving hop limit"); |
894 |
|
|
return; |
895 |
|
|
} |
896 |
|
|
if ((pktinfo = get_rcvpktinfo(mhdr)) == NULL) { |
897 |
|
|
warnx("failed to get receiving packet information"); |
898 |
|
|
return; |
899 |
|
|
} |
900 |
|
|
|
901 |
|
|
if (icp->icmp6_type == ICMP6_ECHO_REPLY) { |
902 |
|
|
if (ntohs(icp->icmp6_id) != ident) |
903 |
|
|
return; /* 'Twas not our ECHO */ |
904 |
|
|
seq = ntohs(icp->icmp6_seq); |
905 |
|
|
++nreceived; |
906 |
|
|
if (timing) { |
907 |
|
|
SIPHASH_CTX ctx; |
908 |
|
|
u_int8_t mac[SIPHASH_DIGEST_LENGTH]; |
909 |
|
|
|
910 |
|
|
if (cc - sizeof(*cp) < sizeof(payload)) { |
911 |
|
|
(void)printf("signature missing!\n"); |
912 |
|
|
return; |
913 |
|
|
} |
914 |
|
|
|
915 |
|
|
memcpy(&payload, icp + 1, sizeof(payload)); |
916 |
|
|
tv64 = &payload.tv64; |
917 |
|
|
|
918 |
|
|
SipHash24_Init(&ctx, &mac_key); |
919 |
|
|
SipHash24_Update(&ctx, tv64, sizeof(*tv64)); |
920 |
|
|
SipHash24_Update(&ctx, &ident, sizeof(ident)); |
921 |
|
|
SipHash24_Update(&ctx, |
922 |
|
|
&icp->icmp6_seq, sizeof(icp->icmp6_seq)); |
923 |
|
|
SipHash24_Final(mac, &ctx); |
924 |
|
|
|
925 |
|
|
if (timingsafe_memcmp(mac, &payload.mac, |
926 |
|
|
sizeof(mac)) != 0) { |
927 |
|
|
(void)printf("signature mismatch!\n"); |
928 |
|
|
return; |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
tp.tv_sec = betoh64(tv64->tv64_sec) - |
932 |
|
|
tv64_offset.tv64_sec; |
933 |
|
|
tp.tv_nsec = betoh64(tv64->tv64_nsec) - |
934 |
|
|
tv64_offset.tv64_nsec; |
935 |
|
|
timespecsub(&ts, &tp, &ts); |
936 |
|
|
triptime = ((double)ts.tv_sec) * 1000.0 + |
937 |
|
|
((double)ts.tv_nsec) / 1000000.0; |
938 |
|
|
tsum += triptime; |
939 |
|
|
tsumsq += triptime * triptime; |
940 |
|
|
if (triptime < tmin) |
941 |
|
|
tmin = triptime; |
942 |
|
|
if (triptime > tmax) |
943 |
|
|
tmax = triptime; |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
if (TST(seq % mx_dup_ck)) { |
947 |
|
|
++nrepeats; |
948 |
|
|
--nreceived; |
949 |
|
|
dupflag = 1; |
950 |
|
|
} else { |
951 |
|
|
SET(seq % mx_dup_ck); |
952 |
|
|
dupflag = 0; |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
if (options & F_QUIET) |
956 |
|
|
return; |
957 |
|
|
|
958 |
|
|
if (options & F_FLOOD) |
959 |
|
|
(void)write(STDOUT_FILENO, &BSPACE, 1); |
960 |
|
|
else { |
961 |
|
|
(void)printf("%d bytes from %s, icmp_seq=%u", cc, |
962 |
|
|
pr_addr(from, fromlen), seq); |
963 |
|
|
(void)printf(" hlim=%d", hoplim); |
964 |
|
|
if ((options & F_VERBOSE) != 0) { |
965 |
|
|
struct sockaddr_in6 dstsa; |
966 |
|
|
|
967 |
|
|
memset(&dstsa, 0, sizeof(dstsa)); |
968 |
|
|
dstsa.sin6_family = AF_INET6; |
969 |
|
|
dstsa.sin6_len = sizeof(dstsa); |
970 |
|
|
dstsa.sin6_scope_id = pktinfo->ipi6_ifindex; |
971 |
|
|
dstsa.sin6_addr = pktinfo->ipi6_addr; |
972 |
|
|
(void)printf(" dst=%s", |
973 |
|
|
pr_addr((struct sockaddr *)&dstsa, |
974 |
|
|
sizeof(dstsa))); |
975 |
|
|
} |
976 |
|
|
if (timing) |
977 |
|
|
(void)printf(" time=%.3f ms", triptime); |
978 |
|
|
if (dupflag) |
979 |
|
|
(void)printf("(DUP!)"); |
980 |
|
|
if (options & F_AUD_RECV) |
981 |
|
|
(void)fputc('\a', stderr); |
982 |
|
|
/* check the data */ |
983 |
|
|
cp = buf + ICMP6ECHOLEN + ICMP6ECHOTMLEN; |
984 |
|
|
dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN; |
985 |
|
|
if (cc != ICMP6ECHOLEN + datalen) { |
986 |
|
|
int delta = cc - (datalen + ICMP6ECHOLEN); |
987 |
|
|
|
988 |
|
|
(void)printf(" (%d bytes %s)", |
989 |
|
|
abs(delta), delta > 0 ? "extra" : "short"); |
990 |
|
|
end = buf + MINIMUM(cc, ICMP6ECHOLEN + datalen); |
991 |
|
|
} |
992 |
|
|
for (i = 8; cp < end; ++i, ++cp, ++dp) { |
993 |
|
|
if (*cp != *dp) { |
994 |
|
|
(void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp); |
995 |
|
|
break; |
996 |
|
|
} |
997 |
|
|
} |
998 |
|
|
} |
999 |
|
|
} else { |
1000 |
|
|
/* We've got something other than an ECHOREPLY */ |
1001 |
|
|
if (!(options & F_VERBOSE)) |
1002 |
|
|
return; |
1003 |
|
|
(void)printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); |
1004 |
|
|
pr_icmph(icp, end); |
1005 |
|
|
} |
1006 |
|
|
|
1007 |
|
|
if (!(options & F_FLOOD)) { |
1008 |
|
|
(void)putchar('\n'); |
1009 |
|
|
if (options & F_VERBOSE) |
1010 |
|
|
pr_exthdrs(mhdr); |
1011 |
|
|
(void)fflush(stdout); |
1012 |
|
|
} |
1013 |
|
|
#undef safeputc |
1014 |
|
|
} |
1015 |
|
|
|
1016 |
|
|
void |
1017 |
|
|
pr_exthdrs(struct msghdr *mhdr) |
1018 |
|
|
{ |
1019 |
|
|
struct cmsghdr *cm; |
1020 |
|
|
|
1021 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1022 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1023 |
|
|
if (cm->cmsg_level != IPPROTO_IPV6) |
1024 |
|
|
continue; |
1025 |
|
|
|
1026 |
|
|
switch (cm->cmsg_type) { |
1027 |
|
|
case IPV6_HOPOPTS: |
1028 |
|
|
printf(" HbH Options: "); |
1029 |
|
|
pr_ip6opt(CMSG_DATA(cm)); |
1030 |
|
|
break; |
1031 |
|
|
case IPV6_DSTOPTS: |
1032 |
|
|
case IPV6_RTHDRDSTOPTS: |
1033 |
|
|
printf(" Dst Options: "); |
1034 |
|
|
pr_ip6opt(CMSG_DATA(cm)); |
1035 |
|
|
break; |
1036 |
|
|
case IPV6_RTHDR: |
1037 |
|
|
printf(" Routing: "); |
1038 |
|
|
pr_rthdr(CMSG_DATA(cm)); |
1039 |
|
|
break; |
1040 |
|
|
} |
1041 |
|
|
} |
1042 |
|
|
} |
1043 |
|
|
|
1044 |
|
|
void |
1045 |
|
|
pr_ip6opt(void *extbuf) |
1046 |
|
|
{ |
1047 |
|
|
struct ip6_hbh *ext; |
1048 |
|
|
int currentlen; |
1049 |
|
|
u_int8_t type; |
1050 |
|
|
size_t extlen; |
1051 |
|
|
socklen_t len; |
1052 |
|
|
void *databuf; |
1053 |
|
|
u_int16_t value2; |
1054 |
|
|
u_int32_t value4; |
1055 |
|
|
|
1056 |
|
|
ext = (struct ip6_hbh *)extbuf; |
1057 |
|
|
extlen = (ext->ip6h_len + 1) * 8; |
1058 |
|
|
printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt, |
1059 |
|
|
(unsigned int)ext->ip6h_len, (unsigned long)extlen); |
1060 |
|
|
|
1061 |
|
|
currentlen = 0; |
1062 |
|
|
while (1) { |
1063 |
|
|
currentlen = inet6_opt_next(extbuf, extlen, currentlen, |
1064 |
|
|
&type, &len, &databuf); |
1065 |
|
|
if (currentlen == -1) |
1066 |
|
|
break; |
1067 |
|
|
switch (type) { |
1068 |
|
|
/* |
1069 |
|
|
* Note that inet6_opt_next automatically skips any padding |
1070 |
|
|
* options. |
1071 |
|
|
*/ |
1072 |
|
|
case IP6OPT_JUMBO: |
1073 |
|
|
inet6_opt_get_val(databuf, 0, &value4, sizeof(value4)); |
1074 |
|
|
printf(" Jumbo Payload Opt: Length %u\n", |
1075 |
|
|
(u_int32_t)ntohl(value4)); |
1076 |
|
|
break; |
1077 |
|
|
case IP6OPT_ROUTER_ALERT: |
1078 |
|
|
inet6_opt_get_val(databuf, 0, &value2, sizeof(value2)); |
1079 |
|
|
printf(" Router Alert Opt: Type %u\n", |
1080 |
|
|
ntohs(value2)); |
1081 |
|
|
break; |
1082 |
|
|
default: |
1083 |
|
|
printf(" Received Opt %u len %lu\n", |
1084 |
|
|
type, (unsigned long)len); |
1085 |
|
|
break; |
1086 |
|
|
} |
1087 |
|
|
} |
1088 |
|
|
return; |
1089 |
|
|
} |
1090 |
|
|
|
1091 |
|
|
void |
1092 |
|
|
pr_rthdr(void *extbuf) |
1093 |
|
|
{ |
1094 |
|
|
struct in6_addr *in6; |
1095 |
|
|
char ntopbuf[INET6_ADDRSTRLEN]; |
1096 |
|
|
struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf; |
1097 |
|
|
int i, segments; |
1098 |
|
|
|
1099 |
|
|
/* print fixed part of the header */ |
1100 |
|
|
printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt, |
1101 |
|
|
rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); |
1102 |
|
|
if ((segments = inet6_rth_segments(extbuf)) >= 0) |
1103 |
|
|
printf("%d segments, ", segments); |
1104 |
|
|
else |
1105 |
|
|
printf("segments unknown, "); |
1106 |
|
|
printf("%d left\n", rh->ip6r_segleft); |
1107 |
|
|
|
1108 |
|
|
for (i = 0; i < segments; i++) { |
1109 |
|
|
in6 = inet6_rth_getaddr(extbuf, i); |
1110 |
|
|
if (in6 == NULL) |
1111 |
|
|
printf(" [%d]<NULL>\n", i); |
1112 |
|
|
else { |
1113 |
|
|
if (!inet_ntop(AF_INET6, in6, ntopbuf, |
1114 |
|
|
sizeof(ntopbuf))) |
1115 |
|
|
strncpy(ntopbuf, "?", sizeof(ntopbuf)); |
1116 |
|
|
printf(" [%d]%s\n", i, ntopbuf); |
1117 |
|
|
} |
1118 |
|
|
} |
1119 |
|
|
|
1120 |
|
|
return; |
1121 |
|
|
|
1122 |
|
|
} |
1123 |
|
|
|
1124 |
|
|
int |
1125 |
|
|
get_hoplim(struct msghdr *mhdr) |
1126 |
|
|
{ |
1127 |
|
|
struct cmsghdr *cm; |
1128 |
|
|
|
1129 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1130 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1131 |
|
|
if (cm->cmsg_len == 0) |
1132 |
|
|
return(-1); |
1133 |
|
|
|
1134 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
1135 |
|
|
cm->cmsg_type == IPV6_HOPLIMIT && |
1136 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(int))) |
1137 |
|
|
return(*(int *)CMSG_DATA(cm)); |
1138 |
|
|
} |
1139 |
|
|
|
1140 |
|
|
return(-1); |
1141 |
|
|
} |
1142 |
|
|
|
1143 |
|
|
struct in6_pktinfo * |
1144 |
|
|
get_rcvpktinfo(struct msghdr *mhdr) |
1145 |
|
|
{ |
1146 |
|
|
struct cmsghdr *cm; |
1147 |
|
|
|
1148 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1149 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1150 |
|
|
if (cm->cmsg_len == 0) |
1151 |
|
|
return(NULL); |
1152 |
|
|
|
1153 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
1154 |
|
|
cm->cmsg_type == IPV6_PKTINFO && |
1155 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) |
1156 |
|
|
return((struct in6_pktinfo *)CMSG_DATA(cm)); |
1157 |
|
|
} |
1158 |
|
|
|
1159 |
|
|
return(NULL); |
1160 |
|
|
} |
1161 |
|
|
|
1162 |
|
|
int |
1163 |
|
|
get_pathmtu(struct msghdr *mhdr) |
1164 |
|
|
{ |
1165 |
|
|
struct cmsghdr *cm; |
1166 |
|
|
struct ip6_mtuinfo *mtuctl = NULL; |
1167 |
|
|
|
1168 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1169 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1170 |
|
|
if (cm->cmsg_len == 0) |
1171 |
|
|
return(0); |
1172 |
|
|
|
1173 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
1174 |
|
|
cm->cmsg_type == IPV6_PATHMTU && |
1175 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) { |
1176 |
|
|
mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm); |
1177 |
|
|
|
1178 |
|
|
/* |
1179 |
|
|
* If the notified destination is different from |
1180 |
|
|
* the one we are pinging, just ignore the info. |
1181 |
|
|
* We check the scope ID only when both notified value |
1182 |
|
|
* and our own value have non-0 values, because we may |
1183 |
|
|
* have used the default scope zone ID for sending, |
1184 |
|
|
* in which case the scope ID value is 0. |
1185 |
|
|
*/ |
1186 |
|
|
if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr, |
1187 |
|
|
&dst.sin6_addr) || |
1188 |
|
|
(mtuctl->ip6m_addr.sin6_scope_id && |
1189 |
|
|
dst.sin6_scope_id && |
1190 |
|
|
mtuctl->ip6m_addr.sin6_scope_id != |
1191 |
|
|
dst.sin6_scope_id)) { |
1192 |
|
|
if ((options & F_VERBOSE) != 0) { |
1193 |
|
|
printf("path MTU for %s is notified. " |
1194 |
|
|
"(ignored)\n", |
1195 |
|
|
pr_addr((struct sockaddr *)&mtuctl->ip6m_addr, |
1196 |
|
|
sizeof(mtuctl->ip6m_addr))); |
1197 |
|
|
} |
1198 |
|
|
return(0); |
1199 |
|
|
} |
1200 |
|
|
|
1201 |
|
|
/* |
1202 |
|
|
* Ignore an invalid MTU. XXX: can we just believe |
1203 |
|
|
* the kernel check? |
1204 |
|
|
*/ |
1205 |
|
|
if (mtuctl->ip6m_mtu < IPV6_MMTU) |
1206 |
|
|
return(0); |
1207 |
|
|
|
1208 |
|
|
/* notification for our destination. return the MTU. */ |
1209 |
|
|
return((int)mtuctl->ip6m_mtu); |
1210 |
|
|
} |
1211 |
|
|
} |
1212 |
|
|
return(0); |
1213 |
|
|
} |
1214 |
|
|
|
1215 |
|
|
/* |
1216 |
|
|
* onint -- |
1217 |
|
|
* SIGINT handler. |
1218 |
|
|
*/ |
1219 |
|
|
void |
1220 |
|
|
onint(int signo) |
1221 |
|
|
{ |
1222 |
|
|
summary(signo); |
1223 |
|
|
|
1224 |
|
|
if (signo) |
1225 |
|
|
_exit(nreceived ? 0 : 1); |
1226 |
|
|
else |
1227 |
|
|
exit(nreceived ? 0 : 1); |
1228 |
|
|
} |
1229 |
|
|
|
1230 |
|
|
/* |
1231 |
|
|
* summary -- |
1232 |
|
|
* Print out statistics. |
1233 |
|
|
*/ |
1234 |
|
|
void |
1235 |
|
|
summary(int signo) |
1236 |
|
|
{ |
1237 |
|
|
char buf[8192], buft[8192]; |
1238 |
|
|
|
1239 |
|
|
buf[0] = '\0'; |
1240 |
|
|
|
1241 |
|
|
snprintf(buft, sizeof(buft), "--- %s ping6 statistics ---\n", |
1242 |
|
|
hostname); |
1243 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1244 |
|
|
snprintf(buft, sizeof(buft), "%lld packets transmitted, ", |
1245 |
|
|
ntransmitted); |
1246 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1247 |
|
|
snprintf(buft, sizeof(buft), "%lld packets received, ", |
1248 |
|
|
nreceived); |
1249 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1250 |
|
|
if (nrepeats) { |
1251 |
|
|
snprintf(buft, sizeof(buft), "+%lld duplicates, ", |
1252 |
|
|
nrepeats); |
1253 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1254 |
|
|
} |
1255 |
|
|
if (ntransmitted) { |
1256 |
|
|
if (nreceived > ntransmitted) |
1257 |
|
|
snprintf(buft, sizeof(buft), |
1258 |
|
|
"-- somebody's duplicating packets!"); |
1259 |
|
|
else |
1260 |
|
|
snprintf(buft, sizeof(buft), "%.1lf%% packet loss", |
1261 |
|
|
((((double)ntransmitted - nreceived) * 100) / |
1262 |
|
|
ntransmitted)); |
1263 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1264 |
|
|
} |
1265 |
|
|
strlcat(buf, "\n", sizeof(buf)); |
1266 |
|
|
if (nreceived && timing) { |
1267 |
|
|
/* Only display average to microseconds */ |
1268 |
|
|
double num = nreceived + nrepeats; |
1269 |
|
|
double avg = tsum / num; |
1270 |
|
|
double dev = sqrt(fmax(0, tsumsq / num - avg * avg)); |
1271 |
|
|
snprintf(buft, sizeof(buft), |
1272 |
|
|
"round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", |
1273 |
|
|
tmin, avg, tmax, dev); |
1274 |
|
|
strlcat(buf, buft, sizeof(buf)); |
1275 |
|
|
} |
1276 |
|
|
write(STDOUT_FILENO, buf, strlen(buf)); |
1277 |
|
|
if (signo == 0) |
1278 |
|
|
(void)fflush(stdout); |
1279 |
|
|
} |
1280 |
|
|
|
1281 |
|
|
/* |
1282 |
|
|
* pr_icmph -- |
1283 |
|
|
* Print a descriptive string about an ICMP header. |
1284 |
|
|
*/ |
1285 |
|
|
void |
1286 |
|
|
pr_icmph(struct icmp6_hdr *icp, u_char *end) |
1287 |
|
|
{ |
1288 |
|
|
char ntop_buf[INET6_ADDRSTRLEN]; |
1289 |
|
|
struct nd_redirect *red; |
1290 |
|
|
|
1291 |
|
|
switch (icp->icmp6_type) { |
1292 |
|
|
case ICMP6_DST_UNREACH: |
1293 |
|
|
switch (icp->icmp6_code) { |
1294 |
|
|
case ICMP6_DST_UNREACH_NOROUTE: |
1295 |
|
|
(void)printf("No Route to Destination\n"); |
1296 |
|
|
break; |
1297 |
|
|
case ICMP6_DST_UNREACH_ADMIN: |
1298 |
|
|
(void)printf("Destination Administratively " |
1299 |
|
|
"Unreachable\n"); |
1300 |
|
|
break; |
1301 |
|
|
case ICMP6_DST_UNREACH_BEYONDSCOPE: |
1302 |
|
|
(void)printf("Destination Unreachable Beyond Scope\n"); |
1303 |
|
|
break; |
1304 |
|
|
case ICMP6_DST_UNREACH_ADDR: |
1305 |
|
|
(void)printf("Destination Host Unreachable\n"); |
1306 |
|
|
break; |
1307 |
|
|
case ICMP6_DST_UNREACH_NOPORT: |
1308 |
|
|
(void)printf("Destination Port Unreachable\n"); |
1309 |
|
|
break; |
1310 |
|
|
default: |
1311 |
|
|
(void)printf("Destination Unreachable, Bad Code: %d\n", |
1312 |
|
|
icp->icmp6_code); |
1313 |
|
|
break; |
1314 |
|
|
} |
1315 |
|
|
/* Print returned IP header information */ |
1316 |
|
|
pr_retip((struct ip6_hdr *)(icp + 1), end); |
1317 |
|
|
break; |
1318 |
|
|
case ICMP6_PACKET_TOO_BIG: |
1319 |
|
|
(void)printf("Packet too big mtu = %d\n", |
1320 |
|
|
(int)ntohl(icp->icmp6_mtu)); |
1321 |
|
|
pr_retip((struct ip6_hdr *)(icp + 1), end); |
1322 |
|
|
break; |
1323 |
|
|
case ICMP6_TIME_EXCEEDED: |
1324 |
|
|
switch (icp->icmp6_code) { |
1325 |
|
|
case ICMP6_TIME_EXCEED_TRANSIT: |
1326 |
|
|
(void)printf("Time to live exceeded\n"); |
1327 |
|
|
break; |
1328 |
|
|
case ICMP6_TIME_EXCEED_REASSEMBLY: |
1329 |
|
|
(void)printf("Frag reassembly time exceeded\n"); |
1330 |
|
|
break; |
1331 |
|
|
default: |
1332 |
|
|
(void)printf("Time exceeded, Bad Code: %d\n", |
1333 |
|
|
icp->icmp6_code); |
1334 |
|
|
break; |
1335 |
|
|
} |
1336 |
|
|
pr_retip((struct ip6_hdr *)(icp + 1), end); |
1337 |
|
|
break; |
1338 |
|
|
case ICMP6_PARAM_PROB: |
1339 |
|
|
(void)printf("Parameter problem: "); |
1340 |
|
|
switch (icp->icmp6_code) { |
1341 |
|
|
case ICMP6_PARAMPROB_HEADER: |
1342 |
|
|
(void)printf("Erroneous Header "); |
1343 |
|
|
break; |
1344 |
|
|
case ICMP6_PARAMPROB_NEXTHEADER: |
1345 |
|
|
(void)printf("Unknown Nextheader "); |
1346 |
|
|
break; |
1347 |
|
|
case ICMP6_PARAMPROB_OPTION: |
1348 |
|
|
(void)printf("Unrecognized Option "); |
1349 |
|
|
break; |
1350 |
|
|
default: |
1351 |
|
|
(void)printf("Bad code(%d) ", icp->icmp6_code); |
1352 |
|
|
break; |
1353 |
|
|
} |
1354 |
|
|
(void)printf("pointer = 0x%02x\n", |
1355 |
|
|
(u_int32_t)ntohl(icp->icmp6_pptr)); |
1356 |
|
|
pr_retip((struct ip6_hdr *)(icp + 1), end); |
1357 |
|
|
break; |
1358 |
|
|
case ICMP6_ECHO_REQUEST: |
1359 |
|
|
(void)printf("Echo Request"); |
1360 |
|
|
/* XXX ID + Seq + Data */ |
1361 |
|
|
break; |
1362 |
|
|
case ICMP6_ECHO_REPLY: |
1363 |
|
|
(void)printf("Echo Reply"); |
1364 |
|
|
/* XXX ID + Seq + Data */ |
1365 |
|
|
break; |
1366 |
|
|
case ICMP6_MEMBERSHIP_QUERY: |
1367 |
|
|
(void)printf("Listener Query"); |
1368 |
|
|
break; |
1369 |
|
|
case ICMP6_MEMBERSHIP_REPORT: |
1370 |
|
|
(void)printf("Listener Report"); |
1371 |
|
|
break; |
1372 |
|
|
case ICMP6_MEMBERSHIP_REDUCTION: |
1373 |
|
|
(void)printf("Listener Done"); |
1374 |
|
|
break; |
1375 |
|
|
case ND_ROUTER_SOLICIT: |
1376 |
|
|
(void)printf("Router Solicitation"); |
1377 |
|
|
break; |
1378 |
|
|
case ND_ROUTER_ADVERT: |
1379 |
|
|
(void)printf("Router Advertisement"); |
1380 |
|
|
break; |
1381 |
|
|
case ND_NEIGHBOR_SOLICIT: |
1382 |
|
|
(void)printf("Neighbor Solicitation"); |
1383 |
|
|
break; |
1384 |
|
|
case ND_NEIGHBOR_ADVERT: |
1385 |
|
|
(void)printf("Neighbor Advertisement"); |
1386 |
|
|
break; |
1387 |
|
|
case ND_REDIRECT: |
1388 |
|
|
red = (struct nd_redirect *)icp; |
1389 |
|
|
(void)printf("Redirect\n"); |
1390 |
|
|
if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf, |
1391 |
|
|
sizeof(ntop_buf))) |
1392 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
1393 |
|
|
(void)printf("Destination: %s", ntop_buf); |
1394 |
|
|
if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf, |
1395 |
|
|
sizeof(ntop_buf))) |
1396 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
1397 |
|
|
(void)printf(" New Target: %s", ntop_buf); |
1398 |
|
|
break; |
1399 |
|
|
default: |
1400 |
|
|
(void)printf("Bad ICMP type: %d", icp->icmp6_type); |
1401 |
|
|
} |
1402 |
|
|
} |
1403 |
|
|
|
1404 |
|
|
/* |
1405 |
|
|
* pr_iph -- |
1406 |
|
|
* Print an IP6 header. |
1407 |
|
|
*/ |
1408 |
|
|
void |
1409 |
|
|
pr_iph(struct ip6_hdr *ip6) |
1410 |
|
|
{ |
1411 |
|
|
u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; |
1412 |
|
|
u_int8_t tc; |
1413 |
|
|
char ntop_buf[INET6_ADDRSTRLEN]; |
1414 |
|
|
|
1415 |
|
|
tc = *(&ip6->ip6_vfc + 1); /* XXX */ |
1416 |
|
|
tc = (tc >> 4) & 0x0f; |
1417 |
|
|
tc |= (ip6->ip6_vfc << 4); |
1418 |
|
|
|
1419 |
|
|
printf("Vr TC Flow Plen Nxt Hlim\n"); |
1420 |
|
|
printf(" %1x %02x %05x %04x %02x %02x\n", |
1421 |
|
|
(ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow), |
1422 |
|
|
ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim); |
1423 |
|
|
if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf))) |
1424 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
1425 |
|
|
printf("%s->", ntop_buf); |
1426 |
|
|
if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf))) |
1427 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
1428 |
|
|
printf("%s\n", ntop_buf); |
1429 |
|
|
} |
1430 |
|
|
|
1431 |
|
|
/* |
1432 |
|
|
* pr_addr -- |
1433 |
|
|
* Return an ascii host address as a dotted quad and optionally with |
1434 |
|
|
* a hostname. |
1435 |
|
|
*/ |
1436 |
|
|
const char * |
1437 |
|
|
pr_addr(struct sockaddr *addr, socklen_t addrlen) |
1438 |
|
|
{ |
1439 |
|
|
static char buf[NI_MAXHOST]; |
1440 |
|
|
int flag = 0; |
1441 |
|
|
|
1442 |
|
|
if ((options & F_HOSTNAME) == 0) |
1443 |
|
|
flag |= NI_NUMERICHOST; |
1444 |
|
|
|
1445 |
|
|
if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0) |
1446 |
|
|
return (buf); |
1447 |
|
|
else |
1448 |
|
|
return "?"; |
1449 |
|
|
} |
1450 |
|
|
|
1451 |
|
|
/* |
1452 |
|
|
* pr_retip -- |
1453 |
|
|
* Dump some info on a returned (via ICMPv6) IPv6 packet. |
1454 |
|
|
*/ |
1455 |
|
|
void |
1456 |
|
|
pr_retip(struct ip6_hdr *ip6, u_char *end) |
1457 |
|
|
{ |
1458 |
|
|
u_char *cp = (u_char *)ip6, nh; |
1459 |
|
|
int hlen; |
1460 |
|
|
|
1461 |
|
|
if (end - (u_char *)ip6 < sizeof(*ip6)) { |
1462 |
|
|
printf("IP6"); |
1463 |
|
|
goto trunc; |
1464 |
|
|
} |
1465 |
|
|
pr_iph(ip6); |
1466 |
|
|
hlen = sizeof(*ip6); |
1467 |
|
|
|
1468 |
|
|
nh = ip6->ip6_nxt; |
1469 |
|
|
cp += hlen; |
1470 |
|
|
while (end - cp >= 8) { |
1471 |
|
|
switch (nh) { |
1472 |
|
|
case IPPROTO_HOPOPTS: |
1473 |
|
|
printf("HBH "); |
1474 |
|
|
hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3; |
1475 |
|
|
nh = ((struct ip6_hbh *)cp)->ip6h_nxt; |
1476 |
|
|
break; |
1477 |
|
|
case IPPROTO_DSTOPTS: |
1478 |
|
|
printf("DSTOPT "); |
1479 |
|
|
hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3; |
1480 |
|
|
nh = ((struct ip6_dest *)cp)->ip6d_nxt; |
1481 |
|
|
break; |
1482 |
|
|
case IPPROTO_FRAGMENT: |
1483 |
|
|
printf("FRAG "); |
1484 |
|
|
hlen = sizeof(struct ip6_frag); |
1485 |
|
|
nh = ((struct ip6_frag *)cp)->ip6f_nxt; |
1486 |
|
|
break; |
1487 |
|
|
case IPPROTO_ROUTING: |
1488 |
|
|
printf("RTHDR "); |
1489 |
|
|
hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3; |
1490 |
|
|
nh = ((struct ip6_rthdr *)cp)->ip6r_nxt; |
1491 |
|
|
break; |
1492 |
|
|
case IPPROTO_AH: |
1493 |
|
|
printf("AH "); |
1494 |
|
|
hlen = (((struct ah *)cp)->ah_hl+2) << 2; |
1495 |
|
|
nh = ((struct ah *)cp)->ah_nh; |
1496 |
|
|
break; |
1497 |
|
|
case IPPROTO_ICMPV6: |
1498 |
|
|
printf("ICMP6: type = %d, code = %d\n", |
1499 |
|
|
*cp, *(cp + 1)); |
1500 |
|
|
return; |
1501 |
|
|
case IPPROTO_ESP: |
1502 |
|
|
printf("ESP\n"); |
1503 |
|
|
return; |
1504 |
|
|
case IPPROTO_TCP: |
1505 |
|
|
printf("TCP: from port %u, to port %u (decimal)\n", |
1506 |
|
|
(*cp * 256 + *(cp + 1)), |
1507 |
|
|
(*(cp + 2) * 256 + *(cp + 3))); |
1508 |
|
|
return; |
1509 |
|
|
case IPPROTO_UDP: |
1510 |
|
|
printf("UDP: from port %u, to port %u (decimal)\n", |
1511 |
|
|
(*cp * 256 + *(cp + 1)), |
1512 |
|
|
(*(cp + 2) * 256 + *(cp + 3))); |
1513 |
|
|
return; |
1514 |
|
|
default: |
1515 |
|
|
printf("Unknown Header(%d)\n", nh); |
1516 |
|
|
return; |
1517 |
|
|
} |
1518 |
|
|
|
1519 |
|
|
if ((cp += hlen) >= end) |
1520 |
|
|
goto trunc; |
1521 |
|
|
} |
1522 |
|
|
if (end - cp < 8) |
1523 |
|
|
goto trunc; |
1524 |
|
|
|
1525 |
|
|
putchar('\n'); |
1526 |
|
|
return; |
1527 |
|
|
|
1528 |
|
|
trunc: |
1529 |
|
|
printf("...\n"); |
1530 |
|
|
return; |
1531 |
|
|
} |
1532 |
|
|
|
1533 |
|
|
void |
1534 |
|
|
fill(char *bp, char *patp) |
1535 |
|
|
{ |
1536 |
|
|
int ii, jj, kk; |
1537 |
|
|
int pat[16]; |
1538 |
|
|
char *cp; |
1539 |
|
|
|
1540 |
|
|
for (cp = patp; *cp; cp++) |
1541 |
|
|
if (!isxdigit((unsigned char)*cp)) |
1542 |
|
|
errx(1, "patterns must be specified as hex digits"); |
1543 |
|
|
ii = sscanf(patp, |
1544 |
|
|
"%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", |
1545 |
|
|
&pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], |
1546 |
|
|
&pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], |
1547 |
|
|
&pat[13], &pat[14], &pat[15]); |
1548 |
|
|
|
1549 |
|
|
/* xxx */ |
1550 |
|
|
if (ii > 0) |
1551 |
|
|
for (kk = 0; |
1552 |
|
|
kk <= MAXPAYLOAD - (8 + sizeof(struct payload) + ii); |
1553 |
|
|
kk += ii) |
1554 |
|
|
for (jj = 0; jj < ii; ++jj) |
1555 |
|
|
bp[jj + kk] = pat[jj]; |
1556 |
|
|
if (!(options & F_QUIET)) { |
1557 |
|
|
(void)printf("PATTERN: 0x"); |
1558 |
|
|
for (jj = 0; jj < ii; ++jj) |
1559 |
|
|
(void)printf("%02x", bp[jj] & 0xFF); |
1560 |
|
|
(void)printf("\n"); |
1561 |
|
|
} |
1562 |
|
|
} |
1563 |
|
|
|
1564 |
|
|
void |
1565 |
|
|
usage(void) |
1566 |
|
|
{ |
1567 |
|
|
(void)fprintf(stderr, |
1568 |
|
|
"usage: ping6 [-dEefHLmnqv] [-c count] [-h hoplimit] " |
1569 |
|
|
"[-I sourceaddr]\n\t[-i wait] [-l preload] [-p pattern] " |
1570 |
|
|
"[-s packetsize] [-V rtable]\n\t[-w maxwait] host\n"); |
1571 |
|
|
exit(1); |
1572 |
|
|
} |