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 
40b08923d6SRobert Mustacchi /*
41cbf54fedSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
42*9c4b0eaaSRobert Mustacchi  * Copyright 2023 Oxide computer Company
43b08923d6SRobert Mustacchi  */
44b08923d6SRobert Mustacchi 
45b08923d6SRobert Mustacchi #include <assert.h>
467c478bd9Sstevel@tonic-gate #include <stdio.h>
477c478bd9Sstevel@tonic-gate #include <strings.h>
487c478bd9Sstevel@tonic-gate #include <errno.h>
497c478bd9Sstevel@tonic-gate #include <fcntl.h>
507c478bd9Sstevel@tonic-gate #include <unistd.h>
517c478bd9Sstevel@tonic-gate #include <signal.h>
527c478bd9Sstevel@tonic-gate #include <limits.h>
537c478bd9Sstevel@tonic-gate #include <math.h>
54b08923d6SRobert Mustacchi #include <locale.h>
55b08923d6SRobert Mustacchi #include <thread.h>
56b08923d6SRobert Mustacchi #include <synch.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #include <sys/time.h>
597c478bd9Sstevel@tonic-gate #include <sys/param.h>
607c478bd9Sstevel@tonic-gate #include <sys/socket.h>
617c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
627c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
637c478bd9Sstevel@tonic-gate #include <sys/file.h>
647c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
65b08923d6SRobert Mustacchi #include <sys/debug.h>
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
687c478bd9Sstevel@tonic-gate #include <net/if.h>
697c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
707c478bd9Sstevel@tonic-gate #include <netinet/in.h>
717c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
727c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h>
737c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
747c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
757c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
767c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
777c478bd9Sstevel@tonic-gate #include <netdb.h>
787c478bd9Sstevel@tonic-gate #include <stdlib.h>
797c478bd9Sstevel@tonic-gate #include <priv_utils.h>
807c478bd9Sstevel@tonic-gate 
810406ceaaSmeem #include <libinetutil.h>
827c478bd9Sstevel@tonic-gate #include "ping.h"
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate  * This macro is used to compare 16bit, wrapping sequence numbers. Inspired by
867c478bd9Sstevel@tonic-gate  * TCP's SEQ_LEQ macro.
877c478bd9Sstevel@tonic-gate  */
887c478bd9Sstevel@tonic-gate #define	PINGSEQ_LEQ(a, b)	((int16_t)((a)-(b)) <= 0)
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate #define	MAX_WAIT		10	/* max sec. to wait for response */
917c478bd9Sstevel@tonic-gate #define	MAX_TRAFFIC_CLASS	255	/* max traffic class for IPv6 */
927c478bd9Sstevel@tonic-gate #define	MAX_FLOW_LABEL		0xFFFFF	/* max flow label for IPv6 */
937c478bd9Sstevel@tonic-gate #define	MAX_TOS			255	/* max type-of-service for IPv4 */
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate #define	TIMEOUT			20	/* default timeout value */
967c478bd9Sstevel@tonic-gate #define	DEFAULT_DATALEN		56
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate #define	MULTICAST_NOLOOP	1	/* multicast options */
997c478bd9Sstevel@tonic-gate #define	MULTICAST_TTL		2
1007c478bd9Sstevel@tonic-gate #define	MULTICAST_IF		4
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate #define	IF_INDEX		0	/* types of -i argument */
1037c478bd9Sstevel@tonic-gate #define	IF_NAME			1
1047c478bd9Sstevel@tonic-gate #define	IF_ADDR			2
1057c478bd9Sstevel@tonic-gate #define	IF_ADDR6		3
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate #ifdef BSD
1087c478bd9Sstevel@tonic-gate #define	setbuf(s, b)	setlinebuf((s))
1097c478bd9Sstevel@tonic-gate #endif /* BSD */
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate /* interface identification */
1137c478bd9Sstevel@tonic-gate union if_id {
1147c478bd9Sstevel@tonic-gate 	int index;		/* interface index (e.g., 1, 2) */
1157c478bd9Sstevel@tonic-gate 	char *name;		/* interface name (e.g., le0, hme0) */
1167c478bd9Sstevel@tonic-gate 	union any_in_addr addr;	/* interface address (e.g., 10.123.4.5) */
1177c478bd9Sstevel@tonic-gate };
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /* stores the interface supplied by the user */
1207c478bd9Sstevel@tonic-gate struct if_entry {
1217c478bd9Sstevel@tonic-gate 	char *str;		/* unresolved, string input */
1227c478bd9Sstevel@tonic-gate 	int id_type;		/* type of ID (index, name, addr, addr6) */
1237c478bd9Sstevel@tonic-gate 	union if_id id;		/* ID */
1247c478bd9Sstevel@tonic-gate };
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate char *progname;
1277c478bd9Sstevel@tonic-gate char *targethost;
1284c10bc16Spwernau char *nexthop;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate static int send_sock;			/* send sockets */
1317c478bd9Sstevel@tonic-gate static int send_sock6;
1327c478bd9Sstevel@tonic-gate static struct sockaddr_in to;		/* where to send */
1337c478bd9Sstevel@tonic-gate static struct sockaddr_in6 to6;
1347c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list[MAX_GWS];	/* gateways */
1357c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list6[MAX_GWS6];
1367c478bd9Sstevel@tonic-gate static int if_index = 0;		/* outgoing interface index */
1377c478bd9Sstevel@tonic-gate boolean_t is_alive = _B_FALSE;		/* is target host alive */
1387c478bd9Sstevel@tonic-gate struct targetaddr *current_targetaddr;	/* current target IP address to probe */
1397c478bd9Sstevel@tonic-gate static struct targetaddr *targetaddr_list; /* list of IP addresses to probe */
1407c478bd9Sstevel@tonic-gate static int num_targetaddrs;		/* no of target addresses to probe */
1417c478bd9Sstevel@tonic-gate static int num_v4 = 0;			/* count of IPv4 addresses */
1427c478bd9Sstevel@tonic-gate static int num_v6 = 0;			/* count of IPv6 addresses */
1437c478bd9Sstevel@tonic-gate boolean_t verbose = _B_FALSE;		/* verbose output */
1447c478bd9Sstevel@tonic-gate boolean_t stats = _B_FALSE;		/* display statistics */
1457c478bd9Sstevel@tonic-gate static boolean_t settos = _B_FALSE;	/* set type-of-service value */
1467c478bd9Sstevel@tonic-gate boolean_t rr_option = _B_FALSE;		/* true if using record route */
1477c478bd9Sstevel@tonic-gate boolean_t send_reply = _B_FALSE;	/* Send an ICMP_{ECHO|TSTAMP}REPLY */
1487c478bd9Sstevel@tonic-gate 					/* that goes to target and comes back */
1497c478bd9Sstevel@tonic-gate 					/* to the the sender via src routing. */
1507c478bd9Sstevel@tonic-gate boolean_t strict = _B_FALSE;		/* true if using strict source route */
1517c478bd9Sstevel@tonic-gate boolean_t ts_option = _B_FALSE;		/* true if using timestamp option */
1527c478bd9Sstevel@tonic-gate boolean_t use_icmp_ts = _B_FALSE;	/* Use ICMP timestamp request */
1537c478bd9Sstevel@tonic-gate boolean_t use_udp = _B_FALSE;		/* Use UDP instead of ICMP */
1547c478bd9Sstevel@tonic-gate boolean_t probe_all = _B_FALSE;		/* probe all the IP addresses */
1557c478bd9Sstevel@tonic-gate boolean_t nflag = _B_FALSE;		/* do not reverse lookup addresses */
156a356273cSpwernau boolean_t bypass = _B_FALSE;		/* bypass IPsec policy */
1577c478bd9Sstevel@tonic-gate static int family_input = AF_UNSPEC;	/* address family supplied by user */
1587c478bd9Sstevel@tonic-gate int datalen = DEFAULT_DATALEN;		/* How much data */
1597c478bd9Sstevel@tonic-gate int ts_flag;				/* timestamp flag value */
1607c478bd9Sstevel@tonic-gate static int num_gw;			/* number of gateways */
1617c478bd9Sstevel@tonic-gate static int eff_num_gw;			/* effective number of gateways */
1627c478bd9Sstevel@tonic-gate 					/* if send_reply, it's 2*num_gw+1 */
1637c478bd9Sstevel@tonic-gate static int num_wraps = -1;		/* no of times 64K icmp_seq wrapped */
1647c478bd9Sstevel@tonic-gate static ushort_t dest_port = 32768 + 666; /* starting port for the UDP probes */
1657c478bd9Sstevel@tonic-gate static char *gw_list[MAXMAX_GWS];	/* list of gateways as user enters */
1667c478bd9Sstevel@tonic-gate static int options;			/* socket options */
1677c478bd9Sstevel@tonic-gate static int moptions;			/* multicast options */
1687c478bd9Sstevel@tonic-gate int npackets;				/* number of packets to send */
1697c478bd9Sstevel@tonic-gate static ushort_t tos;			/* type-of-service value */
1707c478bd9Sstevel@tonic-gate static int hoplimit = -1;		/* time-to-live value */
171bd670b35SErik Nordmark static int dontfrag;			/* IP*_DONTFRAG */
1727c478bd9Sstevel@tonic-gate static int timeout = TIMEOUT;		/* timeout value (sec) for probes */
1737c478bd9Sstevel@tonic-gate static struct if_entry out_if;		/* interface argument */
1747c478bd9Sstevel@tonic-gate int ident;				/* ID for this ping run */
1757c478bd9Sstevel@tonic-gate static hrtime_t t_last_probe_sent;	/* the time we sent the last probe */
176b08923d6SRobert Mustacchi static timer_t timer;			/* timer for waiting */
177b08923d6SRobert Mustacchi static volatile boolean_t timer_done = _B_FALSE; /* timer finished? */
178b08923d6SRobert Mustacchi static struct itimerspec interval = { { 0, 0 }, { 1, 0 } }; /* Interval for */
179b08923d6SRobert Mustacchi 					/* -I. The default interval is 1s. */
180b08923d6SRobert Mustacchi static hrtime_t mintime = NSEC2MSEC(500);	/* minimum time between pings */
181b08923d6SRobert Mustacchi 
182b08923d6SRobert Mustacchi /*
183b08923d6SRobert Mustacchi  * Globals for our name services warning. See ns_warning_thr() for more on why
184b08923d6SRobert Mustacchi  * this exists.
185b08923d6SRobert Mustacchi  */
186b08923d6SRobert Mustacchi static mutex_t ns_lock = ERRORCHECKMUTEX; /* Protects the following data */
187b08923d6SRobert Mustacchi static boolean_t ns_active = _B_FALSE;	/* Lookup is going on */
188b08923d6SRobert Mustacchi static hrtime_t ns_starttime;		/* Time the lookup started */
189b08923d6SRobert Mustacchi static int ns_sleeptime = 2;		/* Time in seconds between checks */
190b08923d6SRobert Mustacchi static int ns_warntime = 2;		/* Time in seconds before warning */
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  * This buffer stores the received packets. Currently it needs to be 32 bit
1947c478bd9Sstevel@tonic-gate  * aligned. In the future, we'll be using 64 bit alignment, so let's use 64 bit
1957c478bd9Sstevel@tonic-gate  * alignment now.
1967c478bd9Sstevel@tonic-gate  */
1977c478bd9Sstevel@tonic-gate static uint64_t in_pkt[(IP_MAXPACKET + 1)/8];
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /* Used to store the ancillary data that comes with the received packets */
2007c478bd9Sstevel@tonic-gate static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate static int ntransmitted;	/* number of packet sent to single IP address */
2037c478bd9Sstevel@tonic-gate int nreceived;			/* # of packets we got back from target host */
2047c478bd9Sstevel@tonic-gate int nreceived_last_target;	/* received from last target IP */
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate  * These are used for statistics. tmin is initialized to maximum longint value.
2077c478bd9Sstevel@tonic-gate  * The max value is also used for timeouts.   All times are in microseconds.
2087c478bd9Sstevel@tonic-gate  */
2097c478bd9Sstevel@tonic-gate long long tmin = LLONG_MAX;
2107c478bd9Sstevel@tonic-gate long long tmax;
2117c478bd9Sstevel@tonic-gate int64_t tsum;			/* sum of all times, for doing average */
2127c478bd9Sstevel@tonic-gate int64_t tsum2;			/* sum of squared times, for std. dev. */
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate static struct targetaddr *build_targetaddr_list(struct addrinfo *,
2157c478bd9Sstevel@tonic-gate     union any_in_addr *);
2167c478bd9Sstevel@tonic-gate extern void check_reply(struct addrinfo *, struct msghdr *, int, ushort_t);
2177c478bd9Sstevel@tonic-gate extern void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
2187c478bd9Sstevel@tonic-gate static struct targetaddr *create_targetaddr_item(int, union any_in_addr *,
2197c478bd9Sstevel@tonic-gate     union any_in_addr *);
2207c478bd9Sstevel@tonic-gate void find_dstaddr(ushort_t, union any_in_addr *);
2217c478bd9Sstevel@tonic-gate static struct ifaddrlist *find_if(struct ifaddrlist *, int);
2227c478bd9Sstevel@tonic-gate static void finish();
2237c478bd9Sstevel@tonic-gate static void get_gwaddrs(char *[], int, union any_in_addr *,
2247c478bd9Sstevel@tonic-gate     union any_in_addr *, int *, int *);
2257c478bd9Sstevel@tonic-gate static void get_hostinfo(char *, int, struct addrinfo **);
2267c478bd9Sstevel@tonic-gate static ushort_t in_cksum(ushort_t *, int);
2277c478bd9Sstevel@tonic-gate static int int_arg(char *s, char *what);
2287c478bd9Sstevel@tonic-gate boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
2297c478bd9Sstevel@tonic-gate static void mirror_gws(union any_in_addr *, int);
230b08923d6SRobert Mustacchi static void *ns_warning_thr(void *);
231*9c4b0eaaSRobert Mustacchi static void parse_interval(const char *s);
2327c478bd9Sstevel@tonic-gate static void pinger(int, struct sockaddr *, struct msghdr *, int);
2337c478bd9Sstevel@tonic-gate char *pr_name(char *, int);
2347c478bd9Sstevel@tonic-gate char *pr_protocol(int);
2357c478bd9Sstevel@tonic-gate static void print_unknown_host_msg(const char *, const char *);
2367c478bd9Sstevel@tonic-gate static void recv_icmp_packet(struct addrinfo *, int, int, ushort_t, ushort_t);
2374c10bc16Spwernau static void resolve_nodes(struct addrinfo **, struct addrinfo **,
2384c10bc16Spwernau     union any_in_addr **);
2397c478bd9Sstevel@tonic-gate void schedule_sigalrm();
2407c478bd9Sstevel@tonic-gate static void select_all_src_addrs(union any_in_addr **, struct addrinfo *,
2417c478bd9Sstevel@tonic-gate     union any_in_addr *, union any_in_addr *);
2427c478bd9Sstevel@tonic-gate static void select_src_addr(union any_in_addr *, int, union any_in_addr *);
2437c478bd9Sstevel@tonic-gate void send_scheduled_probe();
2447c478bd9Sstevel@tonic-gate boolean_t seq_match(ushort_t, int, ushort_t);
2457c478bd9Sstevel@tonic-gate extern void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int,
2467c478bd9Sstevel@tonic-gate     uint_t);
2477c478bd9Sstevel@tonic-gate extern void set_IPv4_options(int, union any_in_addr *, int, struct in_addr *,
2487c478bd9Sstevel@tonic-gate     struct in_addr *);
2494c10bc16Spwernau static void set_nexthop(int, struct addrinfo *, int);
2504c10bc16Spwernau static boolean_t setup_socket(int, int *, int *, int *, ushort_t *,
2514c10bc16Spwernau     struct addrinfo *);
2527c478bd9Sstevel@tonic-gate void sigalrm_handler();
2537c478bd9Sstevel@tonic-gate void tvsub(struct timeval *, struct timeval *);
2547c478bd9Sstevel@tonic-gate static void usage(char *);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate /*
2577c478bd9Sstevel@tonic-gate  * main()
2587c478bd9Sstevel@tonic-gate  */
2598c332a0dSja int
main(int argc,char * argv[])2607c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
2617c478bd9Sstevel@tonic-gate {
2627c478bd9Sstevel@tonic-gate 	struct addrinfo	*ai_dst = NULL;		/* addrinfo host list */
2634c10bc16Spwernau 	struct addrinfo	*ai_nexthop = NULL;		/* addrinfo nexthop */
2647c478bd9Sstevel@tonic-gate 	union any_in_addr *src_addr_list = NULL;	/* src addrs to use */
2657c478bd9Sstevel@tonic-gate 	int recv_sock = -1;				/* receive sockets */
2667c478bd9Sstevel@tonic-gate 	int recv_sock6 = -1;
2677c478bd9Sstevel@tonic-gate 	ushort_t udp_src_port;			/* src ports for UDP probes */
2687c478bd9Sstevel@tonic-gate 	ushort_t udp_src_port6;			/* used to identify replies */
2697c478bd9Sstevel@tonic-gate 	uint_t flowinfo = 0;
2707c478bd9Sstevel@tonic-gate 	uint_t class = 0;
271e11c3f44Smeem 	char abuf[INET6_ADDRSTRLEN];
2727c478bd9Sstevel@tonic-gate 	int c;
2737c478bd9Sstevel@tonic-gate 	int i;
274f4b3ec61Sdh 	boolean_t has_sys_ip_config;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	progname = argv[0];
2777c478bd9Sstevel@tonic-gate 
278b08923d6SRobert Mustacchi 	(void) setlocale(LC_ALL, "");
279b08923d6SRobert Mustacchi 
2807c478bd9Sstevel@tonic-gate 	/*
2811da45818Spwernau 	 * This program needs the net_icmpaccess privilege for creating
282f4b3ec61Sdh 	 * raw ICMP sockets.  It needs sys_ip_config for using the
2831da45818Spwernau 	 * IP_NEXTHOP socket option (IPv4 only).  We'll fail
2847c478bd9Sstevel@tonic-gate 	 * on the socket call and report the error there when we have
2857c478bd9Sstevel@tonic-gate 	 * insufficient privileges.
2861da45818Spwernau 	 *
287f4b3ec61Sdh 	 * Shared-IP zones don't have the sys_ip_config privilege, so
2881da45818Spwernau 	 * we need to check for it in our limit set before trying
2891da45818Spwernau 	 * to set it.
2907c478bd9Sstevel@tonic-gate 	 */
291f4b3ec61Sdh 	has_sys_ip_config = priv_ineffect(PRIV_SYS_IP_CONFIG);
2921da45818Spwernau 
2937c478bd9Sstevel@tonic-gate 	(void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS,
294f4b3ec61Sdh 	    has_sys_ip_config ? PRIV_SYS_IP_CONFIG : (char *)NULL,
2951da45818Spwernau 	    (char *)NULL);
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	setbuf(stdout, (char *)0);
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv,
300bd670b35SErik Nordmark 	    "abA:c:dDF:G:g:I:i:LlnN:P:p:rRSsTt:UvX:x:Y0123?")) != -1) {
3017c478bd9Sstevel@tonic-gate 		switch ((char)c) {
3027c478bd9Sstevel@tonic-gate 		case 'A':
3037c478bd9Sstevel@tonic-gate 			if (strcmp(optarg, "inet") == 0) {
3047c478bd9Sstevel@tonic-gate 				family_input = AF_INET;
3057c478bd9Sstevel@tonic-gate 			} else if (strcmp(optarg, "inet6") == 0) {
3067c478bd9Sstevel@tonic-gate 				family_input = AF_INET6;
3077c478bd9Sstevel@tonic-gate 			} else {
3087c478bd9Sstevel@tonic-gate 				Fprintf(stderr,
3097c478bd9Sstevel@tonic-gate 				    "%s: unknown address family %s\n",
3107c478bd9Sstevel@tonic-gate 				    progname, optarg);
3117c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3127c478bd9Sstevel@tonic-gate 			}
3137c478bd9Sstevel@tonic-gate 			break;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 		case 'a':
3167c478bd9Sstevel@tonic-gate 			probe_all = _B_TRUE;
3177c478bd9Sstevel@tonic-gate 			break;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 		case 'c':
3207c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "traffic class");
3217c478bd9Sstevel@tonic-gate 			if (i > MAX_TRAFFIC_CLASS) {
3227c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: traffic class %d out of "
3237c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3247c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3257c478bd9Sstevel@tonic-gate 			}
3267c478bd9Sstevel@tonic-gate 			class = (uint_t)i;
3277c478bd9Sstevel@tonic-gate 			break;
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 		case 'd':
3307c478bd9Sstevel@tonic-gate 			options |= SO_DEBUG;
3317c478bd9Sstevel@tonic-gate 			break;
3327c478bd9Sstevel@tonic-gate 
333bd670b35SErik Nordmark 		case 'D':
334bd670b35SErik Nordmark 			dontfrag = 1;
335bd670b35SErik Nordmark 			break;
336bd670b35SErik Nordmark 
337a356273cSpwernau 		case 'b':
338a356273cSpwernau 			bypass = _B_TRUE;
339a356273cSpwernau 			break;
340a356273cSpwernau 
3417c478bd9Sstevel@tonic-gate 		case 'F':
3427c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "flow label");
3437c478bd9Sstevel@tonic-gate 			if (i > MAX_FLOW_LABEL) {
3447c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: flow label %d out of "
3457c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3467c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
3477c478bd9Sstevel@tonic-gate 			}
3487c478bd9Sstevel@tonic-gate 			flowinfo = (uint_t)i;
3497c478bd9Sstevel@tonic-gate 			break;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 		case 'I':
3527c478bd9Sstevel@tonic-gate 			stats = _B_TRUE;
353b08923d6SRobert Mustacchi 			parse_interval(optarg);
3547c478bd9Sstevel@tonic-gate 			break;
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		case 'i':
3577c478bd9Sstevel@tonic-gate 			/*
3587c478bd9Sstevel@tonic-gate 			 * this can accept interface index, interface name, and
3597c478bd9Sstevel@tonic-gate 			 * address configured on the interface
3607c478bd9Sstevel@tonic-gate 			 */
3617c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_IF;
3627c478bd9Sstevel@tonic-gate 			out_if.str = optarg;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 			if (inet_pton(AF_INET6, optarg, &out_if.id.addr) > 0) {
3657c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_ADDR6;
3667c478bd9Sstevel@tonic-gate 			} else if (inet_pton(AF_INET, optarg,
3677c478bd9Sstevel@tonic-gate 			    &out_if.id.addr) > 0) {
3687c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_ADDR;
3697c478bd9Sstevel@tonic-gate 			} else if (strcmp(optarg, "0") == 0) {
3707c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_INDEX;
3717c478bd9Sstevel@tonic-gate 				out_if.id.index = 0;
3727c478bd9Sstevel@tonic-gate 			} else if ((out_if.id.index = atoi(optarg)) != 0) {
3737c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_INDEX;
3747c478bd9Sstevel@tonic-gate 			} else {
3757c478bd9Sstevel@tonic-gate 				out_if.id.name = optarg;
3767c478bd9Sstevel@tonic-gate 				out_if.id_type = IF_NAME;
3777c478bd9Sstevel@tonic-gate 			}
3787c478bd9Sstevel@tonic-gate 			break;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 		case 'L':
3817c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_NOLOOP;
3827c478bd9Sstevel@tonic-gate 			break;
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 		case 'l':
3857c478bd9Sstevel@tonic-gate 			send_reply = _B_TRUE;
3867c478bd9Sstevel@tonic-gate 			strict = _B_FALSE;
3877c478bd9Sstevel@tonic-gate 			break;
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		case 'n':
3907c478bd9Sstevel@tonic-gate 			nflag = _B_TRUE;
3917c478bd9Sstevel@tonic-gate 			break;
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		case 'P':
3947c478bd9Sstevel@tonic-gate 			settos = _B_TRUE;
3957c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "type-of-service");
3967c478bd9Sstevel@tonic-gate 			if (i > MAX_TOS) {
3977c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: tos value %d out of "
3987c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
3997c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4007c478bd9Sstevel@tonic-gate 			}
4017c478bd9Sstevel@tonic-gate 			tos = (ushort_t)i;
4027c478bd9Sstevel@tonic-gate 			break;
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 		case 'p':
4057c478bd9Sstevel@tonic-gate 			i = int_arg(optarg, "port number");
4067c478bd9Sstevel@tonic-gate 			if (i > MAX_PORT) {
4077c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: port number %d out of "
4087c478bd9Sstevel@tonic-gate 				    "range\n", progname, i);
4097c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4107c478bd9Sstevel@tonic-gate 			}
4117c478bd9Sstevel@tonic-gate 			dest_port = (ushort_t)i;
4127c478bd9Sstevel@tonic-gate 			break;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		case 'r':
4157c478bd9Sstevel@tonic-gate 			options |= SO_DONTROUTE;
4167c478bd9Sstevel@tonic-gate 			break;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 		case 'R':
4197c478bd9Sstevel@tonic-gate 			rr_option = _B_TRUE;
4207c478bd9Sstevel@tonic-gate 			break;
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		case 'S':
4237c478bd9Sstevel@tonic-gate 			send_reply = _B_TRUE;
4247c478bd9Sstevel@tonic-gate 			strict = _B_TRUE;
4257c478bd9Sstevel@tonic-gate 			break;
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		case 's':
4287c478bd9Sstevel@tonic-gate 			stats = _B_TRUE;
4297c478bd9Sstevel@tonic-gate 			break;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 		case 'T':
4327c478bd9Sstevel@tonic-gate 			ts_option = _B_TRUE;
4337c478bd9Sstevel@tonic-gate 			break;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		case 't':
4367c478bd9Sstevel@tonic-gate 			moptions |= MULTICAST_TTL;
4377c478bd9Sstevel@tonic-gate 			hoplimit = int_arg(optarg, "ttl");
4387c478bd9Sstevel@tonic-gate 			if (hoplimit > MAXTTL) {
4397c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: ttl %d out of range\n",
4407c478bd9Sstevel@tonic-gate 				    progname, hoplimit);
4417c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4427c478bd9Sstevel@tonic-gate 			}
4437c478bd9Sstevel@tonic-gate 			break;
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 		case 'U':
4467c478bd9Sstevel@tonic-gate 			use_udp = _B_TRUE;
4477c478bd9Sstevel@tonic-gate 			use_icmp_ts = _B_FALSE;
4487c478bd9Sstevel@tonic-gate 			break;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 		case 'v':
4517c478bd9Sstevel@tonic-gate 			verbose = _B_TRUE;
4527c478bd9Sstevel@tonic-gate 			break;
4537c478bd9Sstevel@tonic-gate 		/*
4547c478bd9Sstevel@tonic-gate 		 * 'x' and 'X' has been undocumented flags for source routing.
4557c478bd9Sstevel@tonic-gate 		 * Now we document loose source routing with the new flag 'g',
4567c478bd9Sstevel@tonic-gate 		 * which is same as in traceroute. We still keep x/X as
4577c478bd9Sstevel@tonic-gate 		 * as undocumented. 'G', which is for strict source routing is
4587c478bd9Sstevel@tonic-gate 		 * also undocumented.
4597c478bd9Sstevel@tonic-gate 		 */
4607c478bd9Sstevel@tonic-gate 		case 'x':
4617c478bd9Sstevel@tonic-gate 		case 'g':
4627c478bd9Sstevel@tonic-gate 			strict = _B_FALSE;
4637c478bd9Sstevel@tonic-gate 			if (num_gw > MAXMAX_GWS) {
4647c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: too many gateways\n",
4657c478bd9Sstevel@tonic-gate 				    progname);
4667c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4677c478bd9Sstevel@tonic-gate 			}
4687c478bd9Sstevel@tonic-gate 			gw_list[num_gw++] = optarg;
4697c478bd9Sstevel@tonic-gate 			break;
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 		case 'X':
4727c478bd9Sstevel@tonic-gate 		case 'G':
4737c478bd9Sstevel@tonic-gate 			strict = _B_TRUE;
4747c478bd9Sstevel@tonic-gate 			if (num_gw > MAXMAX_GWS) {
4757c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: too many gateways\n",
4767c478bd9Sstevel@tonic-gate 				    progname);
4777c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
4787c478bd9Sstevel@tonic-gate 			}
4797c478bd9Sstevel@tonic-gate 			gw_list[num_gw++] = optarg;
4807c478bd9Sstevel@tonic-gate 			break;
4817c478bd9Sstevel@tonic-gate 
4824c10bc16Spwernau 		case 'N':
4834c10bc16Spwernau 			if (nexthop != NULL) {
4844c10bc16Spwernau 				Fprintf(stderr, "%s: only one next hop gateway"
4854c10bc16Spwernau 				    " allowed\n", progname);
4864c10bc16Spwernau 				exit(EXIT_FAILURE);
4874c10bc16Spwernau 			}
4884c10bc16Spwernau 			nexthop = optarg;
4894c10bc16Spwernau 			break;
4904c10bc16Spwernau 
4917c478bd9Sstevel@tonic-gate 		case 'Y':
4927c478bd9Sstevel@tonic-gate 			use_icmp_ts = _B_TRUE;
4937c478bd9Sstevel@tonic-gate 			use_udp = _B_FALSE;
4947c478bd9Sstevel@tonic-gate 			break;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 		case '0':
4977c478bd9Sstevel@tonic-gate 		case '1':
4987c478bd9Sstevel@tonic-gate 		case '2':
4997c478bd9Sstevel@tonic-gate 		case '3':
5007c478bd9Sstevel@tonic-gate 			ts_flag = (char)c - '0';
5017c478bd9Sstevel@tonic-gate 			break;
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 		case '?':
5047c478bd9Sstevel@tonic-gate 			usage(progname);
5057c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
5067c478bd9Sstevel@tonic-gate 			break;
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 		default:
5097c478bd9Sstevel@tonic-gate 			usage(progname);
5107c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
5117c478bd9Sstevel@tonic-gate 			break;
5127c478bd9Sstevel@tonic-gate 		}
5137c478bd9Sstevel@tonic-gate 	}
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	if (optind >= argc) {
5167c478bd9Sstevel@tonic-gate 		usage(progname);
5177c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	/*
5217c478bd9Sstevel@tonic-gate 	 * send_reply, which sends the probe packet back to itself
5227c478bd9Sstevel@tonic-gate 	 * doesn't work with UDP
5237c478bd9Sstevel@tonic-gate 	 */
5247c478bd9Sstevel@tonic-gate 	if (use_udp)
5257c478bd9Sstevel@tonic-gate 		send_reply = _B_FALSE;
5267c478bd9Sstevel@tonic-gate 
5273c58dfd6Sjbeck 	if (getenv("MACHINE_THAT_GOES_PING") != NULL)
5283c58dfd6Sjbeck 		stats = _B_TRUE;
5293c58dfd6Sjbeck 
5307c478bd9Sstevel@tonic-gate 	targethost = argv[optind];
5317c478bd9Sstevel@tonic-gate 	optind++;
5327c478bd9Sstevel@tonic-gate 	if (optind < argc) {
5337c478bd9Sstevel@tonic-gate 		if (stats) {
5347c478bd9Sstevel@tonic-gate 			datalen = int_arg(argv[optind], "data size");
5357c478bd9Sstevel@tonic-gate 			optind++;
5367c478bd9Sstevel@tonic-gate 			if (optind < argc) {
5377c478bd9Sstevel@tonic-gate 				npackets = int_arg(argv[optind],
5387c478bd9Sstevel@tonic-gate 				    "packet count");
5397c478bd9Sstevel@tonic-gate 				if (npackets < 1) {
5407c478bd9Sstevel@tonic-gate 					Fprintf(stderr, "%s: packet count %d "
5417c478bd9Sstevel@tonic-gate 					    "out of range\n", progname,
5427c478bd9Sstevel@tonic-gate 					    npackets);
5437c478bd9Sstevel@tonic-gate 					exit(EXIT_FAILURE);
5447c478bd9Sstevel@tonic-gate 				}
5457c478bd9Sstevel@tonic-gate 			}
5467c478bd9Sstevel@tonic-gate 		} else {
5477c478bd9Sstevel@tonic-gate 			timeout = int_arg(argv[optind], "timeout");
5487c478bd9Sstevel@tonic-gate 		}
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	/*
5527c478bd9Sstevel@tonic-gate 	 * Let's prepare sockaddr_in* structures, cause we might need both of
5537c478bd9Sstevel@tonic-gate 	 * them.
5547c478bd9Sstevel@tonic-gate 	 */
5557c478bd9Sstevel@tonic-gate 	bzero((char *)&to, sizeof (struct sockaddr_in));
5567c478bd9Sstevel@tonic-gate 	to.sin_family = AF_INET;
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	bzero((char *)&to6, sizeof (struct sockaddr_in6));
5597c478bd9Sstevel@tonic-gate 	to6.sin6_family = AF_INET6;
5607c478bd9Sstevel@tonic-gate 	to6.sin6_flowinfo = htonl((class << 20) | flowinfo);
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	if (stats)
5637c478bd9Sstevel@tonic-gate 		(void) sigset(SIGINT, finish);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	ident = (int)getpid() & 0xFFFF;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	/* resolve the hostnames */
5684c10bc16Spwernau 	resolve_nodes(&ai_dst, &ai_nexthop, &src_addr_list);
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	/*
5717c478bd9Sstevel@tonic-gate 	 * We should make sure datalen is reasonable.
572*9c4b0eaaSRobert Mustacchi 	 *	IP_MAXPACKET >= IPv4/IPv6 header length +
5737c478bd9Sstevel@tonic-gate 	 *			IPv4 options/IPv6 routing header length +
5747c478bd9Sstevel@tonic-gate 	 *			ICMP/ICMP6/UDP header length +
5757c478bd9Sstevel@tonic-gate 	 *			datalen
5767c478bd9Sstevel@tonic-gate 	 */
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	if (family_input == AF_INET6 ||
5797c478bd9Sstevel@tonic-gate 	    (family_input == AF_UNSPEC && num_v6 != 0)) {
5807c478bd9Sstevel@tonic-gate 		size_t exthdr_len = 0;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 		if (send_reply) {
5837c478bd9Sstevel@tonic-gate 			exthdr_len = sizeof (struct ip6_rthdr0) +
5847c478bd9Sstevel@tonic-gate 			    2 * num_gw * sizeof (struct in6_addr);
5857c478bd9Sstevel@tonic-gate 		} else if (num_gw > 0) {
5867c478bd9Sstevel@tonic-gate 			exthdr_len = sizeof (struct ip6_rthdr0) +
5877c478bd9Sstevel@tonic-gate 			    num_gw * sizeof (struct in6_addr);
5887c478bd9Sstevel@tonic-gate 		}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 		/*
5917c478bd9Sstevel@tonic-gate 		 * Size of ICMP6 header and UDP header are the same. Let's
5927c478bd9Sstevel@tonic-gate 		 * use ICMP6_MINLEN.
5937c478bd9Sstevel@tonic-gate 		 */
5947c478bd9Sstevel@tonic-gate 		if (datalen > (IP_MAXPACKET - (sizeof (struct ip6_hdr) +
5957c478bd9Sstevel@tonic-gate 		    exthdr_len + ICMP6_MINLEN))) {
5967c478bd9Sstevel@tonic-gate 			Fprintf(stderr,
5977c478bd9Sstevel@tonic-gate 			    "%s: data size too large for IPv6 packet\n",
5987c478bd9Sstevel@tonic-gate 			    progname);
5997c478bd9Sstevel@tonic-gate 			num_v6 = 0;
6007c478bd9Sstevel@tonic-gate 		}
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	if (family_input == AF_INET ||
6047c478bd9Sstevel@tonic-gate 	    (family_input == AF_UNSPEC && num_v4 != 0)) {
6057c478bd9Sstevel@tonic-gate 		size_t opt_len = 0;
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 		if (send_reply) {
6087c478bd9Sstevel@tonic-gate 			/*
6097c478bd9Sstevel@tonic-gate 			 * Includes 3 bytes code+ptr+len, the intermediate
6107c478bd9Sstevel@tonic-gate 			 * gateways, the actual and the effective target.
6117c478bd9Sstevel@tonic-gate 			 */
6127c478bd9Sstevel@tonic-gate 			opt_len = 3 +
6137c478bd9Sstevel@tonic-gate 			    (2 * num_gw + 2) * sizeof (struct in_addr);
6147c478bd9Sstevel@tonic-gate 		} else if (num_gw > 0) {
6157c478bd9Sstevel@tonic-gate 			opt_len = 3 + (num_gw + 1) * sizeof (struct in_addr);
6167c478bd9Sstevel@tonic-gate 		}
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		if (rr_option) {
6197c478bd9Sstevel@tonic-gate 			opt_len = MAX_IPOPTLEN;
6207c478bd9Sstevel@tonic-gate 		} else if (ts_option) {
6217c478bd9Sstevel@tonic-gate 			if ((ts_flag & 0x0f) <= IPOPT_TS_TSANDADDR) {
6227c478bd9Sstevel@tonic-gate 				opt_len = MAX_IPOPTLEN;
6237c478bd9Sstevel@tonic-gate 			} else {
6247c478bd9Sstevel@tonic-gate 				opt_len += IPOPT_MINOFF +
6257c478bd9Sstevel@tonic-gate 				    2 * sizeof (struct ipt_ta);
6267c478bd9Sstevel@tonic-gate 				/*
6277c478bd9Sstevel@tonic-gate 				 * Note: BSD/4.X is broken in their check so we
6287c478bd9Sstevel@tonic-gate 				 * have to  bump up this number by at least one.
6297c478bd9Sstevel@tonic-gate 				 */
6307c478bd9Sstevel@tonic-gate 				opt_len++;
6317c478bd9Sstevel@tonic-gate 			}
6327c478bd9Sstevel@tonic-gate 		}
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 		/* Round up to 4 byte boundary */
6357c478bd9Sstevel@tonic-gate 		if (opt_len & 0x3)
6367c478bd9Sstevel@tonic-gate 			opt_len = (opt_len & ~0x3) + 4;
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 		if (datalen > (IP_MAXPACKET - (sizeof (struct ip) + opt_len +
6397c478bd9Sstevel@tonic-gate 		    ICMP_MINLEN))) {
6407c478bd9Sstevel@tonic-gate 			Fprintf(stderr,
6417c478bd9Sstevel@tonic-gate 			    "%s: data size too large for IPv4 packet\n",
6427c478bd9Sstevel@tonic-gate 			    progname);
6437c478bd9Sstevel@tonic-gate 			num_v4 = 0;
6447c478bd9Sstevel@tonic-gate 		}
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	if (num_v4 == 0 && num_v6 == 0) {
6487c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
6497c478bd9Sstevel@tonic-gate 	}
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	/* setup the sockets */
6527c478bd9Sstevel@tonic-gate 	if (num_v6 != 0) {
6537c478bd9Sstevel@tonic-gate 		if (!setup_socket(AF_INET6, &send_sock6, &recv_sock6,
6544c10bc16Spwernau 		    &if_index, &udp_src_port6, ai_nexthop))
6557c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
6567c478bd9Sstevel@tonic-gate 	}
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	if (num_v4 != 0) {
6597c478bd9Sstevel@tonic-gate 		if (!setup_socket(AF_INET, &send_sock, &recv_sock, &if_index,
6604c10bc16Spwernau 		    &udp_src_port, ai_nexthop))
6617c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	__priv_relinquish();
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 	/*
6677c478bd9Sstevel@tonic-gate 	 * If sending back to ourself, add the mirror image of current
6687c478bd9Sstevel@tonic-gate 	 * gateways, so that the probes travel to and from the target
6697c478bd9Sstevel@tonic-gate 	 * by visiting the same gateways in reverse order.
6707c478bd9Sstevel@tonic-gate 	 */
6717c478bd9Sstevel@tonic-gate 	if (send_reply) {
6727c478bd9Sstevel@tonic-gate 		if (num_v6 != 0)
6737c478bd9Sstevel@tonic-gate 			mirror_gws(gw_IP_list6, AF_INET6);
6747c478bd9Sstevel@tonic-gate 		if (num_v4 != 0)
6757c478bd9Sstevel@tonic-gate 			mirror_gws(gw_IP_list, AF_INET);
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 		/* We add 1 because we put the target as the middle gateway */
6787c478bd9Sstevel@tonic-gate 		eff_num_gw = 2 * num_gw + 1;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	} else {
6817c478bd9Sstevel@tonic-gate 		eff_num_gw = num_gw;
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	targetaddr_list = build_targetaddr_list(ai_dst, src_addr_list);
6857c478bd9Sstevel@tonic-gate 	current_targetaddr = targetaddr_list;
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	/*
6887c478bd9Sstevel@tonic-gate 	 * Set the starting_seq_num for the first targetaddr.
6897c478bd9Sstevel@tonic-gate 	 * If we are sending ICMP Echo Requests, the sequence number is same as
6907c478bd9Sstevel@tonic-gate 	 * ICMP sequence number, and it starts from zero. If we are sending UDP
6917c478bd9Sstevel@tonic-gate 	 * packets, the sequence number is the destination UDP port number,
6927c478bd9Sstevel@tonic-gate 	 * which starts from dest_port. At each probe, this sequence number is
6937c478bd9Sstevel@tonic-gate 	 * incremented by one.
6947c478bd9Sstevel@tonic-gate 	 * We set the starting_seq_num for first targetaddr here. The
6957c478bd9Sstevel@tonic-gate 	 * following ones will be set by looking at where we left with the last
6967c478bd9Sstevel@tonic-gate 	 * targetaddr.
6977c478bd9Sstevel@tonic-gate 	 */
6987c478bd9Sstevel@tonic-gate 	current_targetaddr->starting_seq_num = use_udp ? dest_port : 0;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	if (stats) {
7017c478bd9Sstevel@tonic-gate 		if (probe_all || !nflag) {
7027c478bd9Sstevel@tonic-gate 			Printf("PING %s: %d data bytes\n", targethost, datalen);
7037c478bd9Sstevel@tonic-gate 		} else {
7047c478bd9Sstevel@tonic-gate 			if (ai_dst->ai_family == AF_INET) {
705e11c3f44Smeem 				(void) inet_ntop(AF_INET,
706e11c3f44Smeem 				    &((struct sockaddr_in *)(void *)
707e11c3f44Smeem 				    ai_dst->ai_addr)->sin_addr,
708e11c3f44Smeem 				    abuf, sizeof (abuf));
7097c478bd9Sstevel@tonic-gate 			} else {
710e11c3f44Smeem 				(void) inet_ntop(AF_INET6,
711e11c3f44Smeem 				    &((struct sockaddr_in6 *)(void *)
712e11c3f44Smeem 				    ai_dst->ai_addr)->sin6_addr,
713e11c3f44Smeem 				    abuf, sizeof (abuf));
7147c478bd9Sstevel@tonic-gate 			}
715e11c3f44Smeem 			Printf("PING %s (%s): %d data bytes\n",
716e11c3f44Smeem 			    targethost, abuf, datalen);
7177c478bd9Sstevel@tonic-gate 		}
7187c478bd9Sstevel@tonic-gate 	}
7197c478bd9Sstevel@tonic-gate 
720b08923d6SRobert Mustacchi 	/* Create our timer for future use */
721b08923d6SRobert Mustacchi 	if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) {
722b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: failed to create timer: %s\n",
723b08923d6SRobert Mustacchi 		    progname, strerror(errno));
724b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
725b08923d6SRobert Mustacchi 	}
726b08923d6SRobert Mustacchi 
727b08923d6SRobert Mustacchi 	/*
728b08923d6SRobert Mustacchi 	 * Finally start up the name services warning thread.
729b08923d6SRobert Mustacchi 	 */
730b08923d6SRobert Mustacchi 	if (thr_create(NULL, 0, ns_warning_thr, NULL,
731b08923d6SRobert Mustacchi 	    THR_DETACHED | THR_DAEMON, NULL) != 0) {
732b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: failed to create name services "
733b08923d6SRobert Mustacchi 		    "thread: %s\n", progname, strerror(errno));
734b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
735b08923d6SRobert Mustacchi 	}
736b08923d6SRobert Mustacchi 
7377c478bd9Sstevel@tonic-gate 	/* Let's get things going */
7387c478bd9Sstevel@tonic-gate 	send_scheduled_probe();
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 	/* SIGALRM is used to send the next scheduled probe */
7417c478bd9Sstevel@tonic-gate 	(void) sigset(SIGALRM, sigalrm_handler);
7427c478bd9Sstevel@tonic-gate 	schedule_sigalrm();
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	/*
7457c478bd9Sstevel@tonic-gate 	 * From now on, we'll always be listening to ICMP packets. As SIGALRM
7467c478bd9Sstevel@tonic-gate 	 * comes in, sigalrm_handler() will be invoked and send another
7477c478bd9Sstevel@tonic-gate 	 * probe.
7487c478bd9Sstevel@tonic-gate 	 */
7497c478bd9Sstevel@tonic-gate 	recv_icmp_packet(ai_dst, recv_sock6, recv_sock, udp_src_port6,
7507c478bd9Sstevel@tonic-gate 	    udp_src_port);
7517c478bd9Sstevel@tonic-gate 
7528c332a0dSja 	return (EXIT_SUCCESS);	/* should never come here */
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate  * Build the target IP address list. Use command line options and
7577c478bd9Sstevel@tonic-gate  * name lookup results returned from name server to determine which addresses
7587c478bd9Sstevel@tonic-gate  * to probe, how many times, in which order.
7597c478bd9Sstevel@tonic-gate  */
7607c478bd9Sstevel@tonic-gate static struct targetaddr *
build_targetaddr_list(struct addrinfo * ai_dst,union any_in_addr * src_addr_list)7617c478bd9Sstevel@tonic-gate build_targetaddr_list(struct addrinfo *ai_dst, union any_in_addr *src_addr_list)
7627c478bd9Sstevel@tonic-gate {
7637c478bd9Sstevel@tonic-gate 	struct targetaddr *head = NULL;
7647c478bd9Sstevel@tonic-gate 	struct targetaddr *targetaddr;
7657c478bd9Sstevel@tonic-gate 	struct targetaddr **nextp;
7667c478bd9Sstevel@tonic-gate 	int num_dst;
7677c478bd9Sstevel@tonic-gate 	int i;
7687c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	aip = ai_dst;
7717c478bd9Sstevel@tonic-gate 	if (probe_all)
7727c478bd9Sstevel@tonic-gate 		num_dst = num_v4 + num_v6;
7737c478bd9Sstevel@tonic-gate 	else
7747c478bd9Sstevel@tonic-gate 		num_dst = 1;
7757c478bd9Sstevel@tonic-gate 	num_targetaddrs = num_dst;
7767c478bd9Sstevel@tonic-gate 	nextp = &head;
7777c478bd9Sstevel@tonic-gate 	for (aip = ai_dst, i = 0; aip != NULL; aip = aip->ai_next, i++) {
7787c478bd9Sstevel@tonic-gate 		if (aip->ai_family == AF_INET && num_v4 != 0) {
7797c478bd9Sstevel@tonic-gate 			targetaddr = create_targetaddr_item(aip->ai_family,
7807c478bd9Sstevel@tonic-gate 			    (union any_in_addr *)
7817c478bd9Sstevel@tonic-gate 			    /* LINTED E_BAD_PTR_CAST_ALIGN */
7827c478bd9Sstevel@tonic-gate 			    &((struct sockaddr_in *)
7837c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin_addr,
7847c478bd9Sstevel@tonic-gate 			    &src_addr_list[i]);
7857c478bd9Sstevel@tonic-gate 		} else if (aip->ai_family == AF_INET6 && num_v6 != 0) {
7867c478bd9Sstevel@tonic-gate 			targetaddr = create_targetaddr_item(aip->ai_family,
7877c478bd9Sstevel@tonic-gate 			    (union any_in_addr *)
7887c478bd9Sstevel@tonic-gate 			    /* LINTED E_BAD_PTR_CAST_ALIGN */
7897c478bd9Sstevel@tonic-gate 			    &((struct sockaddr_in6 *)
7907c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin6_addr,
7917c478bd9Sstevel@tonic-gate 			    &src_addr_list[i]);
7927c478bd9Sstevel@tonic-gate 		} else {
7937c478bd9Sstevel@tonic-gate 			continue;
7947c478bd9Sstevel@tonic-gate 		}
7957c478bd9Sstevel@tonic-gate 		*nextp = targetaddr;
7967c478bd9Sstevel@tonic-gate 		nextp = &targetaddr->next;
7977c478bd9Sstevel@tonic-gate 		if (num_targetaddrs == 1)
7987c478bd9Sstevel@tonic-gate 			break;
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 	if (npackets == 0 && stats)
8017c478bd9Sstevel@tonic-gate 		*nextp = head;	/* keep going indefinitely */
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	return (head);
8047c478bd9Sstevel@tonic-gate }
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate /*
8077c478bd9Sstevel@tonic-gate  * Given an address family, dst and src addresses, by also looking at the
8087c478bd9Sstevel@tonic-gate  * options provided at the command line, this function creates a targetaddr
8097c478bd9Sstevel@tonic-gate  * to be linked with others, forming a global targetaddr list. Each targetaddr
8107c478bd9Sstevel@tonic-gate  * item contains information about probes sent to a specific IP address.
8117c478bd9Sstevel@tonic-gate  */
8127c478bd9Sstevel@tonic-gate static struct targetaddr *
create_targetaddr_item(int family,union any_in_addr * dst_addr,union any_in_addr * src_addr)8137c478bd9Sstevel@tonic-gate create_targetaddr_item(int family, union any_in_addr *dst_addr,
8147c478bd9Sstevel@tonic-gate     union any_in_addr *src_addr)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate 	struct targetaddr *targetaddr;
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	targetaddr = (struct targetaddr *)malloc(sizeof (struct targetaddr));
8197c478bd9Sstevel@tonic-gate 	if (targetaddr == NULL) {
8207c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc %s\n", progname, strerror(errno));
8217c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate 	targetaddr->family = family;
8247c478bd9Sstevel@tonic-gate 	targetaddr->dst_addr = *dst_addr;
8257c478bd9Sstevel@tonic-gate 	targetaddr->src_addr = *src_addr;
8267c478bd9Sstevel@tonic-gate 	if (stats) {
8277c478bd9Sstevel@tonic-gate 		/*
8287c478bd9Sstevel@tonic-gate 		 * npackets is only defined if we are in stats mode.
8297c478bd9Sstevel@tonic-gate 		 * npackets determines how many probes to send to each target
8307c478bd9Sstevel@tonic-gate 		 * IP address. npackets == 0 means send only 1 and move on to
8317c478bd9Sstevel@tonic-gate 		 * next target IP.
8327c478bd9Sstevel@tonic-gate 		 */
8337c478bd9Sstevel@tonic-gate 		if (npackets > 0)
8347c478bd9Sstevel@tonic-gate 			targetaddr->num_probes = npackets;
8357c478bd9Sstevel@tonic-gate 		else
8367c478bd9Sstevel@tonic-gate 			targetaddr->num_probes = 1;
8377c478bd9Sstevel@tonic-gate 	} else {
8387c478bd9Sstevel@tonic-gate 		targetaddr->num_probes = timeout;
8397c478bd9Sstevel@tonic-gate 	}
8407c478bd9Sstevel@tonic-gate 	targetaddr->num_sent = 0;
8417c478bd9Sstevel@tonic-gate 	targetaddr->got_reply = _B_FALSE;
8427c478bd9Sstevel@tonic-gate 	targetaddr->probing_done = _B_FALSE;
8437c478bd9Sstevel@tonic-gate 	targetaddr->starting_seq_num = 0; /* actual value will be set later */
8447c478bd9Sstevel@tonic-gate 	targetaddr->next = NULL;	/* actual value will be set later */
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	return (targetaddr);
8477c478bd9Sstevel@tonic-gate }
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate /*
8507c478bd9Sstevel@tonic-gate  * print "unknown host" message
8517c478bd9Sstevel@tonic-gate  */
8527c478bd9Sstevel@tonic-gate static void
print_unknown_host_msg(const char * protocol,const char * hostname)8537c478bd9Sstevel@tonic-gate print_unknown_host_msg(const char *protocol, const char *hostname)
8547c478bd9Sstevel@tonic-gate {
8557c478bd9Sstevel@tonic-gate 	Fprintf(stderr, "%s: unknown%s host %s\n", progname, protocol,
8567c478bd9Sstevel@tonic-gate 	    hostname);
8577c478bd9Sstevel@tonic-gate }
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate /*
8607c478bd9Sstevel@tonic-gate  * Resolve hostnames for the target host and gateways. Also, determine source
8617c478bd9Sstevel@tonic-gate  * addresses to use for each target address.
8627c478bd9Sstevel@tonic-gate  */
8637c478bd9Sstevel@tonic-gate static void
resolve_nodes(struct addrinfo ** ai_dstp,struct addrinfo ** ai_nexthopp,union any_in_addr ** src_addr_listp)8644c10bc16Spwernau resolve_nodes(struct addrinfo **ai_dstp, struct addrinfo **ai_nexthopp,
8654c10bc16Spwernau     union any_in_addr **src_addr_listp)
8667c478bd9Sstevel@tonic-gate {
8677c478bd9Sstevel@tonic-gate 	struct addrinfo *ai_dst = NULL;
8684c10bc16Spwernau 	struct addrinfo *ai_nexthop = NULL;
8697c478bd9Sstevel@tonic-gate 	struct addrinfo *aip = NULL;
8707c478bd9Sstevel@tonic-gate 	union any_in_addr *src_addr_list = NULL;
8717c478bd9Sstevel@tonic-gate 	int num_resolved_gw = 0;
8727c478bd9Sstevel@tonic-gate 	int num_resolved_gw6 = 0;
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 	get_hostinfo(targethost, family_input, &ai_dst);
8757c478bd9Sstevel@tonic-gate 	if (ai_dst == NULL) {
8767c478bd9Sstevel@tonic-gate 		print_unknown_host_msg("", targethost);
8777c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
8787c478bd9Sstevel@tonic-gate 	}
8794c10bc16Spwernau 	if (nexthop != NULL) {
8804c10bc16Spwernau 		get_hostinfo(nexthop, family_input, &ai_nexthop);
8814c10bc16Spwernau 		if (ai_nexthop == NULL) {
8824c10bc16Spwernau 			print_unknown_host_msg("", nexthop);
8834c10bc16Spwernau 			exit(EXIT_FAILURE);
8844c10bc16Spwernau 		}
8854c10bc16Spwernau 	}
8867c478bd9Sstevel@tonic-gate 	/* Get a count of the v4 & v6 addresses */
8877c478bd9Sstevel@tonic-gate 	for (aip = ai_dst; aip != NULL; aip = aip->ai_next) {
8887c478bd9Sstevel@tonic-gate 		switch (aip->ai_family) {
8897c478bd9Sstevel@tonic-gate 		case AF_INET:
8907c478bd9Sstevel@tonic-gate 			num_v4++;
8917c478bd9Sstevel@tonic-gate 			break;
8927c478bd9Sstevel@tonic-gate 		case AF_INET6:
8937c478bd9Sstevel@tonic-gate 			num_v6++;
8947c478bd9Sstevel@tonic-gate 			break;
8957c478bd9Sstevel@tonic-gate 		}
8967c478bd9Sstevel@tonic-gate 	}
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	if (family_input == AF_UNSPEC && !probe_all) {
8997c478bd9Sstevel@tonic-gate 		family_input = ai_dst->ai_family;
9007c478bd9Sstevel@tonic-gate 	}
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	/* resolve gateways */
9037c478bd9Sstevel@tonic-gate 	if (num_gw > 0) {
9047c478bd9Sstevel@tonic-gate 		get_gwaddrs(gw_list, family_input, gw_IP_list, gw_IP_list6,
9057c478bd9Sstevel@tonic-gate 		    &num_resolved_gw, &num_resolved_gw6);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 		/* we couldn't resolve a gateway as an IPv6 host */
9087c478bd9Sstevel@tonic-gate 		if (num_resolved_gw6 != num_gw && num_v6 != 0 &&
9097c478bd9Sstevel@tonic-gate 		    (family_input == AF_INET6 || family_input == AF_UNSPEC)) {
9107c478bd9Sstevel@tonic-gate 			print_unknown_host_msg(" IPv6",
9117c478bd9Sstevel@tonic-gate 			    gw_list[num_resolved_gw6]);
9127c478bd9Sstevel@tonic-gate 			num_v6 = 0;
9137c478bd9Sstevel@tonic-gate 		}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 		/* we couldn't resolve a gateway as an IPv4 host */
9167c478bd9Sstevel@tonic-gate 		if (num_resolved_gw != num_gw && num_v4 != 0 &&
9177c478bd9Sstevel@tonic-gate 		    (family_input == AF_INET || family_input == AF_UNSPEC)) {
9187c478bd9Sstevel@tonic-gate 			print_unknown_host_msg(" IPv4",
9197c478bd9Sstevel@tonic-gate 			    gw_list[num_resolved_gw]);
9207c478bd9Sstevel@tonic-gate 			num_v4 = 0;
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	if (num_v4 == 0 && num_v6 == 0)
9257c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	select_all_src_addrs(&src_addr_list, ai_dst, gw_IP_list, gw_IP_list6);
9287c478bd9Sstevel@tonic-gate 	*ai_dstp = ai_dst;
9294c10bc16Spwernau 	*ai_nexthopp = ai_nexthop;
9307c478bd9Sstevel@tonic-gate 	*src_addr_listp = src_addr_list;
9317c478bd9Sstevel@tonic-gate }
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate /*
9347c478bd9Sstevel@tonic-gate  * Resolve the gateway names, splitting results into v4 and v6 lists.
9357c478bd9Sstevel@tonic-gate  * Gateway addresses are added to the appropriate passed-in array; the
9367c478bd9Sstevel@tonic-gate  * number of resolved gateways for each af is returned in resolved[6].
9377c478bd9Sstevel@tonic-gate  * Assumes that passed-in arrays are large enough for MAX_GWS[6] addrs
9387c478bd9Sstevel@tonic-gate  * and resolved[6] ptrs are non-null; ignores array and counter if the
9397c478bd9Sstevel@tonic-gate  * address family param makes them irrelevant.
9407c478bd9Sstevel@tonic-gate  */
9417c478bd9Sstevel@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)9427c478bd9Sstevel@tonic-gate get_gwaddrs(char **gw_list, int family, union any_in_addr *gwIPlist,
9437c478bd9Sstevel@tonic-gate     union any_in_addr *gwIPlist6, int *resolved, int *resolved6)
9447c478bd9Sstevel@tonic-gate {
9457c478bd9Sstevel@tonic-gate 	int i;
9467c478bd9Sstevel@tonic-gate 	boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE;
9477c478bd9Sstevel@tonic-gate 	struct addrinfo	*ai = NULL;
9487c478bd9Sstevel@tonic-gate 	struct addrinfo	*aip = NULL;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	*resolved = *resolved6 = 0;
9517c478bd9Sstevel@tonic-gate 	switch (family) {
9527c478bd9Sstevel@tonic-gate 	case AF_UNSPEC:
9537c478bd9Sstevel@tonic-gate 		break;
9547c478bd9Sstevel@tonic-gate 	case AF_INET:
9557c478bd9Sstevel@tonic-gate 		check_v6 = _B_FALSE;
9567c478bd9Sstevel@tonic-gate 		break;
9577c478bd9Sstevel@tonic-gate 	case AF_INET6:
9587c478bd9Sstevel@tonic-gate 		check_v4 = _B_FALSE;
9597c478bd9Sstevel@tonic-gate 		break;
9607c478bd9Sstevel@tonic-gate 	default:
9617c478bd9Sstevel@tonic-gate 		return;
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	if (check_v4 && num_gw >= MAX_GWS) {
9657c478bd9Sstevel@tonic-gate 		check_v4 = _B_FALSE;
9667c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many IPv4 gateways\n", progname);
9677c478bd9Sstevel@tonic-gate 	}
9687c478bd9Sstevel@tonic-gate 	if (check_v6 && num_gw > MAX_GWS6) {
9697c478bd9Sstevel@tonic-gate 		check_v6 = _B_FALSE;
9707c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many IPv6 gateways\n", progname);
9717c478bd9Sstevel@tonic-gate 	}
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_gw; i++) {
9747c478bd9Sstevel@tonic-gate 		if (!check_v4 && !check_v6)
9757c478bd9Sstevel@tonic-gate 			return;
9767c478bd9Sstevel@tonic-gate 		get_hostinfo(gw_list[i], family, &ai);
9777c478bd9Sstevel@tonic-gate 		if (ai == NULL)
9787c478bd9Sstevel@tonic-gate 			return;
9797c478bd9Sstevel@tonic-gate 		if (check_v4 && num_v4 != 0) {
9807c478bd9Sstevel@tonic-gate 			for (aip = ai; aip != NULL; aip = aip->ai_next) {
9817c478bd9Sstevel@tonic-gate 				if (aip->ai_family == AF_INET) {
9827c478bd9Sstevel@tonic-gate 					/* LINTED E_BAD_PTR_CAST_ALIGN */
9837c478bd9Sstevel@tonic-gate 					bcopy(&((struct sockaddr_in *)
9847c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin_addr,
9857c478bd9Sstevel@tonic-gate 					    &gwIPlist[i].addr,
9867c478bd9Sstevel@tonic-gate 					    aip->ai_addrlen);
9877c478bd9Sstevel@tonic-gate 					(*resolved)++;
9887c478bd9Sstevel@tonic-gate 					break;
9897c478bd9Sstevel@tonic-gate 				}
9907c478bd9Sstevel@tonic-gate 			}
9917c478bd9Sstevel@tonic-gate 		} else if (check_v4) {
9927c478bd9Sstevel@tonic-gate 			check_v4 = _B_FALSE;
9937c478bd9Sstevel@tonic-gate 		}
9947c478bd9Sstevel@tonic-gate 		if (check_v6 && num_v6 != 0) {
9957c478bd9Sstevel@tonic-gate 			for (aip = ai; aip != NULL; aip = aip->ai_next) {
9967c478bd9Sstevel@tonic-gate 				if (aip->ai_family == AF_INET6) {
9977c478bd9Sstevel@tonic-gate 					/* LINTED E_BAD_PTR_CAST_ALIGN */
9987c478bd9Sstevel@tonic-gate 					bcopy(&((struct sockaddr_in6 *)
9997c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin6_addr,
10007c478bd9Sstevel@tonic-gate 					    &gwIPlist6[i].addr6,
10017c478bd9Sstevel@tonic-gate 					    aip->ai_addrlen);
10027c478bd9Sstevel@tonic-gate 					(*resolved6)++;
10037c478bd9Sstevel@tonic-gate 					break;
10047c478bd9Sstevel@tonic-gate 				}
10057c478bd9Sstevel@tonic-gate 			}
10067c478bd9Sstevel@tonic-gate 		} else if (check_v6) {
10077c478bd9Sstevel@tonic-gate 			check_v6 = _B_FALSE;
10087c478bd9Sstevel@tonic-gate 		}
10097c478bd9Sstevel@tonic-gate 	}
10107c478bd9Sstevel@tonic-gate 	freeaddrinfo(ai);
10117c478bd9Sstevel@tonic-gate }
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate /*
10147c478bd9Sstevel@tonic-gate  * Given the list of gateways, extends the list with its mirror image. This is
10157c478bd9Sstevel@tonic-gate  * used when -l/-S is used. The middle gateway will be the target address. We'll
10167c478bd9Sstevel@tonic-gate  * leave it blank for now.
10177c478bd9Sstevel@tonic-gate  */
10187c478bd9Sstevel@tonic-gate static void
mirror_gws(union any_in_addr * gwIPlist,int family)10197c478bd9Sstevel@tonic-gate mirror_gws(union any_in_addr *gwIPlist, int family)
10207c478bd9Sstevel@tonic-gate {
10217c478bd9Sstevel@tonic-gate 	int effective_num_gw;
10227c478bd9Sstevel@tonic-gate 	int i;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	/* We add 1 because we put the target as the middle gateway */
10257c478bd9Sstevel@tonic-gate 	effective_num_gw = 2 * num_gw + 1;
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	if ((family == AF_INET && effective_num_gw >= MAX_GWS) ||
10287c478bd9Sstevel@tonic-gate 	    (family == AF_INET6 && effective_num_gw > MAX_GWS6)) {
10297c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: too many %s gateways\n",
10307c478bd9Sstevel@tonic-gate 		    progname, (family == AF_INET) ? "IPv4" : "IPv6");
10317c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
10327c478bd9Sstevel@tonic-gate 	}
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_gw; i++)
10357c478bd9Sstevel@tonic-gate 		gwIPlist[num_gw + i + 1].addr6 = gwIPlist[num_gw - i - 1].addr6;
10367c478bd9Sstevel@tonic-gate }
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate /*
10397c478bd9Sstevel@tonic-gate  * Given IP address or hostname, return addrinfo list.
10407c478bd9Sstevel@tonic-gate  * Assumes that addrinfo ** ptr is non-null.
10417c478bd9Sstevel@tonic-gate  */
10427c478bd9Sstevel@tonic-gate static void
get_hostinfo(char * host,int family,struct addrinfo ** aipp)10437c478bd9Sstevel@tonic-gate get_hostinfo(char *host, int family, struct addrinfo **aipp)
10447c478bd9Sstevel@tonic-gate {
10457c478bd9Sstevel@tonic-gate 	struct addrinfo hints, *ai;
10467c478bd9Sstevel@tonic-gate 	struct in6_addr addr6;
10477c478bd9Sstevel@tonic-gate 	struct in_addr addr;
10487c478bd9Sstevel@tonic-gate 	boolean_t broadcast;		/* is this 255.255.255.255? */
10497c478bd9Sstevel@tonic-gate 	char tmp_buf[INET6_ADDRSTRLEN];
10507c478bd9Sstevel@tonic-gate 	int rc;
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/* check if broadcast */
10537c478bd9Sstevel@tonic-gate 	if (strcmp(host, "255.255.255.255") == 0)
10547c478bd9Sstevel@tonic-gate 		broadcast = _B_TRUE;
10557c478bd9Sstevel@tonic-gate 	else
10567c478bd9Sstevel@tonic-gate 		broadcast = _B_FALSE;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	/* check if IPv4-mapped address or broadcast */
10597c478bd9Sstevel@tonic-gate 	if (((inet_pton(AF_INET6, host, &addr6) > 0) &&
10607c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_V4MAPPED(&addr6)) || broadcast) {
10617c478bd9Sstevel@tonic-gate 		if (!broadcast) {
10627c478bd9Sstevel@tonic-gate 			/*
10637c478bd9Sstevel@tonic-gate 			 * Peel off the "mapping" stuff, leaving 32 bit IPv4
10647c478bd9Sstevel@tonic-gate 			 * address.
10657c478bd9Sstevel@tonic-gate 			 */
10667c478bd9Sstevel@tonic-gate 			IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 			/* convert it back to a string */
10697c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET, (void *)&addr, tmp_buf,
10707c478bd9Sstevel@tonic-gate 			    sizeof (tmp_buf));
10717c478bd9Sstevel@tonic-gate 			/*
10727c478bd9Sstevel@tonic-gate 			 * Now the host is an IPv4 address.
10737c478bd9Sstevel@tonic-gate 			 * Since it previously was a v4 mapped v6 address
10747c478bd9Sstevel@tonic-gate 			 * we can be sure that the size of buffer 'host'
10757c478bd9Sstevel@tonic-gate 			 * is large enough to contain the associated v4
10767c478bd9Sstevel@tonic-gate 			 * address and so we don't need to use a strn/lcpy
10777c478bd9Sstevel@tonic-gate 			 * here.
10787c478bd9Sstevel@tonic-gate 			 */
10797c478bd9Sstevel@tonic-gate 			(void) strcpy(host, tmp_buf);
10807c478bd9Sstevel@tonic-gate 		}
10817c478bd9Sstevel@tonic-gate 		/*
10827c478bd9Sstevel@tonic-gate 		 * If it's a broadcast address, it cannot be an IPv6 address.
10837c478bd9Sstevel@tonic-gate 		 * Also, if it's a mapped address, we convert it into IPv4
10847c478bd9Sstevel@tonic-gate 		 * address because ping will send and receive IPv4 packets for
10857c478bd9Sstevel@tonic-gate 		 * that address. Therefore, it's a failure case to ask
10867c478bd9Sstevel@tonic-gate 		 * get_hostinfo() to treat a broadcast or a mapped address
10877c478bd9Sstevel@tonic-gate 		 * as an IPv6 address.
10887c478bd9Sstevel@tonic-gate 		 */
10897c478bd9Sstevel@tonic-gate 		if (family == AF_INET6) {
10907c478bd9Sstevel@tonic-gate 			return;
10917c478bd9Sstevel@tonic-gate 		}
10927c478bd9Sstevel@tonic-gate 	}
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	(void) memset(&hints, 0, sizeof (hints));
10957c478bd9Sstevel@tonic-gate 	hints.ai_family = family;
10967c478bd9Sstevel@tonic-gate 	hints.ai_flags = AI_ADDRCONFIG;
10977c478bd9Sstevel@tonic-gate 	rc = getaddrinfo(host, NULL, &hints, &ai);
10987c478bd9Sstevel@tonic-gate 	if (rc != 0) {
10997c478bd9Sstevel@tonic-gate 		if (rc != EAI_NONAME)
11007c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: getaddrinfo: %s\n", progname,
11017c478bd9Sstevel@tonic-gate 			    gai_strerror(rc));
11027c478bd9Sstevel@tonic-gate 		return;
11037c478bd9Sstevel@tonic-gate 	}
11047c478bd9Sstevel@tonic-gate 	*aipp = ai;
11057c478bd9Sstevel@tonic-gate }
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate /*
11087c478bd9Sstevel@tonic-gate  * For each IP address of the target host, determine a source address to use.
11097c478bd9Sstevel@tonic-gate  */
11107c478bd9Sstevel@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)11117c478bd9Sstevel@tonic-gate select_all_src_addrs(union any_in_addr **src_addr_list, struct addrinfo *ai,
11127c478bd9Sstevel@tonic-gate     union any_in_addr *gwv4, union any_in_addr *gwv6)
11137c478bd9Sstevel@tonic-gate {
11147c478bd9Sstevel@tonic-gate 	union any_in_addr *list;
11157c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
11167c478bd9Sstevel@tonic-gate 	int num_dst = 1;
11177c478bd9Sstevel@tonic-gate 	int i;
11187c478bd9Sstevel@tonic-gate 
1119e11c3f44Smeem 	if (probe_all) {
1120e11c3f44Smeem 		for (aip = ai; aip->ai_next != NULL; aip = aip->ai_next)
1121e11c3f44Smeem 			num_dst++;
1122e11c3f44Smeem 	}
11237c478bd9Sstevel@tonic-gate 
1124e11c3f44Smeem 	list = calloc((size_t)num_dst, sizeof (union any_in_addr));
11257c478bd9Sstevel@tonic-gate 	if (list == NULL) {
11267c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno));
11277c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
11287c478bd9Sstevel@tonic-gate 	}
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	/*
11317c478bd9Sstevel@tonic-gate 	 * If there's a gateway, a routing header as a consequence, our kernel
11327c478bd9Sstevel@tonic-gate 	 * picks the source address based on the first hop address, rather than
11337c478bd9Sstevel@tonic-gate 	 * final destination address.
11347c478bd9Sstevel@tonic-gate 	 */
11357c478bd9Sstevel@tonic-gate 	if (num_gw > 0) {
11367c478bd9Sstevel@tonic-gate 		if (ai->ai_family == AF_INET)
11377c478bd9Sstevel@tonic-gate 			select_src_addr(gwv4, ai->ai_family, &list[0]);
11387c478bd9Sstevel@tonic-gate 		else
11397c478bd9Sstevel@tonic-gate 			select_src_addr(gwv6, ai->ai_family, &list[0]);
11407c478bd9Sstevel@tonic-gate 		/*
11417c478bd9Sstevel@tonic-gate 		 * Since the first gateway address is fixed, we'll use the same
11427c478bd9Sstevel@tonic-gate 		 * src address for every different final destination address
11437c478bd9Sstevel@tonic-gate 		 * we send to.
11447c478bd9Sstevel@tonic-gate 		 */
11457c478bd9Sstevel@tonic-gate 		for (i = 1; i < num_dst; i++)
11467c478bd9Sstevel@tonic-gate 			list[i] = list[0];
11477c478bd9Sstevel@tonic-gate 	} else {
11487c478bd9Sstevel@tonic-gate 		/*
11497c478bd9Sstevel@tonic-gate 		 * Although something like 'ping -l host' results in a routing
11507c478bd9Sstevel@tonic-gate 		 * header, the first gateway address is the target host's
11517c478bd9Sstevel@tonic-gate 		 * address. Therefore, as far as src address selection goes,
11527c478bd9Sstevel@tonic-gate 		 * the result is same as having no routing header.
11537c478bd9Sstevel@tonic-gate 		 */
11547c478bd9Sstevel@tonic-gate 		for (i = 0, aip = ai; i < num_dst && aip != NULL;
11557c478bd9Sstevel@tonic-gate 		    i++, aip = aip->ai_next) {
11567c478bd9Sstevel@tonic-gate 			if (aip->ai_family == AF_INET) {
11577c478bd9Sstevel@tonic-gate 				if (num_v4 != 0) {
11587c478bd9Sstevel@tonic-gate 					select_src_addr((union any_in_addr *)
11597c478bd9Sstevel@tonic-gate 					    /* LINTED E_BAD_PTR_CAST_ALIGN */
11607c478bd9Sstevel@tonic-gate 					    &((struct sockaddr_in *)
11617c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin_addr,
11627c478bd9Sstevel@tonic-gate 					    aip->ai_family,
11637c478bd9Sstevel@tonic-gate 					    &list[i]);
11647c478bd9Sstevel@tonic-gate 				}
11657c478bd9Sstevel@tonic-gate 			} else {
11667c478bd9Sstevel@tonic-gate 				if (num_v6 != 0) {
11677c478bd9Sstevel@tonic-gate 					select_src_addr((union any_in_addr *)
11687c478bd9Sstevel@tonic-gate 					    /* LINTED E_BAD_PTR_CAST_ALIGN */
11697c478bd9Sstevel@tonic-gate 					    &((struct sockaddr_in6 *)
11707c478bd9Sstevel@tonic-gate 					    aip->ai_addr)->sin6_addr,
11717c478bd9Sstevel@tonic-gate 					    aip->ai_family,
11727c478bd9Sstevel@tonic-gate 					    &list[i]);
11737c478bd9Sstevel@tonic-gate 				}
11747c478bd9Sstevel@tonic-gate 			}
11757c478bd9Sstevel@tonic-gate 		}
11767c478bd9Sstevel@tonic-gate 	}
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	*src_addr_list = list;
11797c478bd9Sstevel@tonic-gate }
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate /*
11827c478bd9Sstevel@tonic-gate  * For a given destination address, determine a source address to use.
11837c478bd9Sstevel@tonic-gate  * Returns wildcard address if it cannot determine the source address.
11847c478bd9Sstevel@tonic-gate  */
11857c478bd9Sstevel@tonic-gate static void
select_src_addr(union any_in_addr * dst_addr,int family,union any_in_addr * src_addr)11867c478bd9Sstevel@tonic-gate select_src_addr(union any_in_addr *dst_addr, int family,
11877c478bd9Sstevel@tonic-gate     union any_in_addr *src_addr)
11887c478bd9Sstevel@tonic-gate {
11897c478bd9Sstevel@tonic-gate 	struct sockaddr *sock;
1190b08923d6SRobert Mustacchi 	struct sockaddr_in *sin = NULL;
1191b08923d6SRobert Mustacchi 	struct sockaddr_in6 *sin6 = NULL;
11927c478bd9Sstevel@tonic-gate 	int tmp_fd;
11937c478bd9Sstevel@tonic-gate 	size_t sock_len;
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6));
11967c478bd9Sstevel@tonic-gate 	if (sock == NULL) {
11977c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
11987c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
11997c478bd9Sstevel@tonic-gate 	}
12007c478bd9Sstevel@tonic-gate 	(void) bzero(sock, sizeof (struct sockaddr_in6));
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
12037c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12047c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)sock;
12057c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
12067c478bd9Sstevel@tonic-gate 		sin->sin_addr = dst_addr->addr;
12077c478bd9Sstevel@tonic-gate 		sin->sin_port = IPPORT_ECHO;	/* port shouldn't be 0 */
12087c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in);
12097c478bd9Sstevel@tonic-gate 	} else {
12107c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12117c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)sock;
12127c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
12137c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = dst_addr->addr6;
12147c478bd9Sstevel@tonic-gate 		sin6->sin6_port = IPPORT_ECHO;	/* port shouldn't be 0 */
12157c478bd9Sstevel@tonic-gate 		sock_len = sizeof (struct sockaddr_in6);
12167c478bd9Sstevel@tonic-gate 	}
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	/* open a UDP socket */
12197c478bd9Sstevel@tonic-gate 	if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) {
12207c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: udp socket: %s\n", progname,
12217c478bd9Sstevel@tonic-gate 		    strerror(errno));
12227c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
12237c478bd9Sstevel@tonic-gate 	}
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	/* connect it */
12267c478bd9Sstevel@tonic-gate 	if (connect(tmp_fd, sock, sock_len) < 0) {
12277c478bd9Sstevel@tonic-gate 		/*
12287c478bd9Sstevel@tonic-gate 		 * If there's no route to the destination, this connect() call
12297c478bd9Sstevel@tonic-gate 		 * fails. We just return all-zero (wildcard) as the source
12307c478bd9Sstevel@tonic-gate 		 * address, so that user can get to see "no route to dest"
12317c478bd9Sstevel@tonic-gate 		 * message, as it'll try to send the probe packet out and will
12327c478bd9Sstevel@tonic-gate 		 * receive ICMP unreachable.
12337c478bd9Sstevel@tonic-gate 		 */
12347c478bd9Sstevel@tonic-gate 		if (family == AF_INET)
12357c478bd9Sstevel@tonic-gate 			src_addr->addr.s_addr = INADDR_ANY;
12367c478bd9Sstevel@tonic-gate 		else
12377c478bd9Sstevel@tonic-gate 			src_addr->addr6 = in6addr_any;
12387c478bd9Sstevel@tonic-gate 		free(sock);
12397c478bd9Sstevel@tonic-gate 		return;
12407c478bd9Sstevel@tonic-gate 	}
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate 	/* get the local sock info */
12437c478bd9Sstevel@tonic-gate 	if (getsockname(tmp_fd, sock, &sock_len) < 0) {
12447c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: getsockname: %s\n", progname,
12457c478bd9Sstevel@tonic-gate 		    strerror(errno));
12467c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
12477c478bd9Sstevel@tonic-gate 	}
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
1250b08923d6SRobert Mustacchi 		assert(sin != NULL);
12517c478bd9Sstevel@tonic-gate 		src_addr->addr = sin->sin_addr;
12527c478bd9Sstevel@tonic-gate 	} else {
1253b08923d6SRobert Mustacchi 		assert(sin6 != NULL);
12547c478bd9Sstevel@tonic-gate 		src_addr->addr6 = sin6->sin6_addr;
12557c478bd9Sstevel@tonic-gate 	}
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	(void) close(tmp_fd);
12587c478bd9Sstevel@tonic-gate 	free(sock);
12597c478bd9Sstevel@tonic-gate }
12607c478bd9Sstevel@tonic-gate 
12614c10bc16Spwernau /*
12624c10bc16Spwernau  * Set the IP_NEXTHOP/IPV6_NEXTHOP socket option.
12634c10bc16Spwernau  * exits on failure
12644c10bc16Spwernau  */
12654c10bc16Spwernau static void
set_nexthop(int family,struct addrinfo * ai_nexthop,int sock)12664c10bc16Spwernau set_nexthop(int family, struct addrinfo	*ai_nexthop, int sock)
12674c10bc16Spwernau {
12684c10bc16Spwernau 	if (family == AF_INET) {
12694c10bc16Spwernau 		ipaddr_t nh;
12704c10bc16Spwernau 
12714c10bc16Spwernau 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12724c10bc16Spwernau 		nh = ((struct sockaddr_in *)ai_nexthop->
12734c10bc16Spwernau 		    ai_addr)->sin_addr.s_addr;
12744c10bc16Spwernau 
1275f4b3ec61Sdh 		/* now we need the sys_ip_config privilege */
12764c10bc16Spwernau 		(void) __priv_bracket(PRIV_ON);
12774c10bc16Spwernau 		if (setsockopt(sock, IPPROTO_IP, IP_NEXTHOP,
12784c10bc16Spwernau 		    &nh, sizeof (ipaddr_t)) < 0) {
12791da45818Spwernau 			if (errno == EPERM)
12801da45818Spwernau 				Fprintf(stderr, "%s: Insufficient privilege "
12811da45818Spwernau 				    "to specify IPv4 nexthop router.\n",
12821da45818Spwernau 				    progname);
12831da45818Spwernau 			else
12841da45818Spwernau 				Fprintf(stderr, "%s: setsockopt %s\n",
12851da45818Spwernau 				    progname, strerror(errno));
12864c10bc16Spwernau 			exit(EXIT_FAILURE);
12874c10bc16Spwernau 		}
12884c10bc16Spwernau 		(void) __priv_bracket(PRIV_OFF);
12894c10bc16Spwernau 		/* revert to non-privileged user */
12904c10bc16Spwernau 	} else {
12914c10bc16Spwernau 		struct sockaddr_in6 *nh;
12924c10bc16Spwernau 
12934c10bc16Spwernau 		/* LINTED E_BAD_PTR_CAST_ALIGN */
12944c10bc16Spwernau 		nh = (struct sockaddr_in6 *)ai_nexthop->
12954c10bc16Spwernau 		    ai_addr;
12964c10bc16Spwernau 
12974c10bc16Spwernau 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_NEXTHOP,
12984c10bc16Spwernau 		    nh, sizeof (struct sockaddr_in6)) < 0) {
12994c10bc16Spwernau 			Fprintf(stderr, "%s: setsockopt %s\n",
13004c10bc16Spwernau 			    progname, strerror(errno));
13014c10bc16Spwernau 			exit(EXIT_FAILURE);
13024c10bc16Spwernau 		}
13034c10bc16Spwernau 	}
13044c10bc16Spwernau }
13054c10bc16Spwernau 
13067c478bd9Sstevel@tonic-gate /*
13077c478bd9Sstevel@tonic-gate  * Setup the socket for the given address family.
13087c478bd9Sstevel@tonic-gate  * Returns _B_TRUE on success, _B_FALSE on failure. Failure is the case when no
13097c478bd9Sstevel@tonic-gate  * interface can be found, or the specified interface (-i) is not found. On
13107c478bd9Sstevel@tonic-gate  * library call failures, it exit()s.
13117c478bd9Sstevel@tonic-gate  */
13127c478bd9Sstevel@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)13137c478bd9Sstevel@tonic-gate setup_socket(int family, int *send_sockp, int *recv_sockp, int *if_index,
13144c10bc16Spwernau     ushort_t *udp_src_port, struct addrinfo *ai_nexthop)
13157c478bd9Sstevel@tonic-gate {
13167c478bd9Sstevel@tonic-gate 	int send_sock;
13177c478bd9Sstevel@tonic-gate 	int recv_sock;
13187c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
13197c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
13207c478bd9Sstevel@tonic-gate 	struct sockaddr *sp;
1321a356273cSpwernau 	struct ipsec_req req;
13227c478bd9Sstevel@tonic-gate 	size_t slen;
13237c478bd9Sstevel@tonic-gate 	int on = 1;
13247c478bd9Sstevel@tonic-gate 	uchar_t char_op;
13257c478bd9Sstevel@tonic-gate 	int int_op;
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	/* now we need the net_icmpaccess privilege */
13287c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_ON);
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	recv_sock = socket(family, SOCK_RAW,
13317c478bd9Sstevel@tonic-gate 	    (family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate 	if (recv_sock < 0) {
13347c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: socket %s\n", progname, strerror(errno));
13357c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
13367c478bd9Sstevel@tonic-gate 	}
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 	/* revert to non-privileged user after opening sockets */
13397c478bd9Sstevel@tonic-gate 	(void) __priv_bracket(PRIV_OFF);
13407c478bd9Sstevel@tonic-gate 
1341a356273cSpwernau 	if (bypass) {
1342a356273cSpwernau 		(void) memset(&req, 0, sizeof (req));
1343a356273cSpwernau 		req.ipsr_ah_req = IPSEC_PREF_NEVER;
1344a356273cSpwernau 		req.ipsr_esp_req = IPSEC_PREF_NEVER;
1345a356273cSpwernau 
1346a356273cSpwernau 		if (setsockopt(recv_sock, (family == AF_INET) ? IPPROTO_IP :
1347a356273cSpwernau 		    IPPROTO_IPV6, IP_SEC_OPT, &req, sizeof (req)) < 0) {
13485086f56fSPaul Wernau 			switch (errno) {
13495086f56fSPaul Wernau 			case EPROTONOSUPPORT:
13505086f56fSPaul Wernau 				/*
13515086f56fSPaul Wernau 				 * No IPsec subsystem or policy loaded.
13525086f56fSPaul Wernau 				 * Bypass implicitly allowed.
13535086f56fSPaul Wernau 				 */
13545086f56fSPaul Wernau 				break;
13555086f56fSPaul Wernau 			case EPERM:
1356a356273cSpwernau 				Fprintf(stderr, "%s: Insufficient privilege "
1357a356273cSpwernau 				    "to bypass IPsec policy.\n", progname);
13585086f56fSPaul Wernau 				exit(EXIT_FAILURE);
13595086f56fSPaul Wernau 				break;
13605086f56fSPaul Wernau 			default:
1361a356273cSpwernau 				Fprintf(stderr, "%s: setsockopt %s\n", progname,
1362a356273cSpwernau 				    strerror(errno));
13635086f56fSPaul Wernau 				exit(EXIT_FAILURE);
13645086f56fSPaul Wernau 				break;
13655086f56fSPaul Wernau 			}
1366a356273cSpwernau 		}
1367a356273cSpwernau 	}
1368a356273cSpwernau 
13697c478bd9Sstevel@tonic-gate 	/*
13707c478bd9Sstevel@tonic-gate 	 * We always receive on raw icmp socket. But the sending socket can be
13717c478bd9Sstevel@tonic-gate 	 * raw icmp or udp, depending on the use of -U flag.
13727c478bd9Sstevel@tonic-gate 	 */
13737c478bd9Sstevel@tonic-gate 	if (use_udp) {
13747c478bd9Sstevel@tonic-gate 		send_sock = socket(family, SOCK_DGRAM, IPPROTO_UDP);
13757c478bd9Sstevel@tonic-gate 		if (send_sock < 0) {
13767c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: socket %s\n", progname,
13777c478bd9Sstevel@tonic-gate 			    strerror(errno));
13787c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
13797c478bd9Sstevel@tonic-gate 		}
13807c478bd9Sstevel@tonic-gate 
1381a356273cSpwernau 		if (bypass) {
1382a356273cSpwernau 			if (setsockopt(send_sock, (family == AF_INET) ?
1383a356273cSpwernau 			    IPPROTO_IP : IPPROTO_IPV6, IP_SEC_OPT, &req,
1384a356273cSpwernau 			    sizeof (req)) < 0) {
13855086f56fSPaul Wernau 				switch (errno) {
13865086f56fSPaul Wernau 				case EPROTONOSUPPORT:
13875086f56fSPaul Wernau 					/*
13885086f56fSPaul Wernau 					 * No IPsec subsystem or policy loaded.
13895086f56fSPaul Wernau 					 * Bypass implicitly allowed.
13905086f56fSPaul Wernau 					 */
13915086f56fSPaul Wernau 					break;
13925086f56fSPaul Wernau 				case EPERM:
1393a356273cSpwernau 					Fprintf(stderr, "%s: Insufficient "
1394a356273cSpwernau 					    "privilege to bypass IPsec "
1395a356273cSpwernau 					    "policy.\n", progname);
13965086f56fSPaul Wernau 					exit(EXIT_FAILURE);
13975086f56fSPaul Wernau 					break;
13985086f56fSPaul Wernau 				default:
1399a356273cSpwernau 					Fprintf(stderr, "%s: setsockopt %s\n",
1400a356273cSpwernau 					    progname, strerror(errno));
14015086f56fSPaul Wernau 					exit(EXIT_FAILURE);
14025086f56fSPaul Wernau 					break;
14035086f56fSPaul Wernau 				}
1404a356273cSpwernau 			}
1405a356273cSpwernau 		}
1406a356273cSpwernau 
14077c478bd9Sstevel@tonic-gate 		/*
14087c478bd9Sstevel@tonic-gate 		 * In order to distinguish replies to our UDP probes from
14097c478bd9Sstevel@tonic-gate 		 * other pings', we need to know our source port number.
14107c478bd9Sstevel@tonic-gate 		 */
14117c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
14127c478bd9Sstevel@tonic-gate 			sp = (struct sockaddr *)&sin;
14137c478bd9Sstevel@tonic-gate 			slen = sizeof (sin);
14147c478bd9Sstevel@tonic-gate 		} else {
14157c478bd9Sstevel@tonic-gate 			sp = (struct sockaddr *)&sin6;
14167c478bd9Sstevel@tonic-gate 			slen = sizeof (sin6);
14177c478bd9Sstevel@tonic-gate 		}
14187c478bd9Sstevel@tonic-gate 		bzero(sp, slen);
14197c478bd9Sstevel@tonic-gate 		sp->sa_family = family;
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 		/* Let's bind() send_sock to wildcard address and port */
14227c478bd9Sstevel@tonic-gate 		if (bind(send_sock, sp, slen) < 0) {
14237c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: bind %s\n", progname,
14247c478bd9Sstevel@tonic-gate 			    strerror(errno));
14257c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14267c478bd9Sstevel@tonic-gate 		}
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 		/* .... and see what port kernel picked for us */
14297c478bd9Sstevel@tonic-gate 		if (getsockname(send_sock, sp, &slen) < 0) {
14307c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: getsockname %s\n", progname,
14317c478bd9Sstevel@tonic-gate 			    strerror(errno));
14327c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14337c478bd9Sstevel@tonic-gate 		}
14347c478bd9Sstevel@tonic-gate 		*udp_src_port = (family == AF_INET) ? sin.sin_port :
14357c478bd9Sstevel@tonic-gate 		    sin6.sin6_port;
14367c478bd9Sstevel@tonic-gate 	} else {
14377c478bd9Sstevel@tonic-gate 		send_sock = recv_sock;
14387c478bd9Sstevel@tonic-gate 	}
14397c478bd9Sstevel@tonic-gate 
1440bd670b35SErik Nordmark 	if (nexthop != NULL)
1441bd670b35SErik Nordmark 		set_nexthop(family, ai_nexthop, send_sock);
1442bd670b35SErik Nordmark 
14437c478bd9Sstevel@tonic-gate 	int_op = 48 * 1024;
14447c478bd9Sstevel@tonic-gate 	if (int_op < datalen)
14457c478bd9Sstevel@tonic-gate 		int_op = datalen;
14467c478bd9Sstevel@tonic-gate 	if (setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, (char *)&int_op,
14477c478bd9Sstevel@tonic-gate 	    sizeof (int_op)) == -1) {
14487c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: setsockopt SO_RCVBUF %s\n", progname,
14497c478bd9Sstevel@tonic-gate 		    strerror(errno));
14507c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
14517c478bd9Sstevel@tonic-gate 	}
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	if (setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, (char *)&int_op,
14547c478bd9Sstevel@tonic-gate 	    sizeof (int_op)) == -1) {
14557c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: setsockopt SO_SNDBUF %s\n", progname,
14567c478bd9Sstevel@tonic-gate 		    strerror(errno));
14577c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
14587c478bd9Sstevel@tonic-gate 	}
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	if (options & SO_DEBUG) {
14617c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, SOL_SOCKET, SO_DEBUG, (char *)&on,
14627c478bd9Sstevel@tonic-gate 		    sizeof (on)) == -1) {
14637c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt SO_DEBUG %s\n",
14647c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
14657c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14667c478bd9Sstevel@tonic-gate 		}
14677c478bd9Sstevel@tonic-gate 	}
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	if (options & SO_DONTROUTE) {
14707c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
14717c478bd9Sstevel@tonic-gate 		    sizeof (on)) == -1) {
14727c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt SO_DONTROUTE %s\n",
14737c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
14747c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
14757c478bd9Sstevel@tonic-gate 		}
14767c478bd9Sstevel@tonic-gate 	}
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_NOLOOP) {
14797c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
14807c478bd9Sstevel@tonic-gate 			char_op = 0;	/* used to turn off option */
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
14837c478bd9Sstevel@tonic-gate 			    (char *)&char_op, sizeof (char_op)) == -1) {
14847c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
14857c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_NOLOOP %s\n", progname,
14867c478bd9Sstevel@tonic-gate 				    strerror(errno));
14877c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14887c478bd9Sstevel@tonic-gate 			}
14897c478bd9Sstevel@tonic-gate 		} else {
14907c478bd9Sstevel@tonic-gate 			int_op = 0;	/* used to turn off option */
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IPV6,
14937c478bd9Sstevel@tonic-gate 			    IPV6_MULTICAST_LOOP, (char *)&int_op,
14947c478bd9Sstevel@tonic-gate 			    sizeof (int_op)) == -1) {
14957c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
14967c478bd9Sstevel@tonic-gate 				    "IPV6_MULTICAST_NOLOOP %s\n", progname,
14977c478bd9Sstevel@tonic-gate 				    strerror(errno));
14987c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
14997c478bd9Sstevel@tonic-gate 			}
15007c478bd9Sstevel@tonic-gate 		}
15017c478bd9Sstevel@tonic-gate 	}
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_TTL) {
15047c478bd9Sstevel@tonic-gate 		char_op = hoplimit;
15057c478bd9Sstevel@tonic-gate 
1506bd670b35SErik Nordmark 		/* Applies to unicast and multicast. */
15077c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
15087c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
15097c478bd9Sstevel@tonic-gate 			    (char *)&char_op, sizeof (char)) == -1) {
15107c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
15117c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_TTL %s\n", progname,
15127c478bd9Sstevel@tonic-gate 				    strerror(errno));
15137c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
15147c478bd9Sstevel@tonic-gate 			}
15157c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_TTL,
15167c478bd9Sstevel@tonic-gate 			    (char *)&hoplimit, sizeof (hoplimit)) == -1) {
15177c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt IP_TTL %s\n",
15187c478bd9Sstevel@tonic-gate 				    progname, strerror(errno));
15197c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
15207c478bd9Sstevel@tonic-gate 			}
15217c478bd9Sstevel@tonic-gate 		}
15227c478bd9Sstevel@tonic-gate 		/*
15237c478bd9Sstevel@tonic-gate 		 * AF_INET6 case is handled in set_ancillary_data() function.
15247c478bd9Sstevel@tonic-gate 		 * This is because when ancillary data is used (for routing
15257c478bd9Sstevel@tonic-gate 		 * header and outgoing interface index), the hoplimit set using
15267c478bd9Sstevel@tonic-gate 		 * setsockopt() is ignored.
15277c478bd9Sstevel@tonic-gate 		 */
15287c478bd9Sstevel@tonic-gate 	}
15297c478bd9Sstevel@tonic-gate 
1530bd670b35SErik Nordmark 	/*
1531bd670b35SErik Nordmark 	 * did the user specify an interface?
1532bd670b35SErik Nordmark 	 * Applies to unicast, broadcast and multicast.
1533bd670b35SErik Nordmark 	 */
15347c478bd9Sstevel@tonic-gate 	if (moptions & MULTICAST_IF) {
15357c478bd9Sstevel@tonic-gate 		struct ifaddrlist *al = NULL;		/* interface list */
15367c478bd9Sstevel@tonic-gate 		struct ifaddrlist *my_if;
15377c478bd9Sstevel@tonic-gate 		char errbuf[ERRBUFSIZE];
15387c478bd9Sstevel@tonic-gate 		int num_ifs;
15397c478bd9Sstevel@tonic-gate 		int num_src_ifs;		/* exclude down and loopback */
15407c478bd9Sstevel@tonic-gate 		int i;
15417c478bd9Sstevel@tonic-gate 
15427c478bd9Sstevel@tonic-gate 		/* pull out the interface list */
1543e11c3f44Smeem 		num_ifs = ifaddrlist(&al, family, LIFC_UNDER_IPMP, errbuf);
15447c478bd9Sstevel@tonic-gate 		if (num_ifs == -1) {
15457c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: %s\n", progname, errbuf);
15467c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
15477c478bd9Sstevel@tonic-gate 		}
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 		/* filter out down and loopback interfaces */
15507c478bd9Sstevel@tonic-gate 		num_src_ifs = 0;
15517c478bd9Sstevel@tonic-gate 		for (i = 0; i < num_ifs; i++) {
15527c478bd9Sstevel@tonic-gate 			if (!(al[i].flags & IFF_LOOPBACK) &&
15537c478bd9Sstevel@tonic-gate 			    (al[i].flags & IFF_UP))
15547c478bd9Sstevel@tonic-gate 				num_src_ifs++;
15557c478bd9Sstevel@tonic-gate 		}
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 		if (num_src_ifs == 0) {
15587c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: can't find any %s interface\n",
15597c478bd9Sstevel@tonic-gate 			    progname, (family == AF_INET) ? "IPv4" : "IPv6");
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 			return (_B_FALSE);	/* failure */
15627c478bd9Sstevel@tonic-gate 		}
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 		/* locate the specified interface */
15657c478bd9Sstevel@tonic-gate 		my_if = find_if(al, num_ifs);
15667c478bd9Sstevel@tonic-gate 		if (my_if == NULL) {
15677c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: %s is an invalid %s interface\n",
15687c478bd9Sstevel@tonic-gate 			    progname, out_if.str,
15697c478bd9Sstevel@tonic-gate 			    (family == AF_INET) ? "IPv4" : "IPv6");
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
15727c478bd9Sstevel@tonic-gate 		}
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 		if (family == AF_INET) {
1575bd670b35SErik Nordmark 			struct in_pktinfo pktinfo;
1576bd670b35SErik Nordmark 
15777c478bd9Sstevel@tonic-gate 			if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_IF,
15787c478bd9Sstevel@tonic-gate 			    (char *)&my_if->addr.addr,
15797c478bd9Sstevel@tonic-gate 			    sizeof (struct in_addr)) == -1) {
15807c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: setsockopt "
15817c478bd9Sstevel@tonic-gate 				    "IP_MULTICAST_IF %s\n", progname,
15827c478bd9Sstevel@tonic-gate 				    strerror(errno));
15837c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
15847c478bd9Sstevel@tonic-gate 			}
1585bd670b35SErik Nordmark 			bzero(&pktinfo, sizeof (pktinfo));
1586bd670b35SErik Nordmark 			pktinfo.ipi_ifindex = my_if->index;
1587bd670b35SErik Nordmark 			if (setsockopt(send_sock, IPPROTO_IP, IP_PKTINFO,
1588bd670b35SErik Nordmark 			    (char *)&pktinfo, sizeof (pktinfo)) == -1) {
1589bd670b35SErik Nordmark 				Fprintf(stderr, "%s: setsockopt "
1590bd670b35SErik Nordmark 				    "IP_PKTINFO %s\n", progname,
1591bd670b35SErik Nordmark 				    strerror(errno));
1592bd670b35SErik Nordmark 				exit(EXIT_FAILURE);
1593bd670b35SErik Nordmark 			}
15947c478bd9Sstevel@tonic-gate 		} else {
15957c478bd9Sstevel@tonic-gate 			/*
15967c478bd9Sstevel@tonic-gate 			 * the outgoing interface is set in set_ancillary_data()
15977c478bd9Sstevel@tonic-gate 			 * function
15987c478bd9Sstevel@tonic-gate 			 */
15997c478bd9Sstevel@tonic-gate 			*if_index = my_if->index;
16007c478bd9Sstevel@tonic-gate 		}
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 		free(al);
16037c478bd9Sstevel@tonic-gate 	}
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate 	if (settos && family == AF_INET) {
16067c478bd9Sstevel@tonic-gate 		int_op = tos;
16077c478bd9Sstevel@tonic-gate 		if (setsockopt(send_sock, IPPROTO_IP, IP_TOS, (char *)&int_op,
16087c478bd9Sstevel@tonic-gate 		    sizeof (int_op)) == -1) {
16097c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IP_TOS %s\n",
16107c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
16117c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
16127c478bd9Sstevel@tonic-gate 		}
16137c478bd9Sstevel@tonic-gate 	}
16147c478bd9Sstevel@tonic-gate 
1615bd670b35SErik Nordmark 	/* We enable or disable to not depend on the kernel default */
1616bd670b35SErik Nordmark 	if (family == AF_INET) {
1617bd670b35SErik Nordmark 		if (setsockopt(send_sock, IPPROTO_IP, IP_DONTFRAG,
1618bd670b35SErik Nordmark 		    (char *)&dontfrag, sizeof (dontfrag)) == -1) {
1619bd670b35SErik Nordmark 			Fprintf(stderr, "%s: setsockopt IP_DONTFRAG %s\n",
1620bd670b35SErik Nordmark 			    progname, strerror(errno));
1621bd670b35SErik Nordmark 			exit(EXIT_FAILURE);
1622bd670b35SErik Nordmark 		}
1623bd670b35SErik Nordmark 	} else {
1624bd670b35SErik Nordmark 		if (setsockopt(send_sock, IPPROTO_IPV6, IPV6_DONTFRAG,
1625bd670b35SErik Nordmark 		    (char *)&dontfrag, sizeof (dontfrag)) == -1) {
1626bd670b35SErik Nordmark 			Fprintf(stderr, "%s: setsockopt IPV6_DONTFRAG %s\n",
1627bd670b35SErik Nordmark 			    progname, strerror(errno));
1628bd670b35SErik Nordmark 			exit(EXIT_FAILURE);
1629bd670b35SErik Nordmark 		}
1630bd670b35SErik Nordmark 	}
1631bd670b35SErik Nordmark 
16327c478bd9Sstevel@tonic-gate 	/* receiving IPv6 extension headers in verbose mode */
16337c478bd9Sstevel@tonic-gate 	if (verbose && family == AF_INET6) {
16347c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS,
16357c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
16367c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVHOPOPTS %s\n",
16377c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
16387c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
16397c478bd9Sstevel@tonic-gate 		}
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS,
16427c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
16437c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVDSTOPTS %s\n",
16447c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
16457c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
16467c478bd9Sstevel@tonic-gate 		}
16477c478bd9Sstevel@tonic-gate 
16487c478bd9Sstevel@tonic-gate 		if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVRTHDR,
16497c478bd9Sstevel@tonic-gate 		    (char *)&on, sizeof (on)) == -1) {
16507c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: setsockopt IPV6_RECVRTHDR %s\n",
16517c478bd9Sstevel@tonic-gate 			    progname, strerror(errno));
16527c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
16537c478bd9Sstevel@tonic-gate 		}
16547c478bd9Sstevel@tonic-gate 	}
16557c478bd9Sstevel@tonic-gate 
1656b08923d6SRobert Mustacchi 	/* Ensure that timestamping is requested on the receive socket */
1657b08923d6SRobert Mustacchi 	if (setsockopt(recv_sock, SOL_SOCKET, SO_TIMESTAMP,
1658b08923d6SRobert Mustacchi 	    &on, sizeof (on)) == -1) {
1659b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: warning: timing accuracy diminished -- "
1660b08923d6SRobert Mustacchi 		    "setsockopt SO_TIMESTAMP failed %s", progname,
1661b08923d6SRobert Mustacchi 		    strerror(errno));
1662b08923d6SRobert Mustacchi 	}
1663b08923d6SRobert Mustacchi 
16647c478bd9Sstevel@tonic-gate 	*send_sockp = send_sock;
16657c478bd9Sstevel@tonic-gate 	*recv_sockp = recv_sock;
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 	/* successful */
16687c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
16697c478bd9Sstevel@tonic-gate }
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate /*
16727c478bd9Sstevel@tonic-gate  * Pull out the record containing all the info about the interface specified by
16737c478bd9Sstevel@tonic-gate  * `out_if'. Skips interfaces which are down or loopback.
16747c478bd9Sstevel@tonic-gate  */
16757c478bd9Sstevel@tonic-gate static struct ifaddrlist *
find_if(struct ifaddrlist * al,int num_ifs)16767c478bd9Sstevel@tonic-gate find_if(struct ifaddrlist *al, int num_ifs)
16777c478bd9Sstevel@tonic-gate {
16787c478bd9Sstevel@tonic-gate 	static struct ifaddrlist tmp_if;
16797c478bd9Sstevel@tonic-gate 	boolean_t found;
16807c478bd9Sstevel@tonic-gate 	int i;
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	i = 0;
16837c478bd9Sstevel@tonic-gate 	found = _B_FALSE;
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	while (i < num_ifs && !found) {
16867c478bd9Sstevel@tonic-gate 		tmp_if = al[i];
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 		/* skip down or loopback interfaces */
16897c478bd9Sstevel@tonic-gate 		if ((tmp_if.flags & IFF_LOOPBACK) || !(tmp_if.flags & IFF_UP)) {
16907c478bd9Sstevel@tonic-gate 			i++;
16917c478bd9Sstevel@tonic-gate 			continue;
16927c478bd9Sstevel@tonic-gate 		}
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 		/* the type of interface id is variable */
16957c478bd9Sstevel@tonic-gate 		switch (out_if.id_type) {
16967c478bd9Sstevel@tonic-gate 		case IF_INDEX:
16977c478bd9Sstevel@tonic-gate 			if (out_if.id.index == tmp_if.index)
16987c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
16997c478bd9Sstevel@tonic-gate 			break;
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 		case IF_NAME:
17027c478bd9Sstevel@tonic-gate 			if (strcmp(out_if.id.name, tmp_if.device) == 0)
17037c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
17047c478bd9Sstevel@tonic-gate 			break;
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 		case IF_ADDR:
17077c478bd9Sstevel@tonic-gate 			if (out_if.id.addr.addr.s_addr ==
17087c478bd9Sstevel@tonic-gate 			    tmp_if.addr.addr.s_addr) {
17097c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
17107c478bd9Sstevel@tonic-gate 			}
17117c478bd9Sstevel@tonic-gate 			break;
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 		case IF_ADDR6:
17147c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&out_if.id.addr.addr6,
17157c478bd9Sstevel@tonic-gate 			    &tmp_if.addr.addr6)) {
17167c478bd9Sstevel@tonic-gate 				found = _B_TRUE;
17177c478bd9Sstevel@tonic-gate 			}
17187c478bd9Sstevel@tonic-gate 			break;
17197c478bd9Sstevel@tonic-gate 
17207c478bd9Sstevel@tonic-gate 		default:
17217c478bd9Sstevel@tonic-gate 			break;
17227c478bd9Sstevel@tonic-gate 		}
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 		i++;
17257c478bd9Sstevel@tonic-gate 	}
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	if (found)
17287c478bd9Sstevel@tonic-gate 		return (&tmp_if);
17297c478bd9Sstevel@tonic-gate 	else
17307c478bd9Sstevel@tonic-gate 		return (NULL);
17317c478bd9Sstevel@tonic-gate }
17327c478bd9Sstevel@tonic-gate 
17337c478bd9Sstevel@tonic-gate /*
17347c478bd9Sstevel@tonic-gate  * Invoked by SIGALRM, sigalrm_handler() is, responsible for calling
17357c478bd9Sstevel@tonic-gate  * send_scheduled_probe() to send next probe.
17367c478bd9Sstevel@tonic-gate  */
17377c478bd9Sstevel@tonic-gate void
sigalrm_handler(void)17387c478bd9Sstevel@tonic-gate sigalrm_handler(void)
17397c478bd9Sstevel@tonic-gate {
17407c478bd9Sstevel@tonic-gate 	/*
1741b08923d6SRobert Mustacchi 	 * If we've been told that we're done, the timer should be cancelled
1742b08923d6SRobert Mustacchi 	 * and not rescheduled, just return.
1743b08923d6SRobert Mustacchi 	 */
1744b08923d6SRobert Mustacchi 	if (timer_done == _B_TRUE)
1745b08923d6SRobert Mustacchi 		return;
1746b08923d6SRobert Mustacchi 
1747b08923d6SRobert Mustacchi 	/*
1748b08923d6SRobert Mustacchi 	 * Guard against denial-of-service attacks. Make sure ping doesn't send
1749b08923d6SRobert Mustacchi 	 * probes for every SIGALRM it receives in the case of errant SIGALRMs.
1750b08923d6SRobert Mustacchi 	 * ping will ignore those which are received too soon (the smaller of
1751b08923d6SRobert Mustacchi 	 * 0.5 sec and the ping interval, if in effect) after it sent the last
1752b08923d6SRobert Mustacchi 	 * probe.  We use gethrtime() instead of gettimeofday() because the
1753b08923d6SRobert Mustacchi 	 * latter is not linear and is prone to resetting or drifting.
17547c478bd9Sstevel@tonic-gate 	 */
1755b08923d6SRobert Mustacchi 	if ((gethrtime() - t_last_probe_sent) < mintime) {
17567c478bd9Sstevel@tonic-gate 		return;
17577c478bd9Sstevel@tonic-gate 	}
17587c478bd9Sstevel@tonic-gate 	send_scheduled_probe();
17597c478bd9Sstevel@tonic-gate 	schedule_sigalrm();
17607c478bd9Sstevel@tonic-gate }
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate /*
17637c478bd9Sstevel@tonic-gate  * Schedule next SIGALRM.
17647c478bd9Sstevel@tonic-gate  */
17657c478bd9Sstevel@tonic-gate void
schedule_sigalrm(void)17667c478bd9Sstevel@tonic-gate schedule_sigalrm(void)
17677c478bd9Sstevel@tonic-gate {
17687c478bd9Sstevel@tonic-gate 	int waittime;
1769b08923d6SRobert Mustacchi 	struct itimerspec it;
17707c478bd9Sstevel@tonic-gate 
1771b08923d6SRobert Mustacchi 	bzero(&it, sizeof (struct itimerspec));
17727c478bd9Sstevel@tonic-gate 	if (npackets == 0 ||
17737c478bd9Sstevel@tonic-gate 	    current_targetaddr->num_sent < current_targetaddr->num_probes) {
1774b08923d6SRobert Mustacchi 		it = interval;
17757c478bd9Sstevel@tonic-gate 	} else {
17767c478bd9Sstevel@tonic-gate 		if (current_targetaddr->got_reply) {
17777c478bd9Sstevel@tonic-gate 			waittime = 2 * tmax / MICROSEC;
17787c478bd9Sstevel@tonic-gate 			if (waittime == 0)
17797c478bd9Sstevel@tonic-gate 				waittime = 1;
17807c478bd9Sstevel@tonic-gate 		} else {
17817c478bd9Sstevel@tonic-gate 			waittime = MAX_WAIT;
17827c478bd9Sstevel@tonic-gate 		}
1783b08923d6SRobert Mustacchi 		it.it_value.tv_sec = waittime;
1784b08923d6SRobert Mustacchi 	}
1785b08923d6SRobert Mustacchi 
1786b08923d6SRobert Mustacchi 	if (timer_settime(timer, TIMER_RELTIME, &it, NULL) != 0) {
1787b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: unexpected error updating time: %s\n",
1788b08923d6SRobert Mustacchi 		    progname, strerror(errno));
1789b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
17907c478bd9Sstevel@tonic-gate 	}
17917c478bd9Sstevel@tonic-gate }
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate /*
17947c478bd9Sstevel@tonic-gate  * Called by sigalrm_handler(), check_reply() or check_reply6(),
17957c478bd9Sstevel@tonic-gate  * send_scheduled_probe() looks at the current_targetaddr and determines what
17967c478bd9Sstevel@tonic-gate  * should be sent next and calls pinger().
17977c478bd9Sstevel@tonic-gate  */
17987c478bd9Sstevel@tonic-gate void
send_scheduled_probe()17997c478bd9Sstevel@tonic-gate send_scheduled_probe()
18007c478bd9Sstevel@tonic-gate {
18017c478bd9Sstevel@tonic-gate 	static struct msghdr msg6;
18027c478bd9Sstevel@tonic-gate 	static boolean_t first_probe = _B_TRUE;
18037c478bd9Sstevel@tonic-gate 	char tmp_buf[INET6_ADDRSTRLEN];
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 	/*
18067c478bd9Sstevel@tonic-gate 	 * We are about to move to next targetaddr if it's either we sent
18077c478bd9Sstevel@tonic-gate 	 * all the probes, or somebody set the probing_done flag to
18087c478bd9Sstevel@tonic-gate 	 * _B_TRUE prompting us to move on.
18097c478bd9Sstevel@tonic-gate 	 */
18107c478bd9Sstevel@tonic-gate 	if (current_targetaddr->num_sent == current_targetaddr->num_probes ||
18117c478bd9Sstevel@tonic-gate 	    current_targetaddr->probing_done) {
18127c478bd9Sstevel@tonic-gate 		/*
18137c478bd9Sstevel@tonic-gate 		 * is this a dead target?
18147c478bd9Sstevel@tonic-gate 		 */
18157c478bd9Sstevel@tonic-gate 		if (!stats && !current_targetaddr->got_reply) {
18167c478bd9Sstevel@tonic-gate 			if (!probe_all) {
18177c478bd9Sstevel@tonic-gate 				Printf("no answer from %s\n", targethost);
18187c478bd9Sstevel@tonic-gate 			} else {
18197c478bd9Sstevel@tonic-gate 				Printf("no answer from %s(%s)\n", targethost,
18207c478bd9Sstevel@tonic-gate 				    inet_ntop(current_targetaddr->family,
1821e11c3f44Smeem 				    &current_targetaddr->dst_addr,
1822e11c3f44Smeem 				    tmp_buf, sizeof (tmp_buf)));
18237c478bd9Sstevel@tonic-gate 			}
18247c478bd9Sstevel@tonic-gate 		}
18257c478bd9Sstevel@tonic-gate 		/*
18267c478bd9Sstevel@tonic-gate 		 * Before we move onto next item, let's do some clean up.
18277c478bd9Sstevel@tonic-gate 		 */
18287c478bd9Sstevel@tonic-gate 		current_targetaddr->got_reply = _B_FALSE;
18297c478bd9Sstevel@tonic-gate 		current_targetaddr->probing_done = _B_FALSE;
18307c478bd9Sstevel@tonic-gate 		/*
18317c478bd9Sstevel@tonic-gate 		 * If this is probe-all without stats mode, then we need to
18327c478bd9Sstevel@tonic-gate 		 * preserve this count. This is needed when we try to map an
18337c478bd9Sstevel@tonic-gate 		 * icmp_seq to IP address. Otherwise, clear it.
18347c478bd9Sstevel@tonic-gate 		 */
18357c478bd9Sstevel@tonic-gate 		if (stats || !probe_all)
18367c478bd9Sstevel@tonic-gate 			current_targetaddr->num_sent = 0;
18377c478bd9Sstevel@tonic-gate 		nreceived_last_target = 0;
18387c478bd9Sstevel@tonic-gate 
18397c478bd9Sstevel@tonic-gate 		current_targetaddr = current_targetaddr->next;
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 		/*
18427c478bd9Sstevel@tonic-gate 		 * Did we reach the end of road?
18437c478bd9Sstevel@tonic-gate 		 */
18447c478bd9Sstevel@tonic-gate 		if (current_targetaddr == NULL) {
1845b08923d6SRobert Mustacchi 			timer_done = _B_TRUE;
18467c478bd9Sstevel@tonic-gate 			if (stats)
18477c478bd9Sstevel@tonic-gate 				finish();
18487c478bd9Sstevel@tonic-gate 			if (is_alive)
18497c478bd9Sstevel@tonic-gate 				exit(EXIT_SUCCESS);
18507c478bd9Sstevel@tonic-gate 			else
18517c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
18527c478bd9Sstevel@tonic-gate 		} else {
18537c478bd9Sstevel@tonic-gate 			/*
18547c478bd9Sstevel@tonic-gate 			 * We use starting_seq_num for authenticating replies.
18557c478bd9Sstevel@tonic-gate 			 * Each time we move to a new targetaddr, which has
18567c478bd9Sstevel@tonic-gate 			 * a different target IP address, we update this field.
18577c478bd9Sstevel@tonic-gate 			 */
1858e11c3f44Smeem 			current_targetaddr->starting_seq_num = use_udp ?
1859e11c3f44Smeem 			    dest_port : (ntransmitted % (MAX_ICMP_SEQ + 1));
18607c478bd9Sstevel@tonic-gate 		}
18617c478bd9Sstevel@tonic-gate 	}
18627c478bd9Sstevel@tonic-gate 
18637c478bd9Sstevel@tonic-gate 	if (current_targetaddr->family == AF_INET6) {
18647c478bd9Sstevel@tonic-gate 		if (send_reply) {
18657c478bd9Sstevel@tonic-gate 			/* sending back to ourself */
18667c478bd9Sstevel@tonic-gate 			to6.sin6_addr = current_targetaddr->src_addr.addr6;
18677c478bd9Sstevel@tonic-gate 		} else {
18687c478bd9Sstevel@tonic-gate 			to6.sin6_addr = current_targetaddr->dst_addr.addr6;
18697c478bd9Sstevel@tonic-gate 		}
18707c478bd9Sstevel@tonic-gate 		/*
18717c478bd9Sstevel@tonic-gate 		 * Setting the ancillary data once is enough, if we are
18727c478bd9Sstevel@tonic-gate 		 * not using source routing through target (-l/-S). In
18737c478bd9Sstevel@tonic-gate 		 * case -l/-S used, the middle gateway will be the
18747c478bd9Sstevel@tonic-gate 		 * IP address of the source, which can be different
18757c478bd9Sstevel@tonic-gate 		 * for each target IP.
18767c478bd9Sstevel@tonic-gate 		 */
18777c478bd9Sstevel@tonic-gate 		if (first_probe ||
18787c478bd9Sstevel@tonic-gate 		    (send_reply && current_targetaddr->num_sent == 0)) {
18797c478bd9Sstevel@tonic-gate 			if (send_reply) {
18807c478bd9Sstevel@tonic-gate 				/* target is the middle gateway now */
18817c478bd9Sstevel@tonic-gate 				gw_IP_list6[num_gw].addr6 =
18827c478bd9Sstevel@tonic-gate 				    current_targetaddr->dst_addr.addr6;
18837c478bd9Sstevel@tonic-gate 			}
18847c478bd9Sstevel@tonic-gate 			set_ancillary_data(&msg6, hoplimit, gw_IP_list6,
18857c478bd9Sstevel@tonic-gate 			    eff_num_gw, if_index);
18867c478bd9Sstevel@tonic-gate 			first_probe = _B_FALSE;
18877c478bd9Sstevel@tonic-gate 		}
18887c478bd9Sstevel@tonic-gate 		pinger(send_sock6, (struct sockaddr *)&to6, &msg6, AF_INET6);
18897c478bd9Sstevel@tonic-gate 	} else {
18907c478bd9Sstevel@tonic-gate 		to.sin_addr = current_targetaddr->dst_addr.addr;
18917c478bd9Sstevel@tonic-gate 		/*
18927c478bd9Sstevel@tonic-gate 		 * Set IPv4 options when sending the first probe to a target
18937c478bd9Sstevel@tonic-gate 		 * IP address. Some options change when the target address
18947c478bd9Sstevel@tonic-gate 		 * changes.
18957c478bd9Sstevel@tonic-gate 		 */
18967c478bd9Sstevel@tonic-gate 		if (current_targetaddr->num_sent == 0) {
18977c478bd9Sstevel@tonic-gate 			if (eff_num_gw > 0) {
18987c478bd9Sstevel@tonic-gate 				gw_IP_list[num_gw].addr =
18997c478bd9Sstevel@tonic-gate 				    current_targetaddr->dst_addr.addr;
19007c478bd9Sstevel@tonic-gate 				/*
19017c478bd9Sstevel@tonic-gate 				 * If send_reply, the target becomes the
19027c478bd9Sstevel@tonic-gate 				 * middle gateway, sender becomes the last
19037c478bd9Sstevel@tonic-gate 				 * gateway.
19047c478bd9Sstevel@tonic-gate 				 */
19057c478bd9Sstevel@tonic-gate 				if (send_reply) {
19067c478bd9Sstevel@tonic-gate 					gw_IP_list[eff_num_gw].addr =
19077c478bd9Sstevel@tonic-gate 					    current_targetaddr->src_addr.addr;
19087c478bd9Sstevel@tonic-gate 				}
19097c478bd9Sstevel@tonic-gate 			}
19107c478bd9Sstevel@tonic-gate 			/*
19117c478bd9Sstevel@tonic-gate 			 * In IPv4, if source routing is used, the target
19127c478bd9Sstevel@tonic-gate 			 * address shows up as the last gateway, hence +1.
19137c478bd9Sstevel@tonic-gate 			 */
19147c478bd9Sstevel@tonic-gate 			set_IPv4_options(send_sock, gw_IP_list,
19157c478bd9Sstevel@tonic-gate 			    (eff_num_gw > 0) ? eff_num_gw + 1 : 0,
19167c478bd9Sstevel@tonic-gate 			    &current_targetaddr->src_addr.addr, &to.sin_addr);
19177c478bd9Sstevel@tonic-gate 		}
19187c478bd9Sstevel@tonic-gate 		pinger(send_sock, (struct sockaddr *)&to, NULL, AF_INET);
19197c478bd9Sstevel@tonic-gate 	}
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	current_targetaddr->num_sent++;
19227c478bd9Sstevel@tonic-gate }
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate /*
19257c478bd9Sstevel@tonic-gate  * recv_icmp_packet()'s job is to listen to icmp packets and filter out
19267c478bd9Sstevel@tonic-gate  * those ping is interested in.
19277c478bd9Sstevel@tonic-gate  */
19287c478bd9Sstevel@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)19297c478bd9Sstevel@tonic-gate recv_icmp_packet(struct addrinfo *ai_dst, int recv_sock6, int recv_sock,
1930afee3dc6SRobert Mustacchi     ushort_t udp_src_port6, ushort_t udp_src_port)
19317c478bd9Sstevel@tonic-gate {
19327c478bd9Sstevel@tonic-gate 	struct msghdr in_msg;
19337c478bd9Sstevel@tonic-gate 	struct iovec iov;
19347c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from6;
19357c478bd9Sstevel@tonic-gate 	fd_set fds;
19367c478bd9Sstevel@tonic-gate 	int result;
19377c478bd9Sstevel@tonic-gate 	int cc;
19387c478bd9Sstevel@tonic-gate 	boolean_t always_true = _B_TRUE; /* lint doesn't like while(_B_TRUE) */
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	while (always_true) {
19417c478bd9Sstevel@tonic-gate 		(void) FD_ZERO(&fds);
19427c478bd9Sstevel@tonic-gate 		if (recv_sock6 != -1)
19437c478bd9Sstevel@tonic-gate 			FD_SET(recv_sock6, &fds);
19447c478bd9Sstevel@tonic-gate 		if (recv_sock != -1)
19457c478bd9Sstevel@tonic-gate 			FD_SET(recv_sock, &fds);
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 		result = select(MAX(recv_sock6, recv_sock) + 1, &fds,
19487c478bd9Sstevel@tonic-gate 		    (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL);
19497c478bd9Sstevel@tonic-gate 		if (result == -1) {
19507c478bd9Sstevel@tonic-gate 			if (errno == EINTR) {
19517c478bd9Sstevel@tonic-gate 				continue;
19527c478bd9Sstevel@tonic-gate 			} else {
19537c478bd9Sstevel@tonic-gate 				Fprintf(stderr, "%s: select %s\n", progname,
19547c478bd9Sstevel@tonic-gate 				    strerror(errno));
19557c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
19567c478bd9Sstevel@tonic-gate 			}
19577c478bd9Sstevel@tonic-gate 		} else if (result > 0) {
19587c478bd9Sstevel@tonic-gate 			in_msg.msg_name = &from6;
19597c478bd9Sstevel@tonic-gate 			in_msg.msg_namelen = sizeof (from6);
19607c478bd9Sstevel@tonic-gate 			iov.iov_base = in_pkt;
19617c478bd9Sstevel@tonic-gate 			iov.iov_len = sizeof (in_pkt);
19627c478bd9Sstevel@tonic-gate 			in_msg.msg_iov = &iov;
19637c478bd9Sstevel@tonic-gate 			in_msg.msg_iovlen = 1;
19647c478bd9Sstevel@tonic-gate 			in_msg.msg_control = ancillary_data;
19657c478bd9Sstevel@tonic-gate 			in_msg.msg_controllen = sizeof (ancillary_data);
19667c478bd9Sstevel@tonic-gate 
19677c478bd9Sstevel@tonic-gate 			/* Do we have an ICMP6 packet waiting? */
19687c478bd9Sstevel@tonic-gate 			if ((recv_sock6 != -1) &&
19697c478bd9Sstevel@tonic-gate 			    (FD_ISSET(recv_sock6, &fds))) {
19707c478bd9Sstevel@tonic-gate 				cc = recvmsg(recv_sock6, &in_msg, 0);
19717c478bd9Sstevel@tonic-gate 				if (cc < 0) {
19727c478bd9Sstevel@tonic-gate 					if (errno != EINTR) {
19737c478bd9Sstevel@tonic-gate 						Fprintf(stderr,
19747c478bd9Sstevel@tonic-gate 						    "%s: recvmsg %s\n",
19757c478bd9Sstevel@tonic-gate 						    progname, strerror(errno));
19767c478bd9Sstevel@tonic-gate 					}
19777c478bd9Sstevel@tonic-gate 					continue;
19787c478bd9Sstevel@tonic-gate 				} else if (cc > 0) {
19797c478bd9Sstevel@tonic-gate 					check_reply6(ai_dst, &in_msg, cc,
19807c478bd9Sstevel@tonic-gate 					    udp_src_port6);
19817c478bd9Sstevel@tonic-gate 				}
19827c478bd9Sstevel@tonic-gate 			}
19837c478bd9Sstevel@tonic-gate 			/* Do we have an ICMP packet waiting? */
19847c478bd9Sstevel@tonic-gate 			if ((recv_sock != -1) && (FD_ISSET(recv_sock, &fds))) {
19857c478bd9Sstevel@tonic-gate 				cc = recvmsg(recv_sock, &in_msg, 0);
19867c478bd9Sstevel@tonic-gate 				if (cc < 0) {
19877c478bd9Sstevel@tonic-gate 					if (errno != EINTR) {
19887c478bd9Sstevel@tonic-gate 						Fprintf(stderr,
19897c478bd9Sstevel@tonic-gate 						    "%s: recvmsg %s\n",
19907c478bd9Sstevel@tonic-gate 						    progname, strerror(errno));
19917c478bd9Sstevel@tonic-gate 					}
19927c478bd9Sstevel@tonic-gate 					continue;
1993cbf54fedSJohn Levon 				} else if (cc > 0) {
19947c478bd9Sstevel@tonic-gate 					check_reply(ai_dst, &in_msg, cc,
19957c478bd9Sstevel@tonic-gate 					    udp_src_port);
19967c478bd9Sstevel@tonic-gate 				}
19977c478bd9Sstevel@tonic-gate 			}
19987c478bd9Sstevel@tonic-gate 		}
19997c478bd9Sstevel@tonic-gate 		/*
20007c478bd9Sstevel@tonic-gate 		 * If we were probing last IP address of the target host and
20017c478bd9Sstevel@tonic-gate 		 * received a reply for each probe sent to this address,
20027c478bd9Sstevel@tonic-gate 		 * then we are done!
20037c478bd9Sstevel@tonic-gate 		 */
20047c478bd9Sstevel@tonic-gate 		if ((npackets > 0) && (current_targetaddr->next == NULL) &&
20057c478bd9Sstevel@tonic-gate 		    (nreceived_last_target == npackets)) {
2006b08923d6SRobert Mustacchi 			timer_done = _B_TRUE;
20077c478bd9Sstevel@tonic-gate 			finish();
20087c478bd9Sstevel@tonic-gate 		}
20097c478bd9Sstevel@tonic-gate 	} /* infinite loop */
20107c478bd9Sstevel@tonic-gate }
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate /*
20137c478bd9Sstevel@tonic-gate  * Given a host (with possibly multiple IP addresses) and an IP address, this
20147c478bd9Sstevel@tonic-gate  * function determines if this IP address is one of the host's addresses to
20157c478bd9Sstevel@tonic-gate  * which we're sending probes. Used to determine if we are interested in a
20167c478bd9Sstevel@tonic-gate  * packet.
20177c478bd9Sstevel@tonic-gate  */
20187c478bd9Sstevel@tonic-gate boolean_t
is_a_target(struct addrinfo * ai,union any_in_addr * addr)20197c478bd9Sstevel@tonic-gate is_a_target(struct addrinfo *ai, union any_in_addr *addr)
20207c478bd9Sstevel@tonic-gate {
20217c478bd9Sstevel@tonic-gate 	int num_addrs;
20227c478bd9Sstevel@tonic-gate 	int i;
20237c478bd9Sstevel@tonic-gate 	struct addrinfo *aip;
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate 	aip = ai;
20267c478bd9Sstevel@tonic-gate 	if (probe_all)
20277c478bd9Sstevel@tonic-gate 		num_addrs = num_v4 + num_v6;
20287c478bd9Sstevel@tonic-gate 	else
20297c478bd9Sstevel@tonic-gate 		num_addrs = 1;
20307c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_addrs && aip != NULL; i++) {
20317c478bd9Sstevel@tonic-gate 		if (aip->ai_family == AF_INET6) {
20327c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
20337c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)
20347c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin6_addr, &addr->addr6))
20357c478bd9Sstevel@tonic-gate 				return (_B_TRUE);
20367c478bd9Sstevel@tonic-gate 		} else {
20377c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
20387c478bd9Sstevel@tonic-gate 			if (((struct sockaddr_in *)
20397c478bd9Sstevel@tonic-gate 			    aip->ai_addr)->sin_addr.s_addr == addr->addr.s_addr)
20407c478bd9Sstevel@tonic-gate 				return (_B_TRUE);
20417c478bd9Sstevel@tonic-gate 		}
20427c478bd9Sstevel@tonic-gate 	}
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	return (_B_FALSE);
20457c478bd9Sstevel@tonic-gate }
20467c478bd9Sstevel@tonic-gate 
20477c478bd9Sstevel@tonic-gate /*
20487c478bd9Sstevel@tonic-gate  * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
20497c478bd9Sstevel@tonic-gate  * will be added on by the kernel.  The ID field is our UNIX process ID,
20507c478bd9Sstevel@tonic-gate  * and the sequence number is an ascending integer.  The first 8 bytes
20517c478bd9Sstevel@tonic-gate  * of the data portion are used to hold a UNIX "timeval" struct in network
20527c478bd9Sstevel@tonic-gate  * byte-order, to compute the round-trip time.
20537c478bd9Sstevel@tonic-gate  */
20547c478bd9Sstevel@tonic-gate static void
pinger(int send_sock,struct sockaddr * whereto,struct msghdr * msg6,int family)20557c478bd9Sstevel@tonic-gate pinger(int send_sock, struct sockaddr *whereto, struct msghdr *msg6,
20567c478bd9Sstevel@tonic-gate     int family)
20577c478bd9Sstevel@tonic-gate {
20587c478bd9Sstevel@tonic-gate 	static uint64_t out_pkt_buf[(IP_MAXPACKET + 1) / 8];
20597c478bd9Sstevel@tonic-gate 	uchar_t *out_pkt = (uchar_t *)&out_pkt_buf;
20607c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
20617c478bd9Sstevel@tonic-gate 	struct icmp *icp = (struct icmp *)out_pkt;
20627c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
20637c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)whereto;
20647c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
20657c478bd9Sstevel@tonic-gate 	struct sockaddr_in *to = (struct sockaddr_in *)whereto;
20667c478bd9Sstevel@tonic-gate 	struct timeval *tp;
20677c478bd9Sstevel@tonic-gate 	struct timeval t_snd;
20687c478bd9Sstevel@tonic-gate 	uchar_t *datap;
20697c478bd9Sstevel@tonic-gate 	struct iovec iov;
20707c478bd9Sstevel@tonic-gate 	int start = 0;
20717c478bd9Sstevel@tonic-gate 	int cc;
20727c478bd9Sstevel@tonic-gate 	int i;
20737c478bd9Sstevel@tonic-gate 
20747c478bd9Sstevel@tonic-gate 	/* using UDP? */
20757c478bd9Sstevel@tonic-gate 	if (use_udp) {
20767c478bd9Sstevel@tonic-gate 		cc = datalen;
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
20797c478bd9Sstevel@tonic-gate 		tp = (struct timeval *)out_pkt;
20807c478bd9Sstevel@tonic-gate 		datap = &out_pkt[sizeof (struct timeval)];
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate 		/*
20837c478bd9Sstevel@tonic-gate 		 * This sets the port whether we are handling a v4 or v6
20847c478bd9Sstevel@tonic-gate 		 * sockaddr structure.
20857c478bd9Sstevel@tonic-gate 		 */
20867c478bd9Sstevel@tonic-gate 		to->sin_port = htons(dest_port);
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 		dest_port = (dest_port + 1) % (MAX_PORT + 1);
20897c478bd9Sstevel@tonic-gate 		ntransmitted++;
20907c478bd9Sstevel@tonic-gate 	} else {	/* using ICMP */
20917c478bd9Sstevel@tonic-gate 		cc = datalen + ICMP_MINLEN;
20927c478bd9Sstevel@tonic-gate 
20937c478bd9Sstevel@tonic-gate 		if (family == AF_INET6) {
20947c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
20957c478bd9Sstevel@tonic-gate 			    ICMP6_ECHO_REPLY : ICMP6_ECHO_REQUEST;
20967c478bd9Sstevel@tonic-gate 		} else if (use_icmp_ts) {	/* family is AF_INET */
20977c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
20987c478bd9Sstevel@tonic-gate 			    ICMP_TSTAMPREPLY : ICMP_TSTAMP;
20997c478bd9Sstevel@tonic-gate 		} else {
21007c478bd9Sstevel@tonic-gate 			icp->icmp_type = send_reply ?
21017c478bd9Sstevel@tonic-gate 			    ICMP_ECHOREPLY : ICMP_ECHO;
21027c478bd9Sstevel@tonic-gate 		}
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate 		icp->icmp_code = 0;
21057c478bd9Sstevel@tonic-gate 		icp->icmp_cksum = 0;
21067c478bd9Sstevel@tonic-gate 		icp->icmp_seq = htons(ntransmitted++ % (MAX_ICMP_SEQ + 1));
21077c478bd9Sstevel@tonic-gate 		if (icp->icmp_seq == 0)
21087c478bd9Sstevel@tonic-gate 			num_wraps++;
21097c478bd9Sstevel@tonic-gate 		icp->icmp_id = htons(ident);		/* ID */
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
21127c478bd9Sstevel@tonic-gate 		tp = (struct timeval *)&out_pkt[ICMP_MINLEN];
21137c478bd9Sstevel@tonic-gate 		datap = &out_pkt[ICMP_MINLEN + sizeof (struct timeval)];
21147c478bd9Sstevel@tonic-gate 	}
21157c478bd9Sstevel@tonic-gate 
21167c478bd9Sstevel@tonic-gate 	start = sizeof (struct timeval);	/* skip for time */
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate 	(void) gettimeofday(&t_snd, (struct timezone *)NULL);
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 	/* if packet is big enough to store timeval OR ... */
21217c478bd9Sstevel@tonic-gate 	if ((datalen >= sizeof (struct timeval)) ||
21227c478bd9Sstevel@tonic-gate 	    (family == AF_INET && use_icmp_ts))
21237c478bd9Sstevel@tonic-gate 		*tp = t_snd;
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 	if (family == AF_INET && use_icmp_ts) {
21267c478bd9Sstevel@tonic-gate 		start = sizeof (struct id_ts);	/* skip for ICMP timestamps */
21277c478bd9Sstevel@tonic-gate 		/* Number of milliseconds since midnight */
21287c478bd9Sstevel@tonic-gate 		icp->icmp_otime = htonl((tp->tv_sec % (24*60*60)) * 1000 +
21297c478bd9Sstevel@tonic-gate 		    tp->tv_usec / 1000);
21307c478bd9Sstevel@tonic-gate 	}
21317c478bd9Sstevel@tonic-gate 
21327c478bd9Sstevel@tonic-gate 	for (i = start; i < datalen; i++)
21337c478bd9Sstevel@tonic-gate 		*datap++ = i;
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 	if (family == AF_INET) {
21367c478bd9Sstevel@tonic-gate 		if (!use_udp)
21377c478bd9Sstevel@tonic-gate 			icp->icmp_cksum = in_cksum((ushort_t *)icp, cc);
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate 		i = sendto(send_sock, (char *)out_pkt, cc, 0, whereto,
21407c478bd9Sstevel@tonic-gate 		    sizeof (struct sockaddr_in));
21417c478bd9Sstevel@tonic-gate 	} else {
21427c478bd9Sstevel@tonic-gate 		/*
21437c478bd9Sstevel@tonic-gate 		 * Fill in the rest of the msghdr structure. msg_control is set
21447c478bd9Sstevel@tonic-gate 		 * in set_ancillary_data().
21457c478bd9Sstevel@tonic-gate 		 */
21467c478bd9Sstevel@tonic-gate 		msg6->msg_name = to6;
21477c478bd9Sstevel@tonic-gate 		msg6->msg_namelen = sizeof (struct sockaddr_in6);
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 		iov.iov_base = out_pkt;
21507c478bd9Sstevel@tonic-gate 		iov.iov_len = cc;
21517c478bd9Sstevel@tonic-gate 
21527c478bd9Sstevel@tonic-gate 		msg6->msg_iov = &iov;
21537c478bd9Sstevel@tonic-gate 		msg6->msg_iovlen = 1;
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate 		i = sendmsg(send_sock, msg6, 0);
21567c478bd9Sstevel@tonic-gate 	}
21577c478bd9Sstevel@tonic-gate 
21587c478bd9Sstevel@tonic-gate 	/* This is a more precise time (right after we send the packet) */
21597c478bd9Sstevel@tonic-gate 	t_last_probe_sent = gethrtime();
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	if (i < 0 || i != cc)  {
21627c478bd9Sstevel@tonic-gate 		if (i < 0) {
21637c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: sendto %s\n", progname,
21647c478bd9Sstevel@tonic-gate 			    strerror(errno));
21657c478bd9Sstevel@tonic-gate 			if (!stats)
21667c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
21677c478bd9Sstevel@tonic-gate 		}
21687c478bd9Sstevel@tonic-gate 		Printf("ping: wrote %s %d chars, ret=%d\n",
21697c478bd9Sstevel@tonic-gate 		    targethost, cc, i);
21707c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
21717c478bd9Sstevel@tonic-gate 	}
21727c478bd9Sstevel@tonic-gate }
21737c478bd9Sstevel@tonic-gate 
21747c478bd9Sstevel@tonic-gate /*
21757c478bd9Sstevel@tonic-gate  * Return a hostname for the given IP address.
21767c478bd9Sstevel@tonic-gate  */
21777c478bd9Sstevel@tonic-gate char *
pr_name(char * addr,int family)21787c478bd9Sstevel@tonic-gate pr_name(char *addr, int family)
21797c478bd9Sstevel@tonic-gate {
21807c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
21817c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
21827c478bd9Sstevel@tonic-gate 	struct sockaddr *sa;
21837c478bd9Sstevel@tonic-gate 	static struct in6_addr prev_addr = IN6ADDR_ANY_INIT;
21847c478bd9Sstevel@tonic-gate 	char *cp;
21857c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
21867c478bd9Sstevel@tonic-gate 	static char buf[NI_MAXHOST + INET6_ADDRSTRLEN + 3];
21877c478bd9Sstevel@tonic-gate 	uint_t slen, alen, hlen;
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 	switch (family) {
21907c478bd9Sstevel@tonic-gate 	case AF_INET:
21917c478bd9Sstevel@tonic-gate 		(void) memset(&sin, 0, sizeof (sin));
21927c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in);
21937c478bd9Sstevel@tonic-gate 		alen = sizeof (struct in_addr);
21947c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
21957c478bd9Sstevel@tonic-gate 		sin.sin_addr = *(struct in_addr *)addr;
21967c478bd9Sstevel@tonic-gate 		sin.sin_port = 0;
21977c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr *)&sin;
21987c478bd9Sstevel@tonic-gate 		break;
21997c478bd9Sstevel@tonic-gate 	case AF_INET6:
22007c478bd9Sstevel@tonic-gate 		(void) memset(&sin6, 0, sizeof (sin6));
22017c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in6);
22027c478bd9Sstevel@tonic-gate 		alen = sizeof (struct in6_addr);
22037c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
22047c478bd9Sstevel@tonic-gate 		sin6.sin6_addr = *(struct in6_addr *)addr;
22057c478bd9Sstevel@tonic-gate 		sin6.sin6_port = 0;
22067c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr *)&sin6;
22077c478bd9Sstevel@tonic-gate 		break;
22087c478bd9Sstevel@tonic-gate 	default:
22097c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (buf), "<invalid address family>");
22107c478bd9Sstevel@tonic-gate 		return (buf);
22117c478bd9Sstevel@tonic-gate 	}
22127c478bd9Sstevel@tonic-gate 	sa->sa_family = family;
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 	/* compare with the buffered (previous) lookup */
22157c478bd9Sstevel@tonic-gate 	if (memcmp(addr, &prev_addr, alen) != 0) {
22167c478bd9Sstevel@tonic-gate 		int flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD;
2217b08923d6SRobert Mustacchi 		mutex_enter(&ns_lock);
2218b08923d6SRobert Mustacchi 		ns_active = _B_TRUE;
2219b08923d6SRobert Mustacchi 		ns_starttime = gethrtime();
2220b08923d6SRobert Mustacchi 		mutex_exit(&ns_lock);
22217c478bd9Sstevel@tonic-gate 		if (getnameinfo(sa, slen, buf, sizeof (buf),
22227c478bd9Sstevel@tonic-gate 		    NULL, 0, flags) != 0) {
22237c478bd9Sstevel@tonic-gate 			/* getnameinfo() failed; return just the address */
22247c478bd9Sstevel@tonic-gate 			if (inet_ntop(family, (const void*)addr,
22257c478bd9Sstevel@tonic-gate 			    buf, sizeof (buf)) == NULL)
22267c478bd9Sstevel@tonic-gate 				buf[0] = 0;
22277c478bd9Sstevel@tonic-gate 		} else if (!nflag) {
22287c478bd9Sstevel@tonic-gate 			/* append numeric address to hostname string */
22297c478bd9Sstevel@tonic-gate 			hlen = strlen(buf);
22307c478bd9Sstevel@tonic-gate 			cp = (char *)(buf + hlen);
22317c478bd9Sstevel@tonic-gate 			(void) snprintf(cp, sizeof (buf) - hlen, " (%s)",
22327c478bd9Sstevel@tonic-gate 			    inet_ntop(family, (const void *)addr, abuf,
22337c478bd9Sstevel@tonic-gate 			    sizeof (abuf)));
22347c478bd9Sstevel@tonic-gate 		}
2235b08923d6SRobert Mustacchi 		mutex_enter(&ns_lock);
2236b08923d6SRobert Mustacchi 		ns_active = _B_FALSE;
2237b08923d6SRobert Mustacchi 		mutex_exit(&ns_lock);
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
22407c478bd9Sstevel@tonic-gate 		prev_addr = *(struct in6_addr *)addr;
22417c478bd9Sstevel@tonic-gate 	}
22427c478bd9Sstevel@tonic-gate 	return (buf);
22437c478bd9Sstevel@tonic-gate }
22447c478bd9Sstevel@tonic-gate 
22457c478bd9Sstevel@tonic-gate /*
22467c478bd9Sstevel@tonic-gate  * Return the protocol string, given its protocol number.
22477c478bd9Sstevel@tonic-gate  */
22487c478bd9Sstevel@tonic-gate char *
pr_protocol(int prot)22497c478bd9Sstevel@tonic-gate pr_protocol(int prot)
22507c478bd9Sstevel@tonic-gate {
22517c478bd9Sstevel@tonic-gate 	static char buf[20];
22527c478bd9Sstevel@tonic-gate 
22537c478bd9Sstevel@tonic-gate 	switch (prot) {
22547c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
22557c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "icmp6", sizeof (buf));
22567c478bd9Sstevel@tonic-gate 		break;
22577c478bd9Sstevel@tonic-gate 
22587c478bd9Sstevel@tonic-gate 	case IPPROTO_ICMP:
22597c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "icmp", sizeof (buf));
22607c478bd9Sstevel@tonic-gate 		break;
22617c478bd9Sstevel@tonic-gate 
22627c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:
22637c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "tcp", sizeof (buf));
22647c478bd9Sstevel@tonic-gate 		break;
22657c478bd9Sstevel@tonic-gate 
22667c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
22677c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "udp", sizeof (buf));
22687c478bd9Sstevel@tonic-gate 		break;
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 	default:
22717c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (buf), "prot %d", prot);
22727c478bd9Sstevel@tonic-gate 		break;
22737c478bd9Sstevel@tonic-gate 	}
22747c478bd9Sstevel@tonic-gate 
22757c478bd9Sstevel@tonic-gate 	return (buf);
22767c478bd9Sstevel@tonic-gate }
22777c478bd9Sstevel@tonic-gate 
22787c478bd9Sstevel@tonic-gate /*
22797c478bd9Sstevel@tonic-gate  * Checks if value is between seq_begin and seq_begin+seq_len. Note that
22807c478bd9Sstevel@tonic-gate  * sequence numbers wrap around after MAX_ICMP_SEQ (== MAX_PORT).
22817c478bd9Sstevel@tonic-gate  */
22827c478bd9Sstevel@tonic-gate boolean_t
seq_match(ushort_t seq_begin,int seq_len,ushort_t value)22837c478bd9Sstevel@tonic-gate seq_match(ushort_t seq_begin, int seq_len, ushort_t value)
22847c478bd9Sstevel@tonic-gate {
22857c478bd9Sstevel@tonic-gate 	/*
22867c478bd9Sstevel@tonic-gate 	 * If seq_len is too big, like some value greater than MAX_ICMP_SEQ/2,
22877c478bd9Sstevel@tonic-gate 	 * truncate it down to MAX_ICMP_SEQ/2. We are not going to accept any
22887c478bd9Sstevel@tonic-gate 	 * reply which come 83hr later!
22897c478bd9Sstevel@tonic-gate 	 */
22907c478bd9Sstevel@tonic-gate 	if (seq_len > MAX_ICMP_SEQ / 2) {
22917c478bd9Sstevel@tonic-gate 		seq_begin = (seq_begin + seq_len - MAX_ICMP_SEQ / 2) %
22927c478bd9Sstevel@tonic-gate 		    (MAX_ICMP_SEQ + 1);
22937c478bd9Sstevel@tonic-gate 		seq_len = MAX_ICMP_SEQ / 2;
22947c478bd9Sstevel@tonic-gate 	}
22957c478bd9Sstevel@tonic-gate 
22967c478bd9Sstevel@tonic-gate 	if (PINGSEQ_LEQ(seq_begin, value) &&
22977c478bd9Sstevel@tonic-gate 	    PINGSEQ_LEQ(value, (seq_begin + seq_len - 1) % (MAX_ICMP_SEQ + 1)))
22987c478bd9Sstevel@tonic-gate 		return (_B_TRUE);
22997c478bd9Sstevel@tonic-gate 	else
23007c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
23017c478bd9Sstevel@tonic-gate }
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate /*
23047c478bd9Sstevel@tonic-gate  * For a given icmp_seq, find which destination address we must have sent this
23057c478bd9Sstevel@tonic-gate  * to.
23067c478bd9Sstevel@tonic-gate  */
23077c478bd9Sstevel@tonic-gate void
find_dstaddr(ushort_t icmpseq,union any_in_addr * ipaddr)23087c478bd9Sstevel@tonic-gate find_dstaddr(ushort_t icmpseq, union any_in_addr *ipaddr)
23097c478bd9Sstevel@tonic-gate {
23107c478bd9Sstevel@tonic-gate 	struct targetaddr *target = targetaddr_list;
23117c478bd9Sstevel@tonic-gate 	int real_seq;
23127c478bd9Sstevel@tonic-gate 	int targetaddr_index;
23137c478bd9Sstevel@tonic-gate 	int real_npackets;
23147c478bd9Sstevel@tonic-gate 	int i;
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	ipaddr->addr6 = in6addr_any;
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate 	/*
23197c478bd9Sstevel@tonic-gate 	 * If this is probe_all and not stats, then the number of probes sent to
23207c478bd9Sstevel@tonic-gate 	 * each IP address may be different (remember, we stop sending to one IP
23217c478bd9Sstevel@tonic-gate 	 * address as soon as it replies). They are stored in target->num_sent
23227c478bd9Sstevel@tonic-gate 	 * field. Since we don't wrap around the list (!stats), they are also
23237c478bd9Sstevel@tonic-gate 	 * preserved.
23247c478bd9Sstevel@tonic-gate 	 */
23257c478bd9Sstevel@tonic-gate 	if (probe_all && !stats) {
23267c478bd9Sstevel@tonic-gate 		do {
23277c478bd9Sstevel@tonic-gate 			if (seq_match(target->starting_seq_num,
23287c478bd9Sstevel@tonic-gate 			    target->num_sent, icmpseq)) {
23297c478bd9Sstevel@tonic-gate 				ipaddr->addr6 = target->dst_addr.addr6;
23307c478bd9Sstevel@tonic-gate 				/*
23317c478bd9Sstevel@tonic-gate 				 * We are not immediately return()ing here.
23327c478bd9Sstevel@tonic-gate 				 * Because of wrapping, we might find another
23337c478bd9Sstevel@tonic-gate 				 * match later, which is more likely to be the
23347c478bd9Sstevel@tonic-gate 				 * real one.
23357c478bd9Sstevel@tonic-gate 				 */
23367c478bd9Sstevel@tonic-gate 			}
23377c478bd9Sstevel@tonic-gate 			target = target->next;
23387c478bd9Sstevel@tonic-gate 		} while (target != NULL);
23397c478bd9Sstevel@tonic-gate 	} else {
23407c478bd9Sstevel@tonic-gate 		/*
23417c478bd9Sstevel@tonic-gate 		 * Find the absolute (non-wrapped) seq number within the last
23427c478bd9Sstevel@tonic-gate 		 * 64K
23437c478bd9Sstevel@tonic-gate 		 */
23447c478bd9Sstevel@tonic-gate 		if (icmpseq < (ntransmitted % (MAX_ICMP_SEQ + 1))) {
23457c478bd9Sstevel@tonic-gate 			real_seq = num_wraps * (MAX_ICMP_SEQ + 1) + icmpseq;
23467c478bd9Sstevel@tonic-gate 		} else {
23477c478bd9Sstevel@tonic-gate 			real_seq = (num_wraps - 1) * (MAX_ICMP_SEQ + 1) +
23487c478bd9Sstevel@tonic-gate 			    icmpseq;
23497c478bd9Sstevel@tonic-gate 		}
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 		/* Make sure it's non-negative */
23527c478bd9Sstevel@tonic-gate 		if (real_seq < 0)
23537c478bd9Sstevel@tonic-gate 			return;
23547c478bd9Sstevel@tonic-gate 		real_npackets = (npackets == 0) ? 1 : npackets;
23557c478bd9Sstevel@tonic-gate 
23567c478bd9Sstevel@tonic-gate 		/*
23577c478bd9Sstevel@tonic-gate 		 * We sent npackets many packets to each of those
23587c478bd9Sstevel@tonic-gate 		 * num_targetaddrs many IP addresses.
23597c478bd9Sstevel@tonic-gate 		 */
23607c478bd9Sstevel@tonic-gate 		targetaddr_index =
23617c478bd9Sstevel@tonic-gate 		    (real_seq % (num_targetaddrs * real_npackets)) /
23627c478bd9Sstevel@tonic-gate 		    real_npackets;
23637c478bd9Sstevel@tonic-gate 		for (i = 0; i < targetaddr_index; i++)
23647c478bd9Sstevel@tonic-gate 			target = target->next;
23657c478bd9Sstevel@tonic-gate 		ipaddr->addr6 = target->dst_addr.addr6;
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate }
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate /*
23707c478bd9Sstevel@tonic-gate  * Checksum routine for Internet Protocol family headers (C Version)
23717c478bd9Sstevel@tonic-gate  */
23727c478bd9Sstevel@tonic-gate static ushort_t
in_cksum(ushort_t * addr,int len)23737c478bd9Sstevel@tonic-gate in_cksum(ushort_t *addr, int len)
23747c478bd9Sstevel@tonic-gate {
23757c478bd9Sstevel@tonic-gate 	int nleft = len;
23767c478bd9Sstevel@tonic-gate 	ushort_t *w = addr;
23777c478bd9Sstevel@tonic-gate 	ushort_t answer;
23787c478bd9Sstevel@tonic-gate 	ushort_t odd_byte = 0;
23797c478bd9Sstevel@tonic-gate 	int sum = 0;
23807c478bd9Sstevel@tonic-gate 
23817c478bd9Sstevel@tonic-gate 	/*
23827c478bd9Sstevel@tonic-gate 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
23837c478bd9Sstevel@tonic-gate 	 *  we add sequential 16 bit words to it, and at the end, fold
23847c478bd9Sstevel@tonic-gate 	 *  back all the carry bits from the top 16 bits into the lower
23857c478bd9Sstevel@tonic-gate 	 *  16 bits.
23867c478bd9Sstevel@tonic-gate 	 */
23877c478bd9Sstevel@tonic-gate 	while (nleft > 1) {
23887c478bd9Sstevel@tonic-gate 		sum += *w++;
23897c478bd9Sstevel@tonic-gate 		nleft -= 2;
23907c478bd9Sstevel@tonic-gate 	}
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 	/* mop up an odd byte, if necessary */
23937c478bd9Sstevel@tonic-gate 	if (nleft == 1) {
23947c478bd9Sstevel@tonic-gate 		*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
23957c478bd9Sstevel@tonic-gate 		sum += odd_byte;
23967c478bd9Sstevel@tonic-gate 	}
23977c478bd9Sstevel@tonic-gate 
23987c478bd9Sstevel@tonic-gate 	/*
23997c478bd9Sstevel@tonic-gate 	 * add back carry outs from top 16 bits to low 16 bits
24007c478bd9Sstevel@tonic-gate 	 */
24017c478bd9Sstevel@tonic-gate 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
24027c478bd9Sstevel@tonic-gate 	sum += (sum >> 16);			/* add carry */
24037c478bd9Sstevel@tonic-gate 	answer = ~sum;				/* truncate to 16 bits */
24047c478bd9Sstevel@tonic-gate 	return (answer);
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate /*
24087c478bd9Sstevel@tonic-gate  * Subtract 2 timeval structs:  out = out - in.
24097c478bd9Sstevel@tonic-gate  * Out is assumed to be >= in.
24107c478bd9Sstevel@tonic-gate  */
24117c478bd9Sstevel@tonic-gate void
tvsub(struct timeval * out,struct timeval * in)24127c478bd9Sstevel@tonic-gate tvsub(struct timeval *out, struct timeval *in)
24137c478bd9Sstevel@tonic-gate {
24147c478bd9Sstevel@tonic-gate 	if ((out->tv_usec -= in->tv_usec) < 0) {
24157c478bd9Sstevel@tonic-gate 		out->tv_sec--;
24167c478bd9Sstevel@tonic-gate 		out->tv_usec += 1000000;
24177c478bd9Sstevel@tonic-gate 	}
24187c478bd9Sstevel@tonic-gate 	out->tv_sec -= in->tv_sec;
24197c478bd9Sstevel@tonic-gate }
24207c478bd9Sstevel@tonic-gate 
24217c478bd9Sstevel@tonic-gate /*
24227c478bd9Sstevel@tonic-gate  * Print out statistics, and give up.
24237c478bd9Sstevel@tonic-gate  * Heavily buffered STDIO is used here, so that all the statistics
24247c478bd9Sstevel@tonic-gate  * will be written with 1 sys-write call.  This is nice when more
24257c478bd9Sstevel@tonic-gate  * than one copy of the program is running on a terminal;  it prevents
24267c478bd9Sstevel@tonic-gate  * the statistics output from becoming intermingled.
24277c478bd9Sstevel@tonic-gate  */
24287c478bd9Sstevel@tonic-gate static void
finish()24297c478bd9Sstevel@tonic-gate finish()
24307c478bd9Sstevel@tonic-gate {
24317c478bd9Sstevel@tonic-gate 	Printf("\n----%s PING Statistics----\n", targethost);
24327c478bd9Sstevel@tonic-gate 	Printf("%d packets transmitted, ", ntransmitted);
24337c478bd9Sstevel@tonic-gate 	Printf("%d packets received, ", nreceived);
24347c478bd9Sstevel@tonic-gate 	if (ntransmitted) {
24357c478bd9Sstevel@tonic-gate 		if (nreceived <= ntransmitted) {
24367c478bd9Sstevel@tonic-gate 			Printf("%d%% packet loss",
24377c478bd9Sstevel@tonic-gate 			    (int)(((ntransmitted-nreceived)*100) /
24387c478bd9Sstevel@tonic-gate 			    ntransmitted));
24397c478bd9Sstevel@tonic-gate 		} else {
24407c478bd9Sstevel@tonic-gate 			Printf("%.2f times amplification",
24417c478bd9Sstevel@tonic-gate 			    (double)nreceived / (double)ntransmitted);
24427c478bd9Sstevel@tonic-gate 		}
24437c478bd9Sstevel@tonic-gate 	}
24447c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 	/* if packet is big enough to store timeval AND ... */
24477c478bd9Sstevel@tonic-gate 	if ((datalen >= sizeof (struct timeval)) && (nreceived > 0)) {
24487c478bd9Sstevel@tonic-gate 		double mean = (double)tsum / nreceived;
24497c478bd9Sstevel@tonic-gate 		double smean = (double)tsum2 / nreceived;
24507c478bd9Sstevel@tonic-gate 		double sd =
24517c478bd9Sstevel@tonic-gate 		    sqrt(((smean - mean*mean) * nreceived) / (nreceived-1));
24527c478bd9Sstevel@tonic-gate 
24537c478bd9Sstevel@tonic-gate 		Printf("round-trip (ms)  min/avg/max/stddev = "
24543c58dfd6Sjbeck 		    TIMEFORMAT "/" TIMEFORMAT "/"
24553c58dfd6Sjbeck 		    TIMEFORMAT "/" TIMEFORMAT "\n",
24563c58dfd6Sjbeck 		    (double)tmin / 1000, mean / 1000,
24573c58dfd6Sjbeck 		    (double)tmax / 1000, sd / 1000);
24587c478bd9Sstevel@tonic-gate 	}
24597c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
24607c478bd9Sstevel@tonic-gate 
24617c478bd9Sstevel@tonic-gate 	exit(is_alive ? EXIT_SUCCESS : EXIT_FAILURE);
24627c478bd9Sstevel@tonic-gate }
24637c478bd9Sstevel@tonic-gate 
24647c478bd9Sstevel@tonic-gate /*
24657c478bd9Sstevel@tonic-gate  * print the usage line
24667c478bd9Sstevel@tonic-gate  */
24677c478bd9Sstevel@tonic-gate static void
usage(char * cmdname)24687c478bd9Sstevel@tonic-gate usage(char *cmdname)
24697c478bd9Sstevel@tonic-gate {
24707c478bd9Sstevel@tonic-gate 	Fprintf(stderr, "usage: %s host [timeout]\n", cmdname);
24717c478bd9Sstevel@tonic-gate 	Fprintf(stderr,
24727c478bd9Sstevel@tonic-gate /* CSTYLED */
2473a252e007SChris Josephes "usage: %s -s [-l | -U] [-abdDLnRrv] [-A addr_family] [-c traffic_class]\n\t"
24744c10bc16Spwernau "[-g gateway [-g gateway ...]] [-N nexthop] [-F flow_label] [-I interval]\n\t"
24757c478bd9Sstevel@tonic-gate "[-i interface] [-P tos] [-p port] [-t ttl] host [data_size] [npackets]\n",
24767c478bd9Sstevel@tonic-gate 	    cmdname);
24777c478bd9Sstevel@tonic-gate }
24787c478bd9Sstevel@tonic-gate 
24797c478bd9Sstevel@tonic-gate /*
24807c478bd9Sstevel@tonic-gate  * Parse integer argument; exit with an error if it's not a number.
24817c478bd9Sstevel@tonic-gate  * Now it also accepts hex. values.
24827c478bd9Sstevel@tonic-gate  */
24837c478bd9Sstevel@tonic-gate static int
int_arg(char * s,char * what)24847c478bd9Sstevel@tonic-gate int_arg(char *s, char *what)
24857c478bd9Sstevel@tonic-gate {
24867c478bd9Sstevel@tonic-gate 	char *cp;
24877c478bd9Sstevel@tonic-gate 	char *ep;
24887c478bd9Sstevel@tonic-gate 	int num;
24897c478bd9Sstevel@tonic-gate 
24907c478bd9Sstevel@tonic-gate 	errno = 0;
24917c478bd9Sstevel@tonic-gate 	if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
24927c478bd9Sstevel@tonic-gate 		cp = s + 2;
24937c478bd9Sstevel@tonic-gate 		num = (int)strtol(cp, &ep, 16);
24947c478bd9Sstevel@tonic-gate 	} else {
24957c478bd9Sstevel@tonic-gate 		num = (int)strtol(s, &ep, 10);
24967c478bd9Sstevel@tonic-gate 	}
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate 	if (errno || *ep != '\0' || num < 0) {
2499b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: bad %s: %s\n", progname, what, s);
25007c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
25017c478bd9Sstevel@tonic-gate 	}
25027c478bd9Sstevel@tonic-gate 
25037c478bd9Sstevel@tonic-gate 	return (num);
25047c478bd9Sstevel@tonic-gate }
2505b08923d6SRobert Mustacchi 
2506b08923d6SRobert Mustacchi /*
2507b08923d6SRobert Mustacchi  * Parse the interval into a itimerspec. The interval used to originally be
2508b08923d6SRobert Mustacchi  * parsed as an integer argument. That means that one used to be able to specify
2509b08923d6SRobert Mustacchi  * an interval in hex. The strtod() family honors that at times, with strtod
2510b08923d6SRobert Mustacchi  * sometimes doing so depending on the compilation environment and strtof() and
2511b08923d6SRobert Mustacchi  * srtold() always doing that. To facilitiate that and not worry about a
2512b08923d6SRobert Mustacchi  * careless Makefile change breaking us, we instead just use strtold here, even
2513b08923d6SRobert Mustacchi  * though we really don't need the precision.
2514b08923d6SRobert Mustacchi  */
2515b08923d6SRobert Mustacchi static void
parse_interval(const char * s)2516*9c4b0eaaSRobert Mustacchi parse_interval(const char *s)
2517b08923d6SRobert Mustacchi {
2518b08923d6SRobert Mustacchi 	long double val;
2519b08923d6SRobert Mustacchi 	char *end;
2520b08923d6SRobert Mustacchi 
2521b08923d6SRobert Mustacchi 	errno = 0;
2522b08923d6SRobert Mustacchi 	val = strtold(s, &end);
2523b08923d6SRobert Mustacchi 	if (errno != 0 || *end != '\0') {
2524b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: bad interval: %s\n", progname, s);
2525b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
2526b08923d6SRobert Mustacchi 	}
2527b08923d6SRobert Mustacchi 
2528b08923d6SRobert Mustacchi 	/*
2529b08923d6SRobert Mustacchi 	 * Check values that we know are going to be bad. Anything greater than
2530b08923d6SRobert Mustacchi 	 * INT_MAX, anything less than 0, look for specific NaNs. Also, clamp
2531b08923d6SRobert Mustacchi 	 * the value at 0.01 seconds.
2532b08923d6SRobert Mustacchi 	 */
2533b08923d6SRobert Mustacchi 	if (val == NAN || val <= 0.0 || val >= INT_MAX) {
2534b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: bad interval: %s\n", progname, s);
2535b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
2536b08923d6SRobert Mustacchi 	}
2537b08923d6SRobert Mustacchi 
2538*9c4b0eaaSRobert Mustacchi 	if (val < 0.01L) {
2539b08923d6SRobert Mustacchi 		Fprintf(stderr, "%s: interval too small: %Lf\n", progname, val);
2540b08923d6SRobert Mustacchi 		exit(EXIT_FAILURE);
2541b08923d6SRobert Mustacchi 	}
2542b08923d6SRobert Mustacchi 
2543b08923d6SRobert Mustacchi 	interval.it_value.tv_sec = (long)val;
2544b08923d6SRobert Mustacchi 	interval.it_value.tv_nsec = (long)((val - interval.it_value.tv_sec) *
2545b08923d6SRobert Mustacchi 	    NANOSEC);
2546b08923d6SRobert Mustacchi 
2547b08923d6SRobert Mustacchi 	if (interval.it_value.tv_sec == 0 &&
2548b08923d6SRobert Mustacchi 	    interval.it_value.tv_nsec < mintime) {
2549b08923d6SRobert Mustacchi 		mintime = interval.it_value.tv_nsec;
2550b08923d6SRobert Mustacchi 	}
2551b08923d6SRobert Mustacchi }
2552b08923d6SRobert Mustacchi 
2553b08923d6SRobert Mustacchi /*
2554b08923d6SRobert Mustacchi  * We should have an SO_TIMESTAMP message for this socket to indicate
2555b08923d6SRobert Mustacchi  * the actual time that the message took. If we don't we'll fall back to
2556b08923d6SRobert Mustacchi  * gettimeofday(); however, that can cause any delays due to DNS
2557b08923d6SRobert Mustacchi  * resolution and the like to end up wreaking havoc on us.
2558b08923d6SRobert Mustacchi  */
2559b08923d6SRobert Mustacchi void
ping_gettime(struct msghdr * msg,struct timeval * tv)2560b08923d6SRobert Mustacchi ping_gettime(struct msghdr *msg, struct timeval *tv)
2561b08923d6SRobert Mustacchi {
2562b08923d6SRobert Mustacchi 	struct cmsghdr *cmsg;
2563b08923d6SRobert Mustacchi 
2564b08923d6SRobert Mustacchi 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
2565b08923d6SRobert Mustacchi 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
2566b08923d6SRobert Mustacchi 		if (cmsg->cmsg_level == SOL_SOCKET &&
2567b08923d6SRobert Mustacchi 		    cmsg->cmsg_type == SO_TIMESTAMP &&
2568b08923d6SRobert Mustacchi 		    cmsg->cmsg_len == CMSG_LEN(sizeof (*tv))) {
2569b08923d6SRobert Mustacchi 			bcopy(CMSG_DATA(cmsg), tv, sizeof (*tv));
2570b08923d6SRobert Mustacchi 			return;
2571b08923d6SRobert Mustacchi 		}
2572b08923d6SRobert Mustacchi 	}
2573b08923d6SRobert Mustacchi 
2574b08923d6SRobert Mustacchi 	(void) gettimeofday(tv, (struct timezone *)NULL);
2575b08923d6SRobert Mustacchi }
2576b08923d6SRobert Mustacchi 
2577b08923d6SRobert Mustacchi /*
2578b08923d6SRobert Mustacchi  * The purpose of this thread is to try and inform a user that we're blocked
2579b08923d6SRobert Mustacchi  * doing name lookups. For various reasons, ping has to try and look up the IP
2580b08923d6SRobert Mustacchi  * addresses it receives via name services unless the -n flag is specified. The
2581b08923d6SRobert Mustacchi  * irony of this is that when trying to use ping to actually diagnose a broken
2582b08923d6SRobert Mustacchi  * network, name services are unlikely to be available and that will result in a
2583b08923d6SRobert Mustacchi  * lot of confusion as to why pings seem like they're not working. As such, we
2584b08923d6SRobert Mustacchi  * basically wake up every 2 seconds and check whether or not we've hit such a
2585b08923d6SRobert Mustacchi  * condition where we should inform the user via stderr.
2586b08923d6SRobert Mustacchi  *
2587b08923d6SRobert Mustacchi  * Once they've been informed, we do not inform them again until approximately a
2588b08923d6SRobert Mustacchi  * minute of time has passed, in case that things are working intermittently.
2589b08923d6SRobert Mustacchi  */
2590b08923d6SRobert Mustacchi /*ARGSUSED*/
2591b08923d6SRobert Mustacchi static void *
ns_warning_thr(void * unused)2592b08923d6SRobert Mustacchi ns_warning_thr(void *unused)
2593b08923d6SRobert Mustacchi {
2594b08923d6SRobert Mustacchi 	for (;;) {
2595b08923d6SRobert Mustacchi 		hrtime_t now;
2596b08923d6SRobert Mustacchi 
2597b08923d6SRobert Mustacchi 		(void) sleep(ns_sleeptime);
2598b08923d6SRobert Mustacchi 		now = gethrtime();
2599b08923d6SRobert Mustacchi 		mutex_enter(&ns_lock);
2600b08923d6SRobert Mustacchi 		if (ns_active == _B_TRUE &&
2601b08923d6SRobert Mustacchi 		    now - ns_starttime >= ns_warntime * NANOSEC) {
2602afee3dc6SRobert Mustacchi 			Fprintf(stderr, "%s: warning: ICMP responses "
2603afee3dc6SRobert Mustacchi 			    "received, but name service lookups are "
2604afee3dc6SRobert Mustacchi 			    "taking a while. Use ping -n to disable "
2605afee3dc6SRobert Mustacchi 			    "name service lookups.\n",
2606afee3dc6SRobert Mustacchi 			    progname);
2607afee3dc6SRobert Mustacchi 			mutex_exit(&ns_lock);
2608afee3dc6SRobert Mustacchi 			return (NULL);
2609b08923d6SRobert Mustacchi 		}
2610b08923d6SRobert Mustacchi 		mutex_exit(&ns_lock);
2611b08923d6SRobert Mustacchi 	}
2612b08923d6SRobert Mustacchi 
2613b08923d6SRobert Mustacchi 	/* LINTED: E_STMT_NOT_REACHED */
2614b08923d6SRobert Mustacchi 	return (NULL);
2615b08923d6SRobert Mustacchi }
2616