libsodium 1.0.8
[tomato.git] / release / src / router / ntpc / ntpc.c
blob845f5cf0f65816dc933af178e00b8e59cd1d1858
1 /*
3 ntpc/ntpsync
4 Copyright (C) 2006-2009 Jonathan Zarate
6 Licensed under GNU GPL v2 or later.
8 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <netdb.h>
15 #include <arpa/inet.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <signal.h>
20 #include <syslog.h>
22 #include <bcmnvram.h>
23 #include <shutils.h>
24 #include <shared.h>
27 #define I_MISC 0
28 #define I_ORTIME 6
29 #define I_TXTIME 10
31 #define TIMEFIX 2208988800UL
34 static void add_word(char *buffer, const char *word, int max)
36 if ((*buffer != 0) && (buffer[strlen(buffer) - 1] != ' '))
37 strlcat(buffer, " ", max);
38 strlcat(buffer, word, max);
41 // 0 = ok, 1 = failed, 2 = permanent failure
42 static int ntpc(struct in_addr addr)
44 uint32_t packet[12];
45 struct timeval txtime;
46 struct timeval rxtime;
47 struct timeval tv;
48 uint32_t u;
49 uint32_t txn;
50 int fd;
51 fd_set fds;
52 int len;
53 struct sockaddr_in sa;
54 char s[64], q[128];
55 time_t t;
56 time_t ntpt;
57 time_t diff;
59 memset(&sa, 0, sizeof(sa));
60 sa.sin_addr = addr;
61 sa.sin_port = htons(123);
62 sa.sin_family = AF_INET;
64 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
65 printf("Unable to create a socket\n");
66 return 1;
69 if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
70 printf("Unable to connect\n");
72 else {
73 memset(&packet, 0, sizeof(packet));
74 packet[I_MISC] = htonl((4 << 27) | (3 << 24)); // VN=v4 | mode=3 (client)
75 // packet[I_MISC] = htonl((3 << 27) | (3 << 24)); // VN=v3 | mode=3 (client)
76 gettimeofday(&txtime, NULL);
77 packet[I_TXTIME] = txn = htonl(txtime.tv_sec + TIMEFIX);
78 send(fd, packet, sizeof(packet), 0);
80 FD_ZERO(&fds);
81 FD_SET(fd, &fds);
82 tv.tv_sec = 3; // no more than 3 seconds
83 tv.tv_usec = 0;
84 if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) {
85 printf("Timeout\n");
87 else {
88 len = recv(fd, packet, sizeof(packet), 0);
89 if (len != sizeof(packet)) {
90 printf("Invalid packet size\n");
92 else {
93 gettimeofday(&rxtime, NULL);
95 u = ntohl(packet[0]);
97 _dprintf("u = 0x%08x\n", u);
98 _dprintf("LI = %u\n", u >> 30);
99 _dprintf("VN = %u\n", (u >> 27) & 0x07);
100 _dprintf("mode = %u\n", (u >> 24) & 0x07);
101 _dprintf("stratum = %u\n", (u >> 16) & 0xFF);
102 _dprintf("poll interval = %u\n", (u >> 8) & 0xFF);
103 _dprintf("precision = %u\n", u & 0xFF);
105 if ((u & 0x07000000) != 0x04000000) { // mode != 4 (server)
106 printf("Invalid response\n");
108 else {
109 close(fd);
111 // notes:
112 // - Windows' ntpd returns vn=3, stratum=0
114 if ((u & 0x00FF0000) == 0) { // stratum == 0
115 printf("Received stratum=0\n");
116 if (!nvram_match("ntp_kiss_ignore", "1")) {
117 return 2;
121 ntpt = ntohl(packet[I_TXTIME]) - TIMEFIX;
122 t = (rxtime.tv_sec - txtime.tv_sec) >> 1;
123 diff = (ntpt - rxtime.tv_sec) + t;
125 _dprintf("txtime = %ld\n", txtime.tv_sec);
126 _dprintf("rxtime = %ld\n", rxtime.tv_sec);
127 _dprintf("ntpt = %ld\n", ntpt);
128 _dprintf("rtt/2 = %ld\n", t);
129 _dprintf("diff = %ld\n", diff);
131 if (diff != 0) {
132 gettimeofday(&tv, NULL);
133 tv.tv_sec += diff;
134 // tv.tv_usec = 0;// sorry, I'm not a time geek :P
135 settimeofday(&tv, NULL);
137 _dprintf("new = %lu\n", tv.tv_sec);
139 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&tv.tv_sec));
140 sprintf(q, "Time Updated: %s [%s%lds]", s, diff > 0 ? "+" : "", diff);
142 else {
143 t = time(0);
144 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
145 sprintf(q, "Time: %s, no change was needed.", s);
147 printf("\n\n%s\n", q);
148 syslog(LOG_INFO, q);
149 return 0;
155 close(fd);
156 return 1;
160 // -----------------------------------------------------------------------------
163 static int ntpc_main(int argc, char **argv)
165 struct hostent *he;
166 struct in_addr ia;
167 const char *s;
168 int i;
170 for (i = 1; i < argc; ++i) {
171 if ((he = gethostbyname(argv[i])) != NULL) {
172 memcpy(&ia, he->h_addr_list[0], sizeof(ia));
173 s = inet_ntoa(ia);
174 if (strcmp(s, argv[i]) == 0) printf("Trying %s: ", s);
175 else printf("Trying %s [%s]:", argv[i], s);
176 if (ntpc(ia) == 0) return 0;
178 else {
179 printf("Unable to resolve: %s\n", argv[i]);
183 if (argc < 2) {
184 printf("Usage: ntpc <server>\n");
187 return 1;
191 // -----------------------------------------------------------------------------
194 static int ntpsync_main(int argc, char **argv)
196 char *servers, *t, *p;
197 char *addr[10];
198 char *ips;
199 char *nvkiss;
200 int count;
201 int i;
202 struct hostent *he;
203 struct in_addr ia;
204 int retries;
205 int nu;
206 char s[512];
207 time_t tt;
208 struct tm *tms;
209 enum { USER, INIT, CRON } mode;
211 nu = nvram_get_int("ntp_updates");
213 mode = USER;
214 if (argc == 2) {
215 if (strcmp(argv[1], "--cron") == 0) { // try for a few minutes
216 if (nu <= 0) {
217 eval("cru", "d", "ntpsync");
218 return 0;
220 mode = CRON;
222 else if (strcmp(argv[1], "--init") == 0) { // try forever, schedule cron job
223 if (fork() != 0) return 0;
224 mode = INIT;
226 else {
227 return 1;
230 else if (argc != 1) {
231 return 1;
234 _dprintf("[ntpsync %ld] start\n", get_uptime());
236 if ((get_wan_proto() != WP_DISABLED) && (mode != INIT) &&
237 (!check_wanup()) && (nvram_match("ntp_tdod", "0"))) {
238 _dprintf("WAN is down, not updating.");
239 return 1;
242 srand(time(0));
243 retries = 0;
245 while (1) {
246 _dprintf("[ntpsync %ld] while\n", get_uptime());
248 count = 0;
250 servers = p = strdup(nvram_safe_get("ntp_server"));
252 if (!servers) {
253 printf("Not enough memory\n");
254 return 1;
256 while ((count < 10) && ((t = strsep(&p, " ")) != NULL)) {
257 if (*t != 0) addr[count++] = t;
259 if (count == 0) addr[count++] = "pool.ntp.org";
261 while (count > 0) {
262 i = (rand() % count);
263 _dprintf("[ntpsync] i=%d addr=%s\n", i, addr[i]);
265 if ((he = gethostbyname(addr[i])) != NULL) {
266 memcpy(&ia, he->h_addr_list[0], sizeof(ia));
267 ips = inet_ntoa(ia);
268 _dprintf("ip = %s\n", ips);
270 nvkiss = nvram_safe_get("ntp_kiss");
271 if (find_word(nvkiss, ips)) {
272 _dprintf("kiss: %s\n", ips);
274 else {
275 switch (ntpc(ia)) {
276 case 0:
277 _dprintf("[ntpsync] %ld OK\n", get_uptime());
278 if (mode == INIT) {
279 tt = time(0);
280 if ((nu > 0) && ((tms = localtime(&tt)) != NULL)) {
282 // add some randomness to make the servers happier / avoid the xx:00 rush
283 sprintf(s, "%d ", (tms->tm_min + 20 + (rand() % 20)) % 60);
285 // schedule every nu hours
286 for (i = 0; i < 24; ++i) {
287 if ((i % nu) == 0) sprintf(s + strlen(s), "%s%d", i ? "," : "", (i + tms->tm_hour + 1) % 24);
289 strcat(s, " * * * ntpsync --cron");
290 eval("cru", "a", "ntpsync", s);
294 // make sure access restriction is ok
295 eval("rcheck");
296 _dprintf("[ntpsync] %ld exit\n", get_uptime());
297 return 0;
298 case 2:
299 while ((nvkiss) && (strlen(nvkiss) > 128)) nvkiss = strchr(nvkiss + 1, ' ');
300 if (nvkiss) strlcpy(s, nvkiss, sizeof(s));
301 else s[0] = 0;
302 add_word(s, ips, sizeof(s));
303 nvram_set("ntp_kiss", s);
304 syslog(LOG_WARNING, "Received a kiss of death packet from %s (%s).", addr[i], ips);
305 break;
310 addr[i] = addr[--count];
312 free(servers);
314 if (mode == USER) break;
315 if ((mode == CRON) && (retries == 5)) break;
317 if (++retries > 300) retries = 300; // 5m
318 _dprintf("[ntpsync %ld] sleep=%d\n", get_uptime(), retries);
319 sleep(retries);
322 _dprintf("[ntpsync] %ld exit\n", get_uptime());
323 return 1;
326 // -----------------------------------------------------------------------------
329 static int ntpstep_main(int argc, char **argv)
331 struct timeval tv;
332 char s[256];
334 if (argc != 2) {
335 printf("Usage: ntpstep <seconds>\n");
336 return 1;
339 gettimeofday(&tv, NULL);
340 tv.tv_sec += atol(argv[1]);
341 settimeofday(&tv, NULL);
343 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&tv.tv_sec));
344 printf("%s\n", s);
345 return 0;
349 // -----------------------------------------------------------------------------
351 int main(int argc, char **argv)
353 openlog("ntpc", LOG_PID, LOG_USER);
354 if (!nvram_contains_word("log_events", "ntp")) {
355 setlogmask(LOG_MASK(LOG_EMERG)); // can't set to 0
358 if (strstr(argv[0], "ntpsync") != NULL) return ntpsync_main(argc, argv);
359 if (strstr(argv[0], "ntpstep") != NULL) return ntpstep_main(argc, argv);
361 return ntpc_main(argc, argv);
367 1 2 3
368 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
369 +---+-----+-----+---------------+---------------+----------------+
370 |LI | VN |Mode | Stratum | Poll | Precision | 0
371 +---+-----+-----+---------------+---------------+----------------+
372 | Root Delay | 1
373 +----------------------------------------------------------------+
374 | Root Dispersion | 2
375 +----------------------------------------------------------------+
376 | Reference Identifier | 3
377 +----------------------------------------------------------------+
378 | Reference Timestamp (64) | 4
379 +----------------------------------------------------------------+
380 | Originate Timestamp (64) | 6
381 +----------------------------------------------------------------+
382 | Receive Timestamp (64) | 8
383 +----------------------------------------------------------------+
384 | Transmit Timestamp (64) | 10
385 +----------------------------------------------------------------+
386 | Key Identifier (optional) (32) | 12
387 +----------------------------------------------------------------+
388 | Message Digest (optional) (128) | 13+
389 +----------------------------------------------------------------+
391 timestamp:
392 since 1900
394 1970-1900:
395 25,567 days can be converted to one of these units:
396 * 2,208,988,800 seconds
397 * 36,816,480 minutes
398 * 613,608 hours
399 * 3652 weeks (rounded down)
401 refs:
402 http://www.faqs.org/rfcs/rfc2030.html
403 http://www.ntp.org/ntpfaq/NTP-s-algo.htm