/* $NetBSD: ntptrace.c,v 1.1.1.1 2000/03/29 12:38:55 simonb Exp $ */ /* * 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 */ #include #include #include #include #include #include #include #include #include #if defined(SYS_HPUX) #include #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" /* * 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 = NTP_MAXSTRATUM+1; /* 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 > NTP_MAXSTRATUM) { 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, 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