📄 ntpclient.c
字号:
/* * ntpclient.c - NTP client * * Copyright 1997, 1999, 2000, 2003 Larry Doolittle <larry@doolittle.boa.org> * Last hack: July 5, 2003 * * 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: * - Double check that the originate timestamp in the received packet * corresponds to what we sent. * - Verify that the return packet came from the host we think * we're talking to. Not necessarily useful since UDP packets * are so easy to forge. * - Write more documentation :-( * * Compile with -D_PRECISION_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 "XXXX fixme - non-automatic build configuration". */#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#define ENABLE_DEBUGextern char *optarg;/* XXXX fixme - non-automatic build configuration */#ifdef linux#include <sys/utsname.h>#include <sys/time.h>typedef u_int32_t __u32;#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)typedef uint32_t __u32;#endif#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 settimeofday) 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. RFC1305 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;};/* prototype for function defined in phaselock.c */int contemplate_data(unsigned int absolute, double skew, double errorbar, int freq);/* prototypes for some local routines */void send_packet(int usd);int rfc1305print(uint32_t *data, struct ntptime *arrival);void udp_handle(int usd, char *data, int data_len, struct sockaddr *sa_source, int sa_len);/* variables with file scope * (I know, bad form, but this is a short program) */static uint32_t incoming_word[325];#define incoming ((char *) incoming_word)#define sizeof_incoming (sizeof(incoming_word)*sizeof(uint32_t))static struct timeval time_of_send;static int live=0;static int set_clock=0; /* non-zero presumably needs root privs *//* when present, debug is a true global, shared with phaselock.c */#ifdef ENABLE_DEBUGint debug=1;#define DEBUG_OPTION "d"#else#define debug 0#define DEBUG_OPTION#endifint 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}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}void send_packet(int usd){ __u32 data[12]; struct timeval now;#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; } bzero((char *) data,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) */ gettimeofday(&now,NULL); data[10] = htonl(now.tv_sec + JAN_1970); /* Transmit Timestamp coarse */ data[11] = htonl(NTPFRAC(now.tv_usec)); /* Transmit Timestamp fine */ send(usd,data,48,0); time_of_send=now;}void get_packet_timestamp(int usd, struct ntptime *udp_arrival_ntp){ struct timeval udp_arrival;#ifdef _PRECISION_SIOCGSTAMP if ( ioctl(usd, SIOCGSTAMP, &udp_arrival) < 0 ) { perror("ioctl-SIOCGSTAMP"); gettimeofday(&udp_arrival,NULL); }#else gettimeofday(&udp_arrival,NULL);#endif udp_arrival_ntp->coarse = udp_arrival.tv_sec + JAN_1970; udp_arrival_ntp->fine = NTPFRAC(udp_arrival.tv_usec);}void check_source(int data_len, struct sockaddr *sa_source, int sa_len){ /* This is where one could check that the source is the server we expect */ if (debug) { struct sockaddr_in *sa_in=(struct sockaddr_in *)sa_source; 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); } }}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);}typedef struct _TimeZoneInfo{ char* strTimeZone; int iOffset; }TimeZoneInfo;TimeZoneInfo g_tzinfo [] ={ {"GMT+1100",-39600}, {"GMT+1000",-36000}, {"GMT+0900",-32400}, {"GMT+0800",-28800}, {"GMT+0700",-25200}, {"GMT+0600",-21600}, {"GMT+0500",-18000}, {"GMT+0400",-14400}, {"GMT+0300",-10800}, {"GMT+0200",-7200}, {"GMT+0100",-3600}, {"GMT+0000",0}, {"GMT-0100",3600}, {"GMT-0200",7200}, {"GMT-0300",10800}, {"GMT-0400",14400}, {"GMT-0500",18000}, {"GMT-0600",21600}, {"GMT-0700",25200}, {"GMT-0800",28800}, {"GMT-0900",32400}, {"GMT-1000",36000}, {"GMT-1100",39600}, {"GMT-1200",43200}, {"0",0},};int SyncTimeZone(){ FILE *fp; char strtmp[256]; int iOffset = 0,i=0; memset(strtmp,0,sizeof(strtmp)); if ( (fp=fopen("/etc/timezone", "r")) == NULL ) return; fscanf(fp,"%s",strtmp); fclose(fp); for (i=0;strcmp(g_tzinfo[i].strTimeZone,"0");i++) { if (!strcmp(g_tzinfo[i].strTimeZone,strtmp)) return g_tzinfo[i].iOffset; } //setenv("TZ", strtmp, 1); return iOffset;}/* Does more than print, so this name is bogus. * It also makes time adjustments, both sudden (-s) * and phase-locking (-l). *//* return value is number of microseconds uncertainty in answer */int rfc1305print(uint32_t *data, struct ntptime *arrival){/* 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;#define Data(i) ntohl(((uint32_t *)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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -