Tomato 1.26 beta (1786)
[tomato.git] / release / src / router / ntpc / ntpc.c
blob1eff8a4c6bd8ec89045afb0e8fc3673e06b8e18e
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 _dprintf(args...) do { } while (0);
28 // #define _dprintf(args...) cprintf(args)
31 #define I_MISC 0
32 #define I_ORTIME 6
33 #define I_TXTIME 10
35 #define TIMEFIX 2208988800UL
38 static void add_word(char *buffer, const char *word, int max)
40 if ((*buffer != 0) && (buffer[strlen(buffer) - 1] != ' '))
41 strlcat(buffer, " ", max);
42 strlcat(buffer, word, max);
45 // 0 = ok, 1 = failed, 2 = permanent failure
46 static int ntpc(struct in_addr addr)
48 uint32_t packet[12];
49 struct timeval txtime;
50 struct timeval rxtime;
51 struct timeval tv;
52 uint32_t u;
53 uint32_t txn;
54 int fd;
55 fd_set fds;
56 int len;
57 struct sockaddr_in sa;
58 char s[64], q[128];
59 time_t t;
60 time_t ntpt;
61 time_t diff;
63 memset(&sa, 0, sizeof(sa));
64 sa.sin_addr = addr;
65 sa.sin_port = htons(123);
66 sa.sin_family = AF_INET;
68 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
69 printf("Unable to create a socket\n");
70 return 1;
73 if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
74 printf("Unable to connect\n");
76 else {
77 memset(&packet, 0, sizeof(packet));
78 packet[I_MISC] = htonl((4 << 27) | (3 << 24)); // VN=v4 | mode=3 (client)
79 // packet[I_MISC] = htonl((3 << 27) | (3 << 24)); // VN=v3 | mode=3 (client)
80 gettimeofday(&txtime, NULL);
81 packet[I_TXTIME] = txn = htonl(txtime.tv_sec + TIMEFIX);
82 send(fd, packet, sizeof(packet), 0);
84 FD_ZERO(&fds);
85 FD_SET(fd, &fds);
86 tv.tv_sec = 3; // no more than 3 seconds
87 tv.tv_usec = 0;
88 if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) {
89 printf("Timeout\n");
91 else {
92 len = recv(fd, packet, sizeof(packet), 0);
93 if (len != sizeof(packet)) {
94 printf("Invalid packet size\n");
96 else {
97 gettimeofday(&rxtime, NULL);
99 u = ntohl(packet[0]);
101 _dprintf("u = 0x%08x\n", u);
102 _dprintf("LI = %u\n", u >> 30);
103 _dprintf("VN = %u\n", (u >> 27) & 0x07);
104 _dprintf("mode = %u\n", (u >> 24) & 0x07);
105 _dprintf("stratum = %u\n", (u >> 16) & 0xFF);
106 _dprintf("poll interval = %u\n", (u >> 8) & 0xFF);
107 _dprintf("precision = %u\n", u & 0xFF);
109 if ((u & 0x07000000) != 0x04000000) { // mode != 4 (server)
110 printf("Invalid response\n");
112 else {
113 close(fd);
115 // notes:
116 // - Windows' ntpd returns vn=3, stratum=0
118 if ((u & 0x00FF0000) == 0) { // stratum == 0
119 printf("Received stratum=0\n");
120 if (!nvram_match("ntp_kiss_ignore", "1")) {
121 return 2;
125 ntpt = ntohl(packet[I_TXTIME]) - TIMEFIX;
126 t = (rxtime.tv_sec - txtime.tv_sec) >> 1;
127 diff = (ntpt - rxtime.tv_sec) + t;
129 _dprintf("txtime = %ld\n", txtime.tv_sec);
130 _dprintf("rxtime = %ld\n", rxtime.tv_sec);
131 _dprintf("ntpt = %ld\n", ntpt);
132 _dprintf("rtt/2 = %ld\n", t);
133 _dprintf("diff = %ld\n", diff);
135 if (diff != 0) {
136 gettimeofday(&tv, NULL);
137 tv.tv_sec += diff;
138 // tv.tv_usec = 0;// sorry, I'm not a time geek :P
139 settimeofday(&tv, NULL);
141 _dprintf("new = %lu\n", tv.tv_sec);
143 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&tv.tv_sec));
144 sprintf(q, "Time Updated: %s [%s%lds]", s, diff > 0 ? "+" : "", diff);
145 printf("\n\n%s\n", q);
146 syslog(LOG_INFO, q);
148 else {
149 t = time(0);
150 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
151 printf("\n\n%s\nNo change was needed.\n", s);
153 return 0;
159 close(fd);
160 return 1;
164 // -----------------------------------------------------------------------------
167 static int ntpc_main(int argc, char **argv)
169 struct hostent *he;
170 struct in_addr ia;
171 const char *s;
172 int i;
174 for (i = 1; i < argc; ++i) {
175 if ((he = gethostbyname(argv[i])) != NULL) {
176 memcpy(&ia, he->h_addr_list[0], sizeof(ia));
177 s = inet_ntoa(ia);
178 if (strcmp(s, argv[i]) == 0) printf("Trying %s: ", s);
179 else printf("Trying %s [%s]:", argv[i], s);
180 if (ntpc(ia) == 0) return 0;
182 else {
183 printf("Unable to resolve: %s\n", argv[i]);
187 if (argc < 2) {
188 printf("Usage: ntpc <server>\n");
191 return 1;
195 // -----------------------------------------------------------------------------
198 static int ntpsync_main(int argc, char **argv)
200 char *servers, *t, *p;
201 char *addr[10];
202 char *ips;
203 char *nvkiss;
204 int count;
205 int i;
206 struct hostent *he;
207 struct in_addr ia;
208 int retries;
209 int nu;
210 char s[512];
211 time_t tt;
212 struct tm *tms;
213 enum { USER, INIT, CRON } mode;
215 nu = nvram_get_int("ntp_updates");
217 mode = USER;
218 if (argc == 2) {
219 if (strcmp(argv[1], "--cron") == 0) { // try for a few minutes
220 if (nu <= 0) {
221 system("cru d ntpsync");
222 return 0;
224 mode = CRON;
226 else if (strcmp(argv[1], "--init") == 0) { // try forever, schedule cron job
227 if (fork() != 0) return 0;
228 mode = INIT;
230 else {
231 return 1;
234 else if (argc != 1) {
235 return 1;
238 _dprintf("[ntpsync %ld] start\n", get_uptime());
240 if ((get_wan_proto() != WP_DISABLED) && (mode != INIT) &&
241 (!check_wanup()) && (nvram_match("ntp_tdod", "0"))) {
242 _dprintf("WAN is down, not updating.");
243 return 1;
246 srand(time(0));
247 retries = 0;
249 while (1) {
250 _dprintf("[ntpsync %ld] while\n", get_uptime());
252 count = 0;
253 servers = p = strdup(nvram_safe_get("ntp_server"));
254 if (!servers) {
255 printf("Not enough memory\n");
256 return 1;
258 while ((count < 10) && ((t = strsep(&p, " ")) != NULL)) {
259 if (*t != 0) addr[count++] = t;
261 if (count == 0) addr[count++] = "pool.ntp.org";
263 while (count > 0) {
264 i = (rand() % count);
265 _dprintf("[ntpsync] i=%d addr=%s\n", i, addr[i]);
267 if ((he = gethostbyname(addr[i])) != NULL) {
268 memcpy(&ia, he->h_addr_list[0], sizeof(ia));
269 ips = inet_ntoa(ia);
270 _dprintf("ip = %s\n", ips);
272 nvkiss = nvram_safe_get("ntp_kiss");
273 if (find_word(nvkiss, ips)) {
274 _dprintf("kiss: %s\n", ips);
276 else {
277 switch (ntpc(ia)) {
278 case 0:
279 _dprintf("[ntpsync] %ld OK\n", get_uptime());
280 if (mode == INIT) {
281 tt = time(0);
282 if ((nu > 0) && ((tms = localtime(&tt)) != NULL)) {
284 // add some randomness to make the servers happier / avoid the xx:00 rush
285 sprintf(s, "cru a ntpsync \"%d ", (tms->tm_min + 20 + (rand() % 20)) % 60);
287 // schedule every nu hours
288 for (i = 0; i < 24; ++i) {
289 if ((i % nu) == 0) sprintf(s + strlen(s), "%s%d", i ? "," : "", (i + tms->tm_hour + 1) % 24);
291 strcat(s, " * * * ntpsync --cron\"");
292 system(s);
296 // make sure access restriction is ok
297 system("rcheck");
298 _dprintf("[ntpsync] %ld exit\n", get_uptime());
299 return 0;
300 case 2:
301 while ((nvkiss) && (strlen(nvkiss) > 128)) nvkiss = strchr(nvkiss + 1, ' ');
302 if (nvkiss) strlcpy(s, nvkiss, sizeof(s));
303 else s[0] = 0;
304 add_word(s, ips, sizeof(s));
305 nvram_set("ntp_kiss", s);
306 syslog(LOG_WARNING, "Received a kiss of death packet from %s (%s).", addr[i], ips);
307 break;
312 addr[i] = addr[--count];
314 free(servers);
316 if (mode == USER) break;
317 if ((mode == CRON) && (retries == 5)) break;
319 if (++retries > 300) retries = 300; // 5m
320 _dprintf("[ntpsync %ld] sleep=%d\n", get_uptime(), retries);
321 sleep(retries);
324 _dprintf("[ntpsync] %ld exit\n", get_uptime());
325 return 1;
328 // -----------------------------------------------------------------------------
331 static int ntpstep_main(int argc, char **argv)
333 struct timeval tv;
334 char s[256];
336 if (argc != 2) {
337 printf("Usage: ntpstep <seconds>\n");
338 return 1;
341 gettimeofday(&tv, NULL);
342 tv.tv_sec += atol(argv[1]);
343 settimeofday(&tv, NULL);
345 strftime(s, sizeof(s), "%a, %d %b %Y %H:%M:%S %z", localtime(&tv.tv_sec));
346 printf("%s\n", s);
347 return 0;
351 // -----------------------------------------------------------------------------
353 int main(int argc, char **argv)
355 openlog("ntpc", LOG_PID, LOG_USER);
356 if (!nvram_contains_word("log_events", "ntp")) {
357 setlogmask(LOG_MASK(LOG_EMERG)); // can't set to 0
360 if (strstr(argv[0], "ntpsync") != NULL) return ntpsync_main(argc, argv);
361 if (strstr(argv[0], "ntpstep") != NULL) return ntpstep_main(argc, argv);
363 return ntpc_main(argc, argv);
369 1 2 3
370 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
371 +---+-----+-----+---------------+---------------+----------------+
372 |LI | VN |Mode | Stratum | Poll | Precision | 0
373 +---+-----+-----+---------------+---------------+----------------+
374 | Root Delay | 1
375 +----------------------------------------------------------------+
376 | Root Dispersion | 2
377 +----------------------------------------------------------------+
378 | Reference Identifier | 3
379 +----------------------------------------------------------------+
380 | Reference Timestamp (64) | 4
381 +----------------------------------------------------------------+
382 | Originate Timestamp (64) | 6
383 +----------------------------------------------------------------+
384 | Receive Timestamp (64) | 8
385 +----------------------------------------------------------------+
386 | Transmit Timestamp (64) | 10
387 +----------------------------------------------------------------+
388 | Key Identifier (optional) (32) | 12
389 +----------------------------------------------------------------+
390 | Message Digest (optional) (128) | 13+
391 +----------------------------------------------------------------+
393 timestamp:
394 since 1900
396 1970-1900:
397 25,567 days can be converted to one of these units:
398 * 2,208,988,800 seconds
399 * 36,816,480 minutes
400 * 613,608 hours
401 * 3652 weeks (rounded down)
403 refs:
404 http://www.faqs.org/rfcs/rfc2030.html
405 http://www.ntp.org/ntpfaq/NTP-s-algo.htm