diff options
-rw-r--r-- | toys/pending/sntp.c | 124 |
1 files changed, 83 insertions, 41 deletions
diff --git a/toys/pending/sntp.c b/toys/pending/sntp.c index 446e3f75..057a30b6 100644 --- a/toys/pending/sntp.c +++ b/toys/pending/sntp.c @@ -4,18 +4,33 @@ * * See https://www.ietf.org/rfc/rfc4330.txt -USE_SNTP(NEWTOY(sntp, "sp:", TOYFLAG_USR|TOYFLAG_BIN)) + modes: oneshot display, oneshot set, persist, serve, multi + + verify source addr + serve time + - precision -6 + - source LOCL + - copy source packet transmit to originate (5->3), 2=4=5 + wait for multicast updates + uniclient: retry at poll interval + +USE_SNTP(NEWTOY(sntp, "m:Sp:asdD[!as]", TOYFLAG_USR|TOYFLAG_BIN)) config SNTP bool "sntp" default n help - usage: sntp [-sm] [-p PORT] SERVER... + usage: sntp [-saS] [-dD[-m ADDRESS] [-p PORT] [SERVER] - Simple Network Time Protocol client, set system clock from a server. + Simple Network Time Protocol client. Query SERVER and display time. -p Use PORT (default 123) - -s Serer + -s Set system clock suddenly + -a Adjust system clock gradually + -S Serve time instead of querying (bind to SERVER address if specified) + -m Wait for updates from multicast ADDRESS (RFC 4330 says use 224.0.1.1) + -d Daemonize (run in background re-querying ) + -D Daemonize but stay in foreground: re-query time every 1000 seconds */ #define FOR_sntp @@ -52,12 +67,6 @@ int xrecvwait(int fd, char *buf, int len, union socksaddr *sa, int timeout) return len; } -/// hardwired getaddrinfo() variant for what we want here. -static struct addrinfo *gai(char *server) -{ - return xgetaddrinfo(server, TT.p, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, 0); -} - // Get time and return ntptime (saving timespec in pointer if not null) // NTP time is high 32 bits = seconds since 1970 (blame RFC 868), low 32 bits // fraction of a second. @@ -69,7 +78,7 @@ static unsigned long long lunchtime(struct timespec *television) if (television) *television = tv; - // Unix time is 1970 but RFCs 868 and 958 said 1900, so add seconds 1900->1970 + // Unix time is 1970 but RFCs 868 and 958 said 1900 so add seconds 1900->1970 // If they'd done a 34/30 bit split the Y2036 problem would be centuries // from now and still give us nanosecond accuracy, but no... return ((tv.tv_sec+SEVENTIES)<<32)+(((long long)tv.tv_nsec)<<32)/1000000000; @@ -79,9 +88,9 @@ static unsigned long long lunchtime(struct timespec *television) static void doublyso(unsigned long long now, struct timespec *tv) { // Y2036 fixup: if time wrapped, it's in the future - tv->tv_sec = (now>>32) + (1L<<32)*!(now&(1L<<63)); + tv->tv_sec = (now>>32) + (1LL<<32)*!(now&(1LL<<63)); tv->tv_sec -= SEVENTIES; // Force signed math for Y2038 fixup - tv->tv_nsec = ((now&((1L<<32)-1))*1000000000)>>32; + tv->tv_nsec = ((now&0xFFFFFFFF)*1000000000)>>32; } // return difference between two timespecs in nanosecs @@ -104,28 +113,63 @@ static void nanomove(struct timespec *ts, long long offset) ts->tv_nsec = nano; } -int multicast = 0; +static void server_packet(char *buf, unsigned long long ref) +{ + *buf = 0x24; + buf[1] = 3; + buf[2] = 10; + buf[3] = 253; + strcpy(buf+12, "LOCL"); + pktime[3] = ref; + pktime[2] = pktime[4] = pktime[5] = newtime; +} void sntp_main(void) { struct timespec tv, tv2; unsigned long long *pktime = (void *)toybuf, now, then, before; long long diff; + long diffsecs, diffnano; struct addrinfo *ai; union socksaddr sa; - int fd, attempts; + int fd, tries = 0; - if (!FLAG(s) && !*toys.optargs) error_exit("Need -s or SERVER"); + if (!(FLAG(S)||FLAG(m)) && !*toys.optargs) + error_exit("Need -Sm or SERVER address"); // Lookup address and open server or client UDP socket if (!TT.p || !*TT.p) TT.p = "123"; - ai = gai(*toys.optargs); - // When root, bind to local server address - if (!getuid()) fd = xbind(gai("")); - else fd = xsocket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); + ai = xgetaddrinfo(*toys.optargs, TT.p, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, + AI_PASSIVE*!*toys.optargs); + + if (FLAG(d)) daemon(0,0); + + // Act as server if necessary + if (FLAG(S)|FLAG(m)) { + fd = xbind(ai); + if (TT.m) { + struct ip_mreq group; + + // subscribe to multicast group + memset(&group, 0, sizeof(group)); + group.imr_multiaddr.s_addr = inet_addr(addr); + xsetsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&group, + sizeof(group)); + } + } else fd = xsocket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); + +// Sm - loop waiting for input +// Dd - loop polling with probe +// else poll 3 times +// if SmDd loop + + for (;;) { + + // Daemon mode isn't limited to 3 tries + if (FLAG(d) || FLAG(D)) { + then = retry_timeout; + } else if (++tries>3) break; - // Try 3 times - for (attempts = 0; attempts < 3; attempts++) { // Prepare outgoing NTP packet memset(toybuf, 0, 48); *toybuf = 0xe3; // li = 3 (unsynchronized), version = 4, mode = 3 (client) @@ -138,7 +182,7 @@ void sntp_main(void) while (now<then) { // TODO: confirm sa matches if (48 == xrecvwait(fd, toybuf, sizeof(toybuf), &sa, then-now)) { - if (multicast || pktime[3] == SWAP_BE64(before)) break; + if (TT.m || pktime[3] == SWAP_BE64(before)) break; } now = millitime(); } @@ -146,29 +190,27 @@ void sntp_main(void) } lunchtime(&tv2); -//printf("before) %ld %ld\n", (long)tv.tv_sec, (long)tv.tv_nsec); -//printf("after) %ld %ld\n", (long)tv2.tv_sec, (long)tv2.tv_nsec); - // determine midpoint of packet transit time according to local clock // (simple calculation: assume each direction took same time so midpoint // is time reported by other clock) diff = nanodiff(&tv, &tv2)/2; -//printf("halfoff = %lld %llx\n", diff, diff); nanomove(&tv, diff); -//printf("midpoint) %ld %ld\n", (long)tv.tv_sec, (long)tv.tv_nsec); doublyso(SWAP_BE64(pktime[5]), &tv2); - diff = nanodiff(&tv2, &tv); -//printf("server) %ld %ld\n", (long)tv2.tv_sec, (long)tv2.tv_nsec); -//printf("offby = %lld\n", diff); - -//int i; -//for (i=0; i<48; ) { -// printf("%02x", toybuf[i]); -// if (!(++i&15)) printf("\n"); -//} - - format_iso_time(toybuf, sizeof(toybuf)-1, &tv); - printf("%s offset %c%d.%09d secs\n", toybuf, (diff<0) ? '-' : '+', - abs(diff/1000000000), abs(diff%1000000000)); + diff = nanodiff(&tv, &tv2); + diffsecs = diff/1000000000; + diffnano = diff%1000000000; + if (FLAG(s)) { + clock_gettime(CLOCK_REALTIME, &tv); + nanomove(&tv, diff); + if (clock_settime(CLOCK_REALTIME, &tv)) perror_exit("clock_settime"); + } else if (FLAG(a)) { + struct timeval tv = {diffsecs, diffnano/1000}; + + if (adjtime(&tv, 0)) perror_exit("adjtime"); + } else { + format_iso_time(toybuf, sizeof(toybuf)-1, &tv2); + printf("%s offset %c%d.%09d secs\n", toybuf, (diff<0) ? '-' : '+', + abs(diffsecs), abs(diffnano)); + } } |