1 |
|
|
/* $OpenBSD: ping.c,v 1.223 2017/07/08 16:22:29 florian Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. Neither the name of the project nor the names of its contributors |
16 |
|
|
* may be used to endorse or promote products derived from this software |
17 |
|
|
* without specific prior written permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
20 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
23 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 |
|
|
* SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
/* |
33 |
|
|
* Copyright (c) 1989, 1993 |
34 |
|
|
* The Regents of the University of California. All rights reserved. |
35 |
|
|
* |
36 |
|
|
* This code is derived from software contributed to Berkeley by |
37 |
|
|
* Mike Muuss. |
38 |
|
|
* |
39 |
|
|
* Redistribution and use in source and binary forms, with or without |
40 |
|
|
* modification, are permitted provided that the following conditions |
41 |
|
|
* are met: |
42 |
|
|
* 1. Redistributions of source code must retain the above copyright |
43 |
|
|
* notice, this list of conditions and the following disclaimer. |
44 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
45 |
|
|
* notice, this list of conditions and the following disclaimer in the |
46 |
|
|
* documentation and/or other materials provided with the distribution. |
47 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
48 |
|
|
* may be used to endorse or promote products derived from this software |
49 |
|
|
* without specific prior written permission. |
50 |
|
|
* |
51 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 |
|
|
* SUCH DAMAGE. |
62 |
|
|
*/ |
63 |
|
|
|
64 |
|
|
/* |
65 |
|
|
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, |
66 |
|
|
* measure round-trip-delays and packet loss across network paths. |
67 |
|
|
* |
68 |
|
|
* Author - |
69 |
|
|
* Mike Muuss |
70 |
|
|
* U. S. Army Ballistic Research Laboratory |
71 |
|
|
* December, 1983 |
72 |
|
|
* |
73 |
|
|
* Status - |
74 |
|
|
* Public Domain. Distribution Unlimited. |
75 |
|
|
* Bugs - |
76 |
|
|
* More statistics could always be gathered. |
77 |
|
|
* This program has to run SUID to ROOT to access the ICMP socket. |
78 |
|
|
*/ |
79 |
|
|
|
80 |
|
|
#include <sys/types.h> |
81 |
|
|
#include <sys/socket.h> |
82 |
|
|
#include <sys/time.h> |
83 |
|
|
#include <sys/uio.h> |
84 |
|
|
|
85 |
|
|
#include <netinet/in.h> |
86 |
|
|
#include <netinet/ip.h> |
87 |
|
|
#include <netinet/ip_icmp.h> |
88 |
|
|
#include <netinet/ip_var.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 <pwd.h> |
102 |
|
|
#include <signal.h> |
103 |
|
|
#include <siphash.h> |
104 |
|
|
#include <stdint.h> |
105 |
|
|
#include <stdio.h> |
106 |
|
|
#include <stdlib.h> |
107 |
|
|
#include <string.h> |
108 |
|
|
#include <time.h> |
109 |
|
|
#include <unistd.h> |
110 |
|
|
|
111 |
|
|
struct tv64 { |
112 |
|
|
u_int64_t tv64_sec; |
113 |
|
|
u_int64_t tv64_nsec; |
114 |
|
|
}; |
115 |
|
|
|
116 |
|
|
struct payload { |
117 |
|
|
struct tv64 tv64; |
118 |
|
|
u_int8_t mac[SIPHASH_DIGEST_LENGTH]; |
119 |
|
|
}; |
120 |
|
|
|
121 |
|
|
#define ECHOLEN 8 /* icmp echo header len excluding time */ |
122 |
|
|
#define ECHOTMLEN sizeof(struct payload) |
123 |
|
|
#define DEFDATALEN (64 - ECHOLEN) /* default data length */ |
124 |
|
|
#define MAXIPLEN 60 |
125 |
|
|
#define MAXICMPLEN 76 |
126 |
|
|
#define MAXPAYLOAD (IP_MAXPACKET - MAXIPLEN - ECHOLEN) |
127 |
|
|
#define IP6LEN 40 |
128 |
|
|
#define EXTRA 256 /* for AH and various other headers. weird. */ |
129 |
|
|
#define MAXPAYLOAD6 IPV6_MAXPACKET - IP6LEN - ECHOLEN |
130 |
|
|
#define MAXWAIT_DEFAULT 10 /* secs to wait for response */ |
131 |
|
|
#define NROUTES 9 /* number of record route slots */ |
132 |
|
|
|
133 |
|
|
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ |
134 |
|
|
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ |
135 |
|
|
#define SET(bit) (A(bit) |= B(bit)) |
136 |
|
|
#define CLR(bit) (A(bit) &= (~B(bit))) |
137 |
|
|
#define TST(bit) (A(bit) & B(bit)) |
138 |
|
|
|
139 |
|
|
/* various options */ |
140 |
|
|
int options; |
141 |
|
|
#define F_FLOOD 0x0001 |
142 |
|
|
#define F_INTERVAL 0x0002 |
143 |
|
|
#define F_HOSTNAME 0x0004 |
144 |
|
|
#define F_PINGFILLED 0x0008 |
145 |
|
|
#define F_QUIET 0x0010 |
146 |
|
|
#define F_RROUTE 0x0020 |
147 |
|
|
#define F_SO_DEBUG 0x0040 |
148 |
|
|
/* 0x0080 */ |
149 |
|
|
#define F_VERBOSE 0x0100 |
150 |
|
|
/* 0x0200 */ |
151 |
|
|
#define F_HDRINCL 0x0400 |
152 |
|
|
#define F_TTL 0x0800 |
153 |
|
|
/* 0x1000 */ |
154 |
|
|
#define F_AUD_RECV 0x2000 |
155 |
|
|
#define F_AUD_MISS 0x4000 |
156 |
|
|
|
157 |
|
|
/* multicast options */ |
158 |
|
|
int moptions; |
159 |
|
|
#define MULTICAST_NOLOOP 0x001 |
160 |
|
|
#define MULTICAST_TTL 0x002 |
161 |
|
|
|
162 |
|
|
#define DUMMY_PORT 10101 |
163 |
|
|
#define PING_USER "_ping" |
164 |
|
|
|
165 |
|
|
/* |
166 |
|
|
* MAX_DUP_CHK is the number of bits in received table, i.e. the maximum |
167 |
|
|
* number of received sequence numbers we can keep track of. Change 128 |
168 |
|
|
* to 8192 for complete accuracy... |
169 |
|
|
*/ |
170 |
|
|
#define MAX_DUP_CHK (8 * 8192) |
171 |
|
|
int mx_dup_ck = MAX_DUP_CHK; |
172 |
|
|
char rcvd_tbl[MAX_DUP_CHK / 8]; |
173 |
|
|
|
174 |
|
|
int datalen = DEFDATALEN; |
175 |
|
|
int maxpayload = MAXPAYLOAD; |
176 |
|
|
u_char outpackhdr[IP_MAXPACKET+sizeof(struct ip)]; |
177 |
|
|
u_char *outpack = outpackhdr+sizeof(struct ip); |
178 |
|
|
char BSPACE = '\b'; /* characters written for flood */ |
179 |
|
|
char DOT = '.'; |
180 |
|
|
char *hostname; |
181 |
|
|
int ident; /* process id to identify our packets */ |
182 |
|
|
int v6flag = 0; /* are we ping6? */ |
183 |
|
|
|
184 |
|
|
/* counters */ |
185 |
|
|
int64_t npackets; /* max packets to transmit */ |
186 |
|
|
int64_t nreceived; /* # of packets we got back */ |
187 |
|
|
int64_t nrepeats; /* number of duplicates */ |
188 |
|
|
int64_t ntransmitted; /* sequence # for outbound packets = #sent */ |
189 |
|
|
int64_t nmissedmax = 1; /* max value of ntransmitted - nreceived - 1 */ |
190 |
|
|
struct timeval interval = {1, 0}; /* interval between packets */ |
191 |
|
|
|
192 |
|
|
/* timing */ |
193 |
|
|
int timing = 0; /* flag to do timing */ |
194 |
|
|
int timinginfo = 0; |
195 |
|
|
unsigned int maxwait = MAXWAIT_DEFAULT; /* max seconds to wait for response */ |
196 |
|
|
double tmin = 999999999.0; /* minimum round trip time */ |
197 |
|
|
double tmax = 0.0; /* maximum round trip time */ |
198 |
|
|
double tsum = 0.0; /* sum of all times, for doing average */ |
199 |
|
|
double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ |
200 |
|
|
|
201 |
|
|
struct tv64 tv64_offset; |
202 |
|
|
SIPHASH_KEY mac_key; |
203 |
|
|
|
204 |
|
|
struct msghdr smsghdr; |
205 |
|
|
struct iovec smsgiov; |
206 |
|
|
|
207 |
|
|
volatile sig_atomic_t seenalrm; |
208 |
|
|
volatile sig_atomic_t seenint; |
209 |
|
|
volatile sig_atomic_t seeninfo; |
210 |
|
|
|
211 |
|
|
void fill(char *, char *); |
212 |
|
|
void summary(void); |
213 |
|
|
void onsignal(int); |
214 |
|
|
void retransmit(int); |
215 |
|
|
int pinger(int); |
216 |
|
|
const char *pr_addr(struct sockaddr *, socklen_t); |
217 |
|
|
void pr_pack(u_char *, int, struct msghdr *); |
218 |
|
|
__dead void usage(void); |
219 |
|
|
|
220 |
|
|
/* IPv4 specific functions */ |
221 |
|
|
void pr_ipopt(int, u_char *); |
222 |
|
|
int in_cksum(u_short *, int); |
223 |
|
|
void pr_icmph(struct icmp *); |
224 |
|
|
void pr_retip(struct ip *); |
225 |
|
|
void pr_iph(struct ip *); |
226 |
|
|
#ifndef SMALL |
227 |
|
|
int map_tos(char *, int *); |
228 |
|
|
#endif /* SMALL */ |
229 |
|
|
|
230 |
|
|
/* IPv6 specific functions */ |
231 |
|
|
int get_hoplim(struct msghdr *); |
232 |
|
|
int get_pathmtu(struct msghdr *, struct sockaddr_in6 *); |
233 |
|
|
void pr_icmph6(struct icmp6_hdr *, u_char *); |
234 |
|
|
void pr_iph6(struct ip6_hdr *); |
235 |
|
|
void pr_exthdrs(struct msghdr *); |
236 |
|
|
void pr_ip6opt(void *); |
237 |
|
|
void pr_rthdr(void *); |
238 |
|
|
void pr_retip6(struct ip6_hdr *, u_char *); |
239 |
|
|
|
240 |
|
|
int |
241 |
|
|
main(int argc, char *argv[]) |
242 |
|
|
{ |
243 |
|
|
struct addrinfo hints, *res; |
244 |
|
|
struct itimerval itimer; |
245 |
|
|
struct sockaddr *from, *dst; |
246 |
|
|
struct sockaddr_in from4, dst4; |
247 |
|
|
struct sockaddr_in6 from6, dst6; |
248 |
|
|
struct cmsghdr *scmsg = NULL; |
249 |
|
|
struct in6_pktinfo *pktinfo = NULL; |
250 |
|
|
struct icmp6_filter filt; |
251 |
|
|
struct passwd *pw; |
252 |
|
|
socklen_t maxsizelen; |
253 |
|
|
int64_t preload; |
254 |
|
|
int ch, i, optval = 1, packlen, maxsize, error, s; |
255 |
|
|
int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0; |
256 |
|
|
u_char *datap, *packet; |
257 |
|
|
u_char ttl = MAXTTL; |
258 |
|
|
char *e, *target, hbuf[NI_MAXHOST], *source = NULL; |
259 |
|
|
char rspace[3 + 4 * NROUTES + 1]; /* record route space */ |
260 |
|
|
const char *errstr; |
261 |
|
|
double intval; |
262 |
|
|
uid_t ouid, uid; |
263 |
|
|
gid_t gid; |
264 |
|
|
u_int rtableid = 0; |
265 |
|
|
extern char *__progname; |
266 |
|
|
|
267 |
|
|
if (strcmp("ping6", __progname) == 0) { |
268 |
|
|
v6flag = 1; |
269 |
|
|
maxpayload = MAXPAYLOAD6; |
270 |
|
|
if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) |
271 |
|
|
err(1, "socket"); |
272 |
|
|
} else { |
273 |
|
|
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) |
274 |
|
|
err(1, "socket"); |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
/* revoke privs */ |
278 |
|
|
ouid = getuid(); |
279 |
|
|
if (ouid == 0 && (pw = getpwnam(PING_USER)) != NULL) { |
280 |
|
|
uid = pw->pw_uid; |
281 |
|
|
gid = pw->pw_gid; |
282 |
|
|
} else { |
283 |
|
|
uid = getuid(); |
284 |
|
|
gid = getgid(); |
285 |
|
|
} |
286 |
|
|
if (setgroups(1, &gid) || |
287 |
|
|
setresgid(gid, gid, gid) || |
288 |
|
|
setresuid(uid, uid, uid)) |
289 |
|
|
err(1, "unable to revoke privs"); |
290 |
|
|
|
291 |
|
|
preload = 0; |
292 |
|
|
datap = &outpack[ECHOLEN + ECHOTMLEN]; |
293 |
|
|
while ((ch = getopt(argc, argv, v6flag ? |
294 |
|
|
"c:dEefHh:I:i:Ll:mNnp:qS:s:V:vw:" : |
295 |
|
|
"DEI:LRS:c:defHi:l:np:qs:T:t:V:vw:")) != -1) { |
296 |
|
|
switch(ch) { |
297 |
|
|
case 'c': |
298 |
|
|
npackets = strtonum(optarg, 0, INT64_MAX, &errstr); |
299 |
|
|
if (errstr) |
300 |
|
|
errx(1, |
301 |
|
|
"number of packets to transmit is %s: %s", |
302 |
|
|
errstr, optarg); |
303 |
|
|
break; |
304 |
|
|
case 'D': |
305 |
|
|
options |= F_HDRINCL; |
306 |
|
|
df = 1; |
307 |
|
|
break; |
308 |
|
|
case 'd': |
309 |
|
|
options |= F_SO_DEBUG; |
310 |
|
|
break; |
311 |
|
|
case 'E': |
312 |
|
|
options |= F_AUD_MISS; |
313 |
|
|
break; |
314 |
|
|
case 'e': |
315 |
|
|
options |= F_AUD_RECV; |
316 |
|
|
break; |
317 |
|
|
case 'f': |
318 |
|
|
if (ouid) |
319 |
|
|
errc(1, EPERM, NULL); |
320 |
|
|
options |= F_FLOOD; |
321 |
|
|
setvbuf(stdout, NULL, _IONBF, 0); |
322 |
|
|
break; |
323 |
|
|
case 'H': |
324 |
|
|
options |= F_HOSTNAME; |
325 |
|
|
break; |
326 |
|
|
case 'h': /* hoplimit */ |
327 |
|
|
hoplimit = strtonum(optarg, 0, IPV6_MAXHLIM, &errstr); |
328 |
|
|
if (errstr) |
329 |
|
|
errx(1, "hoplimit is %s: %s", errstr, optarg); |
330 |
|
|
break; |
331 |
|
|
case 'I': |
332 |
|
|
case 'S': /* deprecated */ |
333 |
|
|
source = optarg; |
334 |
|
|
break; |
335 |
|
|
case 'i': /* wait between sending packets */ |
336 |
|
|
intval = strtod(optarg, &e); |
337 |
|
|
if (*optarg == '\0' || *e != '\0') |
338 |
|
|
errx(1, "illegal timing interval %s", optarg); |
339 |
|
|
if (intval < 1 && ouid) |
340 |
|
|
errx(1, "only root may use interval < 1s"); |
341 |
|
|
interval.tv_sec = (time_t)intval; |
342 |
|
|
interval.tv_usec = |
343 |
|
|
(long)((intval - interval.tv_sec) * 1000000); |
344 |
|
|
if (interval.tv_sec < 0) |
345 |
|
|
errx(1, "illegal timing interval %s", optarg); |
346 |
|
|
/* less than 1/Hz does not make sense */ |
347 |
|
|
if (interval.tv_sec == 0 && interval.tv_usec < 10000) { |
348 |
|
|
warnx("too small interval, raised to 0.01"); |
349 |
|
|
interval.tv_usec = 10000; |
350 |
|
|
} |
351 |
|
|
options |= F_INTERVAL; |
352 |
|
|
break; |
353 |
|
|
case 'L': |
354 |
|
|
moptions |= MULTICAST_NOLOOP; |
355 |
|
|
break; |
356 |
|
|
case 'l': |
357 |
|
|
if (ouid) |
358 |
|
|
errc(1, EPERM, NULL); |
359 |
|
|
preload = strtonum(optarg, 1, INT64_MAX, &errstr); |
360 |
|
|
if (errstr) |
361 |
|
|
errx(1, "preload value is %s: %s", errstr, |
362 |
|
|
optarg); |
363 |
|
|
break; |
364 |
|
|
case 'm': |
365 |
|
|
mflag++; |
366 |
|
|
break; |
367 |
|
|
case 'n': |
368 |
|
|
options &= ~F_HOSTNAME; |
369 |
|
|
break; |
370 |
|
|
case 'p': /* fill buffer with user pattern */ |
371 |
|
|
options |= F_PINGFILLED; |
372 |
|
|
fill((char *)datap, optarg); |
373 |
|
|
break; |
374 |
|
|
case 'q': |
375 |
|
|
options |= F_QUIET; |
376 |
|
|
break; |
377 |
|
|
case 'R': |
378 |
|
|
options |= F_RROUTE; |
379 |
|
|
break; |
380 |
|
|
case 's': /* size of packet to send */ |
381 |
|
|
datalen = strtonum(optarg, 0, maxpayload, &errstr); |
382 |
|
|
if (errstr) |
383 |
|
|
errx(1, "packet size is %s: %s", errstr, |
384 |
|
|
optarg); |
385 |
|
|
break; |
386 |
|
|
#ifndef SMALL |
387 |
|
|
case 'T': |
388 |
|
|
options |= F_HDRINCL; |
389 |
|
|
errno = 0; |
390 |
|
|
errstr = NULL; |
391 |
|
|
if (map_tos(optarg, &tos)) |
392 |
|
|
break; |
393 |
|
|
if (strlen(optarg) > 1 && optarg[0] == '0' && |
394 |
|
|
optarg[1] == 'x') |
395 |
|
|
tos = (int)strtol(optarg, NULL, 16); |
396 |
|
|
else |
397 |
|
|
tos = strtonum(optarg, 0, 255, &errstr); |
398 |
|
|
if (tos < 0 || tos > 255 || errstr || errno) |
399 |
|
|
errx(1, "illegal tos value %s", optarg); |
400 |
|
|
break; |
401 |
|
|
#endif /* SMALL */ |
402 |
|
|
case 't': |
403 |
|
|
options |= F_TTL; |
404 |
|
|
ttl = strtonum(optarg, 0, MAXTTL, &errstr); |
405 |
|
|
if (errstr) |
406 |
|
|
errx(1, "ttl value is %s: %s", errstr, optarg); |
407 |
|
|
break; |
408 |
|
|
case 'V': |
409 |
|
|
rtableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); |
410 |
|
|
if (errstr) |
411 |
|
|
errx(1, "rtable value is %s: %s", errstr, |
412 |
|
|
optarg); |
413 |
|
|
if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rtableid, |
414 |
|
|
sizeof(rtableid)) == -1) |
415 |
|
|
err(1, "setsockopt SO_RTABLE"); |
416 |
|
|
break; |
417 |
|
|
case 'v': |
418 |
|
|
options |= F_VERBOSE; |
419 |
|
|
break; |
420 |
|
|
case 'w': |
421 |
|
|
maxwait = strtonum(optarg, 1, INT_MAX, &errstr); |
422 |
|
|
if (errstr) |
423 |
|
|
errx(1, "maxwait value is %s: %s", |
424 |
|
|
errstr, optarg); |
425 |
|
|
break; |
426 |
|
|
default: |
427 |
|
|
usage(); |
428 |
|
|
} |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
argc -= optind; |
432 |
|
|
argv += optind; |
433 |
|
|
|
434 |
|
|
if (argc != 1) |
435 |
|
|
usage(); |
436 |
|
|
|
437 |
|
|
memset(&dst4, 0, sizeof(dst4)); |
438 |
|
|
memset(&dst6, 0, sizeof(dst6)); |
439 |
|
|
|
440 |
|
|
target = *argv; |
441 |
|
|
|
442 |
|
|
memset(&hints, 0, sizeof(hints)); |
443 |
|
|
hints.ai_family = v6flag ? AF_INET6 : AF_INET; |
444 |
|
|
hints.ai_socktype = SOCK_RAW; |
445 |
|
|
hints.ai_protocol = 0; |
446 |
|
|
hints.ai_flags = AI_CANONNAME; |
447 |
|
|
if ((error = getaddrinfo(target, NULL, &hints, &res))) |
448 |
|
|
errx(1, "%s", gai_strerror(error)); |
449 |
|
|
|
450 |
|
|
switch (res->ai_family) { |
451 |
|
|
case AF_INET: |
452 |
|
|
if (res->ai_addrlen != sizeof(dst4)) |
453 |
|
|
errx(1, "size of sockaddr mismatch"); |
454 |
|
|
dst = (struct sockaddr *)&dst4; |
455 |
|
|
from = (struct sockaddr *)&from4; |
456 |
|
|
break; |
457 |
|
|
case AF_INET6: |
458 |
|
|
if (res->ai_addrlen != sizeof(dst6)) |
459 |
|
|
errx(1, "size of sockaddr mismatch"); |
460 |
|
|
dst = (struct sockaddr *)&dst6; |
461 |
|
|
from = (struct sockaddr *)&from6; |
462 |
|
|
break; |
463 |
|
|
default: |
464 |
|
|
errx(1, "unsupported AF: %d", res->ai_family); |
465 |
|
|
break; |
466 |
|
|
} |
467 |
|
|
|
468 |
|
|
memcpy(dst, res->ai_addr, res->ai_addrlen); |
469 |
|
|
|
470 |
|
|
if (!hostname) { |
471 |
|
|
hostname = res->ai_canonname ? strdup(res->ai_canonname) : |
472 |
|
|
target; |
473 |
|
|
if (!hostname) |
474 |
|
|
err(1, "malloc"); |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
if (res->ai_next) { |
478 |
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, |
479 |
|
|
sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) |
480 |
|
|
strlcpy(hbuf, "?", sizeof(hbuf)); |
481 |
|
|
warnx("Warning: %s has multiple " |
482 |
|
|
"addresses; using %s", hostname, hbuf); |
483 |
|
|
} |
484 |
|
|
freeaddrinfo(res); |
485 |
|
|
|
486 |
|
|
if (source) { |
487 |
|
|
memset(&hints, 0, sizeof(hints)); |
488 |
|
|
hints.ai_family = dst->sa_family; |
489 |
|
|
if ((error = getaddrinfo(source, NULL, &hints, &res))) |
490 |
|
|
errx(1, "%s: %s", source, gai_strerror(error)); |
491 |
|
|
if (res->ai_addrlen != dst->sa_len) |
492 |
|
|
errx(1, "size of sockaddr mismatch"); |
493 |
|
|
memcpy(from, res->ai_addr, res->ai_addrlen); |
494 |
|
|
freeaddrinfo(res); |
495 |
|
|
|
496 |
|
|
if (!v6flag && IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) { |
497 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, |
498 |
|
|
&from4.sin_addr, sizeof(from4.sin_addr)) < 0) |
499 |
|
|
err(1, "setsockopt IP_MULTICAST_IF"); |
500 |
|
|
} else { |
501 |
|
|
if (bind(s, from, from->sa_len) < 0) |
502 |
|
|
err(1, "bind"); |
503 |
|
|
} |
504 |
|
|
} else if (options & F_VERBOSE) { |
505 |
|
|
/* |
506 |
|
|
* get the source address. XXX since we revoked the root |
507 |
|
|
* privilege, we cannot use a raw socket for this. |
508 |
|
|
*/ |
509 |
|
|
int dummy; |
510 |
|
|
socklen_t len = dst->sa_len; |
511 |
|
|
|
512 |
|
|
if ((dummy = socket(dst->sa_family, SOCK_DGRAM, 0)) < 0) |
513 |
|
|
err(1, "UDP socket"); |
514 |
|
|
|
515 |
|
|
memcpy(from, dst, dst->sa_len); |
516 |
|
|
if (v6flag) { |
517 |
|
|
from6.sin6_port = ntohs(DUMMY_PORT); |
518 |
|
|
if (pktinfo && |
519 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_PKTINFO, |
520 |
|
|
(void *)pktinfo, sizeof(*pktinfo))) |
521 |
|
|
err(1, "UDP setsockopt(IPV6_PKTINFO)"); |
522 |
|
|
|
523 |
|
|
if (hoplimit != -1 && |
524 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_UNICAST_HOPS, |
525 |
|
|
(void *)&hoplimit, sizeof(hoplimit))) |
526 |
|
|
err(1, "UDP setsockopt(IPV6_UNICAST_HOPS)"); |
527 |
|
|
|
528 |
|
|
if (hoplimit != -1 && |
529 |
|
|
setsockopt(dummy, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, |
530 |
|
|
(void *)&hoplimit, sizeof(hoplimit))) |
531 |
|
|
err(1, "UDP setsockopt(IPV6_MULTICAST_HOPS)"); |
532 |
|
|
} else { |
533 |
|
|
u_char loop = 0; |
534 |
|
|
|
535 |
|
|
from4.sin_port = ntohs(DUMMY_PORT); |
536 |
|
|
|
537 |
|
|
if ((moptions & MULTICAST_NOLOOP) && setsockopt(dummy, |
538 |
|
|
IPPROTO_IP, IP_MULTICAST_LOOP, &loop, |
539 |
|
|
sizeof(loop)) < 0) |
540 |
|
|
err(1, "setsockopt IP_MULTICAST_LOOP"); |
541 |
|
|
if ((moptions & MULTICAST_TTL) && setsockopt(dummy, |
542 |
|
|
IPPROTO_IP, IP_MULTICAST_TTL, &ttl, |
543 |
|
|
sizeof(ttl)) < 0) |
544 |
|
|
err(1, "setsockopt IP_MULTICAST_TTL"); |
545 |
|
|
} |
546 |
|
|
|
547 |
|
|
if (rtableid > 0 && |
548 |
|
|
setsockopt(dummy, SOL_SOCKET, SO_RTABLE, &rtableid, |
549 |
|
|
sizeof(rtableid)) < 0) |
550 |
|
|
err(1, "setsockopt(SO_RTABLE)"); |
551 |
|
|
|
552 |
|
|
if (connect(dummy, from, len) < 0) |
553 |
|
|
err(1, "UDP connect"); |
554 |
|
|
|
555 |
|
|
if (getsockname(dummy, from, &len) < 0) |
556 |
|
|
err(1, "getsockname"); |
557 |
|
|
|
558 |
|
|
close(dummy); |
559 |
|
|
} |
560 |
|
|
|
561 |
|
|
if (options & F_SO_DEBUG) |
562 |
|
|
(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, &optval, |
563 |
|
|
sizeof(optval)); |
564 |
|
|
|
565 |
|
|
if ((options & F_FLOOD) && (options & F_INTERVAL)) |
566 |
|
|
errx(1, "-f and -i options are incompatible"); |
567 |
|
|
|
568 |
|
|
if ((options & F_FLOOD) && (options & (F_AUD_RECV | F_AUD_MISS))) |
569 |
|
|
warnx("No audible output for flood pings"); |
570 |
|
|
|
571 |
|
|
if (datalen >= sizeof(struct payload)) /* can we time transfer */ |
572 |
|
|
timing = 1; |
573 |
|
|
|
574 |
|
|
if (v6flag) { |
575 |
|
|
/* in F_VERBOSE case, we may get non-echoreply packets*/ |
576 |
|
|
if ((options & F_VERBOSE) && datalen < 2048) /* XXX 2048? */ |
577 |
|
|
packlen = 2048 + IP6LEN + ECHOLEN + EXTRA; |
578 |
|
|
else |
579 |
|
|
packlen = datalen + IP6LEN + ECHOLEN + EXTRA; |
580 |
|
|
} else |
581 |
|
|
packlen = datalen + MAXIPLEN + MAXICMPLEN; |
582 |
|
|
if (!(packet = malloc(packlen))) |
583 |
|
|
err(1, "malloc"); |
584 |
|
|
|
585 |
|
|
if (!(options & F_PINGFILLED)) |
586 |
|
|
for (i = ECHOTMLEN; i < datalen; ++i) |
587 |
|
|
*datap++ = i; |
588 |
|
|
|
589 |
|
|
ident = getpid() & 0xFFFF; |
590 |
|
|
|
591 |
|
|
/* |
592 |
|
|
* When trying to send large packets, you must increase the |
593 |
|
|
* size of both the send and receive buffers... |
594 |
|
|
*/ |
595 |
|
|
maxsizelen = sizeof maxsize; |
596 |
|
|
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &maxsize, &maxsizelen) < 0) |
597 |
|
|
err(1, "getsockopt"); |
598 |
|
|
if (maxsize < packlen && |
599 |
|
|
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(maxsize)) < 0) |
600 |
|
|
err(1, "setsockopt"); |
601 |
|
|
|
602 |
|
|
/* |
603 |
|
|
* When pinging the broadcast address, you can get a lot of answers. |
604 |
|
|
* Doing something so evil is useful if you are trying to stress the |
605 |
|
|
* ethernet, or just want to fill the arp cache to get some stuff for |
606 |
|
|
* /etc/ethers. |
607 |
|
|
*/ |
608 |
|
|
while (setsockopt(s, SOL_SOCKET, SO_RCVBUF, |
609 |
|
|
(void*)&bufspace, sizeof(bufspace)) < 0) { |
610 |
|
|
if ((bufspace -= 1024) <= 0) |
611 |
|
|
err(1, "Cannot set the receive buffer size"); |
612 |
|
|
} |
613 |
|
|
if (bufspace < IP_MAXPACKET) |
614 |
|
|
warnx("Could only allocate a receive buffer of %d bytes " |
615 |
|
|
"(default %d)", bufspace, IP_MAXPACKET); |
616 |
|
|
|
617 |
|
|
if (v6flag) { |
618 |
|
|
unsigned int loop = 0; |
619 |
|
|
|
620 |
|
|
/* |
621 |
|
|
* let the kernel pass extension headers of incoming packets, |
622 |
|
|
* for privileged socket options |
623 |
|
|
*/ |
624 |
|
|
if (options & F_VERBOSE) { |
625 |
|
|
int opton = 1; |
626 |
|
|
|
627 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPOPTS, |
628 |
|
|
&opton, (socklen_t)sizeof(opton))) |
629 |
|
|
err(1, "setsockopt(IPV6_RECVHOPOPTS)"); |
630 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVDSTOPTS, |
631 |
|
|
&opton, (socklen_t)sizeof(opton))) |
632 |
|
|
err(1, "setsockopt(IPV6_RECVDSTOPTS)"); |
633 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, |
634 |
|
|
sizeof(opton))) |
635 |
|
|
err(1, "setsockopt(IPV6_RECVRTHDR)"); |
636 |
|
|
ICMP6_FILTER_SETPASSALL(&filt); |
637 |
|
|
} else { |
638 |
|
|
ICMP6_FILTER_SETBLOCKALL(&filt); |
639 |
|
|
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
if ((moptions & MULTICAST_NOLOOP) && |
643 |
|
|
setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, |
644 |
|
|
sizeof(loop)) < 0) |
645 |
|
|
err(1, "setsockopt IPV6_MULTICAST_LOOP"); |
646 |
|
|
|
647 |
|
|
optval = IPV6_DEFHLIM; |
648 |
|
|
if (IN6_IS_ADDR_MULTICAST(&dst6.sin6_addr)) |
649 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, |
650 |
|
|
&optval, (socklen_t)sizeof(optval)) == -1) |
651 |
|
|
err(1, "IPV6_MULTICAST_HOPS"); |
652 |
|
|
if (mflag != 1) { |
653 |
|
|
optval = mflag > 1 ? 0 : 1; |
654 |
|
|
|
655 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, |
656 |
|
|
&optval, (socklen_t)sizeof(optval)) == -1) |
657 |
|
|
err(1, "setsockopt(IPV6_USE_MIN_MTU)"); |
658 |
|
|
} else { |
659 |
|
|
optval = 1; |
660 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPATHMTU, |
661 |
|
|
&optval, sizeof(optval)) == -1) |
662 |
|
|
err(1, "setsockopt(IPV6_RECVPATHMTU)"); |
663 |
|
|
} |
664 |
|
|
|
665 |
|
|
if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, |
666 |
|
|
(socklen_t)sizeof(filt)) < 0) |
667 |
|
|
err(1, "setsockopt(ICMP6_FILTER)"); |
668 |
|
|
|
669 |
|
|
if (hoplimit != -1) { |
670 |
|
|
/* set IP6 packet options */ |
671 |
|
|
if ((scmsg = malloc( CMSG_SPACE(sizeof(int)))) == NULL) |
672 |
|
|
err(1, "malloc"); |
673 |
|
|
smsghdr.msg_control = (caddr_t)scmsg; |
674 |
|
|
smsghdr.msg_controllen = CMSG_SPACE(sizeof(int)); |
675 |
|
|
|
676 |
|
|
scmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
677 |
|
|
scmsg->cmsg_level = IPPROTO_IPV6; |
678 |
|
|
scmsg->cmsg_type = IPV6_HOPLIMIT; |
679 |
|
|
*(int *)(CMSG_DATA(scmsg)) = hoplimit; |
680 |
|
|
} |
681 |
|
|
|
682 |
|
|
optval = 1; |
683 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, |
684 |
|
|
(socklen_t)sizeof(optval)) < 0) |
685 |
|
|
warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */ |
686 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, |
687 |
|
|
(socklen_t)sizeof(optval)) < 0) |
688 |
|
|
warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */ |
689 |
|
|
} else { |
690 |
|
|
u_char loop = 0; |
691 |
|
|
|
692 |
|
|
if (options & F_TTL) { |
693 |
|
|
if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) |
694 |
|
|
moptions |= MULTICAST_TTL; |
695 |
|
|
else |
696 |
|
|
options |= F_HDRINCL; |
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
if ((options & F_RROUTE) && (options & F_HDRINCL)) |
700 |
|
|
errx(1, "-R option and -D or -T, or -t to unicast" |
701 |
|
|
" destinations are incompatible"); |
702 |
|
|
|
703 |
|
|
if (options & F_HDRINCL) { |
704 |
|
|
struct ip *ip = (struct ip *)outpackhdr; |
705 |
|
|
|
706 |
|
|
setsockopt(s, IPPROTO_IP, IP_HDRINCL, &optval, |
707 |
|
|
sizeof(optval)); |
708 |
|
|
ip->ip_v = IPVERSION; |
709 |
|
|
ip->ip_hl = sizeof(struct ip) >> 2; |
710 |
|
|
ip->ip_tos = tos; |
711 |
|
|
ip->ip_id = 0; |
712 |
|
|
ip->ip_off = htons(df ? IP_DF : 0); |
713 |
|
|
ip->ip_ttl = ttl; |
714 |
|
|
ip->ip_p = IPPROTO_ICMP; |
715 |
|
|
if (source) |
716 |
|
|
ip->ip_src = from4.sin_addr; |
717 |
|
|
else |
718 |
|
|
ip->ip_src.s_addr = INADDR_ANY; |
719 |
|
|
ip->ip_dst = dst4.sin_addr; |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
/* record route option */ |
723 |
|
|
if (options & F_RROUTE) { |
724 |
|
|
if (IN_MULTICAST(ntohl(dst4.sin_addr.s_addr))) |
725 |
|
|
errx(1, "record route not valid to multicast" |
726 |
|
|
" destinations"); |
727 |
|
|
memset(rspace, 0, sizeof(rspace)); |
728 |
|
|
rspace[IPOPT_OPTVAL] = IPOPT_RR; |
729 |
|
|
rspace[IPOPT_OLEN] = sizeof(rspace)-1; |
730 |
|
|
rspace[IPOPT_OFFSET] = IPOPT_MINOFF; |
731 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, |
732 |
|
|
sizeof(rspace)) < 0) |
733 |
|
|
err(1, "record route"); |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
if ((moptions & MULTICAST_NOLOOP) && |
737 |
|
|
setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, |
738 |
|
|
sizeof(loop)) < 0) |
739 |
|
|
err(1, "setsockopt IP_MULTICAST_LOOP"); |
740 |
|
|
if ((moptions & MULTICAST_TTL) && |
741 |
|
|
setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, |
742 |
|
|
sizeof(ttl)) < 0) |
743 |
|
|
err(1, "setsockopt IP_MULTICAST_TTL"); |
744 |
|
|
} |
745 |
|
|
|
746 |
|
|
if (options & F_HOSTNAME) { |
747 |
|
|
if (pledge("stdio inet dns flock rpath cpath wpath", NULL) == -1) |
748 |
|
|
err(1, "pledge"); |
749 |
|
|
} else { |
750 |
|
|
if (pledge("stdio inet flock rpath cpath wpath", NULL) == -1) |
751 |
|
|
err(1, "pledge"); |
752 |
|
|
} |
753 |
|
|
|
754 |
|
|
arc4random_buf(&tv64_offset, sizeof(tv64_offset)); |
755 |
|
|
arc4random_buf(&mac_key, sizeof(mac_key)); |
756 |
|
|
|
757 |
|
|
printf("PING %s (", hostname); |
758 |
|
|
if (options & F_VERBOSE) |
759 |
|
|
printf("%s --> ", pr_addr(from, from->sa_len)); |
760 |
|
|
printf("%s): %d data bytes\n", pr_addr(dst, dst->sa_len), datalen); |
761 |
|
|
|
762 |
|
|
smsghdr.msg_name = dst; |
763 |
|
|
smsghdr.msg_namelen = dst->sa_len; |
764 |
|
|
smsgiov.iov_base = (caddr_t)outpack; |
765 |
|
|
smsghdr.msg_iov = &smsgiov; |
766 |
|
|
smsghdr.msg_iovlen = 1; |
767 |
|
|
|
768 |
|
|
while (preload--) /* Fire off them quickies. */ |
769 |
|
|
pinger(s); |
770 |
|
|
|
771 |
|
|
(void)signal(SIGINT, onsignal); |
772 |
|
|
(void)signal(SIGINFO, onsignal); |
773 |
|
|
|
774 |
|
|
if (!(options & F_FLOOD)) { |
775 |
|
|
(void)signal(SIGALRM, onsignal); |
776 |
|
|
itimer.it_interval = interval; |
777 |
|
|
itimer.it_value = interval; |
778 |
|
|
(void)setitimer(ITIMER_REAL, &itimer, NULL); |
779 |
|
|
if (ntransmitted == 0) |
780 |
|
|
retransmit(s); |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
seenalrm = seenint = 0; |
784 |
|
|
seeninfo = 0; |
785 |
|
|
|
786 |
|
|
for (;;) { |
787 |
|
|
struct msghdr m; |
788 |
|
|
union { |
789 |
|
|
struct cmsghdr hdr; |
790 |
|
|
u_char buf[CMSG_SPACE(1024)]; |
791 |
|
|
} cmsgbuf; |
792 |
|
|
struct iovec iov[1]; |
793 |
|
|
struct pollfd pfd; |
794 |
|
|
struct sockaddr_in peer4; |
795 |
|
|
struct sockaddr_in6 peer6; |
796 |
|
|
ssize_t cc; |
797 |
|
|
int timeout; |
798 |
|
|
|
799 |
|
|
/* signal handling */ |
800 |
|
|
if (seenint) |
801 |
|
|
break; |
802 |
|
|
if (seenalrm) { |
803 |
|
|
retransmit(s); |
804 |
|
|
seenalrm = 0; |
805 |
|
|
if (ntransmitted - nreceived - 1 > nmissedmax) { |
806 |
|
|
nmissedmax = ntransmitted - nreceived - 1; |
807 |
|
|
if (!(options & F_FLOOD) && |
808 |
|
|
(options & F_AUD_MISS)) |
809 |
|
|
fputc('\a', stderr); |
810 |
|
|
} |
811 |
|
|
continue; |
812 |
|
|
} |
813 |
|
|
if (seeninfo) { |
814 |
|
|
summary(); |
815 |
|
|
seeninfo = 0; |
816 |
|
|
continue; |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
if (options & F_FLOOD) { |
820 |
|
|
(void)pinger(s); |
821 |
|
|
timeout = 10; |
822 |
|
|
} else |
823 |
|
|
timeout = INFTIM; |
824 |
|
|
|
825 |
|
|
pfd.fd = s; |
826 |
|
|
pfd.events = POLLIN; |
827 |
|
|
|
828 |
|
|
if (poll(&pfd, 1, timeout) <= 0) |
829 |
|
|
continue; |
830 |
|
|
|
831 |
|
|
if (v6flag) { |
832 |
|
|
m.msg_name = &peer6; |
833 |
|
|
m.msg_namelen = sizeof(peer6); |
834 |
|
|
} else { |
835 |
|
|
m.msg_name = &peer4; |
836 |
|
|
m.msg_namelen = sizeof(peer4); |
837 |
|
|
} |
838 |
|
|
memset(&iov, 0, sizeof(iov)); |
839 |
|
|
iov[0].iov_base = (caddr_t)packet; |
840 |
|
|
iov[0].iov_len = packlen; |
841 |
|
|
m.msg_iov = iov; |
842 |
|
|
m.msg_iovlen = 1; |
843 |
|
|
m.msg_control = (caddr_t)&cmsgbuf.buf; |
844 |
|
|
m.msg_controllen = sizeof(cmsgbuf.buf); |
845 |
|
|
|
846 |
|
|
cc = recvmsg(s, &m, 0); |
847 |
|
|
if (cc < 0) { |
848 |
|
|
if (errno != EINTR) { |
849 |
|
|
warn("recvmsg"); |
850 |
|
|
sleep(1); |
851 |
|
|
} |
852 |
|
|
continue; |
853 |
|
|
} else if (cc == 0) { |
854 |
|
|
int mtu; |
855 |
|
|
|
856 |
|
|
/* |
857 |
|
|
* receive control messages only. Process the |
858 |
|
|
* exceptions (currently the only possibility is |
859 |
|
|
* a path MTU notification.) |
860 |
|
|
*/ |
861 |
|
|
if ((mtu = get_pathmtu(&m, &dst6)) > 0) { |
862 |
|
|
if (options & F_VERBOSE) { |
863 |
|
|
printf("new path MTU (%d) is " |
864 |
|
|
"notified\n", mtu); |
865 |
|
|
} |
866 |
|
|
} |
867 |
|
|
continue; |
868 |
|
|
} else |
869 |
|
|
pr_pack(packet, cc, &m); |
870 |
|
|
|
871 |
|
|
if (npackets && nreceived >= npackets) |
872 |
|
|
break; |
873 |
|
|
} |
874 |
|
|
summary(); |
875 |
|
|
exit(nreceived == 0); |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
void |
879 |
|
|
onsignal(int sig) |
880 |
|
|
{ |
881 |
|
|
switch (sig) { |
882 |
|
|
case SIGALRM: |
883 |
|
|
seenalrm++; |
884 |
|
|
break; |
885 |
|
|
case SIGINT: |
886 |
|
|
seenint++; |
887 |
|
|
break; |
888 |
|
|
case SIGINFO: |
889 |
|
|
seeninfo++; |
890 |
|
|
break; |
891 |
|
|
} |
892 |
|
|
} |
893 |
|
|
|
894 |
|
|
void |
895 |
|
|
fill(char *bp, char *patp) |
896 |
|
|
{ |
897 |
|
|
int ii, jj, kk; |
898 |
|
|
int pat[16]; |
899 |
|
|
char *cp; |
900 |
|
|
|
901 |
|
|
for (cp = patp; *cp; cp++) |
902 |
|
|
if (!isxdigit((unsigned char)*cp)) |
903 |
|
|
errx(1, "patterns must be specified as hex digits"); |
904 |
|
|
ii = sscanf(patp, |
905 |
|
|
"%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", |
906 |
|
|
&pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], |
907 |
|
|
&pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], |
908 |
|
|
&pat[13], &pat[14], &pat[15]); |
909 |
|
|
|
910 |
|
|
if (ii > 0) |
911 |
|
|
for (kk = 0; |
912 |
|
|
kk <= maxpayload - (ECHOLEN + ECHOTMLEN + ii); |
913 |
|
|
kk += ii) |
914 |
|
|
for (jj = 0; jj < ii; ++jj) |
915 |
|
|
bp[jj + kk] = pat[jj]; |
916 |
|
|
if (!(options & F_QUIET)) { |
917 |
|
|
printf("PATTERN: 0x"); |
918 |
|
|
for (jj = 0; jj < ii; ++jj) |
919 |
|
|
printf("%02x", bp[jj] & 0xFF); |
920 |
|
|
printf("\n"); |
921 |
|
|
} |
922 |
|
|
} |
923 |
|
|
|
924 |
|
|
void |
925 |
|
|
summary(void) |
926 |
|
|
{ |
927 |
|
|
printf("\n--- %s ping statistics ---\n", hostname); |
928 |
|
|
printf("%lld packets transmitted, ", ntransmitted); |
929 |
|
|
printf("%lld packets received, ", nreceived); |
930 |
|
|
|
931 |
|
|
if (nrepeats) |
932 |
|
|
printf("%lld duplicates, ", nrepeats); |
933 |
|
|
if (ntransmitted) { |
934 |
|
|
if (nreceived > ntransmitted) |
935 |
|
|
printf("-- somebody's duplicating packets!"); |
936 |
|
|
else |
937 |
|
|
printf("%.1f%% packet loss", |
938 |
|
|
((((double)ntransmitted - nreceived) * 100) / |
939 |
|
|
ntransmitted)); |
940 |
|
|
} |
941 |
|
|
printf("\n"); |
942 |
|
|
if (timinginfo) { |
943 |
|
|
/* Only display average to microseconds */ |
944 |
|
|
double num = nreceived + nrepeats; |
945 |
|
|
double avg = tsum / num; |
946 |
|
|
double dev = sqrt(fmax(0, tsumsq / num - avg * avg)); |
947 |
|
|
printf("round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", |
948 |
|
|
tmin, avg, tmax, dev); |
949 |
|
|
} |
950 |
|
|
} |
951 |
|
|
|
952 |
|
|
/* |
953 |
|
|
* pr_addr -- |
954 |
|
|
* Return address in numeric form or a host name |
955 |
|
|
*/ |
956 |
|
|
const char * |
957 |
|
|
pr_addr(struct sockaddr *addr, socklen_t addrlen) |
958 |
|
|
{ |
959 |
|
|
static char buf[NI_MAXHOST]; |
960 |
|
|
int flag = 0; |
961 |
|
|
|
962 |
|
|
if (!(options & F_HOSTNAME)) |
963 |
|
|
flag |= NI_NUMERICHOST; |
964 |
|
|
|
965 |
|
|
if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0) |
966 |
|
|
return (buf); |
967 |
|
|
else |
968 |
|
|
return "?"; |
969 |
|
|
} |
970 |
|
|
|
971 |
|
|
/* |
972 |
|
|
* retransmit -- |
973 |
|
|
* This routine transmits another ping. |
974 |
|
|
*/ |
975 |
|
|
void |
976 |
|
|
retransmit(int s) |
977 |
|
|
{ |
978 |
|
|
struct itimerval itimer; |
979 |
|
|
static int last_time = 0; |
980 |
|
|
|
981 |
|
|
if (last_time) { |
982 |
|
|
seenint = 1; /* break out of ping event loop */ |
983 |
|
|
return; |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
if (pinger(s) == 0) |
987 |
|
|
return; |
988 |
|
|
|
989 |
|
|
/* |
990 |
|
|
* If we're not transmitting any more packets, change the timer |
991 |
|
|
* to wait two round-trip times if we've received any packets or |
992 |
|
|
* maxwait seconds if we haven't. |
993 |
|
|
*/ |
994 |
|
|
if (nreceived) { |
995 |
|
|
itimer.it_value.tv_sec = 2 * tmax / 1000; |
996 |
|
|
if (itimer.it_value.tv_sec == 0) |
997 |
|
|
itimer.it_value.tv_sec = 1; |
998 |
|
|
} else |
999 |
|
|
itimer.it_value.tv_sec = maxwait; |
1000 |
|
|
itimer.it_interval.tv_sec = 0; |
1001 |
|
|
itimer.it_interval.tv_usec = 0; |
1002 |
|
|
itimer.it_value.tv_usec = 0; |
1003 |
|
|
(void)setitimer(ITIMER_REAL, &itimer, NULL); |
1004 |
|
|
|
1005 |
|
|
/* When the alarm goes off we are done. */ |
1006 |
|
|
last_time = 1; |
1007 |
|
|
} |
1008 |
|
|
|
1009 |
|
|
/* |
1010 |
|
|
* pinger -- |
1011 |
|
|
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet |
1012 |
|
|
* will be added on by the kernel. The ID field is our UNIX process ID, |
1013 |
|
|
* and the sequence number is an ascending integer. The first 8 bytes |
1014 |
|
|
* of the data portion are used to hold a UNIX "timeval" struct in VAX |
1015 |
|
|
* byte-order, to compute the round-trip time. |
1016 |
|
|
*/ |
1017 |
|
|
int |
1018 |
|
|
pinger(int s) |
1019 |
|
|
{ |
1020 |
|
|
struct icmp *icp = NULL; |
1021 |
|
|
struct icmp6_hdr *icp6 = NULL; |
1022 |
|
|
int cc, i; |
1023 |
|
|
u_int16_t seq; |
1024 |
|
|
|
1025 |
|
|
if (npackets && ntransmitted >= npackets) |
1026 |
|
|
return(-1); /* no more transmission */ |
1027 |
|
|
|
1028 |
|
|
seq = htons(ntransmitted++); |
1029 |
|
|
|
1030 |
|
|
if (v6flag) { |
1031 |
|
|
icp6 = (struct icmp6_hdr *)outpack; |
1032 |
|
|
memset(icp6, 0, sizeof(*icp6)); |
1033 |
|
|
icp6->icmp6_cksum = 0; |
1034 |
|
|
icp6->icmp6_type = ICMP6_ECHO_REQUEST; |
1035 |
|
|
icp6->icmp6_code = 0; |
1036 |
|
|
icp6->icmp6_id = htons(ident); |
1037 |
|
|
icp6->icmp6_seq = seq; |
1038 |
|
|
} else { |
1039 |
|
|
icp = (struct icmp *)outpack; |
1040 |
|
|
icp->icmp_type = ICMP_ECHO; |
1041 |
|
|
icp->icmp_code = 0; |
1042 |
|
|
icp->icmp_cksum = 0; |
1043 |
|
|
icp->icmp_seq = seq; |
1044 |
|
|
icp->icmp_id = ident; /* ID */ |
1045 |
|
|
} |
1046 |
|
|
CLR(ntohs(seq) % mx_dup_ck); |
1047 |
|
|
|
1048 |
|
|
if (timing) { |
1049 |
|
|
SIPHASH_CTX ctx; |
1050 |
|
|
struct timespec ts; |
1051 |
|
|
struct payload payload; |
1052 |
|
|
struct tv64 *tv64 = &payload.tv64; |
1053 |
|
|
|
1054 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) |
1055 |
|
|
err(1, "clock_gettime(CLOCK_MONOTONIC)"); |
1056 |
|
|
tv64->tv64_sec = htobe64((u_int64_t)ts.tv_sec + |
1057 |
|
|
tv64_offset.tv64_sec); |
1058 |
|
|
tv64->tv64_nsec = htobe64((u_int64_t)ts.tv_nsec + |
1059 |
|
|
tv64_offset.tv64_nsec); |
1060 |
|
|
|
1061 |
|
|
SipHash24_Init(&ctx, &mac_key); |
1062 |
|
|
SipHash24_Update(&ctx, tv64, sizeof(*tv64)); |
1063 |
|
|
SipHash24_Update(&ctx, &ident, sizeof(ident)); |
1064 |
|
|
SipHash24_Update(&ctx, &seq, sizeof(seq)); |
1065 |
|
|
SipHash24_Final(&payload.mac, &ctx); |
1066 |
|
|
|
1067 |
|
|
memcpy(&outpack[ECHOLEN], &payload, sizeof(payload)); |
1068 |
|
|
} |
1069 |
|
|
|
1070 |
|
|
cc = ECHOLEN + datalen; |
1071 |
|
|
|
1072 |
|
|
if (!v6flag) { |
1073 |
|
|
/* compute ICMP checksum here */ |
1074 |
|
|
icp->icmp_cksum = in_cksum((u_short *)icp, cc); |
1075 |
|
|
|
1076 |
|
|
if (options & F_HDRINCL) { |
1077 |
|
|
struct ip *ip = (struct ip *)outpackhdr; |
1078 |
|
|
|
1079 |
|
|
smsgiov.iov_base = (caddr_t)outpackhdr; |
1080 |
|
|
cc += sizeof(struct ip); |
1081 |
|
|
ip->ip_len = htons(cc); |
1082 |
|
|
ip->ip_sum = in_cksum((u_short *)outpackhdr, cc); |
1083 |
|
|
} |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
smsgiov.iov_len = cc; |
1087 |
|
|
|
1088 |
|
|
i = sendmsg(s, &smsghdr, 0); |
1089 |
|
|
|
1090 |
|
|
if (i < 0 || i != cc) { |
1091 |
|
|
if (i < 0) |
1092 |
|
|
warn("sendmsg"); |
1093 |
|
|
printf("ping: wrote %s %d chars, ret=%d\n", hostname, cc, i); |
1094 |
|
|
} |
1095 |
|
|
if (!(options & F_QUIET) && (options & F_FLOOD)) |
1096 |
|
|
write(STDOUT_FILENO, &DOT, 1); |
1097 |
|
|
|
1098 |
|
|
return (0); |
1099 |
|
|
} |
1100 |
|
|
|
1101 |
|
|
/* |
1102 |
|
|
* pr_pack -- |
1103 |
|
|
* Print out the packet, if it came from us. This logic is necessary |
1104 |
|
|
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets |
1105 |
|
|
* which arrive ('tis only fair). This permits multiple copies of this |
1106 |
|
|
* program to be run without having intermingled output (or statistics!). |
1107 |
|
|
*/ |
1108 |
|
|
void |
1109 |
|
|
pr_pack(u_char *buf, int cc, struct msghdr *mhdr) |
1110 |
|
|
{ |
1111 |
|
|
struct ip *ip = NULL; |
1112 |
|
|
struct icmp *icp = NULL; |
1113 |
|
|
struct icmp6_hdr *icp6 = NULL; |
1114 |
|
|
struct timespec ts, tp; |
1115 |
|
|
struct payload payload; |
1116 |
|
|
struct sockaddr *from; |
1117 |
|
|
socklen_t fromlen; |
1118 |
|
|
double triptime = 0; |
1119 |
|
|
int i, dupflag; |
1120 |
|
|
int hlen = -1, hoplim = -1, echo_reply = 0; |
1121 |
|
|
u_int16_t seq; |
1122 |
|
|
u_char *cp, *dp; |
1123 |
|
|
char* pkttime; |
1124 |
|
|
|
1125 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) |
1126 |
|
|
err(1, "clock_gettime(CLOCK_MONOTONIC)"); |
1127 |
|
|
|
1128 |
|
|
if (v6flag) { |
1129 |
|
|
if (!mhdr || !mhdr->msg_name || |
1130 |
|
|
mhdr->msg_namelen != sizeof(struct sockaddr_in6) || |
1131 |
|
|
((struct sockaddr *)mhdr->msg_name)->sa_family != |
1132 |
|
|
AF_INET6) { |
1133 |
|
|
if (options & F_VERBOSE) |
1134 |
|
|
warnx("invalid peername"); |
1135 |
|
|
return; |
1136 |
|
|
} |
1137 |
|
|
from = (struct sockaddr *)mhdr->msg_name; |
1138 |
|
|
fromlen = mhdr->msg_namelen; |
1139 |
|
|
|
1140 |
|
|
if (cc < sizeof(struct icmp6_hdr)) { |
1141 |
|
|
if (options & F_VERBOSE) |
1142 |
|
|
warnx("packet too short (%d bytes) from %s", cc, |
1143 |
|
|
pr_addr(from, fromlen)); |
1144 |
|
|
return; |
1145 |
|
|
} |
1146 |
|
|
icp6 = (struct icmp6_hdr *)buf; |
1147 |
|
|
|
1148 |
|
|
if ((hoplim = get_hoplim(mhdr)) == -1) { |
1149 |
|
|
warnx("failed to get receiving hop limit"); |
1150 |
|
|
return; |
1151 |
|
|
} |
1152 |
|
|
|
1153 |
|
|
if (icp6->icmp6_type == ICMP6_ECHO_REPLY) { |
1154 |
|
|
if (ntohs(icp6->icmp6_id) != ident) |
1155 |
|
|
return; /* 'Twas not our ECHO */ |
1156 |
|
|
seq = icp6->icmp6_seq; |
1157 |
|
|
echo_reply = 1; |
1158 |
|
|
pkttime = (char *)(icp6 + 1); |
1159 |
|
|
} |
1160 |
|
|
} else { |
1161 |
|
|
if (!mhdr || !mhdr->msg_name || |
1162 |
|
|
mhdr->msg_namelen != sizeof(struct sockaddr_in) || |
1163 |
|
|
((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET) { |
1164 |
|
|
if (options & F_VERBOSE) |
1165 |
|
|
warnx("invalid peername"); |
1166 |
|
|
return; |
1167 |
|
|
} |
1168 |
|
|
|
1169 |
|
|
from = (struct sockaddr *)mhdr->msg_name; |
1170 |
|
|
fromlen = mhdr->msg_namelen; |
1171 |
|
|
|
1172 |
|
|
/* Check the IP header */ |
1173 |
|
|
ip = (struct ip *)buf; |
1174 |
|
|
hlen = ip->ip_hl << 2; |
1175 |
|
|
if (cc < hlen + ICMP_MINLEN) { |
1176 |
|
|
if (options & F_VERBOSE) |
1177 |
|
|
warnx("packet too short (%d bytes) from %s", cc, |
1178 |
|
|
pr_addr(from, fromlen)); |
1179 |
|
|
return; |
1180 |
|
|
} |
1181 |
|
|
|
1182 |
|
|
/* Now the ICMP part */ |
1183 |
|
|
cc -= hlen; |
1184 |
|
|
icp = (struct icmp *)(buf + hlen); |
1185 |
|
|
if (icp->icmp_type == ICMP_ECHOREPLY) { |
1186 |
|
|
if (icp->icmp_id != ident) |
1187 |
|
|
return; /* 'Twas not our ECHO */ |
1188 |
|
|
seq = icp->icmp_seq; |
1189 |
|
|
echo_reply = 1; |
1190 |
|
|
pkttime = (char *)icp->icmp_data; |
1191 |
|
|
} |
1192 |
|
|
} |
1193 |
|
|
|
1194 |
|
|
if (echo_reply) { |
1195 |
|
|
++nreceived; |
1196 |
|
|
if (cc >= ECHOLEN + ECHOTMLEN) { |
1197 |
|
|
SIPHASH_CTX ctx; |
1198 |
|
|
struct tv64 *tv64; |
1199 |
|
|
u_int8_t mac[SIPHASH_DIGEST_LENGTH]; |
1200 |
|
|
|
1201 |
|
|
memcpy(&payload, pkttime, sizeof(payload)); |
1202 |
|
|
tv64 = &payload.tv64; |
1203 |
|
|
|
1204 |
|
|
SipHash24_Init(&ctx, &mac_key); |
1205 |
|
|
SipHash24_Update(&ctx, tv64, sizeof(*tv64)); |
1206 |
|
|
SipHash24_Update(&ctx, &ident, sizeof(ident)); |
1207 |
|
|
SipHash24_Update(&ctx, &seq, sizeof(seq)); |
1208 |
|
|
SipHash24_Final(mac, &ctx); |
1209 |
|
|
|
1210 |
|
|
if (timingsafe_memcmp(mac, &payload.mac, |
1211 |
|
|
sizeof(mac)) != 0) { |
1212 |
|
|
printf("signature mismatch!\n"); |
1213 |
|
|
return; |
1214 |
|
|
} |
1215 |
|
|
timinginfo=1; |
1216 |
|
|
|
1217 |
|
|
tp.tv_sec = betoh64(tv64->tv64_sec) - |
1218 |
|
|
tv64_offset.tv64_sec; |
1219 |
|
|
tp.tv_nsec = betoh64(tv64->tv64_nsec) - |
1220 |
|
|
tv64_offset.tv64_nsec; |
1221 |
|
|
|
1222 |
|
|
timespecsub(&ts, &tp, &ts); |
1223 |
|
|
triptime = ((double)ts.tv_sec) * 1000.0 + |
1224 |
|
|
((double)ts.tv_nsec) / 1000000.0; |
1225 |
|
|
tsum += triptime; |
1226 |
|
|
tsumsq += triptime * triptime; |
1227 |
|
|
if (triptime < tmin) |
1228 |
|
|
tmin = triptime; |
1229 |
|
|
if (triptime > tmax) |
1230 |
|
|
tmax = triptime; |
1231 |
|
|
} |
1232 |
|
|
|
1233 |
|
|
if (TST(ntohs(seq) % mx_dup_ck)) { |
1234 |
|
|
++nrepeats; |
1235 |
|
|
--nreceived; |
1236 |
|
|
dupflag = 1; |
1237 |
|
|
} else { |
1238 |
|
|
SET(ntohs(seq) % mx_dup_ck); |
1239 |
|
|
dupflag = 0; |
1240 |
|
|
} |
1241 |
|
|
|
1242 |
|
|
if (options & F_QUIET) |
1243 |
|
|
return; |
1244 |
|
|
|
1245 |
|
|
if (options & F_FLOOD) |
1246 |
|
|
write(STDOUT_FILENO, &BSPACE, 1); |
1247 |
|
|
else { |
1248 |
|
|
printf("%d bytes from %s: icmp_seq=%u", cc, |
1249 |
|
|
pr_addr(from, fromlen), ntohs(seq)); |
1250 |
|
|
if (v6flag) |
1251 |
|
|
printf(" hlim=%d", hoplim); |
1252 |
|
|
else |
1253 |
|
|
printf(" ttl=%d", ip->ip_ttl); |
1254 |
|
|
if (cc >= ECHOLEN + ECHOTMLEN) |
1255 |
|
|
printf(" time=%.3f ms", triptime); |
1256 |
|
|
if (dupflag) |
1257 |
|
|
printf(" (DUP!)"); |
1258 |
|
|
/* check the data */ |
1259 |
|
|
if (cc - ECHOLEN < datalen) |
1260 |
|
|
printf(" (TRUNC!)"); |
1261 |
|
|
if (v6flag) |
1262 |
|
|
cp = buf + ECHOLEN + ECHOTMLEN; |
1263 |
|
|
else |
1264 |
|
|
cp = (u_char *)&icp->icmp_data[ECHOTMLEN]; |
1265 |
|
|
dp = &outpack[ECHOLEN + ECHOTMLEN]; |
1266 |
|
|
for (i = ECHOLEN + ECHOTMLEN; |
1267 |
|
|
i < cc && i < datalen; |
1268 |
|
|
++i, ++cp, ++dp) { |
1269 |
|
|
if (*cp != *dp) { |
1270 |
|
|
printf("\nwrong data byte #%d " |
1271 |
|
|
"should be 0x%x but was 0x%x", |
1272 |
|
|
i - ECHOLEN, *dp, *cp); |
1273 |
|
|
if (v6flag) |
1274 |
|
|
cp = buf + ECHOLEN; |
1275 |
|
|
else |
1276 |
|
|
cp = (u_char *) |
1277 |
|
|
&icp->icmp_data[0]; |
1278 |
|
|
for (i = ECHOLEN; i < cc && i < datalen; |
1279 |
|
|
++i, ++cp) { |
1280 |
|
|
if ((i % 32) == 8) |
1281 |
|
|
printf("\n\t"); |
1282 |
|
|
printf("%x ", *cp); |
1283 |
|
|
} |
1284 |
|
|
break; |
1285 |
|
|
} |
1286 |
|
|
} |
1287 |
|
|
} |
1288 |
|
|
} else { |
1289 |
|
|
/* We've got something other than an ECHOREPLY */ |
1290 |
|
|
if (!(options & F_VERBOSE)) |
1291 |
|
|
return; |
1292 |
|
|
printf("%d bytes from %s: ", cc, pr_addr(from, fromlen)); |
1293 |
|
|
if (v6flag) |
1294 |
|
|
pr_icmph6(icp6, buf + cc); |
1295 |
|
|
else |
1296 |
|
|
pr_icmph(icp); |
1297 |
|
|
} |
1298 |
|
|
|
1299 |
|
|
/* Display any IP options */ |
1300 |
|
|
if (!v6flag && hlen > sizeof(struct ip)) |
1301 |
|
|
pr_ipopt(hlen, buf); |
1302 |
|
|
|
1303 |
|
|
if (!(options & F_FLOOD)) { |
1304 |
|
|
putchar('\n'); |
1305 |
|
|
if (v6flag && (options & F_VERBOSE)) |
1306 |
|
|
pr_exthdrs(mhdr); |
1307 |
|
|
fflush(stdout); |
1308 |
|
|
if (options & F_AUD_RECV) |
1309 |
|
|
fputc('\a', stderr); |
1310 |
|
|
} |
1311 |
|
|
} |
1312 |
|
|
|
1313 |
|
|
void |
1314 |
|
|
pr_ipopt(int hlen, u_char *buf) |
1315 |
|
|
{ |
1316 |
|
|
static int old_rrlen; |
1317 |
|
|
static char old_rr[MAX_IPOPTLEN]; |
1318 |
|
|
struct sockaddr_in s_in; |
1319 |
|
|
in_addr_t l; |
1320 |
|
|
u_int i, j; |
1321 |
|
|
u_char *cp; |
1322 |
|
|
|
1323 |
|
|
cp = buf + sizeof(struct ip); |
1324 |
|
|
|
1325 |
|
|
s_in.sin_len = sizeof(s_in); |
1326 |
|
|
s_in.sin_family = AF_INET; |
1327 |
|
|
|
1328 |
|
|
for (; hlen > sizeof(struct ip); --hlen, ++cp) { |
1329 |
|
|
switch (*cp) { |
1330 |
|
|
case IPOPT_EOL: |
1331 |
|
|
hlen = 0; |
1332 |
|
|
break; |
1333 |
|
|
case IPOPT_LSRR: |
1334 |
|
|
printf("\nLSRR: "); |
1335 |
|
|
hlen -= 2; |
1336 |
|
|
j = *++cp; |
1337 |
|
|
++cp; |
1338 |
|
|
i = 0; |
1339 |
|
|
if (j > IPOPT_MINOFF) { |
1340 |
|
|
for (;;) { |
1341 |
|
|
l = *++cp; |
1342 |
|
|
l = (l<<8) + *++cp; |
1343 |
|
|
l = (l<<8) + *++cp; |
1344 |
|
|
l = (l<<8) + *++cp; |
1345 |
|
|
if (l == 0) |
1346 |
|
|
printf("\t0.0.0.0"); |
1347 |
|
|
else { |
1348 |
|
|
s_in.sin_addr.s_addr = ntohl(l); |
1349 |
|
|
printf("\t%s", |
1350 |
|
|
pr_addr((struct sockaddr*) |
1351 |
|
|
&s_in, sizeof(s_in))); |
1352 |
|
|
} |
1353 |
|
|
hlen -= 4; |
1354 |
|
|
j -= 4; |
1355 |
|
|
i += 4; |
1356 |
|
|
if (j <= IPOPT_MINOFF) |
1357 |
|
|
break; |
1358 |
|
|
if (i >= MAX_IPOPTLEN) { |
1359 |
|
|
printf("\t(truncated route)"); |
1360 |
|
|
break; |
1361 |
|
|
} |
1362 |
|
|
putchar('\n'); |
1363 |
|
|
} |
1364 |
|
|
} |
1365 |
|
|
break; |
1366 |
|
|
case IPOPT_RR: |
1367 |
|
|
j = *++cp; /* get length */ |
1368 |
|
|
i = *++cp; /* and pointer */ |
1369 |
|
|
hlen -= 2; |
1370 |
|
|
if (i > j) |
1371 |
|
|
i = j; |
1372 |
|
|
i -= IPOPT_MINOFF; |
1373 |
|
|
if (i <= 0) |
1374 |
|
|
continue; |
1375 |
|
|
if (i == old_rrlen && |
1376 |
|
|
cp == buf + sizeof(struct ip) + 2 && |
1377 |
|
|
!memcmp(cp, old_rr, i) && |
1378 |
|
|
!(options & F_FLOOD)) { |
1379 |
|
|
printf("\t(same route)"); |
1380 |
|
|
i = (i + 3) & ~0x3; |
1381 |
|
|
hlen -= i; |
1382 |
|
|
cp += i; |
1383 |
|
|
break; |
1384 |
|
|
} |
1385 |
|
|
if (i < MAX_IPOPTLEN) { |
1386 |
|
|
old_rrlen = i; |
1387 |
|
|
memcpy(old_rr, cp, i); |
1388 |
|
|
} else |
1389 |
|
|
old_rrlen = 0; |
1390 |
|
|
|
1391 |
|
|
printf("\nRR: "); |
1392 |
|
|
j = 0; |
1393 |
|
|
for (;;) { |
1394 |
|
|
l = *++cp; |
1395 |
|
|
l = (l<<8) + *++cp; |
1396 |
|
|
l = (l<<8) + *++cp; |
1397 |
|
|
l = (l<<8) + *++cp; |
1398 |
|
|
if (l == 0) |
1399 |
|
|
printf("\t0.0.0.0"); |
1400 |
|
|
else { |
1401 |
|
|
s_in.sin_addr.s_addr = ntohl(l); |
1402 |
|
|
printf("\t%s", |
1403 |
|
|
pr_addr((struct sockaddr*)&s_in, |
1404 |
|
|
sizeof(s_in))); |
1405 |
|
|
} |
1406 |
|
|
hlen -= 4; |
1407 |
|
|
i -= 4; |
1408 |
|
|
j += 4; |
1409 |
|
|
if (i <= 0) |
1410 |
|
|
break; |
1411 |
|
|
if (j >= MAX_IPOPTLEN) { |
1412 |
|
|
printf("\t(truncated route)"); |
1413 |
|
|
break; |
1414 |
|
|
} |
1415 |
|
|
putchar('\n'); |
1416 |
|
|
} |
1417 |
|
|
break; |
1418 |
|
|
case IPOPT_NOP: |
1419 |
|
|
printf("\nNOP"); |
1420 |
|
|
break; |
1421 |
|
|
default: |
1422 |
|
|
printf("\nunknown option %x", *cp); |
1423 |
|
|
hlen = hlen - (cp[IPOPT_OLEN] - 1); |
1424 |
|
|
cp = cp + (cp[IPOPT_OLEN] - 1); |
1425 |
|
|
break; |
1426 |
|
|
} |
1427 |
|
|
} |
1428 |
|
|
} |
1429 |
|
|
|
1430 |
|
|
/* |
1431 |
|
|
* in_cksum -- |
1432 |
|
|
* Checksum routine for Internet Protocol family headers (C Version) |
1433 |
|
|
*/ |
1434 |
|
|
int |
1435 |
|
|
in_cksum(u_short *addr, int len) |
1436 |
|
|
{ |
1437 |
|
|
int nleft = len; |
1438 |
|
|
u_short *w = addr; |
1439 |
|
|
int sum = 0; |
1440 |
|
|
u_short answer = 0; |
1441 |
|
|
|
1442 |
|
|
/* |
1443 |
|
|
* Our algorithm is simple, using a 32 bit accumulator (sum), we add |
1444 |
|
|
* sequential 16 bit words to it, and at the end, fold back all the |
1445 |
|
|
* carry bits from the top 16 bits into the lower 16 bits. |
1446 |
|
|
*/ |
1447 |
|
|
while (nleft > 1) { |
1448 |
|
|
sum += *w++; |
1449 |
|
|
nleft -= 2; |
1450 |
|
|
} |
1451 |
|
|
|
1452 |
|
|
/* mop up an odd byte, if necessary */ |
1453 |
|
|
if (nleft == 1) { |
1454 |
|
|
*(u_char *)(&answer) = *(u_char *)w ; |
1455 |
|
|
sum += answer; |
1456 |
|
|
} |
1457 |
|
|
|
1458 |
|
|
/* add back carry outs from top 16 bits to low 16 bits */ |
1459 |
|
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
1460 |
|
|
sum += (sum >> 16); /* add carry */ |
1461 |
|
|
answer = ~sum; /* truncate to 16 bits */ |
1462 |
|
|
return(answer); |
1463 |
|
|
} |
1464 |
|
|
|
1465 |
|
|
/* |
1466 |
|
|
* pr_icmph -- |
1467 |
|
|
* Print a descriptive string about an ICMP header. |
1468 |
|
|
*/ |
1469 |
|
|
void |
1470 |
|
|
pr_icmph(struct icmp *icp) |
1471 |
|
|
{ |
1472 |
|
|
switch(icp->icmp_type) { |
1473 |
|
|
case ICMP_ECHOREPLY: |
1474 |
|
|
printf("Echo Reply\n"); |
1475 |
|
|
/* XXX ID + Seq + Data */ |
1476 |
|
|
break; |
1477 |
|
|
case ICMP_UNREACH: |
1478 |
|
|
switch(icp->icmp_code) { |
1479 |
|
|
case ICMP_UNREACH_NET: |
1480 |
|
|
printf("Destination Net Unreachable\n"); |
1481 |
|
|
break; |
1482 |
|
|
case ICMP_UNREACH_HOST: |
1483 |
|
|
printf("Destination Host Unreachable\n"); |
1484 |
|
|
break; |
1485 |
|
|
case ICMP_UNREACH_PROTOCOL: |
1486 |
|
|
printf("Destination Protocol Unreachable\n"); |
1487 |
|
|
break; |
1488 |
|
|
case ICMP_UNREACH_PORT: |
1489 |
|
|
printf("Destination Port Unreachable\n"); |
1490 |
|
|
break; |
1491 |
|
|
case ICMP_UNREACH_NEEDFRAG: |
1492 |
|
|
if (icp->icmp_nextmtu != 0) |
1493 |
|
|
printf("frag needed and DF set (MTU %d)\n", |
1494 |
|
|
ntohs(icp->icmp_nextmtu)); |
1495 |
|
|
else |
1496 |
|
|
printf("frag needed and DF set\n"); |
1497 |
|
|
break; |
1498 |
|
|
case ICMP_UNREACH_SRCFAIL: |
1499 |
|
|
printf("Source Route Failed\n"); |
1500 |
|
|
break; |
1501 |
|
|
case ICMP_UNREACH_NET_UNKNOWN: |
1502 |
|
|
printf("Network Unknown\n"); |
1503 |
|
|
break; |
1504 |
|
|
case ICMP_UNREACH_HOST_UNKNOWN: |
1505 |
|
|
printf("Host Unknown\n"); |
1506 |
|
|
break; |
1507 |
|
|
case ICMP_UNREACH_ISOLATED: |
1508 |
|
|
printf("Source Isolated\n"); |
1509 |
|
|
break; |
1510 |
|
|
case ICMP_UNREACH_NET_PROHIB: |
1511 |
|
|
printf("Dest. Net Administratively Prohibited\n"); |
1512 |
|
|
break; |
1513 |
|
|
case ICMP_UNREACH_HOST_PROHIB: |
1514 |
|
|
printf("Dest. Host Administratively Prohibited\n"); |
1515 |
|
|
break; |
1516 |
|
|
case ICMP_UNREACH_TOSNET: |
1517 |
|
|
printf("Destination Net Unreachable for TOS\n"); |
1518 |
|
|
break; |
1519 |
|
|
case ICMP_UNREACH_TOSHOST: |
1520 |
|
|
printf("Destination Host Unreachable for TOS\n"); |
1521 |
|
|
break; |
1522 |
|
|
case ICMP_UNREACH_FILTER_PROHIB: |
1523 |
|
|
printf("Route administratively prohibited\n"); |
1524 |
|
|
break; |
1525 |
|
|
case ICMP_UNREACH_HOST_PRECEDENCE: |
1526 |
|
|
printf("Host Precedence Violation\n"); |
1527 |
|
|
break; |
1528 |
|
|
case ICMP_UNREACH_PRECEDENCE_CUTOFF: |
1529 |
|
|
printf("Precedence Cutoff\n"); |
1530 |
|
|
break; |
1531 |
|
|
default: |
1532 |
|
|
printf("Dest Unreachable, Unknown Code: %d\n", |
1533 |
|
|
icp->icmp_code); |
1534 |
|
|
break; |
1535 |
|
|
} |
1536 |
|
|
/* Print returned IP header information */ |
1537 |
|
|
pr_retip((struct ip *)icp->icmp_data); |
1538 |
|
|
break; |
1539 |
|
|
case ICMP_SOURCEQUENCH: |
1540 |
|
|
printf("Source Quench\n"); |
1541 |
|
|
pr_retip((struct ip *)icp->icmp_data); |
1542 |
|
|
break; |
1543 |
|
|
case ICMP_REDIRECT: |
1544 |
|
|
switch(icp->icmp_code) { |
1545 |
|
|
case ICMP_REDIRECT_NET: |
1546 |
|
|
printf("Redirect Network"); |
1547 |
|
|
break; |
1548 |
|
|
case ICMP_REDIRECT_HOST: |
1549 |
|
|
printf("Redirect Host"); |
1550 |
|
|
break; |
1551 |
|
|
case ICMP_REDIRECT_TOSNET: |
1552 |
|
|
printf("Redirect Type of Service and Network"); |
1553 |
|
|
break; |
1554 |
|
|
case ICMP_REDIRECT_TOSHOST: |
1555 |
|
|
printf("Redirect Type of Service and Host"); |
1556 |
|
|
break; |
1557 |
|
|
default: |
1558 |
|
|
printf("Redirect, Unknown Code: %d", icp->icmp_code); |
1559 |
|
|
break; |
1560 |
|
|
} |
1561 |
|
|
printf("(New addr: %s)\n", |
1562 |
|
|
inet_ntoa(icp->icmp_gwaddr)); |
1563 |
|
|
pr_retip((struct ip *)icp->icmp_data); |
1564 |
|
|
break; |
1565 |
|
|
case ICMP_ECHO: |
1566 |
|
|
printf("Echo Request\n"); |
1567 |
|
|
/* XXX ID + Seq + Data */ |
1568 |
|
|
break; |
1569 |
|
|
case ICMP_ROUTERADVERT: |
1570 |
|
|
/* RFC1256 */ |
1571 |
|
|
printf("Router Discovery Advertisement\n"); |
1572 |
|
|
printf("(%d entries, lifetime %d seconds)\n", |
1573 |
|
|
icp->icmp_num_addrs, ntohs(icp->icmp_lifetime)); |
1574 |
|
|
break; |
1575 |
|
|
case ICMP_ROUTERSOLICIT: |
1576 |
|
|
/* RFC1256 */ |
1577 |
|
|
printf("Router Discovery Solicitation\n"); |
1578 |
|
|
break; |
1579 |
|
|
case ICMP_TIMXCEED: |
1580 |
|
|
switch(icp->icmp_code) { |
1581 |
|
|
case ICMP_TIMXCEED_INTRANS: |
1582 |
|
|
printf("Time to live exceeded\n"); |
1583 |
|
|
break; |
1584 |
|
|
case ICMP_TIMXCEED_REASS: |
1585 |
|
|
printf("Frag reassembly time exceeded\n"); |
1586 |
|
|
break; |
1587 |
|
|
default: |
1588 |
|
|
printf("Time exceeded, Unknown Code: %d\n", |
1589 |
|
|
icp->icmp_code); |
1590 |
|
|
break; |
1591 |
|
|
} |
1592 |
|
|
pr_retip((struct ip *)icp->icmp_data); |
1593 |
|
|
break; |
1594 |
|
|
case ICMP_PARAMPROB: |
1595 |
|
|
switch(icp->icmp_code) { |
1596 |
|
|
case ICMP_PARAMPROB_OPTABSENT: |
1597 |
|
|
printf("Parameter problem, required option " |
1598 |
|
|
"absent: pointer = 0x%02x\n", |
1599 |
|
|
ntohs(icp->icmp_hun.ih_pptr)); |
1600 |
|
|
break; |
1601 |
|
|
default: |
1602 |
|
|
printf("Parameter problem: pointer = 0x%02x\n", |
1603 |
|
|
ntohs(icp->icmp_hun.ih_pptr)); |
1604 |
|
|
break; |
1605 |
|
|
} |
1606 |
|
|
pr_retip((struct ip *)icp->icmp_data); |
1607 |
|
|
break; |
1608 |
|
|
case ICMP_TSTAMP: |
1609 |
|
|
printf("Timestamp\n"); |
1610 |
|
|
/* XXX ID + Seq + 3 timestamps */ |
1611 |
|
|
break; |
1612 |
|
|
case ICMP_TSTAMPREPLY: |
1613 |
|
|
printf("Timestamp Reply\n"); |
1614 |
|
|
/* XXX ID + Seq + 3 timestamps */ |
1615 |
|
|
break; |
1616 |
|
|
case ICMP_IREQ: |
1617 |
|
|
printf("Information Request\n"); |
1618 |
|
|
/* XXX ID + Seq */ |
1619 |
|
|
break; |
1620 |
|
|
case ICMP_IREQREPLY: |
1621 |
|
|
printf("Information Reply\n"); |
1622 |
|
|
/* XXX ID + Seq */ |
1623 |
|
|
break; |
1624 |
|
|
case ICMP_MASKREQ: |
1625 |
|
|
printf("Address Mask Request\n"); |
1626 |
|
|
break; |
1627 |
|
|
case ICMP_MASKREPLY: |
1628 |
|
|
printf("Address Mask Reply (Mask 0x%08x)\n", |
1629 |
|
|
ntohl(icp->icmp_mask)); |
1630 |
|
|
break; |
1631 |
|
|
default: |
1632 |
|
|
printf("Unknown ICMP type: %d\n", icp->icmp_type); |
1633 |
|
|
} |
1634 |
|
|
} |
1635 |
|
|
|
1636 |
|
|
/* |
1637 |
|
|
* pr_iph -- |
1638 |
|
|
* Print an IP header with options. |
1639 |
|
|
*/ |
1640 |
|
|
void |
1641 |
|
|
pr_iph(struct ip *ip) |
1642 |
|
|
{ |
1643 |
|
|
int hlen; |
1644 |
|
|
u_char *cp; |
1645 |
|
|
|
1646 |
|
|
hlen = ip->ip_hl << 2; |
1647 |
|
|
cp = (u_char *)ip + 20; /* point to options */ |
1648 |
|
|
|
1649 |
|
|
printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); |
1650 |
|
|
printf(" %1x %1x %02x %04x %04x", |
1651 |
|
|
ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); |
1652 |
|
|
printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, |
1653 |
|
|
(ip->ip_off) & 0x1fff); |
1654 |
|
|
printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); |
1655 |
|
|
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); |
1656 |
|
|
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); |
1657 |
|
|
/* dump and option bytes */ |
1658 |
|
|
while (hlen-- > 20) { |
1659 |
|
|
printf("%02x", *cp++); |
1660 |
|
|
} |
1661 |
|
|
putchar('\n'); |
1662 |
|
|
} |
1663 |
|
|
|
1664 |
|
|
/* |
1665 |
|
|
* pr_retip -- |
1666 |
|
|
* Dump some info on a returned (via ICMP) IP packet. |
1667 |
|
|
*/ |
1668 |
|
|
void |
1669 |
|
|
pr_retip(struct ip *ip) |
1670 |
|
|
{ |
1671 |
|
|
int hlen; |
1672 |
|
|
u_char *cp; |
1673 |
|
|
|
1674 |
|
|
pr_iph(ip); |
1675 |
|
|
hlen = ip->ip_hl << 2; |
1676 |
|
|
cp = (u_char *)ip + hlen; |
1677 |
|
|
|
1678 |
|
|
if (ip->ip_p == 6) |
1679 |
|
|
printf("TCP: from port %u, to port %u (decimal)\n", |
1680 |
|
|
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); |
1681 |
|
|
else if (ip->ip_p == 17) |
1682 |
|
|
printf("UDP: from port %u, to port %u (decimal)\n", |
1683 |
|
|
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); |
1684 |
|
|
} |
1685 |
|
|
|
1686 |
|
|
#ifndef SMALL |
1687 |
|
|
int |
1688 |
|
|
map_tos(char *key, int *val) |
1689 |
|
|
{ |
1690 |
|
|
/* DiffServ Codepoints and other TOS mappings */ |
1691 |
|
|
const struct toskeywords { |
1692 |
|
|
const char *keyword; |
1693 |
|
|
int val; |
1694 |
|
|
} *t, toskeywords[] = { |
1695 |
|
|
{ "af11", IPTOS_DSCP_AF11 }, |
1696 |
|
|
{ "af12", IPTOS_DSCP_AF12 }, |
1697 |
|
|
{ "af13", IPTOS_DSCP_AF13 }, |
1698 |
|
|
{ "af21", IPTOS_DSCP_AF21 }, |
1699 |
|
|
{ "af22", IPTOS_DSCP_AF22 }, |
1700 |
|
|
{ "af23", IPTOS_DSCP_AF23 }, |
1701 |
|
|
{ "af31", IPTOS_DSCP_AF31 }, |
1702 |
|
|
{ "af32", IPTOS_DSCP_AF32 }, |
1703 |
|
|
{ "af33", IPTOS_DSCP_AF33 }, |
1704 |
|
|
{ "af41", IPTOS_DSCP_AF41 }, |
1705 |
|
|
{ "af42", IPTOS_DSCP_AF42 }, |
1706 |
|
|
{ "af43", IPTOS_DSCP_AF43 }, |
1707 |
|
|
{ "critical", IPTOS_PREC_CRITIC_ECP }, |
1708 |
|
|
{ "cs0", IPTOS_DSCP_CS0 }, |
1709 |
|
|
{ "cs1", IPTOS_DSCP_CS1 }, |
1710 |
|
|
{ "cs2", IPTOS_DSCP_CS2 }, |
1711 |
|
|
{ "cs3", IPTOS_DSCP_CS3 }, |
1712 |
|
|
{ "cs4", IPTOS_DSCP_CS4 }, |
1713 |
|
|
{ "cs5", IPTOS_DSCP_CS5 }, |
1714 |
|
|
{ "cs6", IPTOS_DSCP_CS6 }, |
1715 |
|
|
{ "cs7", IPTOS_DSCP_CS7 }, |
1716 |
|
|
{ "ef", IPTOS_DSCP_EF }, |
1717 |
|
|
{ "inetcontrol", IPTOS_PREC_INTERNETCONTROL }, |
1718 |
|
|
{ "lowdelay", IPTOS_LOWDELAY }, |
1719 |
|
|
{ "netcontrol", IPTOS_PREC_NETCONTROL }, |
1720 |
|
|
{ "reliability", IPTOS_RELIABILITY }, |
1721 |
|
|
{ "throughput", IPTOS_THROUGHPUT }, |
1722 |
|
|
{ NULL, -1 }, |
1723 |
|
|
}; |
1724 |
|
|
|
1725 |
|
|
for (t = toskeywords; t->keyword != NULL; t++) { |
1726 |
|
|
if (strcmp(key, t->keyword) == 0) { |
1727 |
|
|
*val = t->val; |
1728 |
|
|
return (1); |
1729 |
|
|
} |
1730 |
|
|
} |
1731 |
|
|
|
1732 |
|
|
return (0); |
1733 |
|
|
} |
1734 |
|
|
#endif /* SMALL */ |
1735 |
|
|
|
1736 |
|
|
void |
1737 |
|
|
pr_exthdrs(struct msghdr *mhdr) |
1738 |
|
|
{ |
1739 |
|
|
struct cmsghdr *cm; |
1740 |
|
|
|
1741 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1742 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1743 |
|
|
if (cm->cmsg_level != IPPROTO_IPV6) |
1744 |
|
|
continue; |
1745 |
|
|
|
1746 |
|
|
switch (cm->cmsg_type) { |
1747 |
|
|
case IPV6_HOPOPTS: |
1748 |
|
|
printf(" HbH Options: "); |
1749 |
|
|
pr_ip6opt(CMSG_DATA(cm)); |
1750 |
|
|
break; |
1751 |
|
|
case IPV6_DSTOPTS: |
1752 |
|
|
case IPV6_RTHDRDSTOPTS: |
1753 |
|
|
printf(" Dst Options: "); |
1754 |
|
|
pr_ip6opt(CMSG_DATA(cm)); |
1755 |
|
|
break; |
1756 |
|
|
case IPV6_RTHDR: |
1757 |
|
|
printf(" Routing: "); |
1758 |
|
|
pr_rthdr(CMSG_DATA(cm)); |
1759 |
|
|
break; |
1760 |
|
|
} |
1761 |
|
|
} |
1762 |
|
|
} |
1763 |
|
|
|
1764 |
|
|
void |
1765 |
|
|
pr_ip6opt(void *extbuf) |
1766 |
|
|
{ |
1767 |
|
|
struct ip6_hbh *ext; |
1768 |
|
|
int currentlen; |
1769 |
|
|
u_int8_t type; |
1770 |
|
|
size_t extlen; |
1771 |
|
|
socklen_t len; |
1772 |
|
|
void *databuf; |
1773 |
|
|
u_int16_t value2; |
1774 |
|
|
u_int32_t value4; |
1775 |
|
|
|
1776 |
|
|
ext = (struct ip6_hbh *)extbuf; |
1777 |
|
|
extlen = (ext->ip6h_len + 1) * 8; |
1778 |
|
|
printf("nxt %u, len %u (%lu bytes)\n", ext->ip6h_nxt, |
1779 |
|
|
(unsigned int)ext->ip6h_len, (unsigned long)extlen); |
1780 |
|
|
|
1781 |
|
|
currentlen = 0; |
1782 |
|
|
while (1) { |
1783 |
|
|
currentlen = inet6_opt_next(extbuf, extlen, currentlen, |
1784 |
|
|
&type, &len, &databuf); |
1785 |
|
|
if (currentlen == -1) |
1786 |
|
|
break; |
1787 |
|
|
switch (type) { |
1788 |
|
|
/* |
1789 |
|
|
* Note that inet6_opt_next automatically skips any padding |
1790 |
|
|
* options. |
1791 |
|
|
*/ |
1792 |
|
|
case IP6OPT_JUMBO: |
1793 |
|
|
inet6_opt_get_val(databuf, 0, &value4, sizeof(value4)); |
1794 |
|
|
printf(" Jumbo Payload Opt: Length %u\n", |
1795 |
|
|
(u_int32_t)ntohl(value4)); |
1796 |
|
|
break; |
1797 |
|
|
case IP6OPT_ROUTER_ALERT: |
1798 |
|
|
inet6_opt_get_val(databuf, 0, &value2, sizeof(value2)); |
1799 |
|
|
printf(" Router Alert Opt: Type %u\n", |
1800 |
|
|
ntohs(value2)); |
1801 |
|
|
break; |
1802 |
|
|
default: |
1803 |
|
|
printf(" Received Opt %u len %lu\n", |
1804 |
|
|
type, (unsigned long)len); |
1805 |
|
|
break; |
1806 |
|
|
} |
1807 |
|
|
} |
1808 |
|
|
return; |
1809 |
|
|
} |
1810 |
|
|
|
1811 |
|
|
void |
1812 |
|
|
pr_rthdr(void *extbuf) |
1813 |
|
|
{ |
1814 |
|
|
struct in6_addr *in6; |
1815 |
|
|
char ntopbuf[INET6_ADDRSTRLEN]; |
1816 |
|
|
struct ip6_rthdr *rh = (struct ip6_rthdr *)extbuf; |
1817 |
|
|
int i, segments; |
1818 |
|
|
|
1819 |
|
|
/* print fixed part of the header */ |
1820 |
|
|
printf("nxt %u, len %u (%d bytes), type %u, ", rh->ip6r_nxt, |
1821 |
|
|
rh->ip6r_len, (rh->ip6r_len + 1) << 3, rh->ip6r_type); |
1822 |
|
|
if ((segments = inet6_rth_segments(extbuf)) >= 0) |
1823 |
|
|
printf("%d segments, ", segments); |
1824 |
|
|
else |
1825 |
|
|
printf("segments unknown, "); |
1826 |
|
|
printf("%d left\n", rh->ip6r_segleft); |
1827 |
|
|
|
1828 |
|
|
for (i = 0; i < segments; i++) { |
1829 |
|
|
in6 = inet6_rth_getaddr(extbuf, i); |
1830 |
|
|
if (in6 == NULL) |
1831 |
|
|
printf(" [%d]<NULL>\n", i); |
1832 |
|
|
else { |
1833 |
|
|
if (!inet_ntop(AF_INET6, in6, ntopbuf, |
1834 |
|
|
sizeof(ntopbuf))) |
1835 |
|
|
strncpy(ntopbuf, "?", sizeof(ntopbuf)); |
1836 |
|
|
printf(" [%d]%s\n", i, ntopbuf); |
1837 |
|
|
} |
1838 |
|
|
} |
1839 |
|
|
|
1840 |
|
|
return; |
1841 |
|
|
|
1842 |
|
|
} |
1843 |
|
|
|
1844 |
|
|
int |
1845 |
|
|
get_hoplim(struct msghdr *mhdr) |
1846 |
|
|
{ |
1847 |
|
|
struct cmsghdr *cm; |
1848 |
|
|
|
1849 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1850 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1851 |
|
|
if (cm->cmsg_len == 0) |
1852 |
|
|
return(-1); |
1853 |
|
|
|
1854 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
1855 |
|
|
cm->cmsg_type == IPV6_HOPLIMIT && |
1856 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(int))) |
1857 |
|
|
return(*(int *)CMSG_DATA(cm)); |
1858 |
|
|
} |
1859 |
|
|
|
1860 |
|
|
return(-1); |
1861 |
|
|
} |
1862 |
|
|
|
1863 |
|
|
int |
1864 |
|
|
get_pathmtu(struct msghdr *mhdr, struct sockaddr_in6 *dst) |
1865 |
|
|
{ |
1866 |
|
|
struct cmsghdr *cm; |
1867 |
|
|
struct ip6_mtuinfo *mtuctl = NULL; |
1868 |
|
|
|
1869 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; |
1870 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { |
1871 |
|
|
if (cm->cmsg_len == 0) |
1872 |
|
|
return(0); |
1873 |
|
|
|
1874 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
1875 |
|
|
cm->cmsg_type == IPV6_PATHMTU && |
1876 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(struct ip6_mtuinfo))) { |
1877 |
|
|
mtuctl = (struct ip6_mtuinfo *)CMSG_DATA(cm); |
1878 |
|
|
|
1879 |
|
|
/* |
1880 |
|
|
* If the notified destination is different from |
1881 |
|
|
* the one we are pinging, just ignore the info. |
1882 |
|
|
* We check the scope ID only when both notified value |
1883 |
|
|
* and our own value have non-0 values, because we may |
1884 |
|
|
* have used the default scope zone ID for sending, |
1885 |
|
|
* in which case the scope ID value is 0. |
1886 |
|
|
*/ |
1887 |
|
|
if (!IN6_ARE_ADDR_EQUAL(&mtuctl->ip6m_addr.sin6_addr, |
1888 |
|
|
&dst->sin6_addr) || |
1889 |
|
|
(mtuctl->ip6m_addr.sin6_scope_id && |
1890 |
|
|
dst->sin6_scope_id && |
1891 |
|
|
mtuctl->ip6m_addr.sin6_scope_id != |
1892 |
|
|
dst->sin6_scope_id)) { |
1893 |
|
|
if (options & F_VERBOSE) { |
1894 |
|
|
printf("path MTU for %s is notified. " |
1895 |
|
|
"(ignored)\n", |
1896 |
|
|
pr_addr((struct sockaddr *) |
1897 |
|
|
&mtuctl->ip6m_addr, |
1898 |
|
|
sizeof(mtuctl->ip6m_addr))); |
1899 |
|
|
} |
1900 |
|
|
return(0); |
1901 |
|
|
} |
1902 |
|
|
|
1903 |
|
|
/* |
1904 |
|
|
* Ignore an invalid MTU. XXX: can we just believe |
1905 |
|
|
* the kernel check? |
1906 |
|
|
*/ |
1907 |
|
|
if (mtuctl->ip6m_mtu < IPV6_MMTU) |
1908 |
|
|
return(0); |
1909 |
|
|
|
1910 |
|
|
/* notification for our destination. return the MTU. */ |
1911 |
|
|
return((int)mtuctl->ip6m_mtu); |
1912 |
|
|
} |
1913 |
|
|
} |
1914 |
|
|
return(0); |
1915 |
|
|
} |
1916 |
|
|
|
1917 |
|
|
/* |
1918 |
|
|
* pr_icmph6 -- |
1919 |
|
|
* Print a descriptive string about an ICMP header. |
1920 |
|
|
*/ |
1921 |
|
|
void |
1922 |
|
|
pr_icmph6(struct icmp6_hdr *icp, u_char *end) |
1923 |
|
|
{ |
1924 |
|
|
char ntop_buf[INET6_ADDRSTRLEN]; |
1925 |
|
|
struct nd_redirect *red; |
1926 |
|
|
|
1927 |
|
|
switch (icp->icmp6_type) { |
1928 |
|
|
case ICMP6_DST_UNREACH: |
1929 |
|
|
switch (icp->icmp6_code) { |
1930 |
|
|
case ICMP6_DST_UNREACH_NOROUTE: |
1931 |
|
|
printf("No Route to Destination\n"); |
1932 |
|
|
break; |
1933 |
|
|
case ICMP6_DST_UNREACH_ADMIN: |
1934 |
|
|
printf("Destination Administratively " |
1935 |
|
|
"Unreachable\n"); |
1936 |
|
|
break; |
1937 |
|
|
case ICMP6_DST_UNREACH_BEYONDSCOPE: |
1938 |
|
|
printf("Destination Unreachable Beyond Scope\n"); |
1939 |
|
|
break; |
1940 |
|
|
case ICMP6_DST_UNREACH_ADDR: |
1941 |
|
|
printf("Destination Host Unreachable\n"); |
1942 |
|
|
break; |
1943 |
|
|
case ICMP6_DST_UNREACH_NOPORT: |
1944 |
|
|
printf("Destination Port Unreachable\n"); |
1945 |
|
|
break; |
1946 |
|
|
default: |
1947 |
|
|
printf("Destination Unreachable, Bad Code: %d\n", |
1948 |
|
|
icp->icmp6_code); |
1949 |
|
|
break; |
1950 |
|
|
} |
1951 |
|
|
/* Print returned IP header information */ |
1952 |
|
|
pr_retip6((struct ip6_hdr *)(icp + 1), end); |
1953 |
|
|
break; |
1954 |
|
|
case ICMP6_PACKET_TOO_BIG: |
1955 |
|
|
printf("Packet too big mtu = %d\n", |
1956 |
|
|
(int)ntohl(icp->icmp6_mtu)); |
1957 |
|
|
pr_retip6((struct ip6_hdr *)(icp + 1), end); |
1958 |
|
|
break; |
1959 |
|
|
case ICMP6_TIME_EXCEEDED: |
1960 |
|
|
switch (icp->icmp6_code) { |
1961 |
|
|
case ICMP6_TIME_EXCEED_TRANSIT: |
1962 |
|
|
printf("Time to live exceeded\n"); |
1963 |
|
|
break; |
1964 |
|
|
case ICMP6_TIME_EXCEED_REASSEMBLY: |
1965 |
|
|
printf("Frag reassembly time exceeded\n"); |
1966 |
|
|
break; |
1967 |
|
|
default: |
1968 |
|
|
printf("Time exceeded, Bad Code: %d\n", |
1969 |
|
|
icp->icmp6_code); |
1970 |
|
|
break; |
1971 |
|
|
} |
1972 |
|
|
pr_retip6((struct ip6_hdr *)(icp + 1), end); |
1973 |
|
|
break; |
1974 |
|
|
case ICMP6_PARAM_PROB: |
1975 |
|
|
printf("Parameter problem: "); |
1976 |
|
|
switch (icp->icmp6_code) { |
1977 |
|
|
case ICMP6_PARAMPROB_HEADER: |
1978 |
|
|
printf("Erroneous Header "); |
1979 |
|
|
break; |
1980 |
|
|
case ICMP6_PARAMPROB_NEXTHEADER: |
1981 |
|
|
printf("Unknown Nextheader "); |
1982 |
|
|
break; |
1983 |
|
|
case ICMP6_PARAMPROB_OPTION: |
1984 |
|
|
printf("Unrecognized Option "); |
1985 |
|
|
break; |
1986 |
|
|
default: |
1987 |
|
|
printf("Bad code(%d) ", icp->icmp6_code); |
1988 |
|
|
break; |
1989 |
|
|
} |
1990 |
|
|
printf("pointer = 0x%02x\n", |
1991 |
|
|
(u_int32_t)ntohl(icp->icmp6_pptr)); |
1992 |
|
|
pr_retip6((struct ip6_hdr *)(icp + 1), end); |
1993 |
|
|
break; |
1994 |
|
|
case ICMP6_ECHO_REQUEST: |
1995 |
|
|
printf("Echo Request"); |
1996 |
|
|
/* XXX ID + Seq + Data */ |
1997 |
|
|
break; |
1998 |
|
|
case ICMP6_ECHO_REPLY: |
1999 |
|
|
printf("Echo Reply"); |
2000 |
|
|
/* XXX ID + Seq + Data */ |
2001 |
|
|
break; |
2002 |
|
|
case ICMP6_MEMBERSHIP_QUERY: |
2003 |
|
|
printf("Listener Query"); |
2004 |
|
|
break; |
2005 |
|
|
case ICMP6_MEMBERSHIP_REPORT: |
2006 |
|
|
printf("Listener Report"); |
2007 |
|
|
break; |
2008 |
|
|
case ICMP6_MEMBERSHIP_REDUCTION: |
2009 |
|
|
printf("Listener Done"); |
2010 |
|
|
break; |
2011 |
|
|
case ND_ROUTER_SOLICIT: |
2012 |
|
|
printf("Router Solicitation"); |
2013 |
|
|
break; |
2014 |
|
|
case ND_ROUTER_ADVERT: |
2015 |
|
|
printf("Router Advertisement"); |
2016 |
|
|
break; |
2017 |
|
|
case ND_NEIGHBOR_SOLICIT: |
2018 |
|
|
printf("Neighbor Solicitation"); |
2019 |
|
|
break; |
2020 |
|
|
case ND_NEIGHBOR_ADVERT: |
2021 |
|
|
printf("Neighbor Advertisement"); |
2022 |
|
|
break; |
2023 |
|
|
case ND_REDIRECT: |
2024 |
|
|
red = (struct nd_redirect *)icp; |
2025 |
|
|
printf("Redirect\n"); |
2026 |
|
|
if (!inet_ntop(AF_INET6, &red->nd_rd_dst, ntop_buf, |
2027 |
|
|
sizeof(ntop_buf))) |
2028 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
2029 |
|
|
printf("Destination: %s", ntop_buf); |
2030 |
|
|
if (!inet_ntop(AF_INET6, &red->nd_rd_target, ntop_buf, |
2031 |
|
|
sizeof(ntop_buf))) |
2032 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
2033 |
|
|
printf(" New Target: %s", ntop_buf); |
2034 |
|
|
break; |
2035 |
|
|
default: |
2036 |
|
|
printf("Bad ICMP type: %d", icp->icmp6_type); |
2037 |
|
|
} |
2038 |
|
|
} |
2039 |
|
|
|
2040 |
|
|
/* |
2041 |
|
|
* pr_iph6 -- |
2042 |
|
|
* Print an IP6 header. |
2043 |
|
|
*/ |
2044 |
|
|
void |
2045 |
|
|
pr_iph6(struct ip6_hdr *ip6) |
2046 |
|
|
{ |
2047 |
|
|
u_int32_t flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; |
2048 |
|
|
u_int8_t tc; |
2049 |
|
|
char ntop_buf[INET6_ADDRSTRLEN]; |
2050 |
|
|
|
2051 |
|
|
tc = *(&ip6->ip6_vfc + 1); /* XXX */ |
2052 |
|
|
tc = (tc >> 4) & 0x0f; |
2053 |
|
|
tc |= (ip6->ip6_vfc << 4); |
2054 |
|
|
|
2055 |
|
|
printf("Vr TC Flow Plen Nxt Hlim\n"); |
2056 |
|
|
printf(" %1x %02x %05x %04x %02x %02x\n", |
2057 |
|
|
(ip6->ip6_vfc & IPV6_VERSION_MASK) >> 4, tc, (u_int32_t)ntohl(flow), |
2058 |
|
|
ntohs(ip6->ip6_plen), ip6->ip6_nxt, ip6->ip6_hlim); |
2059 |
|
|
if (!inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf, sizeof(ntop_buf))) |
2060 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
2061 |
|
|
printf("%s->", ntop_buf); |
2062 |
|
|
if (!inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf, sizeof(ntop_buf))) |
2063 |
|
|
strncpy(ntop_buf, "?", sizeof(ntop_buf)); |
2064 |
|
|
printf("%s\n", ntop_buf); |
2065 |
|
|
} |
2066 |
|
|
|
2067 |
|
|
/* |
2068 |
|
|
* pr_retip6 -- |
2069 |
|
|
* Dump some info on a returned (via ICMPv6) IPv6 packet. |
2070 |
|
|
*/ |
2071 |
|
|
void |
2072 |
|
|
pr_retip6(struct ip6_hdr *ip6, u_char *end) |
2073 |
|
|
{ |
2074 |
|
|
u_char *cp = (u_char *)ip6, nh; |
2075 |
|
|
int hlen; |
2076 |
|
|
|
2077 |
|
|
if (end - (u_char *)ip6 < sizeof(*ip6)) { |
2078 |
|
|
printf("IP6"); |
2079 |
|
|
goto trunc; |
2080 |
|
|
} |
2081 |
|
|
pr_iph6(ip6); |
2082 |
|
|
hlen = sizeof(*ip6); |
2083 |
|
|
|
2084 |
|
|
nh = ip6->ip6_nxt; |
2085 |
|
|
cp += hlen; |
2086 |
|
|
while (end - cp >= 8) { |
2087 |
|
|
switch (nh) { |
2088 |
|
|
case IPPROTO_HOPOPTS: |
2089 |
|
|
printf("HBH "); |
2090 |
|
|
hlen = (((struct ip6_hbh *)cp)->ip6h_len+1) << 3; |
2091 |
|
|
nh = ((struct ip6_hbh *)cp)->ip6h_nxt; |
2092 |
|
|
break; |
2093 |
|
|
case IPPROTO_DSTOPTS: |
2094 |
|
|
printf("DSTOPT "); |
2095 |
|
|
hlen = (((struct ip6_dest *)cp)->ip6d_len+1) << 3; |
2096 |
|
|
nh = ((struct ip6_dest *)cp)->ip6d_nxt; |
2097 |
|
|
break; |
2098 |
|
|
case IPPROTO_FRAGMENT: |
2099 |
|
|
printf("FRAG "); |
2100 |
|
|
hlen = sizeof(struct ip6_frag); |
2101 |
|
|
nh = ((struct ip6_frag *)cp)->ip6f_nxt; |
2102 |
|
|
break; |
2103 |
|
|
case IPPROTO_ROUTING: |
2104 |
|
|
printf("RTHDR "); |
2105 |
|
|
hlen = (((struct ip6_rthdr *)cp)->ip6r_len+1) << 3; |
2106 |
|
|
nh = ((struct ip6_rthdr *)cp)->ip6r_nxt; |
2107 |
|
|
break; |
2108 |
|
|
case IPPROTO_AH: |
2109 |
|
|
printf("AH "); |
2110 |
|
|
hlen = (((struct ah *)cp)->ah_hl+2) << 2; |
2111 |
|
|
nh = ((struct ah *)cp)->ah_nh; |
2112 |
|
|
break; |
2113 |
|
|
case IPPROTO_ICMPV6: |
2114 |
|
|
printf("ICMP6: type = %d, code = %d\n", |
2115 |
|
|
*cp, *(cp + 1)); |
2116 |
|
|
return; |
2117 |
|
|
case IPPROTO_ESP: |
2118 |
|
|
printf("ESP\n"); |
2119 |
|
|
return; |
2120 |
|
|
case IPPROTO_TCP: |
2121 |
|
|
printf("TCP: from port %u, to port %u (decimal)\n", |
2122 |
|
|
(*cp * 256 + *(cp + 1)), |
2123 |
|
|
(*(cp + 2) * 256 + *(cp + 3))); |
2124 |
|
|
return; |
2125 |
|
|
case IPPROTO_UDP: |
2126 |
|
|
printf("UDP: from port %u, to port %u (decimal)\n", |
2127 |
|
|
(*cp * 256 + *(cp + 1)), |
2128 |
|
|
(*(cp + 2) * 256 + *(cp + 3))); |
2129 |
|
|
return; |
2130 |
|
|
default: |
2131 |
|
|
printf("Unknown Header(%d)\n", nh); |
2132 |
|
|
return; |
2133 |
|
|
} |
2134 |
|
|
|
2135 |
|
|
if ((cp += hlen) >= end) |
2136 |
|
|
goto trunc; |
2137 |
|
|
} |
2138 |
|
|
if (end - cp < 8) |
2139 |
|
|
goto trunc; |
2140 |
|
|
|
2141 |
|
|
putchar('\n'); |
2142 |
|
|
return; |
2143 |
|
|
|
2144 |
|
|
trunc: |
2145 |
|
|
printf("...\n"); |
2146 |
|
|
return; |
2147 |
|
|
} |
2148 |
|
|
|
2149 |
|
|
__dead void |
2150 |
|
|
usage(void) |
2151 |
|
|
{ |
2152 |
|
|
if (v6flag) { |
2153 |
|
|
fprintf(stderr, |
2154 |
|
|
"usage: ping6 [-dEefHLmnqv] [-c count] [-h hoplimit] " |
2155 |
|
|
"[-I sourceaddr]\n\t[-i wait] [-l preload] [-p pattern] " |
2156 |
|
|
"[-s packetsize] [-V rtable]\n\t[-w maxwait] host\n"); |
2157 |
|
|
} else { |
2158 |
|
|
fprintf(stderr, |
2159 |
|
|
"usage: ping [-DdEefHLnqRv] [-c count] [-I ifaddr]" |
2160 |
|
|
" [-i wait]\n\t[-l preload] [-p pattern] [-s packetsize]" |
2161 |
|
|
#ifndef SMALL |
2162 |
|
|
" [-T toskeyword]" |
2163 |
|
|
#endif /* SMALL */ |
2164 |
|
|
"\n\t[-t ttl] [-V rtable] [-w maxwait] host\n"); |
2165 |
|
|
} |
2166 |
|
|
exit(1); |
2167 |
|
|
} |