2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
8 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that: (1) source code distributions
13 * retain the above copyright notice and this paragraph in its entirety, (2)
14 * distributions including binary code include the above copyright notice and
15 * this paragraph in its entirety in the documentation or other materials
16 * provided with the distribution, and (3) all advertising materials mentioning
17 * features or use of this software display the following acknowledgement:
18 * ``This product includes software developed by the University of California,
19 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
20 * the University nor the names of its contributors may be used to endorse
21 * or promote products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
24 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
31 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <sys/socket.h>
43 #include <netinet/in_systm.h>
44 #include <netinet/in.h>
45 #include <netinet/ip.h>
46 #include <netinet/ip_var.h>
47 #include <netinet/ip_icmp.h>
48 #include <netinet/udp.h>
49 #include <netinet/udp_var.h>
50 #include <netinet/ip6.h>
51 #include <netinet/icmp6.h>
53 #include <arpa/inet.h>
55 #include <libinetutil.h>
56 #include "traceroute.h"
58 int check_reply6(struct msghdr
*, int, int, uchar_t
*, uchar_t
*);
59 void *find_ancillary_data(struct msghdr
*, int, int);
60 extern char *inet_name(union any_in_addr
*, int);
61 static int IPv6_hdrlen(ip6_t
*, int, uint8_t *);
62 static char *pr_type6(uchar_t
);
63 void print_addr6(uchar_t
*, int, struct sockaddr
*);
64 boolean_t
print_icmp_other6(uchar_t
, uchar_t
);
65 void send_probe6(int, struct msghdr
*, struct ip
*, int, int,
66 struct timeval
*, int);
67 void set_ancillary_data(struct msghdr
*, int, union any_in_addr
*, int, uint_t
);
68 struct ip
*set_buffers6(int);
69 static boolean_t
update_hoplimit_ancillary_data(struct msghdr
*, int);
72 * prepares the buffer to be sent as an IP datagram
75 set_buffers6(int plen
)
79 struct udphdr
*outudp
;
83 outip
= (struct ip
*)malloc((size_t)plen
);
85 Fprintf(stderr
, "%s: malloc: %s\n", prog
, strerror(errno
));
90 /* ip6_rthdr0 structure includes one gateway address */
91 optlen
= sizeof (struct ip6_rthdr0
) +
92 gw_count
* sizeof (struct in6_addr
);
95 (void) memset((char *)outip
, 0, (size_t)plen
);
96 outp
= (uchar_t
*)(outip
+ 1);
99 /* LINTED E_BAD_PTR_CAST_ALIGN */
100 outicmp
= (struct icmp
*)outp
;
101 outicmp
->icmp_type
= ICMP6_ECHO_REQUEST
;
102 outicmp
->icmp_id
= htons(ident
);
104 /* LINTED E_BAD_PTR_CAST_ALIGN */
105 outudp
= (struct udphdr
*)outp
;
107 * "source port" is set at bind() call, so we don't do it
110 outudp
->uh_ulen
= htons((ushort_t
)(plen
-
111 (sizeof (struct ip6_hdr
) + optlen
)));
118 * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
119 * header for the probe packets.
122 set_ancillary_data(struct msghdr
*msgp
, int hoplimit
,
123 union any_in_addr
*gwIPlist
, int gw_cnt
, uint_t if_index
)
125 size_t hoplimit_space
;
127 size_t pktinfo_space
;
129 struct cmsghdr
*cmsgp
;
133 msgp
->msg_control
= NULL
;
134 msgp
->msg_controllen
= 0;
137 * Need to figure out size of buffer needed for ancillary data
138 * containing routing header and packet info options.
140 * Portable heuristic to compute upper bound on space needed for
141 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
142 * after both header and data as the worst possible upper bound on space
143 * consumed by padding.
144 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
145 * This is needed because we would like to use CMSG_NXTHDR() while
146 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
147 * parsing than composing the buffer. It requires the pointer it returns
148 * to leave space in buffer for addressing a cmsghdr and we want to make
149 * sure it works for us while we skip beyond the last ancillary data
152 * bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
153 * <option[i] content length> + <pad after data>;
155 * total_bufspace = bufspace[0] + bufspace[1] + ...
156 * ... + bufspace[N-1] + sizeof (struct cmsghdr);
161 /* We'll always set the hoplimit of the outgoing packets */
162 hoplimit_space
= sizeof (int);
163 bufspace
= sizeof (struct cmsghdr
) + _MAX_ALIGNMENT
+
164 hoplimit_space
+ _MAX_ALIGNMENT
;
167 rthdr_space
= inet6_rth_space(IPV6_RTHDR_TYPE_0
, gw_cnt
);
168 bufspace
+= sizeof (struct cmsghdr
) + _MAX_ALIGNMENT
+
169 rthdr_space
+ _MAX_ALIGNMENT
;
173 pktinfo_space
= sizeof (struct in6_pktinfo
);
174 bufspace
+= sizeof (struct cmsghdr
) + _MAX_ALIGNMENT
+
175 pktinfo_space
+ _MAX_ALIGNMENT
;
179 * We need to temporarily set the msgp->msg_controllen to bufspace
180 * (we will later trim it to actual length used). This is needed because
181 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
183 bufspace
+= sizeof (struct cmsghdr
);
184 msgp
->msg_controllen
= bufspace
;
186 msgp
->msg_control
= (struct cmsghdr
*)malloc(bufspace
);
187 if (msgp
->msg_control
== NULL
) {
188 Fprintf(stderr
, "%s: malloc %s\n", prog
, strerror(errno
));
191 cmsgp
= CMSG_FIRSTHDR(msgp
);
194 * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
198 /* set hoplimit ancillary data */
199 cmsgp
->cmsg_level
= IPPROTO_IPV6
;
200 cmsgp
->cmsg_type
= IPV6_HOPLIMIT
;
201 cmsg_datap
= CMSG_DATA(cmsgp
);
202 /* LINTED E_BAD_PTR_CAST_ALIGN */
203 *(int *)cmsg_datap
= hoplimit
;
204 cmsgp
->cmsg_len
= cmsg_datap
+ hoplimit_space
- (uchar_t
*)cmsgp
;
205 cmsgp
= CMSG_NXTHDR(msgp
, cmsgp
);
207 /* set rthdr ancillary data if needed */
209 struct ip6_rthdr0
*rthdr0p
;
211 cmsgp
->cmsg_level
= IPPROTO_IPV6
;
212 cmsgp
->cmsg_type
= IPV6_RTHDR
;
213 cmsg_datap
= CMSG_DATA(cmsgp
);
216 * Initialize rthdr structure
218 /* LINTED E_BAD_PTR_CAST_ALIGN */
219 rthdr0p
= (struct ip6_rthdr0
*)cmsg_datap
;
220 if (inet6_rth_init(rthdr0p
, rthdr_space
,
221 IPV6_RTHDR_TYPE_0
, gw_cnt
) == NULL
) {
222 Fprintf(stderr
, "%s: inet6_rth_init failed\n",
228 * Stuff in gateway addresses
230 for (i
= 0; i
< gw_cnt
; i
++) {
231 if (inet6_rth_add(rthdr0p
,
232 &gwIPlist
[i
].addr6
) == -1) {
234 "%s: inet6_rth_add\n", prog
);
239 cmsgp
->cmsg_len
= cmsg_datap
+ rthdr_space
- (uchar_t
*)cmsgp
;
240 cmsgp
= CMSG_NXTHDR(msgp
, cmsgp
);
243 /* set pktinfo ancillary data if needed */
245 struct in6_pktinfo
*pktinfop
;
247 cmsgp
->cmsg_level
= IPPROTO_IPV6
;
248 cmsgp
->cmsg_type
= IPV6_PKTINFO
;
249 cmsg_datap
= CMSG_DATA(cmsgp
);
251 /* LINTED E_BAD_PTR_CAST_ALIGN */
252 pktinfop
= (struct in6_pktinfo
*)cmsg_datap
;
254 * We don't know if pktinfop->ipi6_addr is aligned properly,
255 * therefore let's use bcopy, instead of assignment.
257 (void) bcopy(&in6addr_any
, &pktinfop
->ipi6_addr
,
258 sizeof (struct in6_addr
));
261 * We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
263 pktinfop
->ipi6_ifindex
= if_index
;
264 cmsgp
->cmsg_len
= cmsg_datap
+ pktinfo_space
- (uchar_t
*)cmsgp
;
265 cmsgp
= CMSG_NXTHDR(msgp
, cmsgp
);
268 msgp
->msg_controllen
= (char *)cmsgp
- (char *)msgp
->msg_control
;
272 * Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
273 * and update the hoplimit.
274 * Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
278 update_hoplimit_ancillary_data(struct msghdr
*msg
, int hoplimit
)
280 struct cmsghdr
*cmsg
;
283 for (cmsg
= CMSG_FIRSTHDR(msg
); cmsg
!= NULL
;
284 cmsg
= CMSG_NXTHDR(msg
, cmsg
)) {
285 if (cmsg
->cmsg_level
== IPPROTO_IPV6
&&
286 cmsg
->cmsg_type
== IPV6_HOPLIMIT
) {
287 /* LINTED E_BAD_PTR_CAST_ALIGN */
288 intp
= (int *)(CMSG_DATA(cmsg
));
298 * send a probe packet to the destination
301 send_probe6(int sndsock
, struct msghdr
*msg6
, struct ip
*outip
, int seq
,
302 int ttl
, struct timeval
*tp
, int packlen
)
305 struct icmp
*outicmp
;
306 struct outdata
*outdata
;
311 struct sockaddr_in6
*to6
;
314 /* ip6_rthdr0 structure includes one gateway address */
315 optlen
= sizeof (struct ip6_rthdr0
) +
316 gw_count
* sizeof (struct in6_addr
);
319 send_size
= packlen
- sizeof (struct ip6_hdr
) - optlen
;
321 /* if using UDP, further discount UDP header size */
323 send_size
-= sizeof (struct udphdr
);
325 /* initialize buffer pointers */
326 outp
= (uchar_t
*)(outip
+ 1);
327 /* LINTED E_BAD_PTR_CAST_ALIGN */
328 outicmp
= (struct icmp
*)outp
;
329 /* LINTED E_BAD_PTR_CAST_ALIGN */
330 outdata
= (struct outdata
*)(outp
+ ICMP6_MINLEN
);
332 if (!update_hoplimit_ancillary_data(msg6
, ttl
)) {
334 "%s: can't find IPV6_HOPLIMIT ancillary data\n", prog
);
344 outicmp
->icmp_seq
= htons(seq
);
346 to6
= (struct sockaddr_in6
*)msg6
->msg_name
;
347 to6
->sin6_port
= htons((port
+ seq
) % (MAX_PORT
+ 1));
351 iov
.iov_len
= send_size
;
353 msg6
->msg_iov
= &iov
;
354 msg6
->msg_iovlen
= 1;
356 cc
= sendmsg(sndsock
, msg6
, 0);
358 if (cc
< 0 || cc
!= send_size
) {
360 Fprintf(stderr
, "%s: sendmsg: %s\n", prog
,
363 Printf("%s: wrote %s %d chars, ret=%d\n",
364 prog
, hostname
, send_size
, cc
);
365 (void) fflush(stdout
);
370 * Return a pointer to the ancillary data for the given cmsg_level and
372 * If not found return NULL.
375 find_ancillary_data(struct msghdr
*msg
, int cmsg_level
, int cmsg_type
)
377 struct cmsghdr
*cmsg
;
379 for (cmsg
= CMSG_FIRSTHDR(msg
); cmsg
!= NULL
;
380 cmsg
= CMSG_NXTHDR(msg
, cmsg
)) {
381 if (cmsg
->cmsg_level
== cmsg_level
&&
382 cmsg
->cmsg_type
== cmsg_type
) {
383 return (CMSG_DATA(cmsg
));
390 * Check out the reply packet to see if it's what we were expecting.
391 * Returns REPLY_GOT_TARGET if the reply comes from the target
392 * REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
393 * REPLY_GOT_OTHER for other kinds of unreachables indicating none of
394 * the above two cases
396 * It also sets the icmp type and icmp code values
399 check_reply6(struct msghdr
*msg
, int cc
, int seq
, uchar_t
*type
, uchar_t
*code
)
401 uchar_t
*buf
= msg
->msg_iov
->iov_base
;
402 struct sockaddr_in6
*from_in6
= (struct sockaddr_in6
*)msg
->msg_name
;
407 char temp_buf
[INET6_ADDRSTRLEN
]; /* use for inet_ntop() */
409 /* Ignore packets > 64k or control buffers that don't fit */
410 if (msg
->msg_flags
& (MSG_TRUNC
|MSG_CTRUNC
)) {
412 Printf("Truncated message: msg_flags 0x%x from %s\n",
415 (void *)&(from_in6
->sin6_addr
),
416 temp_buf
, sizeof (temp_buf
)));
418 return (REPLY_SHORT_PKT
);
420 if (cc
< ICMP6_MINLEN
) {
422 Printf("packet too short (%d bytes) from %s\n",
425 (void *)&(from_in6
->sin6_addr
),
426 temp_buf
, sizeof (temp_buf
)));
428 return (REPLY_SHORT_PKT
);
430 /* LINTED E_BAD_PTR_CAST_ALIGN */
431 icp6
= (icmp6_t
*)buf
;
432 *type
= icp6
->icmp6_type
;
433 *code
= icp6
->icmp6_code
;
436 * traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
437 * ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
438 * ICMP6_PARAMPROB_NEXTHEADER, ignores others
440 if ((*type
== ICMP6_TIME_EXCEEDED
&&
441 *code
== ICMP6_TIME_EXCEED_TRANSIT
) ||
442 *type
== ICMP6_DST_UNREACH
|| *type
== ICMP6_ECHO_REPLY
||
443 *type
== ICMP6_PACKET_TOO_BIG
||
444 (*type
== ICMP6_PARAM_PROB
&&
445 *code
== ICMP6_PARAMPROB_NEXTHEADER
)) {
451 hip6
= (ip6_t
*)&(icp6
->icmp6_data32
[1]);
452 last_hdr
= hip6
->ip6_nxt
;
453 ip6hdr_len
= IPv6_hdrlen(hip6
, cc
, &last_hdr
);
457 if (*type
== ICMP6_ECHO_REPLY
&&
458 icp6
->icmp6_id
== htons(ident
) &&
459 icp6
->icmp6_seq
== htons(seq
)) {
460 return (REPLY_GOT_TARGET
);
463 /* LINTED E_BAD_PTR_CAST_ALIGN */
464 hicmp6
= (icmp6_t
*)((uchar_t
*)hip6
+ ip6hdr_len
);
466 if (ICMP6_MINLEN
<= cc
&&
467 last_hdr
== IPPROTO_ICMPV6
&&
468 hicmp6
->icmp6_id
== htons(ident
) &&
469 hicmp6
->icmp6_seq
== htons(seq
)) {
470 if (*type
== ICMP6_TIME_EXCEEDED
) {
471 return (REPLY_GOT_GATEWAY
);
473 return (REPLY_GOT_OTHER
);
477 /* LINTED E_BAD_PTR_CAST_ALIGN */
478 up
= (struct udphdr
*)((uchar_t
*)hip6
+ ip6hdr_len
);
480 * at least 4 bytes of UDP header is required for this
484 last_hdr
== IPPROTO_UDP
&&
485 up
->uh_sport
== htons(ident
) &&
486 up
->uh_dport
== htons((port
+ seq
) %
488 if (*type
== ICMP6_DST_UNREACH
&&
489 *code
== ICMP6_DST_UNREACH_NOPORT
) {
490 return (REPLY_GOT_TARGET
);
491 } else if (*type
== ICMP6_TIME_EXCEEDED
) {
492 return (REPLY_GOT_GATEWAY
);
494 return (REPLY_GOT_OTHER
);
502 uchar_t
*lp
= (uchar_t
*)icp6
;
503 struct in6_addr
*dst
;
504 struct in6_pktinfo
*pkti
;
506 pkti
= (struct in6_pktinfo
*)find_ancillary_data(msg
,
507 IPPROTO_IPV6
, IPV6_PKTINFO
);
510 "%s: can't find IPV6_PKTINFO ancillary data\n",
514 dst
= &pkti
->ipi6_addr
;
516 Printf("\n%d bytes from %s to ", cc
,
517 inet_ntop(AF_INET6
, (const void *)&(from_in6
->sin6_addr
),
518 temp_buf
, sizeof (temp_buf
)));
519 Printf("%s: icmp type %d (%s) code %d\n",
520 inet_ntop(AF_INET6
, (const void *)dst
,
521 temp_buf
, sizeof (temp_buf
)),
522 *type
, pr_type6(*type
), *code
);
523 for (i
= 0; i
< cc
; i
+= 4) {
525 for (j
= 0; ((j
< 4) && ((i
+ j
) < cc
)); j
++)
526 Printf("%2.2x", *lp
++);
527 (void) putchar('\n');
531 return (REPLY_SHORT_PKT
);
535 * Return the length of the IPv6 related headers (including extension headers)
538 IPv6_hdrlen(ip6_t
*ip6h
, int pkt_len
, uint8_t *last_hdr_rtrn
)
550 length
= sizeof (ip6_t
);
552 whereptr
= ((uint8_t *)&ip6h
[1]); /* point to next hdr */
553 endptr
= ((uint8_t *)ip6h
) + pkt_len
;
555 nexthdr
= ip6h
->ip6_nxt
;
556 *last_hdr_rtrn
= IPPROTO_NONE
;
558 if (whereptr
>= endptr
)
561 while (whereptr
< endptr
) {
562 *last_hdr_rtrn
= nexthdr
;
564 case IPPROTO_HOPOPTS
:
565 hbhhdr
= (ip6_hbh_t
*)whereptr
;
566 exthdrlength
= 8 * (hbhhdr
->ip6h_len
+ 1);
567 if ((uchar_t
*)hbhhdr
+ exthdrlength
> endptr
)
569 nexthdr
= hbhhdr
->ip6h_nxt
;
570 length
+= exthdrlength
;
573 case IPPROTO_DSTOPTS
:
574 desthdr
= (ip6_dest_t
*)whereptr
;
575 exthdrlength
= 8 * (desthdr
->ip6d_len
+ 1);
576 if ((uchar_t
*)desthdr
+ exthdrlength
> endptr
)
578 nexthdr
= desthdr
->ip6d_nxt
;
579 length
+= exthdrlength
;
582 case IPPROTO_ROUTING
:
583 rthdr
= (ip6_rthdr_t
*)whereptr
;
584 exthdrlength
= 8 * (rthdr
->ip6r_len
+ 1);
585 if ((uchar_t
*)rthdr
+ exthdrlength
> endptr
)
587 nexthdr
= rthdr
->ip6r_nxt
;
588 length
+= exthdrlength
;
591 case IPPROTO_FRAGMENT
:
592 /* LINTED E_BAD_PTR_CAST_ALIGN */
593 fraghdr
= (ip6_frag_t
*)whereptr
;
594 if ((uchar_t
*)&fraghdr
[1] > endptr
)
596 nexthdr
= fraghdr
->ip6f_nxt
;
597 length
+= sizeof (struct ip6_frag
);
604 whereptr
= (uint8_t *)ip6h
+ length
;
606 *last_hdr_rtrn
= nexthdr
;
612 * convert an ICMP6 "type" field to a printable string.
615 pr_type6(uchar_t type
)
617 static struct icmptype_table ttab6
[] = {
618 {ICMP6_DST_UNREACH
, "Dest Unreachable"},
619 {ICMP6_PACKET_TOO_BIG
, "Packet Too Big"},
620 {ICMP6_TIME_EXCEEDED
, "Time Exceeded"},
621 {ICMP6_PARAM_PROB
, "Param Problem"},
622 {ICMP6_ECHO_REQUEST
, "Echo Request"},
623 {ICMP6_ECHO_REPLY
, "Echo Reply"},
624 {MLD_LISTENER_QUERY
, "Multicast Listener Query"},
625 {MLD_LISTENER_REPORT
, "Multicast Listener Report"},
626 {MLD_LISTENER_REDUCTION
, "Multicast Listener Done"},
627 {ND_ROUTER_SOLICIT
, "Router Solicitation"},
628 {ND_ROUTER_ADVERT
, "Router Advertisement"},
629 {ND_NEIGHBOR_SOLICIT
, "Neighbor Solicitation"},
630 {ND_NEIGHBOR_ADVERT
, "Neighbor Advertisement"},
631 {ND_REDIRECT
, "Redirect Message"}
635 for (i
= 0; i
< A_CNT(ttab6
); i
++) {
636 if (ttab6
[i
].type
== type
)
637 return (ttab6
[i
].message
);
640 return ("OUT-OF-RANGE");
645 * print the IPv6 src address of the reply packet
648 print_addr6(uchar_t
*buf
, int cc
, struct sockaddr
*from
)
650 /* LINTED E_BAD_PTR_CAST_ALIGN */
651 struct sockaddr_in6
*from_in6
= (struct sockaddr_in6
*)from
;
653 union any_in_addr ip_addr
;
655 char temp_buf
[INET6_ADDRSTRLEN
]; /* use for inet_ntop() */
657 ip_addr
.addr6
= from_in6
->sin6_addr
;
659 /* LINTED E_BAD_PTR_CAST_ALIGN */
662 (void) inet_ntop(AF_INET6
, &(from_in6
->sin6_addr
), temp_buf
,
665 resolved_name
= inet_name(&ip_addr
, AF_INET6
);
667 * If the IPv6 address cannot be resolved to hostname, inet_name()
668 * returns the IPv6 address as a string. In that case, we choose not
669 * to print it twice. This saves us space on display.
671 if (nflag
|| (strcmp(temp_buf
, resolved_name
) == 0))
672 Printf(" %s", temp_buf
);
674 Printf(" %s (%s)", resolved_name
, temp_buf
);
677 Printf(" %d bytes to %s", cc
, inet_ntop(AF_INET6
,
678 (const void *) &(ip
->ip6_dst
), temp_buf
,
684 * ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
685 * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
688 print_icmp_other6(uchar_t type
, uchar_t code
)
690 boolean_t unreach
= _B_FALSE
;
694 /* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
695 case ICMP6_PACKET_TOO_BIG
:
700 case ICMP6_PARAM_PROB
:
701 /* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
702 if (code
== ICMP6_PARAMPROB_NEXTHEADER
) {
708 case ICMP6_DST_UNREACH
:
710 case ICMP6_DST_UNREACH_NOPORT
:
713 case ICMP6_DST_UNREACH_NOROUTE
:
718 case ICMP6_DST_UNREACH_ADMIN
:
723 case ICMP6_DST_UNREACH_ADDR
:
728 case ICMP6_DST_UNREACH_NOTNEIGHBOR
:
735 Printf(" !<%d>", code
);