PageRenderTime 102ms CodeModel.GetById 14ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bsnmp/snmp_ntp/snmp_ntp.c

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