MFC r271910, r273211:

* Add -x waittime and -X timeout options for feature parity.  These are
  equivalent to -W and -t options of ping(8).  Different letters are used
  because both have already been used for another purposes in ping6(8).

* Fix a problem that reply packets are not received when -i T option is set
  and (T < RTT).

 - Use select(2) for timeout instead of interval timer. Remove poll(2) support.
 - Use sigaction(2) instead of signal(3).
 - Exit in SIGINT handler when two signals are received and doing reverse DNS
   lookup as ping(8) does.
 - Remove redundant variables used for getaddrinfo(3).

Approved by:	re (gjb)
This commit is contained in:
hrs 2015-07-23 19:52:03 +00:00
parent b55b825a16
commit c20172e5e8
3 changed files with 223 additions and 235 deletions

View File

@ -4,7 +4,7 @@ PROG= ping6
MAN= ping6.8
CFLAGS+=-DIPSEC -DKAME_SCOPEID -DUSE_RFC2292BIS \
-DHAVE_POLL_H -DHAVE_ARC4RANDOM
-DHAVE_ARC4RANDOM
WARNS?= 2
BINOWN= root

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 5, 2013
.Dd September 22, 2014
.Dt PING6 8
.Os
.Sh NAME
@ -65,6 +65,12 @@ packets to network hosts
.Op Fl i Ar wait
.Ek
.Bk -words
.Op Fl x Ar waittime
.Ek
.Bk -words
.Op Fl X Ar timeout
.Ek
.Bk -words
.Op Fl l Ar preload
.Ek
.Bk -words
@ -191,6 +197,15 @@ The default is to wait for one second between each packet.
This option is incompatible with the
.Fl f
option.
.It Fl x Ar waittime
Time in milliseconds to wait for a reply for each packet sent.
If a reply arrives later,
the packet is not printed as replied,
but considered as replied when calculating statistics.
.It Fl X Ar timeout
Specify a timeout,
in seconds,
before ping exits regardless of how many packets have been received.
.It Fl l Ar preload
If
.Ar preload

View File

@ -125,10 +125,8 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef IPSEC
#include <netipsec/ah.h>
@ -154,6 +152,8 @@ struct tv32 {
#define DEFDATALEN ICMP6ECHOTMLEN
#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN
#define NROUTES 9 /* number of record route slots */
#define MAXWAIT 10000 /* max ms to wait for response */
#define MAXALARM (60 * 60) /* max seconds for alarm timeout */
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
@ -190,6 +190,7 @@ struct tv32 {
#define F_MISSED 0x800000
#define F_DONTFRAG 0x1000000
#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES)
#define F_WAITTIME 0x2000000
u_int options;
#define IN6LEN sizeof(struct in6_addr)
@ -207,7 +208,6 @@ u_int options;
int mx_dup_ck = MAX_DUP_CHK;
char rcvd_tbl[MAX_DUP_CHK / 8];
struct addrinfo *res = NULL;
struct sockaddr_in6 dst; /* who to ping6 */
struct sockaddr_in6 src; /* src addr of this packet */
socklen_t srclen;
@ -223,12 +223,6 @@ u_int8_t nonce[8]; /* nonce field for node information */
int hoplimit = -1; /* hoplimit */
int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */
u_char *packet = NULL;
#ifdef HAVE_POLL_H
struct pollfd fdmaskp[1];
#else
fd_set *fdmaskp = NULL;
int fdmasks;
#endif
/* counters */
long nmissedmax; /* max value of ntransmitted - nreceived - 1 */
@ -236,7 +230,9 @@ long npackets; /* max packets to transmit */
long nreceived; /* # of packets we got back */
long nrepeats; /* number of duplicates */
long ntransmitted; /* sequence # for outbound packets = #sent */
struct timeval interval = {1, 0}; /* interval between packets */
int interval = 1000; /* interval between packets in ms */
int waittime = MAXWAIT; /* timeout for each packet */
long nrcvtimeout = 0; /* # of packets we got back after waittime */
/* timing */
int timing; /* flag to do timing */
@ -253,7 +249,6 @@ struct msghdr smsghdr;
struct iovec smsgiov;
char *scmsg = 0;
volatile sig_atomic_t seenalrm;
volatile sig_atomic_t seenint;
#ifdef SIGINFO
volatile sig_atomic_t seeninfo;
@ -265,7 +260,6 @@ int get_hoplim(struct msghdr *);
int get_pathmtu(struct msghdr *);
struct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
void onsignal(int);
void retransmit(void);
void onint(int);
size_t pingerlen(void);
int pinger(void);
@ -293,19 +287,15 @@ void usage(void);
int
main(int argc, char *argv[])
{
struct itimerval itimer;
struct sockaddr_in6 from;
struct timeval last, intvl;
struct sockaddr_in6 from, *sin6;
#ifndef HAVE_ARC4RANDOM
struct timeval seed;
#endif
#ifdef HAVE_POLL_H
int timeout;
#else
struct timeval timeout, *tv;
#endif
struct addrinfo hints;
struct addrinfo hints, *res;
struct sigaction si_sa;
int cc, i;
int ch, hold, packlen, preload, optval, ret_ga;
int almost_done, ch, hold, packlen, preload, optval, error;
int nig_oldmcprefix = -1;
u_char *datap;
char *e, *target, *ifname = NULL, *gateway = NULL;
@ -326,7 +316,8 @@ main(int argc, char *argv[])
char *policy_in = NULL;
char *policy_out = NULL;
#endif
double intval;
double t;
u_long alarmtimeout;
size_t rthlen;
#ifdef IPV6_USE_MIN_MTU
int mflag = 0;
@ -336,7 +327,7 @@ main(int argc, char *argv[])
memset(&smsghdr, 0, sizeof(smsghdr));
memset(&smsgiov, 0, sizeof(smsgiov));
preload = 0;
alarmtimeout = preload = 0;
datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
#ifndef IPSEC
#define ADDOPTS
@ -348,7 +339,7 @@ main(int argc, char *argv[])
#endif /*IPSEC_POLICY_IPSEC*/
#endif
while ((ch = getopt(argc, argv,
"a:b:c:DdfHg:h:I:i:l:mnNop:qrRS:s:tvwW" ADDOPTS)) != -1) {
"a:b:c:DdfHg:h:I:i:l:mnNop:qrRS:s:tvwWx:X:" ADDOPTS)) != -1) {
#undef ADDOPTS
switch (ch) {
case 'a':
@ -450,22 +441,22 @@ main(int argc, char *argv[])
#endif
break;
case 'i': /* wait between sending packets */
intval = strtod(optarg, &e);
t = strtod(optarg, &e);
if (*optarg == '\0' || *e != '\0')
errx(1, "illegal timing interval %s", optarg);
if (intval < 1 && getuid()) {
if (t < 1 && getuid()) {
errx(1, "%s: only root may use interval < 1s",
strerror(EPERM));
}
interval.tv_sec = (long)intval;
interval.tv_usec =
(long)((intval - interval.tv_sec) * 1000000);
if (interval.tv_sec < 0)
intvl.tv_sec = (long)t;
intvl.tv_usec =
(long)((t - intvl.tv_sec) * 1000000);
if (intvl.tv_sec < 0)
errx(1, "illegal timing interval %s", optarg);
/* less than 1/hz does not make sense */
if (interval.tv_sec == 0 && interval.tv_usec < 1) {
if (intvl.tv_sec == 0 && intvl.tv_usec < 1) {
warnx("too small interval, raised to .000001");
interval.tv_usec = 1;
intvl.tv_usec = 1;
}
options |= F_INTERVAL;
break;
@ -516,10 +507,10 @@ main(int argc, char *argv[])
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMPV6;
ret_ga = getaddrinfo(optarg, NULL, &hints, &res);
if (ret_ga) {
error = getaddrinfo(optarg, NULL, &hints, &res);
if (error) {
errx(1, "invalid source address: %s",
gai_strerror(ret_ga));
gai_strerror(error));
}
/*
* res->ai_family must be AF_INET6 and res->ai_addrlen
@ -556,6 +547,24 @@ main(int argc, char *argv[])
options &= ~F_NOUSERDATA;
options |= F_FQDNOLD;
break;
case 'x':
t = strtod(optarg, &e);
if (*e || e == optarg || t > (double)INT_MAX)
err(EX_USAGE, "invalid timing interval: `%s'",
optarg);
options |= F_WAITTIME;
waittime = (int)t;
break;
case 'X':
alarmtimeout = strtoul(optarg, &e, 0);
if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
errx(EX_USAGE, "invalid timeout: `%s'",
optarg);
if (alarmtimeout > MAXALARM)
errx(EX_USAGE, "invalid timeout: `%s' > %d",
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
case 'P':
@ -622,9 +631,9 @@ main(int argc, char *argv[])
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMPV6;
ret_ga = getaddrinfo(target, NULL, &hints, &res);
if (ret_ga)
errx(1, "%s", gai_strerror(ret_ga));
error = getaddrinfo(target, NULL, &hints, &res);
if (error)
errx(1, "%s", gai_strerror(error));
if (res->ai_canonname)
hostname = res->ai_canonname;
else
@ -647,28 +656,25 @@ main(int argc, char *argv[])
/* set the gateway (next hop) if specified */
if (gateway) {
struct addrinfo ghints, *gres;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMPV6;
memset(&ghints, 0, sizeof(ghints));
ghints.ai_family = AF_INET6;
ghints.ai_socktype = SOCK_RAW;
ghints.ai_protocol = IPPROTO_ICMPV6;
error = getaddrinfo(gateway, NULL, &hints, &gres);
error = getaddrinfo(gateway, NULL, &hints, &res);
if (error) {
errx(1, "getaddrinfo for the gateway %s: %s",
gateway, gai_strerror(error));
}
if (gres->ai_next && (options & F_VERBOSE))
if (res->ai_next && (options & F_VERBOSE))
warnx("gateway resolves to multiple addresses");
if (setsockopt(s, IPPROTO_IPV6, IPV6_NEXTHOP,
gres->ai_addr, gres->ai_addrlen)) {
res->ai_addr, res->ai_addrlen)) {
err(1, "setsockopt(IPV6_NEXTHOP)");
}
freeaddrinfo(gres);
freeaddrinfo(res);
}
/*
@ -898,7 +904,7 @@ main(int argc, char *argv[])
}
if (argc > 1) { /* some intermediate addrs are specified */
int hops, error;
int hops;
#ifdef USE_RFC2292BIS
int rthdrlen;
#endif
@ -920,26 +926,25 @@ main(int argc, char *argv[])
#endif /* USE_RFC2292BIS */
for (hops = 0; hops < argc - 1; hops++) {
struct addrinfo *iaip;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
if ((error = getaddrinfo(argv[hops], NULL, &hints,
&iaip)))
&res)))
errx(1, "%s", gai_strerror(error));
if (SIN6(iaip->ai_addr)->sin6_family != AF_INET6)
if (res->ai_addr->sa_family != AF_INET6)
errx(1,
"bad addr family of an intermediate addr");
sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
#ifdef USE_RFC2292BIS
if (inet6_rth_add(rthdr,
&(SIN6(iaip->ai_addr))->sin6_addr))
if (inet6_rth_add(rthdr, &sin6->sin6_addr))
errx(1, "can't add an intermediate node");
#else /* old advanced API */
if (inet6_rthdr_add(scmsgp,
&(SIN6(iaip->ai_addr))->sin6_addr,
if (inet6_rthdr_add(scmsg, &sin6->sin6_addr,
IPV6_RTHDR_LOOSE))
errx(1, "can't add an intermediate node");
#endif /* USE_RFC2292BIS */
freeaddrinfo(iaip);
freeaddrinfo(res);
}
#ifndef USE_RFC2292BIS
@ -1055,52 +1060,50 @@ main(int argc, char *argv[])
printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src)));
printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst)));
while (preload--) /* Fire off them quickies. */
(void)pinger();
(void)signal(SIGINT, onsignal);
#ifdef SIGINFO
(void)signal(SIGINFO, onsignal);
#endif
if ((options & F_FLOOD) == 0) {
(void)signal(SIGALRM, onsignal);
itimer.it_interval = interval;
itimer.it_value = interval;
(void)setitimer(ITIMER_REAL, &itimer, NULL);
if (ntransmitted == 0)
retransmit();
if (preload == 0)
pinger();
else {
if (npackets != 0 && preload > npackets)
preload = npackets;
while (preload--)
pinger();
}
gettimeofday(&last, NULL);
#ifndef HAVE_POLL_H
fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask);
if ((fdmaskp = malloc(fdmasks)) == NULL)
err(1, "malloc");
#endif
seenalrm = seenint = 0;
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = onsignal;
if (sigaction(SIGINT, &si_sa, 0) == -1)
err(EX_OSERR, "sigaction SIGINT");
seenint = 0;
#ifdef SIGINFO
if (sigaction(SIGINFO, &si_sa, 0) == -1)
err(EX_OSERR, "sigaction SIGINFO");
seeninfo = 0;
#endif
if (alarmtimeout > 0) {
if (sigaction(SIGALRM, &si_sa, 0) == -1)
err(EX_OSERR, "sigaction SIGALRM");
}
if (options & F_FLOOD) {
intvl.tv_sec = 0;
intvl.tv_usec = 10000;
} else if ((options & F_INTERVAL) == 0) {
intvl.tv_sec = interval / 1000;
intvl.tv_usec = interval % 1000 * 1000;
}
for (;;) {
almost_done = 0;
while (seenint == 0) {
struct timeval now, timeout;
struct msghdr m;
struct iovec iov[2];
fd_set rfds;
int n;
/* signal handling */
if (seenalrm) {
/* last packet sent, timeout reached? */
if (npackets && ntransmitted >= npackets)
break;
retransmit();
seenalrm = 0;
continue;
}
if (seenint) {
if (seenint)
onint(SIGINT);
seenint = 0;
continue;
}
#ifdef SIGINFO
if (seeninfo) {
summary();
@ -1108,93 +1111,106 @@ main(int argc, char *argv[])
continue;
}
#endif
if (options & F_FLOOD) {
(void)pinger();
#ifdef HAVE_POLL_H
timeout = 10;
#else
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
tv = &timeout;
#endif
} else {
#ifdef HAVE_POLL_H
timeout = INFTIM;
#else
tv = NULL;
#endif
FD_ZERO(&rfds);
FD_SET(s, &rfds);
gettimeofday(&now, NULL);
timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
while (timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec--;
}
#ifdef HAVE_POLL_H
fdmaskp[0].fd = s;
fdmaskp[0].events = POLLIN;
cc = poll(fdmaskp, 1, timeout);
#else
memset(fdmaskp, 0, fdmasks);
FD_SET(s, fdmaskp);
cc = select(s + 1, fdmaskp, NULL, NULL, tv);
#endif
if (cc < 0) {
if (errno != EINTR) {
#ifdef HAVE_POLL_H
warn("poll");
#else
warn("select");
#endif
sleep(1);
while (timeout.tv_usec > 1000000) {
timeout.tv_usec -= 1000000;
timeout.tv_sec++;
}
if (timeout.tv_sec < 0)
timeout.tv_sec = timeout.tv_usec = 0;
n = select(s + 1, &rfds, NULL, NULL, &timeout);
if (n < 0)
continue; /* EINTR */
if (n == 1) {
m.msg_name = (caddr_t)&from;
m.msg_namelen = sizeof(from);
memset(&iov, 0, sizeof(iov));
iov[0].iov_base = (caddr_t)packet;
iov[0].iov_len = packlen;
m.msg_iov = iov;
m.msg_iovlen = 1;
memset(cm, 0, CONTROLLEN);
m.msg_control = (void *)cm;
m.msg_controllen = CONTROLLEN;
cc = recvmsg(s, &m, 0);
if (cc < 0) {
if (errno != EINTR) {
warn("recvmsg");
sleep(1);
}
continue;
} else if (cc == 0) {
int mtu;
/*
* receive control messages only. Process the
* exceptions (currently the only possibility is
* a path MTU notification.)
*/
if ((mtu = get_pathmtu(&m)) > 0) {
if ((options & F_VERBOSE) != 0) {
printf("new path MTU (%d) is "
"notified\n", mtu);
}
}
continue;
} else {
/*
* an ICMPv6 message (probably an echoreply)
* arrived.
*/
pr_pack(packet, cc, &m);
}
continue;
} else if (cc == 0)
continue;
m.msg_name = (caddr_t)&from;
m.msg_namelen = sizeof(from);
memset(&iov, 0, sizeof(iov));
iov[0].iov_base = (caddr_t)packet;
iov[0].iov_len = packlen;
m.msg_iov = iov;
m.msg_iovlen = 1;
memset(cm, 0, CONTROLLEN);
m.msg_control = (void *)cm;
m.msg_controllen = CONTROLLEN;
cc = recvmsg(s, &m, 0);
if (cc < 0) {
if (errno != EINTR) {
warn("recvmsg");
sleep(1);
}
continue;
} else if (cc == 0) {
int mtu;
if (((options & F_ONCE) != 0 && nreceived > 0) ||
(npackets > 0 && nreceived >= npackets))
break;
}
if (n == 0 || (options & F_FLOOD)) {
if (npackets == 0 || ntransmitted < npackets)
pinger();
else {
if (almost_done)
break;
almost_done = 1;
/*
* receive control messages only. Process the
* exceptions (currently the only possibility is
* a path MTU notification.)
* If we're not transmitting any more packets,
* change the timer to wait two round-trip times
* if we've received any packets or (waittime)
* milliseconds if we haven't.
*/
if ((mtu = get_pathmtu(&m)) > 0) {
if ((options & F_VERBOSE) != 0) {
printf("new path MTU (%d) is "
"notified\n", mtu);
intvl.tv_usec = 0;
if (nreceived) {
intvl.tv_sec = 2 * tmax / 1000;
if (intvl.tv_sec == 0)
intvl.tv_sec = 1;
} else {
intvl.tv_sec = waittime / 1000;
intvl.tv_usec = waittime % 1000 * 1000;
}
}
continue;
} else {
/*
* an ICMPv6 message (probably an echoreply) arrived.
*/
pr_pack(packet, cc, &m);
}
if (((options & F_ONCE) != 0 && nreceived > 0) ||
(npackets > 0 && nreceived >= npackets))
break;
if (ntransmitted - nreceived - 1 > nmissedmax) {
nmissedmax = ntransmitted - nreceived - 1;
if (options & F_MISSED)
(void)write(STDOUT_FILENO, &BBELL, 1);
gettimeofday(&last, NULL);
if (ntransmitted - nreceived - 1 > nmissedmax) {
nmissedmax = ntransmitted - nreceived - 1;
if (options & F_MISSED)
(void)write(STDOUT_FILENO, &BBELL, 1);
}
}
}
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &si_sa, 0);
sigaction(SIGALRM, &si_sa, 0);
summary();
if (res != NULL)
@ -1203,11 +1219,6 @@ main(int argc, char *argv[])
if(packet != NULL)
free(packet);
#ifndef HAVE_POLL_H
if(fdmaskp != NULL)
free(fdmaskp);
#endif
exit(nreceived == 0 ? 2 : 0);
}
@ -1216,10 +1227,8 @@ onsignal(int sig)
{
switch (sig) {
case SIGALRM:
seenalrm++;
break;
case SIGINT:
case SIGALRM:
seenint++;
break;
#ifdef SIGINFO
@ -1230,38 +1239,6 @@ onsignal(int sig)
}
}
/*
* retransmit --
* This routine transmits another ping6.
*/
void
retransmit(void)
{
struct itimerval itimer;
if (pinger() == 0)
return;
/*
* If we're not transmitting any more packets, change the timer
* to wait two round-trip times if we've received any packets or
* ten seconds if we haven't.
*/
#define MAXWAIT 10
if (nreceived) {
itimer.it_value.tv_sec = 2 * tmax / 1000;
if (itimer.it_value.tv_sec == 0)
itimer.it_value.tv_sec = 1;
} else
itimer.it_value.tv_sec = MAXWAIT;
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = 0;
itimer.it_value.tv_usec = 0;
(void)signal(SIGALRM, onsignal);
(void)setitimer(ITIMER_REAL, &itimer, NULL);
}
/*
* pinger --
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
@ -1575,6 +1552,11 @@ pr_pack(u_char *buf, int cc, struct msghdr *mhdr)
if (options & F_QUIET)
return;
if (options & F_WAITTIME && triptime > waittime) {
++nrcvtimeout;
return;
}
if (options & F_FLOOD)
(void)write(STDOUT_FILENO, &BSPACE, 1);
else {
@ -2241,24 +2223,12 @@ tvsub(struct timeval *out, struct timeval *in)
void
onint(int notused __unused)
{
summary();
if (res != NULL)
freeaddrinfo(res);
if(packet != NULL)
free(packet);
#ifndef HAVE_POLL_H
if(fdmaskp != NULL)
free(fdmaskp);
#endif
(void)signal(SIGINT, SIG_DFL);
(void)kill(getpid(), SIGINT);
/* NOTREACHED */
exit(1);
/*
* When doing reverse DNS lookups, the seenint flag might not
* be noticed for a while. Just exit if we get a second SIGINT.
*/
if ((options & F_HOSTNAME) && seenint != 0)
_exit(nreceived ? 0 : 2);
}
/*
@ -2282,6 +2252,8 @@ summary(void)
((((double)ntransmitted - nreceived) * 100.0) /
ntransmitted));
}
if (nrcvtimeout)
printf(", %ld packets out of wait time", nrcvtimeout);
(void)putchar('\n');
if (nreceived && timing) {
/* Only display average to microseconds */
@ -2807,6 +2779,7 @@ usage(void)
#endif
"\n"
" [-p pattern] [-S sourceaddr] [-s packetsize] "
"[hops ...] host\n");
"[-x waittime]\n"
" [-X timeout] [hops ...] host\n");
exit(1);
}