xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c (revision 384e273179b3490ea168990d96f90731b5bc180c)
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  * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>
33  */
34 
35 /*
36  * Portions of this source code were derived from Berkeley 4.3 BSD
37  * under license from the Regents of the University of California.
38  */
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <signal.h>
47 
48 #include <sys/time.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <sys/stropts.h>
53 #include <sys/file.h>
54 #include <arpa/inet.h>
55 #include <net/if.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/in.h>
58 #include <netinet/ip.h>
59 #include <netinet/ip_icmp.h>
60 #include <netinet/udp.h>
61 #include <netdb.h>
62 #include <stdlib.h>
63 
64 #include <libinetutil.h>
65 #include "ping.h"
66 
67 
68 /*
69  * IPv4 source routing option.
70  * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
71  * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
72  */
73 struct ip_sourceroute {
74 	uint8_t ipsr_code;
75 	uint8_t ipsr_len;
76 	uint8_t ipsr_ptr;
77 	/* up to 9 IPv4 addresses */
78 	uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
79 };
80 
81 static void pr_options(uchar_t *, int);
82 static void pr_rropt(uchar_t *, int, boolean_t);
83 static void pr_tsopt(uchar_t *, int);
84 static char *pr_type(int);
85 
86 /*
87  * Set IPv4 options
88  */
89 void
set_IPv4_options(int sock,union any_in_addr * gw_IP_list,int gw_count,struct in_addr * src,struct in_addr * dst)90 set_IPv4_options(int sock, union any_in_addr *gw_IP_list, int gw_count,
91     struct in_addr *src, struct in_addr *dst)
92 {
93 	int req_size;
94 	char srr[ROUTE_SIZE + 1];
95 	char *bufp;
96 	int optsize = ROUTE_SIZE;
97 	struct ip_sourceroute *srp;
98 	struct ip_timestamp *tsp;
99 	int i;
100 
101 	if (rr_option || ts_option || gw_count > 0) {
102 		bzero(srr, sizeof (srr));
103 		bufp = srr;
104 
105 		if (gw_count > 0) {
106 			/* 3 = 1 (code) + 1 (len) + 1 (ptr) of src route opt. */
107 			req_size = 3 + (sizeof (struct in_addr)) * gw_count;
108 
109 			if (optsize < req_size) {
110 				Fprintf(stderr, "%s: too many IPv4 gateways\n",
111 				    progname);
112 				exit(EXIT_FAILURE);
113 			}
114 
115 			srp = (struct ip_sourceroute *)bufp;
116 			srp->ipsr_code = strict ? IPOPT_SSRR : IPOPT_LSRR;
117 			srp->ipsr_len = req_size;
118 			srp->ipsr_ptr = IPOPT_MINOFF;
119 
120 			for (i = 0; i < gw_count; i++) {
121 				bcopy((char *)&gw_IP_list[i].addr,
122 				    &srp->ipsr_addrs[i],
123 				    sizeof (struct in_addr));
124 			}
125 			optsize -= srp->ipsr_len;
126 			bufp += srp->ipsr_len;
127 		}
128 		/* do we send a timestamp option? */
129 		if (ts_option) {
130 			if (optsize < IPOPT_MINOFF) {
131 				Fprintf(stderr,
132 				    "%s: no room for timestamp option\n",
133 				    progname);
134 				exit(EXIT_FAILURE);
135 			}
136 			/* LINTED */
137 			tsp = (struct ip_timestamp *)bufp;
138 			tsp->ipt_code = IPOPT_TS;
139 			tsp->ipt_len = optsize;
140 			tsp->ipt_ptr = IPOPT_MINOFF + 1;
141 			tsp->ipt_flg = ts_flag & 0x0f;
142 
143 			if (tsp->ipt_flg > IPOPT_TS_TSANDADDR) {
144 				req_size = IPOPT_MINOFF +
145 				    2 * sizeof (struct ipt_ta);
146 				/*
147 				 * Note: BSD/4.X is broken in their check so we
148 				 * have to  bump up this number by at least one.
149 				 */
150 				req_size++;
151 
152 				if (optsize < req_size) {
153 					Fprintf(stderr, "%s: no room for "
154 					    "timestamp option\n", progname);
155 					exit(EXIT_FAILURE);
156 				}
157 
158 				bcopy((char *)dst,
159 				    &tsp->ipt_timestamp.ipt_ta[0].ipt_addr,
160 				    sizeof (struct in_addr));
161 
162 				bcopy((char *)src,
163 				    &tsp->ipt_timestamp.ipt_ta[1].ipt_addr,
164 				    sizeof (struct in_addr));
165 				tsp->ipt_len = req_size;
166 
167 			}
168 			optsize -= tsp->ipt_len;
169 			bufp += tsp->ipt_len;
170 		}
171 		/* do we send a record route option? */
172 		if (rr_option) {
173 			if (optsize < IPOPT_MINOFF) {
174 				Fprintf(stderr,
175 				    "%s: no room for record route option\n",
176 				    progname);
177 				exit(EXIT_FAILURE);
178 
179 			}
180 			/*
181 			 * Format of record route option is same as source
182 			 * route option.
183 			 */
184 			srp = (struct ip_sourceroute *)bufp;
185 			srp->ipsr_code = IPOPT_RR;
186 			srp->ipsr_len = optsize;
187 			srp->ipsr_ptr = IPOPT_MINOFF;
188 
189 			optsize -= srp->ipsr_len;
190 			bufp += srp->ipsr_len;
191 		}
192 		optsize = bufp - srr;
193 		/* Round up to 4 byte boundary */
194 		if (optsize & 0x3)
195 			optsize = (optsize & ~0x3) + 4;
196 		if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, srr, optsize) <
197 		    0) {
198 			Fprintf(stderr, "%s: setsockopt IP_OPTIONS %s\n",
199 			    progname, strerror(errno));
200 			exit(EXIT_FAILURE);
201 		}
202 	}
203 }
204 
205 /*
206  * Check out the packet to see if it came from us.  This logic is necessary
207  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
208  * which arrive ('tis only fair).  This permits multiple copies of this
209  * program to be run without having intermingled output (or statistics!).
210  */
211 void
check_reply(struct addrinfo * ai_dst,struct msghdr * msg,int cc,ushort_t udp_src_port)212 check_reply(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
213     ushort_t udp_src_port)
214 {
215 	struct ip *ip;
216 	struct icmp *icp;
217 	struct udphdr *up;
218 	union any_in_addr dst_addr;
219 	uchar_t *buf;
220 	int32_t *intp;
221 	struct sockaddr_in *from;
222 	struct timeval *tp;
223 	struct timeval tv;
224 	int hlen, hlen1;
225 	int64_t triptime;
226 	boolean_t valid_reply = _B_FALSE;
227 	boolean_t reply_matched_current_target = _B_FALSE; /* Is the source */
228 						/* address of this reply same */
229 						/* as where we're sending */
230 						/* currently? */
231 	boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
232 						/* probe all with npackets>0 */
233 						/* and we received reply for */
234 						/* the last probe sent to */
235 						/* targetaddr */
236 	int cc_left;
237 	char tmp_buf[INET6_ADDRSTRLEN];
238 	static char *unreach[] = {
239 	    "Net Unreachable",
240 	    "Host Unreachable",
241 	    "Protocol Unreachable",
242 	    "Port Unreachable",
243 	    "Fragmentation needed and DF set",
244 	    "Source Route Failed",
245 	    /* The following are from RFC1700 */
246 	    "Net Unknown",
247 	    "Host Unknown",
248 	    "Source Host Isolated",
249 	    "Dest Net Prohibited",
250 	    "Dest Host Prohibited",
251 	    "Net Unreachable for TOS",
252 	    "Host Unreachable for TOS",
253 	    "Communication Administratively Prohibited",
254 	    "Host Precedence Violation",
255 	    "Precedence Cutoff in Effect"
256 	};
257 	static char *redirect[] = {
258 	    "Net",
259 	    "Host",
260 	    "TOS Net",
261 	    "TOS Host"
262 	};
263 	static char *timexceed[] = {
264 	    "Time exceeded in transit",
265 	    "Time exceeded during reassembly"
266 	};
267 	boolean_t print_newline = _B_FALSE;
268 	int i;
269 
270 	/* decompose msghdr into useful pieces */
271 	buf = (uchar_t *)msg->msg_iov->iov_base;
272 	from = (struct sockaddr_in *)msg->msg_name;
273 
274 	/* LINTED */
275 	intp = (int32_t *)buf;
276 
277 	ping_gettime(msg, &tv);
278 
279 	/* LINTED */
280 	ip = (struct ip *)buf;
281 	hlen = ip->ip_hl << 2;
282 
283 	if ((cc < sizeof (struct ip)) || (cc < hlen + ICMP_MINLEN)) {
284 		if (verbose) {
285 			Printf("packet too short (%d bytes) from %s\n", cc,
286 			    pr_name4(from));
287 		}
288 		return;
289 	}
290 
291 	cc -= hlen;
292 	/* LINTED */
293 	icp = (struct icmp *)(buf + hlen);
294 
295 	if (ip->ip_p == 0) {
296 		/*
297 		 * Assume that we are running on a pre-4.3BSD system
298 		 * such as SunOS before 4.0
299 		 */
300 		/* LINTED */
301 		icp = (struct icmp *)buf;
302 	}
303 	cc_left = cc - ICMP_MINLEN;
304 
305 	switch (icp->icmp_type) {
306 	case ICMP_UNREACH:
307 		ip = &icp->icmp_ip;
308 		hlen1 = ip->ip_hl << 2;
309 
310 		/* check if we have enough of the packet to work on */
311 		if ((cc_left < sizeof (struct ip)) ||
312 		    (cc_left < hlen1 + sizeof (struct udphdr))) {
313 			if (verbose) {
314 				Printf("packet too short (%d bytes) from %s\n",
315 				    cc, pr_name4(from));
316 			}
317 			return;
318 		}
319 
320 		/* get the UDP packet */
321 		cc_left -= hlen1 + sizeof (struct udphdr);
322 		/* LINTED */
323 		up = (struct udphdr *)((uchar_t *)ip + hlen1);
324 
325 		/* check to see if this is what we sent */
326 		if (icp->icmp_code == ICMP_UNREACH_PORT &&
327 		    ip->ip_p == IPPROTO_UDP &&
328 		    udp_src_port == up->uh_sport &&
329 		    use_udp) {
330 			valid_reply = _B_TRUE;
331 		} else {
332 			valid_reply = _B_FALSE;
333 		}
334 
335 		if (valid_reply) {
336 			/*
337 			 * For this valid reply, if we are still sending to
338 			 * this target IP address, we'd like to do some
339 			 * updates to targetaddr, so hold SIGALRMs.
340 			 */
341 			(void) sighold(SIGALRM);
342 			is_alive = _B_TRUE;
343 			nreceived++;
344 			reply_matched_current_target =
345 			    seq_match(current_targetaddr->starting_seq_num,
346 			    current_targetaddr->num_sent,
347 			    ntohs(up->uh_dport));
348 			if (reply_matched_current_target) {
349 				current_targetaddr->got_reply = _B_TRUE;
350 				nreceived_last_target++;
351 				/*
352 				 * Determine if stats, probe-all, and
353 				 * npackets != 0, and this is the reply for
354 				 * the last probe we sent to current target
355 				 * address.
356 				 */
357 				if (stats && probe_all && npackets > 0 &&
358 				    ((current_targetaddr->starting_seq_num +
359 				    current_targetaddr->num_probes - 1) %
360 				    (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
361 				    (current_targetaddr->num_probes ==
362 				    current_targetaddr->num_sent))
363 					last_reply_from_targetaddr = _B_TRUE;
364 			} else {
365 				/*
366 				 * If it's just probe_all and we just received
367 				 * a reply from a target address we were
368 				 * probing and had timed out (now we are probing
369 				 * some other target address), we ignore
370 				 * this reply.
371 				 */
372 				if (probe_all && !stats) {
373 					valid_reply = _B_FALSE;
374 					/*
375 					 * Only if it's verbose, we get a
376 					 * message regarding this reply,
377 					 * otherwise we are done here.
378 					 */
379 					if (!verbose) {
380 						(void) sigrelse(SIGALRM);
381 						return;
382 					}
383 				}
384 			}
385 		}
386 
387 		/* stats mode doesn't print 'alive' messages */
388 		if (valid_reply && !stats) {
389 			/*
390 			 * if we are still sending to the same target address,
391 			 * then stop it, because we know it's alive.
392 			 */
393 			if (reply_matched_current_target) {
394 				(void) alarm(0);	/* cancel alarm */
395 				(void) sigset(SIGALRM, SIG_IGN);
396 				current_targetaddr->probing_done = _B_TRUE;
397 			}
398 			(void) sigrelse(SIGALRM);
399 
400 			if (!probe_all) {
401 				Printf("%s is alive\n", targethost);
402 			} else {
403 				(void) inet_ntop(AF_INET, (void *)&ip->ip_dst,
404 				    tmp_buf, sizeof (tmp_buf));
405 
406 				if (nflag) {
407 					Printf("%s is alive\n", tmp_buf);
408 				} else {
409 					Printf("%s (%s) is alive\n",
410 					    targethost, tmp_buf);
411 				}
412 			}
413 			if (reply_matched_current_target) {
414 				/*
415 				 * Let's get things going again, but now
416 				 * ping will start sending to next target IP
417 				 * address.
418 				 */
419 				send_scheduled_probe();
420 				(void) sigset(SIGALRM, sigalrm_handler);
421 				schedule_sigalrm();
422 			}
423 			return;
424 		} else {
425 			/*
426 			 * If we are not moving to next targetaddr, let's
427 			 * release the SIGALRM now. We don't want to stall in
428 			 * the middle of probing a targetaddr if the pr_name()
429 			 * call (see below) takes longer.
430 			 */
431 			if (!last_reply_from_targetaddr)
432 				(void) sigrelse(SIGALRM);
433 			/* else, we'll release it later */
434 		}
435 
436 		dst_addr.addr = ip->ip_dst;
437 		if (valid_reply) {
438 			Printf("%d bytes from %s: ", cc, pr_name4(from));
439 			Printf("udp_port=%d. ", ntohs(up->uh_dport));
440 			print_newline = _B_TRUE;
441 		} else if (is_a_target(ai_dst, &dst_addr) || verbose) {
442 			if (icp->icmp_code >= A_CNT(unreach)) {
443 				Printf("ICMP %d Unreachable from gateway %s\n",
444 				    icp->icmp_code, pr_name4(from));
445 			} else {
446 				Printf("ICMP %s from gateway %s\n",
447 				    unreach[icp->icmp_code], pr_name4(from));
448 			}
449 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
450 			    pr_name((char *)&ip->ip_src, AF_INET));
451 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
452 			if (ip->ip_p == IPPROTO_TCP ||
453 			    ip->ip_p == IPPROTO_UDP) {
454 				Printf(" port %d ", ntohs(up->uh_dport));
455 			}
456 			print_newline = _B_TRUE;
457 		}
458 
459 		/* if we are timing and the reply has a timeval */
460 		if (valid_reply && datalen >= sizeof (struct timeval) &&
461 		    cc_left >= sizeof (struct timeval)) {
462 			/* LINTED */
463 			tp = (struct timeval *)((char *)up +
464 			    sizeof (struct udphdr));
465 			(void) tvsub(&tv, tp);
466 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
467 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
468 			tsum += triptime;
469 			tsum2 += triptime*triptime;
470 			if (triptime < tmin)
471 				tmin = triptime;
472 			if (triptime > tmax)
473 				tmax = triptime;
474 			print_newline = _B_TRUE;
475 		}
476 		if (print_newline)
477 			(void) putchar('\n');
478 		/*
479 		 * If it's stats, probe-all, npackets > 0, and we received reply
480 		 * for the last probe sent to this target address, then we
481 		 * don't need to wait anymore, let's move on to next target
482 		 * address, now!
483 		 */
484 		if (last_reply_from_targetaddr) {
485 			(void) alarm(0);	/* cancel alarm */
486 			current_targetaddr->probing_done = _B_TRUE;
487 			(void) sigrelse(SIGALRM);
488 			send_scheduled_probe();
489 			schedule_sigalrm();
490 		}
491 		break;
492 
493 	case ICMP_REDIRECT:
494 		if (cc_left < sizeof (struct ip)) {
495 			if (verbose) {
496 				Printf("packet too short (%d bytes) from %s\n",
497 				    cc, pr_name4(from));
498 			}
499 			return;
500 		}
501 
502 		ip = &icp->icmp_ip;
503 		dst_addr.addr = ip->ip_dst;
504 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
505 			if (icp->icmp_code >= A_CNT(redirect)) {
506 				Printf("ICMP %d redirect from gateway %s\n",
507 				    icp->icmp_code, pr_name4(from));
508 			} else {
509 				Printf("ICMP %s redirect from gateway %s\n",
510 				    redirect[icp->icmp_code], pr_name4(from));
511 			}
512 			Printf(" to %s",
513 			    pr_name((char *)&icp->icmp_gwaddr, AF_INET));
514 			Printf(" for %s\n",
515 			    pr_name((char *)&ip->ip_dst, AF_INET));
516 		}
517 		break;
518 
519 	case ICMP_ECHOREPLY:
520 		if (ntohs(icp->icmp_id) == ident) {
521 			if (!use_udp && !use_icmp_ts)
522 				valid_reply = _B_TRUE;
523 			else
524 				valid_reply = _B_FALSE;
525 		} else {
526 			return;
527 		}
528 
529 		if (valid_reply) {
530 			/*
531 			 * For this valid reply, if we are still sending to
532 			 * this target IP address, we'd like to do some
533 			 * updates to targetaddr, so hold SIGALRMs.
534 			 */
535 			(void) sighold(SIGALRM);
536 			is_alive = _B_TRUE;
537 			nreceived++;
538 			reply_matched_current_target =
539 			    seq_match(current_targetaddr->starting_seq_num,
540 			    current_targetaddr->num_sent,
541 			    ntohs(icp->icmp_seq));
542 			if (reply_matched_current_target) {
543 				current_targetaddr->got_reply = _B_TRUE;
544 				nreceived_last_target++;
545 				/*
546 				 * Determine if stats, probe-all, and
547 				 * npackets != 0, and this is the reply for
548 				 * the last probe we sent to current target
549 				 * address.
550 				 */
551 				if (stats && probe_all && npackets > 0 &&
552 				    ((current_targetaddr->starting_seq_num +
553 				    current_targetaddr->num_probes - 1) %
554 				    (MAX_ICMP_SEQ + 1) ==
555 				    ntohs(icp->icmp_seq)) &&
556 				    (current_targetaddr->num_probes ==
557 				    current_targetaddr->num_sent))
558 					last_reply_from_targetaddr = _B_TRUE;
559 			} else {
560 				/*
561 				 * If it's just probe_all and we just received
562 				 * a reply from a target address we were
563 				 * probing and had timed out (now we are probing
564 				 * some other target address), we ignore
565 				 * this reply.
566 				 */
567 				if (probe_all && !stats) {
568 					valid_reply = _B_FALSE;
569 					/*
570 					 * Only if it's verbose, we get a
571 					 * message regarding this reply,
572 					 * otherwise we are done here.
573 					 */
574 					if (!verbose) {
575 						(void) sigrelse(SIGALRM);
576 						return;
577 					}
578 				}
579 			}
580 		}
581 
582 		if (!stats && valid_reply) {
583 			/*
584 			 * if we are still sending to the same target address,
585 			 * then stop it, because we know it's alive.
586 			 */
587 			if (reply_matched_current_target) {
588 				(void) alarm(0);	/* cancel alarm */
589 				(void) sigset(SIGALRM, SIG_IGN);
590 				current_targetaddr->probing_done = _B_TRUE;
591 			}
592 			(void) sigrelse(SIGALRM);
593 
594 			if (!probe_all) {
595 				Printf("%s is alive\n", targethost);
596 			} else {
597 				/*
598 				 * If we are using send_reply, the real
599 				 * target address is not the src address of the
600 				 * replies. Use icmp_seq to find out where this
601 				 * probe was sent to.
602 				 */
603 				if (send_reply) {
604 					(void) find_dstaddr(
605 					    ntohs(icp->icmp_seq), &dst_addr);
606 					(void) inet_ntop(AF_INET,
607 					    (void *)&dst_addr.addr,
608 					    tmp_buf, sizeof (tmp_buf));
609 				} else {
610 					(void) inet_ntop(AF_INET,
611 					    (void *)&from->sin_addr,
612 					    tmp_buf, sizeof (tmp_buf));
613 				}
614 				if (nflag) {
615 					Printf("%s is alive\n", tmp_buf);
616 				} else {
617 					Printf("%s (%s) is alive\n",
618 					    targethost, tmp_buf);
619 				}
620 			}
621 			if (reply_matched_current_target) {
622 				/*
623 				 * Let's get things going again, but now
624 				 * ping will start sending to next target IP
625 				 * address.
626 				 */
627 				send_scheduled_probe();
628 				(void) sigset(SIGALRM, sigalrm_handler);
629 				schedule_sigalrm();
630 			}
631 			return;
632 		} else {
633 			/*
634 			 * If we are not moving to next targetaddr, let's
635 			 * release the SIGALRM now. We don't want to stall in
636 			 * the middle of probing a targetaddr if the pr_name()
637 			 * call (see below) takes longer.
638 			 */
639 			if (!last_reply_from_targetaddr)
640 				(void) sigrelse(SIGALRM);
641 			/* else, we'll release it later */
642 		}
643 		/*
644 		 * If we are using send_reply, the real target address is
645 		 * not the src address of the replies. Use icmp_seq to find out
646 		 * where this probe was sent to.
647 		 */
648 		if (send_reply) {
649 			(void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
650 			Printf("%d bytes from %s: ", cc,
651 			    pr_name((char *)&dst_addr.addr,  AF_INET));
652 		} else {
653 			Printf("%d bytes from %s: ", cc, pr_name4(from));
654 		}
655 		Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
656 
657 		if (valid_reply && datalen >= sizeof (struct timeval) &&
658 		    cc_left >= sizeof (struct timeval)) {
659 			/* LINTED */
660 			tp = (struct timeval *)&icp->icmp_data[0];
661 			(void) tvsub(&tv, tp);
662 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
663 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
664 			tsum += triptime;
665 			tsum2 += triptime*triptime;
666 			if (triptime < tmin)
667 				tmin = triptime;
668 			if (triptime > tmax)
669 				tmax = triptime;
670 		}
671 		(void) putchar('\n');
672 
673 		/*
674 		 * If it's stats, probe-all, npackets > 0, and we received reply
675 		 * for the last probe sent to this target address, then we
676 		 * don't need to wait anymore, let's move on to next target
677 		 * address, now!
678 		 */
679 		if (last_reply_from_targetaddr) {
680 			(void) alarm(0);	/* cancel alarm */
681 			current_targetaddr->probing_done = _B_TRUE;
682 			(void) sigrelse(SIGALRM);
683 			send_scheduled_probe();
684 			schedule_sigalrm();
685 		}
686 		break;
687 
688 	case ICMP_SOURCEQUENCH:
689 		if (cc_left < sizeof (struct ip)) {
690 			if (verbose) {
691 				Printf("packet too short (%d bytes) from %s\n",
692 				    cc, pr_name4(from));
693 			}
694 			return;
695 		}
696 		ip = &icp->icmp_ip;
697 		hlen1 = ip->ip_hl << 2;
698 		dst_addr.addr = ip->ip_dst;
699 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
700 			Printf("ICMP Source Quench from %s\n", pr_name4(from));
701 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
702 			    pr_name((char *)&ip->ip_src, AF_INET));
703 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
704 
705 			/*
706 			 * if it's a UDP or TCP packet, we need at least first
707 			 * 4 bytes of it to see the src/dst ports
708 			 */
709 			if ((ip->ip_p == IPPROTO_TCP ||
710 			    ip->ip_p == IPPROTO_UDP) &&
711 			    (cc_left >= hlen1 + 4)) {
712 				/* LINTED */
713 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
714 				Printf(" port %d", ntohs(up->uh_dport));
715 			}
716 			(void) putchar('\n');
717 		}
718 		break;
719 
720 	case ICMP_PARAMPROB:
721 		if (cc_left < sizeof (struct ip)) {
722 			if (verbose) {
723 				Printf("packet too short (%d bytes) from %s\n",
724 				    cc, pr_name4(from));
725 			}
726 			return;
727 		}
728 		ip = &icp->icmp_ip;
729 		hlen1 = ip->ip_hl << 2;
730 		dst_addr.addr = ip->ip_dst;
731 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
732 			switch (icp->icmp_code) {
733 			case ICMP_PARAMPROB_OPTABSENT:
734 				Printf("ICMP Missing a Required Option "
735 				    "parameter problem from %s\n",
736 				    pr_name4(from));
737 				Printf(" option type = %d", icp->icmp_pptr);
738 				break;
739 			case ICMP_PARAMPROB_BADLENGTH:
740 				Printf("ICMP Bad Length parameter problem "
741 				    "from %s\n", pr_name4(from));
742 				Printf(" in byte %d", icp->icmp_pptr);
743 				if (icp->icmp_pptr <= hlen1) {
744 					Printf(" (value 0x%x)",
745 					    *((char *)ip + icp->icmp_pptr));
746 				}
747 				break;
748 			case 0:
749 			default:
750 				Printf("ICMP Parameter Problem from %s\n",
751 				    pr_name4(from));
752 				Printf(" in byte %d", icp->icmp_pptr);
753 				if (icp->icmp_pptr <= hlen1) {
754 					Printf(" (value 0x%x)",
755 					    *((char *)ip + icp->icmp_pptr));
756 				}
757 				break;
758 			}
759 
760 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
761 			    pr_name((char *)&ip->ip_src, AF_INET));
762 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
763 
764 			/*
765 			 * if it's a UDP or TCP packet, we need at least first
766 			 * 4 bytes of it to see the src/dst ports
767 			 */
768 			if ((ip->ip_p == IPPROTO_TCP ||
769 			    ip->ip_p == IPPROTO_UDP) &&
770 			    (cc_left >= hlen1 + 4)) {
771 				/* LINTED */
772 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
773 				Printf(" port %d", ntohs(up->uh_dport));
774 			}
775 			(void) putchar('\n');
776 		}
777 		break;
778 
779 	case ICMP_TIMXCEED:
780 		if (cc_left < sizeof (struct ip)) {
781 			if (verbose) {
782 				Printf("packet too short (%d bytes) from %s\n",
783 				    cc, pr_name4(from));
784 			}
785 			return;
786 		}
787 		ip = &icp->icmp_ip;
788 		hlen1 = ip->ip_hl << 2;
789 		dst_addr.addr = ip->ip_dst;
790 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
791 			if (icp->icmp_code >= A_CNT(timexceed)) {
792 				Printf("ICMP %d time exceeded from %s\n",
793 				    icp->icmp_code, pr_name4(from));
794 			} else {
795 				Printf("ICMP %s from %s\n",
796 				    timexceed[icp->icmp_code],
797 				    pr_name4(from));
798 			}
799 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
800 			    pr_name((char *)&ip->ip_src, AF_INET));
801 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
802 			if ((ip->ip_p == IPPROTO_TCP ||
803 			    ip->ip_p == IPPROTO_UDP) &&
804 			    (cc_left >= hlen1 + 4)) {
805 				/* LINTED */
806 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
807 				Printf(" port %d", ntohs(up->uh_dport));
808 			}
809 			(void) putchar('\n');
810 		}
811 		break;
812 
813 	case ICMP_TSTAMPREPLY:
814 		/* the packet should have enough space to store timestamps */
815 		if (cc_left < sizeof (struct id_ts)) {
816 			if (verbose) {
817 				Printf("packet too short (%d bytes) from %s\n",
818 				    cc, pr_name4(from));
819 			}
820 			return;
821 		}
822 
823 		if (ntohs(icp->icmp_id) == ident) {
824 			if (use_icmp_ts)
825 				valid_reply = _B_TRUE;
826 			else
827 				valid_reply = _B_FALSE;
828 		} else {
829 			return;
830 		}
831 
832 		if (valid_reply) {
833 			/*
834 			 * For this valid reply, if we are still sending to
835 			 * this target IP address, we'd like to do some
836 			 * updates to targetaddr, so hold SIGALRMs.
837 			 */
838 			(void) sighold(SIGALRM);
839 			is_alive = _B_TRUE;
840 			nreceived++;
841 			reply_matched_current_target =
842 			    seq_match(current_targetaddr->starting_seq_num,
843 			    current_targetaddr->num_sent,
844 			    ntohs(icp->icmp_seq));
845 			if (reply_matched_current_target) {
846 				current_targetaddr->got_reply = _B_TRUE;
847 				nreceived_last_target++;
848 				/*
849 				 * Determine if stats, probe-all, and
850 				 * npackets != 0, and this is the reply for
851 				 * the last probe we sent to current target
852 				 * address.
853 				 */
854 				if (stats && probe_all && npackets > 0 &&
855 				    ((current_targetaddr->starting_seq_num +
856 				    current_targetaddr->num_probes - 1) %
857 				    (MAX_ICMP_SEQ + 1) ==
858 				    ntohs(icp->icmp_seq)) &&
859 				    (current_targetaddr->num_probes ==
860 				    current_targetaddr->num_sent))
861 					last_reply_from_targetaddr = _B_TRUE;
862 			} else {
863 				/*
864 				 * If it's just probe_all and we just received
865 				 * a reply from a target address we were
866 				 * probing and had timed out (now we are probing
867 				 * some other target address), we ignore
868 				 * this reply.
869 				 */
870 				if (probe_all && !stats) {
871 					valid_reply = _B_FALSE;
872 					/*
873 					 * Only if it's verbose, we get a
874 					 * message regarding this reply,
875 					 * otherwise we are done here.
876 					 */
877 					if (!verbose) {
878 						(void) sigrelse(SIGALRM);
879 						return;
880 					}
881 				}
882 			}
883 		}
884 
885 		if (!stats && valid_reply) {
886 			/*
887 			 * if we are still sending to the same target address,
888 			 * then stop it, because we know it's alive.
889 			 */
890 			if (reply_matched_current_target) {
891 				(void) alarm(0);	/* cancel alarm */
892 				(void) sigset(SIGALRM, SIG_IGN);
893 				current_targetaddr->probing_done = _B_TRUE;
894 			}
895 			(void) sigrelse(SIGALRM);
896 
897 			if (!probe_all) {
898 				Printf("%s is alive\n", targethost);
899 			} else {
900 				/*
901 				 * If we are using send_reply, the real
902 				 * target address is not the src address of the
903 				 * replies. Use icmp_seq to find out where this
904 				 * probe was sent to.
905 				 */
906 				if (send_reply) {
907 					(void) find_dstaddr(
908 					    ntohs(icp->icmp_seq), &dst_addr);
909 					(void) inet_ntop(AF_INET,
910 					    (void *)&dst_addr.addr,
911 					    tmp_buf, sizeof (tmp_buf));
912 				} else {
913 					(void) inet_ntop(AF_INET,
914 					    (void *)&from->sin_addr,
915 					    tmp_buf, sizeof (tmp_buf));
916 				}
917 				if (nflag) {
918 					Printf("%s is alive\n", tmp_buf);
919 				} else {
920 					Printf("%s (%s) is alive\n",
921 					    targethost, tmp_buf);
922 				}
923 			}
924 			if (reply_matched_current_target) {
925 				/*
926 				 * Let's get things going again, but now
927 				 * ping will start sending to next target IP
928 				 * address.
929 				 */
930 				send_scheduled_probe();
931 				(void) sigset(SIGALRM, sigalrm_handler);
932 				schedule_sigalrm();
933 			}
934 			return;
935 		} else {
936 			/*
937 			 * If we are not moving to next targetaddr, let's
938 			 * release the SIGALRM now. We don't want to stall in
939 			 * the middle of probing a targetaddr if the pr_name()
940 			 * call (see below) takes longer.
941 			 */
942 			if (!last_reply_from_targetaddr)
943 				(void) sigrelse(SIGALRM);
944 			/* else, we'll release it later */
945 		}
946 
947 		/*
948 		 * If we are using send_reply, the real target address is
949 		 * not the src address of the replies. Use icmp_seq to find out
950 		 * where this probe was sent to.
951 		 */
952 		if (send_reply) {
953 			(void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
954 			Printf("%d bytes from %s: ", cc,
955 			    pr_name((char *)&dst_addr.addr,  AF_INET));
956 		} else {
957 			Printf("%d bytes from %s: ", cc, pr_name4(from));
958 		}
959 		Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
960 		Printf("orig = %lu, recv = %lu, xmit = %lu ",
961 		    (ulong_t)ntohl(icp->icmp_otime),
962 		    (ulong_t)ntohl(icp->icmp_rtime),
963 		    (ulong_t)ntohl(icp->icmp_ttime));
964 
965 		if (valid_reply) {
966 			/*
967 			 * icp->icmp_otime is the time passed since midnight.
968 			 * Therefore we need to adjust tv value, which is
969 			 * the time passed since Jan 1, 1970.
970 			 */
971 			triptime = (tv.tv_sec % (24LL * 60 * 60)) * MILLISEC +
972 			    (tv.tv_usec / (MICROSEC/MILLISEC));
973 			triptime -= ntohl(icp->icmp_otime);
974 			if (triptime < 0)
975 				triptime += 24LL * 60 * 60 * MILLISEC;
976 
977 			Printf("time=%d. ms", (int)triptime);
978 			triptime *= (MICROSEC/MILLISEC);
979 			tsum += triptime;
980 			tsum2 += triptime*triptime;
981 			if (triptime < tmin)
982 				tmin = triptime;
983 			if (triptime > tmax)
984 				tmax = triptime;
985 		}
986 		(void) putchar('\n');
987 		/*
988 		 * If it's stats, probe-all, npackets > 0, and we received reply
989 		 * for the last probe sent to this target address, then we
990 		 * don't need to wait anymore, let's move on to next target
991 		 * address, now!
992 		 */
993 		if (last_reply_from_targetaddr) {
994 			(void) alarm(0);	/* cancel alarm */
995 			current_targetaddr->probing_done = _B_TRUE;
996 			(void) sigrelse(SIGALRM);
997 			send_scheduled_probe();
998 			schedule_sigalrm();
999 		}
1000 		break;
1001 	case ICMP_ROUTERADVERT:
1002 	case ICMP_ROUTERSOLICIT:
1003 		/* Router discovery messages */
1004 		return;
1005 
1006 	case ICMP_ECHO:
1007 	case ICMP_TSTAMP:
1008 	case ICMP_IREQ:
1009 	case ICMP_MASKREQ:
1010 		/* These were never passed out from the SunOS 4.X kernel. */
1011 		return;
1012 
1013 	case ICMP_IREQREPLY:
1014 	case ICMP_MASKREPLY:
1015 		/* Replies for information and address mask requests */
1016 		return;
1017 
1018 	default:
1019 		if (verbose) {
1020 			Printf("%d bytes from %s:\n", cc, pr_name4(from));
1021 			Printf("icmp_type=%d (%s) ",
1022 			    icp->icmp_type, pr_type(icp->icmp_type));
1023 			Printf("icmp_code=%d\n", icp->icmp_code);
1024 			for (i = 0; i < 12; i++) {
1025 				Printf("x%2.2x: x%8.8x\n",
1026 				    i * sizeof (int32_t), *intp++);
1027 			}
1028 		}
1029 		break;
1030 	}
1031 
1032 	buf += sizeof (struct ip);
1033 	hlen -= sizeof (struct ip);
1034 
1035 	/* if verbose and there exists IP options */
1036 	if (verbose && hlen > 0)
1037 		pr_options((uchar_t *)buf, hlen);
1038 }
1039 
1040 /*
1041  * Print out the ip options.
1042  */
1043 static void
pr_options(uchar_t * opt,int optlength)1044 pr_options(uchar_t *opt, int optlength)
1045 {
1046 	int curlength;
1047 
1048 	Printf("  IP options: ");
1049 	while (optlength > 0) {
1050 		curlength = opt[1];
1051 		switch (*opt) {
1052 		case IPOPT_EOL:
1053 			optlength = 0;
1054 			break;
1055 
1056 		case IPOPT_NOP:
1057 			opt++;
1058 			optlength--;
1059 			continue;
1060 
1061 		case IPOPT_RR:
1062 			Printf(" <record route> ");
1063 			pr_rropt(opt, curlength, _B_TRUE);
1064 			break;
1065 
1066 		case IPOPT_TS:
1067 			Printf(" <time stamp> ");
1068 			pr_tsopt(opt, curlength);
1069 			break;
1070 
1071 		case IPOPT_SECURITY:
1072 			Printf(" <security>");
1073 			break;
1074 
1075 		case IPOPT_LSRR:
1076 			Printf(" <loose source route> ");
1077 			pr_rropt(opt, curlength, _B_FALSE);
1078 			break;
1079 
1080 		case IPOPT_SATID:
1081 			Printf(" <stream id>");
1082 			break;
1083 
1084 		case IPOPT_SSRR:
1085 			Printf(" <strict source route> ");
1086 			pr_rropt(opt, curlength, _B_FALSE);
1087 			break;
1088 
1089 		default:
1090 			Printf(" <option %d, len %d>", *opt, curlength);
1091 			break;
1092 		}
1093 		/*
1094 		 * Following most options comes a length field
1095 		 */
1096 		opt += curlength;
1097 		optlength -= curlength;
1098 	}
1099 	(void) putchar('\n');
1100 }
1101 
1102 /*
1103  * Print out a recorded route option. If rrflag is _B_TRUE, it prints record
1104  * route option, otherwise LSRR/SSRR.
1105  */
1106 static void
pr_rropt(uchar_t * opt,int length,boolean_t rrflag)1107 pr_rropt(uchar_t *opt, int length, boolean_t rrflag)
1108 {
1109 	struct ip_sourceroute *rrp;
1110 	int sr_index = 0;
1111 	struct in_addr addr;
1112 
1113 	rrp = (struct ip_sourceroute *)opt;
1114 
1115 	/* data starts at offset 3 */
1116 	length -= 3;
1117 	while (length > 0) {
1118 		/*
1119 		 * Let's see if we are examining the addr pointed by ipsr_ptr
1120 		 */
1121 		if ((rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) &&
1122 		    rrflag) {
1123 			Printf(" (End of record)");
1124 			break;
1125 		}
1126 
1127 		bcopy(&rrp->ipsr_addrs[sr_index], &addr, sizeof (addr));
1128 		Printf("%s", pr_name((char *)&addr, AF_INET));
1129 
1130 		if (rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) {
1131 			Printf("(Current)");
1132 		}
1133 
1134 		sr_index++;
1135 
1136 		length -= sizeof (addr);
1137 		if (length > 0)
1138 			Printf(", ");
1139 	}
1140 }
1141 
1142 /*
1143  * Print out a timestamp option.
1144  */
1145 static void
pr_tsopt(uchar_t * opt,int length)1146 pr_tsopt(uchar_t *opt, int length)
1147 {
1148 	boolean_t address_present;
1149 	boolean_t rrflag;		/* End at current entry? */
1150 	struct ip_timestamp *tsp;
1151 	int ts_index = 0;
1152 	struct in_addr addr;
1153 	size_t data_len;
1154 	int32_t time;
1155 
1156 	/* LINTED */
1157 	tsp = (struct ip_timestamp *)opt;
1158 
1159 	switch (tsp->ipt_flg) {
1160 	case IPOPT_TS_TSONLY:
1161 		address_present = _B_FALSE;
1162 		data_len = sizeof (tsp->ipt_timestamp.ipt_time[0]);
1163 		rrflag = _B_TRUE;
1164 		break;
1165 	case IPOPT_TS_TSANDADDR:
1166 		address_present = _B_TRUE;
1167 		data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
1168 		rrflag = _B_TRUE;
1169 		break;
1170 	case IPOPT_TS_PRESPEC:
1171 	case 3:
1172 		address_present = _B_TRUE;
1173 		data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
1174 		rrflag = _B_FALSE;
1175 		break;
1176 	default:
1177 		Printf("(Bad flag value: 0x%x)", tsp->ipt_flg);
1178 		return;
1179 	}
1180 	if (tsp->ipt_oflw > 0)
1181 		Printf("(Overflow: %d) ", tsp->ipt_oflw);
1182 
1183 	/* data starts at offset 4 */
1184 	length -= 4;
1185 
1186 	while (length > 0) {
1187 		if (length < data_len)
1188 			break;
1189 
1190 		/* the minimum value of ipt_ptr is 5 */
1191 		if ((tsp->ipt_ptr == ts_index * data_len + 5) && rrflag) {
1192 			Printf(" (End of record)");
1193 			break;
1194 		}
1195 		if (address_present) {
1196 			bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_addr,
1197 			    &addr, sizeof (addr));
1198 			Printf("%s: ", pr_name((char *)&addr, AF_INET));
1199 			bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_time,
1200 			    &time, sizeof (time));
1201 		} else {
1202 			bcopy(&tsp->ipt_timestamp.ipt_time[ts_index],
1203 			    &time, sizeof (time));
1204 		}
1205 		Printf("%d", ntohl(time));
1206 
1207 		if (tsp->ipt_ptr == ts_index * data_len + 5)
1208 			Printf("(Current)");
1209 
1210 		ts_index++;
1211 		length -= data_len;
1212 		if (length > 0)
1213 			Printf(", ");
1214 	}
1215 }
1216 
1217 /*
1218  * Convert an ICMP "type" field to a printable string.
1219  */
1220 static char *
pr_type(int icmp_type)1221 pr_type(int icmp_type)
1222 {
1223 	static struct icmptype_table ttab[] = {
1224 		{ICMP_ECHOREPLY,	"Echo Reply"},
1225 		{1,			"ICMP 1"},
1226 		{2,			"ICMP 2"},
1227 		{ICMP_UNREACH,		"Dest Unreachable"},
1228 		{ICMP_SOURCEQUENCH,	"Source Quench"},
1229 		{ICMP_REDIRECT,		"Redirect"},
1230 		{6,			"ICMP 6"},
1231 		{7,			"ICMP 7"},
1232 		{ICMP_ECHO,		"Echo"},
1233 		{ICMP_ROUTERADVERT,	"Router Advertisement"},
1234 		{ICMP_ROUTERSOLICIT,	"Router Solicitation"},
1235 		{ICMP_TIMXCEED,		"Time Exceeded"},
1236 		{ICMP_PARAMPROB,	"Parameter Problem"},
1237 		{ICMP_TSTAMP,		"Timestamp"},
1238 		{ICMP_TSTAMPREPLY,	"Timestamp Reply"},
1239 		{ICMP_IREQ,		"Info Request"},
1240 		{ICMP_IREQREPLY,	"Info Reply"},
1241 		{ICMP_MASKREQ,		"Netmask Request"},
1242 		{ICMP_MASKREPLY,	"Netmask Reply"}
1243 	};
1244 	int i;
1245 
1246 	for (i = 0; i < A_CNT(ttab); i++) {
1247 		if (ttab[i].type == icmp_type)
1248 			return (ttab[i].message);
1249 	}
1250 
1251 	return ("OUT-OF-RANGE");
1252 }
1253