xref: /titanic_44/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.c (revision a252e0074e35e9003ab1dc3797dccf29ff16ae73)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5a356273cSpwernau  * Common Development and Distribution License (the "License").
63c58dfd6Sjbeck  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  *
21e11c3f44Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
227c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
267c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
277c478bd9Sstevel@tonic-gate  * All Rights Reserved.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
327c478bd9Sstevel@tonic-gate  * The Regents of the University of California.
337c478bd9Sstevel@tonic-gate  * All Rights Reserved.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
367c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
377c478bd9Sstevel@tonic-gate  * contributors.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #include <stdio.h>
417c478bd9Sstevel@tonic-gate #include <strings.h>
427c478bd9Sstevel@tonic-gate #include <errno.h>
437c478bd9Sstevel@tonic-gate #include <fcntl.h>
447c478bd9Sstevel@tonic-gate #include <unistd.h>
457c478bd9Sstevel@tonic-gate #include <signal.h>
467c478bd9Sstevel@tonic-gate #include <limits.h>
477c478bd9Sstevel@tonic-gate #include <math.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <sys/time.h>
507c478bd9Sstevel@tonic-gate #include <sys/param.h>
517c478bd9Sstevel@tonic-gate #include <sys/socket.h>
527c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
537c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
547c478bd9Sstevel@tonic-gate #include <sys/file.h>
557c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
587c478bd9Sstevel@tonic-gate #include <net/if.h>
597c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
607c478bd9Sstevel@tonic-gate #include <netinet/in.h>
617c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
627c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h>
637c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
647c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
657c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
667c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
677c478bd9Sstevel@tonic-gate #include <netdb.h>
687c478bd9Sstevel@tonic-gate #include <stdlib.h>
697c478bd9Sstevel@tonic-gate #include <priv_utils.h>
707c478bd9Sstevel@tonic-gate 
710406ceaaSmeem #include <libinetutil.h>
727c478bd9Sstevel@tonic-gate #include "ping.h"
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /*
757c478bd9Sstevel@tonic-gate  * This macro is used to compare 16bit, wrapping sequence numbers. Inspired by
767c478bd9Sstevel@tonic-gate  * TCP's SEQ_LEQ macro.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate #define	PINGSEQ_LEQ(a, b)	((int16_t)((a)-(b)) <= 0)
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate #define	MAX_WAIT		10	/* max sec. to wait for response */
817c478bd9Sstevel@tonic-gate #define	MAX_TRAFFIC_CLASS	255	/* max traffic class for IPv6 */
827c478bd9Sstevel@tonic-gate #define	MAX_FLOW_LABEL		0xFFFFF	/* max flow label for IPv6 */
837c478bd9Sstevel@tonic-gate #define	MAX_TOS			255	/* max type-of-service for IPv4 */
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate #define	TIMEOUT			20	/* default timeout value */
867c478bd9Sstevel@tonic-gate #define	DEFAULT_DATALEN		56
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate #define	MULTICAST_NOLOOP	1	/* multicast options */
897c478bd9Sstevel@tonic-gate #define	MULTICAST_TTL		2
907c478bd9Sstevel@tonic-gate #define	MULTICAST_IF		4
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate #define	IF_INDEX		0	/* types of -i argument */
937c478bd9Sstevel@tonic-gate #define	IF_NAME			1
947c478bd9Sstevel@tonic-gate #define	IF_ADDR			2
957c478bd9Sstevel@tonic-gate #define	IF_ADDR6		3
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate #ifdef BSD
987c478bd9Sstevel@tonic-gate #define	setbuf(s, b)	setlinebuf((s))
997c478bd9Sstevel@tonic-gate #endif /* BSD */
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate /* interface identification */
1037c478bd9Sstevel@tonic-gate union if_id {
1047c478bd9Sstevel@tonic-gate 	int index;		/* interface index (e.g., 1, 2) */
1057c478bd9Sstevel@tonic-gate 	char *name;		/* interface name (e.g., le0, hme0) */
1067c478bd9Sstevel@tonic-gate 	union any_in_addr addr;	/* interface address (e.g., 10.123.4.5) */
1077c478bd9Sstevel@tonic-gate };
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /* stores the interface supplied by the user */
1107c478bd9Sstevel@tonic-gate struct if_entry {
1117c478bd9Sstevel@tonic-gate 	char *str;		/* unresolved, string input */
1127c478bd9Sstevel@tonic-gate 	int id_type;		/* type of ID (index, name, addr, addr6) */
1137c478bd9Sstevel@tonic-gate 	union if_id id;		/* ID */
1147c478bd9Sstevel@tonic-gate };
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate char *progname;
1177c478bd9Sstevel@tonic-gate char *targethost;
1184c10bc16Spwernau char *nexthop;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate static int send_sock;			/* send sockets */
1217c478bd9Sstevel@tonic-gate static int send_sock6;
1227c478bd9Sstevel@tonic-gate static struct sockaddr_in to;		/* where to send */
1237c478bd9Sstevel@tonic-gate static struct sockaddr_in6 to6;
1247c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list[MAX_GWS];	/* gateways */
1257c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list6[MAX_GWS6];
1267c478bd9Sstevel@tonic-gate static int if_index = 0;		/* outgoing interface index */
1277c478bd9Sstevel@tonic-gate boolean_t is_alive = _B_FALSE;		/* is target host alive */
1287c478bd9Sstevel@tonic-gate struct targetaddr *current_targetaddr;	/* current target IP address to probe */
1297c478bd9Sstevel@tonic-gate static struct targetaddr *targetaddr_list; /* list of IP addresses to probe */
1307c478bd9Sstevel@tonic-gate static int num_targetaddrs;		/* no of target addresses to probe */
1317c478bd9Sstevel@tonic-gate static int num_v4 = 0;			/* count of IPv4 addresses */
1327c478bd9Sstevel@tonic-gate static int num_v6 = 0;			/* count of IPv6 addresses */
1337c478bd9Sstevel@tonic-gate boolean_t verbose = _B_FALSE;		/* verbose output */
1347c478bd9Sstevel@tonic-gate boolean_t stats = _B_FALSE;		/* display statistics */
1357c478bd9Sstevel@tonic-gate static boolean_t settos = _B_FALSE;	/* set type-of-service value */
1367c478bd9Sstevel@tonic-gate boolean_t rr_option = _B_FALSE;		/* true if using record route */
1377c478bd9Sstevel@tonic-gate boolean_t send_reply = _B_FALSE;	/* Send an ICMP_{ECHO|TSTAMP}REPLY */
1387c478bd9Sstevel@tonic-gate 					/* that goes to target and comes back */
1397c478bd9Sstevel@tonic-gate 					/* to the the sender via src routing. */
1407c478bd9Sstevel@tonic-gate boolean_t strict = _B_FALSE;		/* true if using strict source route */
1417c478bd9Sstevel@tonic-gate boolean_t ts_option = _B_FALSE;		/* true if using timestamp option */
1427c478bd9Sstevel@tonic-gate boolean_t use_icmp_ts = _B_FALSE;	/* Use ICMP timestamp request */
1437c478bd9Sstevel@tonic-gate boolean_t use_udp = _B_FALSE;		/* Use UDP instead of ICMP */
1447c478bd9Sstevel@tonic-gate boolean_t probe_all = _B_FALSE;		/* probe all the IP addresses */
1457c478bd9Sstevel@tonic-gate boolean_t nflag = _B_FALSE;		/* do not reverse lookup addresses */
146a356273cSpwernau boolean_t bypass = _B_FALSE;		/* bypass IPsec policy */
1477c478bd9Sstevel@tonic-gate static int family_input = AF_UNSPEC;	/* address family supplied by user */
1487c478bd9Sstevel@tonic-gate int datalen = DEFAULT_DATALEN;		/* How much data */
1497c478bd9Sstevel@tonic-gate int ts_flag;				/* timestamp flag value */
1507c478bd9Sstevel@tonic-gate static int num_gw;			/* number of gateways */
1517c478bd9Sstevel@tonic-gate static int eff_num_gw;			/* effective number of gateways */
1527c478bd9Sstevel@tonic-gate 					/* if send_reply, it's 2*num_gw+1 */
1537c478bd9Sstevel@tonic-gate static int num_wraps = -1;		/* no of times 64K icmp_seq wrapped */
1547c478bd9Sstevel@tonic-gate static ushort_t dest_port = 32768 + 666; /* starting port for the UDP probes */
1557c478bd9Sstevel@tonic-gate static char *gw_list[MAXMAX_GWS];	/* list of gateways as user enters */
1567c478bd9Sstevel@tonic-gate static int interval = 1;		/* interval between transmissions */
1577c478bd9Sstevel@tonic-gate static int options;			/* socket options */
1587c478bd9Sstevel@tonic-gate static int moptions;			/* multicast options */
1597c478bd9Sstevel@tonic-gate int npackets;				/* number of packets to send */
1607c478bd9Sstevel@tonic-gate static ushort_t tos;			/* type-of-service value */
1617c478bd9Sstevel@tonic-gate static int hoplimit = -1;		/* time-to-live value */
162bd670b35SErik Nordmark static int dontfrag;			/* IP*_DONTFRAG */
1637c478bd9Sstevel@tonic-gate static int timeout = TIMEOUT;		/* timeout value (sec) for probes */
1647c478bd9Sstevel@tonic-gate static struct if_entry out_if;		/* interface argument */
1657c478bd9Sstevel@tonic-gate int ident;				/* ID for this ping run */
1667c478bd9Sstevel@tonic-gate static hrtime_t t_last_probe_sent;	/* the time we sent the last probe */
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate  * This buffer stores the received packets. Currently it needs to be 32 bit
1707c478bd9Sstevel@tonic-gate  * aligned. In the future, we'll be using 64 bit alignment, so let's use 64 bit
1717c478bd9Sstevel@tonic-gate  * alignment now.
1727c478bd9Sstevel@tonic-gate  */
1737c478bd9Sstevel@tonic-gate static uint64_t in_pkt[(IP_MAXPACKET + 1)/8];
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate /* Used to store the ancillary data that comes with the received packets */
1767c478bd9Sstevel@tonic-gate static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate static int ntransmitted;	/* number of packet sent to single IP address */
1797c478bd9Sstevel@tonic-gate int nreceived;			/* # of packets we got back from target host */
1807c478bd9Sstevel@tonic-gate int nreceived_last_target;	/* received from last target IP */
1817c478bd9Sstevel@tonic-gate /*
1827c478bd9Sstevel@tonic-gate  * These are used for statistics. tmin is initialized to maximum longint value.
1837c478bd9Sstevel@tonic-gate  * The max value is also used for timeouts.   All times are in microseconds.
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate long long tmin = LLONG_MAX;
1867c478bd9Sstevel@tonic-gate long long tmax;
1877c478bd9Sstevel@tonic-gate int64_t tsum;			/* sum of all times, for doing average */
1887c478bd9Sstevel@tonic-gate int64_t tsum2;			/* sum of squared times, for std. dev. */
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate static struct targetaddr *build_targetaddr_list(struct addrinfo *,
1917c478bd9Sstevel@tonic-gate     union any_in_addr *);
1927c478bd9Sstevel@tonic-gate extern void check_reply(struct addrinfo *, struct msghdr *, int, ushort_t);
1937c478bd9Sstevel@tonic-gate extern void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
1947c478bd9Sstevel@tonic-gate static struct targetaddr *create_targetaddr_item(int, union any_in_addr *,
1957c478bd9Sstevel@tonic-gate     union any_in_addr *);
1967c478bd9Sstevel@tonic-gate void find_dstaddr(ushort_t, union any_in_addr *);
1977c478bd9Sstevel@tonic-gate static struct ifaddrlist *find_if(struct ifaddrlist *, int);
1987c478bd9Sstevel@tonic-gate static void finish();
1997c478bd9Sstevel@tonic-gate static void get_gwaddrs(char *[], int, union any_in_addr *,
2007c478bd9Sstevel@tonic-gate     union any_in_addr *, int *, int *);
2017c478bd9Sstevel@tonic-gate static void get_hostinfo(char *, int, struct addrinfo **);
2027c478bd9Sstevel@tonic-gate static ushort_t in_cksum(ushort_t *, int);
2037c478bd9Sstevel@tonic-gate static int int_arg(char *s, char *what);
2047c478bd9Sstevel@tonic-gate boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
2057c478bd9Sstevel@tonic-gate static void mirror_gws(union any_in_addr *, int);
2067c478bd9Sstevel@tonic-gate static void pinger(int, struct sockaddr *, struct msghdr *, int);
2077c478bd9Sstevel@tonic-gate char *pr_name(char *, int);
2087c478bd9Sstevel@tonic-gate char *pr_protocol(int);
2097c478bd9Sstevel@tonic-gate static void print_unknown_host_msg(const char *, const char *);
2107c478bd9Sstevel@tonic-gate static void recv_icmp_packet(struct addrinfo *, int, int, ushort_t, ushort_t);
2114c10bc16Spwernau static void resolve_nodes(struct addrinfo **, struct addrinfo **,
2124c10bc16Spwernau     union any_in_addr **);
2137c478bd9Sstevel@tonic-gate void schedule_sigalrm();
2147c478bd9Sstevel@tonic-gate static void select_all_src_addrs(union any_in_addr **, struct addrinfo *,
2157c478bd9Sstevel@tonic-gate     union any_in_addr *, union any_in_addr *);
2167c478bd9Sstevel@tonic-gate static void select_src_addr(union any_in_addr *, int, union any_in_addr *);
2177c478bd9Sstevel@tonic-gate void send_scheduled_probe();
2187c478bd9Sstevel@tonic-gate boolean_t seq_match(ushort_t, int, ushort_t);
2197c478bd9Sstevel@tonic-gate extern void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int,
2207c478bd9Sstevel@tonic-gate     uint_t);
2217c478bd9Sstevel@tonic-gate extern void set_IPv4_options(int, union any_in_addr *, int, struct in_addr *,
2227c478bd9Sstevel@tonic-gate     struct in_addr *);
2234c10bc16Spwernau static void set_nexthop(int, struct addrinfo *, int);
2244c10bc16Spwernau static boolean_t setup_socket(int, int *, int *, int *, ushort_t *,
2254c10bc16Spwernau     struct addrinfo *);
2267c478bd9Sstevel@tonic-gate void sigalrm_handler();
2277c478bd9Sstevel@tonic-gate void tvsub(struct timeval *, struct timeval *);
2287c478bd9Sstevel@tonic-gate static void usage(char *);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate /*
2317c478bd9Sstevel@tonic-gate  * main()
2327c478bd9Sstevel@tonic-gate  */
2338c332a0dSja97890 int
main(int argc,char * argv[])2347c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	struct addrinfo	*ai_dst = NULL;		/* addrinfo host list */
2374c10bc16Spwernau 	struct addrinfo	*ai_nexthop = NULL;		/* addrinfo nexthop */
2387c478bd9Sstevel@tonic-gate 	union any_in_addr *src_addr_list = NULL;	/* src addrs to use */
2397c478bd9Sstevel@tonic-gate 	int recv_sock = -1;				/* receive sockets */
2407c478bd9Sstevel@tonic-gate 	int recv_sock6 = -1;
2417c478bd9Sstevel@tonic-gate 	ushort_t udp_src_port;			/* src ports for UDP probes */
2427c478bd9Sstevel@tonic-gate 	ushort_t udp_src_port6;			/* used to identify replies */
2437c478bd9Sstevel@tonic-gate 	uint_t flowinfo = 0;
2447c478bd9Sstevel@tonic-gate 	uint_t class = 0;
245e11c3f44Smeem 	char abuf[INET6_ADDRSTRLEN];
2467c478bd9Sstevel@tonic-gate 	int c;
2477c478bd9Sstevel@tonic-gate 	int i;
248f4b3ec61Sdh155122 	boolean_t has_sys_ip_config;
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	progname = argv[0];
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	/*
2531da45818Spwernau 	 * This program needs the net_icmpaccess privilege for creating
254f4b3ec61Sdh155122 	 * raw ICMP sockets.  It needs sys_ip_config for using the
2551da45818Spwernau 	 * IP_NEXTHOP socket option (IPv4 only).  We'll fail
2567c478bd9Sstevel@tonic-gate 	 * on the socket call and report the error there when we have
2577c478bd9Sstevel@tonic-gate 	 * insufficient privileges.
2581da45818Spwernau 	 *
259f4b3ec61Sdh155122 	 * Shared-IP zones don't have the sys_ip_config privilege, so
2601da45818Spwernau 	 * we need to check for it in our limit set before trying
2611da45818Spwernau 	 * to set it.
2627c478bd9Sstevel@tonic-gate 	 */
263f4b3ec61Sdh155122 	has_sys_ip_config = priv_ineffect(PRIV_SYS_IP_CONFIG);
2641da45818Spwernau 
2657c478bd9Sstevel@tonic-gate 	(void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS,
266f4b3ec61Sdh155122 	    has_sys_ip_config ? PRIV_SYS_IP_CONFIG : (char *)NULL,
2671da45818Spwernau 	    (char *)NULL);
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	setbuf(stdout, (char *)0);
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv,
272bd670b35SErik Nordmark 	    "abA:c:dDF:G:g:I:i:LlnN:P:p:rRSsTt:UvX:x:Y0123?")) != -1) {
2737c478bd9Sstevel@tonic-gate 		switch ((char)c) {
2747c478bd9Sstevel@tonic-gate 		case 'A':
2757c478bd9Sstevel@tonic-gate 			if (strcmp(optarg, "inet") == 0) {
2767c478bd9Sstevel@tonic-gate 				family_input = AF_INET;
2777c478bd9Sstevel@tonic-gate 			} else if (strcmp(optarg, "inet6") == 0) {
2787c478bd9Sstevel@tonic-gate 				family_input = AF_INET6;
2797c478bd9Sstevel@tonic-gate 			} else {
2807c478bd9Sstevel@tonic-gate 				Fprintf(stderr,
2817c478bd9Sstevel@tonic-gate 				    "%s: unknown address family %s\n",
2827c478bd9Sstevel@tonic-gate 				    progname, optarg);
2837c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2847c478bd9Sstevel@tonic-gate 			}
2857c478bd9Sstevel@tonic-gate 			break;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 		case 'a':
2887c478bd9Sstevel@tonic-gate 			probe_all = _B_TRUE;
2897c478bd9Sstevel@tonic-gate 			break;
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 		case 'c':
2927c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "traffic class");
2937c478bd9Sstevel@tonic-gate 			if (i > MAX_TRAFFIC_CLASS) {
2947c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: traffic class %d out of "
2957c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
2967c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2977c478bd9Sstevel@tonic-gate 			}
2987c478bd9Sstevel@tonic-gate 			class = (uint_t)i;
2997c478bd9Sstevel@tonic-gate 			break;
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 		case 'd':
3027c478bd9Sstevel@tonic-gate 			options |= SO_DEBUG;
3037c478bd9Sstevel@tonic-gate 			break;
3047c478bd9Sstevel@tonic-gate 
305bd670b35SErik Nordmark 		case 'D':
306bd670b35SErik Nordmark 			dontfrag = 1;
307bd670b35SErik Nordmark 			break;
308bd670b35SErik Nordmark 
309a356273cSpwernau 		case 'b':
310a356273cSpwernau 			bypass = _B_TRUE;
311a356273cSpwernau 			break;
312a356273cSpwernau 
3137c478bd9Sstevel@tonic-gate 		case 'F':
3147c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "flow label");
3157c478bd9Sstevel@tonic-gate 			if (i > MAX_FLOW_LABEL) {
3167c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: flow label %d out of "
3177c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3187c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3197c478bd9Sstevel@tonic-gate 			}
3207c478bd9Sstevel@tonic-gate 			flowinfo = (uint_t)i;
3217c478bd9Sstevel@tonic-gate 			break;
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 		case 'I':
3247c478bd9Sstevel@tonic-gate 			stats = _B_TRUE;
3257c478bd9Sstevel@tonic-gate 			interval = int_arg(optarg, "interval");
3267c478bd9Sstevel@tonic-gate 			break;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 		case 'i':
3297c478bd9Sstevel@tonic-gate 			/*
3307c478bd9Sstevel@tonic-gate 			 * this can accept interface index, interface name, and
3317c478bd9Sstevel@tonic-gate 			 * address configured on the interface
3327c478bd9Sstevel@tonic-gate 			 */
3337c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_IF;
3347c478bd9Sstevel@tonic-gate 			out_if.str = optarg;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 			if (inet_pton(AF_INET6, optarg, &out_if.id.addr) > 0) {
3377c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_ADDR6;
3387c478bd9Sstevel@tonic-gate 			} else if (inet_pton(AF_INET, optarg,
3397c478bd9Sstevel@tonic-gate 			    &out_if.id.addr) > 0) {
3407c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_ADDR;
3417c478bd9Sstevel@tonic-gate 			} else if (strcmp(optarg, "0") == 0) {
3427c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_INDEX;
3437c478bd9Sstevel@tonic-gate 				out_if.id.index = 0;
3447c478bd9Sstevel@tonic-gate 			} else if ((out_if.id.index = atoi(optarg)) != 0) {
3457c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_INDEX;
3467c478bd9Sstevel@tonic-gate 			} else {
3477c478bd9Sstevel@tonic-gate 				out_if.id.name = optarg;
3487c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_NAME;
3497c478bd9Sstevel@tonic-gate 			}
3507c478bd9Sstevel@tonic-gate 			break;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 		case 'L':
3537c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_NOLOOP;
3547c478bd9Sstevel@tonic-gate 			break;
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		case 'l':
3577c478bd9Sstevel@tonic-gate 			send_reply = _B_TRUE;
3587c478bd9Sstevel@tonic-gate 			strict = _B_FALSE;
3597c478bd9Sstevel@tonic-gate 			break;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		case 'n':
3627c478bd9Sstevel@tonic-gate 			nflag = _B_TRUE;
3637c478bd9Sstevel@tonic-gate 			break;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		case 'P':
3667c478bd9Sstevel@tonic-gate 			settos = _B_TRUE;
3677c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "type-of-service");
3687c478bd9Sstevel@tonic-gate 			if (i > MAX_TOS) {
3697c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: tos value %d out of "
3707c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3717c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3727c478bd9Sstevel@tonic-gate 			}
3737c478bd9Sstevel@tonic-gate 			tos = (ushort_t)i;
3747c478bd9Sstevel@tonic-gate 			break;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 		case 'p':
3777c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "port number");
3787c478bd9Sstevel@tonic-gate 			if (i > MAX_PORT) {
3797c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: port number %d out of "
3807c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3817c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3827c478bd9Sstevel@tonic-gate 			}
3837c478bd9Sstevel@tonic-gate 			dest_port = (ushort_t)i;
3847c478bd9Sstevel@tonic-gate 			break;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		case 'r':
3877c478bd9Sstevel@tonic-gate 			options |= SO_DONTROUTE;
3887c478bd9Sstevel@tonic-gate 			break;
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 		case 'R':
3917c478bd9Sstevel@tonic-gate 			rr_option = _B_TRUE;
3927c478bd9Sstevel@tonic-gate 			break;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 		case 'S':
3957c478bd9Sstevel@tonic-gate 			send_reply = _B_TRUE;
3967c478bd9Sstevel@tonic-gate 			strict = _B_TRUE;
3977c478bd9Sstevel@tonic-gate 			break;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		case 's':
4007c478bd9Sstevel@tonic-gate 			stats = _B_TRUE;
4017c478bd9Sstevel@tonic-gate 			break;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 		case 'T':
4047c478bd9Sstevel@tonic-gate 			ts_option = _B_TRUE;
4057c478bd9Sstevel@tonic-gate 			break;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 		case 't':
4087c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_TTL;
4097c478bd9Sstevel@tonic-gate 			hoplimit = int_arg(optarg, "ttl");
4107c478bd9Sstevel@tonic-gate 			if (hoplimit > MAXTTL) {
4117c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: ttl %d out of range\n",
4127c478bd9Sstevel@tonic-gate 				    progname, hoplimit);
4137c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4147c478bd9Sstevel@tonic-gate 			}
4157c478bd9Sstevel@tonic-gate 			break;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		case 'U':
4187c478bd9Sstevel@tonic-gate 			use_udp = _B_TRUE;
4197c478bd9Sstevel@tonic-gate 			use_icmp_ts = _B_FALSE;
4207c478bd9Sstevel@tonic-gate 			break;
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		case 'v':
4237c478bd9Sstevel@tonic-gate 			verbose = _B_TRUE;
4247c478bd9Sstevel@tonic-gate 			break;
4257c478bd9Sstevel@tonic-gate 		/*
4267c478bd9Sstevel@tonic-gate 		 * 'x' and 'X' has been undocumented flags for source routing.
4277c478bd9Sstevel@tonic-gate 		 * Now we document loose source routing with the new flag 'g',
4287c478bd9Sstevel@tonic-gate 		 * which is same as in traceroute. We still keep x/X as
4297c478bd9Sstevel@tonic-gate 		 * as undocumented. 'G', which is for strict source routing is
4307c478bd9Sstevel@tonic-gate 		 * also undocumented.
4317c478bd9Sstevel@tonic-gate 		 */
4327c478bd9Sstevel@tonic-gate 		case 'x':
4337c478bd9Sstevel@tonic-gate 		case 'g':
4347c478bd9Sstevel@tonic-gate 			strict = _B_FALSE;
4357c478bd9Sstevel@tonic-gate 			if (num_gw > MAXMAX_GWS) {
4367c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: too many gateways\n",
4377c478bd9Sstevel@tonic-gate 				    progname);
4387c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4397c478bd9Sstevel@tonic-gate 			}
4407c478bd9Sstevel@tonic-gate 			gw_list[num_gw++] = optarg;
4417c478bd9Sstevel@tonic-gate 			break;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 		case 'X':
4447c478bd9Sstevel@tonic-gate 		case 'G':
4457c478bd9Sstevel@tonic-gate 			strict = _B_TRUE;
4467c478bd9Sstevel@tonic-gate 			if (num_gw > MAXMAX_GWS) {
4477c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: too many gateways\n",
4487c478bd9Sstevel@tonic-gate 				    progname);
4497c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4507c478bd9Sstevel@tonic-gate 			}
4517c478bd9Sstevel@tonic-gate 			gw_list[num_gw++] = optarg;
4527c478bd9Sstevel@tonic-gate 			break;
4537c478bd9Sstevel@tonic-gate 
4544c10bc16Spwernau 		case 'N':
4554c10bc16Spwernau 			if (nexthop != NULL) {
4564c10bc16Spwernau 				Fprintf(stderr, "%s: only one next hop gateway"
4574c10bc16Spwernau 				    " allowed\n", progname);
4584c10bc16Spwernau 				exit(EXIT_FAILURE);
4594c10bc16Spwernau 			}
4604c10bc16Spwernau 			nexthop = optarg;
4614c10bc16Spwernau 			break;
4624c10bc16Spwernau 
4637c478bd9Sstevel@tonic-gate 		case 'Y':
4647c478bd9Sstevel@tonic-gate 			use_icmp_ts = _B_TRUE;
4657c478bd9Sstevel@tonic-gate 			use_udp = _B_FALSE;
4667c478bd9Sstevel@tonic-gate 			break;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 		case '0':
4697c478bd9Sstevel@tonic-gate 		case '1':
4707c478bd9Sstevel@tonic-gate 		case '2':
4717c478bd9Sstevel@tonic-gate 		case '3':
4727c478bd9Sstevel@tonic-gate 			ts_flag = (char)c - '0';
4737c478bd9Sstevel@tonic-gate 			break;
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 		case '?':
4767c478bd9Sstevel@tonic-gate 			usage(progname);
4777c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
4787c478bd9Sstevel@tonic-gate 			break;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 		default:
4817c478bd9Sstevel@tonic-gate 			usage(progname);
4827c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
4837c478bd9Sstevel@tonic-gate 			break;
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 	}
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	if (optind >= argc) {
4887c478bd9Sstevel@tonic-gate 		usage(progname);
4897c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
4907c478bd9Sstevel@tonic-gate 	}
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	/*
4937c478bd9Sstevel@tonic-gate 	 * send_reply, which sends the probe packet back to itself
4947c478bd9Sstevel@tonic-gate 	 * doesn't work with UDP
4957c478bd9Sstevel@tonic-gate 	 */
4967c478bd9Sstevel@tonic-gate 	if (use_udp)
4977c478bd9Sstevel@tonic-gate 		send_reply = _B_FALSE;
4987c478bd9Sstevel@tonic-gate 
4993c58dfd6Sjbeck 	if (getenv("MACHINE_THAT_GOES_PING") != NULL)
5003c58dfd6Sjbeck 		stats = _B_TRUE;
5013c58dfd6Sjbeck 
5027c478bd9Sstevel@tonic-gate 	targethost = argv[optind];
5037c478bd9Sstevel@tonic-gate 	optind++;
5047c478bd9Sstevel@tonic-gate 	if (optind < argc) {
5057c478bd9Sstevel@tonic-gate 		if (stats) {
5067c478bd9Sstevel@tonic-gate 			datalen = int_arg(argv[optind], "data size");
5077c478bd9Sstevel@tonic-gate 			optind++;
5087c478bd9Sstevel@tonic-gate 			if (optind < argc) {
5097c478bd9Sstevel@tonic-gate 				npackets = int_arg(argv[optind],
5107c478bd9Sstevel@tonic-gate 				    "packet count");
5117c478bd9Sstevel@tonic-gate 				if (npackets < 1) {
5127c478bd9Sstevel@tonic-gate 					Fprintf(stderr, "%s: packet count %d "
5137c478bd9Sstevel@tonic-gate 					    "out of range\n", progname,
5147c478bd9Sstevel@tonic-gate 					    npackets);
5157c478bd9Sstevel@tonic-gate 					exit(EXIT_FAILURE);
5167c478bd9Sstevel@tonic-gate 				}
5177c478bd9Sstevel@tonic-gate 			}
5187c478bd9Sstevel@tonic-gate 		} else {
5197c478bd9Sstevel@tonic-gate 			timeout = int_arg(argv[optind], "timeout");
5207c478bd9Sstevel@tonic-gate 		}
5217c478bd9Sstevel@tonic-gate 	}
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	/*
5247c478bd9Sstevel@tonic-gate 	 * Let's prepare sockaddr_in* structures, cause we might need both of
5257c478bd9Sstevel@tonic-gate 	 * them.
5267c478bd9Sstevel@tonic-gate 	 */
5277c478bd9Sstevel@tonic-gate 	bzero((char *)&to, sizeof (struct sockaddr_in));
5287c478bd9Sstevel@tonic-gate 	to.sin_family = AF_INET;
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	bzero((char *)&to6, sizeof (struct sockaddr_in6));
5317c478bd9Sstevel@tonic-gate 	to6.sin6_family = AF_INET6;
5327c478bd9Sstevel@tonic-gate 	to6.sin6_flowinfo = htonl((class << 20) | flowinfo);
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	if (stats)
5357c478bd9Sstevel@tonic-gate 		(void) sigset(SIGINT, finish);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	ident = (int)getpid() & 0xFFFF;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	/* resolve the hostnames */
5404c10bc16Spwernau 	resolve_nodes(&ai_dst, &ai_nexthop, &src_addr_list);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/*
5437c478bd9Sstevel@tonic-gate 	 * We should make sure datalen is reasonable.
5447c478bd9Sstevel@tonic-gate 	 * 	IP_MAXPACKET >= IPv4/IPv6 header length +
5457c478bd9Sstevel@tonic-gate 	 *			IPv4 options/IPv6 routing header length +
5467c478bd9Sstevel@tonic-gate 	 *			ICMP/ICMP6/UDP header length +
5477c478bd9Sstevel@tonic-gate 	 *			datalen
5487c478bd9Sstevel@tonic-gate 	 */
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	if (family_input == AF_INET6 ||
5517c478bd9Sstevel@tonic-gate 	    (family_input == AF_UNSPEC && num_v6 != 0)) {
5527c478bd9Sstevel@tonic-gate 		size_t exthdr_len = 0;
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		if (send_reply) {
5557c478bd9Sstevel@tonic-gate 			exthdr_len = sizeof (struct ip6_rthdr0) +
5567c478bd9Sstevel@tonic-gate 			    2 * num_gw * sizeof (struct in6_addr);
5577c478bd9Sstevel@tonic-gate 		} else if (num_gw > 0) {
5587c478bd9Sstevel@tonic-gate 			exthdr_len = sizeof (struct ip6_rthdr0) +
5597c478bd9Sstevel@tonic-gate 			    num_gw * sizeof (struct in6_addr);
5607c478bd9Sstevel@tonic-gate 		}
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 		/*
5637c478bd9Sstevel@tonic-gate 		 * Size of ICMP6 header and UDP header are the same. Let's
5647c478bd9Sstevel@tonic-gate 		 * use ICMP6_MINLEN.
5657c478bd9Sstevel@tonic-gate 		 */
5667c478bd9Sstevel@tonic-gate 		if (datalen > (IP_MAXPACKET - (sizeof (struct ip6_hdr) +
5677c478bd9Sstevel@tonic-gate 		    exthdr_len + ICMP6_MINLEN))) {
5687c478bd9Sstevel@tonic-gate 			Fprintf(stderr,
5697c478bd9Sstevel@tonic-gate 			    "%s: data size too large for IPv6 packet\n",
5707c478bd9Sstevel@tonic-gate 			    progname);
5717c478bd9Sstevel@tonic-gate 			num_v6 = 0;
5727c478bd9Sstevel@tonic-gate 		}
5737c478bd9Sstevel@tonic-gate 	}
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	if (family_input == AF_INET ||
5767c478bd9Sstevel@tonic-gate 	    (family_input == AF_UNSPEC && num_v4 != 0)) {
5777c478bd9Sstevel@tonic-gate 		size_t opt_len = 0;
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 		if (send_reply) {
5807c478bd9Sstevel@tonic-gate 			/*
5817c478bd9Sstevel@tonic-gate 			 * Includes 3 bytes code+ptr+len, the intermediate
5827c478bd9Sstevel@tonic-gate 			 * gateways, the actual and the effective target.
5837c478bd9Sstevel@tonic-gate 			 */
5847c478bd9Sstevel@tonic-gate 			opt_len = 3 +
5857c478bd9Sstevel@tonic-gate 			    (2 * num_gw + 2) * sizeof (struct in_addr);
5867c478bd9Sstevel@tonic-gate 		} else if (num_gw > 0) {
5877c478bd9Sstevel@tonic-gate 			opt_len = 3 + (num_gw + 1) * sizeof (struct in_addr);
5887c478bd9Sstevel@tonic-gate 		}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 		if (rr_option) {
5917c478bd9Sstevel@tonic-gate 			opt_len = MAX_IPOPTLEN;
5927c478bd9Sstevel@tonic-gate 		} else if (ts_option) {
5937c478bd9Sstevel@tonic-gate 			if ((ts_flag & 0x0f) <= IPOPT_TS_TSANDADDR) {
5947c478bd9Sstevel@tonic-gate 				opt_len = MAX_IPOPTLEN;
5957c478bd9Sstevel@tonic-gate 			} else {
5967c478bd9Sstevel@tonic-gate 				opt_len += IPOPT_MINOFF +
5977c478bd9Sstevel@tonic-gate 				    2 * sizeof (struct ipt_ta);
5987c478bd9Sstevel@tonic-gate 				/*
5997c478bd9Sstevel@tonic-gate 				 * Note: BSD/4.X is broken in their check so we
6007c478bd9Sstevel@tonic-gate 				 * have to  bump up this number by at least one.
6017c478bd9Sstevel@tonic-gate 				 */
6027c478bd9Sstevel@tonic-gate 				opt_len++;
6037c478bd9Sstevel@tonic-gate 			}
6047c478bd9Sstevel@tonic-gate 		}
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 		/* Round up to 4 byte boundary */
6077c478bd9Sstevel@tonic-gate 		if (opt_len & 0x3)
6087c478bd9Sstevel@tonic-gate 			opt_len = (opt_len & ~0x3) + 4;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 		if (datalen > (IP_MAXPACKET - (sizeof (struct ip) + opt_len +
6117c478bd9Sstevel@tonic-gate 		    ICMP_MINLEN))) {
6127c478bd9Sstevel@tonic-gate 			Fprintf(stderr,
6137c478bd9Sstevel@tonic-gate 			    "%s: data size too large for IPv4 packet\n",
6147c478bd9Sstevel@tonic-gate 			    progname);
6157c478bd9Sstevel@tonic-gate 			num_v4 = 0;
6167c478bd9Sstevel@tonic-gate 		}
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	if (num_v4 == 0 && num_v6 == 0) {
6207c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/* setup the sockets */
6247c478bd9Sstevel@tonic-gate 	if (num_v6 != 0) {
6257c478bd9Sstevel@tonic-gate 		if (!setup_socket(AF_INET6, &send_sock6, &recv_sock6,
6264c10bc16Spwernau 		    &if_index, &udp_src_port6, ai_nexthop))
6277c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
6287c478bd9Sstevel@tonic-gate 	}
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	if (num_v4 != 0) {
6317c478bd9Sstevel@tonic-gate 		if (!setup_socket(AF_INET, &send_sock, &recv_sock, &if_index,
6324c10bc16Spwernau 		    &udp_src_port, ai_nexthop))
6337c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	__priv_relinquish();
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	/*
6397c478bd9Sstevel@tonic-gate 	 * If sending back to ourself, add the mirror image of current
6407c478bd9Sstevel@tonic-gate 	 * gateways, so that the probes travel to and from the target
6417c478bd9Sstevel@tonic-gate 	 * by visiting the same gateways in reverse order.
6427c478bd9Sstevel@tonic-gate 	 */
6437c478bd9Sstevel@tonic-gate 	if (send_reply) {
6447c478bd9Sstevel@tonic-gate 		if (num_v6 != 0)
6457c478bd9Sstevel@tonic-gate 			mirror_gws(gw_IP_list6, AF_INET6);
6467c478bd9Sstevel@tonic-gate 		if (num_v4 != 0)
6477c478bd9Sstevel@tonic-gate 			mirror_gws(gw_IP_list, AF_INET);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 		/* We add 1 because we put the target as the middle gateway */
6507c478bd9Sstevel@tonic-gate 		eff_num_gw = 2 * num_gw + 1;
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	} else {
6537c478bd9Sstevel@tonic-gate 		eff_num_gw = num_gw;
6547c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	targetaddr_list = build_targetaddr_list(ai_dst, src_addr_list);
6577c478bd9Sstevel@tonic-gate 	current_targetaddr = targetaddr_list;
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	/*
6607c478bd9Sstevel@tonic-gate 	 * Set the starting_seq_num for the first targetaddr.
6617c478bd9Sstevel@tonic-gate 	 * If we are sending ICMP Echo Requests, the sequence number is same as
6627c478bd9Sstevel@tonic-gate 	 * ICMP sequence number, and it starts from zero. If we are sending UDP
6637c478bd9Sstevel@tonic-gate 	 * packets, the sequence number is the destination UDP port number,
6647c478bd9Sstevel@tonic-gate 	 * which starts from dest_port. At each probe, this sequence number is
6657c478bd9Sstevel@tonic-gate 	 * incremented by one.
6667c478bd9Sstevel@tonic-gate 	 * We set the starting_seq_num for first targetaddr here. The
6677c478bd9Sstevel@tonic-gate 	 * following ones will be set by looking at where we left with the last
6687c478bd9Sstevel@tonic-gate 	 * targetaddr.
6697c478bd9Sstevel@tonic-gate 	 */
6707c478bd9Sstevel@tonic-gate 	current_targetaddr->starting_seq_num = use_udp ? dest_port : 0;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	if (stats) {
6737c478bd9Sstevel@tonic-gate 		if (probe_all || !nflag) {
6747c478bd9Sstevel@tonic-gate 			Printf("PING %s: %d data bytes\n", targethost, datalen);
6757c478bd9Sstevel@tonic-gate 		} else {
6767c478bd9Sstevel@tonic-gate 			if (ai_dst->ai_family == AF_INET) {
677e11c3f44Smeem 				(void) inet_ntop(AF_INET,
678e11c3f44Smeem 				    &((struct sockaddr_in *)(void *)
6797c478bd9Sstevel@tonic-gate 				    ai_dst->ai_addr)->sin_addr,
680e11c3f44Smeem 				    abuf, sizeof (abuf));
6817c478bd9Sstevel@tonic-gate 			} else {
682e11c3f44Smeem 				(void) inet_ntop(AF_INET6,
683e11c3f44Smeem 				    &((struct sockaddr_in6 *)(void *)
6847c478bd9Sstevel@tonic-gate 				    ai_dst->ai_addr)->sin6_addr,
685e11c3f44Smeem 				    abuf, sizeof (abuf));
6867c478bd9Sstevel@tonic-gate 			}
687e11c3f44Smeem 			Printf("PING %s (%s): %d data bytes\n",
688e11c3f44Smeem 			    targethost, abuf, datalen);
6897c478bd9Sstevel@tonic-gate 		}
6907c478bd9Sstevel@tonic-gate 	}
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	/* Let's get things going */
6937c478bd9Sstevel@tonic-gate 	send_scheduled_probe();
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	/* SIGALRM is used to send the next scheduled probe */
6967c478bd9Sstevel@tonic-gate 	(void) sigset(SIGALRM, sigalrm_handler);
6977c478bd9Sstevel@tonic-gate 	schedule_sigalrm();
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	/*
7007c478bd9Sstevel@tonic-gate 	 * From now on, we'll always be listening to ICMP packets. As SIGALRM
7017c478bd9Sstevel@tonic-gate 	 * comes in, sigalrm_handler() will be invoked and send another
7027c478bd9Sstevel@tonic-gate 	 * probe.
7037c478bd9Sstevel@tonic-gate 	 */
7047c478bd9Sstevel@tonic-gate 	recv_icmp_packet(ai_dst, recv_sock6, recv_sock, udp_src_port6,
7057c478bd9Sstevel@tonic-gate 	    udp_src_port);
7067c478bd9Sstevel@tonic-gate 
7078c332a0dSja97890 	return (EXIT_SUCCESS);	/* should never come here */
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate /*
7117c478bd9Sstevel@tonic-gate  * Build the target IP address list. Use command line options and
7127c478bd9Sstevel@tonic-gate  * name lookup results returned from name server to determine which addresses
7137c478bd9Sstevel@tonic-gate  * to probe, how many times, in which order.
7147c478bd9Sstevel@tonic-gate  */
7157c478bd9Sstevel@tonic-gate static struct targetaddr *
build_targetaddr_list(struct addrinfo * ai_dst,union any_in_addr * src_addr_list)7167c478bd9Sstevel@tonic-gate build_targetaddr_list(struct addrinfo *ai_dst, union any_in_addr *src_addr_list)
7177c478bd9Sstevel@tonic-gate {
7187c478bd9Sstevel@tonic-gate 	struct targetaddr *head = NULL;
7197c478bd9Sstevel@tonic-gate 	struct targetaddr *targetaddr;
7207c478bd9Sstevel@tonic-gate 	struct targetaddr **nextp;
7217c478bd9Sstevel@tonic-gate 	int num_dst;
7227c478bd9Sstevel@tonic-gate 	int i;
7237c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	aip = ai_dst;
7267c478bd9Sstevel@tonic-gate 	if (probe_all)
7277c478bd9Sstevel@tonic-gate 		num_dst = num_v4 + num_v6;
7287c478bd9Sstevel@tonic-gate 	else
7297c478bd9Sstevel@tonic-gate 		num_dst = 1;
7307c478bd9Sstevel@tonic-gate 	num_targetaddrs = num_dst;
7317c478bd9Sstevel@tonic-gate 	nextp = &head;
7327c478bd9Sstevel@tonic-gate 	for (aip = ai_dst, i = 0; aip != NULL; aip = aip->ai_next, i++) {
7337c478bd9Sstevel@tonic-gate 		if (aip->ai_family == AF_INET && num_v4 != 0) {
7347c478bd9Sstevel@tonic-gate 			targetaddr = create_targetaddr_item(aip->ai_family,
7357c478bd9Sstevel@tonic-gate 			    (union any_in_addr *)
7367c478bd9Sstevel@tonic-gate 			    /* LINTED E_BAD_PTR_CAST_ALIGN */
7377c478bd9Sstevel@tonic-gate 			    &((struct sockaddr_in *)
7387c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin_addr,
7397c478bd9Sstevel@tonic-gate 			    &src_addr_list[i]);
7407c478bd9Sstevel@tonic-gate 		} else if (aip->ai_family == AF_INET6 && num_v6 != 0) {
7417c478bd9Sstevel@tonic-gate 			targetaddr = create_targetaddr_item(aip->ai_family,
7427c478bd9Sstevel@tonic-gate 			    (union any_in_addr *)
7437c478bd9Sstevel@tonic-gate 			    /* LINTED E_BAD_PTR_CAST_ALIGN */
7447c478bd9Sstevel@tonic-gate 			    &((struct sockaddr_in6 *)
7457c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin6_addr,
7467c478bd9Sstevel@tonic-gate 			    &src_addr_list[i]);
7477c478bd9Sstevel@tonic-gate 		} else {
7487c478bd9Sstevel@tonic-gate 			continue;
7497c478bd9Sstevel@tonic-gate 		}
7507c478bd9Sstevel@tonic-gate 		*nextp = targetaddr;
7517c478bd9Sstevel@tonic-gate 		nextp = &targetaddr->next;
7527c478bd9Sstevel@tonic-gate 		if (num_targetaddrs == 1)
7537c478bd9Sstevel@tonic-gate 			break;
7547c478bd9Sstevel@tonic-gate 	}
7557c478bd9Sstevel@tonic-gate 	if (npackets == 0 && stats)
7567c478bd9Sstevel@tonic-gate 		*nextp = head;	/* keep going indefinitely */
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	return (head);
7597c478bd9Sstevel@tonic-gate }
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate /*
7627c478bd9Sstevel@tonic-gate  * Given an address family, dst and src addresses, by also looking at the
7637c478bd9Sstevel@tonic-gate  * options provided at the command line, this function creates a targetaddr
7647c478bd9Sstevel@tonic-gate  * to be linked with others, forming a global targetaddr list. Each targetaddr
7657c478bd9Sstevel@tonic-gate  * item contains information about probes sent to a specific IP address.
7667c478bd9Sstevel@tonic-gate  */
7677c478bd9Sstevel@tonic-gate static struct targetaddr *
create_targetaddr_item(int family,union any_in_addr * dst_addr,union any_in_addr * src_addr)7687c478bd9Sstevel@tonic-gate create_targetaddr_item(int family, union any_in_addr *dst_addr,
7697c478bd9Sstevel@tonic-gate     union any_in_addr *src_addr)
7707c478bd9Sstevel@tonic-gate {
7717c478bd9Sstevel@tonic-gate 	struct targetaddr *targetaddr;
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	targetaddr = (struct targetaddr *)malloc(sizeof (struct targetaddr));
7747c478bd9Sstevel@tonic-gate 	if (targetaddr == NULL) {
7757c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc %s\n", progname, strerror(errno));
7767c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
7777c478bd9Sstevel@tonic-gate 	}
7787c478bd9Sstevel@tonic-gate 	targetaddr->family = family;
7797c478bd9Sstevel@tonic-gate 	targetaddr->dst_addr = *dst_addr;
7807c478bd9Sstevel@tonic-gate 	targetaddr->src_addr = *src_addr;
7817c478bd9Sstevel@tonic-gate 	if (stats) {
7827c478bd9Sstevel@tonic-gate 		/*
7837c478bd9Sstevel@tonic-gate 		 * npackets is only defined if we are in stats mode.
7847c478bd9Sstevel@tonic-gate 		 * npackets determines how many probes to send to each target
7857c478bd9Sstevel@tonic-gate 		 * IP address. npackets == 0 means send only 1 and move on to
7867c478bd9Sstevel@tonic-gate 		 * next target IP.
7877c478bd9Sstevel@tonic-gate 		 */
7887c478bd9Sstevel@tonic-gate 		if (npackets > 0)
7897c478bd9Sstevel@tonic-gate 			targetaddr->num_probes = npackets;
7907c478bd9Sstevel@tonic-gate 		else
7917c478bd9Sstevel@tonic-gate 			targetaddr->num_probes = 1;
7927c478bd9Sstevel@tonic-gate 	} else {
7937c478bd9Sstevel@tonic-gate 		targetaddr->num_probes = timeout;
7947c478bd9Sstevel@tonic-gate 	}
7957c478bd9Sstevel@tonic-gate 	targetaddr->num_sent = 0;
7967c478bd9Sstevel@tonic-gate 	targetaddr->got_reply = _B_FALSE;
7977c478bd9Sstevel@tonic-gate 	targetaddr->probing_done = _B_FALSE;
7987c478bd9Sstevel@tonic-gate 	targetaddr->starting_seq_num = 0; /* actual value will be set later */
7997c478bd9Sstevel@tonic-gate 	targetaddr->next = NULL;	/* actual value will be set later */
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	return (targetaddr);
8027c478bd9Sstevel@tonic-gate }
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate /*
8057c478bd9Sstevel@tonic-gate  * print "unknown host" message
8067c478bd9Sstevel@tonic-gate  */
8077c478bd9Sstevel@tonic-gate static void
print_unknown_host_msg(const char * protocol,const char * hostname)8087c478bd9Sstevel@tonic-gate print_unknown_host_msg(const char *protocol, const char *hostname)
8097c478bd9Sstevel@tonic-gate {
8107c478bd9Sstevel@tonic-gate 	Fprintf(stderr, "%s: unknown%s host %s\n", progname, protocol,
8117c478bd9Sstevel@tonic-gate 	    hostname);
8127c478bd9Sstevel@tonic-gate }
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate /*
8157c478bd9Sstevel@tonic-gate  * Resolve hostnames for the target host and gateways. Also, determine source
8167c478bd9Sstevel@tonic-gate  * addresses to use for each target address.
8177c478bd9Sstevel@tonic-gate  */
8187c478bd9Sstevel@tonic-gate static void
resolve_nodes(struct addrinfo ** ai_dstp,struct addrinfo ** ai_nexthopp,union any_in_addr ** src_addr_listp)8194c10bc16Spwernau resolve_nodes(struct addrinfo **ai_dstp, struct addrinfo **ai_nexthopp,
8204c10bc16Spwernau     union any_in_addr **src_addr_listp)
8217c478bd9Sstevel@tonic-gate {
8227c478bd9Sstevel@tonic-gate 	struct addrinfo *ai_dst = NULL;
8234c10bc16Spwernau 	struct addrinfo *ai_nexthop = NULL;
8247c478bd9Sstevel@tonic-gate 	struct addrinfo *aip = NULL;
8257c478bd9Sstevel@tonic-gate 	union any_in_addr *src_addr_list = NULL;
8267c478bd9Sstevel@tonic-gate 	int num_resolved_gw = 0;
8277c478bd9Sstevel@tonic-gate 	int num_resolved_gw6 = 0;
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	get_hostinfo(targethost, family_input, &ai_dst);
8307c478bd9Sstevel@tonic-gate 	if (ai_dst == NULL) {
8317c478bd9Sstevel@tonic-gate 		print_unknown_host_msg("", targethost);
8327c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
8337c478bd9Sstevel@tonic-gate 	}
8344c10bc16Spwernau 	if (nexthop != NULL) {
8354c10bc16Spwernau 		get_hostinfo(nexthop, family_input, &ai_nexthop);
8364c10bc16Spwernau 		if (ai_nexthop == NULL) {
8374c10bc16Spwernau 			print_unknown_host_msg("", nexthop);
8384c10bc16Spwernau 			exit(EXIT_FAILURE);
8394c10bc16Spwernau 		}
8404c10bc16Spwernau 	}
8417c478bd9Sstevel@tonic-gate 	/* Get a count of the v4 & v6 addresses */
8427c478bd9Sstevel@tonic-gate 	for (aip = ai_dst; aip != NULL; aip = aip->ai_next) {
8437c478bd9Sstevel@tonic-gate 		switch (aip->ai_family) {
8447c478bd9Sstevel@tonic-gate 		case AF_INET:
8457c478bd9Sstevel@tonic-gate 			num_v4++;
8467c478bd9Sstevel@tonic-gate 			break;
8477c478bd9Sstevel@tonic-gate 		case AF_INET6:
8487c478bd9Sstevel@tonic-gate 			num_v6++;
8497c478bd9Sstevel@tonic-gate 			break;
8507c478bd9Sstevel@tonic-gate 		}
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	if (family_input == AF_UNSPEC && !probe_all) {
8547c478bd9Sstevel@tonic-gate 		family_input = ai_dst->ai_family;
8557c478bd9Sstevel@tonic-gate 	}
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	/* resolve gateways */
8587c478bd9Sstevel@tonic-gate 	if (num_gw > 0) {
8597c478bd9Sstevel@tonic-gate 		get_gwaddrs(gw_list, family_input, gw_IP_list, gw_IP_list6,
8607c478bd9Sstevel@tonic-gate 		    &num_resolved_gw, &num_resolved_gw6);
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 		/* we couldn't resolve a gateway as an IPv6 host */
8637c478bd9Sstevel@tonic-gate 		if (num_resolved_gw6 != num_gw && num_v6 != 0 &&
8647c478bd9Sstevel@tonic-gate 		    (family_input == AF_INET6 || family_input == AF_UNSPEC)) {
8657c478bd9Sstevel@tonic-gate 			print_unknown_host_msg(" IPv6",
8667c478bd9Sstevel@tonic-gate 			    gw_list[num_resolved_gw6]);
8677c478bd9Sstevel@tonic-gate 			num_v6 = 0;
8687c478bd9Sstevel@tonic-gate 		}
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 		/* we couldn't resolve a gateway as an IPv4 host */
8717c478bd9Sstevel@tonic-gate 		if (num_resolved_gw != num_gw && num_v4 != 0 &&
8727c478bd9Sstevel@tonic-gate 		    (family_input == AF_INET || family_input == AF_UNSPEC)) {
8737c478bd9Sstevel@tonic-gate 			print_unknown_host_msg(" IPv4",
8747c478bd9Sstevel@tonic-gate 			    gw_list[num_resolved_gw]);
8757c478bd9Sstevel@tonic-gate 			num_v4 = 0;
8767c478bd9Sstevel@tonic-gate 		}
8777c478bd9Sstevel@tonic-gate 	}
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 	if (num_v4 == 0 && num_v6 == 0)
8807c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	select_all_src_addrs(&src_addr_list, ai_dst, gw_IP_list, gw_IP_list6);
8837c478bd9Sstevel@tonic-gate 	*ai_dstp = ai_dst;
8844c10bc16Spwernau 	*ai_nexthopp = ai_nexthop;
8857c478bd9Sstevel@tonic-gate 	*src_addr_listp = src_addr_list;
8867c478bd9Sstevel@tonic-gate }
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate /*
8897c478bd9Sstevel@tonic-gate  * Resolve the gateway names, splitting results into v4 and v6 lists.
8907c478bd9Sstevel@tonic-gate  * Gateway addresses are added to the appropriate passed-in array; the
8917c478bd9Sstevel@tonic-gate  * number of resolved gateways for each af is returned in resolved[6].
8927c478bd9Sstevel@tonic-gate  * Assumes that passed-in arrays are large enough for MAX_GWS[6] addrs
8937c478bd9Sstevel@tonic-gate  * and resolved[6] ptrs are non-null; ignores array and counter if the
8947c478bd9Sstevel@tonic-gate  * address family param makes them irrelevant.
8957c478bd9Sstevel@tonic-gate  */
8967c478bd9Sstevel@tonic-gate static void
get_gwaddrs(char ** gw_list,int family,union any_in_addr * gwIPlist,union any_in_addr * gwIPlist6,int * resolved,int * resolved6)8977c478bd9Sstevel@tonic-gate get_gwaddrs(char **gw_list, int family, union any_in_addr *gwIPlist,
8987c478bd9Sstevel@tonic-gate     union any_in_addr *gwIPlist6, int *resolved, int *resolved6)
8997c478bd9Sstevel@tonic-gate {
9007c478bd9Sstevel@tonic-gate 	int i;
9017c478bd9Sstevel@tonic-gate 	boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE;
9027c478bd9Sstevel@tonic-gate 	struct addrinfo	*ai = NULL;
9037c478bd9Sstevel@tonic-gate 	struct addrinfo	*aip = NULL;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	*resolved = *resolved6 = 0;
9067c478bd9Sstevel@tonic-gate 	switch (family) {
9077c478bd9Sstevel@tonic-gate 	case AF_UNSPEC:
9087c478bd9Sstevel@tonic-gate 		break;
9097c478bd9Sstevel@tonic-gate 	case AF_INET:
9107c478bd9Sstevel@tonic-gate 		check_v6 = _B_FALSE;
9117c478bd9Sstevel@tonic-gate 		break;
9127c478bd9Sstevel@tonic-gate 	case AF_INET6:
9137c478bd9Sstevel@tonic-gate 		check_v4 = _B_FALSE;
9147c478bd9Sstevel@tonic-gate 		break;
9157c478bd9Sstevel@tonic-gate 	default:
9167c478bd9Sstevel@tonic-gate 		return;
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	if (check_v4 && num_gw >= MAX_GWS) {
9207c478bd9Sstevel@tonic-gate 		check_v4 = _B_FALSE;
9217c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many IPv4 gateways\n", progname);
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate 	if (check_v6 && num_gw > MAX_GWS6) {
9247c478bd9Sstevel@tonic-gate 		check_v6 = _B_FALSE;
9257c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many IPv6 gateways\n", progname);
9267c478bd9Sstevel@tonic-gate 	}
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_gw; i++) {
9297c478bd9Sstevel@tonic-gate 		if (!check_v4 && !check_v6)
9307c478bd9Sstevel@tonic-gate 			return;
9317c478bd9Sstevel@tonic-gate 		get_hostinfo(gw_list[i], family, &ai);
9327c478bd9Sstevel@tonic-gate 		if (ai == NULL)
9337c478bd9Sstevel@tonic-gate 			return;
9347c478bd9Sstevel@tonic-gate 		if (check_v4 && num_v4 != 0) {
9357c478bd9Sstevel@tonic-gate 			for (aip = ai; aip != NULL; aip = aip->ai_next) {
9367c478bd9Sstevel@tonic-gate 				if (aip->ai_family == AF_INET) {
9377c478bd9Sstevel@tonic-gate 					/* LINTED E_BAD_PTR_CAST_ALIGN */
9387c478bd9Sstevel@tonic-gate 					bcopy(&((struct sockaddr_in *)
9397c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin_addr,
9407c478bd9Sstevel@tonic-gate 					    &gwIPlist[i].addr,
9417c478bd9Sstevel@tonic-gate 					    aip->ai_addrlen);
9427c478bd9Sstevel@tonic-gate 					(*resolved)++;
9437c478bd9Sstevel@tonic-gate 					break;
9447c478bd9Sstevel@tonic-gate 				}
9457c478bd9Sstevel@tonic-gate 			}
9467c478bd9Sstevel@tonic-gate 		} else if (check_v4) {
9477c478bd9Sstevel@tonic-gate 			check_v4 = _B_FALSE;
9487c478bd9Sstevel@tonic-gate 		}
9497c478bd9Sstevel@tonic-gate 		if (check_v6 && num_v6 != 0) {
9507c478bd9Sstevel@tonic-gate 			for (aip = ai; aip != NULL; aip = aip->ai_next) {
9517c478bd9Sstevel@tonic-gate 				if (aip->ai_family == AF_INET6) {
9527c478bd9Sstevel@tonic-gate 					/* LINTED E_BAD_PTR_CAST_ALIGN */
9537c478bd9Sstevel@tonic-gate 					bcopy(&((struct sockaddr_in6 *)
9547c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin6_addr,
9557c478bd9Sstevel@tonic-gate 					    &gwIPlist6[i].addr6,
9567c478bd9Sstevel@tonic-gate 					    aip->ai_addrlen);
9577c478bd9Sstevel@tonic-gate 					(*resolved6)++;
9587c478bd9Sstevel@tonic-gate 					break;
9597c478bd9Sstevel@tonic-gate 				}
9607c478bd9Sstevel@tonic-gate 			}
9617c478bd9Sstevel@tonic-gate 		} else if (check_v6) {
9627c478bd9Sstevel@tonic-gate 			check_v6 = _B_FALSE;
9637c478bd9Sstevel@tonic-gate 		}
9647c478bd9Sstevel@tonic-gate 	}
9657c478bd9Sstevel@tonic-gate 	freeaddrinfo(ai);
9667c478bd9Sstevel@tonic-gate }
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate /*
9697c478bd9Sstevel@tonic-gate  * Given the list of gateways, extends the list with its mirror image. This is
9707c478bd9Sstevel@tonic-gate  * used when -l/-S is used. The middle gateway will be the target address. We'll
9717c478bd9Sstevel@tonic-gate  * leave it blank for now.
9727c478bd9Sstevel@tonic-gate  */
9737c478bd9Sstevel@tonic-gate static void
mirror_gws(union any_in_addr * gwIPlist,int family)9747c478bd9Sstevel@tonic-gate mirror_gws(union any_in_addr *gwIPlist, int family)
9757c478bd9Sstevel@tonic-gate {
9767c478bd9Sstevel@tonic-gate 	int effective_num_gw;
9777c478bd9Sstevel@tonic-gate 	int i;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	/* We add 1 because we put the target as the middle gateway */
9807c478bd9Sstevel@tonic-gate 	effective_num_gw = 2 * num_gw + 1;
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	if ((family == AF_INET && effective_num_gw >= MAX_GWS) ||
9837c478bd9Sstevel@tonic-gate 	    (family == AF_INET6 && effective_num_gw > MAX_GWS6)) {
9847c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many %s gateways\n",
9857c478bd9Sstevel@tonic-gate 		    progname, (family == AF_INET) ? "IPv4" : "IPv6");
9867c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
9877c478bd9Sstevel@tonic-gate 	}
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_gw; i++)
9907c478bd9Sstevel@tonic-gate 		gwIPlist[num_gw + i + 1].addr6 = gwIPlist[num_gw - i - 1].addr6;
9917c478bd9Sstevel@tonic-gate }
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate /*
9947c478bd9Sstevel@tonic-gate  * Given IP address or hostname, return addrinfo list.
9957c478bd9Sstevel@tonic-gate  * Assumes that addrinfo ** ptr is non-null.
9967c478bd9Sstevel@tonic-gate  */
9977c478bd9Sstevel@tonic-gate static void
get_hostinfo(char * host,int family,struct addrinfo ** aipp)9987c478bd9Sstevel@tonic-gate get_hostinfo(char *host, int family, struct addrinfo **aipp)
9997c478bd9Sstevel@tonic-gate {
10007c478bd9Sstevel@tonic-gate 	struct addrinfo hints, *ai;
10017c478bd9Sstevel@tonic-gate 	struct in6_addr addr6;
10027c478bd9Sstevel@tonic-gate 	struct in_addr addr;
10037c478bd9Sstevel@tonic-gate 	boolean_t broadcast;		/* is this 255.255.255.255? */
10047c478bd9Sstevel@tonic-gate 	char tmp_buf[INET6_ADDRSTRLEN];
10057c478bd9Sstevel@tonic-gate 	int rc;
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	/* check if broadcast */
10087c478bd9Sstevel@tonic-gate 	if (strcmp(host, "255.255.255.255") == 0)
10097c478bd9Sstevel@tonic-gate 		broadcast = _B_TRUE;
10107c478bd9Sstevel@tonic-gate 	else
10117c478bd9Sstevel@tonic-gate 		broadcast = _B_FALSE;
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	/* check if IPv4-mapped address or broadcast */
10147c478bd9Sstevel@tonic-gate 	if (((inet_pton(AF_INET6, host, &addr6) > 0) &&
10157c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_V4MAPPED(&addr6)) || broadcast) {
10167c478bd9Sstevel@tonic-gate 		if (!broadcast) {
10177c478bd9Sstevel@tonic-gate 			/*
10187c478bd9Sstevel@tonic-gate 			 * Peel off the "mapping" stuff, leaving 32 bit IPv4
10197c478bd9Sstevel@tonic-gate 			 * address.
10207c478bd9Sstevel@tonic-gate 			 */
10217c478bd9Sstevel@tonic-gate 			IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 			/* convert it back to a string */
10247c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET, (void *)&addr, tmp_buf,
10257c478bd9Sstevel@tonic-gate 			    sizeof (tmp_buf));
10267c478bd9Sstevel@tonic-gate 			/*
10277c478bd9Sstevel@tonic-gate 			 * Now the host is an IPv4 address.
10287c478bd9Sstevel@tonic-gate 			 * Since it previously was a v4 mapped v6 address
10297c478bd9Sstevel@tonic-gate 			 * we can be sure that the size of buffer 'host'
10307c478bd9Sstevel@tonic-gate 			 * is large enough to contain the associated v4
10317c478bd9Sstevel@tonic-gate 			 * address and so we don't need to use a strn/lcpy
10327c478bd9Sstevel@tonic-gate 			 * here.
10337c478bd9Sstevel@tonic-gate 			 */
10347c478bd9Sstevel@tonic-gate 			(void) strcpy(host, tmp_buf);
10357c478bd9Sstevel@tonic-gate 		}
10367c478bd9Sstevel@tonic-gate 		/*
10377c478bd9Sstevel@tonic-gate 		 * If it's a broadcast address, it cannot be an IPv6 address.
10387c478bd9Sstevel@tonic-gate 		 * Also, if it's a mapped address, we convert it into IPv4
10397c478bd9Sstevel@tonic-gate 		 * address because ping will send and receive IPv4 packets for
10407c478bd9Sstevel@tonic-gate 		 * that address. Therefore, it's a failure case to ask
10417c478bd9Sstevel@tonic-gate 		 * get_hostinfo() to treat a broadcast or a mapped address
10427c478bd9Sstevel@tonic-gate 		 * as an IPv6 address.
10437c478bd9Sstevel@tonic-gate 		 */
10447c478bd9Sstevel@tonic-gate 		if (family == AF_INET6) {
10457c478bd9Sstevel@tonic-gate 			return;
10467c478bd9Sstevel@tonic-gate 		}
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	(void) memset(&hints, 0, sizeof (hints));
10507c478bd9Sstevel@tonic-gate 	hints.ai_family = family;
10517c478bd9Sstevel@tonic-gate 	hints.ai_flags = AI_ADDRCONFIG;
10527c478bd9Sstevel@tonic-gate 	rc = getaddrinfo(host, NULL, &hints, &ai);
10537c478bd9Sstevel@tonic-gate 	if (rc != 0) {
10547c478bd9Sstevel@tonic-gate 		if (rc != EAI_NONAME)
10557c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: getaddrinfo: %s\n", progname,
10567c478bd9Sstevel@tonic-gate 			    gai_strerror(rc));
10577c478bd9Sstevel@tonic-gate 		return;
10587c478bd9Sstevel@tonic-gate 	}
10597c478bd9Sstevel@tonic-gate 	*aipp = ai;
10607c478bd9Sstevel@tonic-gate }
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate /*
10637c478bd9Sstevel@tonic-gate  * For each IP address of the target host, determine a source address to use.
10647c478bd9Sstevel@tonic-gate  */
10657c478bd9Sstevel@tonic-gate static void
select_all_src_addrs(union any_in_addr ** src_addr_list,struct addrinfo * ai,union any_in_addr * gwv4,union any_in_addr * gwv6)10667c478bd9Sstevel@tonic-gate select_all_src_addrs(union any_in_addr **src_addr_list, struct addrinfo *ai,
10677c478bd9Sstevel@tonic-gate     union any_in_addr *gwv4, union any_in_addr *gwv6)
10687c478bd9Sstevel@tonic-gate {
10697c478bd9Sstevel@tonic-gate 	union any_in_addr *list;
10707c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
10717c478bd9Sstevel@tonic-gate 	int num_dst = 1;
10727c478bd9Sstevel@tonic-gate 	int i;
10737c478bd9Sstevel@tonic-gate 
1074e11c3f44Smeem 	if (probe_all) {
1075e11c3f44Smeem 		for (aip = ai; aip->ai_next != NULL; aip = aip->ai_next)
1076e11c3f44Smeem 			num_dst++;
1077e11c3f44Smeem 	}
10787c478bd9Sstevel@tonic-gate 
1079e11c3f44Smeem 	list = calloc((size_t)num_dst, sizeof (union any_in_addr));
10807c478bd9Sstevel@tonic-gate 	if (list == NULL) {
10817c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno));
10827c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	/*
10867c478bd9Sstevel@tonic-gate 	 * If there's a gateway, a routing header as a consequence, our kernel
10877c478bd9Sstevel@tonic-gate 	 * picks the source address based on the first hop address, rather than
10887c478bd9Sstevel@tonic-gate 	 * final destination address.
10897c478bd9Sstevel@tonic-gate 	 */
10907c478bd9Sstevel@tonic-gate 	if (num_gw > 0) {
10917c478bd9Sstevel@tonic-gate 		if (ai->ai_family == AF_INET)
10927c478bd9Sstevel@tonic-gate 			select_src_addr(gwv4, ai->ai_family, &list[0]);
10937c478bd9Sstevel@tonic-gate 		else
10947c478bd9Sstevel@tonic-gate 			select_src_addr(gwv6, ai->ai_family, &list[0]);
10957c478bd9Sstevel@tonic-gate 		/*
10967c478bd9Sstevel@tonic-gate 		 * Since the first gateway address is fixed, we'll use the same
10977c478bd9Sstevel@tonic-gate 		 * src address for every different final destination address
10987c478bd9Sstevel@tonic-gate 		 * we send to.
10997c478bd9Sstevel@tonic-gate 		 */
11007c478bd9Sstevel@tonic-gate 		for (i = 1; i < num_dst; i++)
11017c478bd9Sstevel@tonic-gate 			list[i] = list[0];
11027c478bd9Sstevel@tonic-gate 	} else {
11037c478bd9Sstevel@tonic-gate 		/*
11047c478bd9Sstevel@tonic-gate 		 * Although something like 'ping -l host' results in a routing
11057c478bd9Sstevel@tonic-gate 		 * header, the first gateway address is the target host's
11067c478bd9Sstevel@tonic-gate 		 * address. Therefore, as far as src address selection goes,
11077c478bd9Sstevel@tonic-gate 		 * the result is same as having no routing header.
11087c478bd9Sstevel@tonic-gate 		 */
11097c478bd9Sstevel@tonic-gate 		for (i = 0, aip = ai; i < num_dst && aip != NULL;
11107c478bd9Sstevel@tonic-gate 		    i++, aip = aip->ai_next) {
11117c478bd9Sstevel@tonic-gate 			if (aip->ai_family == AF_INET) {
11127c478bd9Sstevel@tonic-gate 				if (num_v4 != 0) {
11137c478bd9Sstevel@tonic-gate 					select_src_addr((union any_in_addr *)
11147c478bd9Sstevel@tonic-gate 					    /* LINTED E_BAD_PTR_CAST_ALIGN */
11157c478bd9Sstevel@tonic-gate 					    &((struct sockaddr_in *)
11167c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin_addr,
11177c478bd9Sstevel@tonic-gate 					    aip->ai_family,
11187c478bd9Sstevel@tonic-gate 					    &list[i]);
11197c478bd9Sstevel@tonic-gate 				}
11207c478bd9Sstevel@tonic-gate 			} else {
11217c478bd9Sstevel@tonic-gate 				if (num_v6 != 0) {
11227c478bd9Sstevel@tonic-gate 					select_src_addr((union any_in_addr *)
11237c478bd9Sstevel@tonic-gate 					    /* LINTED E_BAD_PTR_CAST_ALIGN */
11247c478bd9Sstevel@tonic-gate 					    &((struct sockaddr_in6 *)
11257c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin6_addr,
11267c478bd9Sstevel@tonic-gate 					    aip->ai_family,
11277c478bd9Sstevel@tonic-gate 					    &list[i]);
11287c478bd9Sstevel@tonic-gate 				}
11297c478bd9Sstevel@tonic-gate 			}
11307c478bd9Sstevel@tonic-gate 		}
11317c478bd9Sstevel@tonic-gate 	}
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	*src_addr_list = list;
11347c478bd9Sstevel@tonic-gate }
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate /*
11377c478bd9Sstevel@tonic-gate  * For a given destination address, determine a source address to use.
11387c478bd9Sstevel@tonic-gate  * Returns wildcard address if it cannot determine the source address.
11397c478bd9Sstevel@tonic-gate  */
11407c478bd9Sstevel@tonic-gate static void
select_src_addr(union any_in_addr * dst_addr,int family,union any_in_addr * src_addr)11417c478bd9Sstevel@tonic-gate select_src_addr(union any_in_addr *dst_addr, int family,
11427c478bd9Sstevel@tonic-gate     union any_in_addr *src_addr)
11437c478bd9Sstevel@tonic-gate {
11447c478bd9Sstevel@tonic-gate 	struct sockaddr *sock;
11457c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
11467c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
11477c478bd9Sstevel@tonic-gate 	int tmp_fd;
11487c478bd9Sstevel@tonic-gate 	size_t sock_len;
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 	sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6));
11517c478bd9Sstevel@tonic-gate 	if (sock == NULL) {
11527c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
11537c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
11547c478bd9Sstevel@tonic-gate 	}
11557c478bd9Sstevel@tonic-gate 	(void) bzero(sock, sizeof (struct sockaddr_in6));
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
11587c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
11597c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
11607c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
11617c478bd9Sstevel@tonic-gate 		sin->sin_addr = dst_addr->addr;
11627c478bd9Sstevel@tonic-gate 		sin->sin_port = IPPORT_ECHO;	/* port shouldn't be 0 */
11637c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in);
11647c478bd9Sstevel@tonic-gate 	} else {
11657c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
11667c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
11677c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
11687c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = dst_addr->addr6;
11697c478bd9Sstevel@tonic-gate 		sin6->sin6_port = IPPORT_ECHO;	/* port shouldn't be 0 */
11707c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in6);
11717c478bd9Sstevel@tonic-gate 	}
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	/* open a UDP socket */
11747c478bd9Sstevel@tonic-gate 	if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) {
11757c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: udp socket: %s\n", progname,
11767c478bd9Sstevel@tonic-gate 		    strerror(errno));
11777c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
11787c478bd9Sstevel@tonic-gate 	}
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 	/* connect it */
11817c478bd9Sstevel@tonic-gate 	if (connect(tmp_fd, sock, sock_len) < 0) {
11827c478bd9Sstevel@tonic-gate 		/*
11837c478bd9Sstevel@tonic-gate 		 * If there's no route to the destination, this connect() call
11847c478bd9Sstevel@tonic-gate 		 * fails. We just return all-zero (wildcard) as the source
11857c478bd9Sstevel@tonic-gate 		 * address, so that user can get to see "no route to dest"
11867c478bd9Sstevel@tonic-gate 		 * message, as it'll try to send the probe packet out and will
11877c478bd9Sstevel@tonic-gate 		 * receive ICMP unreachable.
11887c478bd9Sstevel@tonic-gate 		 */
11897c478bd9Sstevel@tonic-gate 		if (family == AF_INET)
11907c478bd9Sstevel@tonic-gate 			src_addr->addr.s_addr = INADDR_ANY;
11917c478bd9Sstevel@tonic-gate 		else
11927c478bd9Sstevel@tonic-gate 			src_addr->addr6 = in6addr_any;
11937c478bd9Sstevel@tonic-gate 		free(sock);
11947c478bd9Sstevel@tonic-gate 		return;
11957c478bd9Sstevel@tonic-gate 	}
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	/* get the local sock info */
11987c478bd9Sstevel@tonic-gate 	if (getsockname(tmp_fd, sock, &sock_len) < 0) {
11997c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: getsockname: %s\n", progname,
12007c478bd9Sstevel@tonic-gate 		    strerror(errno));
12017c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
12027c478bd9Sstevel@tonic-gate 	}
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
12057c478bd9Sstevel@tonic-gate 		src_addr->addr = sin->sin_addr;
12067c478bd9Sstevel@tonic-gate 	} else {
12077c478bd9Sstevel@tonic-gate 		src_addr->addr6 = sin6->sin6_addr;
12087c478bd9Sstevel@tonic-gate 	}
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 	(void) close(tmp_fd);
12117c478bd9Sstevel@tonic-gate 	free(sock);
12127c478bd9Sstevel@tonic-gate }
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate /*
12154c10bc16Spwernau  * Set the IP_NEXTHOP/IPV6_NEXTHOP socket option.
12164c10bc16Spwernau  * exits on failure
12174c10bc16Spwernau  */
12184c10bc16Spwernau static void
set_nexthop(int family,struct addrinfo * ai_nexthop,int sock)12194c10bc16Spwernau set_nexthop(int family, struct addrinfo	*ai_nexthop, int sock)
12204c10bc16Spwernau {
12214c10bc16Spwernau 	if (family == AF_INET) {
12224c10bc16Spwernau 		ipaddr_t nh;
12234c10bc16Spwernau 
12244c10bc16Spwernau 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12254c10bc16Spwernau 		nh = ((struct sockaddr_in *)ai_nexthop->
12264c10bc16Spwernau 		    ai_addr)->sin_addr.s_addr;
12274c10bc16Spwernau 
1228f4b3ec61Sdh155122 		/* now we need the sys_ip_config privilege */
12294c10bc16Spwernau 		(void) __priv_bracket(PRIV_ON);
12304c10bc16Spwernau 		if (setsockopt(sock, IPPROTO_IP, IP_NEXTHOP,
12314c10bc16Spwernau 		    &nh, sizeof (ipaddr_t)) < 0) {
12321da45818Spwernau 			if (errno == EPERM)
12331da45818Spwernau 				Fprintf(stderr, "%s: Insufficient privilege "
12341da45818Spwernau 				    "to specify IPv4 nexthop router.\n",
12351da45818Spwernau 				    progname);
12361da45818Spwernau 			else
12374c10bc16Spwernau 				Fprintf(stderr, "%s: setsockopt %s\n",
12384c10bc16Spwernau 				    progname, strerror(errno));
12394c10bc16Spwernau 			exit(EXIT_FAILURE);
12404c10bc16Spwernau 		}
12414c10bc16Spwernau 		(void) __priv_bracket(PRIV_OFF);
12424c10bc16Spwernau 		/* revert to non-privileged user */
12434c10bc16Spwernau 	} else {
12444c10bc16Spwernau 		struct sockaddr_in6 *nh;
12454c10bc16Spwernau 
12464c10bc16Spwernau 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12474c10bc16Spwernau 		nh = (struct sockaddr_in6 *)ai_nexthop->
12484c10bc16Spwernau 		    ai_addr;
12494c10bc16Spwernau 
12504c10bc16Spwernau 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_NEXTHOP,
12514c10bc16Spwernau 		    nh, sizeof (struct sockaddr_in6)) < 0) {
12524c10bc16Spwernau 			Fprintf(stderr, "%s: setsockopt %s\n",
12534c10bc16Spwernau 			    progname, strerror(errno));
12544c10bc16Spwernau 			exit(EXIT_FAILURE);
12554c10bc16Spwernau 		}
12564c10bc16Spwernau 	}
12574c10bc16Spwernau }
12584c10bc16Spwernau 
12594c10bc16Spwernau /*
12607c478bd9Sstevel@tonic-gate  * Setup the socket for the given address family.
12617c478bd9Sstevel@tonic-gate  * Returns _B_TRUE on success, _B_FALSE on failure. Failure is the case when no
12627c478bd9Sstevel@tonic-gate  * interface can be found, or the specified interface (-i) is not found. On
12637c478bd9Sstevel@tonic-gate  * library call failures, it exit()s.
12647c478bd9Sstevel@tonic-gate  */
12657c478bd9Sstevel@tonic-gate static boolean_t
setup_socket(int family,int * send_sockp,int * recv_sockp,int * if_index,ushort_t * udp_src_port,struct addrinfo * ai_nexthop)12667c478bd9Sstevel@tonic-gate setup_socket(int family, int *send_sockp, int *recv_sockp, int *if_index,
12674c10bc16Spwernau     ushort_t *udp_src_port, struct addrinfo *ai_nexthop)
12687c478bd9Sstevel@tonic-gate {
12697c478bd9Sstevel@tonic-gate 	int send_sock;
12707c478bd9Sstevel@tonic-gate 	int recv_sock;
12717c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
12727c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
12737c478bd9Sstevel@tonic-gate 	struct sockaddr *sp;
1274a356273cSpwernau 	struct ipsec_req req;
12757c478bd9Sstevel@tonic-gate 	size_t slen;
12767c478bd9Sstevel@tonic-gate 	int on = 1;
12777c478bd9Sstevel@tonic-gate 	uchar_t char_op;
12787c478bd9Sstevel@tonic-gate 	int int_op;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	/* now we need the net_icmpaccess privilege */
12817c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 	recv_sock = socket(family, SOCK_RAW,
12847c478bd9Sstevel@tonic-gate 	    (family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	if (recv_sock < 0) {
12877c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: socket %s\n", progname, strerror(errno));
12887c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
12897c478bd9Sstevel@tonic-gate 	}
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	/* revert to non-privileged user after opening sockets */
12927c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_OFF);
12937c478bd9Sstevel@tonic-gate 
1294a356273cSpwernau 	if (bypass) {
1295a356273cSpwernau 		(void) memset(&req, 0, sizeof (req));
1296a356273cSpwernau 		req.ipsr_ah_req = IPSEC_PREF_NEVER;
1297a356273cSpwernau 		req.ipsr_esp_req = IPSEC_PREF_NEVER;
1298a356273cSpwernau 
1299a356273cSpwernau 		if (setsockopt(recv_sock, (family == AF_INET) ? IPPROTO_IP :
1300a356273cSpwernau 		    IPPROTO_IPV6, IP_SEC_OPT, &req, sizeof (req)) < 0) {
13015086f56fSPaul Wernau 			switch (errno) {
13025086f56fSPaul Wernau 			case EPROTONOSUPPORT:
13035086f56fSPaul Wernau 				/*
13045086f56fSPaul Wernau 				 * No IPsec subsystem or policy loaded.
13055086f56fSPaul Wernau 				 * Bypass implicitly allowed.
13065086f56fSPaul Wernau 				 */
13075086f56fSPaul Wernau 				break;
13085086f56fSPaul Wernau 			case EPERM:
1309a356273cSpwernau 				Fprintf(stderr, "%s: Insufficient privilege "
1310a356273cSpwernau 				    "to bypass IPsec policy.\n", progname);
13115086f56fSPaul Wernau 				exit(EXIT_FAILURE);
13125086f56fSPaul Wernau 				break;
13135086f56fSPaul Wernau 			default:
1314a356273cSpwernau 				Fprintf(stderr, "%s: setsockopt %s\n", progname,
1315a356273cSpwernau 				    strerror(errno));
1316a356273cSpwernau 				exit(EXIT_FAILURE);
13175086f56fSPaul Wernau 				break;
13185086f56fSPaul Wernau 			}
1319a356273cSpwernau 		}
1320a356273cSpwernau 	}
1321a356273cSpwernau 
13227c478bd9Sstevel@tonic-gate 	/*
13237c478bd9Sstevel@tonic-gate 	 * We always receive on raw icmp socket. But the sending socket can be
13247c478bd9Sstevel@tonic-gate 	 * raw icmp or udp, depending on the use of -U flag.
13257c478bd9Sstevel@tonic-gate 	 */
13267c478bd9Sstevel@tonic-gate 	if (use_udp) {
13277c478bd9Sstevel@tonic-gate 		send_sock = socket(family, SOCK_DGRAM, IPPROTO_UDP);
13287c478bd9Sstevel@tonic-gate 		if (send_sock < 0) {
13297c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: socket %s\n", progname,
13307c478bd9Sstevel@tonic-gate 			    strerror(errno));
13317c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
13327c478bd9Sstevel@tonic-gate 		}
13337c478bd9Sstevel@tonic-gate 
1334a356273cSpwernau 		if (bypass) {
1335a356273cSpwernau 			if (setsockopt(send_sock, (family == AF_INET) ?
1336a356273cSpwernau 			    IPPROTO_IP : IPPROTO_IPV6, IP_SEC_OPT, &req,
1337a356273cSpwernau 			    sizeof (req)) < 0) {
13385086f56fSPaul Wernau 				switch (errno) {
13395086f56fSPaul Wernau 				case EPROTONOSUPPORT:
13405086f56fSPaul Wernau 					/*
13415086f56fSPaul Wernau 					 * No IPsec subsystem or policy loaded.
13425086f56fSPaul Wernau 					 * Bypass implicitly allowed.
13435086f56fSPaul Wernau 					 */
13445086f56fSPaul Wernau 					break;
13455086f56fSPaul Wernau 				case EPERM:
1346a356273cSpwernau 					Fprintf(stderr, "%s: Insufficient "
1347a356273cSpwernau 					    "privilege to bypass IPsec "
1348a356273cSpwernau 					    "policy.\n", progname);
13495086f56fSPaul Wernau 					exit(EXIT_FAILURE);
13505086f56fSPaul Wernau 					break;
13515086f56fSPaul Wernau 				default:
1352a356273cSpwernau 					Fprintf(stderr, "%s: setsockopt %s\n",
1353a356273cSpwernau 					    progname, strerror(errno));
1354a356273cSpwernau 					exit(EXIT_FAILURE);
13555086f56fSPaul Wernau 					break;
13565086f56fSPaul Wernau 				}
1357a356273cSpwernau 			}
1358a356273cSpwernau 		}
1359a356273cSpwernau 
13607c478bd9Sstevel@tonic-gate 		/*
13617c478bd9Sstevel@tonic-gate 		 * In order to distinguish replies to our UDP probes from
13627c478bd9Sstevel@tonic-gate 		 * other pings', we need to know our source port number.
13637c478bd9Sstevel@tonic-gate 		 */
13647c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
13657c478bd9Sstevel@tonic-gate 			sp = (struct sockaddr *)&sin;
13667c478bd9Sstevel@tonic-gate 			slen = sizeof (sin);
13677c478bd9Sstevel@tonic-gate 		} else {
13687c478bd9Sstevel@tonic-gate 			sp = (struct sockaddr *)&sin6;
13697c478bd9Sstevel@tonic-gate 			slen = sizeof (sin6);
13707c478bd9Sstevel@tonic-gate 		}
13717c478bd9Sstevel@tonic-gate 		bzero(sp, slen);
13727c478bd9Sstevel@tonic-gate 		sp->sa_family = family;
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 		/* Let's bind() send_sock to wildcard address and port */
13757c478bd9Sstevel@tonic-gate 		if (bind(send_sock, sp, slen) < 0) {
13767c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: bind %s\n", progname,
13777c478bd9Sstevel@tonic-gate 			    strerror(errno));
13787c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
13797c478bd9Sstevel@tonic-gate 		}
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 		/* .... and see what port kernel picked for us */
13827c478bd9Sstevel@tonic-gate 		if (getsockname(send_sock, sp, &slen) < 0) {
13837c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: getsockname %s\n", progname,
13847c478bd9Sstevel@tonic-gate 			    strerror(errno));
13857c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
13867c478bd9Sstevel@tonic-gate 		}
13877c478bd9Sstevel@tonic-gate 		*udp_src_port = (family == AF_INET) ? sin.sin_port :
13887c478bd9Sstevel@tonic-gate 		    sin6.sin6_port;
13897c478bd9Sstevel@tonic-gate 	} else {
13907c478bd9Sstevel@tonic-gate 		send_sock = recv_sock;
13917c478bd9Sstevel@tonic-gate 	}
13927c478bd9Sstevel@tonic-gate 
1393bd670b35SErik Nordmark 	if (nexthop != NULL)
1394bd670b35SErik Nordmark 		set_nexthop(family, ai_nexthop, send_sock);
1395bd670b35SErik Nordmark 
13967c478bd9Sstevel@tonic-gate 	int_op = 48 * 1024;
13977c478bd9Sstevel@tonic-gate 	if (int_op < datalen)
13987c478bd9Sstevel@tonic-gate 		int_op = datalen;
13997c478bd9Sstevel@tonic-gate 	if (setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, (char *)&int_op,
14007c478bd9Sstevel@tonic-gate 	    sizeof (int_op)) == -1) {
14017c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: setsockopt SO_RCVBUF %s\n", progname,
14027c478bd9Sstevel@tonic-gate 		    strerror(errno));
14037c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
14047c478bd9Sstevel@tonic-gate 	}
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate 	if (setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, (char *)&int_op,
14077c478bd9Sstevel@tonic-gate 	    sizeof (int_op)) == -1) {
14087c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: setsockopt SO_SNDBUF %s\n", progname,
14097c478bd9Sstevel@tonic-gate 		    strerror(errno));
14107c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
14117c478bd9Sstevel@tonic-gate 	}
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	if (options & SO_DEBUG) {
14147c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, SOL_SOCKET, SO_DEBUG, (char *)&on,
14157c478bd9Sstevel@tonic-gate 		    sizeof (on)) == -1) {
14167c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt SO_DEBUG %s\n",
14177c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
14187c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14197c478bd9Sstevel@tonic-gate 		}
14207c478bd9Sstevel@tonic-gate 	}
14217c478bd9Sstevel@tonic-gate 
14227c478bd9Sstevel@tonic-gate 	if (options & SO_DONTROUTE) {
14237c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
14247c478bd9Sstevel@tonic-gate 		    sizeof (on)) == -1) {
14257c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt SO_DONTROUTE %s\n",
14267c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
14277c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14287c478bd9Sstevel@tonic-gate 		}
14297c478bd9Sstevel@tonic-gate 	}
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_NOLOOP) {
14327c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
14337c478bd9Sstevel@tonic-gate 			char_op = 0;	/* used to turn off option */
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
14367c478bd9Sstevel@tonic-gate 			    (char *)&char_op, sizeof (char_op)) == -1) {
14377c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
14387c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_NOLOOP %s\n", progname,
14397c478bd9Sstevel@tonic-gate 				    strerror(errno));
14407c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14417c478bd9Sstevel@tonic-gate 			}
14427c478bd9Sstevel@tonic-gate 		} else {
14437c478bd9Sstevel@tonic-gate 			int_op = 0;	/* used to turn off option */
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IPV6,
14467c478bd9Sstevel@tonic-gate 			    IPV6_MULTICAST_LOOP, (char *)&int_op,
14477c478bd9Sstevel@tonic-gate 			    sizeof (int_op)) == -1) {
14487c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
14497c478bd9Sstevel@tonic-gate 				    "IPV6_MULTICAST_NOLOOP %s\n", progname,
14507c478bd9Sstevel@tonic-gate 				    strerror(errno));
14517c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14527c478bd9Sstevel@tonic-gate 			}
14537c478bd9Sstevel@tonic-gate 		}
14547c478bd9Sstevel@tonic-gate 	}
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_TTL) {
14577c478bd9Sstevel@tonic-gate 		char_op = hoplimit;
14587c478bd9Sstevel@tonic-gate 
1459bd670b35SErik Nordmark 		/* Applies to unicast and multicast. */
14607c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
14617c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
14627c478bd9Sstevel@tonic-gate 			    (char *)&char_op, sizeof (char)) == -1) {
14637c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
14647c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_TTL %s\n", progname,
14657c478bd9Sstevel@tonic-gate 				    strerror(errno));
14667c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14677c478bd9Sstevel@tonic-gate 			}
14687c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_TTL,
14697c478bd9Sstevel@tonic-gate 			    (char *)&hoplimit, sizeof (hoplimit)) == -1) {
14707c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt IP_TTL %s\n",
14717c478bd9Sstevel@tonic-gate 				    progname, strerror(errno));
14727c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14737c478bd9Sstevel@tonic-gate 			}
14747c478bd9Sstevel@tonic-gate 		}
14757c478bd9Sstevel@tonic-gate 		/*
14767c478bd9Sstevel@tonic-gate 		 * AF_INET6 case is handled in set_ancillary_data() function.
14777c478bd9Sstevel@tonic-gate 		 * This is because when ancillary data is used (for routing
14787c478bd9Sstevel@tonic-gate 		 * header and outgoing interface index), the hoplimit set using
14797c478bd9Sstevel@tonic-gate 		 * setsockopt() is ignored.
14807c478bd9Sstevel@tonic-gate 		 */
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
1483bd670b35SErik Nordmark 	/*
1484bd670b35SErik Nordmark 	 * did the user specify an interface?
1485bd670b35SErik Nordmark 	 * Applies to unicast, broadcast and multicast.
1486bd670b35SErik Nordmark 	 */
14877c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_IF) {
14887c478bd9Sstevel@tonic-gate 		struct ifaddrlist *al = NULL;		/* interface list */
14897c478bd9Sstevel@tonic-gate 		struct ifaddrlist *my_if;
14907c478bd9Sstevel@tonic-gate 		char errbuf[ERRBUFSIZE];
14917c478bd9Sstevel@tonic-gate 		int num_ifs;
14927c478bd9Sstevel@tonic-gate 		int num_src_ifs;		/* exclude down and loopback */
14937c478bd9Sstevel@tonic-gate 		int i;
14947c478bd9Sstevel@tonic-gate 
14957c478bd9Sstevel@tonic-gate 		/* pull out the interface list */
1496e11c3f44Smeem 		num_ifs = ifaddrlist(&al, family, LIFC_UNDER_IPMP, errbuf);
14977c478bd9Sstevel@tonic-gate 		if (num_ifs == -1) {
14987c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: %s\n", progname, errbuf);
14997c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
15007c478bd9Sstevel@tonic-gate 		}
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 		/* filter out down and loopback interfaces */
15037c478bd9Sstevel@tonic-gate 		num_src_ifs = 0;
15047c478bd9Sstevel@tonic-gate 		for (i = 0; i < num_ifs; i++) {
15057c478bd9Sstevel@tonic-gate 			if (!(al[i].flags & IFF_LOOPBACK) &&
15067c478bd9Sstevel@tonic-gate 			    (al[i].flags & IFF_UP))
15077c478bd9Sstevel@tonic-gate 				num_src_ifs++;
15087c478bd9Sstevel@tonic-gate 		}
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 		if (num_src_ifs == 0) {
15117c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: can't find any %s interface\n",
15127c478bd9Sstevel@tonic-gate 			    progname, (family == AF_INET) ? "IPv4" : "IPv6");
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 			return (_B_FALSE);	/* failure */
15157c478bd9Sstevel@tonic-gate 		}
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 		/* locate the specified interface */
15187c478bd9Sstevel@tonic-gate 		my_if = find_if(al, num_ifs);
15197c478bd9Sstevel@tonic-gate 		if (my_if == NULL) {
15207c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: %s is an invalid %s interface\n",
15217c478bd9Sstevel@tonic-gate 			    progname, out_if.str,
15227c478bd9Sstevel@tonic-gate 			    (family == AF_INET) ? "IPv4" : "IPv6");
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
15257c478bd9Sstevel@tonic-gate 		}
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
1528bd670b35SErik Nordmark 			struct in_pktinfo pktinfo;
1529bd670b35SErik Nordmark 
15307c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_IF,
15317c478bd9Sstevel@tonic-gate 			    (char *)&my_if->addr.addr,
15327c478bd9Sstevel@tonic-gate 			    sizeof (struct in_addr)) == -1) {
15337c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
15347c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_IF %s\n", progname,
15357c478bd9Sstevel@tonic-gate 				    strerror(errno));
15367c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
15377c478bd9Sstevel@tonic-gate 			}
1538bd670b35SErik Nordmark 			bzero(&pktinfo, sizeof (pktinfo));
1539bd670b35SErik Nordmark 			pktinfo.ipi_ifindex = my_if->index;
1540bd670b35SErik Nordmark 			if (setsockopt(send_sock, IPPROTO_IP, IP_PKTINFO,
1541bd670b35SErik Nordmark 			    (char *)&pktinfo, sizeof (pktinfo)) == -1) {
1542bd670b35SErik Nordmark 				Fprintf(stderr, "%s: setsockopt "
1543bd670b35SErik Nordmark 				    "IP_PKTINFO %s\n", progname,
1544bd670b35SErik Nordmark 				    strerror(errno));
1545bd670b35SErik Nordmark 				exit(EXIT_FAILURE);
1546bd670b35SErik Nordmark 			}
15477c478bd9Sstevel@tonic-gate 		} else {
15487c478bd9Sstevel@tonic-gate 			/*
15497c478bd9Sstevel@tonic-gate 			 * the outgoing interface is set in set_ancillary_data()
15507c478bd9Sstevel@tonic-gate 			 * function
15517c478bd9Sstevel@tonic-gate 			 */
15527c478bd9Sstevel@tonic-gate 			*if_index = my_if->index;
15537c478bd9Sstevel@tonic-gate 		}
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 		free(al);
15567c478bd9Sstevel@tonic-gate 	}
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	if (settos && family == AF_INET) {
15597c478bd9Sstevel@tonic-gate 		int_op = tos;
15607c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, IPPROTO_IP, IP_TOS, (char *)&int_op,
15617c478bd9Sstevel@tonic-gate 		    sizeof (int_op)) == -1) {
15627c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IP_TOS %s\n",
15637c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
15647c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
15657c478bd9Sstevel@tonic-gate 		}
15667c478bd9Sstevel@tonic-gate 	}
15677c478bd9Sstevel@tonic-gate 
1568bd670b35SErik Nordmark 	/* We enable or disable to not depend on the kernel default */
1569bd670b35SErik Nordmark 	if (family == AF_INET) {
1570bd670b35SErik Nordmark 		if (setsockopt(send_sock, IPPROTO_IP, IP_DONTFRAG,
1571bd670b35SErik Nordmark 		    (char *)&dontfrag, sizeof (dontfrag)) == -1) {
1572bd670b35SErik Nordmark 			Fprintf(stderr, "%s: setsockopt IP_DONTFRAG %s\n",
1573bd670b35SErik Nordmark 			    progname, strerror(errno));
1574bd670b35SErik Nordmark 			exit(EXIT_FAILURE);
1575bd670b35SErik Nordmark 		}
1576bd670b35SErik Nordmark 	} else {
1577bd670b35SErik Nordmark 		if (setsockopt(send_sock, IPPROTO_IPV6, IPV6_DONTFRAG,
1578bd670b35SErik Nordmark 		    (char *)&dontfrag, sizeof (dontfrag)) == -1) {
1579bd670b35SErik Nordmark 			Fprintf(stderr, "%s: setsockopt IPV6_DONTFRAG %s\n",
1580bd670b35SErik Nordmark 			    progname, strerror(errno));
1581bd670b35SErik Nordmark 			exit(EXIT_FAILURE);
1582bd670b35SErik Nordmark 		}
1583bd670b35SErik Nordmark 	}
1584bd670b35SErik Nordmark 
15857c478bd9Sstevel@tonic-gate 	/* receiving IPv6 extension headers in verbose mode */
15867c478bd9Sstevel@tonic-gate 	if (verbose && family == AF_INET6) {
15877c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS,
15887c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
15897c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVHOPOPTS %s\n",
15907c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
15917c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
15927c478bd9Sstevel@tonic-gate 		}
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS,
15957c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
15967c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVDSTOPTS %s\n",
15977c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
15987c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
15997c478bd9Sstevel@tonic-gate 		}
16007c478bd9Sstevel@tonic-gate 
16017c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVRTHDR,
16027c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
16037c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVRTHDR %s\n",
16047c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
16057c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
16067c478bd9Sstevel@tonic-gate 		}
16077c478bd9Sstevel@tonic-gate 	}
16087c478bd9Sstevel@tonic-gate 
16097c478bd9Sstevel@tonic-gate 	*send_sockp = send_sock;
16107c478bd9Sstevel@tonic-gate 	*recv_sockp = recv_sock;
16117c478bd9Sstevel@tonic-gate 
16127c478bd9Sstevel@tonic-gate 	/* successful */
16137c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
16147c478bd9Sstevel@tonic-gate }
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate /*
16177c478bd9Sstevel@tonic-gate  * Pull out the record containing all the info about the interface specified by
16187c478bd9Sstevel@tonic-gate  * `out_if'. Skips interfaces which are down or loopback.
16197c478bd9Sstevel@tonic-gate  */
16207c478bd9Sstevel@tonic-gate static struct ifaddrlist *
find_if(struct ifaddrlist * al,int num_ifs)16217c478bd9Sstevel@tonic-gate find_if(struct ifaddrlist *al, int num_ifs)
16227c478bd9Sstevel@tonic-gate {
16237c478bd9Sstevel@tonic-gate 	static struct ifaddrlist tmp_if;
16247c478bd9Sstevel@tonic-gate 	boolean_t found;
16257c478bd9Sstevel@tonic-gate 	int i;
16267c478bd9Sstevel@tonic-gate 
16277c478bd9Sstevel@tonic-gate 	i = 0;
16287c478bd9Sstevel@tonic-gate 	found = _B_FALSE;
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 	while (i < num_ifs && !found) {
16317c478bd9Sstevel@tonic-gate 		tmp_if = al[i];
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 		/* skip down or loopback interfaces */
16347c478bd9Sstevel@tonic-gate 		if ((tmp_if.flags & IFF_LOOPBACK) || !(tmp_if.flags & IFF_UP)) {
16357c478bd9Sstevel@tonic-gate 			i++;
16367c478bd9Sstevel@tonic-gate 			continue;
16377c478bd9Sstevel@tonic-gate 		}
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 		/* the type of interface id is variable */
16407c478bd9Sstevel@tonic-gate 		switch (out_if.id_type) {
16417c478bd9Sstevel@tonic-gate 		case IF_INDEX:
16427c478bd9Sstevel@tonic-gate 			if (out_if.id.index == tmp_if.index)
16437c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
16447c478bd9Sstevel@tonic-gate 			break;
16457c478bd9Sstevel@tonic-gate 
16467c478bd9Sstevel@tonic-gate 		case IF_NAME:
16477c478bd9Sstevel@tonic-gate 			if (strcmp(out_if.id.name, tmp_if.device) == 0)
16487c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
16497c478bd9Sstevel@tonic-gate 			break;
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		case IF_ADDR:
16527c478bd9Sstevel@tonic-gate 			if (out_if.id.addr.addr.s_addr ==
16537c478bd9Sstevel@tonic-gate 			    tmp_if.addr.addr.s_addr) {
16547c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
16557c478bd9Sstevel@tonic-gate 			}
16567c478bd9Sstevel@tonic-gate 			break;
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate 		case IF_ADDR6:
16597c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&out_if.id.addr.addr6,
16607c478bd9Sstevel@tonic-gate 			    &tmp_if.addr.addr6)) {
16617c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
16627c478bd9Sstevel@tonic-gate 			}
16637c478bd9Sstevel@tonic-gate 			break;
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 		default:
16667c478bd9Sstevel@tonic-gate 			break;
16677c478bd9Sstevel@tonic-gate 		}
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 		i++;
16707c478bd9Sstevel@tonic-gate 	}
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	if (found)
16737c478bd9Sstevel@tonic-gate 		return (&tmp_if);
16747c478bd9Sstevel@tonic-gate 	else
16757c478bd9Sstevel@tonic-gate 		return (NULL);
16767c478bd9Sstevel@tonic-gate }
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate /*
16797c478bd9Sstevel@tonic-gate  * Invoked by SIGALRM, sigalrm_handler() is, responsible for calling
16807c478bd9Sstevel@tonic-gate  * send_scheduled_probe() to send next probe.
16817c478bd9Sstevel@tonic-gate  */
16827c478bd9Sstevel@tonic-gate void
sigalrm_handler(void)16837c478bd9Sstevel@tonic-gate sigalrm_handler(void)
16847c478bd9Sstevel@tonic-gate {
16857c478bd9Sstevel@tonic-gate 	/*
16867c478bd9Sstevel@tonic-gate 	 * Guard againist denial-of-service attacks. Make sure ping doesn't
16877c478bd9Sstevel@tonic-gate 	 * send probes for every SIGALRM it receives. Evil hacker can generate
16887c478bd9Sstevel@tonic-gate 	 * SIGALRMs as fast as it can, but ping will ignore those which are
16897c478bd9Sstevel@tonic-gate 	 * received too soon (earlier than 0.5 sec) after it sent the last
16907c478bd9Sstevel@tonic-gate 	 * probe.  We use gethrtime() instead of gettimeofday() because
16917c478bd9Sstevel@tonic-gate 	 * the latter is not linear and is prone to resetting or drifting
16927c478bd9Sstevel@tonic-gate 	 */
16937c478bd9Sstevel@tonic-gate 	if ((gethrtime() - t_last_probe_sent) < 500000000) {
16947c478bd9Sstevel@tonic-gate 		return;
16957c478bd9Sstevel@tonic-gate 	}
16967c478bd9Sstevel@tonic-gate 	send_scheduled_probe();
16977c478bd9Sstevel@tonic-gate 	schedule_sigalrm();
16987c478bd9Sstevel@tonic-gate }
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate /*
17017c478bd9Sstevel@tonic-gate  * Schedule next SIGALRM.
17027c478bd9Sstevel@tonic-gate  */
17037c478bd9Sstevel@tonic-gate void
schedule_sigalrm(void)17047c478bd9Sstevel@tonic-gate schedule_sigalrm(void)
17057c478bd9Sstevel@tonic-gate {
17067c478bd9Sstevel@tonic-gate 	int waittime;
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 	if (npackets == 0 ||
17097c478bd9Sstevel@tonic-gate 	    current_targetaddr->num_sent < current_targetaddr->num_probes) {
17107c478bd9Sstevel@tonic-gate 		(void) alarm(interval);
17117c478bd9Sstevel@tonic-gate 	} else {
17127c478bd9Sstevel@tonic-gate 		if (current_targetaddr->got_reply) {
17137c478bd9Sstevel@tonic-gate 			waittime = 2 * tmax / MICROSEC;
17147c478bd9Sstevel@tonic-gate 			if (waittime == 0)
17157c478bd9Sstevel@tonic-gate 				waittime = 1;
17167c478bd9Sstevel@tonic-gate 		} else {
17177c478bd9Sstevel@tonic-gate 			waittime = MAX_WAIT;
17187c478bd9Sstevel@tonic-gate 		}
17197c478bd9Sstevel@tonic-gate 		(void) alarm(waittime);
17207c478bd9Sstevel@tonic-gate 	}
17217c478bd9Sstevel@tonic-gate }
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate /*
17247c478bd9Sstevel@tonic-gate  * Called by sigalrm_handler(), check_reply() or check_reply6(),
17257c478bd9Sstevel@tonic-gate  * send_scheduled_probe() looks at the current_targetaddr and determines what
17267c478bd9Sstevel@tonic-gate  * should be sent next and calls pinger().
17277c478bd9Sstevel@tonic-gate  */
17287c478bd9Sstevel@tonic-gate void
send_scheduled_probe()17297c478bd9Sstevel@tonic-gate send_scheduled_probe()
17307c478bd9Sstevel@tonic-gate {
17317c478bd9Sstevel@tonic-gate 	static struct msghdr msg6;
17327c478bd9Sstevel@tonic-gate 	static boolean_t first_probe = _B_TRUE;
17337c478bd9Sstevel@tonic-gate 	char tmp_buf[INET6_ADDRSTRLEN];
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	/*
17367c478bd9Sstevel@tonic-gate 	 * We are about to move to next targetaddr if it's either we sent
17377c478bd9Sstevel@tonic-gate 	 * all the probes, or somebody set the probing_done flag to
17387c478bd9Sstevel@tonic-gate 	 * _B_TRUE prompting us to move on.
17397c478bd9Sstevel@tonic-gate 	 */
17407c478bd9Sstevel@tonic-gate 	if (current_targetaddr->num_sent == current_targetaddr->num_probes ||
17417c478bd9Sstevel@tonic-gate 	    current_targetaddr->probing_done) {
17427c478bd9Sstevel@tonic-gate 		/*
17437c478bd9Sstevel@tonic-gate 		 * is this a dead target?
17447c478bd9Sstevel@tonic-gate 		 */
17457c478bd9Sstevel@tonic-gate 		if (!stats && !current_targetaddr->got_reply) {
17467c478bd9Sstevel@tonic-gate 			if (!probe_all) {
17477c478bd9Sstevel@tonic-gate 				Printf("no answer from %s\n", targethost);
17487c478bd9Sstevel@tonic-gate 			} else {
17497c478bd9Sstevel@tonic-gate 				Printf("no answer from %s(%s)\n", targethost,
17507c478bd9Sstevel@tonic-gate 				    inet_ntop(current_targetaddr->family,
17517c478bd9Sstevel@tonic-gate 				    &current_targetaddr->dst_addr,
17527c478bd9Sstevel@tonic-gate 				    tmp_buf, sizeof (tmp_buf)));
17537c478bd9Sstevel@tonic-gate 			}
17547c478bd9Sstevel@tonic-gate 		}
17557c478bd9Sstevel@tonic-gate 		/*
17567c478bd9Sstevel@tonic-gate 		 * Before we move onto next item, let's do some clean up.
17577c478bd9Sstevel@tonic-gate 		 */
17587c478bd9Sstevel@tonic-gate 		current_targetaddr->got_reply = _B_FALSE;
17597c478bd9Sstevel@tonic-gate 		current_targetaddr->probing_done = _B_FALSE;
17607c478bd9Sstevel@tonic-gate 		/*
17617c478bd9Sstevel@tonic-gate 		 * If this is probe-all without stats mode, then we need to
17627c478bd9Sstevel@tonic-gate 		 * preserve this count. This is needed when we try to map an
17637c478bd9Sstevel@tonic-gate 		 * icmp_seq to IP address. Otherwise, clear it.
17647c478bd9Sstevel@tonic-gate 		 */
17657c478bd9Sstevel@tonic-gate 		if (stats || !probe_all)
17667c478bd9Sstevel@tonic-gate 			current_targetaddr->num_sent = 0;
17677c478bd9Sstevel@tonic-gate 		nreceived_last_target = 0;
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 		current_targetaddr = current_targetaddr->next;
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 		/*
17727c478bd9Sstevel@tonic-gate 		 * Did we reach the end of road?
17737c478bd9Sstevel@tonic-gate 		 */
17747c478bd9Sstevel@tonic-gate 		if (current_targetaddr == NULL) {
17757c478bd9Sstevel@tonic-gate 			(void) alarm(0);	/* cancel alarm */
17767c478bd9Sstevel@tonic-gate 			if (stats)
17777c478bd9Sstevel@tonic-gate 				finish();
17787c478bd9Sstevel@tonic-gate 			if (is_alive)
17797c478bd9Sstevel@tonic-gate 				exit(EXIT_SUCCESS);
17807c478bd9Sstevel@tonic-gate 			else
17817c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
17827c478bd9Sstevel@tonic-gate 		} else {
17837c478bd9Sstevel@tonic-gate 			/*
17847c478bd9Sstevel@tonic-gate 			 * We use starting_seq_num for authenticating replies.
17857c478bd9Sstevel@tonic-gate 			 * Each time we move to a new targetaddr, which has
17867c478bd9Sstevel@tonic-gate 			 * a different target IP address, we update this field.
17877c478bd9Sstevel@tonic-gate 			 */
1788e11c3f44Smeem 			current_targetaddr->starting_seq_num = use_udp ?
1789e11c3f44Smeem 			    dest_port : (ntransmitted % (MAX_ICMP_SEQ + 1));
17907c478bd9Sstevel@tonic-gate 		}
17917c478bd9Sstevel@tonic-gate 	}
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 	if (current_targetaddr->family == AF_INET6) {
17947c478bd9Sstevel@tonic-gate 		if (send_reply) {
17957c478bd9Sstevel@tonic-gate 			/* sending back to ourself */
17967c478bd9Sstevel@tonic-gate 			to6.sin6_addr = current_targetaddr->src_addr.addr6;
17977c478bd9Sstevel@tonic-gate 		} else {
17987c478bd9Sstevel@tonic-gate 			to6.sin6_addr = current_targetaddr->dst_addr.addr6;
17997c478bd9Sstevel@tonic-gate 		}
18007c478bd9Sstevel@tonic-gate 		/*
18017c478bd9Sstevel@tonic-gate 		 * Setting the ancillary data once is enough, if we are
18027c478bd9Sstevel@tonic-gate 		 * not using source routing through target (-l/-S). In
18037c478bd9Sstevel@tonic-gate 		 * case -l/-S used, the middle gateway will be the
18047c478bd9Sstevel@tonic-gate 		 * IP address of the source, which can be different
18057c478bd9Sstevel@tonic-gate 		 * for each target IP.
18067c478bd9Sstevel@tonic-gate 		 */
18077c478bd9Sstevel@tonic-gate 		if (first_probe ||
18087c478bd9Sstevel@tonic-gate 		    (send_reply && current_targetaddr->num_sent == 0)) {
18097c478bd9Sstevel@tonic-gate 			if (send_reply) {
18107c478bd9Sstevel@tonic-gate 				/* target is the middle gateway now */
18117c478bd9Sstevel@tonic-gate 				gw_IP_list6[num_gw].addr6 =
18127c478bd9Sstevel@tonic-gate 				    current_targetaddr->dst_addr.addr6;
18137c478bd9Sstevel@tonic-gate 			}
18147c478bd9Sstevel@tonic-gate 			set_ancillary_data(&msg6, hoplimit, gw_IP_list6,
18157c478bd9Sstevel@tonic-gate 			    eff_num_gw, if_index);
18167c478bd9Sstevel@tonic-gate 			first_probe = _B_FALSE;
18177c478bd9Sstevel@tonic-gate 		}
18187c478bd9Sstevel@tonic-gate 		pinger(send_sock6, (struct sockaddr *)&to6, &msg6, AF_INET6);
18197c478bd9Sstevel@tonic-gate 	} else {
18207c478bd9Sstevel@tonic-gate 		to.sin_addr = current_targetaddr->dst_addr.addr;
18217c478bd9Sstevel@tonic-gate 		/*
18227c478bd9Sstevel@tonic-gate 		 * Set IPv4 options when sending the first probe to a target
18237c478bd9Sstevel@tonic-gate 		 * IP address. Some options change when the target address
18247c478bd9Sstevel@tonic-gate 		 * changes.
18257c478bd9Sstevel@tonic-gate 		 */
18267c478bd9Sstevel@tonic-gate 		if (current_targetaddr->num_sent == 0) {
18277c478bd9Sstevel@tonic-gate 			if (eff_num_gw > 0) {
18287c478bd9Sstevel@tonic-gate 				gw_IP_list[num_gw].addr =
18297c478bd9Sstevel@tonic-gate 				    current_targetaddr->dst_addr.addr;
18307c478bd9Sstevel@tonic-gate 				/*
18317c478bd9Sstevel@tonic-gate 				 * If send_reply, the target becomes the
18327c478bd9Sstevel@tonic-gate 				 * middle gateway, sender becomes the last
18337c478bd9Sstevel@tonic-gate 				 * gateway.
18347c478bd9Sstevel@tonic-gate 				 */
18357c478bd9Sstevel@tonic-gate 				if (send_reply) {
18367c478bd9Sstevel@tonic-gate 					gw_IP_list[eff_num_gw].addr =
18377c478bd9Sstevel@tonic-gate 					    current_targetaddr->src_addr.addr;
18387c478bd9Sstevel@tonic-gate 				}
18397c478bd9Sstevel@tonic-gate 			}
18407c478bd9Sstevel@tonic-gate 			/*
18417c478bd9Sstevel@tonic-gate 			 * In IPv4, if source routing is used, the target
18427c478bd9Sstevel@tonic-gate 			 * address shows up as the last gateway, hence +1.
18437c478bd9Sstevel@tonic-gate 			 */
18447c478bd9Sstevel@tonic-gate 			set_IPv4_options(send_sock, gw_IP_list,
18457c478bd9Sstevel@tonic-gate 			    (eff_num_gw > 0) ? eff_num_gw + 1 : 0,
18467c478bd9Sstevel@tonic-gate 			    &current_targetaddr->src_addr.addr, &to.sin_addr);
18477c478bd9Sstevel@tonic-gate 		}
18487c478bd9Sstevel@tonic-gate 		pinger(send_sock, (struct sockaddr *)&to, NULL, AF_INET);
18497c478bd9Sstevel@tonic-gate 	}
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 	current_targetaddr->num_sent++;
18527c478bd9Sstevel@tonic-gate }
18537c478bd9Sstevel@tonic-gate 
18547c478bd9Sstevel@tonic-gate /*
18557c478bd9Sstevel@tonic-gate  * recv_icmp_packet()'s job is to listen to icmp packets and filter out
18567c478bd9Sstevel@tonic-gate  * those ping is interested in.
18577c478bd9Sstevel@tonic-gate  */
18587c478bd9Sstevel@tonic-gate static void
recv_icmp_packet(struct addrinfo * ai_dst,int recv_sock6,int recv_sock,ushort_t udp_src_port6,ushort_t udp_src_port)18597c478bd9Sstevel@tonic-gate recv_icmp_packet(struct addrinfo *ai_dst, int recv_sock6, int recv_sock,
18607c478bd9Sstevel@tonic-gate ushort_t udp_src_port6, ushort_t udp_src_port)
18617c478bd9Sstevel@tonic-gate {
18627c478bd9Sstevel@tonic-gate 	struct msghdr in_msg;
18637c478bd9Sstevel@tonic-gate 	struct iovec iov;
18647c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from6;
18657c478bd9Sstevel@tonic-gate 	fd_set fds;
18667c478bd9Sstevel@tonic-gate 	int result;
18677c478bd9Sstevel@tonic-gate 	int cc;
18687c478bd9Sstevel@tonic-gate 	boolean_t always_true = _B_TRUE; /* lint doesn't like while(_B_TRUE) */
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 	while (always_true) {
18717c478bd9Sstevel@tonic-gate 		(void) FD_ZERO(&fds);
18727c478bd9Sstevel@tonic-gate 		if (recv_sock6 != -1)
18737c478bd9Sstevel@tonic-gate 			FD_SET(recv_sock6, &fds);
18747c478bd9Sstevel@tonic-gate 		if (recv_sock != -1)
18757c478bd9Sstevel@tonic-gate 			FD_SET(recv_sock, &fds);
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 		result = select(MAX(recv_sock6, recv_sock) + 1, &fds,
18787c478bd9Sstevel@tonic-gate 		    (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL);
18797c478bd9Sstevel@tonic-gate 		if (result == -1) {
18807c478bd9Sstevel@tonic-gate 			if (errno == EINTR) {
18817c478bd9Sstevel@tonic-gate 				continue;
18827c478bd9Sstevel@tonic-gate 			} else {
18837c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: select %s\n", progname,
18847c478bd9Sstevel@tonic-gate 				    strerror(errno));
18857c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
18867c478bd9Sstevel@tonic-gate 			}
18877c478bd9Sstevel@tonic-gate 		} else if (result > 0) {
18887c478bd9Sstevel@tonic-gate 			in_msg.msg_name = &from6;
18897c478bd9Sstevel@tonic-gate 			in_msg.msg_namelen = sizeof (from6);
18907c478bd9Sstevel@tonic-gate 			iov.iov_base = in_pkt;
18917c478bd9Sstevel@tonic-gate 			iov.iov_len = sizeof (in_pkt);
18927c478bd9Sstevel@tonic-gate 			in_msg.msg_iov = &iov;
18937c478bd9Sstevel@tonic-gate 			in_msg.msg_iovlen = 1;
18947c478bd9Sstevel@tonic-gate 			in_msg.msg_control = ancillary_data;
18957c478bd9Sstevel@tonic-gate 			in_msg.msg_controllen = sizeof (ancillary_data);
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 			/* Do we have an ICMP6 packet waiting? */
18987c478bd9Sstevel@tonic-gate 			if ((recv_sock6 != -1) &&
18997c478bd9Sstevel@tonic-gate 			    (FD_ISSET(recv_sock6, &fds))) {
19007c478bd9Sstevel@tonic-gate 				cc = recvmsg(recv_sock6, &in_msg, 0);
19017c478bd9Sstevel@tonic-gate 				if (cc < 0) {
19027c478bd9Sstevel@tonic-gate 					if (errno != EINTR) {
19037c478bd9Sstevel@tonic-gate 						Fprintf(stderr,
19047c478bd9Sstevel@tonic-gate 						    "%s: recvmsg %s\n",
19057c478bd9Sstevel@tonic-gate 						    progname, strerror(errno));
19067c478bd9Sstevel@tonic-gate 					}
19077c478bd9Sstevel@tonic-gate 					continue;
19087c478bd9Sstevel@tonic-gate 				} else if (cc > 0) {
19097c478bd9Sstevel@tonic-gate 					check_reply6(ai_dst, &in_msg, cc,
19107c478bd9Sstevel@tonic-gate 					    udp_src_port6);
19117c478bd9Sstevel@tonic-gate 				}
19127c478bd9Sstevel@tonic-gate 			}
19137c478bd9Sstevel@tonic-gate 			/* Do we have an ICMP packet waiting? */
19147c478bd9Sstevel@tonic-gate 			if ((recv_sock != -1) && (FD_ISSET(recv_sock, &fds))) {
19157c478bd9Sstevel@tonic-gate 				cc = recvmsg(recv_sock, &in_msg, 0);
19167c478bd9Sstevel@tonic-gate 				if (cc < 0) {
19177c478bd9Sstevel@tonic-gate 					if (errno != EINTR) {
19187c478bd9Sstevel@tonic-gate 						Fprintf(stderr,
19197c478bd9Sstevel@tonic-gate 						    "%s: recvmsg %s\n",
19207c478bd9Sstevel@tonic-gate 						    progname, strerror(errno));
19217c478bd9Sstevel@tonic-gate 					}
19227c478bd9Sstevel@tonic-gate 					continue;
19237c478bd9Sstevel@tonic-gate 				} if (cc > 0) {
19247c478bd9Sstevel@tonic-gate 					check_reply(ai_dst, &in_msg, cc,
19257c478bd9Sstevel@tonic-gate 					    udp_src_port);
19267c478bd9Sstevel@tonic-gate 				}
19277c478bd9Sstevel@tonic-gate 			}
19287c478bd9Sstevel@tonic-gate 		}
19297c478bd9Sstevel@tonic-gate 		/*
19307c478bd9Sstevel@tonic-gate 		 * If we were probing last IP address of the target host and
19317c478bd9Sstevel@tonic-gate 		 * received a reply for each probe sent to this address,
19327c478bd9Sstevel@tonic-gate 		 * then we are done!
19337c478bd9Sstevel@tonic-gate 		 */
19347c478bd9Sstevel@tonic-gate 		if ((npackets > 0) && (current_targetaddr->next == NULL) &&
19357c478bd9Sstevel@tonic-gate 		    (nreceived_last_target == npackets)) {
19367c478bd9Sstevel@tonic-gate 			(void) alarm(0);	/* cancel alarm */
19377c478bd9Sstevel@tonic-gate 			finish();
19387c478bd9Sstevel@tonic-gate 		}
19397c478bd9Sstevel@tonic-gate 	} /* infinite loop */
19407c478bd9Sstevel@tonic-gate }
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate /*
19437c478bd9Sstevel@tonic-gate  * Given a host (with possibly multiple IP addresses) and an IP address, this
19447c478bd9Sstevel@tonic-gate  * function determines if this IP address is one of the host's addresses to
19457c478bd9Sstevel@tonic-gate  * which we're sending probes. Used to determine if we are interested in a
19467c478bd9Sstevel@tonic-gate  * packet.
19477c478bd9Sstevel@tonic-gate  */
19487c478bd9Sstevel@tonic-gate boolean_t
is_a_target(struct addrinfo * ai,union any_in_addr * addr)19497c478bd9Sstevel@tonic-gate is_a_target(struct addrinfo *ai, union any_in_addr *addr)
19507c478bd9Sstevel@tonic-gate {
19517c478bd9Sstevel@tonic-gate 	int num_addrs;
19527c478bd9Sstevel@tonic-gate 	int i;
19537c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
19547c478bd9Sstevel@tonic-gate 
19557c478bd9Sstevel@tonic-gate 	aip = ai;
19567c478bd9Sstevel@tonic-gate 	if (probe_all)
19577c478bd9Sstevel@tonic-gate 		num_addrs = num_v4 + num_v6;
19587c478bd9Sstevel@tonic-gate 	else
19597c478bd9Sstevel@tonic-gate 		num_addrs = 1;
19607c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_addrs && aip != NULL; i++) {
19617c478bd9Sstevel@tonic-gate 		if (aip->ai_family == AF_INET6) {
19627c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
19637c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)
19647c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin6_addr, &addr->addr6))
19657c478bd9Sstevel@tonic-gate 				return (_B_TRUE);
19667c478bd9Sstevel@tonic-gate 		} else {
19677c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
19687c478bd9Sstevel@tonic-gate 			if (((struct sockaddr_in *)
19697c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin_addr.s_addr == addr->addr.s_addr)
19707c478bd9Sstevel@tonic-gate 				return (_B_TRUE);
19717c478bd9Sstevel@tonic-gate 		}
19727c478bd9Sstevel@tonic-gate 	}
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 	return (_B_FALSE);
19757c478bd9Sstevel@tonic-gate }
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate /*
19787c478bd9Sstevel@tonic-gate  * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
19797c478bd9Sstevel@tonic-gate  * will be added on by the kernel.  The ID field is our UNIX process ID,
19807c478bd9Sstevel@tonic-gate  * and the sequence number is an ascending integer.  The first 8 bytes
19817c478bd9Sstevel@tonic-gate  * of the data portion are used to hold a UNIX "timeval" struct in network
19827c478bd9Sstevel@tonic-gate  * byte-order, to compute the round-trip time.
19837c478bd9Sstevel@tonic-gate  */
19847c478bd9Sstevel@tonic-gate static void
pinger(int send_sock,struct sockaddr * whereto,struct msghdr * msg6,int family)19857c478bd9Sstevel@tonic-gate pinger(int send_sock, struct sockaddr *whereto, struct msghdr *msg6,
19867c478bd9Sstevel@tonic-gate     int family)
19877c478bd9Sstevel@tonic-gate {
19887c478bd9Sstevel@tonic-gate 	static uint64_t out_pkt_buf[(IP_MAXPACKET + 1) / 8];
19897c478bd9Sstevel@tonic-gate 	uchar_t *out_pkt = (uchar_t *)&out_pkt_buf;
19907c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
19917c478bd9Sstevel@tonic-gate 	struct icmp *icp = (struct icmp *)out_pkt;
19927c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
19937c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)whereto;
19947c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
19957c478bd9Sstevel@tonic-gate 	struct sockaddr_in *to = (struct sockaddr_in *)whereto;
19967c478bd9Sstevel@tonic-gate 	struct timeval *tp;
19977c478bd9Sstevel@tonic-gate 	struct timeval t_snd;
19987c478bd9Sstevel@tonic-gate 	uchar_t *datap;
19997c478bd9Sstevel@tonic-gate 	struct iovec iov;
20007c478bd9Sstevel@tonic-gate 	int start = 0;
20017c478bd9Sstevel@tonic-gate 	int cc;
20027c478bd9Sstevel@tonic-gate 	int i;
20037c478bd9Sstevel@tonic-gate 
20047c478bd9Sstevel@tonic-gate 	/* using UDP? */
20057c478bd9Sstevel@tonic-gate 	if (use_udp) {
20067c478bd9Sstevel@tonic-gate 		cc = datalen;
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
20097c478bd9Sstevel@tonic-gate 		tp = (struct timeval *)out_pkt;
20107c478bd9Sstevel@tonic-gate 		datap = &out_pkt[sizeof (struct timeval)];
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 		/*
20137c478bd9Sstevel@tonic-gate 		 * This sets the port whether we are handling a v4 or v6
20147c478bd9Sstevel@tonic-gate 		 * sockaddr structure.
20157c478bd9Sstevel@tonic-gate 		 */
20167c478bd9Sstevel@tonic-gate 		to->sin_port = htons(dest_port);
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 		dest_port = (dest_port + 1) % (MAX_PORT + 1);
20197c478bd9Sstevel@tonic-gate 		ntransmitted++;
20207c478bd9Sstevel@tonic-gate 	} else {	/* using ICMP */
20217c478bd9Sstevel@tonic-gate 		cc = datalen + ICMP_MINLEN;
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 		if (family == AF_INET6) {
20247c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
20257c478bd9Sstevel@tonic-gate 			    ICMP6_ECHO_REPLY : ICMP6_ECHO_REQUEST;
20267c478bd9Sstevel@tonic-gate 		} else if (use_icmp_ts) {	/* family is AF_INET */
20277c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
20287c478bd9Sstevel@tonic-gate 			    ICMP_TSTAMPREPLY : ICMP_TSTAMP;
20297c478bd9Sstevel@tonic-gate 		} else {
20307c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
20317c478bd9Sstevel@tonic-gate 			    ICMP_ECHOREPLY : ICMP_ECHO;
20327c478bd9Sstevel@tonic-gate 		}
20337c478bd9Sstevel@tonic-gate 
20347c478bd9Sstevel@tonic-gate 		icp->icmp_code = 0;
20357c478bd9Sstevel@tonic-gate 		icp->icmp_cksum = 0;
20367c478bd9Sstevel@tonic-gate 		icp->icmp_seq = htons(ntransmitted++ % (MAX_ICMP_SEQ + 1));
20377c478bd9Sstevel@tonic-gate 		if (icp->icmp_seq == 0)
20387c478bd9Sstevel@tonic-gate 			num_wraps++;
20397c478bd9Sstevel@tonic-gate 		icp->icmp_id = htons(ident);		/* ID */
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
20427c478bd9Sstevel@tonic-gate 		tp = (struct timeval *)&out_pkt[ICMP_MINLEN];
20437c478bd9Sstevel@tonic-gate 		datap = &out_pkt[ICMP_MINLEN + sizeof (struct timeval)];
20447c478bd9Sstevel@tonic-gate 	}
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 	start = sizeof (struct timeval);	/* skip for time */
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 	(void) gettimeofday(&t_snd, (struct timezone *)NULL);
20497c478bd9Sstevel@tonic-gate 
20507c478bd9Sstevel@tonic-gate 	/* if packet is big enough to store timeval OR ... */
20517c478bd9Sstevel@tonic-gate 	if ((datalen >= sizeof (struct timeval)) ||
20527c478bd9Sstevel@tonic-gate 	    (family == AF_INET && use_icmp_ts))
20537c478bd9Sstevel@tonic-gate 		*tp = t_snd;
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate 	if (family == AF_INET && use_icmp_ts) {
20567c478bd9Sstevel@tonic-gate 		start = sizeof (struct id_ts);	/* skip for ICMP timestamps */
20577c478bd9Sstevel@tonic-gate 		/* Number of milliseconds since midnight */
20587c478bd9Sstevel@tonic-gate 		icp->icmp_otime = htonl((tp->tv_sec % (24*60*60)) * 1000 +
20597c478bd9Sstevel@tonic-gate 		    tp->tv_usec / 1000);
20607c478bd9Sstevel@tonic-gate 	}
20617c478bd9Sstevel@tonic-gate 
20627c478bd9Sstevel@tonic-gate 	for (i = start; i < datalen; i++)
20637c478bd9Sstevel@tonic-gate 		*datap++ = i;
20647c478bd9Sstevel@tonic-gate 
20657c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
20667c478bd9Sstevel@tonic-gate 		if (!use_udp)
20677c478bd9Sstevel@tonic-gate 			icp->icmp_cksum = in_cksum((ushort_t *)icp, cc);
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 		i = sendto(send_sock, (char *)out_pkt, cc, 0, whereto,
20707c478bd9Sstevel@tonic-gate 		    sizeof (struct sockaddr_in));
20717c478bd9Sstevel@tonic-gate 	} else {
20727c478bd9Sstevel@tonic-gate 		/*
20737c478bd9Sstevel@tonic-gate 		 * Fill in the rest of the msghdr structure. msg_control is set
20747c478bd9Sstevel@tonic-gate 		 * in set_ancillary_data().
20757c478bd9Sstevel@tonic-gate 		 */
20767c478bd9Sstevel@tonic-gate 		msg6->msg_name = to6;
20777c478bd9Sstevel@tonic-gate 		msg6->msg_namelen = sizeof (struct sockaddr_in6);
20787c478bd9Sstevel@tonic-gate 
20797c478bd9Sstevel@tonic-gate 		iov.iov_base = out_pkt;
20807c478bd9Sstevel@tonic-gate 		iov.iov_len = cc;
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate 		msg6->msg_iov = &iov;
20837c478bd9Sstevel@tonic-gate 		msg6->msg_iovlen = 1;
20847c478bd9Sstevel@tonic-gate 
20857c478bd9Sstevel@tonic-gate 		i = sendmsg(send_sock, msg6, 0);
20867c478bd9Sstevel@tonic-gate 	}
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 	/* This is a more precise time (right after we send the packet) */
20897c478bd9Sstevel@tonic-gate 	t_last_probe_sent = gethrtime();
20907c478bd9Sstevel@tonic-gate 
20917c478bd9Sstevel@tonic-gate 	if (i < 0 || i != cc)  {
20927c478bd9Sstevel@tonic-gate 		if (i < 0) {
20937c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: sendto %s\n", progname,
20947c478bd9Sstevel@tonic-gate 			    strerror(errno));
20957c478bd9Sstevel@tonic-gate 			if (!stats)
20967c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
20977c478bd9Sstevel@tonic-gate 		}
20987c478bd9Sstevel@tonic-gate 		Printf("ping: wrote %s %d chars, ret=%d\n",
20997c478bd9Sstevel@tonic-gate 		    targethost, cc, i);
21007c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
21017c478bd9Sstevel@tonic-gate 	}
21027c478bd9Sstevel@tonic-gate }
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate /*
21057c478bd9Sstevel@tonic-gate  * Return a hostname for the given IP address.
21067c478bd9Sstevel@tonic-gate  */
21077c478bd9Sstevel@tonic-gate char *
pr_name(char * addr,int family)21087c478bd9Sstevel@tonic-gate pr_name(char *addr, int family)
21097c478bd9Sstevel@tonic-gate {
21107c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
21117c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
21127c478bd9Sstevel@tonic-gate 	struct sockaddr *sa;
21137c478bd9Sstevel@tonic-gate 	static struct in6_addr prev_addr = IN6ADDR_ANY_INIT;
21147c478bd9Sstevel@tonic-gate 	char *cp;
21157c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
21167c478bd9Sstevel@tonic-gate 	static char buf[NI_MAXHOST + INET6_ADDRSTRLEN + 3];
21177c478bd9Sstevel@tonic-gate 	uint_t slen, alen, hlen;
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 	switch (family) {
21207c478bd9Sstevel@tonic-gate 	case AF_INET:
21217c478bd9Sstevel@tonic-gate 		(void) memset(&sin, 0, sizeof (sin));
21227c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in);
21237c478bd9Sstevel@tonic-gate 		alen = sizeof (struct in_addr);
21247c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
21257c478bd9Sstevel@tonic-gate 		sin.sin_addr = *(struct in_addr *)addr;
21267c478bd9Sstevel@tonic-gate 		sin.sin_port = 0;
21277c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr *)&sin;
21287c478bd9Sstevel@tonic-gate 		break;
21297c478bd9Sstevel@tonic-gate 	case AF_INET6:
21307c478bd9Sstevel@tonic-gate 		(void) memset(&sin6, 0, sizeof (sin6));
21317c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in6);
21327c478bd9Sstevel@tonic-gate 		alen = sizeof (struct in6_addr);
21337c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
21347c478bd9Sstevel@tonic-gate 		sin6.sin6_addr = *(struct in6_addr *)addr;
21357c478bd9Sstevel@tonic-gate 		sin6.sin6_port = 0;
21367c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr *)&sin6;
21377c478bd9Sstevel@tonic-gate 		break;
21387c478bd9Sstevel@tonic-gate 	default:
21397c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (buf), "<invalid address family>");
21407c478bd9Sstevel@tonic-gate 		return (buf);
21417c478bd9Sstevel@tonic-gate 	}
21427c478bd9Sstevel@tonic-gate 	sa->sa_family = family;
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 	/* compare with the buffered (previous) lookup */
21457c478bd9Sstevel@tonic-gate 	if (memcmp(addr, &prev_addr, alen) != 0) {
21467c478bd9Sstevel@tonic-gate 		int flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD;
21477c478bd9Sstevel@tonic-gate 		if (getnameinfo(sa, slen, buf, sizeof (buf),
21487c478bd9Sstevel@tonic-gate 		    NULL, 0, flags) != 0) {
21497c478bd9Sstevel@tonic-gate 			/* getnameinfo() failed; return just the address */
21507c478bd9Sstevel@tonic-gate 			if (inet_ntop(family, (const void*)addr,
21517c478bd9Sstevel@tonic-gate 			    buf, sizeof (buf)) == NULL)
21527c478bd9Sstevel@tonic-gate 				buf[0] = 0;
21537c478bd9Sstevel@tonic-gate 		} else if (!nflag) {
21547c478bd9Sstevel@tonic-gate 			/* append numeric address to hostname string */
21557c478bd9Sstevel@tonic-gate 			hlen = strlen(buf);
21567c478bd9Sstevel@tonic-gate 			cp = (char *)(buf + hlen);
21577c478bd9Sstevel@tonic-gate 			(void) snprintf(cp, sizeof (buf) - hlen, " (%s)",
21587c478bd9Sstevel@tonic-gate 			    inet_ntop(family, (const void *)addr, abuf,
21597c478bd9Sstevel@tonic-gate 			    sizeof (abuf)));
21607c478bd9Sstevel@tonic-gate 		}
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
21637c478bd9Sstevel@tonic-gate 		prev_addr = *(struct in6_addr *)addr;
21647c478bd9Sstevel@tonic-gate 	}
21657c478bd9Sstevel@tonic-gate 	return (buf);
21667c478bd9Sstevel@tonic-gate }
21677c478bd9Sstevel@tonic-gate 
21687c478bd9Sstevel@tonic-gate /*
21697c478bd9Sstevel@tonic-gate  * Return the protocol string, given its protocol number.
21707c478bd9Sstevel@tonic-gate  */
21717c478bd9Sstevel@tonic-gate char *
pr_protocol(int prot)21727c478bd9Sstevel@tonic-gate pr_protocol(int prot)
21737c478bd9Sstevel@tonic-gate {
21747c478bd9Sstevel@tonic-gate 	static char buf[20];
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate 	switch (prot) {
21777c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
21787c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "icmp6", sizeof (buf));
21797c478bd9Sstevel@tonic-gate 		break;
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMP:
21827c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "icmp", sizeof (buf));
21837c478bd9Sstevel@tonic-gate 		break;
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:
21867c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "tcp", sizeof (buf));
21877c478bd9Sstevel@tonic-gate 		break;
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
21907c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "udp", sizeof (buf));
21917c478bd9Sstevel@tonic-gate 		break;
21927c478bd9Sstevel@tonic-gate 
21937c478bd9Sstevel@tonic-gate 	default:
21947c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (buf), "prot %d", prot);
21957c478bd9Sstevel@tonic-gate 		break;
21967c478bd9Sstevel@tonic-gate 	}
21977c478bd9Sstevel@tonic-gate 
21987c478bd9Sstevel@tonic-gate 	return (buf);
21997c478bd9Sstevel@tonic-gate }
22007c478bd9Sstevel@tonic-gate 
22017c478bd9Sstevel@tonic-gate /*
22027c478bd9Sstevel@tonic-gate  * Checks if value is between seq_begin and seq_begin+seq_len. Note that
22037c478bd9Sstevel@tonic-gate  * sequence numbers wrap around after MAX_ICMP_SEQ (== MAX_PORT).
22047c478bd9Sstevel@tonic-gate  */
22057c478bd9Sstevel@tonic-gate boolean_t
seq_match(ushort_t seq_begin,int seq_len,ushort_t value)22067c478bd9Sstevel@tonic-gate seq_match(ushort_t seq_begin, int seq_len, ushort_t value)
22077c478bd9Sstevel@tonic-gate {
22087c478bd9Sstevel@tonic-gate 	/*
22097c478bd9Sstevel@tonic-gate 	 * If seq_len is too big, like some value greater than MAX_ICMP_SEQ/2,
22107c478bd9Sstevel@tonic-gate 	 * truncate it down to MAX_ICMP_SEQ/2. We are not going to accept any
22117c478bd9Sstevel@tonic-gate 	 * reply which come 83hr later!
22127c478bd9Sstevel@tonic-gate 	 */
22137c478bd9Sstevel@tonic-gate 	if (seq_len > MAX_ICMP_SEQ / 2) {
22147c478bd9Sstevel@tonic-gate 		seq_begin = (seq_begin + seq_len - MAX_ICMP_SEQ / 2) %
22157c478bd9Sstevel@tonic-gate 		    (MAX_ICMP_SEQ + 1);
22167c478bd9Sstevel@tonic-gate 		seq_len = MAX_ICMP_SEQ / 2;
22177c478bd9Sstevel@tonic-gate 	}
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 	if (PINGSEQ_LEQ(seq_begin, value) &&
22207c478bd9Sstevel@tonic-gate 	    PINGSEQ_LEQ(value, (seq_begin + seq_len - 1) % (MAX_ICMP_SEQ + 1)))
22217c478bd9Sstevel@tonic-gate 		return (_B_TRUE);
22227c478bd9Sstevel@tonic-gate 	else
22237c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
22247c478bd9Sstevel@tonic-gate }
22257c478bd9Sstevel@tonic-gate 
22267c478bd9Sstevel@tonic-gate /*
22277c478bd9Sstevel@tonic-gate  * For a given icmp_seq, find which destination address we must have sent this
22287c478bd9Sstevel@tonic-gate  * to.
22297c478bd9Sstevel@tonic-gate  */
22307c478bd9Sstevel@tonic-gate void
find_dstaddr(ushort_t icmpseq,union any_in_addr * ipaddr)22317c478bd9Sstevel@tonic-gate find_dstaddr(ushort_t icmpseq, union any_in_addr *ipaddr)
22327c478bd9Sstevel@tonic-gate {
22337c478bd9Sstevel@tonic-gate 	struct targetaddr *target = targetaddr_list;
22347c478bd9Sstevel@tonic-gate 	int real_seq;
22357c478bd9Sstevel@tonic-gate 	int targetaddr_index;
22367c478bd9Sstevel@tonic-gate 	int real_npackets;
22377c478bd9Sstevel@tonic-gate 	int i;
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 	ipaddr->addr6 = in6addr_any;
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 	/*
22427c478bd9Sstevel@tonic-gate 	 * If this is probe_all and not stats, then the number of probes sent to
22437c478bd9Sstevel@tonic-gate 	 * each IP address may be different (remember, we stop sending to one IP
22447c478bd9Sstevel@tonic-gate 	 * address as soon as it replies). They are stored in target->num_sent
22457c478bd9Sstevel@tonic-gate 	 * field. Since we don't wrap around the list (!stats), they are also
22467c478bd9Sstevel@tonic-gate 	 * preserved.
22477c478bd9Sstevel@tonic-gate 	 */
22487c478bd9Sstevel@tonic-gate 	if (probe_all && !stats) {
22497c478bd9Sstevel@tonic-gate 		do {
22507c478bd9Sstevel@tonic-gate 			if (seq_match(target->starting_seq_num,
22517c478bd9Sstevel@tonic-gate 			    target->num_sent, icmpseq)) {
22527c478bd9Sstevel@tonic-gate 				ipaddr->addr6 = target->dst_addr.addr6;
22537c478bd9Sstevel@tonic-gate 				/*
22547c478bd9Sstevel@tonic-gate 				 * We are not immediately return()ing here.
22557c478bd9Sstevel@tonic-gate 				 * Because of wrapping, we might find another
22567c478bd9Sstevel@tonic-gate 				 * match later, which is more likely to be the
22577c478bd9Sstevel@tonic-gate 				 * real one.
22587c478bd9Sstevel@tonic-gate 				 */
22597c478bd9Sstevel@tonic-gate 			}
22607c478bd9Sstevel@tonic-gate 			target = target->next;
22617c478bd9Sstevel@tonic-gate 		} while (target != NULL);
22627c478bd9Sstevel@tonic-gate 	} else {
22637c478bd9Sstevel@tonic-gate 		/*
22647c478bd9Sstevel@tonic-gate 		 * Find the absolute (non-wrapped) seq number within the last
22657c478bd9Sstevel@tonic-gate 		 * 64K
22667c478bd9Sstevel@tonic-gate 		 */
22677c478bd9Sstevel@tonic-gate 		if (icmpseq < (ntransmitted % (MAX_ICMP_SEQ + 1))) {
22687c478bd9Sstevel@tonic-gate 			real_seq = num_wraps * (MAX_ICMP_SEQ + 1) + icmpseq;
22697c478bd9Sstevel@tonic-gate 		} else {
22707c478bd9Sstevel@tonic-gate 			real_seq = (num_wraps - 1) * (MAX_ICMP_SEQ + 1) +
22717c478bd9Sstevel@tonic-gate 			    icmpseq;
22727c478bd9Sstevel@tonic-gate 		}
22737c478bd9Sstevel@tonic-gate 
22747c478bd9Sstevel@tonic-gate 		/* Make sure it's non-negative */
22757c478bd9Sstevel@tonic-gate 		if (real_seq < 0)
22767c478bd9Sstevel@tonic-gate 			return;
22777c478bd9Sstevel@tonic-gate 		real_npackets = (npackets == 0) ? 1 : npackets;
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 		/*
22807c478bd9Sstevel@tonic-gate 		 * We sent npackets many packets to each of those
22817c478bd9Sstevel@tonic-gate 		 * num_targetaddrs many IP addresses.
22827c478bd9Sstevel@tonic-gate 		 */
22837c478bd9Sstevel@tonic-gate 		targetaddr_index =
22847c478bd9Sstevel@tonic-gate 		    (real_seq % (num_targetaddrs * real_npackets)) /
22857c478bd9Sstevel@tonic-gate 		    real_npackets;
22867c478bd9Sstevel@tonic-gate 		for (i = 0; i < targetaddr_index; i++)
22877c478bd9Sstevel@tonic-gate 			target = target->next;
22887c478bd9Sstevel@tonic-gate 		ipaddr->addr6 = target->dst_addr.addr6;
22897c478bd9Sstevel@tonic-gate 	}
22907c478bd9Sstevel@tonic-gate }
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate /*
22937c478bd9Sstevel@tonic-gate  * Checksum routine for Internet Protocol family headers (C Version)
22947c478bd9Sstevel@tonic-gate  */
22957c478bd9Sstevel@tonic-gate static ushort_t
in_cksum(ushort_t * addr,int len)22967c478bd9Sstevel@tonic-gate in_cksum(ushort_t *addr, int len)
22977c478bd9Sstevel@tonic-gate {
22987c478bd9Sstevel@tonic-gate 	int nleft = len;
22997c478bd9Sstevel@tonic-gate 	ushort_t *w = addr;
23007c478bd9Sstevel@tonic-gate 	ushort_t answer;
23017c478bd9Sstevel@tonic-gate 	ushort_t odd_byte = 0;
23027c478bd9Sstevel@tonic-gate 	int sum = 0;
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate 	/*
23057c478bd9Sstevel@tonic-gate 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
23067c478bd9Sstevel@tonic-gate 	 *  we add sequential 16 bit words to it, and at the end, fold
23077c478bd9Sstevel@tonic-gate 	 *  back all the carry bits from the top 16 bits into the lower
23087c478bd9Sstevel@tonic-gate 	 *  16 bits.
23097c478bd9Sstevel@tonic-gate 	 */
23107c478bd9Sstevel@tonic-gate 	while (nleft > 1) {
23117c478bd9Sstevel@tonic-gate 		sum += *w++;
23127c478bd9Sstevel@tonic-gate 		nleft -= 2;
23137c478bd9Sstevel@tonic-gate 	}
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 	/* mop up an odd byte, if necessary */
23167c478bd9Sstevel@tonic-gate 	if (nleft == 1) {
23177c478bd9Sstevel@tonic-gate 		*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
23187c478bd9Sstevel@tonic-gate 		sum += odd_byte;
23197c478bd9Sstevel@tonic-gate 	}
23207c478bd9Sstevel@tonic-gate 
23217c478bd9Sstevel@tonic-gate 	/*
23227c478bd9Sstevel@tonic-gate 	 * add back carry outs from top 16 bits to low 16 bits
23237c478bd9Sstevel@tonic-gate 	 */
23247c478bd9Sstevel@tonic-gate 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
23257c478bd9Sstevel@tonic-gate 	sum += (sum >> 16);			/* add carry */
23267c478bd9Sstevel@tonic-gate 	answer = ~sum;				/* truncate to 16 bits */
23277c478bd9Sstevel@tonic-gate 	return (answer);
23287c478bd9Sstevel@tonic-gate }
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate /*
23317c478bd9Sstevel@tonic-gate  * Subtract 2 timeval structs:  out = out - in.
23327c478bd9Sstevel@tonic-gate  * Out is assumed to be >= in.
23337c478bd9Sstevel@tonic-gate  */
23347c478bd9Sstevel@tonic-gate void
tvsub(struct timeval * out,struct timeval * in)23357c478bd9Sstevel@tonic-gate tvsub(struct timeval *out, struct timeval *in)
23367c478bd9Sstevel@tonic-gate {
23377c478bd9Sstevel@tonic-gate 	if ((out->tv_usec -= in->tv_usec) < 0) {
23387c478bd9Sstevel@tonic-gate 		out->tv_sec--;
23397c478bd9Sstevel@tonic-gate 		out->tv_usec += 1000000;
23407c478bd9Sstevel@tonic-gate 	}
23417c478bd9Sstevel@tonic-gate 	out->tv_sec -= in->tv_sec;
23427c478bd9Sstevel@tonic-gate }
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate /*
23457c478bd9Sstevel@tonic-gate  * Print out statistics, and give up.
23467c478bd9Sstevel@tonic-gate  * Heavily buffered STDIO is used here, so that all the statistics
23477c478bd9Sstevel@tonic-gate  * will be written with 1 sys-write call.  This is nice when more
23487c478bd9Sstevel@tonic-gate  * than one copy of the program is running on a terminal;  it prevents
23497c478bd9Sstevel@tonic-gate  * the statistics output from becoming intermingled.
23507c478bd9Sstevel@tonic-gate  */
23517c478bd9Sstevel@tonic-gate static void
finish()23527c478bd9Sstevel@tonic-gate finish()
23537c478bd9Sstevel@tonic-gate {
23547c478bd9Sstevel@tonic-gate 	Printf("\n----%s PING Statistics----\n", targethost);
23557c478bd9Sstevel@tonic-gate 	Printf("%d packets transmitted, ", ntransmitted);
23567c478bd9Sstevel@tonic-gate 	Printf("%d packets received, ", nreceived);
23577c478bd9Sstevel@tonic-gate 	if (ntransmitted) {
23587c478bd9Sstevel@tonic-gate 		if (nreceived <= ntransmitted) {
23597c478bd9Sstevel@tonic-gate 			Printf("%d%% packet loss",
23607c478bd9Sstevel@tonic-gate 			    (int)(((ntransmitted-nreceived)*100) /
23617c478bd9Sstevel@tonic-gate 			    ntransmitted));
23627c478bd9Sstevel@tonic-gate 		} else {
23637c478bd9Sstevel@tonic-gate 			Printf("%.2f times amplification",
23647c478bd9Sstevel@tonic-gate 			    (double)nreceived / (double)ntransmitted);
23657c478bd9Sstevel@tonic-gate 		}
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 	/* if packet is big enough to store timeval AND ... */
23707c478bd9Sstevel@tonic-gate 	if ((datalen >= sizeof (struct timeval)) && (nreceived > 0)) {
23717c478bd9Sstevel@tonic-gate 		double mean = (double)tsum / nreceived;
23727c478bd9Sstevel@tonic-gate 		double smean = (double)tsum2 / nreceived;
23737c478bd9Sstevel@tonic-gate 		double sd =
23747c478bd9Sstevel@tonic-gate 		    sqrt(((smean - mean*mean) * nreceived) / (nreceived-1));
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 		Printf("round-trip (ms)  min/avg/max/stddev = "
23773c58dfd6Sjbeck 		    TIMEFORMAT "/" TIMEFORMAT "/"
23783c58dfd6Sjbeck 		    TIMEFORMAT "/" TIMEFORMAT "\n",
23793c58dfd6Sjbeck 		    (double)tmin / 1000, mean / 1000,
23803c58dfd6Sjbeck 		    (double)tmax / 1000, sd / 1000);
23817c478bd9Sstevel@tonic-gate 	}
23827c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
23837c478bd9Sstevel@tonic-gate 
23847c478bd9Sstevel@tonic-gate 	exit(is_alive ? EXIT_SUCCESS : EXIT_FAILURE);
23857c478bd9Sstevel@tonic-gate }
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate /*
23887c478bd9Sstevel@tonic-gate  * print the usage line
23897c478bd9Sstevel@tonic-gate  */
23907c478bd9Sstevel@tonic-gate static void
usage(char * cmdname)23917c478bd9Sstevel@tonic-gate usage(char *cmdname)
23927c478bd9Sstevel@tonic-gate {
23937c478bd9Sstevel@tonic-gate 	Fprintf(stderr, "usage: %s host [timeout]\n", cmdname);
23947c478bd9Sstevel@tonic-gate 	Fprintf(stderr,
23957c478bd9Sstevel@tonic-gate /* CSTYLED */
2396*a252e007SChris Josephes "usage: %s -s [-l | -U] [-abdDLnRrv] [-A addr_family] [-c traffic_class]\n\t"
23974c10bc16Spwernau "[-g gateway [-g gateway ...]] [-N nexthop] [-F flow_label] [-I interval]\n\t"
23987c478bd9Sstevel@tonic-gate "[-i interface] [-P tos] [-p port] [-t ttl] host [data_size] [npackets]\n",
23997c478bd9Sstevel@tonic-gate 	    cmdname);
24007c478bd9Sstevel@tonic-gate }
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate /*
24037c478bd9Sstevel@tonic-gate  * Parse integer argument; exit with an error if it's not a number.
24047c478bd9Sstevel@tonic-gate  * Now it also accepts hex. values.
24057c478bd9Sstevel@tonic-gate  */
24067c478bd9Sstevel@tonic-gate static int
int_arg(char * s,char * what)24077c478bd9Sstevel@tonic-gate int_arg(char *s, char *what)
24087c478bd9Sstevel@tonic-gate {
24097c478bd9Sstevel@tonic-gate 	char *cp;
24107c478bd9Sstevel@tonic-gate 	char *ep;
24117c478bd9Sstevel@tonic-gate 	int num;
24127c478bd9Sstevel@tonic-gate 
24137c478bd9Sstevel@tonic-gate 	errno = 0;
24147c478bd9Sstevel@tonic-gate 	if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
24157c478bd9Sstevel@tonic-gate 		cp = s + 2;
24167c478bd9Sstevel@tonic-gate 		num = (int)strtol(cp, &ep, 16);
24177c478bd9Sstevel@tonic-gate 	} else {
24187c478bd9Sstevel@tonic-gate 		num = (int)strtol(s, &ep, 10);
24197c478bd9Sstevel@tonic-gate 	}
24207c478bd9Sstevel@tonic-gate 
24217c478bd9Sstevel@tonic-gate 	if (errno || *ep != '\0' || num < 0) {
24227c478bd9Sstevel@tonic-gate 		(void) Fprintf(stderr, "%s: bad %s: %s\n",
24237c478bd9Sstevel@tonic-gate 		    progname, what, s);
24247c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
24257c478bd9Sstevel@tonic-gate 	}
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	return (num);
24287c478bd9Sstevel@tonic-gate }
2429