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