/* 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; }
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; }