📄 ntpclient.c
字号:
/* * ntpclient.c - NTP client * * Copyright 1997, 1999, 2000, 2003, 2006, 2007 Larry Doolittle <larry@doolittle.boa.org> * Last hack: December 30, 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (Version 2, * June 1991) as published by the Free Software Foundation. At the * time of writing, that license was published by the FSF with the URL * http://www.gnu.org/copyleft/gpl.html, and is incorporated herein by * reference. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Possible future improvements: * - Write more documentation :-( * - Support leap second processing * - Support IPv6 * - Support multiple (interleaved) servers * * Compile with -DPRECISION_SIOCGSTAMP if your machine really has it. * There are patches floating around to add this to Linux, but * usually you only get an answer to the nearest jiffy. * Hint for Linux hacker wannabes: look at the usage of get_fast_time() * in net/core/dev.c, and its definition in kernel/time.c . * * If the compile gives you any flak, check below in the section * labelled "XXX fixme - non-automatic build configuration". */#define _POSIX_C_SOURCE 199309#ifdef USE_OBSOLETE_GETTIMEOFDAY#define _BSD_SOURCE#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h> /* gethostbyname */#include <arpa/inet.h>#include <time.h>#include <unistd.h>#include <errno.h>#ifdef PRECISION_SIOCGSTAMP#include <sys/ioctl.h>#endif#ifdef USE_OBSOLETE_GETTIMEOFDAY#include <sys/time.h>#endif#include "ntpclient.h"/* Default to the RFC-4330 specified value */#ifndef MIN_INTERVAL#define MIN_INTERVAL 15#endif#ifdef ENABLE_DEBUG#define DEBUG_OPTION "d"int debug=0;#else#define DEBUG_OPTION#endif#ifdef ENABLE_REPLAY#define REPLAY_OPTION "r"#else#define REPLAY_OPTION#endifextern char *optarg; /* according to man 2 getopt */#include <stdint.h>typedef uint32_t u32; /* universal for C99 *//* typedef u_int32_t u32; older Linux installs? *//* XXX fixme - non-automatic build configuration */#ifdef __linux__#include <sys/utsname.h>#include <sys/time.h>#include <sys/timex.h>#elseextern struct hostent *gethostbyname(const char *name);extern int h_errno;#define herror(hostname) \ fprintf(stderr,"Error %d looking up hostname %s\n", h_errno,hostname)#endif/* end configuration for host systems */#define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */#define NTP_PORT (123)/* How to multiply by 4294.967296 quickly (and not quite exactly) * without using floating point or greater than 32-bit integers. * If you want to fix the last 12 microseconds of error, add in * (2911*(x))>>28) */#define NTPFRAC(x) ( 4294*(x) + ( (1981*(x))>>11 ) )/* The reverse of the above, needed if we want to set our microsecond * clock (via clock_settime) based on the incoming time in NTP format. * Basically exact. */#define USEC(x) ( ( (x) >> 12 ) - 759 * ( ( ( (x) >> 10 ) + 32768 ) >> 16 ) )/* Converts NTP delay and dispersion, apparently in seconds scaled * by 65536, to microseconds. RFC-1305 states this time is in seconds, * doesn't mention the scaling. * Should somehow be the same as 1000000 * x / 65536 */#define sec2u(x) ( (x) * 15.2587890625 )struct ntptime { unsigned int coarse; unsigned int fine;};struct ntp_control { u32 time_of_send[2]; int live; int set_clock; /* non-zero presumably needs root privs */ int probe_count; int cycle_time; int goodness; int cross_check; char serv_addr[4];};/* prototypes for some local routines */static void send_packet(int usd, u32 time_sent[2]);static int rfc1305print(u32 *data, struct ntptime *arrival, struct ntp_control *ntpc, int *error);/* static void udp_handle(int usd, char *data, int data_len, struct sockaddr *sa_source, int sa_len); */static int get_current_freq(void){ /* OS dependent routine to get the current value of clock frequency. */#ifdef __linux__ struct timex txc; txc.modes=0; if (__adjtimex(&txc) < 0) { perror("adjtimex"); exit(1); } return txc.freq;#else return 0;#endif}static int set_freq(int new_freq){ /* OS dependent routine to set a new value of clock frequency. */#ifdef __linux__ struct timex txc; txc.modes = ADJ_FREQUENCY; txc.freq = new_freq; if (__adjtimex(&txc) < 0) { perror("adjtimex"); exit(1); } return txc.freq;#else return 0;#endif}static void set_time(struct ntptime *new){#ifndef USE_OBSOLETE_GETTIMEOFDAY /* POSIX 1003.1-2001 way to set the system clock */ struct timespec tv_set; /* it would be even better to subtract half the slop */ tv_set.tv_sec = new->coarse - JAN_1970; /* divide xmttime.fine by 4294.967296 */ tv_set.tv_nsec = USEC(new->fine)*1000; if (clock_settime(CLOCK_REALTIME, &tv_set)<0) { perror("clock_settime"); exit(1); } if (debug) { printf("set time to %lu.%.9lu\n", tv_set.tv_sec, tv_set.tv_nsec); }#else /* Traditional Linux way to set the system clock */ struct timeval tv_set; /* it would be even better to subtract half the slop */ tv_set.tv_sec = new->coarse - JAN_1970; /* divide xmttime.fine by 4294.967296 */ tv_set.tv_usec = USEC(new->fine); if (settimeofday(&tv_set,NULL)<0) { perror("settimeofday"); exit(1); } if (debug) { printf("set time to %lu.%.6lu\n", tv_set.tv_sec, tv_set.tv_usec); }#endif}static void ntpc_gettime(u32 *time_coarse, u32 *time_fine){#ifndef USE_OBSOLETE_GETTIMEOFDAY /* POSIX 1003.1-2001 way to get the system time */ struct timespec now; clock_gettime(CLOCK_REALTIME, &now); *time_coarse = now.tv_sec + JAN_1970; *time_fine = NTPFRAC(now.tv_nsec/1000);#else /* Traditional Linux way to get the system time */ struct timeval now; gettimeofday(&now, NULL); *time_coarse = now.tv_sec + JAN_1970; *time_fine = NTPFRAC(now.tv_usec);#endif}static void send_packet(int usd, u32 time_sent[2]){ u32 data[12];#define LI 0#define VN 3#define MODE 3#define STRATUM 0#define POLL 4#define PREC -6 if (debug) fprintf(stderr,"Sending ...\n"); if (sizeof data != 48) { fprintf(stderr,"size error\n"); return; } memset(data,0,sizeof data); data[0] = htonl ( ( LI << 30 ) | ( VN << 27 ) | ( MODE << 24 ) | ( STRATUM << 16) | ( POLL << 8 ) | ( PREC & 0xff ) ); data[1] = htonl(1<<16); /* Root Delay (seconds) */ data[2] = htonl(1<<16); /* Root Dispersion (seconds) */ ntpc_gettime(time_sent, time_sent+1); data[10] = htonl(time_sent[0]); /* Transmit Timestamp coarse */ data[11] = htonl(time_sent[1]); /* Transmit Timestamp fine */ send(usd,data,48,0);}static void get_packet_timestamp(int usd, struct ntptime *udp_arrival_ntp){#ifdef PRECISION_SIOCGSTAMP /* XXX broken */ struct timeval udp_arrival; if ( ioctl(usd, SIOCGSTAMP, &udp_arrival) < 0 ) { perror("ioctl-SIOCGSTAMP"); gettimeofday(&udp_arrival,NULL); } udp_arrival_ntp->coarse = udp_arrival.tv_sec + JAN_1970; udp_arrival_ntp->fine = NTPFRAC(udp_arrival.tv_usec);#else (void) usd; /* not used */ ntpc_gettime(&udp_arrival_ntp->coarse, &udp_arrival_ntp->fine);#endif}static int check_source(int data_len, struct sockaddr *sa_source, unsigned int sa_len, struct ntp_control *ntpc){ struct sockaddr_in *sa_in=(struct sockaddr_in *)sa_source; (void) sa_len; /* not used */ if (debug) { printf("packet of length %d received\n",data_len); if (sa_source->sa_family==AF_INET) { printf("Source: INET Port %d host %s\n", ntohs(sa_in->sin_port),inet_ntoa(sa_in->sin_addr)); } else { printf("Source: Address family %d\n",sa_source->sa_family); } } /* we could check that the source is the server we expect, but * Denis Vlasenko recommends against it: multihomed hosts get it * wrong too often. */#if 0 if (memcmp(ntpc->serv_addr, &(sa_in->sin_addr), 4)!=0) { return 1; /* fault */ }#else (void) ntpc; /* not used */#endif if (NTP_PORT != ntohs(sa_in->sin_port)) { return 1; /* fault */ } return 0;}static double ntpdiff( struct ntptime *start, struct ntptime *stop){ int a; unsigned int b; a = stop->coarse - start->coarse; if (stop->fine >= start->fine) { b = stop->fine - start->fine; } else { b = start->fine - stop->fine; b = ~b; a -= 1; } return a*1.e6 + b * (1.e6/4294967296.0);}/* Does more than print, so this name is bogus. * It also makes time adjustments, both sudden (-s) * and phase-locking (-l). * sets *error to the number of microseconds uncertainty in answer * returns 0 normally, 1 if the message fails sanity checks */static int rfc1305print(u32 *data, struct ntptime *arrival, struct ntp_control *ntpc, int *error){/* straight out of RFC-1305 Appendix A */ int li, vn, mode, stratum, poll, prec; int delay, disp, refid; struct ntptime reftime, orgtime, rectime, xmttime; double el_time,st_time,skew1,skew2; int freq;#ifdef ENABLE_DEBUG const char *drop_reason=NULL;#endif#define Data(i) ntohl(((u32 *)data)[i]) li = Data(0) >> 30 & 0x03; vn = Data(0) >> 27 & 0x07; mode = Data(0) >> 24 & 0x07; stratum = Data(0) >> 16 & 0xff; poll = Data(0) >> 8 & 0xff; prec = Data(0) & 0xff; if (prec & 0x80) prec|=0xffffff00; delay = Data(1); disp = Data(2); refid = Data(3); reftime.coarse = Data(4); reftime.fine = Data(5); orgtime.coarse = Data(6); orgtime.fine = Data(7); rectime.coarse = Data(8); rectime.fine = Data(9); xmttime.coarse = Data(10); xmttime.fine = Data(11);#undef Data if (debug) { printf("LI=%d VN=%d Mode=%d Stratum=%d Poll=%d Precision=%d\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -