Back to index

nagios-plugins  1.4.16
check_ntp.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002 * 
00003 * Nagios check_ntp plugin
00004 * 
00005 * License: GPL
00006 * Copyright (c) 2006 Sean Finney <seanius@seanius.net>
00007 * Copyright (c) 2006-2008 Nagios Plugins Development Team
00008 * 
00009 * Description:
00010 * 
00011 * This file contains the check_ntp plugin
00012 * 
00013 * This plugin to check ntp servers independant of any commandline
00014 * programs or external libraries.
00015 * 
00016 * 
00017 * This program is free software: you can redistribute it and/or modify
00018 * it under the terms of the GNU General Public License as published by
00019 * the Free Software Foundation, either version 3 of the License, or
00020 * (at your option) any later version.
00021 * 
00022 * This program is distributed in the hope that it will be useful,
00023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00025 * GNU General Public License for more details.
00026 * 
00027 * You should have received a copy of the GNU General Public License
00028 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00029 * 
00030 * 
00031 *****************************************************************************/
00032 
00033 const char *progname = "check_ntp";
00034 const char *copyright = "2006-2008";
00035 const char *email = "nagiosplug-devel@lists.sourceforge.net";
00036 
00037 #include "common.h"
00038 #include "netutils.h"
00039 #include "utils.h"
00040 
00041 static char *server_address=NULL;
00042 static int verbose=0;
00043 static short do_offset=0;
00044 static char *owarn="60";
00045 static char *ocrit="120";
00046 static short do_jitter=0;
00047 static char *jwarn="5000";
00048 static char *jcrit="10000";
00049 
00050 int process_arguments (int, char **);
00051 thresholds *offset_thresholds = NULL;
00052 thresholds *jitter_thresholds = NULL;
00053 void print_help (void);
00054 void print_usage (void);
00055 
00056 /* number of times to perform each request to get a good average. */
00057 #define AVG_NUM 4
00058 
00059 /* max size of control message data */
00060 #define MAX_CM_SIZE 468
00061 
00062 /* this structure holds everything in an ntp request/response as per rfc1305 */
00063 typedef struct {
00064        uint8_t flags;       /* byte with leapindicator,vers,mode. see macros */
00065        uint8_t stratum;     /* clock stratum */
00066        int8_t poll;         /* polling interval */
00067        int8_t precision;    /* precision of the local clock */
00068        int32_t rtdelay;     /* total rt delay, as a fixed point num. see macros */
00069        uint32_t rtdisp;     /* like above, but for max err to primary src */
00070        uint32_t refid;      /* ref clock identifier */
00071        uint64_t refts;      /* reference timestamp.  local time local clock */
00072        uint64_t origts;     /* time at which request departed client */
00073        uint64_t rxts;       /* time at which request arrived at server */
00074        uint64_t txts;       /* time at which request departed server */
00075 } ntp_message;
00076 
00077 /* this structure holds data about results from querying offset from a peer */
00078 typedef struct {
00079        time_t waiting;         /* ts set when we started waiting for a response */
00080        int num_responses;      /* number of successfully recieved responses */
00081        uint8_t stratum;        /* copied verbatim from the ntp_message */
00082        double rtdelay;         /* converted from the ntp_message */
00083        double rtdisp;          /* converted from the ntp_message */
00084        double offset[AVG_NUM]; /* offsets from each response */
00085        uint8_t flags;       /* byte with leapindicator,vers,mode. see macros */
00086 } ntp_server_results;
00087 
00088 /* this structure holds everything in an ntp control message as per rfc1305 */
00089 typedef struct {
00090        uint8_t flags;       /* byte with leapindicator,vers,mode. see macros */
00091        uint8_t op;          /* R,E,M bits and Opcode */
00092        uint16_t seq;        /* Packet sequence */
00093        uint16_t status;     /* Clock status */
00094        uint16_t assoc;      /* Association */
00095        uint16_t offset;     /* Similar to TCP sequence # */
00096        uint16_t count;      /* # bytes of data */
00097        char data[MAX_CM_SIZE]; /* ASCII data of the request */
00098                                /* NB: not necessarily NULL terminated! */
00099 } ntp_control_message;
00100 
00101 /* this is an association/status-word pair found in control packet reponses */
00102 typedef struct {
00103        uint16_t assoc;
00104        uint16_t status;
00105 } ntp_assoc_status_pair;
00106 
00107 /* bits 1,2 are the leap indicator */
00108 #define LI_MASK 0xc0
00109 #define LI(x) ((x&LI_MASK)>>6)
00110 #define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0)
00111 /* and these are the values of the leap indicator */
00112 #define LI_NOWARNING 0x00
00113 #define LI_EXTRASEC 0x01
00114 #define LI_MISSINGSEC 0x02
00115 #define LI_ALARM 0x03
00116 /* bits 3,4,5 are the ntp version */
00117 #define VN_MASK 0x38
00118 #define VN(x) ((x&VN_MASK)>>3)
00119 #define VN_SET(x,y)  do{ x |= ((y<<3)&VN_MASK); }while(0)
00120 #define VN_RESERVED 0x02
00121 /* bits 6,7,8 are the ntp mode */
00122 #define MODE_MASK 0x07
00123 #define MODE(x) (x&MODE_MASK)
00124 #define MODE_SET(x,y)       do{ x |= (y&MODE_MASK); }while(0)
00125 /* here are some values */
00126 #define MODE_CLIENT 0x03
00127 #define MODE_CONTROLMSG 0x06
00128 /* In control message, bits 8-10 are R,E,M bits */
00129 #define REM_MASK 0xe0
00130 #define REM_RESP 0x80
00131 #define REM_ERROR 0x40
00132 #define REM_MORE 0x20
00133 /* In control message, bits 11 - 15 are opcode */
00134 #define OP_MASK 0x1f
00135 #define OP_SET(x,y)   do{ x |= (y&OP_MASK); }while(0)
00136 #define OP_READSTAT 0x01
00137 #define OP_READVAR  0x02
00138 /* In peer status bytes, bits 6,7,8 determine clock selection status */
00139 #define PEER_SEL(x) ((ntohs(x)>>8)&0x07)
00140 #define PEER_INCLUDED 0x04
00141 #define PEER_SYNCSOURCE 0x06
00142 
00152 /* macros to access the left/right 16 bits of a 32-bit ntp "fixed point"
00153    number.  note that these can be used as lvalues too */
00154 #define L16(x) (((uint16_t*)&x)[0])
00155 #define R16(x) (((uint16_t*)&x)[1])
00156 /* macros to access the left/right 32 bits of a 64-bit ntp "fixed point"
00157    number.  these too can be used as lvalues */
00158 #define L32(x) (((uint32_t*)&x)[0])
00159 #define R32(x) (((uint32_t*)&x)[1])
00160 
00161 /* ntp wants seconds since 1/1/00, epoch is 1/1/70.  this is the difference */
00162 #define EPOCHDIFF 0x83aa7e80UL
00163 
00164 /* extract a 32-bit ntp fixed point number into a double */
00165 #define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0)
00166 
00167 /* likewise for a 64-bit ntp fp number */
00168 #define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\
00169                          (ntohl(L32(n))-EPOCHDIFF) + \
00170                          (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\
00171                          0)
00172 
00173 /* convert a struct timeval to a double */
00174 #define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec))
00175 
00176 /* convert an ntp 64-bit fp number to a struct timeval */
00177 #define NTP64toTV(n,t) \
00178        do{ if(!n) t.tv_sec = t.tv_usec = 0; \
00179            else { \
00180                      t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \
00181                      t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \
00182               } \
00183        }while(0)
00184 
00185 /* convert a struct timeval to an ntp 64-bit fp number */
00186 #define TVtoNTP64(t,n) \
00187        do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \
00188               else { \
00189                      L32(n)=htonl(t.tv_sec + EPOCHDIFF); \
00190                      R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \
00191               } \
00192        } while(0)
00193 
00194 /* NTP control message header is 12 bytes, plus any data in the data
00195  * field, plus null padding to the nearest 32-bit boundary per rfc.
00196  */
00197 #define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0))
00198 
00199 /* finally, a little helper or two for debugging: */
00200 #define DBG(x) do{if(verbose>1){ x; }}while(0);
00201 #define PRINTSOCKADDR(x) \
00202        do{ \
00203               printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\
00204        }while(0);
00205 
00206 /* calculate the offset of the local clock */
00207 static inline double calc_offset(const ntp_message *m, const struct timeval *t){
00208        double client_tx, peer_rx, peer_tx, client_rx;
00209        client_tx = NTP64asDOUBLE(m->origts);
00210        peer_rx = NTP64asDOUBLE(m->rxts);
00211        peer_tx = NTP64asDOUBLE(m->txts);
00212        client_rx=TVasDOUBLE((*t));
00213        return (.5*((peer_tx-client_rx)+(peer_rx-client_tx)));
00214 }
00215 
00216 /* print out a ntp packet in human readable/debuggable format */
00217 void print_ntp_message(const ntp_message *p){
00218        struct timeval ref, orig, rx, tx;
00219 
00220        NTP64toTV(p->refts,ref);
00221        NTP64toTV(p->origts,orig);
00222        NTP64toTV(p->rxts,rx);
00223        NTP64toTV(p->txts,tx);
00224 
00225        printf("packet contents:\n");
00226        printf("\tflags: 0x%.2x\n", p->flags);
00227        printf("\t  li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
00228        printf("\t  vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
00229        printf("\t  mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
00230        printf("\tstratum = %d\n", p->stratum);
00231        printf("\tpoll = %g\n", pow(2, p->poll));
00232        printf("\tprecision = %g\n", pow(2, p->precision));
00233        printf("\trtdelay = %-.16g\n", NTP32asDOUBLE(p->rtdelay));
00234        printf("\trtdisp = %-.16g\n", NTP32asDOUBLE(p->rtdisp));
00235        printf("\trefid = %x\n", p->refid);
00236        printf("\trefts = %-.16g\n", NTP64asDOUBLE(p->refts));
00237        printf("\torigts = %-.16g\n", NTP64asDOUBLE(p->origts));
00238        printf("\trxts = %-.16g\n", NTP64asDOUBLE(p->rxts));
00239        printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts));
00240 }
00241 
00242 void print_ntp_control_message(const ntp_control_message *p){
00243        int i=0, numpeers=0;
00244        const ntp_assoc_status_pair *peer=NULL;
00245 
00246        printf("control packet contents:\n");
00247        printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op);
00248        printf("\t  li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK);
00249        printf("\t  vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK);
00250        printf("\t  mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK);
00251        printf("\t  response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP);
00252        printf("\t  more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE);
00253        printf("\t  error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR);
00254        printf("\t  op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK);
00255        printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq));
00256        printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status));
00257        printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc));
00258        printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset));
00259        printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count));
00260        numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair));
00261        if(p->op&REM_RESP && p->op&OP_READSTAT){
00262               peer=(ntp_assoc_status_pair*)p->data;
00263               for(i=0;i<numpeers;i++){
00264                      printf("\tpeer id %.2x status %.2x",
00265                             ntohs(peer[i].assoc), ntohs(peer[i].status));
00266                      if (PEER_SEL(peer[i].status) >= PEER_INCLUDED){
00267                             if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){
00268                                    printf(" <-- current sync source");
00269                             } else {
00270                                    printf(" <-- current sync candidate");
00271                             }
00272                      }
00273                      printf("\n");
00274               }
00275        }
00276 }
00277 
00278 void setup_request(ntp_message *p){
00279        struct timeval t;
00280 
00281        memset(p, 0, sizeof(ntp_message));
00282        LI_SET(p->flags, LI_ALARM);
00283        VN_SET(p->flags, 4);
00284        MODE_SET(p->flags, MODE_CLIENT);
00285        p->poll=4;
00286        p->precision=(int8_t)0xfa;
00287        L16(p->rtdelay)=htons(1);
00288        L16(p->rtdisp)=htons(1);
00289 
00290        gettimeofday(&t, NULL);
00291        TVtoNTP64(t,p->txts);
00292 }
00293 
00294 /* select the "best" server from a list of servers, and return its index.
00295  * this is done by filtering servers based on stratum, dispersion, and
00296  * finally round-trip delay. */
00297 int best_offset_server(const ntp_server_results *slist, int nservers){
00298        int i=0, cserver=0, best_server=-1;
00299 
00300        /* for each server */
00301        for(cserver=0; cserver<nservers; cserver++){
00302               /* We don't want any servers that fails these tests */
00303               /* Sort out servers that didn't respond or responede with a 0 stratum;
00304                * stratum 0 is for reference clocks so no NTP server should ever report
00305                * a stratum 0 */
00306               if ( slist[cserver].stratum == 0){
00307                      if (verbose) printf("discarding peer %d: stratum=%d\n", cserver, slist[cserver].stratum);
00308                      continue;
00309               }
00310               /* Sort out servers with error flags */
00311               if ( LI(slist[cserver].flags) == LI_ALARM ){
00312                      if (verbose) printf("discarding peer %d: flags=%d\n", cserver, LI(slist[cserver].flags));
00313                      continue;
00314               }
00315 
00316               /* If we don't have a server yet, use the first one */
00317               if (best_server == -1) {
00318                      best_server = cserver;
00319                      DBG(printf("using peer %d as our first candidate\n", best_server));
00320                      continue;
00321               }
00322 
00323               /* compare the server to the best one we've seen so far */
00324               /* does it have an equal or better stratum? */
00325               DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
00326               if(slist[cserver].stratum <= slist[best_server].stratum){
00327                      DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
00328                      /* does it have an equal or better dispersion? */
00329                      if(slist[cserver].rtdisp <= slist[best_server].rtdisp){
00330                             DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
00331                             /* does it have a better rtdelay? */
00332                             if(slist[cserver].rtdelay < slist[best_server].rtdelay){
00333                                    DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
00334                                    best_server = cserver;
00335                                    DBG(printf("peer %d is now our best candidate\n", best_server));
00336                             }
00337                      }
00338               }
00339        }
00340 
00341        if(best_server >= 0) {
00342               DBG(printf("best server selected: peer %d\n", best_server));
00343               return best_server;
00344        } else {
00345               DBG(printf("no peers meeting synchronization criteria :(\n"));
00346               return -1;
00347        }
00348 }
00349 
00350 /* do everything we need to get the total average offset
00351  * - we use a certain amount of parallelization with poll() to ensure
00352  *   we don't waste time sitting around waiting for single packets.
00353  * - we also "manually" handle resolving host names and connecting, because
00354  *   we have to do it in a way that our lazy macros don't handle currently :( */
00355 double offset_request(const char *host, int *status){
00356        int i=0, j=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0;
00357        int servers_completed=0, one_written=0, one_read=0, servers_readable=0, best_index=-1;
00358        time_t now_time=0, start_ts=0;
00359        ntp_message *req=NULL;
00360        double avg_offset=0.;
00361        struct timeval recv_time;
00362        struct addrinfo *ai=NULL, *ai_tmp=NULL, hints;
00363        struct pollfd *ufds=NULL;
00364        ntp_server_results *servers=NULL;
00365 
00366        /* setup hints to only return results from getaddrinfo that we'd like */
00367        memset(&hints, 0, sizeof(struct addrinfo));
00368        hints.ai_family = address_family;
00369        hints.ai_protocol = IPPROTO_UDP;
00370        hints.ai_socktype = SOCK_DGRAM;
00371 
00372        /* fill in ai with the list of hosts resolved by the host name */
00373        ga_result = getaddrinfo(host, "123", &hints, &ai);
00374        if(ga_result!=0){
00375               die(STATE_UNKNOWN, "error getting address for %s: %s\n",
00376                   host, gai_strerror(ga_result));
00377        }
00378 
00379        /* count the number of returned hosts, and allocate stuff accordingly */
00380        for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; }
00381        req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts);
00382        if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array");
00383        socklist=(int*)malloc(sizeof(int)*num_hosts);
00384        if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
00385        ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts);
00386        if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array");
00387        servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts);
00388        if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array");
00389        memset(servers, 0, sizeof(ntp_server_results)*num_hosts);
00390        DBG(printf("Found %d peers to check\n", num_hosts));
00391 
00392        /* setup each socket for writing, and the corresponding struct pollfd */
00393        ai_tmp=ai;
00394        for(i=0;ai_tmp;i++){
00395               socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
00396               if(socklist[i] == -1) {
00397                      perror(NULL);
00398                      die(STATE_UNKNOWN, "can not create new socket");
00399               }
00400               if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){
00401                      die(STATE_UNKNOWN, "can't create socket connection");
00402               } else {
00403                      ufds[i].fd=socklist[i];
00404                      ufds[i].events=POLLIN;
00405                      ufds[i].revents=0;
00406               }
00407               ai_tmp = ai_tmp->ai_next;
00408        }
00409 
00410        /* now do AVG_NUM checks to each host.  we stop before timeout/2 seconds
00411         * have passed in order to ensure post-processing and jitter time. */
00412        now_time=start_ts=time(NULL);
00413        while(servers_completed<num_hosts && now_time-start_ts <= socket_timeout/2){
00414               /* loop through each server and find each one which hasn't
00415                * been touched in the past second or so and is still lacking
00416                * some responses.  for each of these servers, send a new request,
00417                * and update the "waiting" timestamp with the current time. */
00418               one_written=0;
00419               now_time=time(NULL);
00420 
00421               for(i=0; i<num_hosts; i++){
00422                      if(servers[i].waiting<now_time && servers[i].num_responses<AVG_NUM){
00423                             if(verbose && servers[i].waiting != 0) printf("re-");
00424                             if(verbose) printf("sending request to peer %d\n", i);
00425                             setup_request(&req[i]);
00426                             write(socklist[i], &req[i], sizeof(ntp_message));
00427                             servers[i].waiting=now_time;
00428                             one_written=1;
00429                             break;
00430                      }
00431               }
00432 
00433               /* quickly poll for any sockets with pending data */
00434               servers_readable=poll(ufds, num_hosts, 100);
00435               if(servers_readable==-1){
00436                      perror("polling ntp sockets");
00437                      die(STATE_UNKNOWN, "communication errors");
00438               }
00439 
00440               /* read from any sockets with pending data */
00441               for(i=0; servers_readable && i<num_hosts; i++){
00442                      if(ufds[i].revents&POLLIN && servers[i].num_responses < AVG_NUM){
00443                             if(verbose) {
00444                                    printf("response from peer %d: ", i);
00445                             }
00446 
00447                             read(ufds[i].fd, &req[i], sizeof(ntp_message));
00448                             gettimeofday(&recv_time, NULL);
00449                             DBG(print_ntp_message(&req[i]));
00450                             respnum=servers[i].num_responses++;
00451                             servers[i].offset[respnum]=calc_offset(&req[i], &recv_time);
00452                             if(verbose) {
00453                                    printf("offset %.10g\n", servers[i].offset[respnum]);
00454                             }
00455                             servers[i].stratum=req[i].stratum;
00456                             servers[i].rtdisp=NTP32asDOUBLE(req[i].rtdisp);
00457                             servers[i].rtdelay=NTP32asDOUBLE(req[i].rtdelay);
00458                             servers[i].waiting=0;
00459                             servers[i].flags=req[i].flags;
00460                             servers_readable--;
00461                             one_read = 1;
00462                             if(servers[i].num_responses==AVG_NUM) servers_completed++;
00463                      }
00464               }
00465               /* lather, rinse, repeat. */
00466        }
00467 
00468        if (one_read == 0) {
00469               die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
00470        }
00471 
00472        /* now, pick the best server from the list */
00473        best_index=best_offset_server(servers, num_hosts);
00474        if(best_index < 0){
00475               *status=STATE_UNKNOWN;
00476        } else {
00477               /* finally, calculate the average offset */
00478               for(i=0; i<servers[best_index].num_responses;i++){
00479                      avg_offset+=servers[best_index].offset[j];
00480               }
00481               avg_offset/=servers[best_index].num_responses;
00482        }
00483 
00484        /* cleanup */
00485        /* FIXME: Not closing the socket to avoid re-use of the local port
00486         * which can cause old NTP packets to be read instead of NTP control
00487         * pactets in jitter_request(). THERE MUST BE ANOTHER WAY...
00488         * for(j=0; j<num_hosts; j++){ close(socklist[j]); } */
00489        free(socklist);
00490        free(ufds);
00491        free(servers);
00492        free(req);
00493        freeaddrinfo(ai);
00494 
00495        if(verbose) printf("overall average offset: %.10g\n", avg_offset);
00496        return avg_offset;
00497 }
00498 
00499 void
00500 setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){
00501        memset(p, 0, sizeof(ntp_control_message));
00502        LI_SET(p->flags, LI_NOWARNING);
00503        VN_SET(p->flags, VN_RESERVED);
00504        MODE_SET(p->flags, MODE_CONTROLMSG);
00505        OP_SET(p->op, opcode);
00506        p->seq = htons(seq);
00507        /* Remaining fields are zero for requests */
00508 }
00509 
00510 /* XXX handle responses with the error bit set */
00511 double jitter_request(const char *host, int *status){
00512        int conn=-1, i, npeers=0, num_candidates=0, syncsource_found=0;
00513        int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0;
00514        int peers_size=0, peer_offset=0;
00515        ntp_assoc_status_pair *peers=NULL;
00516        ntp_control_message req;
00517        const char *getvar = "jitter";
00518        double rval = 0.0, jitter = -1.0;
00519        char *startofvalue=NULL, *nptr=NULL;
00520        void *tmp;
00521 
00522        /* Long-winded explanation:
00523         * Getting the jitter requires a number of steps:
00524         * 1) Send a READSTAT request.
00525         * 2) Interpret the READSTAT reply
00526         *  a) The data section contains a list of peer identifiers (16 bits)
00527         *     and associated status words (16 bits)
00528         *  b) We want the value of 0x06 in the SEL (peer selection) value,
00529         *     which means "current synchronizatin source".  If that's missing,
00530         *     we take anything better than 0x04 (see the rfc for details) but
00531         *     set a minimum of warning.
00532         * 3) Send a READVAR request for information on each peer identified
00533         *    in 2b greater than the minimum selection value.
00534         * 4) Extract the jitter value from the data[] (it's ASCII)
00535         */
00536        my_udp_connect(server_address, 123, &conn);
00537 
00538        /* keep sending requests until the server stops setting the
00539         * REM_MORE bit, though usually this is only 1 packet. */
00540        do{
00541               setup_control_request(&req, OP_READSTAT, 1);
00542               DBG(printf("sending READSTAT request"));
00543               write(conn, &req, SIZEOF_NTPCM(req));
00544               DBG(print_ntp_control_message(&req));
00545               /* Attempt to read the largest size packet possible */
00546               req.count=htons(MAX_CM_SIZE);
00547               DBG(printf("recieving READSTAT response"))
00548               read(conn, &req, SIZEOF_NTPCM(req));
00549               DBG(print_ntp_control_message(&req));
00550               /* Each peer identifier is 4 bytes in the data section, which
00551                * we represent as a ntp_assoc_status_pair datatype.
00552                */
00553               peers_size+=ntohs(req.count);
00554               if((tmp=realloc(peers, peers_size)) == NULL)
00555                      free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n");
00556               peers=tmp;
00557               memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count));
00558               npeers=peers_size/sizeof(ntp_assoc_status_pair);
00559               peer_offset+=ntohs(req.count);
00560        } while(req.op&REM_MORE);
00561 
00562        /* first, let's find out if we have a sync source, or if there are
00563         * at least some candidates.  in the case of the latter we'll issue
00564         * a warning but go ahead with the check on them. */
00565        for (i = 0; i < npeers; i++){
00566               if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){
00567                      num_candidates++;
00568                      if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){
00569                             syncsource_found=1;
00570                             min_peer_sel=PEER_SYNCSOURCE;
00571                      }
00572               }
00573        }
00574        if(verbose) printf("%d candiate peers available\n", num_candidates);
00575        if(verbose && syncsource_found) printf("synchronization source found\n");
00576        if(! syncsource_found){
00577               *status = STATE_UNKNOWN;
00578               if(verbose) printf("warning: no synchronization source found\n");
00579        }
00580 
00581 
00582        for (run=0; run<AVG_NUM; run++){
00583               if(verbose) printf("jitter run %d of %d\n", run+1, AVG_NUM);
00584               for (i = 0; i < npeers; i++){
00585                      /* Only query this server if it is the current sync source */
00586                      if (PEER_SEL(peers[i].status) >= min_peer_sel){
00587                             num_selected++;
00588                             setup_control_request(&req, OP_READVAR, 2);
00589                             req.assoc = peers[i].assoc;
00590                             /* By spec, putting the variable name "jitter"  in the request
00591                              * should cause the server to provide _only_ the jitter value.
00592                              * thus reducing net traffic, guaranteeing us only a single
00593                              * datagram in reply, and making intepretation much simpler
00594                              */
00595                             /* Older servers doesn't know what jitter is, so if we get an
00596                              * error on the first pass we redo it with "dispersion" */
00597                             strncpy(req.data, getvar, MAX_CM_SIZE-1);
00598                             req.count = htons(strlen(getvar));
00599                             DBG(printf("sending READVAR request...\n"));
00600                             write(conn, &req, SIZEOF_NTPCM(req));
00601                             DBG(print_ntp_control_message(&req));
00602 
00603                             req.count = htons(MAX_CM_SIZE);
00604                             DBG(printf("recieving READVAR response...\n"));
00605                             read(conn, &req, SIZEOF_NTPCM(req));
00606                             DBG(print_ntp_control_message(&req));
00607 
00608                             if(req.op&REM_ERROR && strstr(getvar, "jitter")) {
00609                                    if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n");
00610                                    getvar = "dispersion";
00611                                    num_selected--;
00612                                    i--;
00613                                    continue;
00614                             }
00615 
00616                             /* get to the float value */
00617                             if(verbose) {
00618                                    printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc));
00619                             }
00620                             startofvalue = strchr(req.data, '=');
00621                             if(startofvalue != NULL) {
00622                                    startofvalue++;
00623                                    jitter = strtod(startofvalue, &nptr);
00624                             }
00625                             if(startofvalue == NULL || startofvalue==nptr){
00626                                    printf("warning: unable to read server jitter response.\n");
00627                                    *status = STATE_UNKNOWN;
00628                             } else {
00629                                    if(verbose) printf("%g\n", jitter);
00630                                    num_valid++;
00631                                    rval += jitter;
00632                             }
00633                      }
00634               }
00635               if(verbose){
00636                      printf("jitter parsed from %d/%d peers\n", num_valid, num_selected);
00637               }
00638        }
00639 
00640        rval = num_valid ? rval / num_valid : -1.0;
00641 
00642        close(conn);
00643        if(peers!=NULL) free(peers);
00644        /* If we return -1.0, it means no synchronization source was found */
00645        return rval;
00646 }
00647 
00648 int process_arguments(int argc, char **argv){
00649        int c;
00650        int option=0;
00651        static struct option longopts[] = {
00652               {"version", no_argument, 0, 'V'},
00653               {"help", no_argument, 0, 'h'},
00654               {"verbose", no_argument, 0, 'v'},
00655               {"use-ipv4", no_argument, 0, '4'},
00656               {"use-ipv6", no_argument, 0, '6'},
00657               {"warning", required_argument, 0, 'w'},
00658               {"critical", required_argument, 0, 'c'},
00659               {"jwarn", required_argument, 0, 'j'},
00660               {"jcrit", required_argument, 0, 'k'},
00661               {"timeout", required_argument, 0, 't'},
00662               {"hostname", required_argument, 0, 'H'},
00663               {0, 0, 0, 0}
00664        };
00665 
00666 
00667        if (argc < 2)
00668               usage ("\n");
00669 
00670        while (1) {
00671               c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option);
00672               if (c == -1 || c == EOF || c == 1)
00673                      break;
00674 
00675               switch (c) {
00676               case 'h':
00677                      print_help();
00678                      exit(STATE_OK);
00679                      break;
00680               case 'V':
00681                      print_revision(progname, NP_VERSION);
00682                      exit(STATE_OK);
00683                      break;
00684               case 'v':
00685                      verbose++;
00686                      break;
00687               case 'w':
00688                      do_offset=1;
00689                      owarn = optarg;
00690                      break;
00691               case 'c':
00692                      do_offset=1;
00693                      ocrit = optarg;
00694                      break;
00695               case 'j':
00696                      do_jitter=1;
00697                      jwarn = optarg;
00698                      break;
00699               case 'k':
00700                      do_jitter=1;
00701                      jcrit = optarg;
00702                      break;
00703               case 'H':
00704                      if(is_host(optarg) == FALSE)
00705                             usage2(_("Invalid hostname/address"), optarg);
00706                      server_address = strdup(optarg);
00707                      break;
00708               case 't':
00709                      socket_timeout=atoi(optarg);
00710                      break;
00711               case '4':
00712                      address_family = AF_INET;
00713                      break;
00714               case '6':
00715 #ifdef USE_IPV6
00716                      address_family = AF_INET6;
00717 #else
00718                      usage4 (_("IPv6 support not available"));
00719 #endif
00720                      break;
00721               case '?':
00722                      /* print short usage statement if args not parsable */
00723                      usage5 ();
00724                      break;
00725               }
00726        }
00727 
00728        if(server_address == NULL){
00729               usage4(_("Hostname was not supplied"));
00730        }
00731 
00732        return 0;
00733 }
00734 
00735 char *perfd_offset (double offset)
00736 {
00737        return fperfdata ("offset", offset, "s",
00738               TRUE, offset_thresholds->warning->end,
00739               TRUE, offset_thresholds->critical->end,
00740               FALSE, 0, FALSE, 0);
00741 }
00742 
00743 char *perfd_jitter (double jitter)
00744 {
00745        return fperfdata ("jitter", jitter, "s",
00746               do_jitter, jitter_thresholds->warning->end,
00747               do_jitter, jitter_thresholds->critical->end,
00748               TRUE, 0, FALSE, 0);
00749 }
00750 
00751 int main(int argc, char *argv[]){
00752        int result, offset_result, jitter_result;
00753        double offset=0, jitter=0;
00754        char *result_line, *perfdata_line;
00755 
00756        setlocale (LC_ALL, "");
00757        bindtextdomain (PACKAGE, LOCALEDIR);
00758        textdomain (PACKAGE);
00759 
00760        result = offset_result = jitter_result = STATE_OK;
00761 
00762        /* Parse extra opts if any */
00763        argv=np_extra_opts (&argc, argv, progname);
00764 
00765        if (process_arguments (argc, argv) == ERROR)
00766               usage4 (_("Could not parse arguments"));
00767 
00768        set_thresholds(&offset_thresholds, owarn, ocrit);
00769        set_thresholds(&jitter_thresholds, jwarn, jcrit);
00770 
00771        /* initialize alarm signal handling */
00772        signal (SIGALRM, socket_timeout_alarm_handler);
00773 
00774        /* set socket timeout */
00775        alarm (socket_timeout);
00776 
00777        offset = offset_request(server_address, &offset_result);
00778        /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN.
00779         * Now we'll only do that is the offset thresholds were set */
00780        if (do_offset && offset_result == STATE_UNKNOWN) {
00781               result = STATE_CRITICAL;
00782        } else {
00783               result = get_status(fabs(offset), offset_thresholds);
00784        }
00785 
00786        /* If not told to check the jitter, we don't even send packets.
00787         * jitter is checked using NTP control packets, which not all
00788         * servers recognize.  Trying to check the jitter on OpenNTPD
00789         * (for example) will result in an error
00790         */
00791        if(do_jitter){
00792               jitter=jitter_request(server_address, &jitter_result);
00793               result = max_state_alt(result, get_status(jitter, jitter_thresholds));
00794               /* -1 indicates that we couldn't calculate the jitter
00795                * Only overrides STATE_OK from the offset */
00796               if(jitter == -1.0 && result == STATE_OK)
00797                      result = STATE_UNKNOWN;
00798        }
00799        result = max_state_alt(result, jitter_result);
00800 
00801        switch (result) {
00802               case STATE_CRITICAL :
00803                      asprintf(&result_line, _("NTP CRITICAL:"));
00804                      break;
00805               case STATE_WARNING :
00806                      asprintf(&result_line, _("NTP WARNING:"));
00807                      break;
00808               case STATE_OK :
00809                      asprintf(&result_line, _("NTP OK:"));
00810                      break;
00811               default :
00812                      asprintf(&result_line, _("NTP UNKNOWN:"));
00813                      break;
00814        }
00815        if(offset_result == STATE_UNKNOWN){
00816               asprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
00817               asprintf(&perfdata_line, "");
00818        } else {
00819               asprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
00820               asprintf(&perfdata_line, "%s", perfd_offset(offset));
00821        }
00822        if (do_jitter) {
00823               asprintf(&result_line, "%s, jitter=%f", result_line, jitter);
00824               asprintf(&perfdata_line, "%s %s", perfdata_line,  perfd_jitter(jitter));
00825        }
00826        printf("%s|%s\n", result_line, perfdata_line);
00827 
00828        if(server_address!=NULL) free(server_address);
00829        return result;
00830 }
00831 
00832 
00833 
00834 void print_help(void){
00835        print_revision(progname, NP_VERSION);
00836 
00837        printf ("Copyright (c) 2006 Sean Finney\n");
00838        printf (COPYRIGHT, copyright, email);
00839 
00840        printf ("%s\n", _("This plugin checks the selected ntp server"));
00841 
00842        printf ("\n\n");
00843 
00844        print_usage();
00845        printf (UT_HELP_VRSN);
00846        printf (UT_EXTRA_OPTS);
00847        printf (UT_HOST_PORT, 'p', "123");
00848        printf (" %s\n", "-w, --warning=THRESHOLD");
00849        printf ("    %s\n", _("Offset to result in warning status (seconds)"));
00850        printf (" %s\n", "-c, --critical=THRESHOLD");
00851        printf ("    %s\n", _("Offset to result in critical status (seconds)"));
00852        printf (" %s\n", "-j, --jwarn=THRESHOLD");
00853        printf ("    %s\n", _("Warning threshold for jitter"));
00854        printf (" %s\n", "-k, --jcrit=THRESHOLD");
00855        printf ("    %s\n", _("Critical threshold for jitter"));
00856        printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
00857        printf (UT_VERBOSE);
00858 
00859        printf("\n");
00860        printf("%s\n", _("Notes:"));
00861        printf(UT_THRESHOLDS_NOTES);
00862 
00863        printf("\n");
00864        printf("%s\n", _("Examples:"));
00865        printf(" %s\n", _("Normal offset check:"));
00866        printf("  %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1"));
00867        printf("\n");
00868        printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available"));
00869        printf(" %s\n", _("(See Notes above for more details on thresholds formats):"));
00870        printf("  %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200"));
00871 
00872        printf (UT_SUPPORT);
00873 
00874        printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
00875        printf ("%s\n\n", _("check_ntp_time instead."));
00876 }
00877 
00878 void
00879 print_usage(void)
00880 {
00881        printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or"));
00882        printf ("%s\n\n", _("check_ntp_time instead."));
00883        printf ("%s\n", _("Usage:"));
00884        printf(" %s -H <host> [-w <warn>] [-c <crit>] [-j <warn>] [-k <crit>] [-v verbose]\n", progname);
00885 }