diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2015-11-29 22:42:14 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2015-11-29 22:42:14 +0000 |
commit | 8bf223e9e6a49d29c916f8ddf4bb2b555b575378 (patch) | |
tree | d794e8d956b613bf08a1f52f530bbff84fc9f4fa | |
parent | b732d07b4af673943ec65d4a177842c8af03b71f (diff) |
use ping6(8)'s engine; next step in unification
OK benno@
-rw-r--r-- | sbin/ping/ping.c | 296 |
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. */ |