]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/bsnmp/snmp_ntp/snmp_ntp.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / bsnmp / snmp_ntp / snmp_ntp.c
1 /*
2  * Copyright (c) 2005
3  *      Hartmut Brandt.
4  *      All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution of this software and documentation and use in source and
9  * binary forms, with or without modification, are permitted provided that
10  * the following conditions are met:
11  *
12  * 1. Redistributions of source code or documentation must retain the above
13  *    copyright notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
19  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
22  * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $Begemot: bsnmp/snmp_ntp/snmp_ntp.c,v 1.9 2005/10/06 07:15:01 brandt_h Exp $
31  *
32  * NTP interface for SNMPd.
33  */
34
35 #include <sys/queue.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <sys/select.h>
39 #include <sys/socket.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #ifdef HAVE_STDINT_H
44 #include <stdint.h>
45 #elif defined(HAVE_INTTYPES_H)
46 #include <inttypes.h>
47 #endif
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53
54 #include "support.h"
55 #include "snmpmod.h"
56 #include "ntp_tree.h"
57 #include "ntp_oid.h"
58
59 #define NTPC_MAX        576
60 #define NTPC_VERSION    3
61 #define NTPC_MODE       6
62 #define NTPC_DMAX       468
63
64 #define NTPC_BIT_RESP   0x80
65 #define NTPC_BIT_ERROR  0x40
66 #define NTPC_BIT_MORE   0x20
67
68 #define NTPC_OPMASK     0x1f
69 #define NTPC_OP_READSTAT        1
70 #define NTPC_OP_READVAR         2
71
72 /* our module handle */
73 static struct lmodule *module;
74
75 /* debug flag */
76 static uint32_t ntp_debug;
77 #define DBG_DUMP_PKTS   0x01
78 #define DBG_DUMP_VARS   0x02
79
80 /* OIDs */
81 static const struct asn_oid oid_ntpMIB = OIDX_ntpMIB;
82
83 /* the Object Resource registration index */
84 static u_int reg_index;
85
86 /* last time we've fetch the system variables */
87 static uint64_t sysinfo_tick;
88
89 /* cached system variables */
90 static int32_t  sys_leap;
91 static int      sysb_leap;
92 static int32_t  sys_stratum;
93 static int      sysb_stratum;
94 static int32_t  sys_precision;
95 static int      sysb_precision;
96 static char     *sys_rootdelay;
97 static char     *sys_rootdispersion;
98 static char     *sys_refid;
99 static char     sys_reftime[8];
100 static int      sysb_reftime;
101 static int32_t  sys_poll;
102 static int      sysb_poll;
103 static uint32_t sys_peer;
104 static int      sysb_peer;
105 static u_char   sys_clock[8];
106 static int      sysb_clock;
107 static char     *sys_system;
108 static char     *sys_processor;
109 static int      sysb_jitter;
110 static double   sys_jitter;
111 static int      sysb_stability;
112 static double   sys_stability;
113
114 /* last time we've fetch the peer list */
115 static uint64_t peers_tick;
116
117 /* request sequence number generator */
118 static uint16_t seqno;
119
120 /* NTPD socket */
121 static int ntpd_sock;
122 static void *ntpd_fd;
123
124 struct peer {
125         /* required entries for macros */
126         uint32_t        index;
127         TAILQ_ENTRY(peer) link;
128
129         int32_t         config;         /* config bit */
130         u_char          srcadr[4];      /* PeerAddress */
131         uint32_t        srcport;        /* PeerPort */
132         u_char          dstadr[4];      /* HostAddress */
133         uint32_t        dstport;        /* HostPort */
134         int32_t         leap;           /* Leap */
135         int32_t         hmode;          /* Mode */
136         int32_t         stratum;        /* Stratum */
137         int32_t         ppoll;          /* PeerPoll */
138         int32_t         hpoll;          /* HostPoll */
139         int32_t         precision;      /* Precision */
140         char            *rootdelay;     /* RootDelay */
141         char            *rootdispersion;/* RootDispersion */
142         char            *refid;         /* RefId */
143         u_char          reftime[8];     /* RefTime */
144         u_char          orgtime[8];     /* OrgTime */
145         u_char          rcvtime[8];     /* ReceiveTime */
146         u_char          xmttime[8];     /* TransmitTime */
147         u_int32_t       reach;          /* Reach */
148         int32_t         timer;          /* Timer */
149         char            *offset;        /* Offset */
150         char            *delay;         /* Delay */
151         char            *dispersion;    /* Dispersion */
152         int32_t         filt_entries;
153 };
154 TAILQ_HEAD(peer_list, peer);
155
156 /* list of peers */
157 static struct peer_list peers = TAILQ_HEAD_INITIALIZER(peers);
158
159 struct filt {
160         /* required fields */
161         struct asn_oid  index;
162         TAILQ_ENTRY(filt) link;
163
164         char            *offset;
165         char            *delay;
166         char            *dispersion;
167 };
168 TAILQ_HEAD(filt_list, filt);
169
170 /* list of filters */
171 static struct filt_list filts = TAILQ_HEAD_INITIALIZER(filts);
172
173 /* configuration */
174 static u_char *ntp_host;
175 static u_char *ntp_port;
176 static uint32_t ntp_timeout;
177
178 static void ntpd_input(int, void *);
179 static int open_socket(void);
180
181 /* the initialization function */
182 static int
183 ntp_init(struct lmodule *mod, int argc, char *argv[] __unused)
184 {
185
186         module = mod;
187
188         if (argc != 0) {
189                 syslog(LOG_ERR, "bad number of arguments for %s", __func__);
190                 return (EINVAL);
191         }
192
193         ntp_host = strdup("localhost");
194         ntp_port = strdup("ntp");
195         ntp_timeout = 50;               /* 0.5sec */
196
197         return (0);
198 }
199
200 /*
201  * Module is started
202  */
203 static void
204 ntp_start(void)
205 {
206
207         if (open_socket() != -1) {
208                 ntpd_fd = fd_select(ntpd_sock, ntpd_input, NULL, module);
209                 if (ntpd_fd == NULL) {
210                         syslog(LOG_ERR, "fd_select failed on ntpd socket: %m");
211                         return;
212                 }
213         }
214         reg_index = or_register(&oid_ntpMIB, "The MIB for NTP.", module);
215 }
216
217 /*
218  * Called, when the module is to be unloaded after it was successfully loaded
219  */
220 static int
221 ntp_fini(void)
222 {
223
224         or_unregister(reg_index);
225         fd_deselect(ntpd_fd);
226
227         return (0);
228 }
229
230 const struct snmp_module config = {
231         .comment =      "This module implements the NTP MIB",
232         .init =         ntp_init,
233         .start =        ntp_start,
234         .fini =         ntp_fini,
235         .tree =         ntp_ctree,
236         .tree_size =    ntp_CTREE_SIZE,
237 };
238
239 /*
240  * Open the NTPD socket
241  */
242 static int
243 open_socket(void)
244 {
245         struct addrinfo hints, *res, *res0;
246         int     error;
247         const char *cause;
248
249         memset(&hints, 0, sizeof(hints));
250         hints.ai_family = AF_INET;
251         hints.ai_socktype = SOCK_DGRAM;
252
253         error = getaddrinfo(ntp_host, ntp_port, &hints, &res0);
254         if (error) {
255                 syslog(LOG_ERR, "%s(%s): %s", ntp_host, ntp_port,
256                     gai_strerror(error));
257                 return (-1);
258         }
259
260         ntpd_sock = -1;
261         cause = "no address";
262         errno = EADDRNOTAVAIL;
263         for (res = res0; res != NULL; res = res->ai_next) {
264                 ntpd_sock = socket(res->ai_family, res->ai_socktype,
265                     res->ai_protocol);
266                 if (ntpd_sock == -1) {
267                         cause = "socket";
268                         continue;
269                 }
270                 if (connect(ntpd_sock, res->ai_addr, res->ai_addrlen) == -1) {
271                         cause = "connect";
272                         (void)close(ntpd_sock);
273                         ntpd_sock = -1;
274                         continue;
275                 }
276                 break;
277         }
278         if (ntpd_sock == -1) {
279                 syslog(LOG_ERR, "%s: %m", cause);
280                 return (-1);
281         }
282         freeaddrinfo(res0);
283         return (0);
284 }
285
286 /*
287  * Dump a packet
288  */
289 static void
290 dump_packet(const u_char *pkt, size_t ret)
291 {
292         char buf[8 * 3 + 1];
293         size_t i, j;
294
295         for (i = 0; i < ret; i += 8) {
296                 buf[0] = '\0';
297                 for (j = 0; i + j < (size_t)ret && j < 8; j++)
298                         sprintf(buf + strlen(buf), " %02x", pkt[i + j]);
299                 syslog(LOG_INFO, "%04zu:%s", i, buf);
300         }
301 }
302
303 /*
304  * Execute an NTP request.
305  */
306 static int
307 ntpd_request(u_int op, u_int associd, const char *vars)
308 {
309         u_char  *rpkt;
310         u_char  *ptr;
311         size_t  vlen;
312         ssize_t ret;
313
314         if ((rpkt = malloc(NTPC_MAX)) == NULL) {
315                 syslog(LOG_ERR, "%m");
316                 return (-1);
317         }
318         memset(rpkt, 0, NTPC_MAX);
319
320         ptr = rpkt;
321         *ptr++ = (NTPC_VERSION << 3) | NTPC_MODE;
322         *ptr++ = op;
323
324         if (++seqno == 0)
325                 seqno++;
326         *ptr++ = seqno >> 8;
327         *ptr++ = seqno;
328
329         /* skip status */
330         ptr += 2;
331
332         *ptr++ = associd >> 8;
333         *ptr++ = associd;
334
335         /* skip offset */
336         ptr += 2;
337
338         if (vars != NULL) {
339                 vlen = strlen(vars);
340                 if (vlen > NTPC_DMAX) {
341                         syslog(LOG_ERR, "NTP request too long (%zu)", vlen);
342                         free(rpkt);
343                         return (-1);
344                 }
345                 *ptr++ = vlen >> 8;
346                 *ptr++ = vlen;
347
348                 memcpy(ptr, vars, vlen);
349                 ptr += vlen;
350         } else
351                 /* skip data length (is already zero) */
352                 ptr += 2;
353
354         while ((ptr - rpkt) % 4 != 0)
355                 *ptr++ = 0;
356
357         if (ntp_debug & DBG_DUMP_PKTS) {
358                 syslog(LOG_INFO, "sending %zd bytes", ptr - rpkt);
359                 dump_packet(rpkt, ptr - rpkt);
360         }
361
362         ret = send(ntpd_sock, rpkt, ptr - rpkt, 0);
363         if (ret == -1) {
364                 syslog(LOG_ERR, "cannot send to ntpd: %m");
365                 free(rpkt);
366                 return (-1);
367         }
368         return (0);
369 }
370
371 /*
372  * Callback if packet arrived from NTPD
373  */
374 static int
375 ntpd_read(uint16_t *op, uint16_t *associd, u_char **data, size_t *datalen)
376 {
377         u_char  pkt[NTPC_MAX + 1];
378         u_char  *ptr, *nptr;
379         u_int   n;
380         ssize_t ret;
381         size_t  z;
382         u_int   offset;         /* current offset */
383         int     more;           /* more flag */
384         int     sel;
385         struct timeval inc, end, rem;
386         fd_set  iset;
387
388         *datalen = 0;
389         *data = NULL;
390         offset = 0;
391
392         inc.tv_sec = ntp_timeout / 100;
393         inc.tv_usec = (ntp_timeout % 100) * 1000;
394
395         (void)gettimeofday(&end, NULL);
396         timeradd(&end, &inc, &end);
397
398   next:
399         /* compute remaining time */
400         (void)gettimeofday(&rem, NULL);
401         if (timercmp(&rem, &end, >=)) {
402                 /* do a poll */
403                 rem.tv_sec = 0;
404                 rem.tv_usec = 0;
405         } else {
406                 timersub(&end, &rem, &rem);
407         }
408
409         /* select */
410         FD_ZERO(&iset);
411         FD_SET(ntpd_sock, &iset);
412         sel = select(ntpd_sock + 1, &iset, NULL, NULL, &rem);
413         if (sel == -1) {
414                 if (errno == EINTR)
415                         goto next;
416                 syslog(LOG_ERR, "select ntpd_sock: %m");
417                 free(*data);
418                 return (-1);
419         }
420         if (sel == 0) {
421                 syslog(LOG_ERR, "timeout on NTP connection");
422                 free(*data);
423                 return (-1);
424         }
425
426         /* now read it */
427         ret = recv(ntpd_sock, pkt, sizeof(pkt), 0);
428         if (ret == -1) {
429                 syslog(LOG_ERR, "error reading from ntpd: %m");
430                 free(*data);
431                 return (-1);
432         }
433
434         if (ntp_debug & DBG_DUMP_PKTS) {
435                 syslog(LOG_INFO, "got %zd bytes", ret);
436                 dump_packet(pkt, (size_t)ret);
437         }
438
439         ptr = pkt;
440         if ((*ptr & 0x3f) != ((NTPC_VERSION << 3) | NTPC_MODE)) {
441                 syslog(LOG_ERR, "unexpected packet version 0x%x", *ptr);
442                 free(*data);
443                 return (-1);
444         }
445         ptr++;
446
447         if (!(*ptr & NTPC_BIT_RESP)) {
448                 syslog(LOG_ERR, "not a response packet");
449                 return (-1);
450         }
451         if (*ptr & NTPC_BIT_ERROR) {
452                 z = *datalen - 12;
453                 if (z > NTPC_DMAX)
454                         z = NTPC_DMAX;
455                 syslog(LOG_ERR, "error response: %.*s", (int)z, pkt + 12);
456                 free(*data);
457                 return (-1);
458         }
459         more = (*ptr & NTPC_BIT_MORE);
460
461         *op = *ptr++ & NTPC_OPMASK;
462
463         /* seqno */
464         n = *ptr++ << 8;
465         n |= *ptr++;
466
467         if (n != seqno) {
468                 syslog(LOG_ERR, "expecting seqno %u, got %u", seqno, n);
469                 free(*data);
470                 return (-1);
471         }
472
473         /* status */
474         n = *ptr++ << 8;
475         n |= *ptr++;
476
477         /* associd */
478         *associd = *ptr++ << 8;
479         *associd |= *ptr++;
480
481         /* offset */
482         n = *ptr++ << 8;
483         n |= *ptr++;
484
485         if (n != offset) {
486                 syslog(LOG_ERR, "offset: expecting %u, got %u", offset, n);
487                 free(*data);
488                 return (-1);
489         }
490
491         /* count */
492         n = *ptr++ << 8;
493         n |= *ptr++;
494
495         if ((size_t)ret < 12 + n) {
496                 syslog(LOG_ERR, "packet too short");
497                 return (-1);
498         }
499
500         nptr = realloc(*data, *datalen + n);
501         if (nptr == NULL) {
502                 syslog(LOG_ERR, "cannot allocate memory: %m");
503                 free(*data);
504                 return (-1);
505         }
506         *data = nptr;
507
508         memcpy(*data + offset, ptr, n);
509         *datalen += n;
510
511         if (!more)
512                 return (0);
513
514         offset += n;
515         goto next;
516 }
517
518 /*
519  * Send a request and wait for the response
520  */
521 static int
522 ntpd_dialog(u_int op, u_int associd, const char *vars, u_char **data,
523     size_t *datalen)
524 {
525         uint16_t rassocid;
526         uint16_t rop;
527
528         if (ntpd_request(op, associd, vars) == -1)
529                 return (-1);
530         if (ntpd_read(&rop, &rassocid, data, datalen) == -1)
531                 return (-1);
532
533         if (rop != op) {
534                 syslog(LOG_ERR, "bad response op 0x%x", rop);
535                 free(data);
536                 return (-1);
537         }
538
539         if (associd != rassocid) {
540                 syslog(LOG_ERR, "response for wrong associd");
541                 free(data);
542                 return (-1);
543         }
544         return (0);
545 }
546
547 /*
548  * Callback if packet arrived from NTPD
549  */
550 static void
551 ntpd_input(int fd __unused, void *arg __unused)
552 {
553         uint16_t associd;
554         uint16_t op;
555         u_char  *data;
556         size_t  datalen;
557
558         if (ntpd_read(&op, &associd, &data, &datalen) == -1)
559                 return;
560
561         free(data);
562 }
563
564 /*
565  * Find the value of a variable
566  */
567 static int
568 ntpd_parse(u_char **data, size_t *datalen, char **namep, char **valp)
569 {
570         u_char *ptr = *data;
571         u_char *end = ptr + *datalen;
572         char *ptr1;
573         char endc;
574
575         /* skip leading spaces */
576         while (ptr < end && isspace((int)*ptr))
577                 ptr++;
578
579         if (ptr == end)
580                 return (0);
581
582         *namep = ptr;
583
584         /* skip to space or '=' or ','*/
585         while (ptr < end && !isspace((int)*ptr) && *ptr != '=' && *ptr != ',')
586                 ptr++;
587         endc = *ptr;
588         *ptr++ = '\0';
589
590         /* skip space */
591         while (ptr < end && isspace((int)*ptr))
592                 ptr++;
593
594         if (ptr == end || endc == ',') {
595                 /* no value */
596                 *valp = NULL;
597                 *datalen -= ptr - *data;
598                 *data = ptr;
599                 return (1);
600         }
601
602         if (*ptr == '"') {
603                 /* quoted */
604                 ptr++;
605                 *valp = ptr;
606                 while (ptr < end && *ptr != '"')
607                         ptr++;
608                 if (ptr == end)
609                         return (0);
610
611                 *ptr++ = '\0';
612
613                 /* find comma */
614                 while (ptr < end && isspace((int)*ptr) && *ptr == ',')
615                         ptr++;
616         } else {
617                 *valp = ptr;
618
619                 /* skip to end of value */
620                 while (ptr < end && *ptr != ',')
621                         ptr++;
622
623                 /* remove trailing blanks */
624                 for (ptr1 = ptr; ptr1 > *valp; ptr1--)
625                         if (!isspace((int)ptr1[-1]))
626                                 break;
627                 *ptr1 = '\0';
628
629                 if (ptr < end)
630                         ptr++;
631         }
632
633         *datalen -= ptr - *data;
634         *data = ptr;
635
636         return (1);
637 }
638
639 /*
640  * Parse an int32 value
641  */
642 static int
643 val_parse_int32(const char *val, int32_t *p, int32_t min, int32_t max, int base)
644 {
645         long n;
646         char *end;
647
648         errno = 0;
649         n = strtol(val, &end, base);
650         if (errno != 0 || *end != '\0')
651                 return (0);
652         if (n < min || n > max)
653                 return (0);
654         *p = (int32_t)n;
655         return (1);
656 }
657
658 /*
659  * Parse an uint32 value
660  */
661 static int
662 val_parse_uint32(const char *val, uint32_t *p, uint32_t min, uint32_t max,
663     int base)
664 {
665         u_long n;
666         char *end;
667
668         errno = 0;
669         n = strtoul(val, &end, base);
670         if (errno != 0 || *end != '\0')
671                 return (0);
672         if (n < min || n > max)
673                 return (0);
674         *p = (uint32_t)n;
675         return (1);
676 }
677
678 /*
679  * Parse a double
680  */
681 static int
682 val_parse_double(const char *val, double *p)
683 {
684         char *end;
685
686         errno = 0;
687         *p = strtod(val, &end);
688         if (errno != 0 || *end != '\0')
689                 return (0);
690         return (1);
691 }
692
693 static int
694 val_parse_ts(const char *val, char *buf)
695 {
696         int r, n;
697         u_int i, f;
698
699         if (strlen(val) > 2 && val[0] == '0' && val[1] == 'x') {
700                 /* hex format */
701                 r = sscanf(val + 2, "%x.%x%n", &i, &f, &n);
702                 if (r != 2 || (size_t)n != strlen(val + 2))
703                         return (0);
704         } else {
705                 /* probably decimal */
706                 r = sscanf(val, "%d.%d%n", &i, &f, &n);
707                 if (r != 2 || (size_t)n != strlen(val))
708                         return (0);
709         }
710         buf[0] = i >> 24;
711         buf[1] = i >> 16;
712         buf[2] = i >>  8;
713         buf[3] = i >>  0;
714         buf[4] = f >> 24;
715         buf[5] = f >> 16;
716         buf[6] = f >>  8;
717         buf[7] = f >>  0;
718         return (1);
719 }
720
721 /*
722  * Parse an IP address. This resolves non-numeric names.
723  */
724 static int
725 val_parse_ip(const char *val, u_char ip[4])
726 {
727         int r, n, error;
728         struct addrinfo hints, *res0;
729         struct sockaddr_in *sin_local;
730
731         r = sscanf(val, "%hhd.%hhd.%hhd.%hhd%n",
732             &ip[0], &ip[1], &ip[2], &ip[3], &n);
733         if (n == 4 && (size_t)n == strlen(val))
734                 return (0);
735
736         memset(ip, 0, 4);
737
738         memset(&hints, 0, sizeof(hints));
739         hints.ai_family = AF_INET;
740         hints.ai_socktype = SOCK_DGRAM;
741
742         error = getaddrinfo(val, NULL, &hints, &res0);
743         if (error) {
744                 syslog(LOG_ERR, "%s: %s", val, gai_strerror(error));
745                 return (-1);
746         }
747         if (res0 == NULL) {
748                 syslog(LOG_ERR, "%s: no address", val);
749                 return (-1);
750         }
751
752         sin_local = (struct sockaddr_in *)(void *)res0->ai_addr;
753         ip[3] = sin_local->sin_addr.s_addr >> 24;
754         ip[2] = sin_local->sin_addr.s_addr >> 16;
755         ip[1] = sin_local->sin_addr.s_addr >>  8;
756         ip[0] = sin_local->sin_addr.s_addr >>  0;
757
758         freeaddrinfo(res0);
759         return (0);
760 }
761
762 /*
763  * Fetch system info
764  */
765 static int
766 fetch_sysinfo(void)
767 {
768         u_char *data;
769         u_char *ptr;
770         size_t datalen;
771         char *name;
772         char *val;
773
774         if (ntpd_dialog(NTPC_OP_READVAR, 0,
775             "leap,stratum,precision,rootdelay,rootdispersion,refid,reftime,"
776             "poll,peer,clock,system,processor,jitter,stability",
777             &data, &datalen))
778                 return (-1);
779
780         /* clear info */
781         sysb_leap = 0;
782         sysb_stratum = 0;
783         sysb_precision = 0;
784         free(sys_rootdelay);
785         sys_rootdelay = NULL;
786         free(sys_rootdispersion);
787         sys_rootdispersion = NULL;
788         free(sys_refid);
789         sys_refid = NULL;
790         sysb_reftime = 0;
791         sysb_poll = 0;
792         sysb_peer = 0;
793         sysb_clock = 0;
794         free(sys_system);
795         sys_system = NULL;
796         free(sys_processor);
797         sys_processor = NULL;
798         sysb_jitter = 0;
799         sysb_stability = 0;
800
801         ptr = data;
802         while (ntpd_parse(&ptr, &datalen, &name, &val)) {
803                 if (ntp_debug & DBG_DUMP_VARS)
804                         syslog(LOG_DEBUG, "%s: '%s'='%s'", __func__, name, val);
805                 if (strcmp(name, "leap") == 0 ||
806                     strcmp(name, "sys.leap") == 0) {
807                         sysb_leap = val_parse_int32(val, &sys_leap,
808                             0, 3, 2);
809
810                 } else if (strcmp(name, "stratum") == 0 ||
811                     strcmp(name, "sys.stratum") == 0) {
812                         sysb_stratum = val_parse_int32(val, &sys_stratum,
813                             0, 255, 0);
814
815                 } else if (strcmp(name, "precision") == 0 ||
816                     strcmp(name, "sys.precision") == 0) {
817                         sysb_precision = val_parse_int32(val, &sys_precision,
818                             INT32_MIN, INT32_MAX, 0);
819
820                 } else if (strcmp(name, "rootdelay") == 0 ||
821                     strcmp(name, "sys.rootdelay") == 0) {
822                         sys_rootdelay = strdup(val);
823
824                 } else if (strcmp(name, "rootdispersion") == 0 ||
825                     strcmp(name, "sys.rootdispersion") == 0) {
826                         sys_rootdispersion = strdup(val);
827
828                 } else if (strcmp(name, "refid") == 0 ||
829                     strcmp(name, "sys.refid") == 0) {
830                         sys_refid = strdup(val);
831
832                 } else if (strcmp(name, "reftime") == 0 ||
833                     strcmp(name, "sys.reftime") == 0) {
834                         sysb_reftime = val_parse_ts(val, sys_reftime);
835
836                 } else if (strcmp(name, "poll") == 0 ||
837                     strcmp(name, "sys.poll") == 0) {
838                         sysb_poll = val_parse_int32(val, &sys_poll,
839                             INT32_MIN, INT32_MAX, 0);
840
841                 } else if (strcmp(name, "peer") == 0 ||
842                     strcmp(name, "sys.peer") == 0) {
843                         sysb_peer = val_parse_uint32(val, &sys_peer,
844                             0, UINT32_MAX, 0);
845
846                 } else if (strcmp(name, "clock") == 0 ||
847                     strcmp(name, "sys.clock") == 0) {
848                         sysb_clock = val_parse_ts(val, sys_clock);
849
850                 } else if (strcmp(name, "system") == 0 ||
851                     strcmp(name, "sys.system") == 0) {
852                         sys_system = strdup(val);
853
854                 } else if (strcmp(name, "processor") == 0 ||
855                     strcmp(name, "sys.processor") == 0) {
856                         sys_processor = strdup(val);
857
858                 } else if (strcmp(name, "jitter") == 0 ||
859                     strcmp(name, "sys.jitter") == 0) {
860                         sysb_jitter = val_parse_double(val, &sys_jitter);
861
862                 } else if (strcmp(name, "stability") == 0 ||
863                     strcmp(name, "sys.stability") == 0) {
864                         sysb_stability = val_parse_double(val, &sys_stability);
865                 }
866         }
867
868         free(data);
869         return (0);
870 }
871
872 static int
873 parse_filt(char *val, uint16_t associd, int which)
874 {
875         char *w;
876         int cnt;
877         struct filt *f;
878
879         cnt = 0;
880         for (w = strtok(val, " \t"); w != NULL; w = strtok(NULL, " \t")) {
881                 TAILQ_FOREACH(f, &filts, link)
882                         if (f->index.subs[0] == associd &&
883                             f->index.subs[1] == (asn_subid_t)(cnt + 1))
884                                 break;
885                 if (f == NULL) {
886                         f = malloc(sizeof(*f));
887                         memset(f, 0, sizeof(*f));
888                         f->index.len = 2;
889                         f->index.subs[0] = associd;
890                         f->index.subs[1] = cnt + 1;
891
892                         INSERT_OBJECT_OID(f, &filts);
893                 }
894
895                 switch (which) {
896
897                   case 0:
898                         f->offset = strdup(w);
899                         break;
900
901                   case 1:
902                         f->delay = strdup(w);
903                         break;
904
905                   case 2:
906                         f->dispersion = strdup(w);
907                         break;
908
909                   default:
910                         abort();
911                 }
912                 cnt++;
913         }
914         return (cnt);
915 }
916
917 /*
918  * Fetch the complete peer list
919  */
920 static int
921 fetch_peers(void)
922 {
923         u_char *data, *pdata, *ptr;
924         size_t datalen, pdatalen;
925         int i;
926         struct peer *p;
927         struct filt *f;
928         uint16_t associd;
929         char *name, *val;
930
931         /* free the old list */
932         while ((p = TAILQ_FIRST(&peers)) != NULL) {
933                 TAILQ_REMOVE(&peers, p, link);
934                 free(p->rootdelay);
935                 free(p->rootdispersion);
936                 free(p->refid);
937                 free(p->offset);
938                 free(p->delay);
939                 free(p->dispersion);
940                 free(p);
941         }
942         while ((f = TAILQ_FIRST(&filts)) != NULL) {
943                 TAILQ_REMOVE(&filts, f, link);
944                 free(f->offset);
945                 free(f->delay);
946                 free(f->dispersion);
947                 free(f);
948         }
949
950         /* fetch the list of associations */
951         if (ntpd_dialog(NTPC_OP_READSTAT, 0, NULL, &data, &datalen))
952                 return (-1);
953
954         for (i = 0; i < (int)(datalen / 4); i++) {
955                 associd  = data[4 * i + 0] << 8;
956                 associd |= data[4 * i + 1] << 0;
957
958                 /* ask for the association variables */
959                 if (ntpd_dialog(NTPC_OP_READVAR, associd,
960                     "config,srcadr,srcport,dstadr,dstport,leap,hmode,stratum,"
961                     "hpoll,ppoll,precision,rootdelay,rootdispersion,refid,"
962                     "reftime,org,rec,xmt,reach,timer,offset,delay,dispersion,"
963                     "filtdelay,filtoffset,filtdisp",
964                     &pdata, &pdatalen)) {
965                         free(data);
966                         return (-1);
967                 }
968
969                 /* now save and parse the data */
970                 p = malloc(sizeof(*p));
971                 if (p == NULL) {
972                         free(data);
973                         syslog(LOG_ERR, "%m");
974                         return (-1);
975                 }
976                 memset(p, 0, sizeof(*p));
977                 p->index = associd;
978                 INSERT_OBJECT_INT(p, &peers);
979
980                 ptr = pdata;
981                 while (ntpd_parse(&ptr, &pdatalen, &name, &val)) {
982                         if (ntp_debug & DBG_DUMP_VARS)
983                                 syslog(LOG_DEBUG, "%s: '%s'='%s'",
984                                     __func__, name, val);
985                         if (strcmp(name, "config") == 0 ||
986                             strcmp(name, "peer.config") == 0) {
987                                 val_parse_int32(val, &p->config, 0, 1, 0);
988
989                         } else if (strcmp(name, "srcadr") == 0 ||
990                             strcmp(name, "peer.srcadr") == 0) {
991                                 val_parse_ip(val, p->srcadr);
992
993                         } else if (strcmp(name, "srcport") == 0 ||
994                             strcmp(name, "peer.srcport") == 0) {
995                                 val_parse_uint32(val, &p->srcport,
996                                     1, 65535, 0);
997
998                         } else if (strcmp(name, "dstadr") == 0 ||
999                             strcmp(name, "peer.dstadr") == 0) {
1000                                 val_parse_ip(val, p->dstadr);
1001
1002                         } else if (strcmp(name, "dstport") == 0 ||
1003                             strcmp(name, "peer.dstport") == 0) {
1004                                 val_parse_uint32(val, &p->dstport,
1005                                     1, 65535, 0);
1006
1007                         } else if (strcmp(name, "leap") == 0 ||
1008                             strcmp(name, "peer.leap") == 0) {
1009                                 val_parse_int32(val, &p->leap, 0, 3, 2);
1010
1011                         } else if (strcmp(name, "hmode") == 0 ||
1012                             strcmp(name, "peer.hmode") == 0) {
1013                                 val_parse_int32(val, &p->hmode, 0, 7, 0);
1014
1015                         } else if (strcmp(name, "stratum") == 0 ||
1016                             strcmp(name, "peer.stratum") == 0) {
1017                                 val_parse_int32(val, &p->stratum, 0, 255, 0);
1018
1019                         } else if (strcmp(name, "ppoll") == 0 ||
1020                             strcmp(name, "peer.ppoll") == 0) {
1021                                 val_parse_int32(val, &p->ppoll,
1022                                     INT32_MIN, INT32_MAX, 0);
1023
1024                         } else if (strcmp(name, "hpoll") == 0 ||
1025                             strcmp(name, "peer.hpoll") == 0) {
1026                                 val_parse_int32(val, &p->hpoll,
1027                                     INT32_MIN, INT32_MAX, 0);
1028
1029                         } else if (strcmp(name, "precision") == 0 ||
1030                             strcmp(name, "peer.precision") == 0) {
1031                                 val_parse_int32(val, &p->hpoll,
1032                                     INT32_MIN, INT32_MAX, 0);
1033
1034                         } else if (strcmp(name, "rootdelay") == 0 ||
1035                             strcmp(name, "peer.rootdelay") == 0) {
1036                                 p->rootdelay = strdup(val);
1037
1038                         } else if (strcmp(name, "rootdispersion") == 0 ||
1039                             strcmp(name, "peer.rootdispersion") == 0) {
1040                                 p->rootdispersion = strdup(val);
1041
1042                         } else if (strcmp(name, "refid") == 0 ||
1043                             strcmp(name, "peer.refid") == 0) {
1044                                 p->refid = strdup(val);
1045
1046                         } else if (strcmp(name, "reftime") == 0 ||
1047                             strcmp(name, "sys.reftime") == 0) {
1048                                 val_parse_ts(val, p->reftime);
1049
1050                         } else if (strcmp(name, "org") == 0 ||
1051                             strcmp(name, "sys.org") == 0) {
1052                                 val_parse_ts(val, p->orgtime);
1053
1054                         } else if (strcmp(name, "rec") == 0 ||
1055                             strcmp(name, "sys.rec") == 0) {
1056                                 val_parse_ts(val, p->rcvtime);
1057
1058                         } else if (strcmp(name, "xmt") == 0 ||
1059                             strcmp(name, "sys.xmt") == 0) {
1060                                 val_parse_ts(val, p->xmttime);
1061
1062                         } else if (strcmp(name, "reach") == 0 ||
1063                             strcmp(name, "peer.reach") == 0) {
1064                                 val_parse_uint32(val, &p->reach,
1065                                     0, 65535, 0);
1066
1067                         } else if (strcmp(name, "timer") == 0 ||
1068                             strcmp(name, "peer.timer") == 0) {
1069                                 val_parse_int32(val, &p->timer,
1070                                     INT32_MIN, INT32_MAX, 0);
1071
1072                         } else if (strcmp(name, "offset") == 0 ||
1073                             strcmp(name, "peer.offset") == 0) {
1074                                 p->offset = strdup(val);
1075
1076                         } else if (strcmp(name, "delay") == 0 ||
1077                             strcmp(name, "peer.delay") == 0) {
1078                                 p->delay = strdup(val);
1079
1080                         } else if (strcmp(name, "dispersion") == 0 ||
1081                             strcmp(name, "peer.dispersion") == 0) {
1082                                 p->dispersion = strdup(val);
1083
1084                         } else if (strcmp(name, "filtdelay") == 0 ||
1085                             strcmp(name, "peer.filtdelay") == 0) {
1086                                 p->filt_entries = parse_filt(val, associd, 0);
1087
1088                         } else if (strcmp(name, "filtoffset") == 0 ||
1089                             strcmp(name, "peer.filtoffset") == 0) {
1090                                 p->filt_entries = parse_filt(val, associd, 1);
1091
1092                         } else if (strcmp(name, "filtdisp") == 0 ||
1093                             strcmp(name, "peer.filtdisp") == 0) {
1094                                 p->filt_entries = parse_filt(val, associd, 2);
1095                         }
1096                 }
1097                 free(pdata);
1098         }
1099
1100         free(data);
1101         return (0);
1102 }
1103
1104 /*
1105  * System variables - read-only scalars only.
1106  */
1107 int
1108 op_ntpSystem(struct snmp_context *ctx __unused, struct snmp_value *value,
1109     u_int sub, u_int iidx __unused, enum snmp_op op)
1110 {
1111         asn_subid_t which = value->var.subs[sub - 1];
1112
1113         switch (op) {
1114
1115           case SNMP_OP_GETNEXT:
1116                 abort();
1117
1118           case SNMP_OP_GET:
1119                 if (this_tick > sysinfo_tick) {
1120                         if (fetch_sysinfo() == -1)
1121                                 return (SNMP_ERR_GENERR);
1122                         sysinfo_tick = this_tick;
1123                 }
1124
1125                 switch (which) {
1126
1127                   case LEAF_ntpSysLeap:
1128                         if (!sysb_leap)
1129                                 return (SNMP_ERR_NOSUCHNAME);
1130                         value->v.integer = sys_leap;
1131                         break;
1132
1133                   case LEAF_ntpSysStratum:
1134                         if (!sysb_stratum)
1135                                 return (SNMP_ERR_NOSUCHNAME);
1136                         value->v.integer = sys_stratum;
1137                         break;
1138
1139                   case LEAF_ntpSysPrecision:
1140                         if (!sysb_precision)
1141                                 return (SNMP_ERR_NOSUCHNAME);
1142                         value->v.integer = sys_precision;
1143                         break;
1144
1145                   case LEAF_ntpSysRootDelay:
1146                         if (sys_rootdelay == NULL)
1147                                 return (SNMP_ERR_NOSUCHNAME);
1148                         return (string_get(value, sys_rootdelay, -1));
1149
1150                   case LEAF_ntpSysRootDispersion:
1151                         if (sys_rootdispersion == NULL)
1152                                 return (SNMP_ERR_NOSUCHNAME);
1153                         return (string_get(value, sys_rootdispersion, -1));
1154
1155                   case LEAF_ntpSysRefId:
1156                         if (sys_refid == NULL)
1157                                 return (SNMP_ERR_NOSUCHNAME);
1158                         return (string_get(value, sys_refid, -1));
1159
1160                   case LEAF_ntpSysRefTime:
1161                         if (sysb_reftime == 0)
1162                                 return (SNMP_ERR_NOSUCHNAME);
1163                         return (string_get(value, sys_reftime, 8));
1164
1165                   case LEAF_ntpSysPoll:
1166                         if (sysb_poll == 0)
1167                                 return (SNMP_ERR_NOSUCHNAME);
1168                         value->v.integer = sys_poll;
1169                         break;
1170
1171                   case LEAF_ntpSysPeer:
1172                         if (sysb_peer == 0)
1173                                 return (SNMP_ERR_NOSUCHNAME);
1174                         value->v.uint32 = sys_peer;
1175                         break;
1176
1177                   case LEAF_ntpSysClock:
1178                         if (sysb_clock == 0)
1179                                 return (SNMP_ERR_NOSUCHNAME);
1180                         return (string_get(value, sys_clock, 8));
1181
1182                   case LEAF_ntpSysSystem:
1183                         if (sys_system == NULL)
1184                                 return (SNMP_ERR_NOSUCHNAME);
1185                         return (string_get(value, sys_system, -1));
1186
1187                   case LEAF_ntpSysProcessor:
1188                         if (sys_processor == NULL)
1189                                 return (SNMP_ERR_NOSUCHNAME);
1190                         return (string_get(value, sys_processor, -1));
1191
1192                   default:
1193                         abort();
1194                 }
1195                 return (SNMP_ERR_NOERROR);
1196
1197           case SNMP_OP_SET:
1198                 return (SNMP_ERR_NOT_WRITEABLE);
1199
1200           case SNMP_OP_COMMIT:
1201           case SNMP_OP_ROLLBACK:
1202                 abort();
1203         }
1204         abort();
1205 }
1206
1207 int
1208 op_ntpPeersVarTable(struct snmp_context *ctx __unused, struct snmp_value *value,
1209     u_int sub, u_int iidx, enum snmp_op op)
1210 {
1211         asn_subid_t which = value->var.subs[sub - 1];
1212         uint32_t peer;
1213         struct peer *t;
1214
1215         if (this_tick > peers_tick) {
1216                 if (fetch_peers() == -1)
1217                         return (SNMP_ERR_GENERR);
1218                 peers_tick = this_tick;
1219         }
1220
1221         switch (op) {
1222
1223           case SNMP_OP_GETNEXT:
1224                 t = NEXT_OBJECT_INT(&peers, &value->var, sub);
1225                 if (t == NULL)
1226                         return (SNMP_ERR_NOSUCHNAME);
1227                 value->var.len = sub + 1;
1228                 value->var.subs[sub] = t->index;
1229                 break;
1230
1231           case SNMP_OP_GET:
1232                 t = FIND_OBJECT_INT(&peers, &value->var, sub);
1233                 if (t == NULL)
1234                         return (SNMP_ERR_NOSUCHNAME);
1235                 break;
1236
1237           case SNMP_OP_SET:
1238                 if (index_decode(&value->var, sub, iidx, &peer))
1239                         return (SNMP_ERR_NO_CREATION);
1240                 t = FIND_OBJECT_INT(&peers, &value->var, sub);
1241                 if (t != NULL)
1242                         return (SNMP_ERR_NOT_WRITEABLE);
1243                 return (SNMP_ERR_NO_CREATION);
1244
1245           case SNMP_OP_COMMIT:
1246           case SNMP_OP_ROLLBACK:
1247           default:
1248                 abort();
1249         }
1250
1251         /*
1252          * Come here for GET and COMMIT
1253          */
1254         switch (which) {
1255
1256           case LEAF_ntpPeersConfigured:
1257                 value->v.integer = t->config;
1258                 break;
1259
1260           case LEAF_ntpPeersPeerAddress:
1261                 return (ip_get(value, t->srcadr));
1262
1263           case LEAF_ntpPeersPeerPort:
1264                 value->v.uint32 = t->srcport;
1265                 break;
1266
1267           case LEAF_ntpPeersHostAddress:
1268                 return (ip_get(value, t->dstadr));
1269
1270           case LEAF_ntpPeersHostPort:
1271                 value->v.uint32 = t->dstport;
1272                 break;
1273
1274           case LEAF_ntpPeersLeap:
1275                 value->v.integer = t->leap;
1276                 break;
1277
1278           case LEAF_ntpPeersMode:
1279                 value->v.integer = t->hmode;
1280                 break;
1281
1282           case LEAF_ntpPeersStratum:
1283                 value->v.integer = t->stratum;
1284                 break;
1285
1286           case LEAF_ntpPeersPeerPoll:
1287                 value->v.integer = t->ppoll;
1288                 break;
1289
1290           case LEAF_ntpPeersHostPoll:
1291                 value->v.integer = t->hpoll;
1292                 break;
1293
1294           case LEAF_ntpPeersPrecision:
1295                 value->v.integer = t->precision;
1296                 break;
1297
1298           case LEAF_ntpPeersRootDelay:
1299                 return (string_get(value, t->rootdelay, -1));
1300
1301           case LEAF_ntpPeersRootDispersion:
1302                 return (string_get(value, t->rootdispersion, -1));
1303
1304           case LEAF_ntpPeersRefId:
1305                 return (string_get(value, t->refid, -1));
1306
1307           case LEAF_ntpPeersRefTime:
1308                 return (string_get(value, t->reftime, 8));
1309
1310           case LEAF_ntpPeersOrgTime:
1311                 return (string_get(value, t->orgtime, 8));
1312
1313           case LEAF_ntpPeersReceiveTime:
1314                 return (string_get(value, t->rcvtime, 8));
1315
1316           case LEAF_ntpPeersTransmitTime:
1317                 return (string_get(value, t->xmttime, 8));
1318
1319           case LEAF_ntpPeersReach:
1320                 value->v.uint32 = t->reach;
1321                 break;
1322
1323           case LEAF_ntpPeersTimer:
1324                 value->v.uint32 = t->timer;
1325                 break;
1326
1327           case LEAF_ntpPeersOffset:
1328                 return (string_get(value, t->offset, -1));
1329
1330           case LEAF_ntpPeersDelay:
1331                 return (string_get(value, t->delay, -1));
1332
1333           case LEAF_ntpPeersDispersion:
1334                 return (string_get(value, t->dispersion, -1));
1335
1336           default:
1337                 abort();
1338         }
1339         return (SNMP_ERR_NOERROR);
1340 }
1341
1342
1343 int
1344 op_ntpFilterPeersVarTable(struct snmp_context *ctx __unused,
1345     struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op)
1346 {
1347         asn_subid_t which = value->var.subs[sub - 1];
1348         uint32_t peer;
1349         struct peer *t;
1350
1351         if (this_tick > peers_tick) {
1352                 if (fetch_peers() == -1)
1353                         return (SNMP_ERR_GENERR);
1354                 peers_tick = this_tick;
1355         }
1356
1357         switch (op) {
1358
1359           case SNMP_OP_GETNEXT:
1360                 t = NEXT_OBJECT_INT(&peers, &value->var, sub);
1361                 if (t == NULL)
1362                         return (SNMP_ERR_NOSUCHNAME);
1363                 value->var.len = sub + 1;
1364                 value->var.subs[sub] = t->index;
1365                 break;
1366
1367           case SNMP_OP_GET:
1368                 t = FIND_OBJECT_INT(&peers, &value->var, sub);
1369                 if (t == NULL)
1370                         return (SNMP_ERR_NOSUCHNAME);
1371                 break;
1372
1373           case SNMP_OP_SET:
1374                 if (index_decode(&value->var, sub, iidx, &peer))
1375                         return (SNMP_ERR_NO_CREATION);
1376                 t = FIND_OBJECT_INT(&peers, &value->var, sub);
1377                 if (t != NULL)
1378                         return (SNMP_ERR_NOT_WRITEABLE);
1379                 return (SNMP_ERR_NO_CREATION);
1380
1381           case SNMP_OP_COMMIT:
1382           case SNMP_OP_ROLLBACK:
1383           default:
1384                 abort();
1385         }
1386
1387         /*
1388          * Come here for GET and COMMIT
1389          */
1390         switch (which) {
1391
1392           case LEAF_ntpFilterValidEntries:
1393                 value->v.integer = t->filt_entries;
1394                 break;
1395
1396           default:
1397                 abort();
1398         }
1399         return (SNMP_ERR_NOERROR);
1400 }
1401
1402 int
1403 op_ntpFilterRegisterTable(struct snmp_context *ctx __unused, struct snmp_value *value __unused,
1404     u_int sub __unused, u_int iidx __unused, enum snmp_op op __unused)
1405 {
1406         asn_subid_t which = value->var.subs[sub - 1];
1407         uint32_t peer;
1408         uint32_t filt;
1409         struct filt *t;
1410
1411         if (this_tick > peers_tick) {
1412                 if (fetch_peers() == -1)
1413                         return (SNMP_ERR_GENERR);
1414                 peers_tick = this_tick;
1415         }
1416
1417         switch (op) {
1418
1419           case SNMP_OP_GETNEXT:
1420                 t = NEXT_OBJECT_OID(&filts, &value->var, sub);
1421                 if (t == NULL)
1422                         return (SNMP_ERR_NOSUCHNAME);
1423                 index_append(&value->var, sub, &t->index);
1424                 break;
1425
1426           case SNMP_OP_GET:
1427                 t = FIND_OBJECT_OID(&filts, &value->var, sub);
1428                 if (t == NULL)
1429                         return (SNMP_ERR_NOSUCHNAME);
1430                 break;
1431
1432           case SNMP_OP_SET:
1433                 if (index_decode(&value->var, sub, iidx, &peer, &filt))
1434                         return (SNMP_ERR_NO_CREATION);
1435                 t = FIND_OBJECT_OID(&filts, &value->var, sub);
1436                 if (t != NULL)
1437                         return (SNMP_ERR_NOT_WRITEABLE);
1438                 return (SNMP_ERR_NO_CREATION);
1439
1440           case SNMP_OP_COMMIT:
1441           case SNMP_OP_ROLLBACK:
1442           default:
1443                 abort();
1444         }
1445
1446         /*
1447          * Come here for GET and COMMIT
1448          */
1449         switch (which) {
1450
1451           case LEAF_ntpFilterPeersOffset:
1452                 return (string_get(value, t->offset, -1));
1453
1454           case LEAF_ntpFilterPeersDelay:
1455                 return (string_get(value, t->delay, -1));
1456
1457           case LEAF_ntpFilterPeersDispersion:
1458                 return (string_get(value, t->dispersion, -1));
1459
1460           default:
1461                 abort();
1462         }
1463         return (SNMP_ERR_NOERROR);
1464 }
1465
1466 /*
1467  * System variables - read-only scalars only.
1468  */
1469 int
1470 op_begemot_ntp(struct snmp_context *ctx __unused, struct snmp_value *value,
1471     u_int sub, u_int iidx __unused, enum snmp_op op)
1472 {
1473         asn_subid_t which = value->var.subs[sub - 1];
1474         int ret;
1475
1476         switch (op) {
1477
1478           case SNMP_OP_GETNEXT:
1479                 abort();
1480
1481           case SNMP_OP_GET:
1482                 switch (which) {
1483
1484                   case LEAF_begemotNtpHost:
1485                         return (string_get(value, ntp_host, -1));
1486
1487                   case LEAF_begemotNtpPort:
1488                         return (string_get(value, ntp_port, -1));
1489
1490                   case LEAF_begemotNtpTimeout:
1491                         value->v.uint32 = ntp_timeout;
1492                         return (SNMP_ERR_NOERROR);
1493
1494                   case LEAF_begemotNtpDebug:
1495                         value->v.uint32 = ntp_debug;
1496                         return (SNMP_ERR_NOERROR);
1497
1498                   case LEAF_begemotNtpJitter:
1499                         if (this_tick > sysinfo_tick) {
1500                                 if (fetch_sysinfo() == -1)
1501                                         return (SNMP_ERR_GENERR);
1502                                 sysinfo_tick = this_tick;
1503                         }
1504                         if (!sysb_jitter)
1505                                 return (SNMP_ERR_NOSUCHNAME);
1506                         value->v.counter64 = sys_jitter / 1000 * (1ULL << 32);
1507                         return (SNMP_ERR_NOERROR);
1508
1509                   case LEAF_begemotNtpStability:
1510                         if (this_tick > sysinfo_tick) {
1511                                 if (fetch_sysinfo() == -1)
1512                                         return (SNMP_ERR_GENERR);
1513                                 sysinfo_tick = this_tick;
1514                         }
1515                         if (!sysb_stability)
1516                                 return (SNMP_ERR_NOSUCHNAME);
1517                         value->v.counter64 = sys_stability * (1ULL << 32);
1518                         return (SNMP_ERR_NOERROR);
1519                 }
1520                 abort();
1521
1522           case SNMP_OP_SET:
1523                 switch (which) {
1524
1525                   case LEAF_begemotNtpHost:
1526                         /* only at initialization */
1527                         if (community != COMM_INITIALIZE)
1528                                 return (SNMP_ERR_NOT_WRITEABLE);
1529
1530                         if ((ret = string_save(value, ctx, -1, &ntp_host))
1531                             != SNMP_ERR_NOERROR)
1532                                 return (ret);
1533                         return (SNMP_ERR_NOERROR);
1534
1535                   case LEAF_begemotNtpPort:
1536                         /* only at initialization */
1537                         if (community != COMM_INITIALIZE)
1538                                 return (SNMP_ERR_NOT_WRITEABLE);
1539
1540                         if ((ret = string_save(value, ctx, -1, &ntp_port))
1541                             != SNMP_ERR_NOERROR)
1542                                 return (ret);
1543                         return (SNMP_ERR_NOERROR);
1544
1545                   case LEAF_begemotNtpTimeout:
1546                         ctx->scratch->int1 = ntp_timeout;
1547                         if (value->v.uint32 < 1)
1548                                 return (SNMP_ERR_WRONG_VALUE);
1549                         ntp_timeout = value->v.integer;
1550                         return (SNMP_ERR_NOERROR);
1551
1552                   case LEAF_begemotNtpDebug:
1553                         ctx->scratch->int1 = ntp_debug;
1554                         ntp_debug = value->v.integer;
1555                         return (SNMP_ERR_NOERROR);
1556                 }
1557                 abort();
1558
1559           case SNMP_OP_ROLLBACK:
1560                 switch (which) {
1561
1562                   case LEAF_begemotNtpHost:
1563                         string_rollback(ctx, &ntp_host);
1564                         return (SNMP_ERR_NOERROR);
1565
1566                   case LEAF_begemotNtpPort:
1567                         string_rollback(ctx, &ntp_port);
1568                         return (SNMP_ERR_NOERROR);
1569
1570                   case LEAF_begemotNtpTimeout:
1571                         ntp_timeout = ctx->scratch->int1;
1572                         return (SNMP_ERR_NOERROR);
1573
1574                   case LEAF_begemotNtpDebug:
1575                         ntp_debug = ctx->scratch->int1;
1576                         return (SNMP_ERR_NOERROR);
1577                 }
1578                 abort();
1579
1580           case SNMP_OP_COMMIT:
1581                 switch (which) {
1582
1583                   case LEAF_begemotNtpHost:
1584                         string_commit(ctx);
1585                         return (SNMP_ERR_NOERROR);
1586
1587                   case LEAF_begemotNtpPort:
1588                         string_commit(ctx);
1589                         return (SNMP_ERR_NOERROR);
1590
1591                   case LEAF_begemotNtpTimeout:
1592                   case LEAF_begemotNtpDebug:
1593                         return (SNMP_ERR_NOERROR);
1594                 }
1595                 abort();
1596         }
1597         abort();
1598 }