summaryrefslogtreecommitdiff
path: root/sbin/ping/ping.c
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2015-11-29 22:42:14 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2015-11-29 22:42:14 +0000
commit8bf223e9e6a49d29c916f8ddf4bb2b555b575378 (patch)
treed794e8d956b613bf08a1f52f530bbff84fc9f4fa /sbin/ping/ping.c
parentb732d07b4af673943ec65d4a177842c8af03b71f (diff)
use ping6(8)'s engine; next step in unification
OK benno@
Diffstat (limited to 'sbin/ping/ping.c')
-rw-r--r--sbin/ping/ping.c296
1 files changed, 175 insertions, 121 deletions
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
index 408feaabd69..93fc05da306 100644
--- a/sbin/ping/ping.c
+++ b/sbin/ping/ping.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ping.c,v 1.136 2015/11/29 22:41:20 florian Exp $ */
+/* $OpenBSD: ping.c,v 1.137 2015/11/29 22:42:13 florian Exp $ */
/* $NetBSD: ping.c,v 1.20 1995/08/11 22:37:58 cgd Exp $ */
/*
@@ -147,8 +147,7 @@ unsigned long nreceived; /* # of packets we got back */
unsigned long nrepeats; /* number of duplicates */
unsigned long ntransmitted; /* sequence # for outbound packets = #sent */
unsigned long nmissedmax = 1; /* max value of ntransmitted - nreceived - 1 */
-double interval = 1; /* interval between packets */
-struct itimerval interstr; /* interval structure for use with setitimer */
+struct timeval interval = {1, 0}; /* interval between packets */
/* timing */
int timing; /* flag to do timing */
@@ -163,17 +162,21 @@ int bufspace = IP_MAXPACKET;
struct tv64 tv64_offset;
SIPHASH_KEY mac_key;
+volatile sig_atomic_t seenalrm;
+volatile sig_atomic_t seenint;
+volatile sig_atomic_t seeninfo;
+
void fill(char *, char *);
-void catcher(int signo);
-void prtsig(int signo);
-__dead void finish(int signo);
-void summary(int, int);
+void summary(int);
int in_cksum(u_short *, int);
-void pinger(void);
+void onsignal(int);
+void retransmit(void);
+void onint(int);
+int pinger(void);
char *pr_addr(in_addr_t);
int check_icmph(struct ip *);
void pr_icmph(struct icmp *);
-void pr_pack(char *, int, struct sockaddr_in *);
+void pr_pack(char *, int, struct msghdr *);
void pr_retip(struct ip *);
void pr_iph(struct ip *);
#ifndef SMALL
@@ -186,15 +189,17 @@ main(int argc, char *argv[])
{
struct hostent *hp;
struct addrinfo hints, *res;
- struct sockaddr_in from4;
+ struct itimerval itimer;
+ struct sockaddr_in from, from4;
struct sockaddr_in *to;
int ch, i, optval = 1, packlen, preload, maxsize, df = 0, tos = 0;
int error;
u_char *datap, *packet, ttl = MAXTTL, loop = 1;
- char *target, hnamebuf[HOST_NAME_MAX+1], *source = NULL;
+ char *e, *target, hnamebuf[HOST_NAME_MAX+1], *source = NULL;
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
socklen_t maxsizelen;
const char *errstr;
+ double intval;
uid_t uid;
u_int rtableid = 0;
@@ -242,19 +247,23 @@ main(int argc, char *argv[])
source = optarg;
break;
case 'i': /* wait between sending packets */
- interval = strtod(optarg, NULL);
-
- if (interval <= 0 || interval >= INT_MAX)
- errx(1, "bad timing interval: %s", optarg);
-
- if (interval < 1)
- if (getuid())
- errx(1, "%s: only root may use interval < 1s",
- strerror(EPERM));
-
- if (interval < 0.01)
- interval = 0.01;
-
+ intval = strtod(optarg, &e);
+ if (*optarg == '\0' || *e != '\0')
+ errx(1, "illegal timing interval %s", optarg);
+ if (intval < 1 && getuid()) {
+ errx(1, "%s: only root may use interval < 1s",
+ strerror(EPERM));
+ }
+ interval.tv_sec = (time_t)intval;
+ interval.tv_usec =
+ (long)((intval - interval.tv_sec) * 1000000);
+ if (interval.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 < 10000) {
+ warnx("too small interval, raised to 0.01");
+ interval.tv_usec = 10000;
+ }
options |= F_INTERVAL;
break;
case 'L':
@@ -387,12 +396,6 @@ main(int argc, char *argv[])
arc4random_buf(&tv64_offset, sizeof(tv64_offset));
arc4random_buf(&mac_key, sizeof(mac_key));
- memset(&interstr, 0, sizeof(interstr));
-
- interstr.it_value.tv_sec = interval;
- interstr.it_value.tv_usec =
- (long) ((interval - interstr.it_value.tv_sec) * 1000000);
-
if (options & F_FLOOD && options & F_INTERVAL)
errx(1, "-f and -i options are incompatible");
@@ -508,26 +511,54 @@ main(int argc, char *argv[])
err(1, "pledge");
}
- (void)signal(SIGINT, finish);
- (void)signal(SIGALRM, catcher);
- (void)signal(SIGINFO, prtsig);
-
while (preload--) /* fire off them quickies */
pinger();
- if ((options & F_FLOOD) == 0)
- catcher(0); /* start things going */
+ (void)signal(SIGINT, onsignal);
+ (void)signal(SIGINFO, onsignal);
+
+ 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();
+ }
+
+ seenalrm = seenint = 0;
+ seeninfo = 0;
for (;;) {
- struct sockaddr_in from;
- sigset_t omask, nmask;
- socklen_t fromlen;
- struct pollfd pfd;
- ssize_t cc;
- int timeout;
+ struct msghdr m;
+ union {
+ struct cmsghdr hdr;
+ u_char buf[CMSG_SPACE(1024)];
+ } cmsgbuf;
+ struct iovec iov[1];
+ struct pollfd pfd;
+ ssize_t cc;
+ int timeout;
+
+ /* signal handling */
+ if (seenalrm) {
+ retransmit();
+ seenalrm = 0;
+ continue;
+ }
+ if (seenint) {
+ onint(SIGINT);
+ seenint = 0;
+ continue;
+ }
+ if (seeninfo) {
+ summary(0);
+ seeninfo = 0;
+ continue;
+ }
if (options & F_FLOOD) {
- pinger();
+ (void)pinger();
timeout = 10;
} else
timeout = INFTIM;
@@ -538,76 +569,85 @@ main(int argc, char *argv[])
if (poll(&pfd, 1, timeout) <= 0)
continue;
- fromlen = sizeof(from);
- if ((cc = recvfrom(s, packet, packlen, 0,
- (struct sockaddr *)&from, &fromlen)) < 0) {
- if (errno == EINTR)
- continue;
- perror("ping: recvfrom");
+ m.msg_name = &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;
+ m.msg_control = (caddr_t)&cmsgbuf.buf;
+ m.msg_controllen = sizeof(cmsgbuf.buf);
+
+ cc = recvmsg(s, &m, 0);
+ if (cc < 0) {
+ if (errno != EINTR) {
+ warn("recvmsg");
+ sleep(1);
+ }
continue;
- }
- sigemptyset(&nmask);
- sigaddset(&nmask, SIGALRM);
- sigprocmask(SIG_BLOCK, &nmask, &omask);
- pr_pack((char *)packet, cc, &from);
- sigprocmask(SIG_SETMASK, &omask, NULL);
+ } else
+ pr_pack(packet, cc, &m);
+
if (npackets && nreceived >= npackets)
break;
+ if (ntransmitted - nreceived - 1 > nmissedmax) {
+ nmissedmax = ntransmitted - nreceived - 1;
+ if (!(options & F_FLOOD) && (options & F_AUD_MISS))
+ (void)fputc('\a', stderr);
+ }
+
}
- finish(0);
- /* NOTREACHED */
+ summary(0);
+ exit(nreceived == 0);
}
-/*
- * catcher --
- * This routine causes another PING to be transmitted, and then
- * schedules another SIGALRM for 1 second from now.
- *
- * bug --
- * Our sense of time will slowly skew (i.e., packets will not be
- * launched exactly at 1-second intervals). This does not affect the
- * quality of the delay and loss statistics.
- */
-/* ARGSUSED */
+
void
-catcher(int signo)
+onsignal(int sig)
{
- int save_errno = errno;
- unsigned int waittime;
-
- pinger();
- (void)signal(SIGALRM, catcher);
- if (!npackets || ntransmitted < npackets)
- setitimer(ITIMER_REAL, &interstr, (struct itimerval *)0);
- else {
- if (nreceived) {
- waittime = 2 * tmax / 1000000;
- if (!waittime)
- waittime = 1;
- } else
- waittime = maxwait;
- (void)signal(SIGALRM, finish);
- (void)alarm(waittime);
- }
- if (ntransmitted - nreceived - 1 > nmissedmax) {
- nmissedmax = ntransmitted - nreceived - 1;
- if (!(options & F_FLOOD) && (options & F_AUD_MISS))
- write(STDERR_FILENO, "\a", 1);
+ switch (sig) {
+ case SIGALRM:
+ seenalrm++;
+ break;
+ case SIGINT:
+ seenint++;
+ break;
+ case SIGINFO:
+ seeninfo++;
+ break;
}
- errno = save_errno;
}
/*
- * Print statistics when SIGINFO is received.
+ * retransmit --
+ * This routine transmits another ping.
*/
-/* ARGSUSED */
void
-prtsig(int signo)
+retransmit(void)
{
- int save_errno = errno;
+ struct itimerval itimer;
- summary(0, 1);
- errno = save_errno;
+ 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
+ * maxwait seconds if we haven't.
+ */
+ 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, onint);
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
}
/*
@@ -618,13 +658,16 @@ prtsig(int signo)
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*/
-void
+int
pinger(void)
{
struct icmp *icp;
int cc, i;
u_char *packet = outpack;
+ if (npackets && ntransmitted >= npackets)
+ return(-1); /* no more transmission */
+
icp = (struct icmp *)outpack;
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
@@ -685,6 +728,7 @@ pinger(void)
(void)write(STDOUT_FILENO, &DOT, 1);
ntransmitted++;
+ return (0);
}
/*
@@ -695,8 +739,10 @@ pinger(void)
* program to be run without having intermingled output (or statistics!).
*/
void
-pr_pack(char *buf, int cc, struct sockaddr_in *from)
+pr_pack(char *buf, int cc, struct msghdr *mhdr)
{
+ struct sockaddr_in *from;
+ socklen_t fromlen;
struct icmp *icp;
in_addr_t l;
u_int i, j;
@@ -713,6 +759,16 @@ pr_pack(char *buf, int cc, struct sockaddr_in *from)
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
err(1, "clock_gettime(CLOCK_MONOTONIC)");
+ if (!mhdr || !mhdr->msg_name ||
+ mhdr->msg_namelen != sizeof(struct sockaddr_in) ||
+ ((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET) {
+ if (options & F_VERBOSE)
+ warnx("invalid peername");
+ return;
+ }
+ from = (struct sockaddr_in *)mhdr->msg_name;
+ fromlen = mhdr->msg_namelen;
+
/* Check the IP header */
ip = (struct ip *)buf;
hlen = ip->ip_hl << 2;
@@ -967,8 +1023,23 @@ in_cksum(u_short *addr, int len)
return(answer);
}
+/*
+ * onint --
+ * SIGINT handler.
+ */
+void
+onint(int signo)
+{
+ summary(signo);
+
+ if (signo)
+ _exit(nreceived ? 0 : 1);
+ else
+ exit(nreceived ? 0 : 1);
+}
+
void
-summary(int header, int insig)
+summary(int insig)
{
char buf[8192], buft[8192];
@@ -980,11 +1051,10 @@ summary(int header, int insig)
} else
strlcat(buf, "\r", sizeof buf);
- if (header) {
- snprintf(buft, sizeof buft, "--- %s ping statistics ---\n",
- hostname);
- strlcat(buf, buft, sizeof buf);
- }
+
+ snprintf(buft, sizeof buft, "--- %s ping statistics ---\n",
+ hostname);
+ strlcat(buf, buft, sizeof buf);
snprintf(buft, sizeof buft, "%ld packets transmitted, ", ntransmitted);
strlcat(buf, buft, sizeof buf);
@@ -1020,22 +1090,6 @@ summary(int header, int insig)
}
/*
- * finish --
- * Print out statistics, and give up.
- */
-__dead void
-finish(int signo)
-{
- (void)signal(SIGINT, SIG_IGN);
-
- summary(1, signo);
- if (signo)
- _exit(nreceived ? 0 : 1);
- else
- exit(nreceived ? 0 : 1);
-}
-
-/*
* pr_icmph --
* Print a descriptive string about an ICMP header.
*/