#include <assert.h>
#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <math.h>
#include <locale.h>
#include <thread.h>
#include <synch.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/stropts.h>
#include <sys/file.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <stdlib.h>
#include <priv_utils.h>
#include <libinetutil.h>
#include "ping.h"
#define PINGSEQ_LEQ(a, b) ((int16_t)((a)-(b)) <= 0)
#define MAX_WAIT 10
#define MAX_TRAFFIC_CLASS 255
#define MAX_FLOW_LABEL 0xFFFFF
#define MAX_TOS 255
#define TIMEOUT 20
#define DEFAULT_DATALEN 56
#define MULTICAST_NOLOOP 1
#define MULTICAST_TTL 2
#define MULTICAST_IF 4
#define IF_INDEX 0
#define IF_NAME 1
#define IF_ADDR 2
#define IF_ADDR6 3
#ifdef BSD
#define setbuf(s, b) setlinebuf((s))
#endif
union if_id {
int index;
char *name;
union any_in_addr addr;
};
struct if_entry {
char *str;
int id_type;
union if_id id;
};
char *progname;
char *targethost;
char *nexthop;
static int send_sock;
static int send_sock6;
static struct sockaddr_in to;
static struct sockaddr_in6 to6;
static union any_in_addr gw_IP_list[MAX_GWS];
static union any_in_addr gw_IP_list6[MAX_GWS6];
static int if_index = 0;
boolean_t is_alive = _B_FALSE;
struct targetaddr *current_targetaddr;
static struct targetaddr *targetaddr_list;
static int num_targetaddrs;
static int num_v4 = 0;
static int num_v6 = 0;
boolean_t verbose = _B_FALSE;
boolean_t stats = _B_FALSE;
static boolean_t settos = _B_FALSE;
boolean_t rr_option = _B_FALSE;
boolean_t send_reply = _B_FALSE;
boolean_t strict = _B_FALSE;
boolean_t ts_option = _B_FALSE;
boolean_t use_icmp_ts = _B_FALSE;
boolean_t use_udp = _B_FALSE;
boolean_t probe_all = _B_FALSE;
boolean_t nflag = _B_FALSE;
boolean_t bypass = _B_FALSE;
static int family_input = AF_UNSPEC;
int datalen = DEFAULT_DATALEN;
int ts_flag;
static int num_gw;
static int eff_num_gw;
static int num_wraps = -1;
static ushort_t dest_port = 32768 + 666;
static char *gw_list[MAXMAX_GWS];
static int options;
static int moptions;
int npackets;
static ushort_t tos;
static int hoplimit = -1;
static int dontfrag;
static int timeout = TIMEOUT;
static struct if_entry out_if;
int ident;
static hrtime_t t_last_probe_sent;
static timer_t timer;
static volatile boolean_t timer_done = _B_FALSE;
static struct itimerspec interval = { { 0, 0 }, { 1, 0 } };
static hrtime_t mintime = NSEC2MSEC(500);
static mutex_t ns_lock = ERRORCHECKMUTEX;
static boolean_t ns_active = _B_FALSE;
static hrtime_t ns_starttime;
static int ns_sleeptime = 2;
static int ns_warntime = 2;
static uint64_t in_pkt[(IP_MAXPACKET + 1)/8];
static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
static int ntransmitted;
int nreceived;
int nreceived_last_target;
long long tmin = LLONG_MAX;
long long tmax;
int64_t tsum;
int64_t tsum2;
static struct targetaddr *build_targetaddr_list(struct addrinfo *,
union any_in_addr *);
static struct targetaddr *create_targetaddr_item(struct sockaddr *,
union any_in_addr *);
static struct ifaddrlist *find_if(struct ifaddrlist *, int);
static void finish();
static void get_gwaddrs(char *[], int, union any_in_addr *,
union any_in_addr *, int *, int *);
static void get_hostinfo(char *, int, struct addrinfo **);
static ushort_t in_cksum(ushort_t *, int);
static int int_arg(char *s, char *what);
static void mirror_gws(union any_in_addr *, int);
static void *ns_warning_thr(void *);
static void parse_interval(const char *s);
static void pinger(int, struct sockaddr *, struct msghdr *, int);
static void print_unknown_host_msg(const char *, const char *);
static void recv_icmp_packet(struct addrinfo *, int, int, ushort_t, ushort_t);
static void resolve_nodes(struct addrinfo **, struct addrinfo **,
union any_in_addr **);
static void select_all_src_addrs(union any_in_addr **, struct addrinfo *,
union any_in_addr *, union any_in_addr *);
static void select_src_addr(union any_in_addr *, int, union any_in_addr *);
static void set_nexthop(int, struct addrinfo *, int);
static boolean_t setup_socket(int, int *, int *, int *, ushort_t *,
struct addrinfo *);
static void usage(char *);
int
main(int argc, char *argv[])
{
struct addrinfo *ai_dst = NULL;
struct addrinfo *ai_nexthop = NULL;
union any_in_addr *src_addr_list = NULL;
int recv_sock = -1;
int recv_sock6 = -1;
ushort_t udp_src_port;
ushort_t udp_src_port6;
uint_t flowinfo = 0;
uint_t class = 0;
char abuf[INET6_ADDRSTRLEN];
int c;
int i;
boolean_t has_sys_ip_config;
progname = argv[0];
(void) setlocale(LC_ALL, "");
has_sys_ip_config = priv_ineffect(PRIV_SYS_IP_CONFIG);
(void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS,
has_sys_ip_config ? PRIV_SYS_IP_CONFIG : (char *)NULL,
(char *)NULL);
setbuf(stdout, (char *)0);
while ((c = getopt(argc, argv,
"abA:c:dDF:G:g:I:i:LlnN:P:p:rRSsTt:UvX:x:Y0123?")) != -1) {
switch ((char)c) {
case 'A':
if (strcmp(optarg, "inet") == 0) {
family_input = AF_INET;
} else if (strcmp(optarg, "inet6") == 0) {
family_input = AF_INET6;
} else {
Fprintf(stderr,
"%s: unknown address family %s\n",
progname, optarg);
exit(EXIT_FAILURE);
}
break;
case 'a':
probe_all = _B_TRUE;
break;
case 'c':
i = int_arg(optarg, "traffic class");
if (i > MAX_TRAFFIC_CLASS) {
Fprintf(stderr, "%s: traffic class %d out of "
"range\n", progname, i);
exit(EXIT_FAILURE);
}
class = (uint_t)i;
break;
case 'd':
options |= SO_DEBUG;
break;
case 'D':
dontfrag = 1;
break;
case 'b':
bypass = _B_TRUE;
break;
case 'F':
i = int_arg(optarg, "flow label");
if (i > MAX_FLOW_LABEL) {
Fprintf(stderr, "%s: flow label %d out of "
"range\n", progname, i);
exit(EXIT_FAILURE);
}
flowinfo = (uint_t)i;
break;
case 'I':
stats = _B_TRUE;
parse_interval(optarg);
break;
case 'i':
moptions |= MULTICAST_IF;
out_if.str = optarg;
if (inet_pton(AF_INET6, optarg, &out_if.id.addr) > 0) {
out_if.id_type = IF_ADDR6;
} else if (inet_pton(AF_INET, optarg,
&out_if.id.addr) > 0) {
out_if.id_type = IF_ADDR;
} else if (strcmp(optarg, "0") == 0) {
out_if.id_type = IF_INDEX;
out_if.id.index = 0;
} else if ((out_if.id.index = atoi(optarg)) != 0) {
out_if.id_type = IF_INDEX;
} else {
out_if.id.name = optarg;
out_if.id_type = IF_NAME;
}
break;
case 'L':
moptions |= MULTICAST_NOLOOP;
break;
case 'l':
send_reply = _B_TRUE;
strict = _B_FALSE;
break;
case 'n':
nflag = _B_TRUE;
break;
case 'P':
settos = _B_TRUE;
i = int_arg(optarg, "type-of-service");
if (i > MAX_TOS) {
Fprintf(stderr, "%s: tos value %d out of "
"range\n", progname, i);
exit(EXIT_FAILURE);
}
tos = (ushort_t)i;
break;
case 'p':
i = int_arg(optarg, "port number");
if (i > MAX_PORT) {
Fprintf(stderr, "%s: port number %d out of "
"range\n", progname, i);
exit(EXIT_FAILURE);
}
dest_port = (ushort_t)i;
break;
case 'r':
options |= SO_DONTROUTE;
break;
case 'R':
rr_option = _B_TRUE;
break;
case 'S':
send_reply = _B_TRUE;
strict = _B_TRUE;
break;
case 's':
stats = _B_TRUE;
break;
case 'T':
ts_option = _B_TRUE;
break;
case 't':
moptions |= MULTICAST_TTL;
hoplimit = int_arg(optarg, "ttl");
if (hoplimit > MAXTTL) {
Fprintf(stderr, "%s: ttl %d out of range\n",
progname, hoplimit);
exit(EXIT_FAILURE);
}
break;
case 'U':
use_udp = _B_TRUE;
use_icmp_ts = _B_FALSE;
break;
case 'v':
verbose = _B_TRUE;
break;
case 'x':
case 'g':
strict = _B_FALSE;
if (num_gw > MAXMAX_GWS) {
Fprintf(stderr, "%s: too many gateways\n",
progname);
exit(EXIT_FAILURE);
}
gw_list[num_gw++] = optarg;
break;
case 'X':
case 'G':
strict = _B_TRUE;
if (num_gw > MAXMAX_GWS) {
Fprintf(stderr, "%s: too many gateways\n",
progname);
exit(EXIT_FAILURE);
}
gw_list[num_gw++] = optarg;
break;
case 'N':
if (nexthop != NULL) {
Fprintf(stderr, "%s: only one next hop gateway"
" allowed\n", progname);
exit(EXIT_FAILURE);
}
nexthop = optarg;
break;
case 'Y':
use_icmp_ts = _B_TRUE;
use_udp = _B_FALSE;
break;
case '0':
case '1':
case '2':
case '3':
ts_flag = (char)c - '0';
break;
case '?':
usage(progname);
exit(EXIT_FAILURE);
break;
default:
usage(progname);
exit(EXIT_FAILURE);
break;
}
}
if (optind >= argc) {
usage(progname);
exit(EXIT_FAILURE);
}
if (use_udp)
send_reply = _B_FALSE;
if (getenv("MACHINE_THAT_GOES_PING") != NULL)
stats = _B_TRUE;
targethost = argv[optind];
optind++;
if (optind < argc) {
if (stats) {
datalen = int_arg(argv[optind], "data size");
optind++;
if (optind < argc) {
npackets = int_arg(argv[optind],
"packet count");
if (npackets < 1) {
Fprintf(stderr, "%s: packet count %d "
"out of range\n", progname,
npackets);
exit(EXIT_FAILURE);
}
}
} else {
timeout = int_arg(argv[optind], "timeout");
}
}
bzero((char *)&to, sizeof (struct sockaddr_in));
to.sin_family = AF_INET;
bzero((char *)&to6, sizeof (struct sockaddr_in6));
to6.sin6_family = AF_INET6;
to6.sin6_flowinfo = htonl((class << 20) | flowinfo);
if (stats)
(void) sigset(SIGINT, finish);
ident = (int)getpid() & 0xFFFF;
resolve_nodes(&ai_dst, &ai_nexthop, &src_addr_list);
if (family_input == AF_INET6 ||
(family_input == AF_UNSPEC && num_v6 != 0)) {
size_t exthdr_len = 0;
if (send_reply) {
exthdr_len = sizeof (struct ip6_rthdr0) +
2 * num_gw * sizeof (struct in6_addr);
} else if (num_gw > 0) {
exthdr_len = sizeof (struct ip6_rthdr0) +
num_gw * sizeof (struct in6_addr);
}
if (datalen > (IP_MAXPACKET - (sizeof (struct ip6_hdr) +
exthdr_len + ICMP6_MINLEN))) {
Fprintf(stderr,
"%s: data size too large for IPv6 packet\n",
progname);
num_v6 = 0;
}
}
if (family_input == AF_INET ||
(family_input == AF_UNSPEC && num_v4 != 0)) {
size_t opt_len = 0;
if (send_reply) {
opt_len = 3 +
(2 * num_gw + 2) * sizeof (struct in_addr);
} else if (num_gw > 0) {
opt_len = 3 + (num_gw + 1) * sizeof (struct in_addr);
}
if (rr_option) {
opt_len = MAX_IPOPTLEN;
} else if (ts_option) {
if ((ts_flag & 0x0f) <= IPOPT_TS_TSANDADDR) {
opt_len = MAX_IPOPTLEN;
} else {
opt_len += IPOPT_MINOFF +
2 * sizeof (struct ipt_ta);
opt_len++;
}
}
if (opt_len & 0x3)
opt_len = (opt_len & ~0x3) + 4;
if (datalen > (IP_MAXPACKET - (sizeof (struct ip) + opt_len +
ICMP_MINLEN))) {
Fprintf(stderr,
"%s: data size too large for IPv4 packet\n",
progname);
num_v4 = 0;
}
}
if (num_v4 == 0 && num_v6 == 0) {
exit(EXIT_FAILURE);
}
if (num_v6 != 0) {
if (!setup_socket(AF_INET6, &send_sock6, &recv_sock6,
&if_index, &udp_src_port6, ai_nexthop))
exit(EXIT_FAILURE);
}
if (num_v4 != 0) {
if (!setup_socket(AF_INET, &send_sock, &recv_sock, &if_index,
&udp_src_port, ai_nexthop))
exit(EXIT_FAILURE);
}
__priv_relinquish();
if (send_reply) {
if (num_v6 != 0)
mirror_gws(gw_IP_list6, AF_INET6);
if (num_v4 != 0)
mirror_gws(gw_IP_list, AF_INET);
eff_num_gw = 2 * num_gw + 1;
} else {
eff_num_gw = num_gw;
}
targetaddr_list = build_targetaddr_list(ai_dst, src_addr_list);
current_targetaddr = targetaddr_list;
current_targetaddr->starting_seq_num = use_udp ? dest_port : 0;
if (stats) {
if (probe_all || !nflag) {
Printf("PING %s: %d data bytes\n", targethost, datalen);
} else {
if (ai_dst->ai_family == AF_INET) {
(void) inet_ntop(AF_INET,
&((struct sockaddr_in *)(void *)
ai_dst->ai_addr)->sin_addr,
abuf, sizeof (abuf));
} else {
(void) inet_ntop(AF_INET6,
&((struct sockaddr_in6 *)(void *)
ai_dst->ai_addr)->sin6_addr,
abuf, sizeof (abuf));
}
Printf("PING %s (%s): %d data bytes\n",
targethost, abuf, datalen);
}
}
if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) {
Fprintf(stderr, "%s: failed to create timer: %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
if (thr_create(NULL, 0, ns_warning_thr, NULL,
THR_DETACHED | THR_DAEMON, NULL) != 0) {
Fprintf(stderr, "%s: failed to create name services "
"thread: %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
send_scheduled_probe();
(void) sigset(SIGALRM, sigalrm_handler);
schedule_sigalrm();
recv_icmp_packet(ai_dst, recv_sock6, recv_sock, udp_src_port6,
udp_src_port);
return (EXIT_SUCCESS);
}
static struct targetaddr *
build_targetaddr_list(struct addrinfo *ai_dst, union any_in_addr *src_addr_list)
{
struct targetaddr *head = NULL;
struct targetaddr *targetaddr;
struct targetaddr **nextp;
int num_dst;
int i;
struct addrinfo *aip;
aip = ai_dst;
if (probe_all)
num_dst = num_v4 + num_v6;
else
num_dst = 1;
num_targetaddrs = num_dst;
nextp = &head;
for (aip = ai_dst, i = 0; aip != NULL; aip = aip->ai_next, i++) {
if (aip->ai_family == AF_INET && num_v4 != 0) {
targetaddr = create_targetaddr_item(aip->ai_addr,
&src_addr_list[i]);
} else if (aip->ai_family == AF_INET6 && num_v6 != 0) {
targetaddr = create_targetaddr_item(aip->ai_addr,
&src_addr_list[i]);
} else {
continue;
}
*nextp = targetaddr;
nextp = &targetaddr->next;
if (num_targetaddrs == 1)
break;
}
if (npackets == 0 && stats)
*nextp = head;
return (head);
}
static struct targetaddr *
create_targetaddr_item(struct sockaddr *dst_addr, union any_in_addr *src_addr)
{
struct targetaddr *targetaddr;
struct sockaddr_in *dst4 = (struct sockaddr_in *)dst_addr;
struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst_addr;
targetaddr = (struct targetaddr *)malloc(sizeof (struct targetaddr));
if (targetaddr == NULL) {
Fprintf(stderr, "%s: malloc %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
targetaddr->family = dst_addr->sa_family;
switch (dst_addr->sa_family) {
case AF_INET:
memset(&targetaddr->dst_addr, 0, sizeof (targetaddr->dst_addr));
targetaddr->dst_addr.addr = dst4->sin_addr;
targetaddr->dst_scope = 0;
break;
case AF_INET6:
memset(&targetaddr->dst_addr, 0, sizeof (targetaddr->dst_addr));
targetaddr->dst_addr.addr6 = dst6->sin6_addr;
targetaddr->dst_scope = dst6->sin6_scope_id;
if (if_index != 0 && targetaddr->dst_scope != 0 &&
if_index != targetaddr->dst_scope) {
Fprintf(stderr, "%s: warning: conflicting scopes; using"
" %s\n", progname, pr_if(targetaddr->dst_scope));
}
break;
}
targetaddr->src_addr = *src_addr;
if (stats) {
if (npackets > 0)
targetaddr->num_probes = npackets;
else
targetaddr->num_probes = 1;
} else {
targetaddr->num_probes = timeout;
}
targetaddr->num_sent = 0;
targetaddr->got_reply = _B_FALSE;
targetaddr->probing_done = _B_FALSE;
targetaddr->starting_seq_num = 0;
targetaddr->next = NULL;
return (targetaddr);
}
static void
print_unknown_host_msg(const char *protocol, const char *hostname)
{
Fprintf(stderr, "%s: unknown%s host %s\n", progname, protocol,
hostname);
}
static void
resolve_nodes(struct addrinfo **ai_dstp, struct addrinfo **ai_nexthopp,
union any_in_addr **src_addr_listp)
{
struct addrinfo *ai_dst = NULL;
struct addrinfo *ai_nexthop = NULL;
struct addrinfo *aip = NULL;
union any_in_addr *src_addr_list = NULL;
int num_resolved_gw = 0;
int num_resolved_gw6 = 0;
get_hostinfo(targethost, family_input, &ai_dst);
if (ai_dst == NULL) {
print_unknown_host_msg("", targethost);
exit(EXIT_FAILURE);
}
if (nexthop != NULL) {
get_hostinfo(nexthop, family_input, &ai_nexthop);
if (ai_nexthop == NULL) {
print_unknown_host_msg("", nexthop);
exit(EXIT_FAILURE);
}
}
for (aip = ai_dst; aip != NULL; aip = aip->ai_next) {
switch (aip->ai_family) {
case AF_INET:
num_v4++;
break;
case AF_INET6:
num_v6++;
break;
}
}
if (family_input == AF_UNSPEC && !probe_all) {
family_input = ai_dst->ai_family;
}
if (num_gw > 0) {
get_gwaddrs(gw_list, family_input, gw_IP_list, gw_IP_list6,
&num_resolved_gw, &num_resolved_gw6);
if (num_resolved_gw6 != num_gw && num_v6 != 0 &&
(family_input == AF_INET6 || family_input == AF_UNSPEC)) {
print_unknown_host_msg(" IPv6",
gw_list[num_resolved_gw6]);
num_v6 = 0;
}
if (num_resolved_gw != num_gw && num_v4 != 0 &&
(family_input == AF_INET || family_input == AF_UNSPEC)) {
print_unknown_host_msg(" IPv4",
gw_list[num_resolved_gw]);
num_v4 = 0;
}
}
if (num_v4 == 0 && num_v6 == 0)
exit(EXIT_FAILURE);
select_all_src_addrs(&src_addr_list, ai_dst, gw_IP_list, gw_IP_list6);
*ai_dstp = ai_dst;
*ai_nexthopp = ai_nexthop;
*src_addr_listp = src_addr_list;
}
static void
get_gwaddrs(char **gw_list, int family, union any_in_addr *gwIPlist,
union any_in_addr *gwIPlist6, int *resolved, int *resolved6)
{
int i;
boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE;
struct addrinfo *ai = NULL;
struct addrinfo *aip = NULL;
*resolved = *resolved6 = 0;
switch (family) {
case AF_UNSPEC:
break;
case AF_INET:
check_v6 = _B_FALSE;
break;
case AF_INET6:
check_v4 = _B_FALSE;
break;
default:
return;
}
if (check_v4 && num_gw >= MAX_GWS) {
check_v4 = _B_FALSE;
Fprintf(stderr, "%s: too many IPv4 gateways\n", progname);
}
if (check_v6 && num_gw > MAX_GWS6) {
check_v6 = _B_FALSE;
Fprintf(stderr, "%s: too many IPv6 gateways\n", progname);
}
for (i = 0; i < num_gw; i++) {
if (!check_v4 && !check_v6)
return;
get_hostinfo(gw_list[i], family, &ai);
if (ai == NULL)
return;
if (check_v4 && num_v4 != 0) {
for (aip = ai; aip != NULL; aip = aip->ai_next) {
if (aip->ai_family == AF_INET) {
bcopy(&((struct sockaddr_in *)
aip->ai_addr)->sin_addr,
&gwIPlist[i].addr,
aip->ai_addrlen);
(*resolved)++;
break;
}
}
} else if (check_v4) {
check_v4 = _B_FALSE;
}
if (check_v6 && num_v6 != 0) {
for (aip = ai; aip != NULL; aip = aip->ai_next) {
if (aip->ai_family == AF_INET6) {
bcopy(&((struct sockaddr_in6 *)
aip->ai_addr)->sin6_addr,
&gwIPlist6[i].addr6,
aip->ai_addrlen);
(*resolved6)++;
break;
}
}
} else if (check_v6) {
check_v6 = _B_FALSE;
}
}
freeaddrinfo(ai);
}
static void
mirror_gws(union any_in_addr *gwIPlist, int family)
{
int effective_num_gw;
int i;
effective_num_gw = 2 * num_gw + 1;
if ((family == AF_INET && effective_num_gw >= MAX_GWS) ||
(family == AF_INET6 && effective_num_gw > MAX_GWS6)) {
Fprintf(stderr, "%s: too many %s gateways\n",
progname, (family == AF_INET) ? "IPv4" : "IPv6");
exit(EXIT_FAILURE);
}
for (i = 0; i < num_gw; i++)
gwIPlist[num_gw + i + 1].addr6 = gwIPlist[num_gw - i - 1].addr6;
}
static void
get_hostinfo(char *host, int family, struct addrinfo **aipp)
{
struct addrinfo hints, *ai;
struct in6_addr addr6;
struct in_addr addr;
boolean_t broadcast;
char tmp_buf[INET6_ADDRSTRLEN];
int rc;
if (strcmp(host, "255.255.255.255") == 0)
broadcast = _B_TRUE;
else
broadcast = _B_FALSE;
if (((inet_pton(AF_INET6, host, &addr6) > 0) &&
IN6_IS_ADDR_V4MAPPED(&addr6)) || broadcast) {
if (!broadcast) {
IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
(void) inet_ntop(AF_INET, (void *)&addr, tmp_buf,
sizeof (tmp_buf));
(void) strcpy(host, tmp_buf);
}
if (family == AF_INET6) {
return;
}
}
(void) memset(&hints, 0, sizeof (hints));
hints.ai_family = family;
hints.ai_flags = AI_ADDRCONFIG;
rc = getaddrinfo(host, NULL, &hints, &ai);
if (rc != 0) {
if (rc != EAI_NONAME)
Fprintf(stderr, "%s: getaddrinfo: %s\n", progname,
gai_strerror(rc));
return;
}
*aipp = ai;
}
static void
select_all_src_addrs(union any_in_addr **src_addr_list, struct addrinfo *ai,
union any_in_addr *gwv4, union any_in_addr *gwv6)
{
union any_in_addr *list;
struct addrinfo *aip;
int num_dst = 1;
int i;
if (probe_all) {
for (aip = ai; aip->ai_next != NULL; aip = aip->ai_next)
num_dst++;
}
list = calloc((size_t)num_dst, sizeof (union any_in_addr));
if (list == NULL) {
Fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
if (num_gw > 0) {
if (ai->ai_family == AF_INET)
select_src_addr(gwv4, ai->ai_family, &list[0]);
else
select_src_addr(gwv6, ai->ai_family, &list[0]);
for (i = 1; i < num_dst; i++)
list[i] = list[0];
} else {
for (i = 0, aip = ai; i < num_dst && aip != NULL;
i++, aip = aip->ai_next) {
if (aip->ai_family == AF_INET) {
if (num_v4 != 0) {
select_src_addr((union any_in_addr *)
&((struct sockaddr_in *)
aip->ai_addr)->sin_addr,
aip->ai_family,
&list[i]);
}
} else {
if (num_v6 != 0) {
select_src_addr((union any_in_addr *)
&((struct sockaddr_in6 *)
aip->ai_addr)->sin6_addr,
aip->ai_family,
&list[i]);
}
}
}
}
*src_addr_list = list;
}
static void
select_src_addr(union any_in_addr *dst_addr, int family,
union any_in_addr *src_addr)
{
struct sockaddr *sock;
struct sockaddr_in *sin = NULL;
struct sockaddr_in6 *sin6 = NULL;
int tmp_fd;
size_t sock_len;
sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6));
if (sock == NULL) {
Fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
(void) bzero(sock, sizeof (struct sockaddr_in6));
if (family == AF_INET) {
sin = (struct sockaddr_in *)sock;
sin->sin_family = AF_INET;
sin->sin_addr = dst_addr->addr;
sin->sin_port = IPPORT_ECHO;
sock_len = sizeof (struct sockaddr_in);
} else {
sin6 = (struct sockaddr_in6 *)sock;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = dst_addr->addr6;
sin6->sin6_port = IPPORT_ECHO;
sock_len = sizeof (struct sockaddr_in6);
}
if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) {
Fprintf(stderr, "%s: udp socket: %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (connect(tmp_fd, sock, sock_len) < 0) {
if (family == AF_INET)
src_addr->addr.s_addr = INADDR_ANY;
else
src_addr->addr6 = in6addr_any;
free(sock);
return;
}
if (getsockname(tmp_fd, sock, &sock_len) < 0) {
Fprintf(stderr, "%s: getsockname: %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (family == AF_INET) {
assert(sin != NULL);
src_addr->addr = sin->sin_addr;
} else {
assert(sin6 != NULL);
src_addr->addr6 = sin6->sin6_addr;
}
(void) close(tmp_fd);
free(sock);
}
static void
set_nexthop(int family, struct addrinfo *ai_nexthop, int sock)
{
if (family == AF_INET) {
ipaddr_t nh;
nh = ((struct sockaddr_in *)ai_nexthop->
ai_addr)->sin_addr.s_addr;
(void) __priv_bracket(PRIV_ON);
if (setsockopt(sock, IPPROTO_IP, IP_NEXTHOP,
&nh, sizeof (ipaddr_t)) < 0) {
if (errno == EPERM)
Fprintf(stderr, "%s: Insufficient privilege "
"to specify IPv4 nexthop router.\n",
progname);
else
Fprintf(stderr, "%s: setsockopt %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
(void) __priv_bracket(PRIV_OFF);
} else {
struct sockaddr_in6 *nh;
nh = (struct sockaddr_in6 *)ai_nexthop->
ai_addr;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_NEXTHOP,
nh, sizeof (struct sockaddr_in6)) < 0) {
Fprintf(stderr, "%s: setsockopt %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
static boolean_t
setup_socket(int family, int *send_sockp, int *recv_sockp, int *if_index,
ushort_t *udp_src_port, struct addrinfo *ai_nexthop)
{
int send_sock;
int recv_sock;
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
struct sockaddr *sp;
struct ipsec_req req;
size_t slen;
int on = 1;
uchar_t char_op;
int int_op;
(void) __priv_bracket(PRIV_ON);
recv_sock = socket(family, SOCK_RAW,
(family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
if (recv_sock < 0) {
Fprintf(stderr, "%s: socket %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
(void) __priv_bracket(PRIV_OFF);
if (bypass) {
(void) memset(&req, 0, sizeof (req));
req.ipsr_ah_req = IPSEC_PREF_NEVER;
req.ipsr_esp_req = IPSEC_PREF_NEVER;
if (setsockopt(recv_sock, (family == AF_INET) ? IPPROTO_IP :
IPPROTO_IPV6, IP_SEC_OPT, &req, sizeof (req)) < 0) {
switch (errno) {
case EPROTONOSUPPORT:
break;
case EPERM:
Fprintf(stderr, "%s: Insufficient privilege "
"to bypass IPsec policy.\n", progname);
exit(EXIT_FAILURE);
break;
default:
Fprintf(stderr, "%s: setsockopt %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
break;
}
}
}
if (use_udp) {
send_sock = socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (send_sock < 0) {
Fprintf(stderr, "%s: socket %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (bypass) {
if (setsockopt(send_sock, (family == AF_INET) ?
IPPROTO_IP : IPPROTO_IPV6, IP_SEC_OPT, &req,
sizeof (req)) < 0) {
switch (errno) {
case EPROTONOSUPPORT:
break;
case EPERM:
Fprintf(stderr, "%s: Insufficient "
"privilege to bypass IPsec "
"policy.\n", progname);
exit(EXIT_FAILURE);
break;
default:
Fprintf(stderr, "%s: setsockopt %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
break;
}
}
}
if (family == AF_INET) {
sp = (struct sockaddr *)&sin;
slen = sizeof (sin);
} else {
sp = (struct sockaddr *)&sin6;
slen = sizeof (sin6);
}
bzero(sp, slen);
sp->sa_family = family;
if (bind(send_sock, sp, slen) < 0) {
Fprintf(stderr, "%s: bind %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (getsockname(send_sock, sp, &slen) < 0) {
Fprintf(stderr, "%s: getsockname %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
*udp_src_port = (family == AF_INET) ? sin.sin_port :
sin6.sin6_port;
} else {
send_sock = recv_sock;
}
if (nexthop != NULL)
set_nexthop(family, ai_nexthop, send_sock);
int_op = 48 * 1024;
if (int_op < datalen)
int_op = datalen;
if (setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, (char *)&int_op,
sizeof (int_op)) == -1) {
Fprintf(stderr, "%s: setsockopt SO_RCVBUF %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, (char *)&int_op,
sizeof (int_op)) == -1) {
Fprintf(stderr, "%s: setsockopt SO_SNDBUF %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (options & SO_DEBUG) {
if (setsockopt(send_sock, SOL_SOCKET, SO_DEBUG, (char *)&on,
sizeof (on)) == -1) {
Fprintf(stderr, "%s: setsockopt SO_DEBUG %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (options & SO_DONTROUTE) {
if (setsockopt(send_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
sizeof (on)) == -1) {
Fprintf(stderr, "%s: setsockopt SO_DONTROUTE %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (moptions & MULTICAST_NOLOOP) {
if (family == AF_INET) {
char_op = 0;
if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&char_op, sizeof (char_op)) == -1) {
Fprintf(stderr, "%s: setsockopt "
"IP_MULTICAST_NOLOOP %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
} else {
int_op = 0;
if (setsockopt(send_sock, IPPROTO_IPV6,
IPV6_MULTICAST_LOOP, (char *)&int_op,
sizeof (int_op)) == -1) {
Fprintf(stderr, "%s: setsockopt "
"IPV6_MULTICAST_NOLOOP %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
}
}
if (moptions & MULTICAST_TTL) {
char_op = hoplimit;
if (family == AF_INET) {
if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
(char *)&char_op, sizeof (char)) == -1) {
Fprintf(stderr, "%s: setsockopt "
"IP_MULTICAST_TTL %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
if (setsockopt(send_sock, IPPROTO_IP, IP_TTL,
(char *)&hoplimit, sizeof (hoplimit)) == -1) {
Fprintf(stderr, "%s: setsockopt IP_TTL %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
if (moptions & MULTICAST_IF) {
struct ifaddrlist *al = NULL;
struct ifaddrlist *my_if;
char errbuf[ERRBUFSIZE];
int num_ifs;
int num_src_ifs;
int i;
num_ifs = ifaddrlist(&al, family, LIFC_UNDER_IPMP, errbuf);
if (num_ifs == -1) {
Fprintf(stderr, "%s: %s\n", progname, errbuf);
exit(EXIT_FAILURE);
}
num_src_ifs = 0;
for (i = 0; i < num_ifs; i++) {
if (!(al[i].flags & IFF_LOOPBACK) &&
(al[i].flags & IFF_UP))
num_src_ifs++;
}
if (num_src_ifs == 0) {
Fprintf(stderr, "%s: can't find any %s interface\n",
progname, (family == AF_INET) ? "IPv4" : "IPv6");
return (_B_FALSE);
}
my_if = find_if(al, num_ifs);
if (my_if == NULL) {
Fprintf(stderr, "%s: %s is an invalid %s interface\n",
progname, out_if.str,
(family == AF_INET) ? "IPv4" : "IPv6");
return (_B_FALSE);
}
if (family == AF_INET) {
struct in_pktinfo pktinfo;
if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&my_if->addr.addr,
sizeof (struct in_addr)) == -1) {
Fprintf(stderr, "%s: setsockopt "
"IP_MULTICAST_IF %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
bzero(&pktinfo, sizeof (pktinfo));
pktinfo.ipi_ifindex = my_if->index;
if (setsockopt(send_sock, IPPROTO_IP, IP_PKTINFO,
(char *)&pktinfo, sizeof (pktinfo)) == -1) {
Fprintf(stderr, "%s: setsockopt "
"IP_PKTINFO %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
} else {
*if_index = my_if->index;
}
free(al);
}
if (settos && family == AF_INET) {
int_op = tos;
if (setsockopt(send_sock, IPPROTO_IP, IP_TOS, (char *)&int_op,
sizeof (int_op)) == -1) {
Fprintf(stderr, "%s: setsockopt IP_TOS %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (family == AF_INET) {
if (setsockopt(send_sock, IPPROTO_IP, IP_DONTFRAG,
(char *)&dontfrag, sizeof (dontfrag)) == -1) {
Fprintf(stderr, "%s: setsockopt IP_DONTFRAG %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
} else {
if (setsockopt(send_sock, IPPROTO_IPV6, IPV6_DONTFRAG,
(char *)&dontfrag, sizeof (dontfrag)) == -1) {
Fprintf(stderr, "%s: setsockopt IPV6_DONTFRAG %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (verbose && family == AF_INET6) {
if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS,
(char *)&on, sizeof (on)) == -1) {
Fprintf(stderr, "%s: setsockopt IPV6_RECVHOPOPTS %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS,
(char *)&on, sizeof (on)) == -1) {
Fprintf(stderr, "%s: setsockopt IPV6_RECVDSTOPTS %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVRTHDR,
(char *)&on, sizeof (on)) == -1) {
Fprintf(stderr, "%s: setsockopt IPV6_RECVRTHDR %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
if (setsockopt(recv_sock, SOL_SOCKET, SO_TIMESTAMP,
&on, sizeof (on)) == -1) {
Fprintf(stderr, "%s: warning: timing accuracy diminished -- "
"setsockopt SO_TIMESTAMP failed %s", progname,
strerror(errno));
}
*send_sockp = send_sock;
*recv_sockp = recv_sock;
return (_B_TRUE);
}
static struct ifaddrlist *
find_if(struct ifaddrlist *al, int num_ifs)
{
static struct ifaddrlist tmp_if;
boolean_t found;
int i;
i = 0;
found = _B_FALSE;
while (i < num_ifs && !found) {
tmp_if = al[i];
if ((tmp_if.flags & IFF_LOOPBACK) || !(tmp_if.flags & IFF_UP)) {
i++;
continue;
}
switch (out_if.id_type) {
case IF_INDEX:
if (out_if.id.index == tmp_if.index)
found = _B_TRUE;
break;
case IF_NAME:
if (strcmp(out_if.id.name, tmp_if.device) == 0)
found = _B_TRUE;
break;
case IF_ADDR:
if (out_if.id.addr.addr.s_addr ==
tmp_if.addr.addr.s_addr) {
found = _B_TRUE;
}
break;
case IF_ADDR6:
if (IN6_ARE_ADDR_EQUAL(&out_if.id.addr.addr6,
&tmp_if.addr.addr6)) {
found = _B_TRUE;
}
break;
default:
break;
}
i++;
}
if (found)
return (&tmp_if);
else
return (NULL);
}
void
sigalrm_handler(void)
{
if (timer_done == _B_TRUE)
return;
if ((gethrtime() - t_last_probe_sent) < mintime) {
return;
}
send_scheduled_probe();
schedule_sigalrm();
}
void
schedule_sigalrm(void)
{
int waittime;
struct itimerspec it;
bzero(&it, sizeof (struct itimerspec));
if (npackets == 0 ||
current_targetaddr->num_sent < current_targetaddr->num_probes) {
it = interval;
} else {
if (current_targetaddr->got_reply) {
waittime = 2 * tmax / MICROSEC;
if (waittime == 0)
waittime = 1;
} else {
waittime = MAX_WAIT;
}
it.it_value.tv_sec = waittime;
}
if (timer_settime(timer, TIMER_RELTIME, &it, NULL) != 0) {
Fprintf(stderr, "%s: unexpected error updating time: %s\n",
progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
void
send_scheduled_probe()
{
static struct msghdr msg6;
static boolean_t first_probe = _B_TRUE;
char tmp_buf[INET6_ADDRSTRLEN];
if (current_targetaddr->num_sent == current_targetaddr->num_probes ||
current_targetaddr->probing_done) {
if (!stats && !current_targetaddr->got_reply) {
if (!probe_all) {
Printf("no answer from %s\n", targethost);
} else {
Printf("no answer from %s(%s)\n", targethost,
inet_ntop(current_targetaddr->family,
¤t_targetaddr->dst_addr,
tmp_buf, sizeof (tmp_buf)));
}
}
current_targetaddr->got_reply = _B_FALSE;
current_targetaddr->probing_done = _B_FALSE;
if (stats || !probe_all)
current_targetaddr->num_sent = 0;
nreceived_last_target = 0;
current_targetaddr = current_targetaddr->next;
if (current_targetaddr == NULL) {
timer_done = _B_TRUE;
if (stats)
finish();
if (is_alive)
exit(EXIT_SUCCESS);
else
exit(EXIT_FAILURE);
} else {
current_targetaddr->starting_seq_num = use_udp ?
dest_port : (ntransmitted % (MAX_ICMP_SEQ + 1));
}
}
if (current_targetaddr->family == AF_INET6) {
if (send_reply) {
to6.sin6_addr = current_targetaddr->src_addr.addr6;
} else {
to6.sin6_addr = current_targetaddr->dst_addr.addr6;
}
to6.sin6_scope_id = current_targetaddr->dst_scope;
if (first_probe ||
(send_reply && current_targetaddr->num_sent == 0)) {
int scope = if_index;
if (scope != 0 && to6.sin6_scope_id != 0)
scope = 0;
if (send_reply) {
gw_IP_list6[num_gw].addr6 =
current_targetaddr->dst_addr.addr6;
}
set_ancillary_data(&msg6, hoplimit, gw_IP_list6,
eff_num_gw, scope);
first_probe = _B_FALSE;
}
pinger(send_sock6, (struct sockaddr *)&to6, &msg6, AF_INET6);
} else {
to.sin_addr = current_targetaddr->dst_addr.addr;
if (current_targetaddr->num_sent == 0) {
if (eff_num_gw > 0) {
gw_IP_list[num_gw].addr =
current_targetaddr->dst_addr.addr;
if (send_reply) {
gw_IP_list[eff_num_gw].addr =
current_targetaddr->src_addr.addr;
}
}
set_IPv4_options(send_sock, gw_IP_list,
(eff_num_gw > 0) ? eff_num_gw + 1 : 0,
¤t_targetaddr->src_addr.addr, &to.sin_addr);
}
pinger(send_sock, (struct sockaddr *)&to, NULL, AF_INET);
}
current_targetaddr->num_sent++;
}
static void
recv_icmp_packet(struct addrinfo *ai_dst, int recv_sock6, int recv_sock,
ushort_t udp_src_port6, ushort_t udp_src_port)
{
struct msghdr in_msg;
struct iovec iov;
struct sockaddr_in6 from6;
fd_set fds;
int result;
int cc;
boolean_t always_true = _B_TRUE;
while (always_true) {
(void) FD_ZERO(&fds);
if (recv_sock6 != -1)
FD_SET(recv_sock6, &fds);
if (recv_sock != -1)
FD_SET(recv_sock, &fds);
result = select(MAX(recv_sock6, recv_sock) + 1, &fds,
(fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL);
if (result == -1) {
if (errno == EINTR) {
continue;
} else {
Fprintf(stderr, "%s: select %s\n", progname,
strerror(errno));
exit(EXIT_FAILURE);
}
} else if (result > 0) {
in_msg.msg_name = &from6;
in_msg.msg_namelen = sizeof (from6);
iov.iov_base = in_pkt;
iov.iov_len = sizeof (in_pkt);
in_msg.msg_iov = &iov;
in_msg.msg_iovlen = 1;
in_msg.msg_control = ancillary_data;
in_msg.msg_controllen = sizeof (ancillary_data);
if ((recv_sock6 != -1) &&
(FD_ISSET(recv_sock6, &fds))) {
cc = recvmsg(recv_sock6, &in_msg, 0);
if (cc < 0) {
if (errno != EINTR) {
Fprintf(stderr,
"%s: recvmsg %s\n",
progname, strerror(errno));
}
continue;
} else if (cc > 0) {
check_reply6(ai_dst, &in_msg, cc,
udp_src_port6);
}
}
if ((recv_sock != -1) && (FD_ISSET(recv_sock, &fds))) {
cc = recvmsg(recv_sock, &in_msg, 0);
if (cc < 0) {
if (errno != EINTR) {
Fprintf(stderr,
"%s: recvmsg %s\n",
progname, strerror(errno));
}
continue;
} else if (cc > 0) {
check_reply(ai_dst, &in_msg, cc,
udp_src_port);
}
}
}
if ((npackets > 0) && (current_targetaddr->next == NULL) &&
(nreceived_last_target == npackets)) {
timer_done = _B_TRUE;
finish();
}
}
}
boolean_t
is_a_target(struct addrinfo *ai, union any_in_addr *addr)
{
int num_addrs;
int i;
struct addrinfo *aip;
aip = ai;
if (probe_all)
num_addrs = num_v4 + num_v6;
else
num_addrs = 1;
for (i = 0; i < num_addrs && aip != NULL; i++) {
if (aip->ai_family == AF_INET6) {
if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)
aip->ai_addr)->sin6_addr, &addr->addr6))
return (_B_TRUE);
} else {
if (((struct sockaddr_in *)
aip->ai_addr)->sin_addr.s_addr == addr->addr.s_addr)
return (_B_TRUE);
}
}
return (_B_FALSE);
}
static void
pinger(int send_sock, struct sockaddr *whereto, struct msghdr *msg6,
int family)
{
static uint64_t out_pkt_buf[(IP_MAXPACKET + 1) / 8];
uchar_t *out_pkt = (uchar_t *)&out_pkt_buf;
struct icmp *icp = (struct icmp *)out_pkt;
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)whereto;
struct sockaddr_in *to = (struct sockaddr_in *)whereto;
struct timeval *tp;
struct timeval t_snd;
uchar_t *datap;
struct iovec iov;
int start = 0;
int cc;
int i;
if (use_udp) {
cc = datalen;
tp = (struct timeval *)out_pkt;
datap = &out_pkt[sizeof (struct timeval)];
to->sin_port = htons(dest_port);
dest_port = (dest_port + 1) % (MAX_PORT + 1);
ntransmitted++;
} else {
cc = datalen + ICMP_MINLEN;
if (family == AF_INET6) {
icp->icmp_type = send_reply ?
ICMP6_ECHO_REPLY : ICMP6_ECHO_REQUEST;
} else if (use_icmp_ts) {
icp->icmp_type = send_reply ?
ICMP_TSTAMPREPLY : ICMP_TSTAMP;
} else {
icp->icmp_type = send_reply ?
ICMP_ECHOREPLY : ICMP_ECHO;
}
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = htons(ntransmitted++ % (MAX_ICMP_SEQ + 1));
if (icp->icmp_seq == 0)
num_wraps++;
icp->icmp_id = htons(ident);
tp = (struct timeval *)&out_pkt[ICMP_MINLEN];
datap = &out_pkt[ICMP_MINLEN + sizeof (struct timeval)];
}
start = sizeof (struct timeval);
(void) gettimeofday(&t_snd, (struct timezone *)NULL);
if ((datalen >= sizeof (struct timeval)) ||
(family == AF_INET && use_icmp_ts))
*tp = t_snd;
if (family == AF_INET && use_icmp_ts) {
start = sizeof (struct id_ts);
icp->icmp_otime = htonl((tp->tv_sec % (24*60*60)) * 1000 +
tp->tv_usec / 1000);
}
for (i = start; i < datalen; i++)
*datap++ = i;
if (family == AF_INET) {
if (!use_udp)
icp->icmp_cksum = in_cksum((ushort_t *)icp, cc);
i = sendto(send_sock, (char *)out_pkt, cc, 0, whereto,
sizeof (struct sockaddr_in));
} else {
msg6->msg_name = to6;
msg6->msg_namelen = sizeof (struct sockaddr_in6);
iov.iov_base = out_pkt;
iov.iov_len = cc;
msg6->msg_iov = &iov;
msg6->msg_iovlen = 1;
i = sendmsg(send_sock, msg6, 0);
}
t_last_probe_sent = gethrtime();
if (i < 0 || i != cc) {
if (i < 0) {
Fprintf(stderr, "%s: sendto %s\n", progname,
strerror(errno));
if (!stats)
exit(EXIT_FAILURE);
}
Printf("ping: wrote %s %d chars, ret=%d\n",
targethost, cc, i);
(void) fflush(stdout);
}
}
char *
pr_name(char *addr, int family)
{
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr *sa;
switch (family) {
case AF_INET:
(void) memset(&sin, 0, sizeof (sin));
sin.sin_addr = *(struct in_addr *)addr;
sin.sin_port = 0;
sa = (struct sockaddr *)&sin;
break;
case AF_INET6:
(void) memset(&sin6, 0, sizeof (sin6));
sin6.sin6_addr = *(struct in6_addr *)addr;
sin6.sin6_port = 0;
sa = (struct sockaddr *)&sin6;
break;
default:
sa = (struct sockaddr *)&sin6;
break;
}
sa->sa_family = family;
return (pr_name_sa(sa));
}
char *
pr_name_sa(const struct sockaddr *sa)
{
const struct sockaddr_in *sin = (struct sockaddr_in *)sa;
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
int family = sa->sa_family;
static struct in6_addr prev_addr = IN6ADDR_ANY_INIT;
static int prev_scope = 0;
char *cp;
char abuf[INET6_ADDRSTRLEN];
static char buf[NI_MAXHOST + INET6_ADDRSTRLEN + 3];
uint_t slen, alen, hlen;
char *addr;
int scope;
switch (family) {
case AF_INET:
slen = sizeof (struct sockaddr_in);
alen = sizeof (struct in_addr);
addr = (char *)&sin->sin_addr;
scope = 0;
break;
case AF_INET6:
slen = sizeof (struct sockaddr_in6);
alen = sizeof (struct in6_addr);
addr = (char *)&sin6->sin6_addr;
scope = sin6->sin6_scope_id;
break;
default:
(void) snprintf(buf, sizeof (buf), "<invalid address family>");
return (buf);
}
if (scope != prev_scope || memcmp(addr, &prev_addr, alen) != 0) {
int flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD;
mutex_enter(&ns_lock);
ns_active = _B_TRUE;
ns_starttime = gethrtime();
mutex_exit(&ns_lock);
if (getnameinfo(sa, slen, buf, sizeof (buf),
NULL, 0, flags) != 0) {
if (getnameinfo(sa, slen, buf, sizeof (buf),
NULL, 0, NI_NUMERICHOST) != 0) {
buf[0] = 0;
}
} else if (!nflag) {
hlen = strlen(buf);
cp = (char *)(buf + hlen);
(void) snprintf(cp, sizeof (buf) - hlen, " (%s)",
inet_ntop(family, (const void *)addr, abuf,
sizeof (abuf)));
}
mutex_enter(&ns_lock);
ns_active = _B_FALSE;
mutex_exit(&ns_lock);
prev_addr = *(struct in6_addr *)addr;
prev_scope = scope;
}
return (buf);
}
char *
pr_name4(const struct sockaddr_in *sin)
{
return (pr_name_sa((const struct sockaddr *)sin));
}
char *
pr_name6(const struct sockaddr_in6 *sin6)
{
return (pr_name_sa((const struct sockaddr *)sin6));
}
char *
pr_protocol(int prot)
{
static char buf[20];
switch (prot) {
case IPPROTO_ICMPV6:
(void) strlcpy(buf, "icmp6", sizeof (buf));
break;
case IPPROTO_ICMP:
(void) strlcpy(buf, "icmp", sizeof (buf));
break;
case IPPROTO_TCP:
(void) strlcpy(buf, "tcp", sizeof (buf));
break;
case IPPROTO_UDP:
(void) strlcpy(buf, "udp", sizeof (buf));
break;
default:
(void) snprintf(buf, sizeof (buf), "prot %d", prot);
break;
}
return (buf);
}
char *
pr_if(int ifindex)
{
static struct ifaddrlist *al = NULL;
static int count = 0;
static char errbuf[ERRBUFSIZE];
int i;
if (al == NULL) {
count = ifaddrlist(&al, AF_INET6, 0, errbuf);
if (count < 0)
count = 0;
}
for (i = 0; i < count; i++) {
if (al[i].index == ifindex)
return (al[i].device);
}
(void) sprintf(errbuf, "%d", ifindex);
return (errbuf);
}
boolean_t
seq_match(ushort_t seq_begin, int seq_len, ushort_t value)
{
if (seq_len > MAX_ICMP_SEQ / 2) {
seq_begin = (seq_begin + seq_len - MAX_ICMP_SEQ / 2) %
(MAX_ICMP_SEQ + 1);
seq_len = MAX_ICMP_SEQ / 2;
}
if (PINGSEQ_LEQ(seq_begin, value) &&
PINGSEQ_LEQ(value, (seq_begin + seq_len - 1) % (MAX_ICMP_SEQ + 1)))
return (_B_TRUE);
else
return (_B_FALSE);
}
void
find_dstaddr(ushort_t icmpseq, union any_in_addr *ipaddr)
{
struct targetaddr *target = targetaddr_list;
int real_seq;
int targetaddr_index;
int real_npackets;
int i;
ipaddr->addr6 = in6addr_any;
if (probe_all && !stats) {
do {
if (seq_match(target->starting_seq_num,
target->num_sent, icmpseq)) {
ipaddr->addr6 = target->dst_addr.addr6;
}
target = target->next;
} while (target != NULL);
} else {
if (icmpseq < (ntransmitted % (MAX_ICMP_SEQ + 1))) {
real_seq = num_wraps * (MAX_ICMP_SEQ + 1) + icmpseq;
} else {
real_seq = (num_wraps - 1) * (MAX_ICMP_SEQ + 1) +
icmpseq;
}
if (real_seq < 0)
return;
real_npackets = (npackets == 0) ? 1 : npackets;
targetaddr_index =
(real_seq % (num_targetaddrs * real_npackets)) /
real_npackets;
for (i = 0; i < targetaddr_index; i++)
target = target->next;
ipaddr->addr6 = target->dst_addr.addr6;
}
}
static ushort_t
in_cksum(ushort_t *addr, int len)
{
int nleft = len;
ushort_t *w = addr;
ushort_t answer;
ushort_t odd_byte = 0;
int sum = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
sum += odd_byte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
void
tvsub(struct timeval *out, struct timeval *in)
{
if ((out->tv_usec -= in->tv_usec) < 0) {
out->tv_sec--;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
static void
finish()
{
Printf("\n----%s PING Statistics----\n", targethost);
Printf("%d packets transmitted, ", ntransmitted);
Printf("%d packets received, ", nreceived);
if (ntransmitted) {
if (nreceived <= ntransmitted) {
Printf("%d%% packet loss",
(int)(((ntransmitted-nreceived)*100) /
ntransmitted));
} else {
Printf("%.2f times amplification",
(double)nreceived / (double)ntransmitted);
}
}
(void) putchar('\n');
if ((datalen >= sizeof (struct timeval)) && (nreceived > 0)) {
double mean = (double)tsum / nreceived;
double smean = (double)tsum2 / nreceived;
double sd =
sqrt(((smean - mean*mean) * nreceived) / (nreceived-1));
Printf("round-trip (ms) min/avg/max/stddev = "
TIMEFORMAT "/" TIMEFORMAT "/"
TIMEFORMAT "/" TIMEFORMAT "\n",
(double)tmin / 1000, mean / 1000,
(double)tmax / 1000, sd / 1000);
}
(void) fflush(stdout);
exit(is_alive ? EXIT_SUCCESS : EXIT_FAILURE);
}
static void
usage(char *cmdname)
{
Fprintf(stderr, "usage: %s host [timeout]\n", cmdname);
Fprintf(stderr,
"usage: %s -s [-l | -U] [-abdDLnRrv] [-A addr_family] [-c traffic_class]\n\t"
"[-g gateway [-g gateway ...]] [-N nexthop] [-F flow_label] [-I interval]\n\t"
"[-i interface] [-P tos] [-p port] [-t ttl] host [data_size] [npackets]\n",
cmdname);
}
static int
int_arg(char *s, char *what)
{
char *cp;
char *ep;
int num;
errno = 0;
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
cp = s + 2;
num = (int)strtol(cp, &ep, 16);
} else {
num = (int)strtol(s, &ep, 10);
}
if (errno || *ep != '\0' || num < 0) {
Fprintf(stderr, "%s: bad %s: %s\n", progname, what, s);
exit(EXIT_FAILURE);
}
return (num);
}
static void
parse_interval(const char *s)
{
long double val;
char *end;
errno = 0;
val = strtold(s, &end);
if (errno != 0 || *end != '\0') {
Fprintf(stderr, "%s: bad interval: %s\n", progname, s);
exit(EXIT_FAILURE);
}
if (val == NAN || val <= 0.0 || val >= INT_MAX) {
Fprintf(stderr, "%s: bad interval: %s\n", progname, s);
exit(EXIT_FAILURE);
}
if (val < 0.01L) {
Fprintf(stderr, "%s: interval too small: %Lf\n", progname, val);
exit(EXIT_FAILURE);
}
interval.it_value.tv_sec = (long)val;
interval.it_value.tv_nsec = (long)((val - interval.it_value.tv_sec) *
NANOSEC);
if (interval.it_value.tv_sec == 0 &&
interval.it_value.tv_nsec < mintime) {
mintime = interval.it_value.tv_nsec;
}
}
void
ping_gettime(struct msghdr *msg, struct timeval *tv)
{
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof (*tv))) {
bcopy(CMSG_DATA(cmsg), tv, sizeof (*tv));
return;
}
}
(void) gettimeofday(tv, (struct timezone *)NULL);
}
static void *
ns_warning_thr(void *unused)
{
for (;;) {
hrtime_t now;
(void) sleep(ns_sleeptime);
now = gethrtime();
mutex_enter(&ns_lock);
if (ns_active == _B_TRUE &&
now - ns_starttime >= ns_warntime * NANOSEC) {
Fprintf(stderr, "%s: warning: ICMP responses "
"received, but name service lookups are "
"taking a while. Use ping -n to disable "
"name service lookups.\n",
progname);
mutex_exit(&ns_lock);
return (NULL);
}
mutex_exit(&ns_lock);
}
return (NULL);
}