Use select() timeouts instead of SIGALRM to schedule packet transmission.
Fixes bin/6649 and removes the last abusive signal handler. Use SO_TIMESTAMP to get the kernel to timestamp packets on reception. Fixes bin/5658 and provides slightly better accuracy. Explicitly zero and terminate the IP options when using -R. PR: bin/5658 PR: bin/6649
This commit is contained in:
parent
ceed3816a0
commit
039d6aa45a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=36378
175
sbin/ping/ping.c
175
sbin/ping/ping.c
@ -45,7 +45,7 @@ static const char copyright[] =
|
||||
static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id: ping.c,v 1.35 1998/05/25 03:50:51 steve Exp $";
|
||||
"$Id: ping.c,v 1.36 1998/05/25 06:53:17 steve Exp $";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
@ -84,6 +84,7 @@ static const char rcsid[] =
|
||||
#include <sys/socket.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
@ -164,14 +165,13 @@ volatile sig_atomic_t siginfo_p;
|
||||
|
||||
static void fill(char *, char *);
|
||||
static u_short in_cksum(u_short *, int);
|
||||
static void catcher(int sig);
|
||||
static void check_status(void);
|
||||
static void finish(void) __dead2;
|
||||
static void pinger(void);
|
||||
static char *pr_addr(struct in_addr);
|
||||
static void pr_icmph(struct icmp *);
|
||||
static void pr_iph(struct ip *);
|
||||
static void pr_pack(char *, int, struct sockaddr_in *);
|
||||
static void pr_pack(char *, int, struct sockaddr_in *, struct timeval *);
|
||||
static void pr_retip(struct ip *);
|
||||
static void status(int);
|
||||
static void stopit(int);
|
||||
@ -183,12 +183,12 @@ main(argc, argv)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
{
|
||||
struct timeval timeout;
|
||||
struct timeval last, intvl;
|
||||
struct hostent *hp;
|
||||
struct sockaddr_in *to;
|
||||
struct termios ts;
|
||||
register int i;
|
||||
int ch, fdmask, hold, packlen, preload, sockerrno;
|
||||
int ch, hold, packlen, preload, sockerrno, almost_done = 0;
|
||||
struct in_addr ifaddr;
|
||||
unsigned char ttl, loop;
|
||||
u_char *datap, *packet;
|
||||
@ -199,6 +199,10 @@ main(argc, argv)
|
||||
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
|
||||
#endif
|
||||
struct sigaction si_sa;
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
struct sockaddr_in from;
|
||||
char ctrl[sizeof(struct cmsghdr) + sizeof(struct timeval)];
|
||||
|
||||
/*
|
||||
* Do the stuff that we need root priv's for *first*, and
|
||||
@ -377,9 +381,11 @@ main(argc, argv)
|
||||
/* record route option */
|
||||
if (options & F_RROUTE) {
|
||||
#ifdef IP_OPTIONS
|
||||
bzero(rspace, sizeof(rspace));
|
||||
rspace[IPOPT_OPTVAL] = IPOPT_RR;
|
||||
rspace[IPOPT_OLEN] = sizeof(rspace) - 1;
|
||||
rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
|
||||
rspace[sizeof(rspace) - 1] = IPOPT_EOL;
|
||||
if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
|
||||
sizeof(rspace)) < 0)
|
||||
err(EX_OSERR, "setsockopt IP_OPTIONS");
|
||||
@ -407,6 +413,12 @@ main(argc, argv)
|
||||
err(EX_OSERR, "setsockopt IP_MULTICAST_IF");
|
||||
}
|
||||
}
|
||||
#ifdef SO_TIMESTAMP
|
||||
{ int on = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0)
|
||||
err(EX_OSERR, "setsockopt SO_TIMESTAMP");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When pinging the broadcast address, you can get a lot of answers.
|
||||
@ -439,16 +451,21 @@ main(argc, argv)
|
||||
err(EX_OSERR, "sigaction SIGINT");
|
||||
}
|
||||
|
||||
si_sa.sa_handler = catcher;
|
||||
if (sigaction(SIGALRM, &si_sa, 0) == -1) {
|
||||
err(EX_OSERR, "sigaction SIGALRM");
|
||||
}
|
||||
|
||||
si_sa.sa_handler = status;
|
||||
if (sigaction(SIGINFO, &si_sa, 0) == -1) {
|
||||
err(EX_OSERR, "sigaction");
|
||||
}
|
||||
|
||||
bzero(&msg, sizeof(msg));
|
||||
msg.msg_name = (caddr_t)&from;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
#ifdef SO_TIMESTAMP
|
||||
msg.msg_control = (caddr_t)ctrl;
|
||||
#endif
|
||||
iov.iov_base = packet;
|
||||
iov.iov_len = packlen;
|
||||
|
||||
if (tcgetattr(STDOUT_FILENO, &ts) != -1) {
|
||||
reset_kerninfo = !(ts.c_lflag & NOKERNINFO);
|
||||
ts.c_lflag |= NOKERNINFO;
|
||||
@ -458,36 +475,85 @@ main(argc, argv)
|
||||
while (preload--) /* fire off them quickies */
|
||||
pinger();
|
||||
|
||||
if ((options & F_FLOOD) == 0)
|
||||
catcher(0); /* start things going */
|
||||
if (options & F_FLOOD) {
|
||||
intvl.tv_sec = 0;
|
||||
intvl.tv_usec = 10000;
|
||||
} else {
|
||||
intvl.tv_sec = interval;
|
||||
intvl.tv_usec = 0;
|
||||
}
|
||||
|
||||
pinger(); /* send the first ping */
|
||||
(void)gettimeofday(&last, NULL);
|
||||
|
||||
while (!finish_up) {
|
||||
struct sockaddr_in from;
|
||||
register int cc;
|
||||
int fromlen;
|
||||
int n;
|
||||
struct timeval timeout, now;
|
||||
fd_set rfds;
|
||||
|
||||
check_status();
|
||||
if (options & F_FLOOD) {
|
||||
pinger();
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 10000;
|
||||
fdmask = 1 << s;
|
||||
if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
|
||||
(fd_set *)NULL, &timeout) < 1)
|
||||
continue;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(s, &rfds);
|
||||
(void)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--;
|
||||
}
|
||||
fromlen = sizeof(from);
|
||||
if ((cc = recvfrom(s, (char *)packet, packlen, 0,
|
||||
(struct sockaddr *)&from, &fromlen)) < 0) {
|
||||
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 == 1) {
|
||||
struct timeval *t = 0;
|
||||
#ifdef SO_TIMESTAMP
|
||||
struct cmsghdr *cmsg = (struct cmsghdr *)&ctrl;
|
||||
|
||||
msg.msg_controllen = sizeof(ctrl);
|
||||
#endif
|
||||
msg.msg_namelen = sizeof(from);
|
||||
if ((cc = recvmsg(s, &msg, 0)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
perror("ping: recvfrom");
|
||||
perror("ping: recvmsg");
|
||||
continue;
|
||||
}
|
||||
pr_pack((char *)packet, cc, &from);
|
||||
#ifdef SO_TIMESTAMP
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_TIMESTAMP &&
|
||||
cmsg->cmsg_len == (sizeof *cmsg + sizeof *t))
|
||||
t = (struct timeval *)CMSG_DATA(cmsg);
|
||||
#endif
|
||||
if (t == 0) {
|
||||
(void)gettimeofday(&now, NULL);
|
||||
t = &now;
|
||||
}
|
||||
pr_pack((char *)packet, cc, &from, t);
|
||||
if (npackets && nreceived >= npackets)
|
||||
break;
|
||||
}
|
||||
if (n == 0) {
|
||||
if (!npackets || ntransmitted < npackets)
|
||||
pinger();
|
||||
else {
|
||||
if (almost_done)
|
||||
break;
|
||||
almost_done = 1;
|
||||
if (nreceived) {
|
||||
intvl.tv_sec = 2 * tmax / 1000;
|
||||
if (!intvl.tv_sec)
|
||||
intvl.tv_sec = 1;
|
||||
} else
|
||||
intvl.tv_sec = MAXWAIT;
|
||||
}
|
||||
(void)gettimeofday(&last, NULL);
|
||||
}
|
||||
}
|
||||
finish();
|
||||
/* NOTREACHED */
|
||||
exit(0); /* Make the compiler happy */
|
||||
@ -506,45 +572,6 @@ stopit(sig)
|
||||
finish_up = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static void
|
||||
catcher(int sig)
|
||||
{
|
||||
int waittime;
|
||||
struct sigaction si_sa;
|
||||
|
||||
pinger();
|
||||
|
||||
if (!npackets || ntransmitted < npackets)
|
||||
(void)alarm((u_int)interval);
|
||||
else {
|
||||
if (nreceived) {
|
||||
waittime = 2 * tmax / 1000;
|
||||
if (!waittime)
|
||||
waittime = 1;
|
||||
} else
|
||||
waittime = MAXWAIT;
|
||||
|
||||
si_sa.sa_handler = stopit;
|
||||
sigemptyset(&si_sa.sa_mask);
|
||||
si_sa.sa_flags = 0;
|
||||
if (sigaction(SIGALRM, &si_sa, 0) == -1) {
|
||||
finish_up = 1;
|
||||
return;
|
||||
}
|
||||
(void)alarm((u_int)waittime);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pinger --
|
||||
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
|
||||
@ -552,9 +579,6 @@ catcher(int sig)
|
||||
* and the sequence number is an ascending integer. The first 8 bytes
|
||||
* of the data portion are used to hold a UNIX "timeval" struct in host
|
||||
* byte-order, to compute the round-trip time.
|
||||
*
|
||||
* bug --
|
||||
* this does far too much to be called from a signal handler.
|
||||
*/
|
||||
static void
|
||||
pinger(void)
|
||||
@ -609,10 +633,11 @@ pinger(void)
|
||||
* program to be run without having intermingled output (or statistics!).
|
||||
*/
|
||||
static void
|
||||
pr_pack(buf, cc, from)
|
||||
pr_pack(buf, cc, from, tv)
|
||||
char *buf;
|
||||
int cc;
|
||||
struct sockaddr_in *from;
|
||||
struct timeval *tv;
|
||||
{
|
||||
register struct icmp *icp;
|
||||
register u_long l;
|
||||
@ -621,12 +646,10 @@ pr_pack(buf, cc, from)
|
||||
static int old_rrlen;
|
||||
static char old_rr[MAX_IPOPTLEN];
|
||||
struct ip *ip;
|
||||
struct timeval tv, *tp;
|
||||
struct timeval *tp;
|
||||
double triptime;
|
||||
int hlen, dupflag;
|
||||
|
||||
(void)gettimeofday(&tv, (struct timezone *)NULL);
|
||||
|
||||
/* Check the IP header */
|
||||
ip = (struct ip *)buf;
|
||||
hlen = ip->ip_hl << 2;
|
||||
@ -654,9 +677,9 @@ pr_pack(buf, cc, from)
|
||||
#endif
|
||||
/* Avoid unaligned data: */
|
||||
memcpy(&tv1,tp,sizeof(tv1));
|
||||
tvsub(&tv, &tv1);
|
||||
triptime = ((double)tv.tv_sec) * 1000.0 +
|
||||
((double)tv.tv_usec) / 1000.0;
|
||||
tvsub(tv, &tv1);
|
||||
triptime = ((double)tv->tv_sec) * 1000.0 +
|
||||
((double)tv->tv_usec) / 1000.0;
|
||||
tsum += triptime;
|
||||
tsumsq += triptime * triptime;
|
||||
if (triptime < tmin)
|
||||
|
Loading…
Reference in New Issue
Block a user