2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that: (1) source code distributions
12 * retain the above copyright notice and this paragraph in its entirety, (2)
13 * distributions including binary code include the above copyright notice and
14 * this paragraph in its entirety in the documentation or other materials
15 * provided with the distribution, and (3) all advertising materials mentioning
16 * features or use of this software display the following acknowledgement:
17 * ``This product includes software developed by the University of California,
18 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
19 * the University nor the names of its contributors may be used to endorse
20 * or promote products derived from this software without specific prior
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <sys/socket.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/in.h>
43 #include <netinet/ip.h>
44 #include <netinet/ip_var.h>
45 #include <netinet/ip_icmp.h>
46 #include <netinet/udp.h>
47 #include <netinet/udp_var.h>
49 #include <arpa/inet.h>
52 #include <libinetutil.h>
53 #include "traceroute.h"
56 * IPv4 source routing option.
57 * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
58 * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
60 struct ip_sourceroute
{
64 /* up to 9 IPv4 addresses */
65 uint8_t ipsr_addrs
[1][sizeof (struct in_addr
)];
68 int check_reply(struct msghdr
*, int, int, uchar_t
*, uchar_t
*);
69 extern ushort_t
in_cksum(ushort_t
*, int);
70 extern char *inet_name(union any_in_addr
*, int);
71 static char *pr_type(uchar_t
);
72 void print_addr(uchar_t
*, int, struct sockaddr
*);
73 boolean_t
print_icmp_other(uchar_t
, uchar_t
);
74 void send_probe(int, struct sockaddr
*, struct ip
*, int, int,
75 struct timeval
*, int);
76 struct ip
*set_buffers(int);
77 void set_IPv4opt_sourcerouting(int, union any_in_addr
*, union any_in_addr
*);
80 * prepares the buffer to be sent as an IP datagram
86 uchar_t
*outp
; /* packet following the IP header (UDP/ICMP) */
87 struct udphdr
*outudp
;
91 outip
= (struct ip
*)malloc((size_t)plen
);
93 Fprintf(stderr
, "%s: malloc: %s\n", prog
, strerror(errno
));
98 /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
99 optlen
= 8 + gw_count
* sizeof (struct in_addr
);
102 (void) memset((char *)outip
, 0, (size_t)plen
);
103 outp
= (uchar_t
*)(outip
+ 1);
105 outip
->ip_v
= IPVERSION
;
110 * LBNL bug fixed: missing '- optlen' before, causing optlen
113 * BSD bug: BSD touches the header fields 'len' and 'ip_off'
114 * even when HDRINCL is set. It applies htons() on these
115 * fields. It should send the header untouched when HDRINCL
118 outip
->ip_len
= htons(plen
- optlen
);
119 outip
->ip_off
= htons(off
);
120 outip
->ip_hl
= (outp
- (uchar_t
*)outip
) >> 2;
122 /* setup ICMP or UDP */
124 outip
->ip_p
= IPPROTO_ICMP
;
126 /* LINTED E_BAD_PTR_CAST_ALIGN */
127 outicmp
= (struct icmp
*)outp
;
128 outicmp
->icmp_type
= ICMP_ECHO
;
129 outicmp
->icmp_id
= htons(ident
);
131 outip
->ip_p
= IPPROTO_UDP
;
133 /* LINTED E_BAD_PTR_CAST_ALIGN */
134 outudp
= (struct udphdr
*)outp
;
135 outudp
->uh_sport
= htons(ident
);
137 htons((ushort_t
)(plen
- (sizeof (struct ip
) + optlen
)));
144 * Setup the source routing for IPv4.
147 set_IPv4opt_sourcerouting(int sndsock
, union any_in_addr
*ip_addr
,
148 union any_in_addr
*gwIPlist
)
151 struct ip_sourceroute
*srp
;
152 uchar_t optlist
[MAX_IPOPTLEN
];
156 if ((pe
= getprotobyname("ip")) == NULL
) {
157 Fprintf(stderr
, "%s: unknown protocol ip\n", prog
);
161 gwV4_count
= (gw_count
< MAX_GWS
) ? gw_count
: MAX_GWS
- 1;
163 gwIPlist
[gwV4_count
].addr
= ip_addr
->addr
;
166 * the option length passed to setsockopt() needs to be a multiple of
167 * 32 bits. Therefore we need to use a 1-byte padding (source routing
168 * information takes 4x+3 bytes).
170 optlist
[0] = IPOPT_NOP
;
172 srp
= (struct ip_sourceroute
*)&optlist
[1];
173 srp
->ipsr_code
= IPOPT_LSRR
;
174 /* 3 = 1 (code) + 1 (len) + 1 (ptr) */
175 srp
->ipsr_len
= 3 + (gwV4_count
+ 1) * sizeof (gwIPlist
[0].addr
);
176 srp
->ipsr_ptr
= IPOPT_MINOFF
;
178 for (i
= 0; i
<= gwV4_count
; i
++) {
179 (void) bcopy((char *)&gwIPlist
[i
].addr
, &srp
->ipsr_addrs
[i
],
180 sizeof (struct in_addr
));
183 if (setsockopt(sndsock
, pe
->p_proto
, IP_OPTIONS
, (const char *)optlist
,
184 srp
->ipsr_len
+ 1) < 0) {
185 Fprintf(stderr
, "%s: IP_OPTIONS: %s\n", prog
, strerror(errno
));
191 * send a probe packet to the destination
194 send_probe(int sndsock
, struct sockaddr
*to
, struct ip
*outip
,
195 int seq
, int ttl
, struct timeval
*tp
, int packlen
)
199 uchar_t
*outp
; /* packet following the IP header (UDP/ICMP) */
200 struct udphdr
*outudp
;
201 struct icmp
*outicmp
;
202 struct outdata
*outdata
;
207 /* initialize buffer pointers */
208 outp
= (uchar_t
*)(outip
+ 1);
209 /* LINTED E_BAD_PTR_CAST_ALIGN */
210 outudp
= (struct udphdr
*)outp
;
211 /* LINTED E_BAD_PTR_CAST_ALIGN */
212 outicmp
= (struct icmp
*)outp
;
213 /* LINTED E_BAD_PTR_CAST_ALIGN */
214 outdata
= (struct outdata
*)(outp
+ ICMP_MINLEN
);
217 /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
218 optlen
= 8 + gw_count
* sizeof (struct in_addr
);
222 send_size
= packlen
- optlen
;
223 } else if (useicmp
) {
224 send_size
= packlen
- optlen
- sizeof (struct ip
);
226 send_size
= packlen
- optlen
- sizeof (struct ip
) -
227 sizeof (struct udphdr
);
231 outip
->ip_id
= htons(ident
+ seq
);
234 * If a raw IPv4 packet is going to be sent, the Time to Live
235 * field in the packet was initialized above. Otherwise, it is
236 * initialized here using the IPPROTO_IP level socket option.
239 if (setsockopt(sndsock
, IPPROTO_IP
, IP_TTL
, (char *)&ttl
,
241 Fprintf(stderr
, "%s: IP_TTL: %s\n", prog
,
248 * In most cases, the kernel will recalculate the ip checksum.
249 * But we must do it anyway so that the udp checksum comes out
254 in_cksum((ushort_t
*)outip
, sizeof (*outip
) + optlen
);
255 if (outip
->ip_sum
== 0)
256 outip
->ip_sum
= 0xffff;
265 outicmp
->icmp_seq
= htons(seq
);
267 outudp
->uh_dport
= htons((port
+ seq
) % (MAX_PORT
+ 1));
271 /* LINTED E_BAD_PTR_CAST_ALIGN */
272 ((struct sockaddr_in
*)to
)->sin_port
= outudp
->uh_dport
;
274 /* (We can only do the checksum if we know our ip address) */
277 outicmp
->icmp_cksum
= 0;
278 outicmp
->icmp_cksum
= in_cksum((ushort_t
*)outicmp
,
279 packlen
- (sizeof (struct ip
) + optlen
));
280 if (outicmp
->icmp_cksum
== 0)
281 outicmp
->icmp_cksum
= 0xffff;
283 /* Checksum (must save and restore ip header) */
285 ui
= (struct udpiphdr
*)outip
;
289 ui
->ui_len
= outudp
->uh_ulen
;
291 outudp
->uh_sum
= in_cksum((ushort_t
*)ui
, packlen
);
292 if (outudp
->uh_sum
== 0)
293 outudp
->uh_sum
= 0xffff;
299 cc
= sendto(sndsock
, (char *)outip
, send_size
, 0, to
,
300 sizeof (struct sockaddr_in
));
301 } else if (useicmp
) {
302 cc
= sendto(sndsock
, (char *)outicmp
, send_size
, 0, to
,
303 sizeof (struct sockaddr_in
));
305 cc
= sendto(sndsock
, (char *)outp
, send_size
, 0, to
,
306 sizeof (struct sockaddr_in
));
309 if (cc
< 0 || cc
!= send_size
) {
311 Fprintf(stderr
, "%s: sendto: %s\n", prog
,
314 Printf("%s: wrote %s %d chars, ret=%d\n",
315 prog
, hostname
, send_size
, cc
);
316 (void) fflush(stdout
);
321 * Check out the reply packet to see if it's what we were expecting.
322 * Returns REPLY_GOT_TARGET if the reply comes from the target
323 * REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
324 * REPLY_GOT_OTHER for other kinds of unreachables indicating none of
325 * the above two cases
327 * It also sets the icmp type and icmp code values
330 check_reply(struct msghdr
*msg
, int cc
, int seq
, uchar_t
*type
, uchar_t
*code
)
332 uchar_t
*buf
= msg
->msg_iov
->iov_base
;
333 struct sockaddr_in
*from_in
= (struct sockaddr_in
*)msg
->msg_name
;
339 /* LINTED E_BAD_PTR_CAST_ALIGN */
340 ip
= (struct ip
*)buf
;
341 hlen
= ip
->ip_hl
<< 2;
342 if (cc
< hlen
+ ICMP_MINLEN
) {
344 Printf("packet too short (%d bytes) from %s\n",
345 cc
, inet_ntoa(from_in
->sin_addr
));
347 return (REPLY_SHORT_PKT
);
350 /* LINTED E_BAD_PTR_CAST_ALIGN */
351 icp
= (struct icmp
*)(buf
+ hlen
);
353 *type
= icp
->icmp_type
;
354 *code
= icp
->icmp_code
;
357 * traceroute interpretes only ICMP_TIMXCEED_INTRANS, ICMP_UNREACH and
358 * ICMP_ECHOREPLY, ignores others
360 if ((*type
== ICMP_TIMXCEED
&& *code
== ICMP_TIMXCEED_INTRANS
) ||
361 *type
== ICMP_UNREACH
|| *type
== ICMP_ECHOREPLY
) {
368 hlen
= hip
->ip_hl
<< 2;
371 if (*type
== ICMP_ECHOREPLY
&&
372 icp
->icmp_id
== htons(ident
) &&
373 icp
->icmp_seq
== htons(seq
))
374 return (REPLY_GOT_TARGET
);
376 /* LINTED E_BAD_PTR_CAST_ALIGN */
377 hicmp
= (struct icmp
*)((uchar_t
*)hip
+ hlen
);
379 if (ICMP_MINLEN
<= cc
&&
380 hip
->ip_p
== IPPROTO_ICMP
&&
381 hicmp
->icmp_id
== htons(ident
) &&
382 hicmp
->icmp_seq
== htons(seq
)) {
383 return ((*type
== ICMP_TIMXCEED
) ?
384 REPLY_GOT_GATEWAY
: REPLY_GOT_OTHER
);
387 /* LINTED E_BAD_PTR_CAST_ALIGN */
388 up
= (struct udphdr
*)((uchar_t
*)hip
+ hlen
);
390 * at least 4 bytes of UDP header is required for this
394 hip
->ip_p
== IPPROTO_UDP
&&
395 up
->uh_sport
== htons(ident
) &&
396 up
->uh_dport
== htons((port
+ seq
) %
398 if (*type
== ICMP_UNREACH
&&
399 *code
== ICMP_UNREACH_PORT
) {
400 return (REPLY_GOT_TARGET
);
401 } else if (*type
== ICMP_TIMXCEED
) {
402 return (REPLY_GOT_GATEWAY
);
404 return (REPLY_GOT_OTHER
);
412 uchar_t
*lp
= (uchar_t
*)ip
;
415 Printf("\n%d bytes from %s to ", cc
,
416 inet_ntoa(from_in
->sin_addr
));
417 Printf("%s: icmp type %d (%s) code %d\n",
418 inet_ntoa(ip
->ip_dst
), *type
, pr_type(*type
), *code
);
419 for (i
= 0; i
< cc
; i
+= 4) {
421 for (j
= 0; ((j
< 4) && ((i
+ j
) < cc
)); j
++)
422 Printf("%2.2x", *lp
++);
423 (void) putchar('\n');
427 return (REPLY_SHORT_PKT
);
431 * convert an ICMP "type" field to a printable string.
434 pr_type(uchar_t type
)
436 static struct icmptype_table ttab
[] = {
437 {ICMP_ECHOREPLY
, "Echo Reply"},
440 {ICMP_UNREACH
, "Dest Unreachable"},
441 {ICMP_SOURCEQUENCH
, "Source Quench"},
442 {ICMP_REDIRECT
, "Redirect"},
446 {ICMP_ROUTERADVERT
, "Router Advertisement"},
447 {ICMP_ROUTERSOLICIT
, "Router Solicitation"},
448 {ICMP_TIMXCEED
, "Time Exceeded"},
449 {ICMP_PARAMPROB
, "Param Problem"},
450 {ICMP_TSTAMP
, "Timestamp"},
451 {ICMP_TSTAMPREPLY
, "Timestamp Reply"},
452 {ICMP_IREQ
, "Info Request"},
453 {ICMP_IREQREPLY
, "Info Reply"},
454 {ICMP_MASKREQ
, "Netmask Request"},
455 {ICMP_MASKREPLY
, "Netmask Reply"}
459 for (i
= 0; i
< A_CNT(ttab
); i
++) {
460 if (ttab
[i
].type
== type
)
461 return (ttab
[i
].message
);
464 return ("OUT-OF-RANGE");
468 * print the IPv4 src address of the reply packet
471 print_addr(uchar_t
*buf
, int cc
, struct sockaddr
*from
)
473 /* LINTED E_BAD_PTR_CAST_ALIGN */
474 struct sockaddr_in
*from_in
= (struct sockaddr_in
*)from
;
476 union any_in_addr ip_addr
;
478 ip_addr
.addr
= from_in
->sin_addr
;
480 /* LINTED E_BAD_PTR_CAST_ALIGN */
481 ip
= (struct ip
*)buf
;
484 Printf(" %s", inet_ntoa(from_in
->sin_addr
));
486 Printf(" %s (%s)", inet_name(&ip_addr
, AF_INET
),
487 inet_ntoa(from_in
->sin_addr
));
491 Printf(" %d bytes to %s", cc
, inet_ntoa(ip
->ip_dst
));
495 * ICMP messages which doesn't mean we got the target, or we got a gateway, are
496 * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
499 print_icmp_other(uchar_t type
, uchar_t code
)
501 boolean_t unreach
= _B_FALSE
;
504 * this function only prints '!*' for ICMP unreachable messages,
507 if (type
!= ICMP_UNREACH
) {
512 case ICMP_UNREACH_PORT
:
515 case ICMP_UNREACH_NET_UNKNOWN
:
516 case ICMP_UNREACH_NET
:
521 case ICMP_UNREACH_HOST_UNKNOWN
:
522 case ICMP_UNREACH_HOST
:
527 case ICMP_UNREACH_PROTOCOL
:
531 case ICMP_UNREACH_NEEDFRAG
:
536 case ICMP_UNREACH_SRCFAIL
:
541 case ICMP_UNREACH_FILTER_PROHIB
:
542 case ICMP_UNREACH_NET_PROHIB
:
543 case ICMP_UNREACH_HOST_PROHIB
:
548 case ICMP_UNREACH_TOSNET
:
549 case ICMP_UNREACH_TOSHOST
:
554 case ICMP_UNREACH_ISOLATED
:
555 case ICMP_UNREACH_HOST_PRECEDENCE
:
556 case ICMP_UNREACH_PRECEDENCE_CUTOFF
:
563 Printf(" !<%d>", code
);