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 */
75static struct lmodule *module;
76
77/* debug flag */
78static uint32_t ntp_debug;
79#define DBG_DUMP_PKTS	0x01
80#define	DBG_DUMP_VARS	0x02
81
82/* OIDs */
83static const struct asn_oid oid_ntpMIB = OIDX_ntpMIB;
84
85/* the Object Resource registration index */
86static u_int reg_index;
87
88/* last time we've fetch the system variables */
89static uint64_t sysinfo_tick;
90
91/* cached system variables */
92static int32_t	sys_leap;
93static int	sysb_leap;
94static int32_t	sys_stratum;
95static int	sysb_stratum;
96static int32_t	sys_precision;
97static int	sysb_precision;
98static char	*sys_rootdelay;
99static char	*sys_rootdispersion;
100static char	*sys_refid;
101static char	sys_reftime[8];
102static int	sysb_reftime;
103static int32_t	sys_poll;
104static int	sysb_poll;
105static uint32_t	sys_peer;
106static int	sysb_peer;
107static u_char	sys_clock[8];
108static int	sysb_clock;
109static char	*sys_system;
110static char	*sys_processor;
111static int	sysb_jitter;
112static double	sys_jitter;
113static int	sysb_stability;
114static double	sys_stability;
115
116/* last time we've fetch the peer list */
117static uint64_t peers_tick;
118
119/* request sequence number generator */
120static uint16_t	seqno;
121
122/* NTPD socket */
123static int ntpd_sock;
124static void *ntpd_fd;
125
126struct 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};
156TAILQ_HEAD(peer_list, peer);
157
158/* list of peers */
159static struct peer_list peers = TAILQ_HEAD_INITIALIZER(peers);
160
161struct 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};
170TAILQ_HEAD(filt_list, filt);
171
172/* list of filters */
173static struct filt_list filts = TAILQ_HEAD_INITIALIZER(filts);
174
175/* configuration */
176static u_char *ntp_host;
177static u_char *ntp_port;
178static uint32_t ntp_timeout;
179
180static void ntpd_input(int, void *);
181static int open_socket(void);
182
183/* the initialization function */
184static int
185ntp_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 */
205static void
206ntp_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 */
222static int
223ntp_fini(void)
224{
225
226	or_unregister(reg_index);
227	fd_deselect(ntpd_fd);
228
229	return (0);
230}
231
232const 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 */
244static int
245open_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 */
291static void
292dump_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 */
308static int
309ntpd_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 */
376static int
377ntpd_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 */
523static int
524ntpd_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 */
552static void
553ntpd_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 */
569static int
570ntpd_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 */
644static int
645val_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 */
663static int
664val_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 */
683static int
684val_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
695static int
696val_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 */
726static int
727val_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 */
767static int
768fetch_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
874static int
875parse_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 */
922static int
923fetch_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 */
1109int
1110op_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
1209int
1210op_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
1345int
1346op_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
1404int
1405op_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 */
1471int
1472op_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