sntp.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /* sntp.c - sntp client and server
  2. *
  3. * Copyright 2019 Rob Landley <rob@landley.net>
  4. *
  5. * See https://www.ietf.org/rfc/rfc4330.txt
  6. modes: oneshot display, oneshot set, persist, serve, multi
  7. USE_SNTP(NEWTOY(sntp, ">1M :m :Sp:t#<0=1>16asdDqr#<4>17=10[!as]", TOYFLAG_USR|TOYFLAG_BIN))
  8. config SNTP
  9. bool "sntp"
  10. default y
  11. help
  12. usage: sntp [-saSdDq] [-r SHIFT] [-mM[ADDRESS]] [-p PORT] [SERVER]
  13. Simple Network Time Protocol client. Query SERVER and display time.
  14. -p Use PORT (default 123)
  15. -s Set system clock suddenly
  16. -a Adjust system clock gradually
  17. -S Serve time instead of querying (bind to SERVER address if specified)
  18. -m Wait for updates from multicast ADDRESS (RFC 4330 default 224.0.1.1)
  19. -M Multicast server on ADDRESS (default 224.0.0.1)
  20. -t TTL (multicast only, default 1)
  21. -d Daemonize (run in background re-querying )
  22. -D Daemonize but stay in foreground: re-query time every 1000 seconds
  23. -r Retry shift (every 1<<SHIFT seconds)
  24. -q Quiet (don't display time)
  25. */
  26. #define FOR_sntp
  27. #include "toys.h"
  28. #include <sys/timex.h>
  29. GLOBALS(
  30. long r, t;
  31. char *p, *m, *M;
  32. )
  33. // Seconds from 1900 to 1970, including appropriate leap days
  34. #define SEVENTIES 2208988800ULL
  35. // Get time and return ntptime (saving timespec in pointer if not null)
  36. // NTP time is high 32 bits = seconds since 1970 (blame RFC 868), low 32 bits
  37. // fraction of a second.
  38. // diff is how far off we think our clock is from reality (in nanoseconds)
  39. static unsigned long long lunchtime(struct timespec *television, long long diff)
  40. {
  41. struct timespec tv;
  42. clock_gettime(CLOCK_REALTIME, &tv);
  43. if (diff) nanomove(&tv, diff);
  44. if (television) *television = tv;
  45. // Unix time is 1970 but RFCs 868 and 958 said 1900 so add seconds 1900->1970
  46. // If they'd done a 34/30 bit split the Y2036 problem would be centuries
  47. // from now and still give us nanosecond accuracy, but no...
  48. return ((tv.tv_sec+SEVENTIES)<<32)+(((long long)tv.tv_nsec)<<32)/1000000000;
  49. }
  50. // convert ntptime back to struct timespec.
  51. static void doublyso(unsigned long long now, struct timespec *tv)
  52. {
  53. // Y2036 fixup: if time wrapped, it's in the future
  54. tv->tv_sec = (now>>32) + (1LL<<32)*!(now&(1LL<<63));
  55. tv->tv_sec -= SEVENTIES; // Force signed math for Y2038 fixup
  56. tv->tv_nsec = ((now&0xFFFFFFFF)*1000000000)>>32;
  57. }
  58. void sntp_main(void)
  59. {
  60. struct timespec tv, tv2;
  61. unsigned long long *pktime = (void *)toybuf, now, then, before QUIET;
  62. long long diff = 0;
  63. struct addrinfo *ai;
  64. union socksaddr sa;
  65. int fd, tries = 0;
  66. if (FLAG(d)) xvdaemon();
  67. if (FLAG(M)) toys.optflags |= FLAG_S;
  68. if (!(FLAG(S)||FLAG(m)) && !*toys.optargs)
  69. error_exit("Need -SMm or SERVER address");
  70. // Lookup address and open server or client UDP socket
  71. if (!TT.p || !*TT.p) TT.p = "123";
  72. ai = xgetaddrinfo(*toys.optargs, TT.p, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
  73. AI_PASSIVE*!*toys.optargs);
  74. // Act as server if necessary
  75. if (FLAG(S)||FLAG(m)) {
  76. fd = xbindany(ai);
  77. if (TT.m || TT.M) {
  78. struct ip_mreq group;
  79. int t = 0;
  80. // subscribe to multicast group
  81. memset(&group, 0, sizeof(group));
  82. group.imr_multiaddr.s_addr = inet_addr(TT.m ? TT.m : TT.M);
  83. xsetsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
  84. xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &t, 4);
  85. t = TT.t;
  86. xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &t, 4);
  87. }
  88. } else fd = xsocket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
  89. // -Sm = loop waiting for input
  90. // -Dd = loop polling time and waiting until next poll period
  91. // Otherwise poll up to 3 times to get 2 responses, then exit
  92. // loop sending/receiving packets
  93. for (;;) {
  94. now = millitime();
  95. // If we're in server or multicast client mode, don't poll
  96. if (FLAG(m) || FLAG(S)) then = -1;
  97. // daemon and oneshot modes send a packet each time through outer loop
  98. else {
  99. then = now + 3000;
  100. if (FLAG(d)||FLAG(D)||FLAG(M)) then = now + (1<<TT.r)*1000;
  101. // Send NTP query packet
  102. memset(toybuf, 0, 48);
  103. *toybuf = 0xe3; // li = 3 (unsynchronized), version = 4, mode = 3 (client)
  104. toybuf[2] = 8; // poll frequency 1<<8 = 256 seconds
  105. pktime[5] = SWAP_BE64(before = lunchtime(&tv, diff));
  106. xsendto(fd, toybuf, 48, ai->ai_addr);
  107. }
  108. // Loop receiving packets until it's time to send the next one.
  109. for (;;) {
  110. int strike;
  111. // Wait to receive a packet
  112. if (then>0 && then<(now = millitime())) break;;
  113. strike = xrecvwait(fd, toybuf, sizeof(toybuf), &sa, then-now);
  114. if (strike<1) {
  115. if (!(FLAG(S)||FLAG(m)||FLAG(D)||FLAG(d)) && ++tries == 3)
  116. error_exit("no reply from %s", *toys.optargs);
  117. break;
  118. }
  119. if (strike<48) continue;
  120. // Validate packet
  121. if (!FLAG(S) || FLAG(m)) {
  122. char buf[128];
  123. int mode = 7&*toybuf;
  124. // Is source address what we expect?
  125. xstrncpy(buf, ntop(ai->ai_addr), 128);
  126. strike = strcmp(buf, ntop((void *)&sa));
  127. // Does this reply's originate timestamp match the packet we sent?
  128. if (!FLAG(S) && !FLAG(m) && before != SWAP_BE64(pktime[3])) continue;
  129. // Ignore packets from wrong address or with wrong mode
  130. if (strike && !FLAG(S)) continue;
  131. if (!((FLAG(m) && mode==5) || (FLAG(S) && mode==3) ||
  132. (!FLAG(m) && !FLAG(S) && mode==4))) continue;
  133. }
  134. // If received a -S request packet, send server packet
  135. if (strike) {
  136. char *buf = toybuf+48;
  137. *buf = 0x24; // LI 0 VN 4 mode 4.
  138. buf[1] = 3; // stratum 3
  139. buf[2] = 10; // recommended retry every 1<<10=1024 seconds
  140. buf[3] = 250; // precision -6, minimum allowed
  141. strcpy(buf+12, "LOCL");
  142. pktime[6+3] = pktime[5]; // send back reference time they sent us
  143. // everything else is current time
  144. pktime[6+2] = pktime[6+4] = pktime[6+5] = SWAP_BE64(lunchtime(0, 0));
  145. xsendto(fd, buf, 48, (void *)&sa);
  146. // Got a time packet from a recognized server
  147. } else {
  148. int unset = !diff;
  149. // First packet: figure out how far off our clock is from what server
  150. // said and try again. Don't set clock, just record offset to use
  151. // generating second request. (We know this time is in the past
  152. // because transmission took time, but it's a start. And if time is
  153. // miraculously exact, don't loop.)
  154. lunchtime(&tv2, diff);
  155. diff = nanodiff(&tv, &tv2);
  156. if (unset && diff) break;
  157. // Second packet: determine midpoint of packet transit time according
  158. // to local clock, assuming each direction took same time so midpoint
  159. // is time server reported. The first tv was the adjusted time
  160. // we sent the packet at, tv2 is what server replied, so now diff
  161. // is round trip time.
  162. // What time did the server say and how far off are we?
  163. nanomove(&tv, diff/2);
  164. doublyso(SWAP_BE64(pktime[5]), &tv2);
  165. diff = nanodiff(&tv, &tv2);
  166. if (FLAG(s)) {
  167. // Do read/adjust/set to lose as little time as possible.
  168. clock_gettime(CLOCK_REALTIME, &tv2);
  169. nanomove(&tv2, diff);
  170. if (clock_settime(CLOCK_REALTIME, &tv2))
  171. perror_exit("clock_settime");
  172. } else if (FLAG(a)) {
  173. struct timex tx;
  174. // call adjtimex() to move the clock gradually
  175. nanomove(&tv2, diff);
  176. memset(&tx, 0, sizeof(struct timex));
  177. tx.offset = tv2.tv_sec*1000000+tv2.tv_nsec/1000;
  178. tx.modes = ADJ_OFFSET_SINGLESHOT;
  179. if (adjtimex(&tx) == -1) perror_exit("adjtimex");
  180. }
  181. // Display the time and offset
  182. if (!FLAG(q)) {
  183. format_iso_time(toybuf, sizeof(toybuf)-1, &tv2);
  184. printf("%s offset %c%lld.%09lld secs\n", toybuf, (diff<0) ? '-' : '+',
  185. llabs(diff/1000000000), llabs(diff%1000000000));
  186. }
  187. // If we're not in daemon mode, we're done. (Can't get here for -S.)
  188. if (!FLAG(d) && !FLAG(D)) return;
  189. }
  190. }
  191. }
  192. }