Ditch the sysvinit stuff
[profile/ivi/iputils.git] / clockdiff.c
1 #include <time.h>
2 #include <sys/types.h>
3 #include <sys/param.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <math.h>
8 #include <string.h>
9 #include <sys/time.h>
10 #include <sys/timex.h>
11 #include <errno.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <netinet/ip.h>
15 #include <netinet/ip_icmp.h>
16 #define TSPTYPES
17 #include <protocols/timed.h>
18 #include <fcntl.h>
19 #include <netdb.h>
20 #include <arpa/inet.h>
21 #include <errno.h>
22 #include <linux/types.h>
23
24 void usage(void) __attribute__((noreturn));
25
26 #define MAX_HOSTNAMELEN NI_MAXHOST
27
28 /*
29  * Checksum routine for Internet Protocol family headers.
30  *
31  * This routine is very heavily used in the network
32  * code and should be modified for each CPU to be as fast as possible.
33  *
34  * This implementation is TAHOE version.
35  */
36
37 #undef  ADDCARRY
38 #define ADDCARRY(sum) { \
39         if (sum & 0xffff0000) { \
40                 sum &= 0xffff; \
41                 sum++; \
42         } \
43 }
44
45 int in_cksum(u_short *addr, int len)
46 {
47         union word {
48                 char    c[2];
49                 u_short s;
50         } u;
51         int sum = 0;
52
53         while (len > 0) {
54                 /*
55                  * add by words.
56                  */
57                 while ((len -= 2) >= 0) {
58                         if ((unsigned long)addr & 0x1) {
59                                 /* word is not aligned */
60                                 u.c[0] = *(char *)addr;
61                                 u.c[1] = *((char *)addr+1);
62                                 sum += u.s;
63                                 addr++;
64                         } else
65                                 sum += *addr++;
66                         ADDCARRY(sum);
67                 }
68                 if (len == -1)
69                         /*
70                          * Odd number of bytes.
71                          */
72                         u.c[0] = *(u_char *)addr;
73         }
74         if (len == -1) {
75                 /* The last mbuf has odd # of bytes. Follow the
76                    standard (the odd byte is shifted left by 8 bits) */
77                 u.c[1] = 0;
78                 sum += u.s;
79                 ADDCARRY(sum);
80         }
81         return (~sum & 0xffff);
82 }
83
84 #define ON              1
85 #define OFF             0
86
87 #define RANGE           1               /* best expected round-trip time, ms */
88 #define MSGS            50
89 #define TRIALS          10
90
91 #define GOOD            0
92 #define UNREACHABLE     2
93 #define NONSTDTIME      3
94 #define HOSTDOWN        0x7fffffff
95
96
97 int interactive = 0;
98 int id;
99 int sock;
100 int sock_raw;
101 struct sockaddr_in server;
102 int ip_opt_len = 0;
103
104 #define BIASP           43199999
105 #define BIASN           -43200000
106 #define MODULO          86400000
107 #define PROCESSING_TIME 0       /* ms. to reduce error in measurement */
108
109 #define PACKET_IN       1024
110
111 int measure_delta;
112 int measure_delta1;
113 static u_short seqno, seqno0, acked;
114 long rtt = 1000;
115 long min_rtt;
116 long rtt_sigma = 0;
117
118 /*
119  * Measures the differences between machines' clocks using
120  * ICMP timestamp messages.
121  */
122 int
123 measure(struct sockaddr_in * addr)
124 {
125         socklen_t length;
126         int msgcount;
127         int cc, count;
128         fd_set ready;
129         long sendtime, recvtime, histime,  histime1;
130         long min1, min2, diff;
131         long delta1, delta2;
132         struct timeval tv1, tout;
133         u_char packet[PACKET_IN], opacket[64];
134         struct icmphdr *icp = (struct icmphdr *) packet;
135         struct icmphdr *oicp = (struct icmphdr *) opacket;
136         struct iphdr *ip = (struct iphdr *) packet;
137
138         min1 = min2 = 0x7fffffff;
139         min_rtt = 0x7fffffff;
140         measure_delta = HOSTDOWN;
141         measure_delta1 = HOSTDOWN;
142
143 /* empties the icmp input queue */
144         FD_ZERO(&ready);
145
146 empty:
147         tout.tv_sec = tout.tv_usec = 0;
148         FD_SET(sock_raw, &ready);
149         if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
150                 length = sizeof(struct sockaddr_in);
151                 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
152                     (struct sockaddr *)NULL, &length);
153                 if (cc < 0)
154                         return -1;
155                 goto empty;
156         }
157
158         /*
159          * To measure the difference, select MSGS messages whose round-trip
160          * time is smaller than RANGE if ckrange is 1, otherwise simply
161          * select MSGS messages regardless of round-trip transmission time.
162          * Choose the smallest transmission time in each of the two directions.
163          * Use these two latter quantities to compute the delta between
164          * the two clocks.
165          */
166
167         length = sizeof(struct sockaddr_in);
168         oicp->type = ICMP_TIMESTAMP;
169         oicp->code = 0;
170         oicp->checksum = 0;
171         oicp->un.echo.id = id;
172         ((__u32*)(oicp+1))[0] = 0;
173         ((__u32*)(oicp+1))[1] = 0;
174         ((__u32*)(oicp+1))[2] = 0;
175         FD_ZERO(&ready);
176         msgcount = 0;
177
178         acked = seqno = seqno0 = 0;
179
180         for (msgcount = 0; msgcount < MSGS; ) {
181
182         /*
183          * If no answer is received for TRIALS consecutive times,
184          * the machine is assumed to be down
185          */
186                 if (seqno - acked > TRIALS)
187                         return HOSTDOWN;
188
189                 oicp->un.echo.sequence = ++seqno;
190                 oicp->checksum = 0;
191
192                 (void)gettimeofday (&tv1, (struct timezone *)0);
193                 *(__u32*)(oicp+1) = htonl((tv1.tv_sec % (24*60*60)) * 1000
194                                           + tv1.tv_usec / 1000);
195                 oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp) + 12);
196
197                 count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
198                                (struct sockaddr *)addr, sizeof(struct sockaddr_in));
199
200                 if (count < 0)
201                         return UNREACHABLE;
202
203                 for (;;) {
204                         FD_ZERO(&ready);
205                         FD_SET(sock_raw, &ready);
206                         {
207                           long tmo = rtt + rtt_sigma;
208                           tout.tv_sec =  tmo/1000;
209                           tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
210                         }
211
212                         if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
213                             (fd_set *)0, &tout)) <= 0)
214                                 break;
215
216                         (void)gettimeofday(&tv1, (struct timezone *)0);
217                         cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
218                             (struct sockaddr *)NULL, &length);
219
220                         if (cc < 0)
221                                 return(-1);
222
223                         icp = (struct icmphdr *)(packet + (ip->ihl << 2));
224                         if( icp->type == ICMP_TIMESTAMPREPLY &&
225                             icp->un.echo.id == id && icp->un.echo.sequence >= seqno0 &&
226                                                   icp->un.echo.sequence <= seqno) {
227                           if (acked < icp->un.echo.sequence)
228                             acked = icp->un.echo.sequence;
229
230                           recvtime = (tv1.tv_sec % (24*60*60)) * 1000 +
231                                      tv1.tv_usec / 1000;
232                           sendtime = ntohl(*(__u32*)(icp+1));
233                           diff = recvtime - sendtime;
234                 /*
235                  * diff can be less than 0 aroud midnight
236                  */
237                           if (diff < 0)
238                             continue;
239                           rtt = (rtt * 3 + diff)/4;
240                           rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
241                           msgcount++;
242                           histime = ntohl(((__u32*)(icp+1))[1]);
243                           histime1 = ntohl(((__u32*)(icp+1))[2]);
244                 /*
245                  * a hosts using a time format different from
246                  * ms. since midnight UT (as per RFC792) should
247                  * set the high order bit of the 32-bit time
248                  * value it transmits.
249                  */
250                         if ((histime & 0x80000000) != 0)
251                           return NONSTDTIME;
252
253                         if (interactive) {
254                           printf(".");
255                           fflush(stdout);
256                         }
257
258                         delta1 = histime - sendtime;
259                 /*
260                  * Handles wrap-around to avoid that around
261                  * midnight small time differences appear
262                  * enormous. However, the two machine's clocks
263                  * must be within 12 hours from each other.
264                  */
265                         if (delta1 < BIASN)
266                                 delta1 += MODULO;
267                         else if (delta1 > BIASP)
268                                 delta1 -= MODULO;
269
270                         delta2 = recvtime - histime;
271                         if (delta2 < BIASN)
272                                 delta2 += MODULO;
273                         else if (delta2 > BIASP)
274                                 delta2 -= MODULO;
275
276                         if (delta1 < min1)
277                                 min1 = delta1;
278                         if (delta2 < min2)
279                                 min2 = delta2;
280                         if (delta1 + delta2 < min_rtt) {
281                           min_rtt  = delta1 + delta2;
282                           measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
283                         }
284                         if (diff < RANGE) {
285                                 min1 = delta1;
286                                 min2 = delta2;
287                                 goto good_exit;
288                         }
289                       }
290                 }
291         }
292
293 good_exit:
294         measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
295         return GOOD;
296 }
297
298 char *myname, *hisname;
299
300 int
301 measure_opt(struct sockaddr_in * addr)
302 {
303         socklen_t length;
304         int msgcount;
305         int cc, count;
306         fd_set ready;
307         long sendtime, recvtime, histime, histime1;
308         long min1, min2, diff;
309         long delta1, delta2;
310         struct timeval tv1, tout;
311         u_char packet[PACKET_IN], opacket[64];
312         struct icmphdr *icp = (struct icmphdr *) packet;
313         struct icmphdr *oicp = (struct icmphdr *) opacket;
314         struct iphdr *ip = (struct iphdr *) packet;
315
316         min1 = min2 = 0x7fffffff;
317         min_rtt = 0x7fffffff;
318         measure_delta = HOSTDOWN;
319         measure_delta1 = HOSTDOWN;
320
321 /* empties the icmp input queue */
322         FD_ZERO(&ready);
323 empty:
324         tout.tv_sec = tout.tv_usec = 0;
325         FD_SET(sock_raw, &ready);
326         if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
327                 length = sizeof(struct sockaddr_in);
328                 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
329                     (struct sockaddr *)NULL, &length);
330                 if (cc < 0)
331                         return -1;
332                 goto empty;
333         }
334
335         /*
336          * To measure the difference, select MSGS messages whose round-trip
337          * time is smaller than RANGE if ckrange is 1, otherwise simply
338          * select MSGS messages regardless of round-trip transmission time.
339          * Choose the smallest transmission time in each of the two directions.
340          * Use these two latter quantities to compute the delta between
341          * the two clocks.
342          */
343
344         length = sizeof(struct sockaddr_in);
345         oicp->type = ICMP_ECHO;
346         oicp->code = 0;
347         oicp->checksum = 0;
348         oicp->un.echo.id = id;
349         ((__u32*)(oicp+1))[0] = 0;
350         ((__u32*)(oicp+1))[1] = 0;
351         ((__u32*)(oicp+1))[2] = 0;
352
353         FD_ZERO(&ready);
354         msgcount = 0;
355
356         acked = seqno = seqno0 = 0;
357
358         for (msgcount = 0; msgcount < MSGS; ) {
359
360         /*
361          * If no answer is received for TRIALS consecutive times,
362          * the machine is assumed to be down
363          */
364                 if ( seqno - acked > TRIALS) {
365                         errno = EHOSTDOWN;
366                         return HOSTDOWN;
367                 }
368                 oicp->un.echo.sequence = ++seqno;
369                 oicp->checksum = 0;
370
371                 gettimeofday (&tv1, NULL);
372                 ((__u32*)(oicp+1))[0] = htonl((tv1.tv_sec % (24*60*60)) * 1000
373                                               + tv1.tv_usec / 1000);
374                 oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp)+12);
375
376                 count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
377                                (struct sockaddr *)addr, sizeof(struct sockaddr_in));
378
379                 if (count < 0) {
380                         errno = EHOSTUNREACH;
381                         return UNREACHABLE;
382                 }
383
384                 for (;;) {
385                         FD_ZERO(&ready);
386                         FD_SET(sock_raw, &ready);
387                         {
388                                 long tmo = rtt + rtt_sigma;
389                                 tout.tv_sec =  tmo/1000;
390                                 tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
391                         }
392
393                         if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
394                             (fd_set *)0, &tout)) <= 0)
395                                 break;
396
397                         (void)gettimeofday(&tv1, (struct timezone *)0);
398                         cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
399                                       (struct sockaddr *)NULL, &length);
400
401                         if (cc < 0)
402                                 return(-1);
403
404                         icp = (struct icmphdr *)(packet + (ip->ihl << 2));
405                         if (icp->type == ICMP_ECHOREPLY &&
406                             packet[20] == IPOPT_TIMESTAMP &&
407                             icp->un.echo.id == id &&
408                             icp->un.echo.sequence >= seqno0 &&
409                             icp->un.echo.sequence <= seqno) {
410                                 int i;
411                                 __u8 *opt = packet+20;
412
413                                 if (acked < icp->un.echo.sequence)
414                                         acked = icp->un.echo.sequence;
415                                 if ((opt[3]&0xF) != IPOPT_TS_PRESPEC) {
416                                         fprintf(stderr, "Wrong timestamp %d\n", opt[3]&0xF);
417                                         return NONSTDTIME;
418                                 }
419                                 if (opt[3]>>4) {
420                                         if ((opt[3]>>4) != 1 || ip_opt_len != 4+3*8)
421                                                 fprintf(stderr, "Overflow %d hops\n", opt[3]>>4);
422                                 }
423                                 sendtime = recvtime = histime = histime1 = 0;
424                                 for (i=0; i < (opt[2]-5)/8; i++) {
425                                         __u32 *timep = (__u32*)(opt+4+i*8+4);
426                                         __u32 t = ntohl(*timep);
427
428                                         if (t & 0x80000000)
429                                                 return NONSTDTIME;
430
431                                         if (i == 0)
432                                                 sendtime = t;
433                                         if (i == 1)
434                                                 histime = histime1 = t;
435                                         if (i == 2) {
436                                                 if (ip_opt_len == 4+4*8)
437                                                         histime1 = t;
438                                                 else
439                                                         recvtime = t;
440                                         }
441                                         if (i == 3)
442                                                 recvtime = t;
443                                 }
444
445                                 if (!(sendtime&histime&histime1&recvtime)) {
446                                         fprintf(stderr, "wrong timestamps\n");
447                                         return -1;
448                                 }
449
450                                 diff = recvtime - sendtime;
451                                 /*
452                                  * diff can be less than 0 aroud midnight
453                                  */
454                                 if (diff < 0)
455                                         continue;
456                                 rtt = (rtt * 3 + diff)/4;
457                                 rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
458                                 msgcount++;
459
460                                 if (interactive) {
461                                         printf(".");
462                                         fflush(stdout);
463                                 }
464
465                                 delta1 = histime - sendtime;
466                                 /*
467                                  * Handles wrap-around to avoid that around
468                                  * midnight small time differences appear
469                                  * enormous. However, the two machine's clocks
470                                  * must be within 12 hours from each other.
471                                  */
472                                 if (delta1 < BIASN)
473                                         delta1 += MODULO;
474                                 else if (delta1 > BIASP)
475                                         delta1 -= MODULO;
476
477                                 delta2 = recvtime - histime1;
478                                 if (delta2 < BIASN)
479                                         delta2 += MODULO;
480                                 else if (delta2 > BIASP)
481                                         delta2 -= MODULO;
482
483                                 if (delta1 < min1)
484                                         min1 = delta1;
485                                 if (delta2 < min2)
486                                         min2 = delta2;
487                                 if (delta1 + delta2 < min_rtt) {
488                                         min_rtt  = delta1 + delta2;
489                                         measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
490                                 }
491                                 if (diff < RANGE) {
492                                         min1 = delta1;
493                                         min2 = delta2;
494                                         goto good_exit;
495                                 }
496                         }
497                 }
498         }
499
500 good_exit:
501         measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
502         return GOOD;
503 }
504
505
506 /*
507  * Clockdiff computes the difference between the time of the machine on
508  * which it is called and the time of the machines given as argument.
509  * The time differences measured by clockdiff are obtained using a sequence
510  * of ICMP TSTAMP messages which are returned to the sender by the IP module
511  * in the remote machine.
512  * In order to compare clocks of machines in different time zones, the time
513  * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
514  * If a hosts uses a different time format, it should set the high order
515  * bit of the 32-bit quantity it transmits.
516  * However, VMS apparently transmits the time in milliseconds since midnight
517  * local time (rather than GMT) without setting the high order bit.
518  * Furthermore, it does not understand daylight-saving time.  This makes
519  * clockdiff behaving inconsistently with hosts running VMS.
520  *
521  * In order to reduce the sensitivity to the variance of message transmission
522  * time, clockdiff sends a sequence of messages.  Yet, measures between
523  * two `distant' hosts can be affected by a small error. The error can, however,
524  * be reduced by increasing the number of messages sent in each measurement.
525  */
526
527 void
528 usage() {
529   fprintf(stderr, "Usage: clockdiff [-o] <host>\n");
530   exit(1);
531 }
532
533
534 int
535 main(int argc, char *argv[])
536 {
537         int measure_status;
538         struct hostent * hp;
539         char hostname[MAX_HOSTNAMELEN];
540         int s_errno = 0;
541
542         if (argc < 2) {
543                 if (setuid(getuid())) {
544                         perror("clockdiff: setuid");
545                         exit(-1);
546                 }
547                 usage();
548         }
549
550         sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
551         s_errno = errno;
552
553         if (setuid(getuid())) {
554                 perror("clockdiff: setuid");
555                 exit(-1);
556         }
557
558         if (argc == 3) {
559                 if (strcmp(argv[1], "-o") == 0) {
560                         ip_opt_len = 4 + 4*8;
561                         argv++;
562                 } else if (strcmp(argv[1], "-o1") == 0) {
563                         ip_opt_len = 4 + 3*8;
564                         argv++;
565                 } else
566                         usage();
567         } else if (argc != 2)
568                 usage();
569
570         if (sock_raw < 0)  {
571                 errno = s_errno;
572                 perror("clockdiff: socket");
573                 exit(1);
574         }
575
576         if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
577                 interactive = 1;
578
579         id = getpid();
580
581         (void)gethostname(hostname,sizeof(hostname));
582         hp = gethostbyname(hostname);
583         if (hp == NULL) {
584                 fprintf(stderr, "clockdiff: %s: my host not found\n", hostname);
585                 exit(1);
586         }
587         myname = strdup(hp->h_name);
588
589         hp = gethostbyname(argv[1]);
590         if (hp == NULL) {
591                 fprintf(stderr, "clockdiff: %s: host not found\n", argv[1]);
592                 exit(1);
593         }
594         hisname = strdup(hp->h_name);
595
596         memset(&server, 0, sizeof(server));
597         server.sin_family = hp->h_addrtype;
598         memcpy(&(server.sin_addr.s_addr), hp->h_addr, 4);
599
600         if (connect(sock_raw, (struct sockaddr*)&server, sizeof(server)) == -1) {
601                 perror("connect");
602                 exit(1);
603         }
604         if (ip_opt_len) {
605                 struct sockaddr_in myaddr;
606                 socklen_t addrlen = sizeof(myaddr);
607                 unsigned char rspace[ip_opt_len];
608
609                 memset(rspace, 0, sizeof(rspace));
610                 rspace[0] = IPOPT_TIMESTAMP;
611                 rspace[1] = ip_opt_len;
612                 rspace[2] = 5;
613                 rspace[3] = IPOPT_TS_PRESPEC;
614                 if (getsockname(sock_raw, (struct sockaddr*)&myaddr, &addrlen) == -1) {
615                         perror("getsockname");
616                         exit(1);
617                 }
618                 ((__u32*)(rspace+4))[0*2] = myaddr.sin_addr.s_addr;
619                 ((__u32*)(rspace+4))[1*2] = server.sin_addr.s_addr;
620                 ((__u32*)(rspace+4))[2*2] = myaddr.sin_addr.s_addr;
621                 if (ip_opt_len == 4+4*8) {
622                         ((__u32*)(rspace+4))[2*2] = server.sin_addr.s_addr;
623                         ((__u32*)(rspace+4))[3*2] = myaddr.sin_addr.s_addr;
624                 }
625
626                 if (setsockopt(sock_raw, IPPROTO_IP, IP_OPTIONS, rspace, ip_opt_len) < 0) {
627                         perror("ping: IP_OPTIONS (fallback to icmp tstamps)");
628                         ip_opt_len = 0;
629                 }
630         }
631
632         nice(-16);
633
634         if ((measure_status = (ip_opt_len ? measure_opt : measure)(&server)) < 0) {
635                 if (errno)
636                         perror("measure");
637                 else
638                         fprintf(stderr, "measure: unknown failure\n");
639                 exit(1);
640         }
641
642         switch (measure_status) {
643         case HOSTDOWN:
644                 fprintf(stderr, "%s is down\n", hisname);
645                 exit(1);
646         case NONSTDTIME:
647                 fprintf(stderr, "%s time transmitted in a non-standard format\n", hisname);
648                 exit(1);
649         case UNREACHABLE:
650                 fprintf(stderr, "%s is unreachable\n", hisname);
651                 exit(1);
652         default:
653                 break;
654         }
655
656
657         {
658                 time_t now = time(NULL);
659
660                 if (interactive)
661                         printf("\nhost=%s rtt=%ld(%ld)ms/%ldms delta=%dms/%dms %s", hisname,
662                                rtt, rtt_sigma, min_rtt,
663                                measure_delta, measure_delta1,
664                                ctime(&now));
665                 else
666                         printf("%ld %d %d\n", now, measure_delta, measure_delta1);
667         }
668         exit(0);
669 }