#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#endif
#include <sysexits.h>
#include <getopt.h>
#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 */
#define F_SWEEP 0x2000000
#define F_CONNECT 0x4000000
#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES)
+#define F_WAITTIME 0x8000000
u_int options;
static int longopt_flag = 0;
char *boundif;
unsigned int ifscope;
int nocell;
-#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 */
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 */
+static int interval = 1000; /* interval between packets in ms */
+static int waittime = MAXWAIT; /* timeout for each packet */
+static long nrcvtimeout = 0; /* # of packets we got back after waittime */
+
long snpackets = 0; /* max packets to transmit in one sweep */
long sntransmitted = 0; /* # of packets we sent in this sweep */
int sweepmax = 0; /* max value of payload in sweep */
int get_so_traffic_class(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);
void summary(void);
void tvsub(struct timeval *, struct timeval *);
int setpolicy(int, char *);
-char *nigroup(char *);
+char *nigroup(char *, int);
static int str2sotc(const char *, bool *);
static int str2netservicetype(const char *, bool *);
static u_int8_t str2tclass(const char *, bool *);
int
main(int argc, char *argv[])
{
- struct itimerval itimer;
+ struct timeval last, intvl;
struct sockaddr_in6 from;
-#ifndef HAVE_ARC4RANDOM
- struct timeval seed;
-#endif
-#ifdef HAVE_POLL_H
- int timeout;
-#else
- struct timeval timeout, *tv;
-#endif
struct addrinfo hints;
int cc, i;
- int ch, hold, packlen, preload, optval, ret_ga;
+ int almost_done, ch, hold, packlen, preload, optval, ret_ga;
+ int nig_oldmcprefix = -1;
u_char *datap;
char *e, *target, *ifname = NULL, *gateway = NULL;
int ip6optlen = 0;
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;
/* T_CLASS value -1 means default, so -2 means do not bother */
int tclass = -2;
bool valid;
- struct timeval intvl;
/* just to be sure */
memset(&smsghdr, 0, sizeof(smsghdr));
#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 < 0.1 && getuid()) {
- errx(1, "%s: only root may use interval < 0.1s",
+ 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;
break;
case 'N':
options |= F_NIGROUP;
+ nig_oldmcprefix++;
break;
case 'o':
options |= F_ONCE;
errx(1, "illegal TOS value -- %s", optarg);
rcvtclass = 1;
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':
}
if (options & F_NIGROUP) {
- target = nigroup(argv[argc - 1]);
+ target = nigroup(argv[argc - 1], nig_oldmcprefix);
if (target == NULL) {
usage();
/*NOTREACHED*/
if (ret_ga)
errx(1, "getaddrinfo -- %s", gai_strerror(ret_ga));
if (res->ai_canonname)
- hostname = res->ai_canonname;
+ hostname = strdup(res->ai_canonname);
else
hostname = target;
}
if ((options & F_NOUSERDATA) == 0) {
+ if (datalen >= sizeof(struct tv32)) {
+ /* we can time transfer */
+ timing = 1;
+ } else
+ timing = 0;
/* in F_VERBOSE case, we may get non-echoreply packets*/
if (options & F_VERBOSE)
packlen = MAX(2048, sweepmax) + IP6LEN + ICMP6ECHOLEN + EXTRA;
packlen = 2048 + IP6LEN + ICMP6ECHOLEN + EXTRA;
}
- if (!(packet = (u_char *)malloc((u_int)MAX(datalen, sweepmax))))
+ if (!(packet = (u_char *)malloc(packlen)))
err(1, "Unable to allocate packet");
if (!(options & F_PINGFILLED))
for (i = ICMP6ECHOLEN; i < MAX(datalen, sweepmax); ++i)
*datap++ = i;
ident = getpid() & 0xFFFF;
-#ifndef HAVE_ARC4RANDOM
- gettimeofday(&seed, NULL);
- srand((unsigned int)(seed.tv_sec ^ seed.tv_usec ^ (long)ident));
- memset(nonce, 0, sizeof(nonce));
- for (i = 0; i < sizeof(nonce); i += sizeof(int))
- *((int *)&nonce[i]) = rand();
-#else
- memset(nonce, 0, sizeof(nonce));
- for (i = 0; i < sizeof(nonce); i += sizeof(u_int32_t))
- *((u_int32_t *)&nonce[i]) = arc4random();
-#endif
+ arc4random_buf(nonce, sizeof(nonce));
optval = 1;
if (options & F_DONTFRAG)
if (setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG,
&optval, sizeof(optval)) == -1)
err(1, "IPV6_DONTFRAG");
hold = 1;
+
if (options & F_SO_DEBUG)
(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
sizeof(hold));
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();
+ if (preload == 0)
+ pinger();
+ else {
+ if (npackets != 0 && preload > npackets)
+ preload = npackets;
+ while (preload--)
+ pinger();
+ }
+ gettimeofday(&last, NULL);
/*
* rdar://25829310
#ifdef SIGINFO
(void)signal(SIGINFO, onsignal);
#endif
-
- if ((options & F_FLOOD) == 0) {
- if (signal(SIGALRM, onsignal) == SIG_ERR)
- warn("signal(SIGALRM)");
- itimer.it_interval = interval;
- itimer.it_value = interval;
- (void)setitimer(ITIMER_REAL, &itimer, NULL);
- if (ntransmitted == 0)
- retransmit();
+ if (alarmtimeout > 0) {
+ (void)signal(SIGALRM, onsignal);
}
if (options & F_FLOOD) {
intvl.tv_sec = 0;
intvl.tv_usec = 10000;
- } else {
- intvl.tv_sec = interval.tv_sec;
- intvl.tv_usec = interval.tv_usec;
+ } else if ((options & F_INTERVAL) == 0) {
+ intvl.tv_sec = interval / 1000;
+ intvl.tv_usec = interval % 1000 * 1000;
}
-
-#ifndef HAVE_POLL_H
- fdmasks = howmany(s + 1, NFDBITS) * sizeof(fd_mask);
- if ((fdmaskp = malloc(fdmasks)) == NULL)
- err(1, "malloc");
-#endif
/* For control (ancillary) data received from recvmsg() */
cm = (struct cmsghdr *)malloc(CONTROLLEN);
if (cm == NULL)
err(1, "malloc");
- 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) {
- seenalrm = 0;
- /* last packet sent, timeout reached? */
- if (npackets && ntransmitted >= npackets)
- break;
- /* sweep done ? */
- if (sweepmax && datalen > sweepmax)
- break;
- retransmit();
- continue;
- }
- if (seenint) {
- seenint = 0;
+ if (seenint)
onint(SIGINT);
- continue;
- }
#ifdef SIGINFO
if (seeninfo) {
- seeninfo = 0;
summary();
+ seeninfo = 0;
continue;
}
#endif
-
- if (options & F_FLOOD) {
- if (pinger() != 0)
- break;
-#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
- }
-#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);
+ 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--;
}
- continue;
- } else if (cc == 0) {
- continue;
+ while (timeout.tv_usec > 1000000) {
+ timeout.tv_usec -= 1000000;
+ timeout.tv_sec++;
}
- 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 (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;
+ m.msg_flags = 0;
+
+ 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);
+ /*
+ * 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 {
- /*
- * an ICMPv6 message (probably an echoreply) arrived.
- */
- pr_pack(packet, cc, &m);
+ if (((options & F_ONCE) != 0 && nreceived > 0) ||
+ (npackets > 0 && nreceived >= npackets) ||
+ (sweepmax && datalen > sweepmax))
+ break;
}
- 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);
+ if (n == 0 || (options & F_FLOOD)) {
+ if (npackets == 0 || ntransmitted < npackets)
+ pinger();
+ else {
+ if (almost_done)
+ break;
+ almost_done = 1;
+ /*
+ * 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.
+ */
+ 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;
+ }
+ }
+ gettimeofday(&last, NULL);
+ if (ntransmitted - nreceived - 1 > nmissedmax) {
+ nmissedmax = ntransmitted - nreceived - 1;
+ if (options & F_MISSED)
+ (void)write(STDOUT_FILENO, &BBELL, 1);
+ }
}
}
+ sigemptyset(&newset);
+ if (sigprocmask(SIG_SETMASK, &newset, NULL) != 0)
+ err(EX_OSERR, "sigprocmask(newset)");
summary();
- if (res != NULL)
- freeaddrinfo(res);
-
if (packet != NULL)
free(packet);
- if (cm != NULL)
- free(cm);
-
- if (scmsg != NULL)
- free(scmsg);
-
-#ifndef HAVE_POLL_H
- if (fdmaskp != NULL)
- free(fdmaskp);
-#endif
-
exit(nreceived == 0 ? 2 : 0);
}
fflush(stdout);
switch (sig) {
- case SIGALRM:
- seenalrm++;
- break;
case SIGINT:
+ case SIGALRM:
seenint++;
break;
#ifdef SIGINFO
}
}
-/*
- * 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
icp->icmp6_code = 0;
icp->icmp6_id = htons(ident);
icp->icmp6_seq = ntohs(seq);
- if (datalen >= sizeof(struct tv32)) {
- /* we can time transfer */
- timing = 1;
- } else
- timing = 0;
if (timing) {
struct timeval tv;
struct tv32 *tv32;
if (options & F_QUIET)
return;
+ if (options & F_WAITTIME && triptime > waittime) {
+ ++nrcvtimeout;
+ return;
+ }
+
if (options & F_FLOOD)
(void)write(STDOUT_FILENO, &BSPACE, 1);
else {
dp = outpack + ICMP6ECHOLEN + ICMP6ECHOTMLEN;
for (i = 8; cp < end; ++i, ++cp, ++dp) {
if (*cp != *dp) {
- (void)printf("\nwrong data byte #%d "
- "should be 0x%x but was 0x%x",
- i, *dp, *cp);
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", i, *dp, *cp);
break;
}
}
if (TST(seq % mx_dup_ck)) {
++nrepeats;
--nreceived;
+ dupflag = 1;
} else {
SET(seq % mx_dup_ck);
+ dupflag = 0;
}
if (options & F_QUIET)
putchar(')');
goto fqdnend;
}
- ttl = (int32_t)ntohl(*(u_int32_t *)
- &buf[off+ICMP6ECHOLEN+8]);
+ ttl = (int32_t)ntohl(*(u_long *)&buf[off+ICMP6ECHOLEN+8]);
if (comma)
printf(",");
if (!(ni->ni_flags & NI_FQDN_FLAG_VALIDTTL)) {
void *bufp;
struct cmsghdr *cm;
+ bufsize = 0;
bufp = mhdr->msg_control;
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
void
onint(int notused __unused)
{
- summary();
-
- if (res != NULL)
- freeaddrinfo(res);
-
- if (packet != NULL)
- free(packet);
-
- if (cm != NULL)
- free(cm);
-
- if (scmsg != NULL)
- free(scmsg);
-
-#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);
}
/*
((((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 */
#endif
char *
-nigroup(char *name)
+nigroup(char *name, int nig_oldmcprefix)
{
char *p;
char *q;
size_t l;
char hbuf[NI_MAXHOST];
struct in6_addr in6;
+ int valid;
p = strchr(name, '.');
if (!p)
*q = tolower(*(unsigned char *)q);
}
- /* generate 8 bytes of pseudo-random value. */
+ /* generate 16 bytes of pseudo-random value. */
memset(&ctxt, 0, sizeof(ctxt));
MD5Init(&ctxt);
c = l & 0xff;
MD5Update(&ctxt, (unsigned char *)name, l);
MD5Final(digest, &ctxt);
- if (inet_pton(AF_INET6, "ff02::2:0000:0000", &in6) != 1)
+ if (nig_oldmcprefix) {
+ /* draft-ietf-ipngwg-icmp-name-lookup */
+ valid = inet_pton(AF_INET6, "ff02::2:0000:0000", &in6);
+ } else {
+ /* RFC 4620 */
+ valid = inet_pton(AF_INET6, "ff02::2:ff00:0000", &in6);
+ }
+ if (valid != 1)
return NULL; /*XXX*/
+
+ if (nig_oldmcprefix) {
+ /* draft-ietf-ipngwg-icmp-name-lookup */
bcopy(digest, &in6.s6_addr[12], 4);
+ } else {
+ /* RFC 4620 */
+ bcopy(digest, &in6.s6_addr[13], 3);
+ }
if (inet_ntop(AF_INET6, &in6, hbuf, sizeof(hbuf)) == NULL)
return NULL;