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