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