traceroute.c revision 1.20 1 /* $NetBSD: traceroute.c,v 1.20 1997/10/31 23:32:24 ross 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.20 1997/10/31 23:32:24 ross 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 struct outdata *outdata; /* last output (udp) packet */
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 void fool_memcpy(void *dst, const void *src, size_t len);
338
339 int
340 main(int argc, char **argv)
341 {
342 register int op, code, n;
343 register char *cp;
344 register u_char *outp;
345 register u_int32_t *ap;
346 register struct sockaddr_in *from = &wherefrom;
347 register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
348 register struct hostinfo *hi;
349 int on = 1;
350 register struct protoent *pe;
351 register int ttl, probe, i;
352 register int seq = 0;
353 int tos = 0, settos = 0, ttl_flag = 0;
354 register int lsrr = 0;
355 register u_short off = 0;
356 struct ifaddrlist *al;
357 char errbuf[132];
358
359 if ((cp = strrchr(argv[0], '/')) != NULL)
360 prog = cp + 1;
361 else
362 prog = argv[0];
363
364 opterr = 0;
365 while ((op = getopt(argc, argv, "dDFInlrvxf:g:i:m:p:q:s:t:w:")) != -1)
366 switch (op) {
367
368 case 'd':
369 options |= SO_DEBUG;
370 break;
371
372 case 'D':
373 dump = 1;
374 break;
375
376 case 'f':
377 first_ttl = str2val(optarg, "first ttl", 1, 255);
378 break;
379
380 case 'F':
381 off = IP_DF;
382 break;
383
384 case 'g':
385 if (lsrr >= NGATEWAYS) {
386 Fprintf(stderr,
387 "%s: No more than %d gateways\n",
388 prog, NGATEWAYS);
389 exit(1);
390 }
391 getaddr(gwlist + lsrr, optarg);
392 ++lsrr;
393 break;
394
395 case 'i':
396 device = optarg;
397 break;
398
399 case 'I':
400 ++useicmp;
401 break;
402
403 case 'l':
404 ++ttl_flag;
405 break;
406
407 case 'm':
408 max_ttl = str2val(optarg, "max ttl", 1, 255);
409 break;
410
411 case 'n':
412 ++nflag;
413 break;
414
415 case 'p':
416 port = str2val(optarg, "port", 1, -1);
417 break;
418
419 case 'q':
420 nprobes = str2val(optarg, "nprobes", 1, -1);
421 break;
422
423 case 'r':
424 options |= SO_DONTROUTE;
425 break;
426
427 case 's':
428 /*
429 * set the ip source address of the outbound
430 * probe (e.g., on a multi-homed host).
431 */
432 source = optarg;
433 break;
434
435 case 't':
436 tos = str2val(optarg, "tos", 0, 255);
437 ++settos;
438 break;
439
440 case 'v':
441 ++verbose;
442 break;
443
444 case 'x':
445 docksum = (docksum == 0);
446 break;
447
448 case 'w':
449 waittime = str2val(optarg, "wait time", 2, -1);
450 break;
451
452 default:
453 usage();
454 }
455
456 if (first_ttl > max_ttl) {
457 Fprintf(stderr,
458 "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
459 prog, first_ttl, max_ttl);
460 exit(1);
461 }
462
463 if (!docksum)
464 Fprintf(stderr, "%s: Warning: ckecksums disabled\n", prog);
465
466 if (lsrr > 0)
467 optlen = (lsrr + 1) * sizeof(gwlist[0]);
468 minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
469 if (useicmp)
470 minpacket += 8; /* XXX magic number */
471 else
472 minpacket += sizeof(*outudp);
473 if (packlen == 0)
474 packlen = minpacket; /* minimum sized packet */
475 else if (minpacket > packlen || packlen > maxpacket) {
476 Fprintf(stderr, "%s: packet size must be %d <= s <= %d\n",
477 prog, minpacket, maxpacket);
478 exit(1);
479 }
480
481 /* Process destination and optional packet size */
482 switch (argc - optind) {
483
484 case 2:
485 packlen = str2val(argv[optind + 1],
486 "packet length", minpacket, -1);
487 /* Fall through */
488
489 case 1:
490 hostname = argv[optind];
491 hi = gethostinfo(hostname);
492 setsin(to, hi->addrs[0]);
493 if (hi->n > 1)
494 Fprintf(stderr,
495 "%s: Warning: %s has multiple addresses; using %s\n",
496 prog, hostname, inet_ntoa(to->sin_addr));
497 hostname = hi->name;
498 hi->name = NULL;
499 freehostinfo(hi);
500 break;
501
502 default:
503 usage();
504 }
505
506 #ifdef HAVE_SETLINEBUF
507 setlinebuf (stdout);
508 #else
509 setvbuf(stdout, NULL, _IOLBF, 0);
510 #endif
511
512 outip = (struct ip *)malloc((unsigned)packlen);
513 if (outip == NULL) {
514 Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
515 exit(1);
516 }
517 memset((char *)outip, 0, packlen);
518
519 outip->ip_v = IPVERSION;
520 if (settos)
521 outip->ip_tos = tos;
522 #ifdef BYTESWAP_IP_LEN
523 outip->ip_len = htons(packlen);
524 #else
525 outip->ip_len = packlen;
526 #endif
527 outip->ip_off = off;
528 outp = (u_char *)(outip + 1);
529 #ifdef HAVE_RAW_OPTIONS
530 if (lsrr > 0) {
531 register u_char *optlist;
532
533 optlist = outp;
534 outp += optlen;
535
536 /* final hop */
537 gwlist[lsrr] = to->sin_addr.s_addr;
538
539 outip->ip_dst.s_addr = gwlist[0];
540
541 /* force 4 byte alignment */
542 optlist[0] = IPOPT_NOP;
543 /* loose source route option */
544 optlist[1] = IPOPT_LSRR;
545 i = lsrr * sizeof(gwlist[0]);
546 optlist[2] = i + 3;
547 /* Pointer to LSRR addresses */
548 optlist[3] = IPOPT_MINOFF;
549 memcpy(optlist + 4, gwlist + 1, i);
550 } else
551 #endif
552 outip->ip_dst = to->sin_addr;
553
554 outip->ip_hl = (outp - (u_char *)outip) >> 2;
555 ident = (getpid() & 0xffff) | 0x8000;
556 if (useicmp) {
557 outip->ip_p = IPPROTO_ICMP;
558
559 outicmp = (struct icmp *)outp;
560 outicmp->icmp_type = ICMP_ECHO;
561 outicmp->icmp_id = htons(ident);
562
563 outdata = (struct outdata *)(outp + 8); /* XXX magic number */
564 } else {
565 outip->ip_p = IPPROTO_UDP;
566
567 outudp = (struct udphdr *)outp;
568 outudp->uh_sport = htons(ident);
569 outudp->uh_ulen =
570 htons((u_short)(packlen - (sizeof(*outip) + optlen)));
571 outdata = (struct outdata *)(outudp + 1);
572 }
573
574 cp = "icmp";
575 if ((pe = getprotobyname(cp)) == NULL) {
576 Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
577 exit(1);
578 }
579 if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
580 Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
581 exit(1);
582 }
583 if (options & SO_DEBUG)
584 (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
585 sizeof(on));
586 if (options & SO_DONTROUTE)
587 (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
588 sizeof(on));
589
590 #ifndef __hpux
591 sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
592 #else
593 sndsock = socket(AF_INET, SOCK_RAW,
594 useicmp ? IPPROTO_ICMP : IPPROTO_UDP);
595 #endif
596 if (sndsock < 0) {
597 Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
598 exit(1);
599 }
600
601 /* Revert to non-privileged user after opening sockets */
602 setuid(getuid());
603
604 #if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
605 if (lsrr > 0) {
606 u_char optlist[MAX_IPOPTLEN];
607
608 cp = "ip";
609 if ((pe = getprotobyname(cp)) == NULL) {
610 Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
611 exit(1);
612 }
613
614 /* final hop */
615 gwlist[lsrr] = to->sin_addr.s_addr;
616 ++lsrr;
617
618 /* force 4 byte alignment */
619 optlist[0] = IPOPT_NOP;
620 /* loose source route option */
621 optlist[1] = IPOPT_LSRR;
622 i = lsrr * sizeof(gwlist[0]);
623 optlist[2] = i + 3;
624 /* Pointer to LSRR addresses */
625 optlist[3] = IPOPT_MINOFF;
626 memcpy(optlist + 4, gwlist, i);
627
628 if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, optlist,
629 i + sizeof(gwlist[0]))) < 0) {
630 Fprintf(stderr, "%s: IP_OPTIONS: %s\n",
631 prog, strerror(errno));
632 exit(1);
633 }
634 }
635 #endif
636
637 #ifdef SO_SNDBUF
638 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
639 sizeof(packlen)) < 0) {
640 Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
641 exit(1);
642 }
643 #endif
644 #ifdef IP_HDRINCL
645 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
646 sizeof(on)) < 0) {
647 Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
648 exit(1);
649 }
650 #else
651 #ifdef IP_TOS
652 if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
653 (char *)&tos, sizeof(tos)) < 0) {
654 Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
655 prog, tos, strerror(errno));
656 exit(1);
657 }
658 #endif
659 #endif
660 if (options & SO_DEBUG)
661 (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
662 sizeof(on));
663 if (options & SO_DONTROUTE)
664 (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
665 sizeof(on));
666
667 /* Get the interface address list */
668 n = ifaddrlist(&al, errbuf);
669 if (n < 0) {
670 Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
671 exit(1);
672 }
673 if (n == 0) {
674 Fprintf(stderr,
675 "%s: Can't find any network interfaces\n", prog);
676 exit(1);
677 }
678
679 /* Look for a specific device */
680 if (device != NULL) {
681 for (i = n; i > 0; --i, ++al)
682 if (strcmp(device, al->device) == 0)
683 break;
684 if (i <= 0) {
685 Fprintf(stderr, "%s: Can't find interface %s\n",
686 prog, device);
687 exit(1);
688 }
689 }
690
691 /* Determine our source address */
692 if (source == NULL) {
693 /*
694 * If a device was specified, use the interface address.
695 * Otherwise, use the first interface found.
696 * Warn if there are more than one.
697 */
698 setsin(from, al->addr);
699 if (n > 1 && device == NULL) {
700 Fprintf(stderr,
701 "%s: Warning: Multiple interfaces found; using %s @ %s\n",
702 prog, inet_ntoa(from->sin_addr), al->device);
703 }
704 } else {
705 hi = gethostinfo(source);
706 source = hi->name;
707 hi->name = NULL;
708 if (device == NULL) {
709 /*
710 * Use the first interface found.
711 * Warn if there are more than one.
712 */
713 setsin(from, hi->addrs[0]);
714 if (hi->n > 1)
715 Fprintf(stderr,
716 "%s: Warning: %s has multiple addresses; using %s\n",
717 prog, source, inet_ntoa(from->sin_addr));
718 } else {
719 /*
720 * Make sure the source specified matches the
721 * interface address.
722 */
723 for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
724 if (*ap == al->addr)
725 break;
726 if (i <= 0) {
727 Fprintf(stderr,
728 "%s: %s is not on interface %s\n",
729 prog, source, device);
730 exit(1);
731 }
732 setsin(from, *ap);
733 }
734 freehostinfo(hi);
735 }
736 outip->ip_src = from->sin_addr;
737 #ifndef IP_HDRINCL
738 if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
739 Fprintf(stderr, "%s: bind: %s\n",
740 prog, strerror(errno));
741 exit (1);
742 }
743 #endif
744
745 setuid(getuid());
746 Fprintf(stderr, "%s to %s (%s)",
747 prog, hostname, inet_ntoa(to->sin_addr));
748 if (source)
749 Fprintf(stderr, " from %s", source);
750 Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
751 (void)fflush(stderr);
752
753 for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
754 u_int32_t lastaddr = 0;
755 int got_there = 0;
756 int unreachable = 0;
757
758 Printf("%2d ", ttl);
759 for (probe = 0; probe < nprobes; ++probe) {
760 register int cc;
761 struct timeval t1, t2;
762 struct timezone tz;
763 register struct ip *ip;
764
765 (void)gettimeofday(&t1, &tz);
766 send_probe(++seq, ttl, &t1);
767 while ((cc = wait_for_reply(s, from, &t1)) != 0) {
768 (void)gettimeofday(&t2, &tz);
769 /*
770 * Since we'll be receiving all ICMP
771 * messages to this host above, we may
772 * never end up with cc=0, so we need
773 * an additional termination check.
774 */
775 if (t2.tv_sec - t1.tv_sec > waittime) {
776 cc = 0;
777 break;
778 }
779 i = packet_ok(packet, cc, from, seq);
780 /* Skip short packet */
781 if (i == 0)
782 continue;
783 if (from->sin_addr.s_addr != lastaddr) {
784 print(packet, cc, from);
785 lastaddr = from->sin_addr.s_addr;
786 }
787 ip = (struct ip *)packet;
788 Printf(" %.3f ms", deltaT(&t1, &t2));
789 if (ttl_flag)
790 Printf(" (ttl = %d)", ip->ip_ttl);
791 if (i == -2) {
792 #ifndef ARCHAIC
793 if (ip->ip_ttl <= 1)
794 Printf(" !");
795 #endif
796 ++got_there;
797 break;
798 }
799 /* time exceeded in transit */
800 if (i == -1)
801 break;
802 code = i - 1;
803 switch (code) {
804
805 case ICMP_UNREACH_PORT:
806 #ifndef ARCHAIC
807 if (ip->ip_ttl <= 1)
808 Printf(" !");
809 #endif
810 ++got_there;
811 break;
812
813 case ICMP_UNREACH_NET:
814 ++unreachable;
815 Printf(" !N");
816 break;
817
818 case ICMP_UNREACH_HOST:
819 ++unreachable;
820 Printf(" !H");
821 break;
822
823 case ICMP_UNREACH_PROTOCOL:
824 ++got_there;
825 Printf(" !P");
826 break;
827
828 case ICMP_UNREACH_NEEDFRAG:
829 ++unreachable;
830 Printf(" !F");
831 break;
832
833 case ICMP_UNREACH_SRCFAIL:
834 ++unreachable;
835 Printf(" !S");
836 break;
837
838 /* rfc1716 */
839 #ifndef ICMP_UNREACH_FILTER_PROHIB
840 #define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohibited filter */
841 #endif
842 case ICMP_UNREACH_FILTER_PROHIB:
843 ++unreachable;
844 Printf(" !X");
845 break;
846
847 default:
848 ++unreachable;
849 Printf(" !<%d>", code);
850 break;
851 }
852 break;
853 }
854 if (cc == 0)
855 Printf(" *");
856 (void)fflush(stdout);
857 }
858 putchar('\n');
859 if (got_there ||
860 (unreachable > 0 && unreachable >= nprobes))
861 break;
862 }
863 exit(0);
864 /*NOTREACHED*/
865 }
866 #if 1
867 /*
868 * Without this, gcc on alpha inlines memcpy and gets alignment faults
869 */
870 void
871 fool_memcpy(void *dst, const void *src, size_t len)
872 {
873 (void)memcpy(dst,src,len);
874 }
875 #endif
876
877 int
878 wait_for_reply(register int sock, register struct sockaddr_in *fromp,
879 register struct timeval *tp)
880 {
881 fd_set fds;
882 struct timeval now, wait;
883 struct timezone tz;
884 register int cc = 0;
885 int fromlen = sizeof(*fromp);
886
887 FD_ZERO(&fds);
888 FD_SET(sock, &fds);
889
890 wait.tv_sec = tp->tv_sec + waittime;
891 wait.tv_usec = tp->tv_usec;
892 (void)gettimeofday(&now, &tz);
893 tvsub(&wait, &now);
894
895 if (select(sock + 1, &fds, NULL, NULL, &wait) > 0)
896 cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
897 (struct sockaddr *)fromp, &fromlen);
898
899 return(cc);
900 }
901
902 void
903 dump_packet()
904 {
905 u_char *p;
906 int i;
907
908 Fprintf(stderr, "packet data:");
909
910 #ifdef __hpux
911 for (p = useicmp ? (u_char *)outicmp : (u_char *)outudp, i = 0; i <
912 i < packlen - (sizeof(*outip) + optlen); i++)
913 #else
914 for (p = (u_char *)outip, i = 0; i < packlen; i++)
915 #endif
916 {
917 if ((i % 24) == 0)
918 Fprintf(stderr, "\n ");
919 Fprintf(stderr, " %02x", *p++);
920 }
921 Fprintf(stderr, "\n");
922 }
923
924 void
925 send_probe(register int seq, int ttl, register struct timeval *tp)
926 {
927 register int cc;
928 register struct udpiphdr * ui;
929 struct ip tip;
930
931 outip->ip_ttl = ttl;
932 #ifndef __hpux
933 outip->ip_id = htons(ident + seq);
934 #endif
935
936 /*
937 * In most cases, the kernel will recalculate the ip checksum.
938 * But we must do it anyway so that the udp checksum comes out
939 * right.
940 */
941 if (docksum) {
942 outip->ip_sum =
943 in_cksum((u_short *)outip, sizeof(*outip) + optlen);
944 if (outip->ip_sum == 0)
945 outip->ip_sum = 0xffff;
946 }
947
948 /* Payload */
949 outsetup.seq = seq;
950 outsetup.ttl = ttl;
951 outsetup.tv = *tp;
952 fool_memcpy((char *)outdata,(char *)&outsetup,sizeof(outsetup));
953
954 if (useicmp)
955 outicmp->icmp_seq = htons(seq);
956 else
957 outudp->uh_dport = htons(port + seq);
958
959 /* (We can only do the checksum if we know our ip address) */
960 if (docksum) {
961 if (useicmp) {
962 outicmp->icmp_cksum = 0;
963 outicmp->icmp_cksum = in_cksum((u_short *)outicmp,
964 packlen - (sizeof(*outip) + optlen));
965 if (outicmp->icmp_cksum == 0)
966 outicmp->icmp_cksum = 0xffff;
967 } else {
968 /* Checksum (must save and restore ip header) */
969 tip = *outip;
970 ui = (struct udpiphdr *)outip;
971 #ifndef __NetBSD__
972 ui->ui_next = 0;
973 ui->ui_prev = 0;
974 ui->ui_x1 = 0;
975 #else
976 memset(ui->ui_x1, 0, sizeof(ui->ui_x1));
977 #endif
978 ui->ui_len = outudp->uh_ulen;
979 outudp->uh_sum = 0;
980 outudp->uh_sum = in_cksum((u_short *)ui, packlen);
981 if (outudp->uh_sum == 0)
982 outudp->uh_sum = 0xffff;
983 *outip = tip;
984 }
985 }
986
987 /* XXX undocumented debugging hack */
988 if (verbose > 1) {
989 register const u_short *sp;
990 register int nshorts, i;
991
992 sp = (u_short *)outip;
993 nshorts = (u_int)packlen / sizeof(u_short);
994 i = 0;
995 Printf("[ %d bytes", packlen);
996 while (--nshorts >= 0) {
997 if ((i++ % 8) == 0)
998 Printf("\n\t");
999 Printf(" %04x", ntohs(*sp++));
1000 }
1001 if (packlen & 1) {
1002 if ((i % 8) == 0)
1003 Printf("\n\t");
1004 Printf(" %02x", *(u_char *)sp);
1005 }
1006 Printf("]\n");
1007 }
1008
1009 #if !defined(IP_HDRINCL) && defined(IP_TTL)
1010 if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
1011 (char *)&ttl, sizeof(ttl)) < 0) {
1012 Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
1013 prog, ttl, strerror(errno));
1014 exit(1);
1015 }
1016 #endif
1017 if (dump)
1018 dump_packet();
1019
1020 #ifdef __hpux
1021 cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp,
1022 packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto));
1023 if (cc > 0)
1024 cc += sizeof(*outip) + optlen;
1025 #else
1026 cc = sendto(sndsock, (char *)outip,
1027 packlen, 0, &whereto, sizeof(whereto));
1028 #endif
1029 if (cc < 0 || cc != packlen) {
1030 if (cc < 0)
1031 Fprintf(stderr, "%s: sendto: %s\n",
1032 prog, strerror(errno));
1033 Printf("%s: wrote %s %d chars, ret=%d\n",
1034 prog, hostname, packlen, cc);
1035 (void)fflush(stdout);
1036 }
1037 }
1038
1039 double
1040 deltaT(struct timeval *t1p, struct timeval *t2p)
1041 {
1042 register double dt;
1043
1044 dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
1045 (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
1046 return (dt);
1047 }
1048
1049 /*
1050 * Convert an ICMP "type" field to a printable string.
1051 */
1052 char *
1053 pr_type(register u_char t)
1054 {
1055 static char *ttab[] = {
1056 "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
1057 "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
1058 "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
1059 "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
1060 "Info Reply"
1061 };
1062
1063 if (t > 16)
1064 return("OUT-OF-RANGE");
1065
1066 return(ttab[t]);
1067 }
1068
1069 int
1070 packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
1071 register int seq)
1072 {
1073 register struct icmp *icp;
1074 register u_char type, code;
1075 register int hlen;
1076 #ifndef ARCHAIC
1077 register struct ip *ip;
1078
1079 ip = (struct ip *) buf;
1080 hlen = ip->ip_hl << 2;
1081 if (cc < hlen + ICMP_MINLEN) {
1082 if (verbose)
1083 Printf("packet too short (%d bytes) from %s\n", cc,
1084 inet_ntoa(from->sin_addr));
1085 return (0);
1086 }
1087 cc -= hlen;
1088 icp = (struct icmp *)(buf + hlen);
1089 #else
1090 icp = (struct icmp *)buf;
1091 #endif
1092 type = icp->icmp_type;
1093 code = icp->icmp_code;
1094 if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
1095 type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
1096 register struct ip *hip;
1097 register struct udphdr *up;
1098 register struct icmp *hicmp;
1099
1100 hip = &icp->icmp_ip;
1101 hlen = hip->ip_hl << 2;
1102 if (useicmp) {
1103 /* XXX */
1104 if (type == ICMP_ECHOREPLY &&
1105 icp->icmp_id == htons(ident) &&
1106 icp->icmp_seq == htons(seq))
1107 return (-2);
1108
1109 hicmp = (struct icmp *)((u_char *)hip + hlen);
1110 /* XXX 8 is a magic number */
1111 if (hlen + 8 <= cc &&
1112 hip->ip_p == IPPROTO_ICMP &&
1113 hicmp->icmp_id == htons(ident) &&
1114 hicmp->icmp_seq == htons(seq))
1115 return (type == ICMP_TIMXCEED ? -1 : code + 1);
1116 } else {
1117 up = (struct udphdr *)((u_char *)hip + hlen);
1118 /* XXX 8 is a magic number */
1119 if (hlen + 12 <= cc &&
1120 hip->ip_p == IPPROTO_UDP &&
1121 up->uh_sport == htons(ident) &&
1122 up->uh_dport == htons(port + seq))
1123 return (type == ICMP_TIMXCEED ? -1 : code + 1);
1124 }
1125 }
1126 #ifndef ARCHAIC
1127 if (verbose) {
1128 register int i;
1129 u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
1130
1131 Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
1132 Printf("%s: icmp type %d (%s) code %d\n",
1133 inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
1134 for (i = 4; i < cc ; i += sizeof(*lp))
1135 Printf("%2d: x%8.8x\n", i, *lp++);
1136 }
1137 #endif
1138 return(0);
1139 }
1140
1141
1142 void
1143 print(register u_char *buf, register int cc, register struct sockaddr_in *from)
1144 {
1145 register struct ip *ip;
1146 register int hlen;
1147
1148 ip = (struct ip *) buf;
1149 hlen = ip->ip_hl << 2;
1150 cc -= hlen;
1151
1152 if (nflag)
1153 Printf(" %s", inet_ntoa(from->sin_addr));
1154 else
1155 Printf(" %s (%s)", inetname(from->sin_addr),
1156 inet_ntoa(from->sin_addr));
1157
1158 if (verbose)
1159 Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
1160 }
1161
1162 /*
1163 * Checksum routine for Internet Protocol family headers (C Version)
1164 */
1165 u_short
1166 in_cksum(register u_short *addr, register int len)
1167 {
1168 register int nleft = len;
1169 register u_short *w = addr;
1170 register u_short answer;
1171 register int sum = 0;
1172
1173 /*
1174 * Our algorithm is simple, using a 32 bit accumulator (sum),
1175 * we add sequential 16 bit words to it, and at the end, fold
1176 * back all the carry bits from the top 16 bits into the lower
1177 * 16 bits.
1178 */
1179 while (nleft > 1) {
1180 sum += *w++;
1181 nleft -= 2;
1182 }
1183
1184 /* mop up an odd byte, if necessary */
1185 if (nleft == 1)
1186 sum += *(u_char *)w;
1187
1188 /*
1189 * add back carry outs from top 16 bits to low 16 bits
1190 */
1191 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1192 sum += (sum >> 16); /* add carry */
1193 answer = ~sum; /* truncate to 16 bits */
1194 return (answer);
1195 }
1196
1197 /*
1198 * Subtract 2 timeval structs: out = out - in.
1199 * Out is assumed to be >= in.
1200 */
1201 void
1202 tvsub(register struct timeval *out, register struct timeval *in)
1203 {
1204
1205 if ((out->tv_usec -= in->tv_usec) < 0) {
1206 --out->tv_sec;
1207 out->tv_usec += 1000000;
1208 }
1209 out->tv_sec -= in->tv_sec;
1210 }
1211
1212 /*
1213 * Construct an Internet address representation.
1214 * If the nflag has been supplied, give
1215 * numeric value, otherwise try for symbolic name.
1216 */
1217 char *
1218 inetname(struct in_addr in)
1219 {
1220 register char *cp;
1221 register struct hostent *hp;
1222 static int first = 1;
1223 static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
1224
1225 if (first && !nflag) {
1226 first = 0;
1227 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
1228 (cp = strchr(domain, '.')) != NULL) {
1229 (void)strncpy(domain, cp + 1, sizeof(domain) - 1);
1230 domain[sizeof(domain) - 1] = '\0';
1231 } else
1232 domain[0] = '\0';
1233 }
1234 if (!nflag && in.s_addr != INADDR_ANY) {
1235 hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
1236 if (hp != NULL) {
1237 if ((cp = strchr(hp->h_name, '.')) != NULL &&
1238 strcmp(cp + 1, domain) == 0)
1239 *cp = '\0';
1240 (void)strncpy(line, hp->h_name, sizeof(line) - 1);
1241 line[sizeof(line) - 1] = '\0';
1242 return (line);
1243 }
1244 }
1245 return (inet_ntoa(in));
1246 }
1247
1248 struct hostinfo *
1249 gethostinfo(register char *hostname)
1250 {
1251 register int n;
1252 register struct hostent *hp;
1253 register struct hostinfo *hi;
1254 register char **p;
1255 register u_int32_t *ap;
1256 struct in_addr addr;
1257
1258 hi = calloc(1, sizeof(*hi));
1259 if (hi == NULL) {
1260 Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1261 exit(1);
1262 }
1263 if (inet_aton(hostname, &addr) != 0) {
1264 hi->name = savestr(hostname);
1265 hi->n = 1;
1266 hi->addrs = calloc(1, sizeof(hi->addrs[0]));
1267 if (hi->addrs == NULL) {
1268 Fprintf(stderr, "%s: calloc %s\n",
1269 prog, strerror(errno));
1270 exit(1);
1271 }
1272 hi->addrs[0] = addr.s_addr;
1273 return (hi);
1274 }
1275
1276 hp = gethostbyname(hostname);
1277 if (hp == NULL) {
1278 Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
1279 exit(1);
1280 }
1281 if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
1282 Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
1283 exit(1);
1284 }
1285 hi->name = savestr(hp->h_name);
1286 for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
1287 continue;
1288 hi->n = n;
1289 hi->addrs = calloc(n, sizeof(hi->addrs[0]));
1290 if (hi->addrs == NULL) {
1291 Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1292 exit(1);
1293 }
1294 for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
1295 memcpy(ap, *p, sizeof(*ap));
1296 return (hi);
1297 }
1298
1299 void
1300 freehostinfo(register struct hostinfo *hi)
1301 {
1302 if (hi->name != NULL) {
1303 free(hi->name);
1304 hi->name = NULL;
1305 }
1306 free((char *)hi->addrs);
1307 free((char *)hi);
1308 }
1309
1310 void
1311 getaddr(register u_int32_t *ap, register char *hostname)
1312 {
1313 register struct hostinfo *hi;
1314
1315 hi = gethostinfo(hostname);
1316 *ap = hi->addrs[0];
1317 freehostinfo(hi);
1318 }
1319
1320 void
1321 setsin(register struct sockaddr_in *sin, register u_int32_t addr)
1322 {
1323
1324 memset(sin, 0, sizeof(*sin));
1325 #ifdef HAVE_SOCKADDR_SA_LEN
1326 sin->sin_len = sizeof(*sin);
1327 #endif
1328 sin->sin_family = AF_INET;
1329 sin->sin_addr.s_addr = addr;
1330 }
1331
1332 /* String to value with optional min and max. Handles decimal and hex. */
1333 int
1334 str2val(register const char *str, register const char *what,
1335 register int mi, register int ma)
1336 {
1337 register const char *cp;
1338 register int val;
1339 char *ep;
1340
1341 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
1342 cp = str + 2;
1343 val = (int)strtol(cp, &ep, 16);
1344 } else
1345 val = (int)strtol(str, &ep, 10);
1346 if (*ep != '\0') {
1347 Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
1348 prog, str, what);
1349 exit(1);
1350 }
1351 if (val < mi && mi >= 0) {
1352 if (mi == 0)
1353 Fprintf(stderr, "%s: %s must be >= %d\n",
1354 prog, what, mi);
1355 else
1356 Fprintf(stderr, "%s: %s must be > %d\n",
1357 prog, what, mi - 1);
1358 exit(1);
1359 }
1360 if (val > ma && ma >= 0) {
1361 Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
1362 exit(1);
1363 }
1364 return (val);
1365 }
1366
1367 __dead void
1368 usage(void)
1369 {
1370 extern char version[];
1371
1372 Fprintf(stderr, "Version %s\n", version);
1373 Fprintf(stderr, "Usage: %s [-dDFIlnrvx] [-g gateway] [-i iface] \
1374 [-f first_ttl] [-m max_ttl]\n\t[ -p port] [-q nqueries] [-s src_addr] [-t tos] \
1375 [-w waittime]\n\thost [packetlen]\n",
1376 prog);
1377 exit(1);
1378 }
1379