xref: /netbsd-src/external/bsd/ntp/dist/sntp/main.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1 /*	$NetBSD: main.c,v 1.6 2012/02/03 21:35:55 christos 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((void *)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 
207 	if(rpktl > 0)
208 		sw_case = 1;
209 	else
210 		sw_case = rpktl;
211 
212 	switch(sw_case) {
213 	case SERVER_UNUSEABLE:
214 		return -1;
215 		break;
216 
217 	case PACKET_UNUSEABLE:
218 		break;
219 
220 	case SERVER_AUTH_FAIL:
221 		break;
222 
223 	case KOD_DEMOBILIZE:
224 		/* Received a DENY or RESTR KOD packet */
225 		hostname = addrinfo_to_str(host);
226 		ref = (char *)&rpkt->refid;
227 		add_entry(hostname, ref);
228 
229 		if (ENABLED_OPT(NORMALVERBOSE))
230 			printf("sntp handle_pkt: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n",
231 				   ref[0], ref[1], ref[2], ref[3],
232 				   hostname);
233 
234 		msyslog(LOG_WARNING, "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
235 			ref[0], ref[1], ref[2], ref[3], hostname);
236 		break;
237 
238 	case KOD_RATE:
239 		/* Hmm... probably we should sleep a bit here */
240 		break;
241 
242 	case 1:
243 		if (ENABLED_OPT(NORMALVERBOSE)) {
244 			getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
245 				sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
246 			printf("sntp handle_pkt: Received %i bytes from %s\n", rpktl, addr_buf);
247 		}
248 
249 		GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL);
250 
251 		p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME");
252 		if (p_SNTP_PRETEND_TIME) {
253 			long long input_time;
254 			sscanf(p_SNTP_PRETEND_TIME, "%lld", &input_time);
255 			tv_dst.tv_sec = (time_t)input_time;
256 		}
257 
258 		offset_calculation(rpkt, rpktl, &tv_dst, &offset,
259 				   &precision, &root_dispersion);
260 
261 		for (digits = 0; (precision *= 10.) < 1.; ++digits)
262 			/* empty */ ;
263 		if (digits > 6)
264 			digits = 6;
265 
266 		ts_str = tv_to_str(&tv_dst);
267 		printf("%s ", ts_str);
268 		if (offset > 0)
269 			printf("+");
270 		printf("%.*f", digits, offset);
271 		if (root_dispersion > 0.)
272 			printf(" +/- %f secs", root_dispersion);
273 		printf("\n");
274 		free(ts_str);
275 
276 		if (p_SNTP_PRETEND_TIME)
277 			return 0;
278 
279 		if (ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME))
280 			return set_time(offset);
281 
282 		return 0;
283 	}
284 
285 	return 1;
286 }
287 
288 void
289 offset_calculation (
290 	struct pkt *rpkt,
291 	int rpktl,
292 	struct timeval *tv_dst,
293 	double *offset,
294 	double *precision,
295 	double *root_dispersion
296 	)
297 {
298 	l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst;
299 	u_fp p_rdly, p_rdsp;
300 	double t21, t34, delta;
301 
302 	/* Convert timestamps from network to host byte order */
303 	p_rdly = NTOHS_FP(rpkt->rootdelay);
304 	p_rdsp = NTOHS_FP(rpkt->rootdisp);
305 	NTOHL_FP(&rpkt->reftime, &p_ref);
306 	NTOHL_FP(&rpkt->org, &p_org);
307 	NTOHL_FP(&rpkt->rec, &p_rec);
308 	NTOHL_FP(&rpkt->xmt, &p_xmt);
309 
310 	*precision = LOGTOD(rpkt->precision);
311 #ifdef DEBUG
312 	printf("sntp precision: %f\n", *precision);
313 #endif /* DEBUG */
314 
315 	*root_dispersion = FPTOD(p_rdsp);
316 
317 #ifdef DEBUG
318 	printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
319 	printf("sntp rootdisp: %f\n", *root_dispersion);
320 
321 	pkt_output(rpkt, rpktl, stdout);
322 
323 	printf("sntp offset_calculation: rpkt->reftime:\n");
324 	l_fp_output(&(rpkt->reftime), stdout);
325 	printf("sntp offset_calculation: rpkt->org:\n");
326 	l_fp_output(&(rpkt->org), stdout);
327 	printf("sntp offset_calculation: rpkt->rec:\n");
328 	l_fp_output(&(rpkt->rec), stdout);
329 	printf("sntp offset_calculation: rpkt->rec:\n");
330 	l_fp_output_bin(&(rpkt->rec), stdout);
331 	printf("sntp offset_calculation: rpkt->rec:\n");
332 	l_fp_output_dec(&(rpkt->rec), stdout);
333 	printf("sntp offset_calculation: rpkt->xmt:\n");
334 	l_fp_output(&(rpkt->xmt), stdout);
335 #endif
336 
337 	/* Compute offset etc. */
338 	tmp = p_rec;
339 	L_SUB(&tmp, &p_org);
340 	LFPTOD(&tmp, t21);
341 	TVTOTS(tv_dst, &dst);
342 	dst.l_ui += JAN_1970;
343 	tmp = p_xmt;
344 	L_SUB(&tmp, &dst);
345 	LFPTOD(&tmp, t34);
346 	*offset = (t21 + t34) / 2.;
347 	delta = t21 - t34;
348 
349 	if (ENABLED_OPT(NORMALVERBOSE))
350 		printf("sntp offset_calculation:\tt21: %.6f\t\t t34: %.6f\n\t\tdelta: %.6f\t offset: %.6f\n",
351 			   t21, t34, delta, *offset);
352 }
353 
354 /* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
355 int
356 on_wire (
357 	struct addrinfo *host,
358 	struct addrinfo *bcast
359 	)
360 {
361 	char addr_buf[INET6_ADDRSTRLEN];
362 	register int try;
363 	SOCKET sock;
364 	struct key *pkt_key = NULL;
365 	int key_id = 0;
366 	struct timeval tv_xmt;
367 	struct pkt x_pkt;
368 	int error, rpktl, handle_pkt_res;
369 
370 
371 	if (ENABLED_OPT(AUTHENTICATION)) {
372 		key_id = (int) atol(OPT_ARG(AUTHENTICATION));
373 		get_key(key_id, &pkt_key);
374 	}
375 	for (try=0; try<5; try++) {
376 		memset(&r_pkt, 0, sizeof rbuf);
377 
378 		error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL);
379 		tv_xmt.tv_sec += JAN_1970;
380 
381 #ifdef DEBUG
382 		printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec,
383 				(unsigned int) tv_xmt.tv_usec);
384 #endif
385 
386 		if (bcast) {
387 			create_socket(&sock, (sockaddr_u *)bcast->ai_addr);
388 			rpktl = recv_bcst_pkt(sock, &r_pkt, sizeof rbuf, (sockaddr_u *)bcast->ai_addr);
389 			closesocket(sock);
390 		} else {
391 			int pkt_len = generate_pkt(&x_pkt, &tv_xmt, key_id, pkt_key);
392 
393 			create_socket(&sock, (sockaddr_u *)host->ai_addr);
394 			sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, pkt_len);
395 			rpktl = recvpkt(sock, &r_pkt, sizeof rbuf, &x_pkt);
396 			closesocket(sock);
397 		}
398 
399 		handle_pkt_res = handle_pkt(rpktl, &r_pkt, host);
400 		if (handle_pkt_res < 1)
401 			return handle_pkt_res;
402 	}
403 
404 	getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
405 	msyslog(LOG_DEBUG, "Received no useable packet from %s!", addr_buf);
406 
407 	return -1;
408 }
409 
410 /* Compute the 8 bits for li_vn_mode */
411 void
412 set_li_vn_mode (
413 	struct pkt *spkt,
414 	char leap,
415 	char version,
416 	char mode
417 	)
418 {
419 	if (leap > 3) {
420 		msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3 using max. 3");
421 		leap = 3;
422 	}
423 
424 	if (mode > 7) {
425 		msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3");
426 		mode = 3;
427 	}
428 
429 	spkt->li_vn_mode  = leap << 6;
430 	spkt->li_vn_mode |= version << 3;
431 	spkt->li_vn_mode |= mode;
432 }
433 
434 /* set_time corrects the local clock by offset with either settimeofday() or by default
435  * with adjtime()/adjusttimeofday().
436  */
437 int
438 set_time(
439 	double offset
440 	)
441 {
442 	struct timeval tp;
443 
444 	if (ENABLED_OPT(SETTOD)) {
445 		GETTIMEOFDAY(&tp, NULL);
446 
447 		tp.tv_sec += (long)offset;
448 		tp.tv_usec += 1e6 * (offset - (long)offset);
449 		NORMALIZE_TIMEVAL(tp);
450 
451 		if (SETTIMEOFDAY(&tp, NULL) < 0) {
452 			msyslog(LOG_ERR, "Time not set: settimeofday(): %m");
453 			return -1;
454 		}
455 		return 0;
456 	}
457 
458 	tp.tv_sec = (long)offset;
459 	tp.tv_usec = 1e6 * (offset - (long)offset);
460 	NORMALIZE_TIMEVAL(tp);
461 
462 	if (ADJTIMEOFDAY(&tp, NULL) < 0) {
463 		msyslog(LOG_ERR, "Time not set: adjtime(): %m");
464 		return -1;
465 	}
466 	return 0;
467 }
468