Unleashed v1.4
[unleashed.git] / usr / src / cmd / cmd-inet / usr.sbin / ping / ping_aux6.c
blob296aefd6d5198768b563622a130b7899dcb82607
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * Copyright 2015, Joyent, Inc.
35 * Portions of this source code were derived from Berkeley 4.3 BSD
36 * under license from the Regents of the University of California.
39 #include <stdio.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <signal.h>
47 #include <sys/time.h>
48 #include <sys/param.h>
49 #include <sys/socket.h>
50 #include <sys/sockio.h>
51 #include <sys/stropts.h>
52 #include <sys/file.h>
53 #include <arpa/inet.h>
54 #include <net/if.h>
55 #include <netinet/in_systm.h>
56 #include <netinet/in.h>
57 #include <netinet/ip.h>
58 #include <netinet/ip_icmp.h>
59 #include <netinet/ip6.h>
60 #include <netinet/icmp6.h>
61 #include <netinet/udp.h>
62 #include <netdb.h>
63 #include <stdlib.h>
65 #include <libinetutil.h>
66 #include "ping.h"
68 void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
69 extern void find_dstaddr(ushort_t, union any_in_addr *);
70 static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
71 extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
72 static void pr_ext_headers(struct msghdr *);
73 extern char *pr_name(char *, int);
74 extern char *pr_protocol(int);
75 static void pr_rthdr(unsigned char *);
76 static char *pr_type6(uchar_t);
77 extern void schedule_sigalrm();
78 extern void send_scheduled_probe();
79 extern boolean_t seq_match(ushort_t, int, ushort_t);
80 void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
81 extern void sigalrm_handler();
82 extern void tvsub(struct timeval *, struct timeval *);
86 * Initialize the msghdr for specifying the hoplimit, outgoing interface and
87 * routing header.
89 void
90 set_ancillary_data(struct msghdr *msgp, int hoplimit,
91 union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
93 size_t hoplimit_space;
94 size_t rthdr_space;
95 size_t pktinfo_space;
96 size_t bufspace;
97 struct cmsghdr *cmsgp;
98 uchar_t *cmsg_datap;
99 static boolean_t first = B_TRUE;
100 int i;
102 if (hoplimit == -1 && gw_cnt == 0 && if_index == 0)
103 return;
106 * Need to figure out size of buffer needed for ancillary data
107 * containing routing header and packet info options.
109 * Portable heuristic to compute upper bound on space needed for
110 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
111 * after both header and data as the worst possible upper bound on space
112 * consumed by padding.
113 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
114 * This is needed because we would like to use CMSG_NXTHDR() while
115 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
116 * parsing than composing the buffer. It requires the pointer it returns
117 * to leave space in buffer for addressing a cmsghdr and we want to make
118 * sure it works for us while we skip beyond the last ancillary data
119 * option.
121 * bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
122 * <option[i] content length> + <pad after data>;
124 * total_bufspace = bufspace[0] + bufspace[1] + ...
125 * ... + bufspace[N-1] + sizeof (struct cmsghdr);
128 rthdr_space = 0;
129 pktinfo_space = 0;
130 hoplimit_space = 0;
131 bufspace = 0;
133 if (hoplimit != -1) {
134 hoplimit_space = sizeof (int);
135 bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
136 hoplimit_space + _MAX_ALIGNMENT;
139 if (gw_cnt > 0) {
140 rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
141 bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
142 rthdr_space + _MAX_ALIGNMENT;
145 if (if_index != 0) {
146 pktinfo_space = sizeof (struct in6_pktinfo);
147 bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
148 pktinfo_space + _MAX_ALIGNMENT;
152 * We need to temporarily set the msgp->msg_controllen to bufspace
153 * (we will later trim it to actual length used). This is needed because
154 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
156 bufspace += sizeof (struct cmsghdr);
157 msgp->msg_controllen = bufspace;
160 * This function is called more than once only if -l/-S used,
161 * since we need to modify the middle gateway. So, don't alloc
162 * new memory, just reuse what msg6 points to.
164 if (first) {
165 first = B_FALSE;
166 msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
167 if (msgp->msg_control == NULL) {
168 Fprintf(stderr, "%s: malloc %s\n",
169 progname, strerror(errno));
170 exit(EXIT_FAILURE);
173 cmsgp = CMSG_FIRSTHDR(msgp);
176 * Fill ancillary data. First hoplimit, then rthdr and pktinfo.
179 /* set hoplimit ancillary data if needed */
180 if (hoplimit != -1) {
181 cmsgp->cmsg_level = IPPROTO_IPV6;
182 cmsgp->cmsg_type = IPV6_HOPLIMIT;
183 cmsg_datap = CMSG_DATA(cmsgp);
184 /* LINTED */
185 *(int *)cmsg_datap = hoplimit;
186 cmsgp->cmsg_len = cmsg_datap + hoplimit_space -
187 (uchar_t *)cmsgp;
188 cmsgp = CMSG_NXTHDR(msgp, cmsgp);
191 /* set rthdr ancillary data if needed */
192 if (gw_cnt > 0) {
193 struct ip6_rthdr0 *rthdr0p;
195 cmsgp->cmsg_level = IPPROTO_IPV6;
196 cmsgp->cmsg_type = IPV6_RTHDR;
197 cmsg_datap = CMSG_DATA(cmsgp);
200 * Initialize rthdr structure
202 /* LINTED */
203 rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
204 if (inet6_rth_init(rthdr0p, rthdr_space,
205 IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
206 Fprintf(stderr, "%s: inet6_rth_init failed\n",
207 progname);
208 exit(EXIT_FAILURE);
212 * Stuff in gateway addresses
214 for (i = 0; i < gw_cnt; i++) {
215 if (inet6_rth_add(rthdr0p, &gwIPlist[i].addr6) == -1) {
216 Fprintf(stderr,
217 "%s: inet6_rth_add\n", progname);
218 exit(EXIT_FAILURE);
222 cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
223 cmsgp = CMSG_NXTHDR(msgp, cmsgp);
226 /* set pktinfo ancillary data if needed */
227 if (if_index != 0) {
228 struct in6_pktinfo *pktinfop;
230 cmsgp->cmsg_level = IPPROTO_IPV6;
231 cmsgp->cmsg_type = IPV6_PKTINFO;
232 cmsg_datap = CMSG_DATA(cmsgp);
234 /* LINTED */
235 pktinfop = (struct in6_pktinfo *)cmsg_datap;
237 * We don't know if pktinfop->ipi6_addr is aligned properly,
238 * therefore let's use bcopy, instead of assignment.
240 (void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
241 sizeof (struct in6_addr));
244 * We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
246 pktinfop->ipi6_ifindex = if_index;
247 cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
248 cmsgp = CMSG_NXTHDR(msgp, cmsgp);
251 msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
255 * Check out the packet to see if it came from us. This logic is necessary
256 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
257 * which arrive ('tis only fair). This permits multiple copies of this
258 * program to be run without having intermingled output (or statistics!).
260 void
261 check_reply6(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
262 ushort_t udp_src_port)
264 struct icmp6_hdr *icmp6;
265 ip6_t *ip6h;
266 nd_redirect_t *nd_rdrct;
267 struct udphdr *up;
268 union any_in_addr dst_addr;
269 uchar_t *buf;
270 int32_t *intp;
271 struct sockaddr_in6 *from6;
272 struct timeval tv;
273 struct timeval *tp;
274 int64_t triptime;
275 boolean_t valid_reply = B_FALSE;
276 boolean_t reply_matched_current_target = B_FALSE; /* Is the source */
277 /* address of this reply same */
278 /* as where we're sending */
279 /* currently? */
280 boolean_t last_reply_from_targetaddr = B_FALSE; /* Is this stats, */
281 /* probe all with npackets>0 */
282 /* and we received reply for */
283 /* the last probe sent to */
284 /* targetaddr */
285 uint32_t ip6hdr_len;
286 uint8_t last_hdr;
287 int cc_left;
288 int i;
289 char tmp_buf[INET6_ADDRSTRLEN];
290 static char *unreach6[] = {
291 "No Route to Destination",
292 "Communication Administratively Prohibited",
293 "Not a Neighbor (obsoleted ICMPv6 code)",
294 "Address Unreachable",
295 "Port Unreachable"
297 static char *timexceed6[] = {
298 "Hop limit exceeded in transit",
299 "Fragment reassembly time exceeded"
301 static char *param_prob6[] = {
302 "Erroneous header field encountered",
303 "Unrecognized next header type encountered",
304 "Unrecognized IPv6 option encountered"
306 boolean_t print_newline = B_FALSE;
308 /* decompose msghdr into useful pieces */
309 buf = (uchar_t *)msg->msg_iov->iov_base;
310 from6 = (struct sockaddr_in6 *)msg->msg_name;
312 /* LINTED */
313 intp = (int32_t *)buf;
315 ping_gettime(msg, &tv);
317 /* Ignore packets > 64k or control buffers that don't fit */
318 if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
319 if (verbose) {
320 Printf("Truncated message: msg_flags 0x%x from %s\n",
321 msg->msg_flags,
322 pr_name((char *)&from6->sin6_addr, AF_INET6));
324 return;
326 if (cc < ICMP6_MINLEN) {
327 if (verbose) {
328 Printf("packet too short (%d bytes) from %s\n", cc,
329 pr_name((char *)&from6->sin6_addr, AF_INET6));
331 return;
333 /* LINTED */
334 icmp6 = (struct icmp6_hdr *)buf;
335 cc_left = cc - ICMP6_MINLEN;
337 switch (icmp6->icmp6_type) {
338 case ICMP6_DST_UNREACH:
339 /* LINTED */
340 ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
341 if (cc_left < sizeof (ip6_t)) {
342 if (verbose) {
343 Printf("packet too short (%d bytes) from %s\n",
345 pr_name((char *)&from6->sin6_addr,
346 AF_INET6));
348 return;
352 * Determine the total length of IPv6 header and extension
353 * headers, also the upper layer header (UDP, TCP, ICMP, etc.)
354 * following.
356 ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
358 cc_left -= ip6hdr_len;
360 /* LINTED */
361 up = (struct udphdr *)((char *)ip6h + ip6hdr_len);
362 if (cc_left < sizeof (struct udphdr)) {
363 if (verbose) {
364 Printf("packet too short (%d bytes) from %s\n",
366 pr_name((char *)&from6->sin6_addr,
367 AF_INET6));
369 return;
371 cc_left -= sizeof (struct udphdr);
373 /* determine if this is *the* reply */
374 if (icmp6->icmp6_code == ICMP6_DST_UNREACH_NOPORT &&
375 last_hdr == IPPROTO_UDP &&
376 udp_src_port == up->uh_sport &&
377 use_udp) {
378 valid_reply = B_TRUE;
379 } else {
380 valid_reply = B_FALSE;
383 if (valid_reply) {
385 * For this valid reply, if we are still sending to
386 * this target IP address, we'd like to do some
387 * updates to targetaddr, so hold SIGALRMs.
389 (void) sighold(SIGALRM);
390 is_alive = B_TRUE;
391 nreceived++;
392 reply_matched_current_target =
393 seq_match(current_targetaddr->starting_seq_num,
394 current_targetaddr->num_sent,
395 ntohs(up->uh_dport));
396 if (reply_matched_current_target) {
397 current_targetaddr->got_reply = B_TRUE;
398 nreceived_last_target++;
400 * Determine if stats, probe-all, and
401 * npackets != 0, and this is the reply for
402 * the last probe we sent to current target
403 * address.
405 if (stats && probe_all && npackets > 0 &&
406 ((current_targetaddr->starting_seq_num +
407 current_targetaddr->num_probes - 1) %
408 (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
409 (current_targetaddr->num_probes ==
410 current_targetaddr->num_sent))
411 last_reply_from_targetaddr = B_TRUE;
412 } else {
414 * If it's just probe_all and we just received
415 * a reply from a target address we were
416 * probing and had timed out (now we are probing
417 * some other target address), we ignore
418 * this reply.
420 if (probe_all && !stats) {
421 valid_reply = B_FALSE;
423 * Only if it's verbose, we get a
424 * message regarding this reply,
425 * otherwise we are done here.
427 if (!verbose) {
428 (void) sigrelse(SIGALRM);
429 return;
435 if (valid_reply && !stats) {
437 * if we are still sending to the same target address,
438 * then stop it, because we know it's alive.
440 if (reply_matched_current_target) {
441 (void) alarm(0); /* cancel alarm */
442 (void) sigset(SIGALRM, SIG_IGN);
443 current_targetaddr->probing_done = B_TRUE;
445 (void) sigrelse(SIGALRM);
447 if (!probe_all) {
448 Printf("%s is alive\n", targethost);
449 } else {
450 (void) inet_ntop(AF_INET6,
451 (void *)&ip6h->ip6_dst,
452 tmp_buf, sizeof (tmp_buf));
453 if (nflag) {
454 Printf("%s is alive\n", tmp_buf);
455 } else {
456 Printf("%s (%s) is alive\n",
457 targethost, tmp_buf);
460 if (reply_matched_current_target) {
462 * Let's get things going again, but now
463 * ping will start sending to next target IP
464 * address.
466 send_scheduled_probe();
467 (void) sigset(SIGALRM, sigalrm_handler);
468 schedule_sigalrm();
470 return;
471 } else {
473 * If we are not moving to next targetaddr, let's
474 * release the SIGALRM now. We don't want to stall in
475 * the middle of probing a targetaddr if the pr_name()
476 * call (see below) takes longer.
478 if (!last_reply_from_targetaddr)
479 (void) sigrelse(SIGALRM);
480 /* else, we'll release it later */
483 dst_addr.addr6 = ip6h->ip6_dst;
484 if (valid_reply) {
485 Printf("%d bytes from %s: ", cc,
486 pr_name((char *)&from6->sin6_addr, AF_INET6));
487 Printf("udp_port=%d. ", ntohs(up->uh_dport));
488 print_newline = B_TRUE;
489 } else if (is_a_target(ai_dst, &dst_addr)|| verbose) {
490 if (icmp6->icmp6_code >= A_CNT(unreach6)) {
491 Printf("ICMPv6 %d Unreachable from gateway "
492 "%s\n", icmp6->icmp6_code,
493 pr_name((char *)&from6->sin6_addr,
494 AF_INET6));
495 } else {
496 Printf("ICMPv6 %s from gateway %s\n",
497 unreach6[icmp6->icmp6_code],
498 pr_name((char *)&from6->sin6_addr,
499 AF_INET6));
501 Printf(" for %s from %s", pr_protocol(last_hdr),
502 pr_name((char *)&ip6h->ip6_src, AF_INET6));
503 Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
504 AF_INET6));
505 if (last_hdr == IPPROTO_TCP || last_hdr == IPPROTO_UDP)
506 Printf(" port %d ", ntohs(up->uh_dport));
507 print_newline = B_TRUE;
511 * Update and print the stats, if it's a valid reply and
512 * contains a timestamp.
514 if (valid_reply && datalen >= sizeof (struct timeval) &&
515 cc_left >= sizeof (struct timeval)) {
516 /* LINTED */
517 tp = (struct timeval *)((char *)up +
518 sizeof (struct udphdr));
519 (void) tvsub(&tv, tp);
520 triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
521 Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
522 tsum += triptime;
523 tsum2 += triptime*triptime;
524 if (triptime < tmin)
525 tmin = triptime;
526 if (triptime > tmax)
527 tmax = triptime;
528 print_newline = B_TRUE;
530 if (print_newline)
531 (void) putchar('\n');
533 * If it's stats, probe-all, npackets > 0, and we received reply
534 * for the last probe sent to this target address, then we
535 * don't need to wait anymore, let's move on to next target
536 * address, now!
538 if (last_reply_from_targetaddr) {
539 (void) alarm(0); /* cancel alarm */
540 current_targetaddr->probing_done = B_TRUE;
541 (void) sigrelse(SIGALRM);
542 send_scheduled_probe();
543 schedule_sigalrm();
545 break;
547 case ICMP6_PACKET_TOO_BIG:
548 /* LINTED */
549 ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
550 if (cc_left < sizeof (ip6_t)) {
551 if (verbose) {
552 Printf("packet too short (%d bytes) from %s\n",
553 cc, pr_name((char *)&from6->sin6_addr,
554 AF_INET6));
556 return;
558 ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
560 dst_addr.addr6 = ip6h->ip6_dst;
561 if (is_a_target(ai_dst, &dst_addr) || verbose) {
562 Printf("ICMPv6 packet too big from %s\n",
563 pr_name((char *)&from6->sin6_addr, AF_INET6));
565 Printf(" for %s from %s", pr_protocol(last_hdr),
566 pr_name((char *)&ip6h->ip6_src, AF_INET6));
567 Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
568 AF_INET6));
569 if ((last_hdr == IPPROTO_TCP ||
570 last_hdr == IPPROTO_UDP) &&
571 (cc_left >= (ip6hdr_len + 4))) {
572 /* LINTED */
573 up = (struct udphdr *)
574 ((char *)ip6h + ip6hdr_len);
575 Printf(" port %d ", ntohs(up->uh_dport));
577 Printf(" MTU = %d\n", ntohl(icmp6->icmp6_mtu));
579 break;
581 case ICMP6_TIME_EXCEEDED:
582 /* LINTED */
583 ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
584 if (cc_left < sizeof (ip6_t)) {
585 if (verbose) {
586 Printf("packet too short (%d bytes) from %s\n",
588 pr_name((char *)&from6->sin6_addr,
589 AF_INET6));
591 return;
593 ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
595 dst_addr.addr6 = ip6h->ip6_dst;
596 if (is_a_target(ai_dst, &dst_addr) || verbose) {
597 if (icmp6->icmp6_code >= A_CNT(timexceed6)) {
598 Printf("ICMPv6 %d time exceeded from %s\n",
599 icmp6->icmp6_code,
600 pr_name((char *)&from6->sin6_addr,
601 AF_INET6));
602 } else {
603 Printf("ICMPv6 %s from %s\n",
604 timexceed6[icmp6->icmp6_code],
605 pr_name((char *)&from6->sin6_addr,
606 AF_INET6));
608 Printf(" for %s from %s", pr_protocol(last_hdr),
609 pr_name((char *)&ip6h->ip6_src, AF_INET6));
610 Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
611 AF_INET6));
612 if ((last_hdr == IPPROTO_TCP ||
613 last_hdr == IPPROTO_UDP) &&
614 (cc_left >= (ip6hdr_len + 4))) {
615 /* LINTED */
616 up = (struct udphdr *)
617 ((char *)ip6h + ip6hdr_len);
618 Printf(" port %d", ntohs(up->uh_dport));
620 (void) putchar('\n');
622 break;
624 case ICMP6_PARAM_PROB:
625 /* LINTED */
626 ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
627 if (cc_left < sizeof (ip6_t)) {
628 if (verbose) {
629 Printf("packet too short (%d bytes) from %s\n",
631 pr_name((char *)&from6->sin6_addr,
632 AF_INET6));
634 return;
636 ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
638 dst_addr.addr6 = ip6h->ip6_dst;
639 if (is_a_target(ai_dst, &dst_addr) || verbose) {
640 if (icmp6->icmp6_code >= A_CNT(param_prob6)) {
641 Printf("ICMPv6 %d parameter problem from %s\n",
642 icmp6->icmp6_code,
643 pr_name((char *)&from6->sin6_addr,
644 AF_INET6));
645 } else {
646 Printf("ICMPv6 %s from %s\n",
647 param_prob6[icmp6->icmp6_code],
648 pr_name((char *)&from6->sin6_addr,
649 AF_INET6));
651 icmp6->icmp6_pptr = ntohl(icmp6->icmp6_pptr);
652 Printf(" in byte %d", icmp6->icmp6_pptr);
653 if (icmp6->icmp6_pptr <= ip6hdr_len) {
654 Printf(" (value 0x%x)",
655 *((uchar_t *)ip6h + icmp6->icmp6_pptr));
657 Printf(" for %s from %s", pr_protocol(last_hdr),
658 pr_name((char *)&ip6h->ip6_src, AF_INET6));
659 Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
660 AF_INET6));
661 if ((last_hdr == IPPROTO_TCP ||
662 last_hdr == IPPROTO_UDP) &&
663 (cc_left >= (ip6hdr_len + 4))) {
664 /* LINTED */
665 up = (struct udphdr *)
666 ((char *)ip6h + ip6hdr_len);
667 Printf(" port %d", ntohs(up->uh_dport));
669 (void) putchar('\n');
671 break;
673 case ICMP6_ECHO_REQUEST:
674 return;
676 case ICMP6_ECHO_REPLY:
677 if (ntohs(icmp6->icmp6_id) == ident) {
678 if (!use_udp)
679 valid_reply = B_TRUE;
680 else
681 valid_reply = B_FALSE;
682 } else {
683 return;
686 if (valid_reply) {
688 * For this valid reply, if we are still sending to
689 * this target IP address, we'd like to do some
690 * updates to targetaddr, so hold SIGALRMs.
692 (void) sighold(SIGALRM);
693 is_alive = B_TRUE;
694 nreceived++;
695 reply_matched_current_target =
696 seq_match(current_targetaddr->starting_seq_num,
697 current_targetaddr->num_sent,
698 ntohs(icmp6->icmp6_seq));
699 if (reply_matched_current_target) {
700 current_targetaddr->got_reply = B_TRUE;
701 nreceived_last_target++;
703 * Determine if stats, probe-all, and
704 * npackets != 0, and this is the reply for
705 * the last probe we sent to current target
706 * address.
708 if (stats && probe_all && npackets > 0 &&
709 ((current_targetaddr->starting_seq_num +
710 current_targetaddr->num_probes - 1) %
711 (MAX_ICMP_SEQ + 1) ==
712 ntohs(icmp6->icmp6_seq)) &&
713 (current_targetaddr->num_probes ==
714 current_targetaddr->num_sent))
715 last_reply_from_targetaddr = B_TRUE;
716 } else {
718 * If it's just probe_all and we just received
719 * a reply from a target address we were
720 * probing and had timed out (now we are probing
721 * some other target address), we ignore
722 * this reply.
724 if (probe_all && !stats) {
725 valid_reply = B_FALSE;
727 * Only if it's verbose, we get a
728 * message regarding this reply,
729 * otherwise we are done here.
731 if (!verbose) {
732 (void) sigrelse(SIGALRM);
733 return;
739 if (!stats && valid_reply) {
741 * if we are still sending to the same target address,
742 * then stop it, because we know it's alive.
744 if (reply_matched_current_target) {
745 (void) alarm(0); /* cancel alarm */
746 (void) sigset(SIGALRM, SIG_IGN);
747 current_targetaddr->probing_done = B_TRUE;
749 (void) sigrelse(SIGALRM);
751 if (!probe_all) {
752 Printf("%s is alive\n", targethost);
753 } else {
755 * If we are using send_reply, the real
756 * target address is not the src address of the
757 * replies. Use icmp_seq to find out where this
758 * probe was sent to.
760 if (send_reply) {
761 (void) find_dstaddr(
762 ntohs(icmp6->icmp6_seq), &dst_addr);
763 (void) inet_ntop(AF_INET6,
764 (void *)&dst_addr.addr6,
765 tmp_buf, sizeof (tmp_buf));
766 } else {
767 (void) inet_ntop(AF_INET6,
768 (void *)&from6->sin6_addr,
769 tmp_buf, sizeof (tmp_buf));
772 if (nflag) {
773 Printf("%s is alive\n", tmp_buf);
774 } else {
775 Printf("%s (%s) is alive\n",
776 targethost, tmp_buf);
779 if (reply_matched_current_target) {
781 * Let's get things going again, but now
782 * ping will start sending to next target IP
783 * address.
785 send_scheduled_probe();
786 (void) sigset(SIGALRM, sigalrm_handler);
787 schedule_sigalrm();
789 return;
790 } else {
792 * If we are not moving to next targetaddr, let's
793 * release the SIGALRM now. We don't want to stall in
794 * the middle of probing a targetaddr if the pr_name()
795 * call (see below) takes longer.
797 if (!last_reply_from_targetaddr)
798 (void) sigrelse(SIGALRM);
799 /* else, we'll release it later */
803 * If we are using send_reply, the real target address is
804 * not the src address of the replies. Use icmp_seq to find out
805 * where this probe was sent to.
807 if (send_reply) {
808 (void) find_dstaddr(ntohs(icmp6->icmp6_seq), &dst_addr);
809 Printf("%d bytes from %s: ", cc,
810 pr_name((char *)&dst_addr.addr6, AF_INET6));
811 } else {
812 Printf("%d bytes from %s: ", cc,
813 pr_name((char *)&from6->sin6_addr, AF_INET6));
815 Printf("icmp_seq=%d. ", ntohs(icmp6->icmp6_seq));
817 if (valid_reply && datalen >= sizeof (struct timeval) &&
818 cc_left >= sizeof (struct timeval)) {
819 /* LINTED */
820 tp = (struct timeval *)&icmp6->icmp6_data16[2];
821 (void) tvsub(&tv, tp);
822 triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
823 Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
824 tsum += triptime;
825 tsum2 += triptime*triptime;
826 if (triptime < tmin)
827 tmin = triptime;
828 if (triptime > tmax)
829 tmax = triptime;
831 (void) putchar('\n');
833 * If it's stats, probe-all, npackets > 0, and we received reply
834 * for the last probe sent to this target address, then we
835 * don't need to wait anymore, let's move on to next target
836 * address, now!
838 if (last_reply_from_targetaddr) {
839 (void) alarm(0); /* cancel alarm */
840 current_targetaddr->probing_done = B_TRUE;
841 (void) sigrelse(SIGALRM);
842 send_scheduled_probe();
843 schedule_sigalrm();
845 break;
847 case MLD_LISTENER_QUERY:
848 case MLD_LISTENER_REPORT:
849 case MLD_LISTENER_REDUCTION:
850 case ND_ROUTER_SOLICIT:
851 case ND_ROUTER_ADVERT:
852 case ND_NEIGHBOR_SOLICIT:
853 case ND_NEIGHBOR_ADVERT:
854 return;
856 case ND_REDIRECT:
857 nd_rdrct = (nd_redirect_t *)icmp6;
859 if (cc_left < sizeof (nd_redirect_t) - ICMP6_MINLEN) {
860 if (verbose) {
861 Printf("packet too short (%d bytes) from %s\n",
863 pr_name((char *)&from6->sin6_addr,
864 AF_INET6));
866 return;
868 dst_addr.addr6 = nd_rdrct->nd_rd_dst;
869 if (is_a_target(ai_dst, &dst_addr) || verbose) {
870 Printf("ICMPv6 redirect from gateway %s\n",
871 pr_name((char *)&from6->sin6_addr, AF_INET6));
873 Printf(" to %s",
874 pr_name((char *)&nd_rdrct->nd_rd_target, AF_INET6));
875 Printf(" for %s\n",
876 pr_name((char *)&nd_rdrct->nd_rd_dst, AF_INET6));
878 break;
880 default:
881 if (verbose) {
882 Printf("%d bytes from %s:\n", cc,
883 pr_name((char *)&from6->sin6_addr, AF_INET6));
884 Printf("icmp6_type=%d (%s) ", icmp6->icmp6_type,
885 pr_type6(icmp6->icmp6_type));
886 Printf("icmp6_code=%d\n", icmp6->icmp6_code);
887 for (i = 0; i < 12; i++) {
888 Printf("x%2.2x: x%8.8x\n",
889 i * sizeof (int32_t), *intp++);
892 break;
896 * If it's verbose mode and we recv'd ancillary data, print extension
897 * headers.
899 if (verbose && msg->msg_controllen > 0)
900 pr_ext_headers(msg);
904 * Convert an ICMP6 "type" field to a printable string.
906 static char *
907 pr_type6(uchar_t icmp6_type)
909 static struct icmptype_table ttab6[] = {
910 {ICMP6_DST_UNREACH, "Dest Unreachable"},
911 {ICMP6_PACKET_TOO_BIG, "Packet Too Big"},
912 {ICMP6_TIME_EXCEEDED, "Time Exceeded"},
913 {ICMP6_PARAM_PROB, "Parameter Problem"},
914 {ICMP6_ECHO_REQUEST, "Echo Request"},
915 {ICMP6_ECHO_REPLY, "Echo Reply"},
916 {MLD_LISTENER_QUERY, "Multicast Listener Query"},
917 {MLD_LISTENER_REPORT, "Multicast Listener Report"},
918 {MLD_LISTENER_REDUCTION, "Multicast Listener Done"},
919 {ND_ROUTER_SOLICIT, "Router Solicitation"},
920 {ND_ROUTER_ADVERT, "Router Advertisement"},
921 {ND_NEIGHBOR_SOLICIT, "Neighbor Solicitation"},
922 {ND_NEIGHBOR_ADVERT, "Neighbor Advertisement"},
923 {ND_REDIRECT, "Redirect Message"},
925 int i;
927 for (i = 0; i < A_CNT(ttab6); i++) {
928 if (ttab6[i].type == icmp6_type)
929 return (ttab6[i].message);
933 return ("OUT-OF-RANGE");
937 * Return the length of the IPv6 related headers (including extension headers).
938 * It also sets the *last_hdr_rtrn to the first upper layer protocol header
939 * following IPv6 header and extension headers. If print_flag is B_TRUE, it
940 * prints extension headers.
942 static int
943 IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
945 int length;
946 int exthdrlength;
947 uint8_t nexthdr;
948 uint8_t *whereptr;
949 ip6_hbh_t *hbhhdr;
950 ip6_dest_t *desthdr;
951 ip6_rthdr_t *rthdr;
952 ip6_frag_t *fraghdr;
953 uint8_t *endptr;
955 length = sizeof (ip6_t);
957 whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
958 endptr = ((uint8_t *)ip6h) + pkt_len;
960 nexthdr = ip6h->ip6_nxt;
961 *last_hdr_rtrn = IPPROTO_NONE;
963 if (whereptr >= endptr)
964 return (length);
966 while (whereptr < endptr) {
967 *last_hdr_rtrn = nexthdr;
968 switch (nexthdr) {
969 case IPPROTO_HOPOPTS:
970 hbhhdr = (ip6_hbh_t *)whereptr;
971 exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
972 if ((uchar_t *)hbhhdr + exthdrlength > endptr)
973 return (length);
974 nexthdr = hbhhdr->ip6h_nxt;
975 length += exthdrlength;
976 break;
978 case IPPROTO_DSTOPTS:
979 desthdr = (ip6_dest_t *)whereptr;
980 exthdrlength = 8 * (desthdr->ip6d_len + 1);
981 if ((uchar_t *)desthdr + exthdrlength > endptr)
982 return (length);
983 nexthdr = desthdr->ip6d_nxt;
984 length += exthdrlength;
985 break;
987 case IPPROTO_ROUTING:
988 rthdr = (ip6_rthdr_t *)whereptr;
989 exthdrlength = 8 * (rthdr->ip6r_len + 1);
990 if ((uchar_t *)rthdr + exthdrlength > endptr)
991 return (length);
992 nexthdr = rthdr->ip6r_nxt;
993 length += exthdrlength;
994 break;
996 case IPPROTO_FRAGMENT:
997 /* LINTED */
998 fraghdr = (ip6_frag_t *)whereptr;
999 if ((uchar_t *)&fraghdr[1] > endptr)
1000 return (length);
1001 nexthdr = fraghdr->ip6f_nxt;
1002 length += sizeof (struct ip6_frag);
1003 break;
1005 case IPPROTO_NONE:
1006 default:
1007 return (length);
1009 whereptr = (uint8_t *)ip6h + length;
1011 *last_hdr_rtrn = nexthdr;
1013 return (length);
1017 * Print extension headers
1019 static void
1020 pr_ext_headers(struct msghdr *msg)
1022 struct cmsghdr *cmsg;
1024 Printf(" IPv6 extension headers: ");
1026 for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
1027 cmsg = CMSG_NXTHDR(msg, cmsg)) {
1028 if (cmsg->cmsg_level == IPPROTO_IPV6) {
1029 switch (cmsg->cmsg_type) {
1030 case IPV6_HOPOPTS:
1031 Printf(" <hop-by-hop options>");
1032 break;
1034 case IPV6_DSTOPTS:
1035 Printf(" <destination options (after routing"
1036 "header)>");
1037 break;
1039 case IPV6_RTHDRDSTOPTS:
1040 Printf(" <destination options (before routing"
1041 "header)>");
1042 break;
1044 case IPV6_RTHDR:
1045 pr_rthdr((uchar_t *)CMSG_DATA(cmsg));
1046 break;
1048 default:
1049 Printf(" <option type %d>", cmsg->cmsg_type);
1050 break;
1054 (void) putchar('\n');
1058 * Print the routing header 0 information
1060 static void
1061 pr_rthdr(uchar_t *buf)
1063 ip6_rthdr_t *rthdr;
1064 ip6_rthdr0_t *rthdr0;
1065 struct in6_addr *gw_addr;
1066 int i, num_addr;
1068 rthdr = (ip6_rthdr_t *)buf;
1069 Printf(" <type %d routing header, segleft %u> ",
1070 rthdr->ip6r_type, rthdr->ip6r_segleft);
1072 if (rthdr->ip6r_type == 0) {
1073 /* LINTED */
1074 rthdr0 = (ip6_rthdr0_t *)buf;
1075 gw_addr = (struct in6_addr *)(rthdr0 + 1);
1076 num_addr = rthdr0->ip6r0_len / 2;
1078 for (i = 0; i < num_addr; i++) {
1079 Printf("%s", pr_name((char *)gw_addr, AF_INET6));
1080 if (i == (num_addr - rthdr0->ip6r0_segleft))
1081 Printf("(Current)");
1082 gw_addr++;
1083 if (i != num_addr - 1)
1084 Printf(", ");