traceroute.c revision 1.23 1 /* $NetBSD: traceroute.c,v 1.23 1998/07/06 06:59:06 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that: (1) source code distributions
9 * retain the above copyright notice and this paragraph in its entirety, (2)
10 * distributions including binary code include the above copyright notice and
11 * this paragraph in its entirety in the documentation or other materials
12 * provided with the distribution, and (3) all advertising materials mentioning
13 * features or use of this software display the following acknowledgement:
14 * ``This product includes software developed by the University of California,
15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16 * the University nor the names of its contributors may be used to endorse
17 * or promote products derived from this software without specific prior
18 * written permission.
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 */
23
24 #include <sys/cdefs.h>
25 #ifndef lint
26 #if 0
27 static const char rcsid[] =
28 "@(#)Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp (LBL)";
29 #else
30 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997\n\
31 The Regents of the University of California. All rights reserved.\n");
32 __RCSID("$NetBSD: traceroute.c,v 1.23 1998/07/06 06:59:06 mrg Exp $");
33 #endif
34 #endif
35
36 /*
37 * traceroute host - trace the route ip packets follow going to "host".
38 *
39 * Attempt to trace the route an ip packet would follow to some
40 * internet host. We find out intermediate hops by launching probe
41 * packets with a small ttl (time to live) then listening for an
42 * icmp "time exceeded" reply from a gateway. We start our probes
43 * with a ttl of one and increase by one until we get an icmp "port
44 * unreachable" (which means we got to "host") or hit a max (which
45 * defaults to 30 hops & can be changed with the -m flag). Three
46 * probes (change with -q flag) are sent at each ttl setting and a
47 * line is printed showing the ttl, address of the gateway and
48 * round trip time of each probe. If the probe answers come from
49 * different gateways, the address of each responding system will
50 * be printed. If there is no response within a 5 sec. timeout
51 * interval (changed with the -w flag), a "*" is printed for that
52 * probe.
53 *
54 * Probe packets are UDP format. We don't want the destination
55 * host to process them so the destination port is set to an
56 * unlikely value (if some clod on the destination is using that
57 * value, it can be changed with the -p flag).
58 *
59 * A sample use might be:
60 *
61 * [yak 71]% traceroute nis.nsf.net.
62 * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
63 * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
64 * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
65 * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
66 * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
67 * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
68 * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
69 * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
70 * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
71 * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
72 * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
73 * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
74 *
75 * Note that lines 2 & 3 are the same. This is due to a buggy
76 * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
77 * packets with a zero ttl.
78 *
79 * A more interesting example is:
80 *
81 * [yak 72]% traceroute allspice.lcs.mit.edu.
82 * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
83 * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
84 * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
85 * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
86 * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
87 * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
88 * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
89 * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
90 * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
91 * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
92 * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
93 * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
94 * 12 * * *
95 * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
96 * 14 * * *
97 * 15 * * *
98 * 16 * * *
99 * 17 * * *
100 * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
101 *
102 * (I start to see why I'm having so much trouble with mail to
103 * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
104 * either don't send ICMP "time exceeded" messages or send them
105 * with a ttl too small to reach us. 14 - 17 are running the
106 * MIT C Gateway code that doesn't send "time exceeded"s. God
107 * only knows what's going on with 12.
108 *
109 * The silent gateway 12 in the above may be the result of a bug in
110 * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
111 * sends an unreachable message using whatever ttl remains in the
112 * original datagram. Since, for gateways, the remaining ttl is
113 * zero, the icmp "time exceeded" is guaranteed to not make it back
114 * to us. The behavior of this bug is slightly more interesting
115 * when it appears on the destination system:
116 *
117 * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
118 * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
119 * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
120 * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
121 * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
122 * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
123 * 7 * * *
124 * 8 * * *
125 * 9 * * *
126 * 10 * * *
127 * 11 * * *
128 * 12 * * *
129 * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
130 *
131 * Notice that there are 12 "gateways" (13 is the final
132 * destination) and exactly the last half of them are "missing".
133 * What's really happening is that rip (a Sun-3 running Sun OS3.5)
134 * is using the ttl from our arriving datagram as the ttl in its
135 * icmp reply. So, the reply will time out on the return path
136 * (with no notice sent to anyone since icmp's aren't sent for
137 * icmp's) until we probe with a ttl that's at least twice the path
138 * length. I.e., rip is really only 7 hops away. A reply that
139 * returns with a ttl of 1 is a clue this problem exists.
140 * Traceroute prints a "!" after the time if the ttl is <= 1.
141 * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
142 * non-standard (HPUX) software, expect to see this problem
143 * frequently and/or take care picking the target host of your
144 * probes.
145 *
146 * Other possible annotations after the time are !H, !N, !P (got a host,
147 * network or protocol unreachable, respectively), !S or !F (source
148 * route failed or fragmentation needed -- neither of these should
149 * ever occur and the associated gateway is busted if you see one). If
150 * almost all the probes result in some kind of unreachable, traceroute
151 * will give up and exit.
152 *
153 * Notes
154 * -----
155 * This program must be run by root or be setuid. (I suggest that
156 * you *don't* make it setuid -- casual use could result in a lot
157 * of unnecessary traffic on our poor, congested nets.)
158 *
159 * This program requires a kernel mod that does not appear in any
160 * system available from Berkeley: A raw ip socket using proto
161 * IPPROTO_RAW must interpret the data sent as an ip datagram (as
162 * opposed to data to be wrapped in a ip datagram). See the README
163 * file that came with the source to this program for a description
164 * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
165 * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
166 * MODIFIED TO RUN THIS PROGRAM.
167 *
168 * The udp port usage may appear bizarre (well, ok, it is bizarre).
169 * The problem is that an icmp message only contains 8 bytes of
170 * data from the original datagram. 8 bytes is the size of a udp
171 * header so, if we want to associate replies with the original
172 * datagram, the necessary information must be encoded into the
173 * udp header (the ip id could be used but there's no way to
174 * interlock with the kernel's assignment of ip id's and, anyway,
175 * it would have taken a lot more kernel hacking to allow this
176 * code to set the ip id). So, to allow two or more users to
177 * use traceroute simultaneously, we use this task's pid as the
178 * source port (the high bit is set to move the port number out
179 * of the "likely" range). To keep track of which probe is being
180 * replied to (so times and/or hop counts don't get confused by a
181 * reply that was delayed in transit), we increment the destination
182 * port number before each probe.
183 *
184 * Don't use this as a coding example. I was trying to find a
185 * routing problem and this code sort-of popped out after 48 hours
186 * without sleep. I was amazed it ever compiled, much less ran.
187 *
188 * I stole the idea for this program from Steve Deering. Since
189 * the first release, I've learned that had I attended the right
190 * IETF working group meetings, I also could have stolen it from Guy
191 * Almes or Matt Mathis. I don't know (or care) who came up with
192 * the idea first. I envy the originators' perspicacity and I'm
193 * glad they didn't keep the idea a secret.
194 *
195 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
196 * enhancements to the original distribution.
197 *
198 * I've hacked up a round-trip-route version of this that works by
199 * sending a loose-source-routed udp datagram through the destination
200 * back to yourself. Unfortunately, SO many gateways botch source
201 * routing, the thing is almost worthless. Maybe one day...
202 *
203 * -- Van Jacobson (van (at) ee.lbl.gov)
204 * Tue Dec 20 03:50:13 PST 1988
205 */
206
207 #include <sys/param.h>
208 #include <sys/file.h>
209 #include <sys/ioctl.h>
210 #ifdef HAVE_SYS_SELECT_H
211 #include <sys/select.h>
212 #endif
213 #include <sys/socket.h>
214 #include <sys/time.h>
215
216 #include <netinet/in_systm.h>
217 #include <netinet/in.h>
218 #include <netinet/ip.h>
219 #include <netinet/ip_var.h>
220 #include <netinet/ip_icmp.h>
221 #include <netinet/udp.h>
222 #include <netinet/udp_var.h>
223
224 #include <arpa/inet.h>
225
226 #include <ctype.h>
227 #include <errno.h>
228 #ifdef HAVE_MALLOC_H
229 #include <malloc.h>
230 #endif
231 #include <memory.h>
232 #include <netdb.h>
233 #include <stdio.h>
234 #include <stdlib.h>
235 #include <string.h>
236 #include <unistd.h>
237
238 #include "gnuc.h"
239 #ifdef HAVE_OS_PROTO_H
240 #include "os-proto.h"
241 #endif
242
243 #include "ifaddrlist.h"
244 #include "savestr.h"
245
246 /* Maximum number of gateways (include room for one noop) */
247 #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
248
249 #ifndef MAXHOSTNAMELEN
250 #define MAXHOSTNAMELEN 64
251 #endif
252
253 #define Fprintf (void)fprintf
254 #define Printf (void)printf
255
256 /* Host name and address list */
257 struct hostinfo {
258 char *name;
259 int n;
260 u_int32_t *addrs;
261 };
262
263 /* Data section of the probe packet */
264 struct outdata {
265 u_char seq; /* sequence number of this packet */
266 u_char ttl; /* ttl packet left with */
267 struct timeval tv; /* time packet left */
268 };
269
270 u_char packet[512]; /* last inbound (icmp) packet */
271
272 struct ip *outip; /* last output (udp) packet */
273 struct udphdr *outudp; /* last output (udp) packet */
274 void *outmark; /* packed location of struct outdata */
275 struct outdata outsetup; /* setup and copy for alignment */
276
277 struct icmp *outicmp; /* last output (icmp) packet */
278
279 /* loose source route gateway list (including room for final destination) */
280 u_int32_t gwlist[NGATEWAYS + 1];
281
282 int s; /* receive (icmp) socket file descriptor */
283 int sndsock; /* send (udp/icmp) socket file descriptor */
284
285 struct sockaddr whereto; /* Who to try to reach */
286 struct sockaddr_in wherefrom; /* Who we are */
287 int packlen; /* total length of packet */
288 int minpacket; /* min ip packet size */
289 int maxpacket = 32 * 1024; /* max ip packet size */
290
291 char *prog;
292 char *source;
293 char *hostname;
294 char *device;
295
296 int nprobes = 3;
297 int max_ttl = 30;
298 int first_ttl = 1;
299 u_short ident;
300 u_short port = 32768 + 666; /* start udp dest port # for probe packets */
301
302 int options; /* socket options */
303 int verbose;
304 int waittime = 5; /* time to wait for response (in seconds) */
305 int nflag; /* print addresses numerically */
306 int dump;
307 int useicmp; /* use icmp echo instead of udp packets */
308 #ifdef CANT_HACK_CKSUM
309 int docksum = 0; /* don't calculate checksums */
310 #else
311 int docksum = 1; /* calculate checksums */
312 #endif
313 int optlen; /* length of ip options */
314
315 extern int optind;
316 extern int opterr;
317 extern char *optarg;
318
319 /* Forwards */
320 double deltaT(struct timeval *, struct timeval *);
321 void freehostinfo(struct hostinfo *);
322 void getaddr(u_int32_t *, char *);
323 struct hostinfo *gethostinfo(char *);
324 u_short in_cksum(u_short *, int);
325 char *inetname(struct in_addr);
326 int main(int, char **);
327 int packet_ok(u_char *, int, struct sockaddr_in *, int);
328 char *pr_type(u_char);
329 void print(u_char *, int, struct sockaddr_in *);
330 void dump_packet(void);
331 void send_probe(int, int, struct timeval *);
332 void setsin(struct sockaddr_in *, u_int32_t);
333 int str2val(const char *, const char *, int, int);
334 void tvsub(struct timeval *, struct timeval *);
335 __dead void usage(void);
336 int wait_for_reply(int, struct sockaddr_in *, struct timeval *);
337
338 int
339 main(int argc, char **argv)
340 {
341 register int op, code, n;
342 register char *cp;
343 register u_char *outp;
344 register u_int32_t *ap;
345 register struct sockaddr_in *from = &wherefrom;
346 register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
347 register struct hostinfo *hi;
348 int on = 1;
349 register struct protoent *pe;
350 register int ttl, probe, i;
351 register int seq = 0;
352 int tos = 0, settos = 0, ttl_flag = 0;
353 register int lsrr = 0;
354 register u_short off = 0;
355 struct ifaddrlist *al;
356 char errbuf[132];
357
358 if ((cp = strrchr(argv[0], '/')) != NULL)
359 prog = cp + 1;
360 else
361 prog = argv[0];
362
363 opterr = 0;
364 while ((op = getopt(argc, argv, "dDFInlrvxf:g:i:m:p:q:s:t:w:")) != -1)
365 switch (op) {
366
367 case 'd':
368 options |= SO_DEBUG;
369 break;
370
371 case 'D':
372 dump = 1;
373 break;
374
375 case 'f':
376 first_ttl = str2val(optarg, "first ttl", 1, 255);
377 break;
378
379 case 'F':
380 off = IP_DF;
381 break;
382
383 case 'g':
384 if (lsrr >= NGATEWAYS) {
385 Fprintf(stderr,
386 "%s: No more than %d gateways\n",
387 prog, NGATEWAYS);
388 exit(1);
389 }
390 getaddr(gwlist + lsrr, optarg);
391 ++lsrr;
392 break;
393
394 case 'i':
395 device = optarg;
396 break;
397
398 case 'I':
399 ++useicmp;
400 break;
401
402 case 'l':
403 ++ttl_flag;
404 break;
405
406 case 'm':
407 max_ttl = str2val(optarg, "max ttl", 1, 255);
408 break;
409
410 case 'n':
411 ++nflag;
412 break;
413
414 case 'p':
415 port = str2val(optarg, "port", 1, -1);
416 break;
417
418 case 'q':
419 nprobes = str2val(optarg, "nprobes", 1, -1);
420 break;
421
422 case 'r':
423 options |= SO_DONTROUTE;
424 break;
425
426 case 's':
427 /*
428 * set the ip source address of the outbound
429 * probe (e.g., on a multi-homed host).
430 */
431 source = optarg;
432 break;
433
434 case 't':
435 tos = str2val(optarg, "tos", 0, 255);
436 ++settos;
437 break;
438
439 case 'v':
440 ++verbose;
441 break;
442
443 case 'x':
444 docksum = (docksum == 0);
445 break;
446
447 case 'w':
448 waittime = str2val(optarg, "wait time", 2, -1);
449 break;
450
451 default:
452 usage();
453 }
454
455 if (first_ttl > max_ttl) {
456 Fprintf(stderr,
457 "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
458 prog, first_ttl, max_ttl);
459 exit(1);
460 }
461
462 if (!docksum)
463 Fprintf(stderr, "%s: Warning: ckecksums disabled\n", prog);
464
465 if (lsrr > 0)
466 optlen = (lsrr + 1) * sizeof(gwlist[0]);
467 minpacket = sizeof(*outip) + sizeof(struct outdata) + optlen;
468 if (useicmp)
469 minpacket += 8; /* XXX magic number */
470 else
471 minpacket += sizeof(*outudp);
472 if (packlen == 0)
473 packlen = minpacket; /* minimum sized packet */
474 else if (minpacket > packlen || packlen > maxpacket) {
475 Fprintf(stderr, "%s: packet size must be %d <= s <= %d\n",
476 prog, minpacket, maxpacket);
477 exit(1);
478 }
479
480 /* Process destination and optional packet size */
481 switch (argc - optind) {
482
483 case 2:
484 packlen = str2val(argv[optind + 1],
485 "packet length", minpacket, -1);
486 /* Fall through */
487
488 case 1:
489 hostname = argv[optind];
490 hi = gethostinfo(hostname);
491 setsin(to, hi->addrs[0]);
492 if (hi->n > 1)
493 Fprintf(stderr,
494 "%s: Warning: %s has multiple addresses; using %s\n",
495 prog, hostname, inet_ntoa(to->sin_addr));
496 hostname = hi->name;
497 hi->name = NULL;
498 freehostinfo(hi);
499 break;
500
501 default:
502 usage();
503 }
504
505 #ifdef HAVE_SETLINEBUF
506 setlinebuf (stdout);
507 #else
508 setvbuf(stdout, NULL, _IOLBF, 0);
509 #endif
510
511 outip = (struct ip *)malloc((unsigned)packlen);
512 if (outip == NULL) {
513 Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
514 exit(1);
515 }
516 memset((char *)outip, 0, packlen);
517
518 outip->ip_v = IPVERSION;
519 if (settos)
520 outip->ip_tos = tos;
521 #ifdef BYTESWAP_IP_LEN
522 outip->ip_len = htons(packlen);
523 #else
524 outip->ip_len = packlen;
525 #endif
526 outip->ip_off = off;
527 outp = (u_char *)(outip + 1);
528 #ifdef HAVE_RAW_OPTIONS
529 if (lsrr > 0) {
530 register u_char *optlist;
531
532 optlist = outp;
533 outp += optlen;
534
535 /* final hop */
536 gwlist[lsrr] = to->sin_addr.s_addr;
537
538 outip->ip_dst.s_addr = gwlist[0];
539
540 /* force 4 byte alignment */
541 optlist[0] = IPOPT_NOP;
542 /* loose source route option */
543 optlist[1] = IPOPT_LSRR;
544 i = lsrr * sizeof(gwlist[0]);
545 optlist[2] = i + 3;
546 /* Pointer to LSRR addresses */
547 optlist[3] = IPOPT_MINOFF;
548 memcpy(optlist + 4, gwlist + 1, i);
549 } else
550 #endif
551 outip->ip_dst = to->sin_addr;
552
553 outip->ip_hl = (outp - (u_char *)outip) >> 2;
554 ident = (getpid() & 0xffff) | 0x8000;
555 if (useicmp) {
556 outip->ip_p = IPPROTO_ICMP;
557
558 outicmp = (struct icmp *)outp;
559 outicmp->icmp_type = ICMP_ECHO;
560 outicmp->icmp_id = htons(ident);
561
562 outmark = outp + 8; /* XXX magic number */
563 } else {
564 outip->ip_p = IPPROTO_UDP;
565
566 outudp = (struct udphdr *)outp;
567 outudp->uh_sport = htons(ident);
568 outudp->uh_ulen =
569 htons((u_short)(packlen - (sizeof(*outip) + optlen)));
570 outmark = outudp + 1;
571 }
572
573 cp = "icmp";
574 if ((pe = getprotobyname(cp)) == NULL) {
575 Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
576 exit(1);
577 }
578 if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
579 Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
580 exit(1);
581 }
582 if (options & SO_DEBUG)
583 (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
584 sizeof(on));
585 if (options & SO_DONTROUTE)
586 (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
587 sizeof(on));
588
589 #ifndef __hpux
590 sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
591 #else
592 sndsock = socket(AF_INET, SOCK_RAW,
593 useicmp ? IPPROTO_ICMP : IPPROTO_UDP);
594 #endif
595 if (sndsock < 0) {
596 Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
597 exit(1);
598 }
599
600 /* Revert to non-privileged user after opening sockets */
601 setuid(getuid());
602
603 #if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
604 if (lsrr > 0) {
605 u_char optlist[MAX_IPOPTLEN];
606
607 cp = "ip";
608 if ((pe = getprotobyname(cp)) == NULL) {
609 Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
610 exit(1);
611 }
612
613 /* final hop */
614 gwlist[lsrr] = to->sin_addr.s_addr;
615 ++lsrr;
616
617 /* force 4 byte alignment */
618 optlist[0] = IPOPT_NOP;
619 /* loose source route option */
620 optlist[1] = IPOPT_LSRR;
621 i = lsrr * sizeof(gwlist[0]);
622 optlist[2] = i + 3;
623 /* Pointer to LSRR addresses */
624 optlist[3] = IPOPT_MINOFF;
625 memcpy(optlist + 4, gwlist, i);
626
627 if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, optlist,
628 i + sizeof(gwlist[0]))) < 0) {
629 Fprintf(stderr, "%s: IP_OPTIONS: %s\n",
630 prog, strerror(errno));
631 exit(1);
632 }
633 }
634 #endif
635
636 #ifdef SO_SNDBUF
637 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
638 sizeof(packlen)) < 0) {
639 Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
640 exit(1);
641 }
642 #endif
643 #ifdef IP_HDRINCL
644 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
645 sizeof(on)) < 0) {
646 Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
647 exit(1);
648 }
649 #else
650 #ifdef IP_TOS
651 if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
652 (char *)&tos, sizeof(tos)) < 0) {
653 Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
654 prog, tos, strerror(errno));
655 exit(1);
656 }
657 #endif
658 #endif
659 if (options & SO_DEBUG)
660 (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
661 sizeof(on));
662 if (options & SO_DONTROUTE)
663 (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
664 sizeof(on));
665
666 /* Get the interface address list */
667 n = ifaddrlist(&al, errbuf, sizeof errbuf);
668 if (n < 0) {
669 Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
670 exit(1);
671 }
672 if (n == 0) {
673 Fprintf(stderr,
674 "%s: Can't find any network interfaces\n", prog);
675 exit(1);
676 }
677
678 /* Look for a specific device */
679 if (device != NULL) {
680 for (i = n; i > 0; --i, ++al)
681 if (strcmp(device, al->device) == 0)
682 break;
683 if (i <= 0) {
684 Fprintf(stderr, "%s: Can't find interface %s\n",
685 prog, device);
686 exit(1);
687 }
688 }
689
690 /* Determine our source address */
691 if (source == NULL) {
692 /*
693 * If a device was specified, use the interface address.
694 * Otherwise, use the first interface found.
695 * Warn if there are more than one.
696 */
697 setsin(from, al->addr);
698 if (n > 1 && device == NULL) {
699 Fprintf(stderr,
700 "%s: Warning: Multiple interfaces found; using %s @ %s\n",
701 prog, inet_ntoa(from->sin_addr), al->device);
702 }
703 } else {
704 hi = gethostinfo(source);
705 source = hi->name;
706 hi->name = NULL;
707 if (device == NULL) {
708 /*
709 * Use the first interface found.
710 * Warn if there are more than one.
711 */
712 setsin(from, hi->addrs[0]);
713 if (hi->n > 1)
714 Fprintf(stderr,
715 "%s: Warning: %s has multiple addresses; using %s\n",
716 prog, source, inet_ntoa(from->sin_addr));
717 } else {
718 /*
719 * Make sure the source specified matches the
720 * interface address.
721 */
722 for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
723 if (*ap == al->addr)
724 break;
725 if (i <= 0) {
726 Fprintf(stderr,
727 "%s: %s is not on interface %s\n",
728 prog, source, device);
729 exit(1);
730 }
731 setsin(from, *ap);
732 }
733 freehostinfo(hi);
734 }
735 outip->ip_src = from->sin_addr;
736 #ifndef IP_HDRINCL
737 if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
738 Fprintf(stderr, "%s: bind: %s\n",
739 prog, strerror(errno));
740 exit (1);
741 }
742 #endif
743
744 setuid(getuid());
745 Fprintf(stderr, "%s to %s (%s)",
746 prog, hostname, inet_ntoa(to->sin_addr));
747 if (source)
748 Fprintf(stderr, " from %s", source);
749 Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
750 (void)fflush(stderr);
751
752 for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
753 u_int32_t lastaddr = 0;
754 int got_there = 0;
755 int unreachable = 0;
756
757 Printf("%2d ", ttl);
758 for (probe = 0; probe < nprobes; ++probe) {
759 register int cc;
760 struct timeval t1, t2;
761 struct timezone tz;
762 register struct ip *ip;
763
764 (void)gettimeofday(&t1, &tz);
765 send_probe(++seq, ttl, &t1);
766 while ((cc = wait_for_reply(s, from, &t1)) != 0) {
767 (void)gettimeofday(&t2, &tz);
768 /*
769 * Since we'll be receiving all ICMP
770 * messages to this host above, we may
771 * never end up with cc=0, so we need
772 * an additional termination check.
773 */
774 if (t2.tv_sec - t1.tv_sec > waittime) {
775 cc = 0;
776 break;
777 }
778 i = packet_ok(packet, cc, from, seq);
779 /* Skip short packet */
780 if (i == 0)
781 continue;
782 if (from->sin_addr.s_addr != lastaddr) {
783 print(packet, cc, from);
784 lastaddr = from->sin_addr.s_addr;
785 }
786 ip = (struct ip *)packet;
787 Printf(" %.3f ms", deltaT(&t1, &t2));
788 if (ttl_flag)
789 Printf(" (ttl = %d)", ip->ip_ttl);
790 if (i == -2) {
791 #ifndef ARCHAIC
792 if (ip->ip_ttl <= 1)
793 Printf(" !");
794 #endif
795 ++got_there;
796 break;
797 }
798 /* time exceeded in transit */
799 if (i == -1)
800 break;
801 code = i - 1;
802 switch (code) {
803
804 case ICMP_UNREACH_PORT:
805 #ifndef ARCHAIC
806 if (ip->ip_ttl <= 1)
807 Printf(" !");
808 #endif
809 ++got_there;
810 break;
811
812 case ICMP_UNREACH_NET:
813 ++unreachable;
814 Printf(" !N");
815 break;
816
817 case ICMP_UNREACH_HOST:
818 ++unreachable;
819 Printf(" !H");
820 break;
821
822 case ICMP_UNREACH_PROTOCOL:
823 ++got_there;
824 Printf(" !P");
825 break;
826
827 case ICMP_UNREACH_NEEDFRAG:
828 ++unreachable;
829 Printf(" !F");
830 break;
831
832 case ICMP_UNREACH_SRCFAIL:
833 ++unreachable;
834 Printf(" !S");
835 break;
836
837 /* rfc1716 */
838 #ifndef ICMP_UNREACH_FILTER_PROHIB
839 #define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohibited filter */
840 #endif
841 case ICMP_UNREACH_FILTER_PROHIB:
842 ++unreachable;
843 Printf(" !X");
844 break;
845
846 default:
847 ++unreachable;
848 Printf(" !<%d>", code);
849 break;
850 }
851 break;
852 }
853 if (cc == 0)
854 Printf(" *");
855 (void)fflush(stdout);
856 }
857 putchar('\n');
858 if (got_there ||
859 (unreachable > 0 && unreachable >= nprobes))
860 break;
861 }
862 exit(0);
863 }
864
865 int
866 wait_for_reply(register int sock, register struct sockaddr_in *fromp,
867 register struct timeval *tp)
868 {
869 fd_set fds;
870 struct timeval now, wait;
871 struct timezone tz;
872 register int cc = 0;
873 int fromlen = sizeof(*fromp);
874
875 FD_ZERO(&fds);
876 FD_SET(sock, &fds);
877
878 wait.tv_sec = tp->tv_sec + waittime;
879 wait.tv_usec = tp->tv_usec;
880 (void)gettimeofday(&now, &tz);
881 tvsub(&wait, &now);
882
883 if (select(sock + 1, &fds, NULL, NULL, &wait) > 0)
884 cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
885 (struct sockaddr *)fromp, &fromlen);
886
887 return(cc);
888 }
889
890 void
891 dump_packet()
892 {
893 u_char *p;
894 int i;
895
896 Fprintf(stderr, "packet data:");
897
898 #ifdef __hpux
899 for (p = useicmp ? (u_char *)outicmp : (u_char *)outudp, i = 0; i <
900 i < packlen - (sizeof(*outip) + optlen); i++)
901 #else
902 for (p = (u_char *)outip, i = 0; i < packlen; i++)
903 #endif
904 {
905 if ((i % 24) == 0)
906 Fprintf(stderr, "\n ");
907 Fprintf(stderr, " %02x", *p++);
908 }
909 Fprintf(stderr, "\n");
910 }
911
912 void
913 send_probe(register int seq, int ttl, register struct timeval *tp)
914 {
915 register int cc;
916 register struct udpiphdr * ui;
917 struct ip tip;
918
919 outip->ip_ttl = ttl;
920 #ifndef __hpux
921 outip->ip_id = htons(ident + seq);
922 #endif
923
924 /*
925 * In most cases, the kernel will recalculate the ip checksum.
926 * But we must do it anyway so that the udp checksum comes out
927 * right.
928 */
929 if (docksum) {
930 outip->ip_sum =
931 in_cksum((u_short *)outip, sizeof(*outip) + optlen);
932 if (outip->ip_sum == 0)
933 outip->ip_sum = 0xffff;
934 }
935
936 /* Payload */
937 outsetup.seq = seq;
938 outsetup.ttl = ttl;
939 outsetup.tv = *tp;
940 memcpy(outmark,&outsetup,sizeof(outsetup));
941
942 if (useicmp)
943 outicmp->icmp_seq = htons(seq);
944 else
945 outudp->uh_dport = htons(port + seq);
946
947 /* (We can only do the checksum if we know our ip address) */
948 if (docksum) {
949 if (useicmp) {
950 outicmp->icmp_cksum = 0;
951 outicmp->icmp_cksum = in_cksum((u_short *)outicmp,
952 packlen - (sizeof(*outip) + optlen));
953 if (outicmp->icmp_cksum == 0)
954 outicmp->icmp_cksum = 0xffff;
955 } else {
956 /* Checksum (must save and restore ip header) */
957 tip = *outip;
958 ui = (struct udpiphdr *)outip;
959 #ifndef __NetBSD__
960 ui->ui_next = 0;
961 ui->ui_prev = 0;
962 ui->ui_x1 = 0;
963 #else
964 memset(ui->ui_x1, 0, sizeof(ui->ui_x1));
965 #endif
966 ui->ui_len = outudp->uh_ulen;
967 outudp->uh_sum = 0;
968 outudp->uh_sum = in_cksum((u_short *)ui, packlen);
969 if (outudp->uh_sum == 0)
970 outudp->uh_sum = 0xffff;
971 *outip = tip;
972 }
973 }
974
975 /* XXX undocumented debugging hack */
976 if (verbose > 1) {
977 register const u_short *sp;
978 register int nshorts, i;
979
980 sp = (u_short *)outip;
981 nshorts = (u_int)packlen / sizeof(u_short);
982 i = 0;
983 Printf("[ %d bytes", packlen);
984 while (--nshorts >= 0) {
985 if ((i++ % 8) == 0)
986 Printf("\n\t");
987 Printf(" %04x", ntohs(*sp++));
988 }
989 if (packlen & 1) {
990 if ((i % 8) == 0)
991 Printf("\n\t");
992 Printf(" %02x", *(u_char *)sp);
993 }
994 Printf("]\n");
995 }
996
997 #if !defined(IP_HDRINCL) && defined(IP_TTL)
998 if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
999 (char *)&ttl, sizeof(ttl)) < 0) {
1000 Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
1001 prog, ttl, strerror(errno));
1002 exit(1);
1003 }
1004 #endif
1005 if (dump)
1006 dump_packet();
1007
1008 #ifdef __hpux
1009 cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp,
1010 packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto));
1011 if (cc > 0)
1012 cc += sizeof(*outip) + optlen;
1013 #else
1014 cc = sendto(sndsock, (char *)outip,
1015 packlen, 0, &whereto, sizeof(whereto));
1016 #endif
1017 if (cc < 0 || cc != packlen) {
1018 if (cc < 0)
1019 Fprintf(stderr, "%s: sendto: %s\n",
1020 prog, strerror(errno));
1021 Printf("%s: wrote %s %d chars, ret=%d\n",
1022 prog, hostname, packlen, cc);
1023 (void)fflush(stdout);
1024 }
1025 }
1026
1027 double
1028 deltaT(struct timeval *t1p, struct timeval *t2p)
1029 {
1030 register double dt;
1031
1032 dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
1033 (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
1034 return (dt);
1035 }
1036
1037 /*
1038 * Convert an ICMP "type" field to a printable string.
1039 */
1040 char *
1041 pr_type(register u_char t)
1042 {
1043 static char *ttab[] = {
1044 "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
1045 "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
1046 "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
1047 "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
1048 "Info Reply"
1049 };
1050
1051 if (t > 16)
1052 return("OUT-OF-RANGE");
1053
1054 return(ttab[t]);
1055 }
1056
1057 int
1058 packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
1059 register int seq)
1060 {
1061 register struct icmp *icp;
1062 register u_char type, code;
1063 register int hlen;
1064 #ifndef ARCHAIC
1065 register struct ip *ip;
1066
1067 ip = (struct ip *) buf;
1068 hlen = ip->ip_hl << 2;
1069 if (cc < hlen + ICMP_MINLEN) {
1070 if (verbose)
1071 Printf("packet too short (%d bytes) from %s\n", cc,
1072 inet_ntoa(from->sin_addr));
1073 return (0);
1074 }
1075 cc -= hlen;
1076 icp = (struct icmp *)(buf + hlen);
1077 #else
1078 icp = (struct icmp *)buf;
1079 #endif
1080 type = icp->icmp_type;
1081 code = icp->icmp_code;
1082 if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
1083 type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
1084 register struct ip *hip;
1085 register struct udphdr *up;
1086 register struct icmp *hicmp;
1087
1088 hip = &icp->icmp_ip;
1089 hlen = hip->ip_hl << 2;
1090 if (useicmp) {
1091 /* XXX */
1092 if (type == ICMP_ECHOREPLY &&
1093 icp->icmp_id == htons(ident) &&
1094 icp->icmp_seq == htons(seq))
1095 return (-2);
1096
1097 hicmp = (struct icmp *)((u_char *)hip + hlen);
1098 /* XXX 8 is a magic number */
1099 if (hlen + 8 <= cc &&
1100 hip->ip_p == IPPROTO_ICMP &&
1101 hicmp->icmp_id == htons(ident) &&
1102 hicmp->icmp_seq == htons(seq))
1103 return (type == ICMP_TIMXCEED ? -1 : code + 1);
1104 } else {
1105 up = (struct udphdr *)((u_char *)hip + hlen);
1106 /* XXX 8 is a magic number */
1107 if (hlen + 12 <= cc &&
1108 hip->ip_p == IPPROTO_UDP &&
1109 up->uh_sport == htons(ident) &&
1110 up->uh_dport == htons(port + seq))
1111 return (type == ICMP_TIMXCEED ? -1 : code + 1);
1112 }
1113 }
1114 #ifndef ARCHAIC
1115 if (verbose) {
1116 register int i;
1117 u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
1118
1119 Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
1120 Printf("%s: icmp type %d (%s) code %d\n",
1121 inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
1122 for (i = 4; i < cc ; i += sizeof(*lp))
1123 Printf("%2d: x%8.8x\n", i, *lp++);
1124 }
1125 #endif
1126 return(0);
1127 }
1128
1129
1130 void
1131 print(register u_char *buf, register int cc, register struct sockaddr_in *from)
1132 {
1133 register struct ip *ip;
1134 register int hlen;
1135
1136 ip = (struct ip *) buf;
1137 hlen = ip->ip_hl << 2;
1138 cc -= hlen;
1139
1140 if (nflag)
1141 Printf(" %s", inet_ntoa(from->sin_addr));
1142 else
1143 Printf(" %s (%s)", inetname(from->sin_addr),
1144 inet_ntoa(from->sin_addr));
1145
1146 if (verbose)
1147 Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
1148 }
1149
1150 /*
1151 * Checksum routine for Internet Protocol family headers (C Version)
1152 */
1153 u_short
1154 in_cksum(register u_short *addr, register int len)
1155 {
1156 register int nleft = len;
1157 register u_short *w = addr;
1158 register u_short answer;
1159 register int sum = 0;
1160
1161 /*
1162 * Our algorithm is simple, using a 32 bit accumulator (sum),
1163 * we add sequential 16 bit words to it, and at the end, fold
1164 * back all the carry bits from the top 16 bits into the lower
1165 * 16 bits.
1166 */
1167 while (nleft > 1) {
1168 sum += *w++;
1169 nleft -= 2;
1170 }
1171
1172 /* mop up an odd byte, if necessary */
1173 if (nleft == 1)
1174 sum += *(u_char *)w;
1175
1176 /*
1177 * add back carry outs from top 16 bits to low 16 bits
1178 */
1179 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1180 sum += (sum >> 16); /* add carry */
1181 answer = ~sum; /* truncate to 16 bits */
1182 return (answer);
1183 }
1184
1185 /*
1186 * Subtract 2 timeval structs: out = out - in.
1187 * Out is assumed to be >= in.
1188 */
1189 void
1190 tvsub(register struct timeval *out, register struct timeval *in)
1191 {
1192
1193 if ((out->tv_usec -= in->tv_usec) < 0) {
1194 --out->tv_sec;
1195 out->tv_usec += 1000000;
1196 }
1197 out->tv_sec -= in->tv_sec;
1198 }
1199
1200 /*
1201 * Construct an Internet address representation.
1202 * If the nflag has been supplied, give
1203 * numeric value, otherwise try for symbolic name.
1204 */
1205 char *
1206 inetname(struct in_addr in)
1207 {
1208 register char *cp;
1209 register struct hostent *hp;
1210 static int first = 1;
1211 static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
1212
1213 if (first && !nflag) {
1214 int rv;
1215
1216 first = 0;
1217 rv = gethostname(domain, sizeof domain);
1218 domain[sizeof(domain) - 1] = '\0';
1219 if (rv == 0 && (cp = strchr(domain, '.')) != NULL) {
1220 (void)strncpy(domain, cp + 1, sizeof(domain) - 1);
1221 } else
1222 domain[0] = '\0';
1223 }
1224 if (!nflag && in.s_addr != INADDR_ANY) {
1225 hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
1226 if (hp != NULL) {
1227 if ((cp = strchr(hp->h_name, '.')) != NULL &&
1228 strcmp(cp + 1, domain) == 0)
1229 *cp = '\0';
1230 (void)strncpy(line, hp->h_name, sizeof(line) - 1);
1231 line[sizeof(line) - 1] = '\0';
1232 return (line);
1233 }
1234 }
1235 return (inet_ntoa(in));
1236 }
1237
1238 struct hostinfo *
1239 gethostinfo(register char *hostname)
1240 {
1241 register int n;
1242 register struct hostent *hp;
1243 register struct hostinfo *hi;
1244 register char **p;
1245 register u_int32_t *ap;
1246 struct in_addr addr;
1247
1248 hi = calloc(1, sizeof(*hi));
1249 if (hi == NULL) {
1250 Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1251 exit(1);
1252 }
1253 if (inet_aton(hostname, &addr) != 0) {
1254 hi->name = savestr(hostname);
1255 hi->n = 1;
1256 hi->addrs = calloc(1, sizeof(hi->addrs[0]));
1257 if (hi->addrs == NULL) {
1258 Fprintf(stderr, "%s: calloc %s\n",
1259 prog, strerror(errno));
1260 exit(1);
1261 }
1262 hi->addrs[0] = addr.s_addr;
1263 return (hi);
1264 }
1265
1266 hp = gethostbyname(hostname);
1267 if (hp == NULL) {
1268 Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
1269 exit(1);
1270 }
1271 if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
1272 Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
1273 exit(1);
1274 }
1275 hi->name = savestr(hp->h_name);
1276 for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
1277 continue;
1278 hi->n = n;
1279 hi->addrs = calloc(n, sizeof(hi->addrs[0]));
1280 if (hi->addrs == NULL) {
1281 Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1282 exit(1);
1283 }
1284 for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
1285 memcpy(ap, *p, sizeof(*ap));
1286 return (hi);
1287 }
1288
1289 void
1290 freehostinfo(register struct hostinfo *hi)
1291 {
1292 if (hi->name != NULL) {
1293 free(hi->name);
1294 hi->name = NULL;
1295 }
1296 free((char *)hi->addrs);
1297 free((char *)hi);
1298 }
1299
1300 void
1301 getaddr(register u_int32_t *ap, register char *hostname)
1302 {
1303 register struct hostinfo *hi;
1304
1305 hi = gethostinfo(hostname);
1306 *ap = hi->addrs[0];
1307 freehostinfo(hi);
1308 }
1309
1310 void
1311 setsin(register struct sockaddr_in *sin, register u_int32_t addr)
1312 {
1313
1314 memset(sin, 0, sizeof(*sin));
1315 #ifdef HAVE_SOCKADDR_SA_LEN
1316 sin->sin_len = sizeof(*sin);
1317 #endif
1318 sin->sin_family = AF_INET;
1319 sin->sin_addr.s_addr = addr;
1320 }
1321
1322 /* String to value with optional min and max. Handles decimal and hex. */
1323 int
1324 str2val(register const char *str, register const char *what,
1325 register int mi, register int ma)
1326 {
1327 register const char *cp;
1328 register int val;
1329 char *ep;
1330
1331 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
1332 cp = str + 2;
1333 val = (int)strtol(cp, &ep, 16);
1334 } else
1335 val = (int)strtol(str, &ep, 10);
1336 if (*ep != '\0') {
1337 Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
1338 prog, str, what);
1339 exit(1);
1340 }
1341 if (val < mi && mi >= 0) {
1342 if (mi == 0)
1343 Fprintf(stderr, "%s: %s must be >= %d\n",
1344 prog, what, mi);
1345 else
1346 Fprintf(stderr, "%s: %s must be > %d\n",
1347 prog, what, mi - 1);
1348 exit(1);
1349 }
1350 if (val > ma && ma >= 0) {
1351 Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
1352 exit(1);
1353 }
1354 return (val);
1355 }
1356
1357 __dead void
1358 usage(void)
1359 {
1360 extern char version[];
1361
1362 Fprintf(stderr, "Version %s\n", version);
1363 Fprintf(stderr, "Usage: %s [-dDFIlnrvx] [-g gateway] [-i iface] \
1364 [-f first_ttl] [-m max_ttl]\n\t[ -p port] [-q nqueries] [-s src_addr] [-t tos] \
1365 [-w waittime]\n\thost [packetlen]\n",
1366 prog);
1367 exit(1);
1368 }
1369