main.c revision 1.3 1 /* $NetBSD: main.c,v 1.3 2012/02/01 07:46:23 kardel Exp $ */
2
3 #include <config.h>
4
5 #include "main.h"
6 #include "kod_management.h"
7 #include "networking.h"
8 #include "utilities.h"
9 #include "log.h"
10
11 int ai_fam_pref = AF_UNSPEC;
12
13 struct key *keys = NULL;
14
15 void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode);
16 int sntp_main (int argc, char **argv);
17 int on_wire (struct addrinfo *host, struct addrinfo *bcastaddr);
18 int set_time (double offset);
19
20 #define NORMALIZE_TIMEVAL(tv) \
21 do { \
22 while ((tv).tv_usec < 0) { \
23 (tv).tv_usec += 1000000; \
24 (tv).tv_sec--; \
25 } \
26 while ((tv).tv_usec > 999999) { \
27 (tv).tv_usec -= 1000000; \
28 (tv).tv_sec++; \
29 } \
30 } while (0)
31
32
33 /*
34 * The actual main function.
35 */
36 int
37 sntp_main (
38 int argc,
39 char **argv
40 )
41 {
42 register int c;
43 struct kod_entry *reason = NULL;
44 int optct;
45 /* boolean, u_int quiets gcc4 signed overflow warning */
46 u_int sync_data_suc;
47 struct addrinfo **bcastaddr = NULL;
48 struct addrinfo **resh = NULL;
49 struct addrinfo *ai;
50 int resc;
51 int kodc;
52 int ow_ret;
53 int bcast = 0;
54 char *hostname;
55
56 optct = optionProcess(&sntpOptions, argc, argv);
57 argc -= optct;
58 argv += optct;
59
60 /* Initialize logging system */
61 init_logging();
62 if (HAVE_OPT(LOGFILE))
63 open_logfile(OPT_ARG(LOGFILE));
64
65 msyslog(LOG_NOTICE, "Started sntp");
66
67 /* IPv6 available? */
68 if (isc_net_probeipv6() != ISC_R_SUCCESS) {
69 ai_fam_pref = AF_INET;
70 #ifdef DEBUG
71 printf("No ipv6 support available, forcing ipv4\n");
72 #endif
73 } else {
74 /* Check for options -4 and -6 */
75 if (HAVE_OPT(IPV4))
76 ai_fam_pref = AF_INET;
77 else if (HAVE_OPT(IPV6))
78 ai_fam_pref = AF_INET6;
79 }
80
81 /* Parse config file if declared TODO */
82
83 /*
84 * If there's a specified KOD file init KOD system. If not use
85 * default file. For embedded systems with no writable
86 * filesystem, -K /dev/null can be used to disable KoD storage.
87 */
88 if (HAVE_OPT(KOD))
89 kod_init_kod_db(OPT_ARG(KOD));
90 else
91 kod_init_kod_db("/var/db/ntp-kod");
92
93 if (HAVE_OPT(KEYFILE))
94 auth_init(OPT_ARG(KEYFILE), &keys);
95
96 #ifdef EXERCISE_KOD_DB
97 add_entry("192.168.169.170", "DENY");
98 add_entry("192.168.169.171", "DENY");
99 add_entry("192.168.169.172", "DENY");
100 add_entry("192.168.169.173", "DENY");
101 add_entry("192.168.169.174", "DENY");
102 delete_entry("192.168.169.174", "DENY");
103 delete_entry("192.168.169.172", "DENY");
104 delete_entry("192.168.169.170", "DENY");
105 if ((kodc = search_entry("192.168.169.173", &reason)) == 0)
106 printf("entry for 192.168.169.173 not found but should have been!\n");
107 else
108 free(reason);
109 #endif
110
111 /* Considering employing a variable that prevents functions of doing anything until
112 * everything is initialized properly
113 */
114 resc = resolve_hosts((const char **)argv, argc, &resh, ai_fam_pref);
115 if (resc < 1) {
116 printf("Unable to resolve hostname(s)\n");
117 return -1;
118 }
119 bcast = ENABLED_OPT(BROADCAST);
120 if (bcast) {
121 const char * myargv[2];
122
123 myargv[0] = OPT_ARG(BROADCAST);
124 myargv[1] = NULL;
125 bcast = resolve_hosts(myargv, 1, &bcastaddr, ai_fam_pref);
126 }
127
128 /* Select a certain ntp server according to simple criteria? For now
129 * let's just pay attention to previous KoDs.
130 */
131 sync_data_suc = FALSE;
132 for (c = 0; c < resc && !sync_data_suc; c++) {
133 ai = resh[c];
134 do {
135 hostname = addrinfo_to_str(ai);
136 if ((kodc = search_entry(hostname, &reason)) == 0) {
137 if (is_reachable(ai)) {
138 ow_ret = on_wire(ai, bcast ? bcastaddr[0] : NULL);
139 if (0 == ow_ret)
140 sync_data_suc = TRUE;
141 }
142 } else {
143 printf("%d prior KoD%s for %s, skipping.\n",
144 kodc, (kodc > 1) ? "s" : "", hostname);
145 free(reason);
146 }
147 free(hostname);
148 ai = ai->ai_next;
149 } while (NULL != ai);
150 freeaddrinfo(resh[c]);
151 }
152 free(resh);
153
154 if (!sync_data_suc)
155 return 1;
156 return 0;
157 }
158
159 static union {
160 struct pkt pkt;
161 char buf[1500];
162 } rbuf;
163
164 #define r_pkt rbuf.pkt
165
166 int
167 generate_pkt (
168 struct pkt *x_pkt,
169 const struct timeval *tv_xmt,
170 int key_id,
171 struct key *pkt_key
172 )
173 {
174 l_fp xmt;
175 int pkt_len = LEN_PKT_NOMAC;
176 memset(x_pkt, 0, sizeof(struct pkt));
177 TVTOTS(tv_xmt, &xmt);
178 HTONL_FP(&xmt, &(x_pkt->xmt));
179 x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
180 x_pkt->ppoll = 8;
181 /* FIXME! Modus broadcast + adr. check -> bdr. pkt */
182 set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, 4, 3);
183 if (pkt_key != NULL) {
184 int mac_size = 20; /* max room for MAC */
185 x_pkt->exten[0] = htonl(key_id);
186 mac_size = make_mac((char *)x_pkt, pkt_len, mac_size, pkt_key, (char *)&x_pkt->exten[1]);
187 if (mac_size)
188 pkt_len += mac_size + 4;
189 }
190 return pkt_len;
191 }
192
193 int
194 handle_pkt (
195 int rpktl,
196 struct pkt *rpkt,
197 struct addrinfo *host
198 )
199 {
200 struct timeval tv_dst;
201 int sw_case, digits;
202 char *hostname = NULL, *ref, *ts_str = NULL;
203 double offset, precision, root_dispersion;
204 char addr_buf[INET6_ADDRSTRLEN];
205 char *p_SNTP_PRETEND_TIME;
206 time_t pretend_time;
207
208 if(rpktl > 0)
209 sw_case = 1;
210 else
211 sw_case = rpktl;
212
213 switch(sw_case) {
214 case SERVER_UNUSEABLE:
215 return -1;
216 break;
217
218 case PACKET_UNUSEABLE:
219 break;
220
221 case SERVER_AUTH_FAIL:
222 break;
223
224 case KOD_DEMOBILIZE:
225 /* Received a DENY or RESTR KOD packet */
226 hostname = addrinfo_to_str(host);
227 ref = (char *)&rpkt->refid;
228 add_entry(hostname, ref);
229
230 if (ENABLED_OPT(NORMALVERBOSE))
231 printf("sntp handle_pkt: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n",
232 ref[0], ref[1], ref[2], ref[3],
233 hostname);
234
235 msyslog(LOG_WARNING, "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
236 ref[0], ref[1], ref[2], ref[3], hostname);
237 break;
238
239 case KOD_RATE:
240 /* Hmm... probably we should sleep a bit here */
241 break;
242
243 case 1:
244 if (ENABLED_OPT(NORMALVERBOSE)) {
245 getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
246 sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
247 printf("sntp handle_pkt: Received %i bytes from %s\n", rpktl, addr_buf);
248 }
249
250 GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL);
251
252 p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME");
253 if (p_SNTP_PRETEND_TIME) {
254 #if SIZEOF_TIME_T == 4
255 sscanf(p_SNTP_PRETEND_TIME, "%ld", &pretend_time);
256 #elif SIZEOF_TIME_T == 8
257 sscanf(p_SNTP_PRETEND_TIME, "%lld", &pretend_time);
258 #else
259 # include "GRONK: unexpected value for SIZEOF_TIME_T"
260 #endif
261 tv_dst.tv_sec = pretend_time;
262 }
263
264 offset_calculation(rpkt, rpktl, &tv_dst, &offset,
265 &precision, &root_dispersion);
266
267 for (digits = 0; (precision *= 10.) < 1.; ++digits)
268 /* empty */ ;
269 if (digits > 6)
270 digits = 6;
271
272 ts_str = tv_to_str(&tv_dst);
273 printf("%s ", ts_str);
274 if (offset > 0)
275 printf("+");
276 printf("%.*f", digits, offset);
277 if (root_dispersion > 0.)
278 printf(" +/- %f secs", root_dispersion);
279 printf("\n");
280 free(ts_str);
281
282 if (p_SNTP_PRETEND_TIME)
283 return 0;
284
285 if (ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME))
286 return set_time(offset);
287
288 return 0;
289 }
290
291 return 1;
292 }
293
294 void
295 offset_calculation (
296 struct pkt *rpkt,
297 int rpktl,
298 struct timeval *tv_dst,
299 double *offset,
300 double *precision,
301 double *root_dispersion
302 )
303 {
304 l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst;
305 u_fp p_rdly, p_rdsp;
306 double t21, t34, delta;
307
308 /* Convert timestamps from network to host byte order */
309 p_rdly = NTOHS_FP(rpkt->rootdelay);
310 p_rdsp = NTOHS_FP(rpkt->rootdisp);
311 NTOHL_FP(&rpkt->reftime, &p_ref);
312 NTOHL_FP(&rpkt->org, &p_org);
313 NTOHL_FP(&rpkt->rec, &p_rec);
314 NTOHL_FP(&rpkt->xmt, &p_xmt);
315
316 *precision = LOGTOD(rpkt->precision);
317 #ifdef DEBUG
318 printf("sntp precision: %f\n", *precision);
319 #endif /* DEBUG */
320
321 *root_dispersion = FPTOD(p_rdsp);
322
323 #ifdef DEBUG
324 printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
325 printf("sntp rootdisp: %f\n", *root_dispersion);
326
327 pkt_output(rpkt, rpktl, stdout);
328
329 printf("sntp offset_calculation: rpkt->reftime:\n");
330 l_fp_output(&(rpkt->reftime), stdout);
331 printf("sntp offset_calculation: rpkt->org:\n");
332 l_fp_output(&(rpkt->org), stdout);
333 printf("sntp offset_calculation: rpkt->rec:\n");
334 l_fp_output(&(rpkt->rec), stdout);
335 printf("sntp offset_calculation: rpkt->rec:\n");
336 l_fp_output_bin(&(rpkt->rec), stdout);
337 printf("sntp offset_calculation: rpkt->rec:\n");
338 l_fp_output_dec(&(rpkt->rec), stdout);
339 printf("sntp offset_calculation: rpkt->xmt:\n");
340 l_fp_output(&(rpkt->xmt), stdout);
341 #endif
342
343 /* Compute offset etc. */
344 tmp = p_rec;
345 L_SUB(&tmp, &p_org);
346 LFPTOD(&tmp, t21);
347 TVTOTS(tv_dst, &dst);
348 dst.l_ui += JAN_1970;
349 tmp = p_xmt;
350 L_SUB(&tmp, &dst);
351 LFPTOD(&tmp, t34);
352 *offset = (t21 + t34) / 2.;
353 delta = t21 - t34;
354
355 if (ENABLED_OPT(NORMALVERBOSE))
356 printf("sntp offset_calculation:\tt21: %.6f\t\t t34: %.6f\n\t\tdelta: %.6f\t offset: %.6f\n",
357 t21, t34, delta, *offset);
358 }
359
360 /* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
361 int
362 on_wire (
363 struct addrinfo *host,
364 struct addrinfo *bcast
365 )
366 {
367 char addr_buf[INET6_ADDRSTRLEN];
368 register int try;
369 SOCKET sock;
370 struct key *pkt_key = NULL;
371 int key_id = 0;
372 struct timeval tv_xmt;
373 struct pkt x_pkt;
374 int error, rpktl, handle_pkt_res;
375
376
377 if (ENABLED_OPT(AUTHENTICATION)) {
378 key_id = (int) atol(OPT_ARG(AUTHENTICATION));
379 get_key(key_id, &pkt_key);
380 }
381 for (try=0; try<5; try++) {
382 memset(&r_pkt, 0, sizeof rbuf);
383
384 error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL);
385 tv_xmt.tv_sec += JAN_1970;
386
387 #ifdef DEBUG
388 printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec,
389 (unsigned int) tv_xmt.tv_usec);
390 #endif
391
392 if (bcast) {
393 create_socket(&sock, (sockaddr_u *)bcast->ai_addr);
394 rpktl = recv_bcst_pkt(sock, &r_pkt, sizeof rbuf, (sockaddr_u *)bcast->ai_addr);
395 closesocket(sock);
396 } else {
397 int pkt_len = generate_pkt(&x_pkt, &tv_xmt, key_id, pkt_key);
398
399 create_socket(&sock, (sockaddr_u *)host->ai_addr);
400 sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, pkt_len);
401 rpktl = recvpkt(sock, &r_pkt, sizeof rbuf, &x_pkt);
402 closesocket(sock);
403 }
404
405 handle_pkt_res = handle_pkt(rpktl, &r_pkt, host);
406 if (handle_pkt_res < 1)
407 return handle_pkt_res;
408 }
409
410 getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
411 msyslog(LOG_DEBUG, "Received no useable packet from %s!", addr_buf);
412
413 return -1;
414 }
415
416 /* Compute the 8 bits for li_vn_mode */
417 void
418 set_li_vn_mode (
419 struct pkt *spkt,
420 char leap,
421 char version,
422 char mode
423 )
424 {
425 if (leap > 3) {
426 msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3 using max. 3");
427 leap = 3;
428 }
429
430 if (mode > 7) {
431 msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3");
432 mode = 3;
433 }
434
435 spkt->li_vn_mode = leap << 6;
436 spkt->li_vn_mode |= version << 3;
437 spkt->li_vn_mode |= mode;
438 }
439
440 /* set_time corrects the local clock by offset with either settimeofday() or by default
441 * with adjtime()/adjusttimeofday().
442 */
443 int
444 set_time(
445 double offset
446 )
447 {
448 struct timeval tp;
449
450 if (ENABLED_OPT(SETTOD)) {
451 GETTIMEOFDAY(&tp, NULL);
452
453 tp.tv_sec += (long)offset;
454 tp.tv_usec += 1e6 * (offset - (long)offset);
455 NORMALIZE_TIMEVAL(tp);
456
457 if (SETTIMEOFDAY(&tp, NULL) < 0) {
458 msyslog(LOG_ERR, "Time not set: settimeofday(): %m");
459 return -1;
460 }
461 return 0;
462 }
463
464 tp.tv_sec = (long)offset;
465 tp.tv_usec = 1e6 * (offset - (long)offset);
466 NORMALIZE_TIMEVAL(tp);
467
468 if (ADJTIMEOFDAY(&tp, NULL) < 0) {
469 msyslog(LOG_ERR, "Time not set: adjtime(): %m");
470 return -1;
471 }
472 return 0;
473 }
474