Home | History | Annotate | Line # | Download | only in sntp
main.c revision 1.1
      1 /*	$NetBSD: main.c,v 1.1 2009/12/13 16:57:10 kardel Exp $	*/
      2 
      3 #include <l_stdlib.h>
      4 #include <ntp_fp.h>
      5 #include <ntp.h>
      6 #include <ntp_stdlib.h>
      7 #include <ntp_unixtime.h>
      8 #include <isc/result.h>
      9 #include <isc/net.h>
     10 #include <stdio.h>
     11 
     12 #include <sntp-opts.h>
     13 
     14 #include "crypto.h"
     15 #include "kod_management.h"
     16 #include "networking.h"
     17 #include "utilities.h"
     18 #include "log.h"
     19 
     20 char *progname = "sntp";	/* for msyslog */
     21 
     22 int ai_fam_pref = AF_UNSPEC;
     23 volatile int debug;
     24 
     25 struct key *keys = NULL;
     26 
     27 void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode);
     28 int sntp_main (int argc, char **argv);
     29 int on_wire (struct addrinfo *host);
     30 int set_time (double offset);
     31 
     32 
     33 int
     34 main (
     35 		int argc,
     36 		char **argv
     37 		)
     38 {
     39 	return sntp_main(argc, argv);
     40 }
     41 
     42 /*
     43  * The actual main function.
     44  */
     45 int
     46 sntp_main (
     47 		int argc,
     48 		char **argv
     49 		)
     50 {
     51 	register int c;
     52 	struct kod_entry *reason = NULL;
     53 	int optct;
     54 	int sync_data_suc = 0;
     55 	struct addrinfo **resh = NULL;
     56 	struct addrinfo *ai;
     57 	int resc;
     58 	int kodc;
     59 	int ow_ret;
     60 	char *hostname;
     61 
     62 	/* IPv6 available? */
     63 	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
     64 		ai_fam_pref = AF_INET;
     65 #ifdef DEBUG
     66 		printf("No ipv6 support available, forcing ipv4\n");
     67 #endif
     68 	}
     69 	else {
     70 		/* Check for options -4 and -6 */
     71 		if (HAVE_OPT(IPV4))
     72 			ai_fam_pref = AF_INET;
     73 		else if (HAVE_OPT(IPV6))
     74 			ai_fam_pref = AF_INET6;
     75 	}
     76 
     77 	log_msg("Started sntp", 0);
     78 
     79 	optct = optionProcess(&sntpOptions, argc, argv);
     80 	argc -= optct;
     81 	argv += optct;
     82 
     83 	/* Parse config file if declared TODO */
     84 
     85 	/* Initialize logging system */
     86 	if (HAVE_OPT(FILELOG))
     87 		init_log(OPT_ARG(FILELOG));
     88 
     89 	/*
     90 	 * If there's a specified KOD file init KOD system.  If not use
     91 	 * default file.  For embedded systems with no writable
     92 	 * filesystem, -K /dev/null can be used to disable KoD storage.
     93 	 */
     94 	if (HAVE_OPT(KOD))
     95 		kod_init_kod_db(OPT_ARG(KOD));
     96 	else
     97 		kod_init_kod_db("/var/db/ntp-kod");
     98 
     99 	if (HAVE_OPT(KEYFILE))
    100 		auth_init(OPT_ARG(KEYFILE), &keys);
    101 
    102 #ifdef EXERCISE_KOD_DB
    103 	add_entry("192.168.169.170", "DENY");
    104 	add_entry("192.168.169.171", "DENY");
    105 	add_entry("192.168.169.172", "DENY");
    106 	add_entry("192.168.169.173", "DENY");
    107 	add_entry("192.168.169.174", "DENY");
    108 	delete_entry("192.168.169.174", "DENY");
    109 	delete_entry("192.168.169.172", "DENY");
    110 	delete_entry("192.168.169.170", "DENY");
    111 	if ((kodc = search_entry("192.168.169.173", &reason)) == 0)
    112 		printf("entry for 192.168.169.173 not found but should have been!\n");
    113 	else
    114 		free(reason);
    115 #endif
    116 
    117 	/* Considering employing a variable that prevents functions of doing anything until
    118 	 * everything is initialized properly
    119 	 */
    120 	resc = resolve_hosts(argv, argc, &resh, ai_fam_pref);
    121 
    122 	if (resc < 1) {
    123 		printf("Unable to resolve hostname(s)\n");
    124 		return -1;
    125 	}
    126 
    127 	/* Select a certain ntp server according to simple criteria? For now
    128 	 * let's just pay attention to previous KoDs.
    129 	 */
    130 	for (c = 0; c < resc && !sync_data_suc; c++) {
    131 		ai = resh[c];
    132 		do {
    133 			hostname = addrinfo_to_str(ai);
    134 
    135 			if ((kodc = search_entry(hostname, &reason)) == 0) {
    136 				if (is_reachable(ai)) {
    137 					ow_ret = on_wire(ai);
    138 					if (ow_ret < 0)
    139 						printf("on_wire failed for server %s!\n", hostname);
    140 					else
    141 						sync_data_suc = 1;
    142 				}
    143 			} else {
    144 				printf("%d prior KoD%s for %s, skipping.\n",
    145 					kodc, (kodc > 1) ? "s" : "", hostname);
    146 				free(reason);
    147 			}
    148 			free(hostname);
    149 			ai = ai->ai_next;
    150 		} while (NULL != ai);
    151 		freeaddrinfo(resh[c]);
    152 	}
    153 	free(resh);
    154 
    155 	return 0;
    156 }
    157 
    158 /* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
    159 int
    160 on_wire (
    161 		struct addrinfo *host
    162 					)
    163 {
    164 	char logmsg[32 + INET6_ADDRSTRLEN];
    165 	char addr_buf[INET6_ADDRSTRLEN];
    166 	register int try;
    167 	SOCKET sock;
    168 	struct pkt x_pkt;
    169 	struct pkt r_pkt;
    170 	char *ref;
    171 
    172 	for(try=0; try<5; try++) {
    173 		struct timeval tv_xmt, tv_dst;
    174 		double t21, t34, delta, offset, precision, root_dispersion;
    175 		int digits, error, rpktl, sw_case;
    176 		char *hostname = NULL, *ts_str = NULL;
    177 		char *log_str;
    178 		u_fp p_rdly, p_rdsp;
    179 		l_fp p_rec, p_xmt, p_ref, p_org, xmt, tmp, dst;
    180 
    181 		memset(&r_pkt, 0, sizeof(r_pkt));
    182 		memset(&x_pkt, 0, sizeof(x_pkt));
    183 
    184 		error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL);
    185 
    186 		tv_xmt.tv_sec += JAN_1970;
    187 
    188 #ifdef DEBUG
    189 		printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec,
    190 				(unsigned int) tv_xmt.tv_usec);
    191 #endif
    192 
    193 		TVTOTS(&tv_xmt, &xmt);
    194 		HTONL_FP(&xmt, &(x_pkt.xmt));
    195 
    196 		x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
    197 		x_pkt.ppoll = 8;
    198 		/* FIXME! Modus broadcast + adr. check -> bdr. pkt */
    199 		set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3);
    200 
    201 		create_socket(&sock, (sockaddr_u *)host->ai_addr);
    202 
    203 		sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, LEN_PKT_NOMAC);
    204 		rpktl = recvpkt(sock, &r_pkt, &x_pkt);
    205 
    206 		closesocket(sock);
    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 *)&r_pkt.refid;
    228 				add_entry(hostname, ref);
    229 
    230 				if (ENABLED_OPT(NORMALVERBOSE))
    231 					printf("sntp on_wire: 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 				log_str = emalloc(INET6_ADDRSTRLEN + 72);
    236 				snprintf(log_str, INET6_ADDRSTRLEN + 72,
    237 					 "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
    238 					 ref[0], ref[1], ref[2], ref[3],
    239 					 hostname);
    240 				log_msg(log_str, 2);
    241 				free(log_str);
    242 				break;
    243 
    244 			case KOD_RATE:
    245 				/* Hmm... probably we should sleep a bit here */
    246 				break;
    247 
    248 			case 1:
    249 
    250 			/* Convert timestamps from network to host byte order */
    251 			p_rdly = NTOHS_FP(r_pkt.rootdelay);
    252 			p_rdsp = NTOHS_FP(r_pkt.rootdisp);
    253 			NTOHL_FP(&r_pkt.reftime, &p_ref);
    254 			NTOHL_FP(&r_pkt.org, &p_org);
    255 			NTOHL_FP(&r_pkt.rec, &p_rec);
    256 			NTOHL_FP(&r_pkt.xmt, &p_xmt);
    257 
    258 			if (ENABLED_OPT(NORMALVERBOSE)) {
    259 				getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
    260 						sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
    261 
    262 				printf("sntp on_wire: Received %i bytes from %s\n", rpktl, addr_buf);
    263 			}
    264 
    265 			precision = LOGTOD(r_pkt.precision);
    266 #ifdef DEBUG
    267 			printf("sntp precision: %f\n", precision);
    268 #endif /* DEBUG */
    269 			for (digits = 0; (precision *= 10.) < 1.; ++digits)
    270 				/* empty */ ;
    271 			if (digits > 6)
    272 				digits = 6;
    273 
    274 			root_dispersion = FPTOD(p_rdsp);
    275 
    276 #ifdef DEBUG
    277 			printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
    278 			printf("sntp rootdisp: %f\n", root_dispersion);
    279 
    280 			pkt_output(&r_pkt, rpktl, stdout);
    281 
    282 			printf("sntp on_wire: r_pkt.reftime:\n");
    283 			l_fp_output(&(r_pkt.reftime), stdout);
    284 			printf("sntp on_wire: r_pkt.org:\n");
    285 			l_fp_output(&(r_pkt.org), stdout);
    286 			printf("sntp on_wire: r_pkt.rec:\n");
    287 			l_fp_output(&(r_pkt.rec), stdout);
    288 			printf("sntp on_wire: r_pkt.rec:\n");
    289 			l_fp_output_bin(&(r_pkt.rec), stdout);
    290 			printf("sntp on_wire: r_pkt.rec:\n");
    291 			l_fp_output_dec(&(r_pkt.rec), stdout);
    292 			printf("sntp on_wire: r_pkt.xmt:\n");
    293 			l_fp_output(&(r_pkt.xmt), stdout);
    294 #endif
    295 
    296 			/* Compute offset etc. */
    297 			GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL);
    298 
    299 			tv_dst.tv_sec += JAN_1970;
    300 
    301 			tmp = p_rec;
    302 			L_SUB(&tmp, &p_org);
    303 
    304 			LFPTOD(&tmp, t21);
    305 
    306 			TVTOTS(&tv_dst, &dst);
    307 
    308 			tmp = dst;
    309 			L_SUB(&tmp, &p_xmt);
    310 
    311 			LFPTOD(&tmp, t34);
    312 
    313 			offset = (t21 + t34) / 2.;
    314 			delta = t21 - t34;
    315 
    316 			if(ENABLED_OPT(NORMALVERBOSE))
    317 				printf("sntp on_wire:\tt21: %.6f\t\t t34: %.6f\n\t\tdelta: %.6f\t offset: %.6f\n",
    318 					t21, t34, delta, offset);
    319 
    320 			ts_str = tv_to_str(&tv_dst);
    321 
    322 			printf("%s ", ts_str);
    323 
    324 			if(offset > 0)
    325 				printf("+");
    326 
    327 			printf("%.*f", digits, offset);
    328 
    329 			if (root_dispersion > 0.)
    330 				printf(" +/- %f secs", root_dispersion);
    331 
    332 			printf("\n");
    333 
    334 			free(ts_str);
    335 
    336 			if(ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME))
    337 				return set_time(offset);
    338 
    339 			return 0;
    340 		}
    341 	}
    342 
    343 	getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
    344 
    345 	snprintf(logmsg, sizeof(logmsg), "Received no useable packet from %s!", addr_buf);
    346 	log_msg(logmsg, 1);
    347 
    348 	if (ENABLED_OPT(NORMALVERBOSE))
    349 		printf("sntp on_wire: Received no useable packet from %s!\n", addr_buf);
    350 
    351 	return -1;
    352 }
    353 
    354 /* Compute the 8 bits for li_vn_mode */
    355 void
    356 set_li_vn_mode (
    357 		struct pkt *spkt,
    358 		char leap,
    359 		char version,
    360 		char mode
    361 	       )
    362 {
    363 
    364 	if(leap > 3) {
    365 		debug_msg("set_li_vn_mode: leap > 3 using max. 3");
    366 		leap = 3;
    367 	}
    368 
    369 	if(mode > 7) {
    370 		debug_msg("set_li_vn_mode: mode > 7, using client mode 3");
    371 		mode = 3;
    372 	}
    373 
    374 	spkt->li_vn_mode  = leap << 6;
    375 	spkt->li_vn_mode |= version << 3;
    376 	spkt->li_vn_mode |= mode;
    377 }
    378 
    379 /* set_time corrects the local clock by offset with either settimeofday() or by default
    380  * with adjtime()/adjusttimeofday().
    381  */
    382 int
    383 set_time (
    384 		double offset
    385 	 )
    386 {
    387 	struct timeval tp;
    388 
    389 	if(ENABLED_OPT(SETTOD)) {
    390 		GETTIMEOFDAY(&tp, (struct timezone *)NULL);
    391 
    392 		tp.tv_sec += (int) offset;
    393 		tp.tv_usec += offset - (double)((int)offset);
    394 
    395 		if(SETTIMEOFDAY(&tp, (struct timezone *)NULL) < 0) {
    396 			printf("set_time: settimeofday(): Time not set: %s\n",
    397 				strerror(errno));
    398 			return -1;
    399 		}
    400 		else {
    401 			return 0;
    402 		}
    403 	}
    404 	else {
    405 		tp.tv_sec = (int) offset;
    406 		tp.tv_usec = offset - (double)((int)offset);
    407 
    408 		if(ADJTIMEOFDAY(&tp, NULL) < 0) {
    409 			printf("set_time: adjtime(): Time not set: %s\n",
    410 				strerror(errno));
    411 			return -1;
    412 		}
    413 		else {
    414 			return 0;
    415 		}
    416 	}
    417 }
    418