/* XXX handle responses with the error bit set */
double jitter_request(const char *host, int *status){
	int conn=-1, i, npeers=0, num_candidates=0, syncsource_found=0;
	int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0;
	int peers_size=0, peer_offset=0, bytes_read=0;
	ntp_assoc_status_pair *peers=NULL;
	ntp_control_message req;
	const char *getvar = "jitter";
	double rval = 0.0, jitter = -1.0;
	char *startofvalue=NULL, *nptr=NULL;
	void *tmp;
	int ntp_cm_ints = sizeof(uint16_t) * 5 + sizeof(uint8_t) * 2;

	/* Long-winded explanation:
	 * Getting the jitter requires a number of steps:
	 * 1) Send a READSTAT request.
	 * 2) Interpret the READSTAT reply
	 *  a) The data section contains a list of peer identifiers (16 bits)
	 *     and associated status words (16 bits)
	 *  b) We want the value of 0x06 in the SEL (peer selection) value,
	 *     which means "current synchronizatin source".  If that's missing,
	 *     we take anything better than 0x04 (see the rfc for details) but
	 *     set a minimum of warning.
	 * 3) Send a READVAR request for information on each peer identified
	 *    in 2b greater than the minimum selection value.
	 * 4) Extract the jitter value from the data[] (it's ASCII)
	 */
	my_udp_connect(server_address, 123, &conn);

	/* keep sending requests until the server stops setting the
	 * REM_MORE bit, though usually this is only 1 packet. */
	do{
		setup_control_request(&req, OP_READSTAT, 1);
		DBG(printf("sending READSTAT request"));
		write(conn, &req, SIZEOF_NTPCM(req));
		DBG(print_ntp_control_message(&req));
		/* Attempt to read the largest size packet possible */
		req.count=htons(MAX_CM_SIZE);
		DBG(printf("recieving READSTAT response"))
		read(conn, &req, SIZEOF_NTPCM(req));
		DBG(print_ntp_control_message(&req));
		/* Each peer identifier is 4 bytes in the data section, which
	 	 * we represent as a ntp_assoc_status_pair datatype.
	 	 */
		peers_size+=ntohs(req.count);
		if((tmp=realloc(peers, peers_size)) == NULL)
			free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
		peers=tmp;
		memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count));
		npeers=peers_size/sizeof(ntp_assoc_status_pair);
		peer_offset+=ntohs(req.count);
	} while(req.op&REM_MORE);

	/* first, let's find out if we have a sync source, or if there are
	 * at least some candidates.  in the case of the latter we'll issue
	 * a warning but go ahead with the check on them. */
	for (i = 0; i < npeers; i++){
		if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){
			num_candidates++;
			if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){
				syncsource_found=1;
				min_peer_sel=PEER_SYNCSOURCE;
			}
		}
	}
	if(verbose) printf("%d candiate peers available\n", num_candidates);
	if(verbose && syncsource_found) printf("synchronization source found\n");
	if(! syncsource_found){
		*status = STATE_UNKNOWN;
		if(verbose) printf("warning: no synchronization source found\n");
	}


	for (run=0; run<AVG_NUM; run++){
		if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM);
		for (i = 0; i < npeers; i++){
			/* Only query this server if it is the current sync source */
			if (PEER_SEL(peers[i].status) >= min_peer_sel){
				num_selected++;
				setup_control_request(&req, OP_READVAR, 2);
				req.assoc = peers[i].assoc;
				/* By spec, putting the variable name "jitter"  in the request
				 * should cause the server to provide _only_ the jitter value.
				 * thus reducing net traffic, guaranteeing us only a single
				 * datagram in reply, and making intepretation much simpler
				 */
				/* Older servers doesn't know what jitter is, so if we get an
				 * error on the first pass we redo it with "dispersion" */
				strncpy(req.data, getvar, MAX_CM_SIZE-1);
				req.count = htons(strlen(getvar));
				DBG(printf("sending READVAR request...\n"));
				write(conn, &req, SIZEOF_NTPCM(req));
				DBG(print_ntp_control_message(&req));

				req.count = htons(MAX_CM_SIZE);
				DBG(printf("recieving READVAR response...\n"));

				/* cov-66524 - req.data not null terminated before usage. Also covers verifying struct was returned correctly*/
				if ((bytes_read = read(conn, &req, SIZEOF_NTPCM(req))) == -1)
					die(STATE_UNKNOWN, _("Cannot read from socket: %s"), strerror(errno));
				if (bytes_read != ntp_cm_ints + req.count)
					die(STATE_UNKNOWN, _("Invalid NTP response: %d bytes read does not equal %d plus %d data segment"), bytes_read, ntp_cm_ints, req.count); 
				/* else null terminate */
				strncpy(req.data[req.count], "\0", 1);

				DBG(print_ntp_control_message(&req));

				if(req.op&REM_ERROR && strstr(getvar, "jitter")) {
					if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n");
					getvar = "dispersion";
					num_selected--;
					i--;
					continue;
				}

				/* get to the float value */
				if(verbose) {
					printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
				}
				startofvalue = strchr(req.data, '=');
				if(startofvalue != NULL) {
					startofvalue++;
					jitter = strtod(startofvalue, &nptr);
				}
				if(startofvalue == NULL || startofvalue==nptr){
					printf("warning: unable to read server jitter response.\n");
					*status = STATE_UNKNOWN;
				} else {
					if(verbose) printf("%g\n", jitter);
					num_valid++;
					rval += jitter;
				}
			}
		}
		if(verbose){
			printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
		}
	}

	rval = num_valid ? rval / num_valid : -1.0;

	close(conn);
	if(peers!=NULL) free(peers);
	/* If we return -1.0, it means no synchronization source was found */
	return rval;
}
Beispiel #2
0
int
main (int argc, char **argv)
{
	int sd;
	int result = STATE_UNKNOWN;
	time_t conntime;

	setlocale (LC_ALL, "");
	bindtextdomain (PACKAGE, LOCALEDIR);
	textdomain (PACKAGE);

	/* Parse extra opts if any */
	argv=np_extra_opts (&argc, argv, progname);

	if (process_arguments (argc, argv) == ERROR)
		usage4 (_("Could not parse arguments"));

	/* initialize alarm signal handling */
	signal (SIGALRM, socket_timeout_alarm_handler);

	/* set socket timeout */
	alarm (socket_timeout);
	time (&start_time);

	/* try to connect to the host at the given port number */
	if (use_udp) {
		result = my_udp_connect (server_address, server_port, &sd);
	} else {
		result = my_tcp_connect (server_address, server_port, &sd);
	}

	if (result != STATE_OK) {
		if (check_critical_time == TRUE)
			result = STATE_CRITICAL;
		else if (check_warning_time == TRUE)
			result = STATE_WARNING;
		else
			result = STATE_UNKNOWN;
		die (result,
		           _("TIME UNKNOWN - could not connect to server %s, port %d\n"),
		           server_address, server_port);
	}

	if (use_udp) {
		if (send (sd, "", 0, 0) < 0) {
			if (check_critical_time == TRUE)
				result = STATE_CRITICAL;
			else if (check_warning_time == TRUE)
				result = STATE_WARNING;
			else
				result = STATE_UNKNOWN;
			die (result,
			  _("TIME UNKNOWN - could not send UDP request to server %s, port %d\n"),
			  server_address, server_port);
		}
	}

	/* watch for the connection string */
	result = recv (sd, (void *)&raw_server_time, sizeof (raw_server_time), 0);

	/* close the connection */
	close (sd);

	/* reset the alarm */
	time (&end_time);
	alarm (0);

	/* return a WARNING status if we couldn't read any data */
	if (result <= 0) {
		if (check_critical_time == TRUE)
			result = STATE_CRITICAL;
		else if (check_warning_time == TRUE)
			result = STATE_WARNING;
		else
			result = STATE_UNKNOWN;
		die (result,
							 _("TIME UNKNOWN - no data received from server %s, port %d\n"),
							 server_address, server_port);
	}

	result = STATE_OK;

	conntime = (end_time - start_time);
	if (check_critical_time == TRUE && conntime > critical_time)
		result = STATE_CRITICAL;
	else if (check_warning_time == TRUE && conntime > warning_time)
		result = STATE_WARNING;

	if (result != STATE_OK)
		die (result, _("TIME %s - %d second response time|%s\n"),
		     state_text (result), (int)conntime,
		     perfdata ("time", (long)conntime, "s",
		               check_warning_time, (long)warning_time,
		               check_critical_time, (long)critical_time,
		               TRUE, 0, FALSE, 0));

	server_time = ntohl (raw_server_time) - UNIX_EPOCH;
	if (server_time > (unsigned long)end_time)
		diff_time = server_time - (unsigned long)end_time;
	else
		diff_time = (unsigned long)end_time - server_time;

	if (check_critical_diff == TRUE && diff_time > critical_diff)
		result = STATE_CRITICAL;
	else if (check_warning_diff == TRUE && diff_time > warning_diff)
		result = STATE_WARNING;

	printf (_("TIME %s - %lu second time difference|%s %s\n"),
	        state_text (result), diff_time,
	        perfdata ("time", (long)conntime, "s",
	                  check_warning_time, (long)warning_time,
	                  check_critical_time, (long)critical_time,
	                  TRUE, 0, FALSE, 0),
	        perfdata ("offset", diff_time, "s",
	                  check_warning_diff, warning_diff,
	                  check_critical_diff, critical_diff,
	                  TRUE, 0, FALSE, 0));
	return result;
}