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