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 40*b08923d6SRobert Mustacchi /* 41*b08923d6SRobert Mustacchi * Copyright 2015, Joyent, Inc. 42*b08923d6SRobert Mustacchi */ 43*b08923d6SRobert Mustacchi 44*b08923d6SRobert Mustacchi #include <assert.h> 457c478bd9Sstevel@tonic-gate #include <stdio.h> 467c478bd9Sstevel@tonic-gate #include <strings.h> 477c478bd9Sstevel@tonic-gate #include <errno.h> 487c478bd9Sstevel@tonic-gate #include <fcntl.h> 497c478bd9Sstevel@tonic-gate #include <unistd.h> 507c478bd9Sstevel@tonic-gate #include <signal.h> 517c478bd9Sstevel@tonic-gate #include <limits.h> 527c478bd9Sstevel@tonic-gate #include <math.h> 53*b08923d6SRobert Mustacchi #include <locale.h> 54*b08923d6SRobert Mustacchi #include <thread.h> 55*b08923d6SRobert Mustacchi #include <synch.h> 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate #include <sys/time.h> 587c478bd9Sstevel@tonic-gate #include <sys/param.h> 597c478bd9Sstevel@tonic-gate #include <sys/socket.h> 607c478bd9Sstevel@tonic-gate #include <sys/sockio.h> 617c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 627c478bd9Sstevel@tonic-gate #include <sys/file.h> 637c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 64*b08923d6SRobert Mustacchi #include <sys/debug.h> 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate #include <arpa/inet.h> 677c478bd9Sstevel@tonic-gate #include <net/if.h> 687c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h> 697c478bd9Sstevel@tonic-gate #include <netinet/in.h> 707c478bd9Sstevel@tonic-gate #include <netinet/ip.h> 717c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h> 727c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h> 737c478bd9Sstevel@tonic-gate #include <netinet/ip6.h> 747c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h> 757c478bd9Sstevel@tonic-gate #include <netinet/udp.h> 767c478bd9Sstevel@tonic-gate #include <netdb.h> 777c478bd9Sstevel@tonic-gate #include <stdlib.h> 787c478bd9Sstevel@tonic-gate #include <priv_utils.h> 797c478bd9Sstevel@tonic-gate 800406ceaaSmeem #include <libinetutil.h> 817c478bd9Sstevel@tonic-gate #include "ping.h" 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate /* 847c478bd9Sstevel@tonic-gate * This macro is used to compare 16bit, wrapping sequence numbers. Inspired by 857c478bd9Sstevel@tonic-gate * TCP's SEQ_LEQ macro. 867c478bd9Sstevel@tonic-gate */ 877c478bd9Sstevel@tonic-gate #define PINGSEQ_LEQ(a, b) ((int16_t)((a)-(b)) <= 0) 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate #define MAX_WAIT 10 /* max sec. to wait for response */ 907c478bd9Sstevel@tonic-gate #define MAX_TRAFFIC_CLASS 255 /* max traffic class for IPv6 */ 917c478bd9Sstevel@tonic-gate #define MAX_FLOW_LABEL 0xFFFFF /* max flow label for IPv6 */ 927c478bd9Sstevel@tonic-gate #define MAX_TOS 255 /* max type-of-service for IPv4 */ 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate #define TIMEOUT 20 /* default timeout value */ 957c478bd9Sstevel@tonic-gate #define DEFAULT_DATALEN 56 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate #define MULTICAST_NOLOOP 1 /* multicast options */ 987c478bd9Sstevel@tonic-gate #define MULTICAST_TTL 2 997c478bd9Sstevel@tonic-gate #define MULTICAST_IF 4 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate #define IF_INDEX 0 /* types of -i argument */ 1027c478bd9Sstevel@tonic-gate #define IF_NAME 1 1037c478bd9Sstevel@tonic-gate #define IF_ADDR 2 1047c478bd9Sstevel@tonic-gate #define IF_ADDR6 3 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate #ifdef BSD 1077c478bd9Sstevel@tonic-gate #define setbuf(s, b) setlinebuf((s)) 1087c478bd9Sstevel@tonic-gate #endif /* BSD */ 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate /* interface identification */ 1127c478bd9Sstevel@tonic-gate union if_id { 1137c478bd9Sstevel@tonic-gate int index; /* interface index (e.g., 1, 2) */ 1147c478bd9Sstevel@tonic-gate char *name; /* interface name (e.g., le0, hme0) */ 1157c478bd9Sstevel@tonic-gate union any_in_addr addr; /* interface address (e.g., 10.123.4.5) */ 1167c478bd9Sstevel@tonic-gate }; 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate /* stores the interface supplied by the user */ 1197c478bd9Sstevel@tonic-gate struct if_entry { 1207c478bd9Sstevel@tonic-gate char *str; /* unresolved, string input */ 1217c478bd9Sstevel@tonic-gate int id_type; /* type of ID (index, name, addr, addr6) */ 1227c478bd9Sstevel@tonic-gate union if_id id; /* ID */ 1237c478bd9Sstevel@tonic-gate }; 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate char *progname; 1267c478bd9Sstevel@tonic-gate char *targethost; 1274c10bc16Spwernau char *nexthop; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate static int send_sock; /* send sockets */ 1307c478bd9Sstevel@tonic-gate static int send_sock6; 1317c478bd9Sstevel@tonic-gate static struct sockaddr_in to; /* where to send */ 1327c478bd9Sstevel@tonic-gate static struct sockaddr_in6 to6; 1337c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list[MAX_GWS]; /* gateways */ 1347c478bd9Sstevel@tonic-gate static union any_in_addr gw_IP_list6[MAX_GWS6]; 1357c478bd9Sstevel@tonic-gate static int if_index = 0; /* outgoing interface index */ 1367c478bd9Sstevel@tonic-gate boolean_t is_alive = _B_FALSE; /* is target host alive */ 1377c478bd9Sstevel@tonic-gate struct targetaddr *current_targetaddr; /* current target IP address to probe */ 1387c478bd9Sstevel@tonic-gate static struct targetaddr *targetaddr_list; /* list of IP addresses to probe */ 1397c478bd9Sstevel@tonic-gate static int num_targetaddrs; /* no of target addresses to probe */ 1407c478bd9Sstevel@tonic-gate static int num_v4 = 0; /* count of IPv4 addresses */ 1417c478bd9Sstevel@tonic-gate static int num_v6 = 0; /* count of IPv6 addresses */ 1427c478bd9Sstevel@tonic-gate boolean_t verbose = _B_FALSE; /* verbose output */ 1437c478bd9Sstevel@tonic-gate boolean_t stats = _B_FALSE; /* display statistics */ 1447c478bd9Sstevel@tonic-gate static boolean_t settos = _B_FALSE; /* set type-of-service value */ 1457c478bd9Sstevel@tonic-gate boolean_t rr_option = _B_FALSE; /* true if using record route */ 1467c478bd9Sstevel@tonic-gate boolean_t send_reply = _B_FALSE; /* Send an ICMP_{ECHO|TSTAMP}REPLY */ 1477c478bd9Sstevel@tonic-gate /* that goes to target and comes back */ 1487c478bd9Sstevel@tonic-gate /* to the the sender via src routing. */ 1497c478bd9Sstevel@tonic-gate boolean_t strict = _B_FALSE; /* true if using strict source route */ 1507c478bd9Sstevel@tonic-gate boolean_t ts_option = _B_FALSE; /* true if using timestamp option */ 1517c478bd9Sstevel@tonic-gate boolean_t use_icmp_ts = _B_FALSE; /* Use ICMP timestamp request */ 1527c478bd9Sstevel@tonic-gate boolean_t use_udp = _B_FALSE; /* Use UDP instead of ICMP */ 1537c478bd9Sstevel@tonic-gate boolean_t probe_all = _B_FALSE; /* probe all the IP addresses */ 1547c478bd9Sstevel@tonic-gate boolean_t nflag = _B_FALSE; /* do not reverse lookup addresses */ 155a356273cSpwernau boolean_t bypass = _B_FALSE; /* bypass IPsec policy */ 1567c478bd9Sstevel@tonic-gate static int family_input = AF_UNSPEC; /* address family supplied by user */ 1577c478bd9Sstevel@tonic-gate int datalen = DEFAULT_DATALEN; /* How much data */ 1587c478bd9Sstevel@tonic-gate int ts_flag; /* timestamp flag value */ 1597c478bd9Sstevel@tonic-gate static int num_gw; /* number of gateways */ 1607c478bd9Sstevel@tonic-gate static int eff_num_gw; /* effective number of gateways */ 1617c478bd9Sstevel@tonic-gate /* if send_reply, it's 2*num_gw+1 */ 1627c478bd9Sstevel@tonic-gate static int num_wraps = -1; /* no of times 64K icmp_seq wrapped */ 1637c478bd9Sstevel@tonic-gate static ushort_t dest_port = 32768 + 666; /* starting port for the UDP probes */ 1647c478bd9Sstevel@tonic-gate static char *gw_list[MAXMAX_GWS]; /* list of gateways as user enters */ 1657c478bd9Sstevel@tonic-gate static int options; /* socket options */ 1667c478bd9Sstevel@tonic-gate static int moptions; /* multicast options */ 1677c478bd9Sstevel@tonic-gate int npackets; /* number of packets to send */ 1687c478bd9Sstevel@tonic-gate static ushort_t tos; /* type-of-service value */ 1697c478bd9Sstevel@tonic-gate static int hoplimit = -1; /* time-to-live value */ 170bd670b35SErik Nordmark static int dontfrag; /* IP*_DONTFRAG */ 1717c478bd9Sstevel@tonic-gate static int timeout = TIMEOUT; /* timeout value (sec) for probes */ 1727c478bd9Sstevel@tonic-gate static struct if_entry out_if; /* interface argument */ 1737c478bd9Sstevel@tonic-gate int ident; /* ID for this ping run */ 1747c478bd9Sstevel@tonic-gate static hrtime_t t_last_probe_sent; /* the time we sent the last probe */ 175*b08923d6SRobert Mustacchi static timer_t timer; /* timer for waiting */ 176*b08923d6SRobert Mustacchi static volatile boolean_t timer_done = _B_FALSE; /* timer finished? */ 177*b08923d6SRobert Mustacchi static struct itimerspec interval = { { 0, 0 }, { 1, 0 } }; /* Interval for */ 178*b08923d6SRobert Mustacchi /* -I. The default interval is 1s. */ 179*b08923d6SRobert Mustacchi static hrtime_t mintime = NSEC2MSEC(500); /* minimum time between pings */ 180*b08923d6SRobert Mustacchi 181*b08923d6SRobert Mustacchi /* 182*b08923d6SRobert Mustacchi * Globals for our name services warning. See ns_warning_thr() for more on why 183*b08923d6SRobert Mustacchi * this exists. 184*b08923d6SRobert Mustacchi */ 185*b08923d6SRobert Mustacchi static mutex_t ns_lock = ERRORCHECKMUTEX; /* Protects the following data */ 186*b08923d6SRobert Mustacchi static boolean_t ns_active = _B_FALSE; /* Lookup is going on */ 187*b08923d6SRobert Mustacchi static hrtime_t ns_starttime; /* Time the lookup started */ 188*b08923d6SRobert Mustacchi static int ns_sleeptime = 2; /* Time in seconds between checks */ 189*b08923d6SRobert Mustacchi static int ns_warntime = 2; /* Time in seconds before warning */ 190*b08923d6SRobert Mustacchi static int ns_warninter = 60; /* Time in seconds between warnings */ 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); 230*b08923d6SRobert Mustacchi static void *ns_warning_thr(void *); 231*b08923d6SRobert Mustacchi static void parse_interval(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 */ 2598c332a0dSja97890 int 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; 274f4b3ec61Sdh155122 boolean_t has_sys_ip_config; 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate progname = argv[0]; 2777c478bd9Sstevel@tonic-gate 278*b08923d6SRobert Mustacchi (void) setlocale(LC_ALL, ""); 279*b08923d6SRobert Mustacchi 2807c478bd9Sstevel@tonic-gate /* 2811da45818Spwernau * This program needs the net_icmpaccess privilege for creating 282f4b3ec61Sdh155122 * 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 * 287f4b3ec61Sdh155122 * 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 */ 291f4b3ec61Sdh155122 has_sys_ip_config = priv_ineffect(PRIV_SYS_IP_CONFIG); 2921da45818Spwernau 2937c478bd9Sstevel@tonic-gate (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS, 294f4b3ec61Sdh155122 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; 353*b08923d6SRobert 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. 5727c478bd9Sstevel@tonic-gate * 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 *) 7077c478bd9Sstevel@tonic-gate 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 *) 7127c478bd9Sstevel@tonic-gate 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 720*b08923d6SRobert Mustacchi /* Create our timer for future use */ 721*b08923d6SRobert Mustacchi if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) { 722*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: failed to create timer: %s\n", 723*b08923d6SRobert Mustacchi progname, strerror(errno)); 724*b08923d6SRobert Mustacchi exit(EXIT_FAILURE); 725*b08923d6SRobert Mustacchi } 726*b08923d6SRobert Mustacchi 727*b08923d6SRobert Mustacchi /* 728*b08923d6SRobert Mustacchi * Finally start up the name services warning thread. 729*b08923d6SRobert Mustacchi */ 730*b08923d6SRobert Mustacchi if (thr_create(NULL, 0, ns_warning_thr, NULL, 731*b08923d6SRobert Mustacchi THR_DETACHED | THR_DAEMON, NULL) != 0) { 732*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: failed to create name services " 733*b08923d6SRobert Mustacchi "thread: %s\n", progname, strerror(errno)); 734*b08923d6SRobert Mustacchi exit(EXIT_FAILURE); 735*b08923d6SRobert Mustacchi } 736*b08923d6SRobert 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 7528c332a0dSja97890 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 * 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 * 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 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 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 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 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 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 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 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; 1190*b08923d6SRobert Mustacchi struct sockaddr_in *sin = NULL; 1191*b08923d6SRobert 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) { 1250*b08923d6SRobert Mustacchi assert(sin != NULL); 12517c478bd9Sstevel@tonic-gate src_addr->addr = sin->sin_addr; 12527c478bd9Sstevel@tonic-gate } else { 1253*b08923d6SRobert 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 12617c478bd9Sstevel@tonic-gate /* 12624c10bc16Spwernau * Set the IP_NEXTHOP/IPV6_NEXTHOP socket option. 12634c10bc16Spwernau * exits on failure 12644c10bc16Spwernau */ 12654c10bc16Spwernau static void 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 1275f4b3ec61Sdh155122 /* 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 12844c10bc16Spwernau Fprintf(stderr, "%s: setsockopt %s\n", 12854c10bc16Spwernau 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 13064c10bc16Spwernau /* 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 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)); 1363a356273cSpwernau 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)); 1401a356273cSpwernau 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 1656*b08923d6SRobert Mustacchi /* Ensure that timestamping is requested on the receive socket */ 1657*b08923d6SRobert Mustacchi if (setsockopt(recv_sock, SOL_SOCKET, SO_TIMESTAMP, 1658*b08923d6SRobert Mustacchi &on, sizeof (on)) == -1) { 1659*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: warning: timing accuracy diminished -- " 1660*b08923d6SRobert Mustacchi "setsockopt SO_TIMESTAMP failed %s", progname, 1661*b08923d6SRobert Mustacchi strerror(errno)); 1662*b08923d6SRobert Mustacchi } 1663*b08923d6SRobert 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 * 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 17387c478bd9Sstevel@tonic-gate sigalrm_handler(void) 17397c478bd9Sstevel@tonic-gate { 17407c478bd9Sstevel@tonic-gate /* 1741*b08923d6SRobert Mustacchi * If we've been told that we're done, the timer should be cancelled 1742*b08923d6SRobert Mustacchi * and not rescheduled, just return. 17437c478bd9Sstevel@tonic-gate */ 1744*b08923d6SRobert Mustacchi if (timer_done == _B_TRUE) 1745*b08923d6SRobert Mustacchi return; 1746*b08923d6SRobert Mustacchi 1747*b08923d6SRobert Mustacchi /* 1748*b08923d6SRobert Mustacchi * Guard against denial-of-service attacks. Make sure ping doesn't send 1749*b08923d6SRobert Mustacchi * probes for every SIGALRM it receives in the case of errant SIGALRMs. 1750*b08923d6SRobert Mustacchi * ping will ignore those which are received too soon (the smaller of 1751*b08923d6SRobert Mustacchi * 0.5 sec and the ping interval, if in effect) after it sent the last 1752*b08923d6SRobert Mustacchi * probe. We use gethrtime() instead of gettimeofday() because the 1753*b08923d6SRobert Mustacchi * latter is not linear and is prone to resetting or drifting. 1754*b08923d6SRobert Mustacchi */ 1755*b08923d6SRobert 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 17667c478bd9Sstevel@tonic-gate schedule_sigalrm(void) 17677c478bd9Sstevel@tonic-gate { 17687c478bd9Sstevel@tonic-gate int waittime; 1769*b08923d6SRobert Mustacchi struct itimerspec it; 17707c478bd9Sstevel@tonic-gate 1771*b08923d6SRobert Mustacchi bzero(&it, sizeof (struct itimerspec)); 17727c478bd9Sstevel@tonic-gate if (npackets == 0 || 17737c478bd9Sstevel@tonic-gate current_targetaddr->num_sent < current_targetaddr->num_probes) { 1774*b08923d6SRobert 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 } 1783*b08923d6SRobert Mustacchi it.it_value.tv_sec = waittime; 1784*b08923d6SRobert Mustacchi } 1785*b08923d6SRobert Mustacchi 1786*b08923d6SRobert Mustacchi if (timer_settime(timer, TIMER_RELTIME, &it, NULL) != 0) { 1787*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: unexpected error updating time: %s\n", 1788*b08923d6SRobert Mustacchi progname, strerror(errno)); 1789*b08923d6SRobert 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 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, 18217c478bd9Sstevel@tonic-gate ¤t_targetaddr->dst_addr, 18227c478bd9Sstevel@tonic-gate 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) { 1845*b08923d6SRobert 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 ¤t_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 19297c478bd9Sstevel@tonic-gate recv_icmp_packet(struct addrinfo *ai_dst, int recv_sock6, int recv_sock, 19307c478bd9Sstevel@tonic-gate 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; 19937c478bd9Sstevel@tonic-gate } 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)) { 2006*b08923d6SRobert 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 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 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 * 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; 2217*b08923d6SRobert Mustacchi mutex_enter(&ns_lock); 2218*b08923d6SRobert Mustacchi ns_active = _B_TRUE; 2219*b08923d6SRobert Mustacchi ns_starttime = gethrtime(); 2220*b08923d6SRobert 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 } 2235*b08923d6SRobert Mustacchi mutex_enter(&ns_lock); 2236*b08923d6SRobert Mustacchi ns_active = _B_FALSE; 2237*b08923d6SRobert 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 * 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 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 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 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 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 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 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 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) { 2499*b08923d6SRobert 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 } 2505*b08923d6SRobert Mustacchi 2506*b08923d6SRobert Mustacchi /* 2507*b08923d6SRobert Mustacchi * Parse the interval into a itimerspec. The interval used to originally be 2508*b08923d6SRobert Mustacchi * parsed as an integer argument. That means that one used to be able to specify 2509*b08923d6SRobert Mustacchi * an interval in hex. The strtod() family honors that at times, with strtod 2510*b08923d6SRobert Mustacchi * sometimes doing so depending on the compilation environment and strtof() and 2511*b08923d6SRobert Mustacchi * srtold() always doing that. To facilitiate that and not worry about a 2512*b08923d6SRobert Mustacchi * careless Makefile change breaking us, we instead just use strtold here, even 2513*b08923d6SRobert Mustacchi * though we really don't need the precision. 2514*b08923d6SRobert Mustacchi */ 2515*b08923d6SRobert Mustacchi static void 2516*b08923d6SRobert Mustacchi parse_interval(char *s) 2517*b08923d6SRobert Mustacchi { 2518*b08923d6SRobert Mustacchi long double val; 2519*b08923d6SRobert Mustacchi char *end; 2520*b08923d6SRobert Mustacchi 2521*b08923d6SRobert Mustacchi errno = 0; 2522*b08923d6SRobert Mustacchi val = strtold(s, &end); 2523*b08923d6SRobert Mustacchi if (errno != 0 || *end != '\0') { 2524*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: bad interval: %s\n", progname, s); 2525*b08923d6SRobert Mustacchi exit(EXIT_FAILURE); 2526*b08923d6SRobert Mustacchi } 2527*b08923d6SRobert Mustacchi 2528*b08923d6SRobert Mustacchi /* 2529*b08923d6SRobert Mustacchi * Check values that we know are going to be bad. Anything greater than 2530*b08923d6SRobert Mustacchi * INT_MAX, anything less than 0, look for specific NaNs. Also, clamp 2531*b08923d6SRobert Mustacchi * the value at 0.01 seconds. 2532*b08923d6SRobert Mustacchi */ 2533*b08923d6SRobert Mustacchi if (val == NAN || val <= 0.0 || val >= INT_MAX) { 2534*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: bad interval: %s\n", progname, s); 2535*b08923d6SRobert Mustacchi exit(EXIT_FAILURE); 2536*b08923d6SRobert Mustacchi } 2537*b08923d6SRobert Mustacchi 2538*b08923d6SRobert Mustacchi if (val < 0.01) { 2539*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: interval too small: %Lf\n", progname, val); 2540*b08923d6SRobert Mustacchi exit(EXIT_FAILURE); 2541*b08923d6SRobert Mustacchi } 2542*b08923d6SRobert Mustacchi 2543*b08923d6SRobert Mustacchi interval.it_value.tv_sec = (long)val; 2544*b08923d6SRobert Mustacchi interval.it_value.tv_nsec = (long)((val - interval.it_value.tv_sec) * 2545*b08923d6SRobert Mustacchi NANOSEC); 2546*b08923d6SRobert Mustacchi 2547*b08923d6SRobert Mustacchi if (interval.it_value.tv_sec == 0 && 2548*b08923d6SRobert Mustacchi interval.it_value.tv_nsec < mintime) { 2549*b08923d6SRobert Mustacchi mintime = interval.it_value.tv_nsec; 2550*b08923d6SRobert Mustacchi } 2551*b08923d6SRobert Mustacchi } 2552*b08923d6SRobert Mustacchi 2553*b08923d6SRobert Mustacchi /* 2554*b08923d6SRobert Mustacchi * We should have an SO_TIMESTAMP message for this socket to indicate 2555*b08923d6SRobert Mustacchi * the actual time that the message took. If we don't we'll fall back to 2556*b08923d6SRobert Mustacchi * gettimeofday(); however, that can cause any delays due to DNS 2557*b08923d6SRobert Mustacchi * resolution and the like to end up wreaking havoc on us. 2558*b08923d6SRobert Mustacchi */ 2559*b08923d6SRobert Mustacchi void 2560*b08923d6SRobert Mustacchi ping_gettime(struct msghdr *msg, struct timeval *tv) 2561*b08923d6SRobert Mustacchi { 2562*b08923d6SRobert Mustacchi struct cmsghdr *cmsg; 2563*b08923d6SRobert Mustacchi 2564*b08923d6SRobert Mustacchi for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; 2565*b08923d6SRobert Mustacchi cmsg = CMSG_NXTHDR(msg, cmsg)) { 2566*b08923d6SRobert Mustacchi if (cmsg->cmsg_level == SOL_SOCKET && 2567*b08923d6SRobert Mustacchi cmsg->cmsg_type == SO_TIMESTAMP && 2568*b08923d6SRobert Mustacchi cmsg->cmsg_len == CMSG_LEN(sizeof (*tv))) { 2569*b08923d6SRobert Mustacchi bcopy(CMSG_DATA(cmsg), tv, sizeof (*tv)); 2570*b08923d6SRobert Mustacchi return; 2571*b08923d6SRobert Mustacchi } 2572*b08923d6SRobert Mustacchi } 2573*b08923d6SRobert Mustacchi 2574*b08923d6SRobert Mustacchi (void) gettimeofday(tv, (struct timezone *)NULL); 2575*b08923d6SRobert Mustacchi } 2576*b08923d6SRobert Mustacchi 2577*b08923d6SRobert Mustacchi /* 2578*b08923d6SRobert Mustacchi * The purpose of this thread is to try and inform a user that we're blocked 2579*b08923d6SRobert Mustacchi * doing name lookups. For various reasons, ping has to try and look up the IP 2580*b08923d6SRobert Mustacchi * addresses it receives via name services unless the -n flag is specified. The 2581*b08923d6SRobert Mustacchi * irony of this is that when trying to use ping to actually diagnose a broken 2582*b08923d6SRobert Mustacchi * network, name services are unlikely to be available and that will result in a 2583*b08923d6SRobert Mustacchi * lot of confusion as to why pings seem like they're not working. As such, we 2584*b08923d6SRobert Mustacchi * basically wake up every 2 seconds and check whether or not we've hit such a 2585*b08923d6SRobert Mustacchi * condition where we should inform the user via stderr. 2586*b08923d6SRobert Mustacchi * 2587*b08923d6SRobert Mustacchi * Once they've been informed, we do not inform them again until approximately a 2588*b08923d6SRobert Mustacchi * minute of time has passed, in case that things are working intermittently. 2589*b08923d6SRobert Mustacchi */ 2590*b08923d6SRobert Mustacchi /*ARGSUSED*/ 2591*b08923d6SRobert Mustacchi static void * 2592*b08923d6SRobert Mustacchi ns_warning_thr(void *unused) 2593*b08923d6SRobert Mustacchi { 2594*b08923d6SRobert Mustacchi hrtime_t last_warn = 0; 2595*b08923d6SRobert Mustacchi for (;;) { 2596*b08923d6SRobert Mustacchi hrtime_t now; 2597*b08923d6SRobert Mustacchi 2598*b08923d6SRobert Mustacchi (void) sleep(ns_sleeptime); 2599*b08923d6SRobert Mustacchi now = gethrtime(); 2600*b08923d6SRobert Mustacchi mutex_enter(&ns_lock); 2601*b08923d6SRobert Mustacchi if (ns_active == _B_TRUE && 2602*b08923d6SRobert Mustacchi now - ns_starttime >= ns_warntime * NANOSEC) { 2603*b08923d6SRobert Mustacchi if (now - last_warn >= 2604*b08923d6SRobert Mustacchi ns_warninter * NANOSEC) { 2605*b08923d6SRobert Mustacchi last_warn = now; 2606*b08923d6SRobert Mustacchi Fprintf(stderr, "%s: warning: ICMP responses " 2607*b08923d6SRobert Mustacchi "received, but name service lookups are " 2608*b08923d6SRobert Mustacchi "taking a while. Use ping -n to disable " 2609*b08923d6SRobert Mustacchi "name service lookups.\n", 2610*b08923d6SRobert Mustacchi progname); 2611*b08923d6SRobert Mustacchi } 2612*b08923d6SRobert Mustacchi } 2613*b08923d6SRobert Mustacchi mutex_exit(&ns_lock); 2614*b08923d6SRobert Mustacchi } 2615*b08923d6SRobert Mustacchi 2616*b08923d6SRobert Mustacchi /* LINTED: E_STMT_NOT_REACHED */ 2617*b08923d6SRobert Mustacchi return (NULL); 2618*b08923d6SRobert Mustacchi } 2619