xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
1 /*
2  * CDDL HEADER START
3  *
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.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Copyright 2015, Joyent, Inc.
32  */
33 
34 /*
35  * Portions of this source code were derived from Berkeley 4.3 BSD
36  * under license from the Regents of the University of California.
37  */
38 
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>
46 
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>
64 
65 #include <libinetutil.h>
66 #include "ping.h"
67 
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 *);
83 
84 
85 /*
86  * Initialize the msghdr for specifying the hoplimit, outgoing interface and
87  * routing header.
88  */
89 void
90 set_ancillary_data(struct msghdr *msgp, int hoplimit,
91     union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
92 {
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;
101 
102 	if (hoplimit == -1 && gw_cnt == 0 && if_index == 0)
103 		return;
104 
105 	/*
106 	 * Need to figure out size of buffer needed for ancillary data
107 	 * containing routing header and packet info options.
108 	 *
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.
120 	 *
121 	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
122 	 *		<option[i] content length> + <pad after data>;
123 	 *
124 	 * total_bufspace = bufspace[0] + bufspace[1] + ...
125 	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
126 	 */
127 
128 	rthdr_space = 0;
129 	pktinfo_space = 0;
130 	hoplimit_space = 0;
131 	bufspace = 0;
132 
133 	if (hoplimit != -1) {
134 		hoplimit_space = sizeof (int);
135 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
136 		    hoplimit_space + _MAX_ALIGNMENT;
137 	}
138 
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;
143 	}
144 
145 	if (if_index != 0) {
146 		pktinfo_space = sizeof (struct in6_pktinfo);
147 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
148 		    pktinfo_space + _MAX_ALIGNMENT;
149 	}
150 
151 	/*
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.
155 	 */
156 	bufspace += sizeof (struct cmsghdr);
157 	msgp->msg_controllen = bufspace;
158 
159 	/*
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.
163 	 */
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);
171 		}
172 	};
173 	cmsgp = CMSG_FIRSTHDR(msgp);
174 
175 	/*
176 	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo.
177 	 */
178 
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);
189 	}
190 
191 	/* set rthdr ancillary data if needed */
192 	if (gw_cnt > 0) {
193 		struct ip6_rthdr0 *rthdr0p;
194 
195 		cmsgp->cmsg_level = IPPROTO_IPV6;
196 		cmsgp->cmsg_type = IPV6_RTHDR;
197 		cmsg_datap = CMSG_DATA(cmsgp);
198 
199 		/*
200 		 * Initialize rthdr structure
201 		 */
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);
209 		}
210 
211 		/*
212 		 * Stuff in gateway addresses
213 		 */
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);
219 			}
220 		}
221 
222 		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
223 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
224 	}
225 
226 	/* set pktinfo ancillary data if needed */
227 	if (if_index != 0) {
228 		struct in6_pktinfo *pktinfop;
229 
230 		cmsgp->cmsg_level = IPPROTO_IPV6;
231 		cmsgp->cmsg_type = IPV6_PKTINFO;
232 		cmsg_datap = CMSG_DATA(cmsgp);
233 
234 		/* LINTED */
235 		pktinfop = (struct in6_pktinfo *)cmsg_datap;
236 		/*
237 		 * We don't know if pktinfop->ipi6_addr is aligned properly,
238 		 * therefore let's use bcopy, instead of assignment.
239 		 */
240 		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
241 		    sizeof (struct in6_addr));
242 
243 		/*
244 		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
245 		 */
246 		pktinfop->ipi6_ifindex = if_index;
247 		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
248 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
249 	}
250 
251 	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
252 }
253 
254 /*
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!).
259  */
260 void
261 check_reply6(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
262     ushort_t udp_src_port)
263 {
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"
296 	};
297 	static char *timexceed6[] = {
298 	    "Hop limit exceeded in transit",
299 	    "Fragment reassembly time exceeded"
300 	};
301 	static char *param_prob6[] = {
302 	    "Erroneous header field encountered",
303 	    "Unrecognized next header type encountered",
304 	    "Unrecognized IPv6 option encountered"
305 	};
306 	boolean_t print_newline = _B_FALSE;
307 
308 	/* decompose msghdr into useful pieces */
309 	buf = (uchar_t *)msg->msg_iov->iov_base;
310 	from6 = (struct sockaddr_in6 *)msg->msg_name;
311 
312 	/* LINTED */
313 	intp = (int32_t *)buf;
314 
315 	ping_gettime(msg, &tv);
316 
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));
323 		}
324 		return;
325 	}
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));
330 		}
331 		return;
332 	}
333 	/* LINTED */
334 	icmp6 = (struct icmp6_hdr *)buf;
335 	cc_left = cc - ICMP6_MINLEN;
336 
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",
344 				    cc,
345 				    pr_name((char *)&from6->sin6_addr,
346 				    AF_INET6));
347 			}
348 			return;
349 		}
350 
351 		/*
352 		 * Determine the total length of IPv6 header and extension
353 		 * headers, also the upper layer header (UDP, TCP, ICMP, etc.)
354 		 * following.
355 		 */
356 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
357 
358 		cc_left -= ip6hdr_len;
359 
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",
365 				    cc,
366 				    pr_name((char *)&from6->sin6_addr,
367 				    AF_INET6));
368 			}
369 			return;
370 		}
371 		cc_left -= sizeof (struct udphdr);
372 
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;
381 		}
382 
383 		if (valid_reply) {
384 			/*
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.
388 			 */
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++;
399 				/*
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.
404 				 */
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 {
413 				/*
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.
419 				 */
420 				if (probe_all && !stats) {
421 					valid_reply = _B_FALSE;
422 					/*
423 					 * Only if it's verbose, we get a
424 					 * message regarding this reply,
425 					 * otherwise we are done here.
426 					 */
427 					if (!verbose) {
428 						(void) sigrelse(SIGALRM);
429 						return;
430 					}
431 				}
432 			}
433 		}
434 
435 		if (valid_reply && !stats) {
436 			/*
437 			 * if we are still sending to the same target address,
438 			 * then stop it, because we know it's alive.
439 			 */
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;
444 			}
445 			(void) sigrelse(SIGALRM);
446 
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);
458 				}
459 			}
460 			if (reply_matched_current_target) {
461 				/*
462 				 * Let's get things going again, but now
463 				 * ping will start sending to next target IP
464 				 * address.
465 				 */
466 				send_scheduled_probe();
467 				(void) sigset(SIGALRM, sigalrm_handler);
468 				schedule_sigalrm();
469 			}
470 			return;
471 		} else {
472 			/*
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.
477 			 */
478 			if (!last_reply_from_targetaddr)
479 				(void) sigrelse(SIGALRM);
480 			/* else, we'll release it later */
481 		}
482 
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));
500 			}
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;
508 		}
509 
510 		/*
511 		 * Update and print the stats, if it's a valid reply and
512 		 * contains a timestamp.
513 		 */
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;
529 		}
530 		if (print_newline)
531 			(void) putchar('\n');
532 		/*
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!
537 		 */
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();
544 		}
545 		break;
546 
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));
555 			}
556 			return;
557 		}
558 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
559 
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));
564 
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));
576 			}
577 			Printf(" MTU = %d\n", ntohl(icmp6->icmp6_mtu));
578 		}
579 		break;
580 
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",
587 				    cc,
588 				    pr_name((char *)&from6->sin6_addr,
589 				    AF_INET6));
590 			}
591 			return;
592 		}
593 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
594 
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));
607 			}
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));
619 			}
620 			(void) putchar('\n');
621 		}
622 		break;
623 
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",
630 				    cc,
631 				    pr_name((char *)&from6->sin6_addr,
632 				    AF_INET6));
633 			}
634 			return;
635 		}
636 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
637 
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));
650 			}
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));
656 			}
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));
668 			}
669 			(void) putchar('\n');
670 		}
671 		break;
672 
673 	case ICMP6_ECHO_REQUEST:
674 		return;
675 
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;
684 		}
685 
686 		if (valid_reply) {
687 			/*
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.
691 			 */
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++;
702 				/*
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.
707 				 */
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 {
717 				/*
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.
723 				 */
724 				if (probe_all && !stats) {
725 					valid_reply = _B_FALSE;
726 					/*
727 					 * Only if it's verbose, we get a
728 					 * message regarding this reply,
729 					 * otherwise we are done here.
730 					 */
731 					if (!verbose) {
732 						(void) sigrelse(SIGALRM);
733 						return;
734 					}
735 				}
736 			}
737 		}
738 
739 		if (!stats && valid_reply) {
740 			/*
741 			 * if we are still sending to the same target address,
742 			 * then stop it, because we know it's alive.
743 			 */
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;
748 			}
749 			(void) sigrelse(SIGALRM);
750 
751 			if (!probe_all) {
752 				Printf("%s is alive\n", targethost);
753 			} else {
754 				/*
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.
759 				 */
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));
770 				}
771 
772 				if (nflag) {
773 					Printf("%s is alive\n", tmp_buf);
774 				} else {
775 					Printf("%s (%s) is alive\n",
776 					    targethost, tmp_buf);
777 				}
778 			}
779 			if (reply_matched_current_target) {
780 				/*
781 				 * Let's get things going again, but now
782 				 * ping will start sending to next target IP
783 				 * address.
784 				 */
785 				send_scheduled_probe();
786 				(void) sigset(SIGALRM, sigalrm_handler);
787 				schedule_sigalrm();
788 			}
789 			return;
790 		} else {
791 			/*
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.
796 			 */
797 			if (!last_reply_from_targetaddr)
798 				(void) sigrelse(SIGALRM);
799 			/* else, we'll release it later */
800 		}
801 
802 		/*
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.
806 		 */
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));
814 		}
815 		Printf("icmp_seq=%d. ", ntohs(icmp6->icmp6_seq));
816 
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;
830 		}
831 		(void) putchar('\n');
832 		/*
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!
837 		 */
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();
844 		}
845 		break;
846 
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;
855 
856 	case ND_REDIRECT:
857 		nd_rdrct = (nd_redirect_t *)icmp6;
858 
859 		if (cc_left < sizeof (nd_redirect_t) - ICMP6_MINLEN) {
860 			if (verbose) {
861 				Printf("packet too short (%d bytes) from %s\n",
862 				    cc,
863 				    pr_name((char *)&from6->sin6_addr,
864 				    AF_INET6));
865 			}
866 			return;
867 		}
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));
872 
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));
877 		}
878 		break;
879 
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++);
890 			}
891 		}
892 		break;
893 	}
894 
895 	/*
896 	 * If it's verbose mode and we recv'd ancillary data, print extension
897 	 * headers.
898 	 */
899 	if (verbose && msg->msg_controllen > 0)
900 		pr_ext_headers(msg);
901 }
902 
903 /*
904  * Convert an ICMP6 "type" field to a printable string.
905  */
906 static char *
907 pr_type6(uchar_t icmp6_type)
908 {
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"},
924 	};
925 	int i;
926 
927 	for (i = 0; i < A_CNT(ttab6); i++) {
928 		if (ttab6[i].type == icmp6_type)
929 			return (ttab6[i].message);
930 
931 	}
932 
933 	return ("OUT-OF-RANGE");
934 }
935 
936 /*
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.
941  */
942 static int
943 IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
944 {
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;
954 
955 	length = sizeof (ip6_t);
956 
957 	whereptr = ((uint8_t *)&ip6h[1]); 	/* point to next hdr */
958 	endptr = ((uint8_t *)ip6h) + pkt_len;
959 
960 	nexthdr = ip6h->ip6_nxt;
961 	*last_hdr_rtrn = IPPROTO_NONE;
962 
963 	if (whereptr >= endptr)
964 		return (length);
965 
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;
977 
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;
986 
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;
995 
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;
1004 
1005 		case IPPROTO_NONE:
1006 		default:
1007 			return (length);
1008 		}
1009 		whereptr = (uint8_t *)ip6h + length;
1010 	}
1011 	*last_hdr_rtrn = nexthdr;
1012 
1013 	return (length);
1014 }
1015 
1016 /*
1017  * Print extension headers
1018  */
1019 static void
1020 pr_ext_headers(struct msghdr *msg)
1021 {
1022 	struct cmsghdr *cmsg;
1023 
1024 	Printf("  IPv6 extension headers: ");
1025 
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;
1033 
1034 			case IPV6_DSTOPTS:
1035 				Printf(" <destination options (after routing"
1036 				    "header)>");
1037 				break;
1038 
1039 			case IPV6_RTHDRDSTOPTS:
1040 				Printf(" <destination options (before routing"
1041 				    "header)>");
1042 				break;
1043 
1044 			case IPV6_RTHDR:
1045 				pr_rthdr((uchar_t *)CMSG_DATA(cmsg));
1046 				break;
1047 
1048 			default:
1049 				Printf(" <option type %d>", cmsg->cmsg_type);
1050 				break;
1051 			}
1052 		}
1053 	}
1054 	(void) putchar('\n');
1055 }
1056 
1057 /*
1058  * Print the routing header 0 information
1059  */
1060 static void
1061 pr_rthdr(uchar_t *buf)
1062 {
1063 	ip6_rthdr_t *rthdr;
1064 	ip6_rthdr0_t *rthdr0;
1065 	struct in6_addr *gw_addr;
1066 	int i, num_addr;
1067 
1068 	rthdr = (ip6_rthdr_t *)buf;
1069 	Printf(" <type %d routing header, segleft %u> ",
1070 	    rthdr->ip6r_type, rthdr->ip6r_segleft);
1071 
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;
1077 
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(",  ");
1085 		}
1086 	}
1087 }
1088