OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / ntpclient / ntpclient.c
1 /*
2  * ntpclient.c - NTP client
3  *
4  * Copyright 1997, 1999, 2000, 2003  Larry Doolittle  <larry@doolittle.boa.org>
5  * Last hack: July 5, 2003
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License (Version 2,
9  *  June 1991) as published by the Free Software Foundation.  At the
10  *  time of writing, that license was published by the FSF with the URL
11  *  http://www.gnu.org/copyleft/gpl.html, and is incorporated herein by
12  *  reference.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  Possible future improvements:
20  *      - Double check that the originate timestamp in the received packet
21  *        corresponds to what we sent.
22  *      - Verify that the return packet came from the host we think
23  *        we're talking to.  Not necessarily useful since UDP packets
24  *        are so easy to forge.
25  *      - Write more documentation  :-(
26  *
27  *  Compile with -D_PRECISION_SIOCGSTAMP if your machine really has it.
28  *  There are patches floating around to add this to Linux, but
29  *  usually you only get an answer to the nearest jiffy.
30  *  Hint for Linux hacker wannabes: look at the usage of get_fast_time()
31  *  in net/core/dev.c, and its definition in kernel/time.c .
32  *
33  *  If the compile gives you any flak, check below in the section
34  *  labelled "XXXX fixme - non-automatic build configuration".
35  */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netdb.h>     /* gethostbyname */
44 #include <arpa/inet.h>
45 #include <time.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #ifdef _PRECISION_SIOCGSTAMP
49 #include <sys/ioctl.h>
50 #endif
51
52 #define ENABLE_DEBUG
53
54 extern char *optarg;
55
56 /* XXXX fixme - non-automatic build configuration */
57 #ifdef linux
58 #include <sys/utsname.h>
59 #include <sys/time.h>
60 typedef u_int32_t __u32;
61 #include <sys/timex.h>
62 #else
63 extern struct hostent *gethostbyname(const char *name);
64 extern int h_errno;
65 #define herror(hostname) \
66         fprintf(stderr,"Error %d looking up hostname %s\n", h_errno,hostname)
67 typedef uint32_t __u32;
68 #endif
69
70 #define JAN_1970        0x83aa7e80      /* 2208988800 1970 - 1900 in seconds */
71 #define NTP_PORT (123)
72
73 /* How to multiply by 4294.967296 quickly (and not quite exactly)
74  * without using floating point or greater than 32-bit integers.
75  * If you want to fix the last 12 microseconds of error, add in
76  * (2911*(x))>>28)
77  */
78 #define NTPFRAC(x) ( 4294*(x) + ( (1981*(x))>>11 ) )
79
80 /* The reverse of the above, needed if we want to set our microsecond
81  * clock (via settimeofday) based on the incoming time in NTP format.
82  * Basically exact.
83  */
84 #define USEC(x) ( ( (x) >> 12 ) - 759 * ( ( ( (x) >> 10 ) + 32768 ) >> 16 ) )
85
86 /* Converts NTP delay and dispersion, apparently in seconds scaled
87  * by 65536, to microseconds.  RFC1305 states this time is in seconds,
88  * doesn't mention the scaling.
89  * Should somehow be the same as 1000000 * x / 65536
90  */
91 #define sec2u(x) ( (x) * 15.2587890625 )
92
93 struct ntptime {
94         unsigned int coarse;
95         unsigned int fine;
96 };
97
98 /* prototype for function defined in phaselock.c */
99 int contemplate_data(unsigned int absolute, double skew, double errorbar, int freq);
100
101 /* prototypes for some local routines */
102 void send_packet(int usd);
103 int rfc1305print(uint32_t *data, struct ntptime *arrival);
104 void udp_handle(int usd, char *data, int data_len, struct sockaddr *sa_source, int sa_len);
105
106 /* variables with file scope
107  * (I know, bad form, but this is a short program) */
108 static uint32_t incoming_word[325];
109 #define incoming ((char *) incoming_word)
110 #define sizeof_incoming (sizeof(incoming_word)*sizeof(uint32_t))
111 static struct timeval time_of_send;
112 static int live=0;
113 static int set_clock=0;   /* non-zero presumably needs root privs */
114
115 /* when present, debug is a true global, shared with phaselock.c */
116 #ifdef ENABLE_DEBUG
117 int debug=0;
118 #define DEBUG_OPTION "d"
119 #else
120 #define debug 0
121 #define DEBUG_OPTION
122 #endif
123
124 int get_current_freq(void)
125 {
126         /* OS dependent routine to get the current value of clock frequency.
127          */
128 #ifdef linux
129         struct timex txc;
130         txc.modes=0;
131         if (__adjtimex(&txc) < 0) {
132                 perror("adjtimex"); exit(1);
133         }
134         return txc.freq;
135 #else
136         return 0;
137 #endif
138 }
139
140 int set_freq(int new_freq)
141 {
142         /* OS dependent routine to set a new value of clock frequency.
143          */
144 #ifdef linux
145         struct timex txc;
146         txc.modes = ADJ_FREQUENCY;
147         txc.freq = new_freq;
148         if (__adjtimex(&txc) < 0) {
149                 perror("adjtimex"); exit(1);
150         }
151         return txc.freq;
152 #else
153         return 0;
154 #endif
155 }
156
157 void send_packet(int usd)
158 {
159         __u32 data[12];
160         struct timeval now;
161 #define LI 0
162 #define VN 3
163 #define MODE 3
164 #define STRATUM 0
165 #define POLL 4 
166 #define PREC -6
167
168         if (debug) fprintf(stderr,"Sending ...\n");
169         if (sizeof(data) != 48) {
170                 fprintf(stderr,"size error\n");
171                 return;
172         }
173         bzero((char *) data,sizeof(data));
174         data[0] = htonl (
175                 ( LI << 30 ) | ( VN << 27 ) | ( MODE << 24 ) |
176                 ( STRATUM << 16) | ( POLL << 8 ) | ( PREC & 0xff ) );
177         data[1] = htonl(1<<16);  /* Root Delay (seconds) */
178         data[2] = htonl(1<<16);  /* Root Dispersion (seconds) */
179         gettimeofday(&now,NULL);
180         data[10] = htonl(now.tv_sec + JAN_1970); /* Transmit Timestamp coarse */
181         data[11] = htonl(NTPFRAC(now.tv_usec));  /* Transmit Timestamp fine   */
182         send(usd,data,48,0);
183         time_of_send=now;
184 }
185
186 void get_packet_timestamp(int usd, struct ntptime *udp_arrival_ntp)
187 {
188         struct timeval udp_arrival;
189 #ifdef _PRECISION_SIOCGSTAMP
190         if ( ioctl(usd, SIOCGSTAMP, &udp_arrival) < 0 ) {
191                 perror("ioctl-SIOCGSTAMP");
192                 gettimeofday(&udp_arrival,NULL);
193         }
194 #else
195         gettimeofday(&udp_arrival,NULL);
196 #endif
197         udp_arrival_ntp->coarse = udp_arrival.tv_sec + JAN_1970;
198         udp_arrival_ntp->fine   = NTPFRAC(udp_arrival.tv_usec);
199 }
200
201 void check_source(int data_len, struct sockaddr *sa_source, int sa_len)
202 {
203         /* This is where one could check that the source is the server we expect */
204         if (debug) {
205                 struct sockaddr_in *sa_in=(struct sockaddr_in *)sa_source;
206                 printf("packet of length %d received\n",data_len);
207                 if (sa_source->sa_family==AF_INET) {
208                         printf("Source: INET Port %d host %s\n",
209                                 ntohs(sa_in->sin_port),inet_ntoa(sa_in->sin_addr));
210                 } else {
211                         printf("Source: Address family %d\n",sa_source->sa_family);
212                 }
213         }
214 }
215
216 double ntpdiff( struct ntptime *start, struct ntptime *stop)
217 {
218         int a;
219         unsigned int b;
220         a = stop->coarse - start->coarse;
221         if (stop->fine >= start->fine) {
222                 b = stop->fine - start->fine;
223         } else {
224                 b = start->fine - stop->fine;
225                 b = ~b;
226                 a -= 1;
227         }
228         
229         return a*1.e6 + b * (1.e6/4294967296.0);
230 }
231
232 /* Does more than print, so this name is bogus.
233  * It also makes time adjustments, both sudden (-s)
234  * and phase-locking (-l).  */
235 /* return value is number of microseconds uncertainty in answer */
236 int rfc1305print(uint32_t *data, struct ntptime *arrival)
237 {
238 /* straight out of RFC-1305 Appendix A */
239         int li, vn, mode, stratum, poll, prec;
240         int delay, disp, refid;
241         struct ntptime reftime, orgtime, rectime, xmttime;
242         double el_time,st_time,skew1,skew2;
243         int freq;
244
245 #define Data(i) ntohl(((uint32_t *)data)[i])
246         li      = Data(0) >> 30 & 0x03;
247         vn      = Data(0) >> 27 & 0x07;
248         mode    = Data(0) >> 24 & 0x07;
249         stratum = Data(0) >> 16 & 0xff;
250         poll    = Data(0) >>  8 & 0xff;
251         prec    = Data(0)       & 0xff;
252         if (prec & 0x80) prec|=0xffffff00;
253         delay   = Data(1);
254         disp    = Data(2);
255         refid   = Data(3);
256         reftime.coarse = Data(4);
257         reftime.fine   = Data(5);
258         orgtime.coarse = Data(6);
259         orgtime.fine   = Data(7);
260         rectime.coarse = Data(8);
261         rectime.fine   = Data(9);
262         xmttime.coarse = Data(10);
263         xmttime.fine   = Data(11);
264 #undef Data
265
266         if (set_clock) {   /* you'd better be root, or ntpclient will crash! */
267                 struct timeval tv_set;
268                 /* it would be even better to subtract half the slop */
269                 tv_set.tv_sec  = xmttime.coarse - JAN_1970;
270                 /* divide xmttime.fine by 4294.967296 */
271                 tv_set.tv_usec = USEC(xmttime.fine);
272                 if (settimeofday(&tv_set,NULL)<0) {
273                         perror("settimeofday");
274                         exit(1);
275                 }
276                 if (debug) {
277                         printf("set time to %lu.%.6lu\n", tv_set.tv_sec, tv_set.tv_usec);
278                 }
279         }
280
281         if (debug) {
282         printf("LI=%d  VN=%d  Mode=%d  Stratum=%d  Poll=%d  Precision=%d\n",
283                 li, vn, mode, stratum, poll, prec);
284         printf("Delay=%.1f  Dispersion=%.1f  Refid=%u.%u.%u.%u\n",
285                 sec2u(delay),sec2u(disp),
286                 refid>>24&0xff, refid>>16&0xff, refid>>8&0xff, refid&0xff);
287         printf("Reference %u.%.10u\n", reftime.coarse, reftime.fine);
288         printf("Originate %u.%.10u\n", orgtime.coarse, orgtime.fine);
289         printf("Receive   %u.%.10u\n", rectime.coarse, rectime.fine);
290         printf("Transmit  %u.%.10u\n", xmttime.coarse, xmttime.fine);
291         printf("Our recv  %u.%.10u\n", arrival->coarse, arrival->fine);
292         }
293         el_time=ntpdiff(&orgtime,arrival);   /* elapsed */
294         st_time=ntpdiff(&rectime,&xmttime);  /* stall */
295         skew1=ntpdiff(&orgtime,&rectime);
296         skew2=ntpdiff(&xmttime,arrival);
297         freq=get_current_freq();
298         if (debug) {
299         printf("Total elapsed: %9.2f\n"
300                "Server stall:  %9.2f\n"
301                "Slop:          %9.2f\n",
302                 el_time, st_time, el_time-st_time);
303         printf("Skew:          %9.2f\n"
304                "Frequency:     %9d\n"
305                " day   second     elapsed    stall     skew  dispersion  freq\n",
306                 (skew1-skew2)/2, freq);
307         }
308         /* Not the ideal order for printing, but we want to be sure
309          * to do all the time-sensitive thinking (and time setting)
310          * before we start the output, especially fflush() (which
311          * could be slow).  Of course, if debug is turned on, speed
312          * has gone down the drain anyway. */
313         if (live) {
314                 int new_freq;
315                 new_freq = contemplate_data(arrival->coarse, (skew1-skew2)/2,
316                         el_time+sec2u(disp), freq);
317                 if (!debug && new_freq != freq) set_freq(new_freq);
318         }
319         printf("%d %.5d.%.3d  %8.1f %8.1f  %8.1f %8.1f %9d\n",
320                 arrival->coarse/86400, arrival->coarse%86400,
321                 arrival->fine/4294967, el_time, st_time,
322                 (skew1-skew2)/2, sec2u(disp), freq);
323         fflush(stdout);
324         return(el_time-st_time);
325 }
326
327 void stuff_net_addr(struct in_addr *p, char *hostname)
328 {
329         struct hostent *ntpserver;
330         ntpserver=gethostbyname(hostname);
331         if (ntpserver == NULL) {
332                 herror(hostname);
333                 exit(1);
334         }
335         if (ntpserver->h_length != 4) {
336                 fprintf(stderr,"oops %d\n",ntpserver->h_length);
337                 exit(1);
338         }
339         memcpy(&(p->s_addr),ntpserver->h_addr_list[0],4);
340 }
341
342 void setup_receive(int usd, unsigned int interface, short port)
343 {
344         struct sockaddr_in sa_rcvr;
345         bzero((char *) &sa_rcvr, sizeof(sa_rcvr));
346         sa_rcvr.sin_family=AF_INET;
347         sa_rcvr.sin_addr.s_addr=htonl(interface);
348         sa_rcvr.sin_port=htons(port);
349         if(bind(usd,(struct sockaddr *) &sa_rcvr,sizeof(sa_rcvr)) == -1) {
350                 fprintf(stderr,"could not bind to udp port %d\n",port);
351                 perror("bind");
352                 exit(1);
353         }
354         listen(usd,3);
355 }
356
357 void setup_transmit(int usd, char *host, short port)
358 {
359         struct sockaddr_in sa_dest;
360         bzero((char *) &sa_dest, sizeof(sa_dest));
361         sa_dest.sin_family=AF_INET;
362         stuff_net_addr(&(sa_dest.sin_addr),host);
363         sa_dest.sin_port=htons(port);
364         if (connect(usd,(struct sockaddr *)&sa_dest,sizeof(sa_dest))==-1)
365                 {perror("connect");exit(1);}
366 }
367
368 int primary_loop(int usd, int num_probes, int interval, int goodness)
369 {
370         fd_set fds;
371         struct sockaddr sa_xmit;
372         int i, pack_len, sa_xmit_len, probes_sent, error;
373         struct timeval to;
374         struct ntptime udp_arrival_ntp;
375
376         if (debug) printf("Listening...\n");
377
378         probes_sent=0;
379         sa_xmit_len=sizeof(sa_xmit);
380         to.tv_sec=0;
381         to.tv_usec=0;
382         for (;;) {
383                 FD_ZERO(&fds);
384                 FD_SET(usd,&fds);
385                 i=select(usd+1,&fds,NULL,NULL,&to);  /* Wait on read or error */
386                 if ((i!=1)||(!FD_ISSET(usd,&fds))) {
387                         if (i==EINTR) continue;
388                         if (i<0) perror("select");
389                         if (to.tv_sec == 0) {
390                                 if (probes_sent >= num_probes &&
391                                         num_probes != 0) break;
392                                 send_packet(usd);
393                                 ++probes_sent;
394                                 to.tv_sec=interval;
395                                 to.tv_usec=0;
396                         }       
397                         continue;
398                 }
399                 pack_len=recvfrom(usd,incoming,sizeof_incoming,0,
400                                   &sa_xmit,&sa_xmit_len);
401                 error = goodness+1;
402                 if (pack_len<0) {
403                         perror("recvfrom");
404                 } else if (pack_len>0 && (unsigned)pack_len<sizeof_incoming){
405                         get_packet_timestamp(usd, &udp_arrival_ntp);
406                         check_source(pack_len, &sa_xmit, sa_xmit_len);
407                         error = rfc1305print(incoming_word, &udp_arrival_ntp);
408                         /* udp_handle(usd,incoming,pack_len,&sa_xmit,sa_xmit_len); */
409                 } else {
410                         printf("Ooops.  pack_len=%d\n",pack_len);
411                         fflush(stdout);
412                 }
413                 if ( error < goodness && goodness != 0) return 0;
414                 if (probes_sent >= num_probes && num_probes != 0) break;
415         }
416         return -1;
417 }
418
419 void do_replay(void)
420 {
421         char line[100];
422         int n, day, freq, absolute;
423         float sec, el_time, st_time, disp;
424         double skew, errorbar;
425         int simulated_freq = 0;
426         unsigned int last_fake_time = 0;
427         double fake_delta_time = 0.0;
428
429         while (fgets(line,sizeof(line),stdin)) {
430                 n=sscanf(line,"%d %f %f %f %lf %f %d",
431                         &day, &sec, &el_time, &st_time, &skew, &disp, &freq);
432                 if (n==7) {
433                         fputs(line,stdout);
434                         absolute=day*86400+(int)sec;
435                         errorbar=el_time+disp;
436                         if (debug) printf("contemplate %u %.1f %.1f %d\n",
437                                 absolute,skew,errorbar,freq);
438                         if (last_fake_time==0) simulated_freq=freq;
439                         fake_delta_time += (absolute-last_fake_time)*((double)(freq-simulated_freq))/65536;
440                         if (debug) printf("fake %f %d \n", fake_delta_time, simulated_freq);
441                         skew += fake_delta_time;
442                         freq = simulated_freq;
443                         last_fake_time=absolute;
444                         simulated_freq = contemplate_data(absolute, skew, errorbar, freq);
445                 } else {
446                         fprintf(stderr,"Replay input error\n");
447                         exit(2);
448                 }
449         }
450 }
451
452 void usage(char *argv0)
453 {
454         fprintf(stderr,
455         "Usage: %s [-c count] [-d] [-g goodness] -h hostname [-i interval]\n"
456         "\t[-l] [-p port] [-r] [-s] \n",
457         argv0);
458 }
459
460 /* Copy each token in wordlist delimited by space into word */
461 #define foreach(word, wordlist, next) \
462         for (next = &wordlist[strspn(wordlist, ",")], \
463              strncpy(word, next, sizeof(word)), \
464              word[strcspn(word, ",")] = '\0', \
465              word[sizeof(word) - 1] = '\0', \
466              next = strchr(next, ','); \
467              strlen(word); \
468              next = next ? &next[strspn(next, ",")] : "", \
469              strncpy(word, next, sizeof(word)), \
470              word[strcspn(word, ",")] = '\0', \
471              word[sizeof(word) - 1] = '\0', \
472              next = strchr(next, ','))
473
474 int main(int argc, char *argv[]) {
475         int usd;  /* socket */
476         int c;
477         /* These parameters are settable from the command line
478            the initializations here provide default behavior */
479         short int udp_local_port=0;   /* default of 0 means kernel chooses */
480         int cycle_time=600;           /* seconds */
481         int probe_count=0;            /* default of 0 means loop forever */
482         /* int debug=0; is a global above */
483         int goodness=0;
484         char *hostname=NULL;          /* must be set */
485         int replay=0;                 /* replay mode overrides everything */
486         char ntps[32], *next;
487         int res=1;
488
489         for (;;) {
490                 c = getopt( argc, argv, "c:" DEBUG_OPTION "g:h:i:lp:rs");
491                 if (c == EOF) break;
492                 switch (c) {
493                         case 'c':
494                                 probe_count = atoi(optarg);
495                                 break;
496 #ifdef ENABLE_DEBUG
497                         case 'd':
498                                 ++debug;
499                                 break;
500 #endif
501                         case 'g':
502                                 goodness = atoi(optarg);
503                                 break;
504                         case 'h':
505                                 hostname = optarg;
506                                 break;
507                         case 'i':
508                                 cycle_time = atoi(optarg);
509                                 break;
510                         case 'l':
511                                 live++;
512                                 break;
513                         case 'p':
514                                 udp_local_port = atoi(optarg);
515                                 break;
516                         case 'r':
517                                 replay++;
518                                 break;
519                         case 's':
520                                 set_clock++;
521                                 probe_count = 1;
522                                 break;
523                         default:
524                                 usage(argv[0]);
525                                 exit(1);
526                 }
527         }
528         if (replay) {
529                 do_replay();
530                 exit(0);
531         }
532         if (hostname == NULL) {
533                 usage(argv[0]);
534                 exit(1);
535         }
536         if (debug) {
537                 printf("Configuration:\n"
538                 "  -c probe_count %d\n"
539                 "  -d (debug)     %d\n"
540                 "  -g goodness    %d\n"
541                 "  -h hostname    %s\n"
542                 "  -i interval    %d\n"
543                 "  -l live        %d\n"
544                 "  -p local_port  %d\n"
545                 "  -s set_clock   %d\n",
546                 probe_count, debug, goodness, hostname, cycle_time,
547                 live, udp_local_port, set_clock );
548         }
549
550         foreach(ntps, hostname, next) {
551
552                 /* Startup sequence */
553                 if ((usd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1)
554                         {perror ("socket");exit(1);}
555
556                 setup_receive(usd, INADDR_ANY, udp_local_port);
557
558                 setup_transmit(usd, ntps, NTP_PORT);
559
560                 if (!primary_loop(usd, probe_count, cycle_time, goodness)) {
561                         close(usd);
562                         res=0;
563                         break;
564                 }
565                 else
566                         res=1;
567
568                 close(usd);
569         }
570         return 0;
571 }