diff options
Diffstat (limited to 'ntptrace/ntptrace.c')
-rw-r--r-- | ntptrace/ntptrace.c | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/ntptrace/ntptrace.c b/ntptrace/ntptrace.c new file mode 100644 index 0000000..8115c50 --- /dev/null +++ b/ntptrace/ntptrace.c @@ -0,0 +1,802 @@ +/* + * ntptrace - show the chain from an NTP host leading back to + * its source of time + * + * Jeffrey Mogul DECWRL 13 January 1993 + * + * Inspired by a script written by Glenn Trewitt + * + * Large portions stolen from ntpdate.c + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ntp_fp.h" +#include "ntp.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntptrace.h" +#include "ntp_string.h" +#include "ntp_syslog.h" +#include "ntp_select.h" +#include "ntp_stdlib.h" +#include "recvbuff.h" + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <netdb.h> +#ifdef HAVE_SYS_SIGNAL_H +# include <sys/signal.h> +#else +# include <signal.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +/* + * only 16 stratums, so this is more than enough. + */ +int maxhosts = 20; + +/* + * Debugging flag + */ +volatile int debug = 0; + +#ifndef SYS_VXWORKS +int nonames = 0; /* if set, don't print hostnames */ +#else +int nonames = 1; /* if set, don't print hostnames */ +#endif +/* + * Program name. + */ +char *progname; + +/* + * Systemwide parameters and flags + */ +int sys_retries = 5; /* # of retry attempts per server */ +int sys_timeout = 2; /* timeout time, in seconds */ +struct server **sys_servers; /* the server list */ +int sys_numservers = 0; /* number of servers to poll */ +int sys_maxservers = STRATUM_UNSPEC; /* max number of servers to deal with */ +int sys_version = NTP_OLDVERSION; /* version to poll with */ + + +/* + * File descriptor masks etc. for call to select + */ +int fd; +fd_set fdmask; + +/* + * Miscellaneous flags + */ +int verbose = 0; +int always_step = 0; + +int ntptracemain P((int, char **)); +static void DoTrace P((struct server *)); +static void DoTransmit P((struct server *)); +static int DoReceive P((struct server *)); +static int ReceiveBuf P((struct server *, struct recvbuf *)); +static struct server *addserver P((struct in_addr *)); +static struct server *addservbyname P((const char *)); +static void setup_io P((void)); +static void sendpkt P((struct sockaddr_in *, struct pkt *, int)); +static int getipaddr P((const char *, u_int32 *)); +static int decodeipaddr P((const char *, u_int32 *)); +static void printserver P((struct server *, FILE *)); +static void printrefid P((FILE *, struct server *)); +void input_handler P((l_fp * x)); + +#ifdef SYS_WINNT +int on = 1; +WORD wVersionRequested; +WSADATA wsaData; + +HANDLE TimerThreadHandle = NULL; /* 1998/06/03 - Used in ntplib/machines.c */ +void timer(void) { ; }; /* 1998/06/03 - Used in ntplib/machines.c */ +#endif /* SYS_WINNT */ + +void +input_handler(l_fp * x) +{ ; +} + +#ifdef NO_MAIN_ALLOWED +CALL(ntptrace,"ntptrace",ntptracemain); +#endif + +/* + * Main program. Initialize us and loop waiting for I/O and/or + * timer expiries. + */ +#ifndef NO_MAIN_ALLOWED +int +main( + int argc, + char *argv[] + ) +{ + return ntptracemain(argc, argv); +} +#endif + +int +ntptracemain( + int argc, + char *argv[] + ) +{ + struct server *firstserver; + int errflg; + int c; + + errflg = 0; + progname = argv[0]; + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, "dm:no:r:t:v")) != EOF) + switch (c) { + case 'd': + ++debug; + break; + case 'm': + maxhosts = atoi(ntp_optarg); + break; + case 'n': + nonames = 1; + break; + case 'o': + sys_version = atoi(ntp_optarg); + break; + case 'r': + sys_retries = atoi(ntp_optarg); + if (sys_retries < 1) { + (void)fprintf(stderr, + "%s: retries (%d) too small\n", + progname, sys_retries); + errflg++; + } + break; + case 't': + sys_timeout = atoi(ntp_optarg); + if (sys_timeout < 1) { + (void)fprintf(stderr, + "%s: timeout (%d) too short\n", + progname, sys_timeout); + errflg++; + } + break; + case 'v': + verbose = 1; + break; + case '?': + ++errflg; + break; + default: + break; + } + + if (errflg || (argc - ntp_optind) > 1) { + (void) fprintf(stderr, + "usage: %s [-dnv] [-m maxhosts] [-o version#] [-r retries] [-t timeout] [server]\n", + progname); + exit(2); + } + +#ifdef SYS_WINNT + wVersionRequested = MAKEWORD(1,1); + if (WSAStartup(wVersionRequested, &wsaData)) { + msyslog(LOG_ERR, "No useable winsock.dll: %m"); + exit(1); + } +#endif /* SYS_WINNT */ + + sys_servers = (struct server **) + emalloc(sys_maxservers * sizeof(struct server *)); + + if (debug) { +#ifdef HAVE_SETVBUF + static char buf[BUFSIZ]; + setvbuf(stdout, buf, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + } + + if (debug || verbose) + msyslog(LOG_NOTICE, "%s", Version); + + if ((argc - ntp_optind) == 1) + firstserver = addservbyname(argv[ntp_optind]); + else + firstserver = addservbyname("localhost"); + + if (firstserver == NULL) { + /* a message has already been printed */ + exit(2); + } + + /* + * Initialize the time of day routines and the I/O subsystem + */ + setup_io(); + + DoTrace(firstserver); + +#ifdef SYS_WINNT + WSACleanup(); +#endif + return(0); +} /* main end */ + + +static void +DoTrace( + register struct server *server + ) +{ + int retries = sys_retries; + + if (!server->srcadr.sin_addr.s_addr) { + if (nonames) + printf("%s:\t*Not Synchronized*\n", ntoa(&server->srcadr)); + else + printf("%s:\t*Not Synchronized*\n", ntohost(&server->srcadr)); + fflush(stdout); + return; + } + + if (!verbose) { + if (nonames) + printf("%s: ", ntoa(&server->srcadr)); + else + printf("%s: ", ntohost(&server->srcadr)); + fflush(stdout); + } + while (retries-- > 0) { + DoTransmit(server); + if (DoReceive(server)) + return; + } + if (verbose) { + if (nonames) + printf("%s:\t*Timeout*\n", ntoa(&server->srcadr)); + else + printf("%s:\t*Timeout*\n", ntohost(&server->srcadr)); + } + else + printf("\t*Timeout*\n"); +} + +/* + * Dotransmit - transmit a packet to the given server + */ +static void +DoTransmit( + register struct server *server + ) +{ + struct pkt xpkt; + + if (debug) + printf("DoTransmit(%s)\n", ntoa(&server->srcadr)); + + /* + * Fill in the packet and let 'er rip. + */ + xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC, + sys_version, MODE_CLIENT); + xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC); + xpkt.ppoll = NTP_MINPOLL; + xpkt.precision = NTPTRACE_PRECISION; + xpkt.rootdelay = htonl(NTPTRACE_DISTANCE); + xpkt.rootdispersion = htonl(NTPTRACE_DISP); + xpkt.refid = htonl(NTPTRACE_REFID); + L_CLR(&xpkt.reftime); + L_CLR(&xpkt.org); + L_CLR(&xpkt.rec); + + /* + * just timestamp packet and send it away. + */ + get_systime(&(server->xmt)); + HTONL_FP(&server->xmt, &xpkt.xmt); + sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC); + + if (debug) + printf("DoTransmit to %s\n", ntoa(&(server->srcadr))); +} + +/* + * DoReceive - attempt to receive a packet from a specific server + */ +static int +DoReceive( + register struct server *server + ) +{ + register int n; + fd_set fds; + struct timeval timeout; + l_fp ts; + register struct recvbuf *rb; + int fromlen; + int status; + + /* + * Loop until we see the packet we want or until we time out + */ + for (;;) { + fds = fdmask; + timeout.tv_sec = sys_timeout; + timeout.tv_usec = 0; + n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &timeout); + + if (n == 0) { /* timed out */ + if (debug) + printf("timeout\n"); + return(0); + } + else if (n == -1) { + msyslog(LOG_ERR, "select() error: %m"); + return(0); + } + get_systime(&ts); + + if (free_recvbuffs() == 0) { + msyslog(LOG_ERR, "no buffers"); + exit(1); + } + + rb = get_free_recv_buffer(); + + fromlen = sizeof(struct sockaddr_in); + rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt, + sizeof(rb->recv_pkt), 0, + (struct sockaddr *)&rb->recv_srcadr, &fromlen); + if (rb->recv_length == -1) { + freerecvbuf(rb); + continue; + } + + /* + * Got one. Mark how and when it got here, + * put it on the full list. + */ + rb->recv_time = ts; + add_full_recv_buffer(rb); + + status = ReceiveBuf(server, rb); + + freerecvbuf(rb); + + return(status); + } +} + +/* + * receive - receive and process an incoming frame + * Return 1 on success, 0 on failure + */ +static int +ReceiveBuf( + struct server *server, + struct recvbuf *rbufp + ) +{ + register struct pkt *rpkt; + register s_fp di; + l_fp t10, t23; + l_fp org; + l_fp rec; + l_fp ci; + struct server *nextserver; + struct in_addr nextia; + + + if (debug) { + printf("ReceiveBuf(%s, ", ntoa(&server->srcadr)); + printf("%s)\n", ntoa(&rbufp->recv_srcadr)); + } + + /* + * Check to see if the packet basically looks like something + * intended for us. + */ + if (rbufp->recv_length < LEN_PKT_NOMAC) { + if (debug) + printf("receive: packet length %d\n", + rbufp->recv_length); + return(0); /* funny length packet */ + } + if (rbufp->recv_srcadr.sin_addr.s_addr != server->srcadr.sin_addr.s_addr) { + if (debug) + printf("receive: wrong server\n"); + return(0); /* funny length packet */ + } + + rpkt = &(rbufp->recv_pkt); + + if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION) { + if (debug) + printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode)); + return(0); + } + if (PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) { + if (debug) + printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode)); + return(0); + } + + if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER + && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) + || rpkt->stratum >= STRATUM_UNSPEC) { + if (debug) + printf("receive: mode %d stratum %d\n", + PKT_MODE(rpkt->li_vn_mode), rpkt->stratum); + return(0); + } + + /* + * Decode the org timestamp and make sure we're getting a response + * to our last request. + */ + NTOHL_FP(&rpkt->org, &org); + if (!L_ISEQU(&org, &server->xmt)) { + if (debug) + printf("receive: pkt.org and peer.xmt differ\n"); + return(0); + } + + /* + * Looks good. Record info from the packet. + */ + + server->leap = PKT_LEAP(rpkt->li_vn_mode); + server->stratum = PKT_TO_STRATUM(rpkt->stratum); + server->precision = rpkt->precision; + server->rootdelay = ntohl(rpkt->rootdelay); + server->rootdispersion = ntohl(rpkt->rootdispersion); + server->refid = rpkt->refid; + NTOHL_FP(&rpkt->reftime, &server->reftime); + NTOHL_FP(&rpkt->rec, &rec); + NTOHL_FP(&rpkt->xmt, &server->org); + + /* + * Make sure the server is at least somewhat sane. If not, try + * again. + */ + if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) { + return(0); + } + + /* + * Calculate the round trip delay (di) and the clock offset (ci). + * We use the equations (reordered from those in the spec): + * + * d = (t2 - t3) - (t1 - t0) + * c = ((t2 - t3) + (t1 - t0)) / 2 + */ + t10 = server->org; /* pkt.xmt == t1 */ + L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/ + + t23 = rec; /* pkt.rec == t2 */ + L_SUB(&t23, &org); /* pkt->org == t3 */ + + /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */ + ci = t10; + L_ADD(&ci, &t23); + L_RSHIFT(&ci); + + /* + * Calculate di in t23 in full precision, then truncate + * to an s_fp. + */ + L_SUB(&t23, &t10); + di = LFPTOFP(&t23); + + server->offset = ci; + server->delay = di; + + printserver(server, stdout); + + /* + * End of recursion if we reach stratum 1 or a local refclock + */ + if ((server->stratum <= 1) || (--maxhosts <= 0) || ((server->refid & 0xff) == 127)) + return(1); + + nextia.s_addr = server->refid; + nextserver = addserver(&nextia); + if (nextserver) + DoTrace(nextserver); + return(1); +} + +/* XXX ELIMINATE addserver (almost) identical to ntpdate.c, ntptrace.c */ +/* + * addserver - Allocate a new structure for server. + * Returns a pointer to that structure. + */ +static struct server * +addserver( + struct in_addr *iap + ) +{ + register struct server *server; + static int toomany = 0; + + if (sys_numservers >= sys_maxservers) { + if (!toomany) { + toomany = 1; + msyslog(LOG_ERR, + "too many servers (> %d) specified, remainder not used", + sys_maxservers); + } + return(NULL); + } + + server = (struct server *)emalloc(sizeof(struct server)); + memset((char *)server, 0, sizeof(struct server)); + + server->srcadr.sin_family = AF_INET; + server->srcadr.sin_addr = *iap; + server->srcadr.sin_port = htons(NTP_PORT); + + sys_servers[sys_numservers++] = server; + + return(server); +} + + +/* + * addservbyname - determine a server's address and allocate a new structure + * for it. Returns a pointer to that structure. + */ +static struct server * +addservbyname( + const char *serv + ) +{ + u_int32 ipaddr; + struct in_addr ia; + + if (!getipaddr(serv, &ipaddr)) { + msyslog(LOG_ERR, "can't find host %s\n", serv); + return(NULL); + } + + ia.s_addr = ipaddr; + return(addserver(&ia)); +} + + +static void +setup_io(void) +{ + /* + * Init buffer free list and stat counters + */ + init_recvbuff(sys_maxservers + 2); + + /* create a datagram (UDP) socket */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) +#ifndef SYS_WINNT + < 0 +#else + == INVALID_SOCKET +#endif + ) { + msyslog(LOG_ERR, "socket() failed: %m"); + exit(1); + /*NOTREACHED*/ + } + + FD_ZERO(&fdmask); + FD_SET(fd, &fdmask); +} + + + +/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ +/* + * sendpkt - send a packet to the specified destination + */ +static void +sendpkt( + struct sockaddr_in *dest, + struct pkt *pkt, + int len + ) +{ + int cc; + + cc = sendto(fd, (char *)pkt, (size_t)len, 0, (struct sockaddr *)dest, + sizeof(struct sockaddr_in)); + if (cc == -1) { +#ifndef SYS_WINNT + if (errno != EWOULDBLOCK && errno != ENOBUFS) +#else /* SYS_WINNT */ + int iSockErr = WSAGetLastError(); + if (iSockErr != WSAEWOULDBLOCK && iSockErr != WSAENOBUFS) +#endif /* SYS_WINNT */ + msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest)); + } +} + +/* + * getipaddr - given a host name, return its host address + */ +static int +getipaddr( + const char *host, + u_int32 *num + ) +{ + struct hostent *hp; + + if (decodeipaddr(host, num)) { + return 1; + } else if ((hp = gethostbyname(host)) != 0) { + memmove((char *)num, hp->h_addr, sizeof(long)); + return 1; + } + return 0; +} + +/* + * decodeipaddr - return a host address (this is crude, but careful) + */ +static int +decodeipaddr( + const char *num, + u_int32 *ipaddr + ) +{ + register const char *cp; + register char *bp; + register int i; + register int temp; + char buf[80]; /* will core dump on really stupid stuff */ + + cp = num; + *ipaddr = 0; + for (i = 0; i < 4; i++) { + bp = buf; + while (isdigit((int)*cp)) + *bp++ = *cp++; + if (bp == buf) + break; + + if (i < 3) { + if (*cp++ != '.') + break; + } else if (*cp != '\0') + break; + + *bp = '\0'; + temp = atoi(buf); + if (temp > 255) + break; + *ipaddr <<= 8; + *ipaddr += temp; + } + + if (i < 4) + return 0; + *ipaddr = htonl(*ipaddr); + return 1; +} + + +/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */ +/* + * printserver - print detail information for a server + */ +static void +printserver( + register struct server *pp, + FILE *fp + ) +{ + u_fp synchdist; + + synchdist = pp->rootdispersion + (pp->rootdelay/2); + + if (!verbose) { + (void) fprintf(fp, "stratum %d, offset %s, synch distance %s", + pp->stratum, lfptoa(&pp->offset, 6), ufptoa(synchdist, 5)); + if (pp->stratum == 1) { + (void) fprintf(fp, ", refid "); + printrefid(fp, pp); + } + (void) fprintf(fp, "\n"); + return; + } + + (void) fprintf(fp, "server %s, port %d\n", ntoa(&pp->srcadr), + ntohs(pp->srcadr.sin_port)); + + (void) fprintf(fp, "stratum %d, precision %d, leap %c%c\n", + pp->stratum, pp->precision, pp->leap & 0x2 ? '1' : '0', + pp->leap & 0x1 ? '1' : '0'); + + (void) fprintf(fp, "refid "); + printrefid(fp, pp); + + (void) fprintf(fp, " delay %s, dispersion %s ", fptoa(pp->delay, 5), + ufptoa(pp->dispersion, 5)); + (void) fprintf(fp, "offset %s\n", lfptoa(&pp->offset, 6)); + (void) fprintf(fp, "rootdelay %s, rootdispersion %s", + ufptoa(pp->rootdelay, 5), ufptoa(pp->rootdispersion, 5)); + (void) fprintf(fp, ", synch dist %s\n", ufptoa(synchdist, 5)); + + (void) fprintf(fp, "reference time: %s\n", + prettydate(&pp->reftime)); + (void) fprintf(fp, "originate timestamp: %s\n", + prettydate(&pp->org)); + (void) fprintf(fp, "transmit timestamp: %s\n", + prettydate(&pp->xmt)); + + (void) fprintf(fp, "\n"); + +} + +static void +printrefid( + FILE *fp, + struct server *pp + ) +{ + char junk[5]; + char *str; + + if (pp->stratum == 1) { + junk[4] = 0; + memmove(junk, (char *)&pp->refid, 4); + str = junk; + (void) fprintf(fp, "'%s'", str); + } else { + if (nonames) { + str = numtoa(pp->refid); + (void) fprintf(fp, "[%s]", str); + } + else { + str = numtohost(pp->refid); + (void) fprintf(fp, "%s", str); + } + } +} + +#if !defined(HAVE_VSPRINTF) +int +vsprintf( + char *str, + const char *fmt, + va_list ap + ) +{ + FILE f; + int len; + + f._flag = _IOWRT+_IOSTRG; + f._ptr = str; + f._cnt = 32767; + len = _doprnt(fmt, ap, &f); + *f._ptr = 0; + return (len); +} +#endif |