xref: /freebsd/contrib/bsnmp/snmp_ntp/snmp_ntp.c (revision 09a53ad8f1318c5daae6cfb19d97f4f6459f0013)
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 }
1599