1122405Sharti/*
2122405Sharti * Copyright (c) 2001-2003
3122405Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4122405Sharti *	All rights reserved.
5122405Sharti *
6122405Sharti * Author: Harti Brandt <harti@freebsd.org>
7122405Sharti *
8122405Sharti * Redistribution of this software and documentation and use in source and
9122405Sharti * binary forms, with or without modification, are permitted provided that
10122405Sharti * the following conditions are met:
11122405Sharti *
12122405Sharti * 1. Redistributions of source code or documentation must retain the above
13122405Sharti *    copyright notice, this list of conditions and the following disclaimer.
14122405Sharti * 2. Redistributions in binary form must reproduce the above copyright
15122405Sharti *    notice, this list of conditions and the following disclaimer in the
16122405Sharti *    documentation and/or other materials provided with the distribution.
17122405Sharti *
18122405Sharti * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
19122405Sharti * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20122405Sharti * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21122405Sharti * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
22122405Sharti * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
23122405Sharti * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24122405Sharti * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25122405Sharti * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26122405Sharti * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27122405Sharti * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28122405Sharti * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29122405Sharti *
30122405Sharti * $FreeBSD$
31122405Sharti *
32122405Sharti * Netgraph interface for SNMPd.
33122405Sharti */
34122405Sharti#include <sys/types.h>
35122405Sharti#include <sys/param.h>
36122405Sharti#include <sys/linker.h>
37122405Sharti#include <sys/socket.h>
38122405Sharti#include <sys/syslog.h>
39122405Sharti#include <sys/queue.h>
40122405Sharti#include <sys/sysctl.h>
41122405Sharti#include <stdio.h>
42122405Sharti#include <stdlib.h>
43122405Sharti#include <errno.h>
44122405Sharti#include <unistd.h>
45122405Sharti#include <string.h>
46122405Sharti#include <netgraph.h>
47152268Sharti#include <bsnmp/snmpmod.h>
48122405Sharti#include "snmp_netgraph.h"
49122405Sharti#include "netgraph_tree.h"
50122405Sharti#include "netgraph_oid.h"
51122405Sharti
52122405Sharti/* maximum message size */
53122405Sharti#define RESBUFSIZ	20000
54122405Sharti
55122405Sharti/* default node name */
56122405Sharti#define NODENAME	"NgSnmpd"
57122405Sharti
58122405Sharti/* my node Id */
59122405Sharting_ID_t snmp_node;
60122405Shartiu_char *snmp_nodename;
61122405Sharti
62122405Sharti/* the Object Resource registration index */
63122405Shartistatic u_int reg_index;
64122405Shartistatic const struct asn_oid oid_begemotNg = OIDX_begemotNg;
65122405Sharti
66122405Sharti/* configuration */
67122405Sharti/* this must be smaller than int32_t because the functions in libnetgraph
68122405Sharti * falsely return an int */
69122405Shartistatic size_t resbufsiz = RESBUFSIZ;
70122405Shartistatic u_int timeout = 1000;
71122405Shartistatic u_int debug_level;
72122405Sharti
73122405Sharti/* number of microseconds per clock tick */
74122405Shartistatic struct clockinfo clockinfo;
75122405Sharti
76122405Sharti/* Csock buffers. Communication on the csock is asynchronuous. This means
77122405Sharti * if we wait for a specific response, we may get other messages. Put these
78122405Sharti * into a queue and execute them when we are idle. */
79122405Shartistruct csock_buf {
80122405Sharti	STAILQ_ENTRY(csock_buf) link;
81122405Sharti	struct ng_mesg *mesg;
82122758Sharti	char path[NG_PATHSIZ];
83122405Sharti};
84122405Shartistatic STAILQ_HEAD(, csock_buf) csock_bufs =
85122405Sharti	STAILQ_HEAD_INITIALIZER(csock_bufs);
86122405Sharti
87122405Sharti/*
88122405Sharti * We dispatch unsolicieted messages by node cookies and ids.
89122405Sharti * So we must keep a list of hook names and dispatch functions.
90122405Sharti */
91122405Shartistruct msgreg {
92122405Sharti	u_int32_t 	cookie;
93122405Sharti	ng_ID_t		id;
94122405Sharti	ng_cookie_f	*func;
95122405Sharti	void		*arg;
96122405Sharti	const struct lmodule *mod;
97122405Sharti	SLIST_ENTRY(msgreg) link;
98122405Sharti};
99122405Shartistatic SLIST_HEAD(, msgreg) msgreg_list =
100122405Sharti	SLIST_HEAD_INITIALIZER(msgreg_list);
101122405Sharti
102122405Sharti/*
103122405Sharti * Data messages are dispatched by hook names.
104122405Sharti */
105122405Shartistruct datareg {
106122758Sharti	char		hook[NG_HOOKSIZ];
107122405Sharti	ng_hook_f	*func;
108122405Sharti	void		*arg;
109122405Sharti	const struct lmodule *mod;
110122405Sharti	SLIST_ENTRY(datareg) link;
111122405Sharti};
112122405Shartistatic SLIST_HEAD(, datareg) datareg_list =
113122405Sharti	SLIST_HEAD_INITIALIZER(datareg_list);
114122405Sharti
115122405Sharti/* the netgraph sockets */
116122405Shartistatic int csock, dsock;
117122405Shartistatic void *csock_fd, *dsock_fd;
118122405Sharti
119122405Sharti/* our module handle */
120122405Shartistatic struct lmodule *module;
121122405Sharti
122122405Sharti/* statistics */
123122405Shartistatic u_int32_t stats[LEAF_begemotNgTooLargeDatas+1];
124122405Sharti
125122405Sharti/* netgraph type list */
126122405Shartistruct ngtype {
127122758Sharti	char		name[NG_TYPESIZ];
128122405Sharti	struct asn_oid	index;
129122405Sharti	TAILQ_ENTRY(ngtype) link;
130122405Sharti};
131122405ShartiTAILQ_HEAD(ngtype_list, ngtype);
132122405Sharti
133122405Shartistatic struct ngtype_list ngtype_list;
134146529Shartistatic uint64_t ngtype_tick;
135122405Sharti
136122405Sharti
137122405Sharti/*
138122405Sharti * Register a function to receive unsolicited messages
139122405Sharti */
140122405Shartivoid *
141122405Sharting_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id,
142122405Sharti    ng_cookie_f *func, void *arg)
143122405Sharti{
144122405Sharti	struct msgreg *d;
145122405Sharti
146122405Sharti	if ((d = malloc(sizeof(*d))) == NULL)
147122405Sharti		return (NULL);
148122405Sharti
149122405Sharti	d->cookie = cookie;
150122405Sharti	d->id = id;
151122405Sharti	d->func = func;
152122405Sharti	d->arg = arg;
153122405Sharti	d->mod = mod;
154122405Sharti
155122405Sharti	SLIST_INSERT_HEAD(&msgreg_list, d, link);
156122405Sharti
157122405Sharti	return (d);
158122405Sharti}
159122405Sharti
160122405Sharti/*
161122405Sharti * Remove a registration.
162122405Sharti */
163122405Shartivoid
164122405Sharting_unregister_cookie(void *dd)
165122405Sharti{
166122405Sharti	struct msgreg *d = dd;
167122405Sharti
168122405Sharti	SLIST_REMOVE(&msgreg_list, d, msgreg, link);
169122405Sharti	free(d);
170122405Sharti}
171122405Sharti
172122405Sharti/*
173122405Sharti * Register a function for hook data.
174122405Sharti */
175122405Shartivoid *
176122405Sharting_register_hook(const struct lmodule *mod, const char *hook,
177122405Sharti    ng_hook_f *func, void *arg)
178122405Sharti{
179122405Sharti	struct datareg *d;
180122405Sharti
181122405Sharti	if ((d = malloc(sizeof(*d))) == NULL)
182122405Sharti		return (NULL);
183122405Sharti
184122405Sharti	strcpy(d->hook, hook);
185122405Sharti	d->func = func;
186122405Sharti	d->arg = arg;
187122405Sharti	d->mod = mod;
188122405Sharti
189122405Sharti	SLIST_INSERT_HEAD(&datareg_list, d, link);
190122405Sharti
191122405Sharti	return (d);
192122405Sharti}
193122405Sharti
194122405Sharti/*
195122405Sharti * Unregister a hook function
196122405Sharti */
197122405Shartivoid
198122405Sharting_unregister_hook(void *dd)
199122405Sharti{
200122405Sharti	struct datareg *d = dd;
201122405Sharti
202122405Sharti	SLIST_REMOVE(&datareg_list, d, datareg, link);
203122405Sharti	free(d);
204122405Sharti}
205122405Sharti
206122405Sharti/*
207122405Sharti * Unregister all hooks and cookies for that module. Note: doesn't disconnect
208122405Sharti * any hooks!
209122405Sharti */
210122405Shartivoid
211122405Sharting_unregister_module(const struct lmodule *mod)
212122405Sharti{
213122405Sharti	struct msgreg *m, *m1;
214122405Sharti	struct datareg *d, *d1;
215122405Sharti
216122405Sharti	m = SLIST_FIRST(&msgreg_list);
217122405Sharti	while (m != NULL) {
218122405Sharti		m1 = SLIST_NEXT(m, link);
219122405Sharti		if (m->mod == mod) {
220122405Sharti			SLIST_REMOVE(&msgreg_list, m, msgreg, link);
221122405Sharti			free(m);
222122405Sharti		}
223122405Sharti		m = m1;
224122405Sharti	}
225122405Sharti
226122405Sharti	d = SLIST_FIRST(&datareg_list);
227122405Sharti	while (d != NULL) {
228122405Sharti		d1 = SLIST_NEXT(d, link);
229122405Sharti		if (d->mod == mod) {
230122405Sharti			SLIST_REMOVE(&datareg_list, d, datareg, link);
231122405Sharti			free(d);
232122405Sharti		}
233122405Sharti		d = d1;
234122405Sharti	}
235122405Sharti}
236122405Sharti
237122405Sharti/*
238122405Sharti * Dispatch a message to the correct module and delete it. More than one
239122405Sharti * module can get a message.
240122405Sharti */
241122405Shartistatic void
242122405Sharticsock_handle(struct ng_mesg *mesg, const char *path)
243122405Sharti{
244122405Sharti	struct msgreg *d, *d1;
245122405Sharti	u_int id;
246122405Sharti	int len;
247122405Sharti
248122405Sharti	if (sscanf(path, "[%x]:%n", &id, &len) != 1 ||
249122405Sharti	    (u_int)len != strlen(path)) {
250122405Sharti		syslog(LOG_ERR, "cannot parse message path '%s'", path);
251122405Sharti		id = 0;
252122405Sharti	}
253122405Sharti
254122405Sharti	d = SLIST_FIRST(&msgreg_list);
255122405Sharti	while (d != NULL) {
256122405Sharti		d1 = SLIST_NEXT(d, link);
257122405Sharti		if (d->cookie == mesg->header.typecookie &&
258122405Sharti		    (d->id == 0 || d->id == id || id == 0))
259122405Sharti			(*d->func)(mesg, path, id, d->arg);
260122405Sharti		d = d1;
261122405Sharti	}
262122405Sharti	free(mesg);
263122405Sharti}
264122405Sharti
265122405Sharti/*
266122405Sharti * Input from the control socket.
267122405Sharti */
268122405Shartistatic struct ng_mesg *
269122405Sharticsock_read(char *path)
270122405Sharti{
271122405Sharti	struct ng_mesg *mesg;
272122405Sharti	int ret, err;
273122405Sharti
274122405Sharti	if ((mesg = malloc(resbufsiz + 1)) == NULL) {
275122405Sharti		stats[LEAF_begemotNgNoMems]++;
276122405Sharti		syslog(LOG_CRIT, "out of memory");
277122405Sharti		errno = ENOMEM;
278122405Sharti		return (NULL);
279122405Sharti	}
280122405Sharti	if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) {
281122405Sharti		err = errno;
282122405Sharti		free(mesg);
283122405Sharti		if (errno == EWOULDBLOCK) {
284122405Sharti			errno = err;
285122405Sharti			return (NULL);
286122405Sharti		}
287122405Sharti		stats[LEAF_begemotNgMsgReadErrs]++;
288122405Sharti		syslog(LOG_WARNING, "read from csock: %m");
289122405Sharti		errno = err;
290122405Sharti		return (NULL);
291122405Sharti	}
292122405Sharti	if (ret == 0) {
293122405Sharti		syslog(LOG_DEBUG, "node closed -- exiting");
294122405Sharti		exit(0);
295122405Sharti	}
296122405Sharti	if ((size_t)ret > resbufsiz) {
297122405Sharti		stats[LEAF_begemotNgTooLargeMsgs]++;
298122405Sharti		syslog(LOG_WARNING, "ng message too large");
299122405Sharti		free(mesg);
300122405Sharti		errno = EFBIG;
301122405Sharti		return (NULL);
302122405Sharti	}
303122405Sharti	return (mesg);
304122405Sharti}
305122405Sharti
306122405Shartistatic void
307122405Sharticsock_input(int fd __unused, void *udata __unused)
308122405Sharti{
309122405Sharti	struct ng_mesg *mesg;
310122758Sharti	char path[NG_PATHSIZ];
311122405Sharti
312122405Sharti	if ((mesg = csock_read(path)) == NULL)
313122405Sharti		return;
314122405Sharti
315122405Sharti	csock_handle(mesg, path);
316122405Sharti}
317122405Sharti
318122405Sharti/*
319122405Sharti * Write a message to a node.
320122405Sharti */
321122405Shartiint
322122405Sharting_output(const char *path, u_int cookie, u_int opcode,
323122405Sharti    const void *arg, size_t arglen)
324122405Sharti{
325122405Sharti	return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen));
326122405Sharti}
327122405Shartiint
328122405Sharting_output_node(const char *node, u_int cookie, u_int opcode,
329122405Sharti    const void *arg, size_t arglen)
330122405Sharti{
331122758Sharti	char path[NG_PATHSIZ];
332122405Sharti
333122405Sharti	sprintf(path, "%s:", node);
334122405Sharti	return (ng_output(path, cookie, opcode, arg, arglen));
335122405Sharti}
336122405Shartiint
337122405Sharting_output_id(ng_ID_t node, u_int cookie, u_int opcode,
338122405Sharti    const void *arg, size_t arglen)
339122405Sharti{
340122758Sharti	char path[NG_PATHSIZ];
341122405Sharti
342122405Sharti	sprintf(path, "[%x]:", node);
343122405Sharti	return (ng_output(path, cookie, opcode, arg, arglen));
344122405Sharti}
345122405Sharti
346122405Sharti
347122405Sharti
348122405Sharti/*
349122405Sharti * Execute a synchronuous dialog with the csock. All message we receive, that
350122405Sharti * do not match our request, are queue until the next call to the IDLE function.
351122405Sharti */
352122405Shartistruct ng_mesg *
353122405Sharting_dialog(const char *path, u_int cookie, u_int opcode,
354122405Sharti    const void *arg, size_t arglen)
355122405Sharti{
356122405Sharti	int token, err;
357122405Sharti	struct ng_mesg *mesg;
358122758Sharti	char rpath[NG_PATHSIZ];
359122405Sharti	struct csock_buf *b;
360122405Sharti	struct timeval end, tv;
361122405Sharti
362122405Sharti	if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0)
363122405Sharti		return (NULL);
364122405Sharti
365122405Sharti	if (csock_fd)
366122405Sharti		fd_suspend(csock_fd);
367122405Sharti
368122405Sharti	gettimeofday(&end, NULL);
369122405Sharti	tv.tv_sec = timeout / 1000;
370122405Sharti	tv.tv_usec = (timeout % 1000) * 1000;
371122405Sharti	timeradd(&end, &tv, &end);
372122405Sharti	for (;;) {
373122405Sharti		mesg = NULL;
374122405Sharti		gettimeofday(&tv, NULL);
375122405Sharti		if (timercmp(&tv, &end, >=)) {
376122405Sharti  block:
377122405Sharti			syslog(LOG_WARNING, "no response for request %u/%u",
378122405Sharti			    cookie, opcode);
379122405Sharti			errno = EWOULDBLOCK;
380122405Sharti			break;
381122405Sharti		}
382122405Sharti		timersub(&end, &tv, &tv);
383122405Sharti		if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick)
384122405Sharti			goto block;
385122405Sharti
386122405Sharti		if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
387122405Sharti			syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m");
388122405Sharti		if ((mesg = csock_read(rpath)) == NULL) {
389122405Sharti			if (errno == EWOULDBLOCK)
390122405Sharti				continue;
391122405Sharti			break;
392122405Sharti		}
393122405Sharti		if (mesg->header.token == (u_int)token)
394122405Sharti			break;
395122405Sharti		if ((b = malloc(sizeof(*b))) == NULL) {
396122405Sharti			stats[LEAF_begemotNgNoMems]++;
397122405Sharti			syslog(LOG_ERR, "out of memory");
398122405Sharti			free(mesg);
399122405Sharti			continue;
400122405Sharti		}
401122405Sharti		b->mesg = mesg;
402122405Sharti		strcpy(b->path, rpath);
403122405Sharti		STAILQ_INSERT_TAIL(&csock_bufs, b, link);
404122405Sharti	}
405122405Sharti
406122405Sharti	tv.tv_sec = 0;
407122405Sharti	tv.tv_usec = 0;
408122405Sharti	if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
409122405Sharti		syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m");
410122405Sharti
411122405Sharti	if (csock_fd) {
412122405Sharti		err = errno;
413122405Sharti		fd_resume(csock_fd);
414122405Sharti		errno = err;
415122405Sharti	}
416122405Sharti
417122405Sharti	return (mesg);
418122405Sharti}
419122405Shartistruct ng_mesg *
420122405Sharting_dialog_node(const char *node, u_int cookie, u_int opcode,
421122405Sharti    const void *arg, size_t arglen)
422122405Sharti{
423122758Sharti	char path[NG_PATHSIZ];
424122405Sharti
425122405Sharti	sprintf(path, "%s:", node);
426122405Sharti	return (ng_dialog(path, cookie, opcode, arg, arglen));
427122405Sharti}
428122405Shartistruct ng_mesg *
429122405Sharting_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,
430122405Sharti    const void *arg, size_t arglen)
431122405Sharti{
432122758Sharti	char path[NG_PATHSIZ];
433122405Sharti
434122405Sharti	sprintf(path, "[%x]:", id);
435122405Sharti	return (ng_dialog(path, cookie, opcode, arg, arglen));
436122405Sharti}
437122405Sharti
438122405Sharti
439122405Sharti/*
440122405Sharti * Send a data message to a given hook.
441122405Sharti */
442122405Shartiint
443122405Sharting_send_data(const char *hook, const void *sndbuf, size_t sndlen)
444122405Sharti{
445122405Sharti	return (NgSendData(dsock, hook, sndbuf, sndlen));
446122405Sharti}
447122405Sharti
448122405Sharti/*
449122405Sharti * Input from a data socket. Dispatch to the function for that hook.
450122405Sharti */
451122405Shartistatic void
452122405Shartidsock_input(int fd __unused, void *udata __unused)
453122405Sharti{
454122405Sharti	u_char *resbuf, embuf[100];
455122405Sharti	ssize_t len;
456122758Sharti	char hook[NG_HOOKSIZ];
457122405Sharti	struct datareg *d, *d1;
458122405Sharti
459122405Sharti	if ((resbuf = malloc(resbufsiz + 1)) == NULL) {
460122405Sharti		stats[LEAF_begemotNgNoMems]++;
461122405Sharti		syslog(LOG_CRIT, "out of memory");
462122405Sharti		(void)NgRecvData(fd, embuf, sizeof(embuf), hook);
463122405Sharti		errno = ENOMEM;
464122405Sharti		return;
465122405Sharti	}
466122405Sharti	if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) {
467122405Sharti		stats[LEAF_begemotNgDataReadErrs]++;
468122405Sharti		syslog(LOG_ERR, "reading message: %m");
469122405Sharti		free(resbuf);
470122405Sharti		return;
471122405Sharti	}
472122405Sharti	if (len == 0) {
473122405Sharti		free(resbuf);
474122405Sharti		return;
475122405Sharti	}
476122405Sharti	if ((size_t)len == resbufsiz + 1) {
477122405Sharti		stats[LEAF_begemotNgTooLargeDatas]++;
478122405Sharti		syslog(LOG_WARNING, "message too long");
479122405Sharti		free(resbuf);
480122405Sharti		return;
481122405Sharti	}
482122405Sharti
483122405Sharti	/*
484122405Sharti	 * Dispatch message. Maybe dispatched to more than one function.
485122405Sharti	 */
486122405Sharti	d = SLIST_FIRST(&datareg_list);
487122405Sharti	while (d != NULL) {
488122405Sharti		d1 = SLIST_NEXT(d, link);
489122405Sharti		if (strcmp(hook, d->hook) == 0)
490122405Sharti			(*d->func)(hook, resbuf, len, d->arg);
491122405Sharti		d = d1;
492122405Sharti	}
493122405Sharti
494122405Sharti	free(resbuf);
495122405Sharti}
496122405Sharti
497122405Sharti/*
498122405Sharti * The SNMP daemon is about to wait for an event. Look whether we have
499122405Sharti * netgraph messages waiting. If yes, drain the queue.
500122405Sharti */
501122405Shartistatic void
502122405Sharting_idle(void)
503122405Sharti{
504122405Sharti	struct csock_buf *b;
505122405Sharti
506122405Sharti	/* execute waiting csock_bufs */
507122405Sharti	while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) {
508122405Sharti		STAILQ_REMOVE_HEAD(&csock_bufs, link);
509122405Sharti		csock_handle(b->mesg, b->path);
510122405Sharti		free(b);
511122405Sharti	}
512122405Sharti}
513122405Sharti
514122405Sharti/*
515122405Sharti * Called when the module is loaded. Returning a non-zero value means,
516122405Sharti * rejecting the initialisation.
517122405Sharti *
518122405Sharti * We make the netgraph socket.
519122405Sharti */
520122405Shartistatic int
521122405Sharting_init(struct lmodule *mod, int argc, char *argv[])
522122405Sharti{
523122405Sharti	int name[2];
524122405Sharti	size_t len;
525122405Sharti
526122405Sharti	module = mod;
527122405Sharti
528122405Sharti	if (argc == 0) {
529122405Sharti		if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL)
530122405Sharti			return (ENOMEM);
531122405Sharti		strcpy(snmp_nodename, NODENAME);
532122405Sharti	} else {
533122758Sharti		if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL)
534122405Sharti			return (ENOMEM);
535122758Sharti		strlcpy(snmp_nodename, argv[0], NG_NODESIZ);
536122405Sharti	}
537122405Sharti
538122405Sharti	/* fetch clockinfo (for the number of microseconds per tick) */
539122405Sharti	name[0] = CTL_KERN;
540122405Sharti	name[1] = KERN_CLOCKRATE;
541122405Sharti	len = sizeof(clockinfo);
542122405Sharti	if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1)
543122405Sharti		return (errno);
544122405Sharti
545122405Sharti	TAILQ_INIT(&ngtype_list);
546122405Sharti
547122405Sharti	return (0);
548122405Sharti}
549122405Sharti
550122405Sharti/*
551122405Sharti * Get the node Id/name/type of a node.
552122405Sharti */
553122405Sharting_ID_t
554122405Sharting_node_id(const char *path)
555122405Sharti{
556122405Sharti	struct ng_mesg *resp;
557122405Sharti	ng_ID_t id;
558122405Sharti
559122405Sharti	if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO,
560122405Sharti	    NULL, 0)) == NULL)
561122405Sharti		return (0);
562122405Sharti	id = ((struct nodeinfo *)(void *)resp->data)->id;
563122405Sharti	free(resp);
564122405Sharti	return (id);
565122405Sharti}
566122405Sharting_ID_t
567122405Sharting_node_id_node(const char *node)
568122405Sharti{
569122405Sharti	struct ng_mesg *resp;
570122405Sharti	ng_ID_t id;
571122405Sharti
572122405Sharti	if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO,
573122405Sharti	    NULL, 0)) == NULL)
574122405Sharti		return (0);
575122405Sharti	id = ((struct nodeinfo *)(void *)resp->data)->id;
576122405Sharti	free(resp);
577122405Sharti	return (id);
578122405Sharti}
579122405Sharting_ID_t
580122405Sharting_node_name(ng_ID_t id, char *name)
581122405Sharti{
582122405Sharti	struct ng_mesg *resp;
583122405Sharti
584122405Sharti	if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
585122405Sharti	    NULL, 0)) == NULL)
586122405Sharti		return (0);
587122405Sharti	strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name);
588122405Sharti	free(resp);
589122405Sharti	return (id);
590122405Sharti
591122405Sharti}
592122405Sharting_ID_t
593122405Sharting_node_type(ng_ID_t id, char *type)
594122405Sharti{
595122405Sharti	struct ng_mesg *resp;
596122405Sharti
597122405Sharti	if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
598122405Sharti	    NULL, 0)) == NULL)
599122405Sharti		return (0);
600122405Sharti	strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type);
601122405Sharti	free(resp);
602122405Sharti	return (id);
603122405Sharti}
604122405Sharti
605122405Sharti/*
606122405Sharti * Connect our node to some other node
607122405Sharti */
608122405Shartiint
609122405Sharting_connect_node(const char *node, const char *ourhook, const char *peerhook)
610122405Sharti{
611122405Sharti	struct ngm_connect conn;
612122405Sharti
613122758Sharti	snprintf(conn.path, NG_PATHSIZ, "%s:", node);
614122758Sharti	strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
615122758Sharti	strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
616122405Sharti	return (NgSendMsg(csock, ".:",
617122405Sharti	    NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
618122405Sharti}
619122405Shartiint
620122405Sharting_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook)
621122405Sharti{
622122405Sharti	struct ngm_connect conn;
623122405Sharti
624122758Sharti	snprintf(conn.path, NG_PATHSIZ, "[%x]:", id);
625122758Sharti	strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
626122758Sharti	strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
627122405Sharti	return (NgSendMsg(csock, ".:",
628122405Sharti	    NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
629122405Sharti}
630122405Sharti
631122405Shartiint
632122405Sharting_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
633122405Sharti    const char *peerhook)
634122405Sharti{
635122405Sharti	struct ngm_connect conn;
636122758Sharti	char path[NG_PATHSIZ];
637122405Sharti
638122758Sharti	snprintf(path, NG_PATHSIZ, "[%x]:", id);
639122405Sharti
640122758Sharti	snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
641122758Sharti	strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
642122758Sharti	strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
643122405Sharti	return (NgSendMsg(csock, path,
644122405Sharti	    NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
645122405Sharti}
646122405Sharti
647122405Shartiint
648122405Sharting_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
649122405Sharti    const char *peerhook)
650122405Sharti{
651122405Sharti	struct ngm_connect conn;
652122758Sharti	char path[NG_PATHSIZ];
653122405Sharti	ng_ID_t tee;
654122405Sharti
655122405Sharti	if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0)
656122405Sharti		return (-1);
657122405Sharti
658122758Sharti	snprintf(path, NG_PATHSIZ, "[%x]:", tee);
659122405Sharti
660122758Sharti	snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
661122758Sharti	strlcpy(conn.ourhook, "right", NG_HOOKSIZ);
662122758Sharti	strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
663122405Sharti	return (NgSendMsg(csock, path,
664122405Sharti	    NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
665122405Sharti}
666122405Sharti
667122405Sharti/*
668122405Sharti * Ensure that a node of type 'type' is connected to 'hook' of 'node'
669122405Sharti * and return its node id. tee nodes between node and the target node
670122405Sharti * are skipped. If the type is wrong, or the hook is a dead-end return 0.
671122405Sharti * If type is NULL, it is not checked.
672122405Sharti */
673122405Shartistatic ng_ID_t
674122405Sharting_next_node_id_internal(ng_ID_t node, const char *type, const char *hook,
675122405Sharti    int skip_tee)
676122405Sharti{
677122405Sharti	struct ng_mesg *resp;
678122405Sharti	struct hooklist *hooklist;
679122405Sharti	u_int i;
680122405Sharti
681122405Sharti	if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
682122405Sharti	    NULL, 0)) == NULL) {
683122405Sharti		syslog(LOG_ERR, "get hook list: %m");
684122405Sharti		exit(1);
685122405Sharti	}
686122405Sharti	hooklist = (struct hooklist *)(void *)resp->data;
687122405Sharti
688122405Sharti	for (i = 0; i < hooklist->nodeinfo.hooks; i++)
689122405Sharti		if (strcmp(hooklist->link[i].ourhook, hook) == 0)
690122405Sharti			break;
691122405Sharti
692122405Sharti	if (i == hooklist->nodeinfo.hooks) {
693122405Sharti		free(resp);
694122405Sharti		return (0);
695122405Sharti	}
696122405Sharti
697122405Sharti	node = hooklist->link[i].nodeinfo.id;
698122405Sharti
699122405Sharti	if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
700122405Sharti		if (strcmp(hooklist->link[i].peerhook, "left") == 0)
701122405Sharti			node = ng_next_node_id(node, type, "right");
702122405Sharti		else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
703122405Sharti			node = ng_next_node_id(node, type, "left");
704122405Sharti		else if (type != NULL &&
705122405Sharti		    strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
706122405Sharti			node = 0;
707122405Sharti
708122405Sharti	} else if (type != NULL &&
709122405Sharti	    strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
710122405Sharti		node = 0;
711122405Sharti
712122405Sharti	free(resp);
713122405Sharti
714122405Sharti	return (node);
715122405Sharti}
716122405Sharti
717122405Sharti/*
718122405Sharti * Ensure that a node of type 'type' is connected to 'hook' of 'node'
719122405Sharti * and return its node id. tee nodes between node and the target node
720122405Sharti * are skipped. If the type is wrong, or the hook is a dead-end return 0.
721122405Sharti * If type is NULL, it is not checked.
722122405Sharti */
723122405Sharting_ID_t
724122405Sharting_next_node_id(ng_ID_t node, const char *type, const char *hook)
725122405Sharti{
726122405Sharti	return (ng_next_node_id_internal(node, type, hook, 1));
727122405Sharti}
728122405Sharti
729122405Sharting_ID_t
730122405Sharting_mkpeer_id(ng_ID_t id, const char *nodename, const char *type,
731122405Sharti    const char *hook, const char *peerhook)
732122405Sharti{
733122758Sharti	char path[NG_PATHSIZ];
734122405Sharti	struct ngm_mkpeer mkpeer;
735122405Sharti	struct ngm_name name;
736122405Sharti
737122758Sharti	strlcpy(mkpeer.type, type, NG_TYPESIZ);
738122758Sharti	strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ);
739122758Sharti	strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ);
740122405Sharti
741122405Sharti	sprintf(path, "[%x]:", id);
742122405Sharti	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,
743122405Sharti	    &mkpeer, sizeof(mkpeer)) == -1)
744122405Sharti		return (0);
745122405Sharti
746128235Sharti	if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0)
747122405Sharti		return (0);
748122405Sharti
749122405Sharti	if (nodename != NULL) {
750122405Sharti		strcpy(name.name, nodename);
751122405Sharti		sprintf(path, "[%x]:", id);
752122405Sharti		if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME,
753122405Sharti		    &name, sizeof(name)) == -1)
754122405Sharti			return (0);
755122405Sharti	}
756122405Sharti	return (id);
757122405Sharti}
758122405Sharti
759122405Sharti/*
760122405Sharti * SHutdown node
761122405Sharti */
762122405Shartiint
763122405Sharting_shutdown_id(ng_ID_t id)
764122405Sharti{
765122758Sharti	char path[NG_PATHSIZ];
766122405Sharti
767122758Sharti	snprintf(path, NG_PATHSIZ, "[%x]:", id);
768122405Sharti	return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
769122405Sharti	    NGM_SHUTDOWN, NULL, 0));
770122405Sharti}
771122405Sharti
772122405Sharti/*
773122405Sharti * Disconnect one of our hooks
774122405Sharti */
775122405Shartiint
776122405Sharting_rmhook(const char *ourhook)
777122405Sharti{
778122405Sharti	struct ngm_rmhook rmhook;
779122405Sharti
780122758Sharti	strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ);
781122405Sharti	return (NgSendMsg(csock, ".:",
782122405Sharti	    NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
783122405Sharti}
784122405Sharti
785122405Sharti/*
786122405Sharti * Disconnect a hook of a node
787122405Sharti */
788122405Shartiint
789122405Sharting_rmhook_id(ng_ID_t id, const char *hook)
790122405Sharti{
791122405Sharti	struct ngm_rmhook rmhook;
792122758Sharti	char path[NG_PATHSIZ];
793122405Sharti
794122758Sharti	strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ);
795122758Sharti	snprintf(path, NG_PATHSIZ, "[%x]:", id);
796122405Sharti	return (NgSendMsg(csock, path,
797122405Sharti	    NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
798122405Sharti}
799122405Sharti
800122405Sharti/*
801122405Sharti * Disconnect a hook and shutdown all tee nodes that were connected to that
802122405Sharti * hook.
803122405Sharti */
804122405Shartiint
805122405Sharting_rmhook_tee_id(ng_ID_t node, const char *hook)
806122405Sharti{
807122405Sharti	struct ng_mesg *resp;
808122405Sharti	struct hooklist *hooklist;
809122405Sharti	u_int i;
810122405Sharti	int first = 1;
811122405Sharti	ng_ID_t next_node;
812122405Sharti	const char *next_hook;
813122405Sharti
814122405Sharti  again:
815122405Sharti	/* if we have just shutdown a tee node, which had no other hooks
816122405Sharti	 * connected, the node id may already be wrong here. */
817122405Sharti	if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
818122405Sharti	    NULL, 0)) == NULL)
819122405Sharti		return (0);
820122405Sharti
821122405Sharti	hooklist = (struct hooklist *)(void *)resp->data;
822122405Sharti
823122405Sharti	for (i = 0; i < hooklist->nodeinfo.hooks; i++)
824122405Sharti		if (strcmp(hooklist->link[i].ourhook, hook) == 0)
825122405Sharti			break;
826122405Sharti
827122405Sharti	if (i == hooklist->nodeinfo.hooks) {
828122405Sharti		free(resp);
829122405Sharti		return (0);
830122405Sharti	}
831122405Sharti
832122405Sharti	next_node = 0;
833122405Sharti	next_hook = NULL;
834122405Sharti	if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
835122405Sharti		if (strcmp(hooklist->link[i].peerhook, "left") == 0) {
836122405Sharti			next_node = hooklist->link[i].nodeinfo.id;
837122405Sharti			next_hook = "right";
838122405Sharti		} else if (strcmp(hooklist->link[i].peerhook, "right") == 0) {
839122405Sharti			next_node = hooklist->link[i].nodeinfo.id;
840122405Sharti			next_hook = "left";
841122405Sharti		}
842122405Sharti	}
843122405Sharti	free(resp);
844122405Sharti
845122405Sharti	if (first) {
846122405Sharti		ng_rmhook_id(node, hook);
847122405Sharti		first = 0;
848122405Sharti	} else {
849122405Sharti		ng_shutdown_id(node);
850122405Sharti	}
851122405Sharti	if ((node = next_node) == 0)
852122405Sharti		return (0);
853122405Sharti	hook = next_hook;
854122405Sharti
855122405Sharti	goto again;
856122405Sharti}
857122405Sharti
858122405Sharti/*
859122405Sharti * Get the peer hook of a hook on a given node. Skip any tee nodes in between
860122405Sharti */
861122405Shartiint
862122405Sharting_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook)
863122405Sharti{
864122405Sharti	struct ng_mesg *resp;
865122405Sharti	struct hooklist *hooklist;
866122405Sharti	u_int i;
867122405Sharti	int ret;
868122405Sharti
869122405Sharti	if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
870122405Sharti	    NULL, 0)) == NULL) {
871122405Sharti		syslog(LOG_ERR, "get hook list: %m");
872122405Sharti		exit(1);
873122405Sharti	}
874122405Sharti	hooklist = (struct hooklist *)(void *)resp->data;
875122405Sharti
876122405Sharti	for (i = 0; i < hooklist->nodeinfo.hooks; i++)
877122405Sharti		if (strcmp(hooklist->link[i].ourhook, hook) == 0)
878122405Sharti			break;
879122405Sharti
880122405Sharti	if (i == hooklist->nodeinfo.hooks) {
881122405Sharti		free(resp);
882122405Sharti		return (-1);
883122405Sharti	}
884122405Sharti
885122405Sharti	node = hooklist->link[i].nodeinfo.id;
886122405Sharti
887122405Sharti	ret = 0;
888122405Sharti	if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
889122405Sharti		if (strcmp(hooklist->link[i].peerhook, "left") == 0)
890122405Sharti			ret = ng_peer_hook_id(node, "right", peerhook);
891122405Sharti		else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
892122405Sharti			ret = ng_peer_hook_id(node, "left", peerhook);
893122405Sharti		else
894122405Sharti			strcpy(peerhook, hooklist->link[i].peerhook);
895122405Sharti
896122405Sharti	} else
897122405Sharti		strcpy(peerhook, hooklist->link[i].peerhook);
898122405Sharti
899122405Sharti	free(resp);
900122405Sharti
901122405Sharti	return (ret);
902122405Sharti}
903122405Sharti
904122405Sharti
905122405Sharti/*
906122405Sharti * Now the module is started. Select on the sockets, so that we can get
907122405Sharti * unsolicited input.
908122405Sharti */
909122405Shartistatic void
910122405Sharting_start(void)
911122405Sharti{
912122405Sharti	if (snmp_node == 0) {
913122405Sharti		if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
914122405Sharti			syslog(LOG_ERR, "NgMkSockNode: %m");
915122405Sharti			exit(1);
916122405Sharti		}
917122405Sharti		snmp_node = ng_node_id(".:");
918122405Sharti	}
919122405Sharti
920122405Sharti	if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) {
921122405Sharti		syslog(LOG_ERR, "fd_select failed on csock: %m");
922122405Sharti		return;
923122405Sharti	}
924122405Sharti	if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) {
925122405Sharti		syslog(LOG_ERR, "fd_select failed on dsock: %m");
926122405Sharti		return;
927122405Sharti	}
928122405Sharti
929122405Sharti	reg_index = or_register(&oid_begemotNg,
930122405Sharti	    "The MIB for the NetGraph access module for SNMP.", module);
931122405Sharti}
932122405Sharti
933122405Sharti/*
934122405Sharti * Called, when the module is to be unloaded after it was successfully loaded
935122405Sharti */
936122405Shartistatic int
937122405Sharting_fini(void)
938122405Sharti{
939122405Sharti	struct ngtype *t;
940122405Sharti
941122405Sharti	while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
942122405Sharti		TAILQ_REMOVE(&ngtype_list, t, link);
943122405Sharti		free(t);
944122405Sharti	}
945122405Sharti
946122405Sharti	if (csock_fd != NULL)
947122405Sharti		fd_deselect(csock_fd);
948122405Sharti	(void)close(csock);
949122405Sharti
950122405Sharti	if (dsock_fd != NULL)
951122405Sharti		fd_deselect(dsock_fd);
952122405Sharti	(void)close(dsock);
953122405Sharti
954122405Sharti	free(snmp_nodename);
955122405Sharti
956122405Sharti	or_unregister(reg_index);
957122405Sharti
958122405Sharti	return (0);
959122405Sharti}
960122405Sharti
961122405Sharticonst struct snmp_module config = {
962122405Sharti	"This module implements access to the netgraph sub-system",
963122405Sharti	ng_init,
964122405Sharti	ng_fini,
965122405Sharti	ng_idle,
966122405Sharti	NULL,
967122405Sharti	NULL,
968122405Sharti	ng_start,
969122405Sharti	NULL,
970122405Sharti	netgraph_ctree,
971122405Sharti	netgraph_CTREE_SIZE,
972122405Sharti	NULL
973122405Sharti};
974122405Sharti
975122405Shartiint
976122405Shartiop_ng_config(struct snmp_context *ctx, struct snmp_value *value,
977122405Sharti    u_int sub, u_int iidx __unused, enum snmp_op op)
978122405Sharti{
979122405Sharti	asn_subid_t which = value->var.subs[sub - 1];
980122405Sharti	int ret;
981122405Sharti
982122405Sharti	switch (op) {
983122405Sharti
984122405Sharti	  case SNMP_OP_GETNEXT:
985122405Sharti		abort();
986122405Sharti
987122405Sharti	  case SNMP_OP_GET:
988122405Sharti		/*
989122405Sharti		 * Come here for GET, GETNEXT and COMMIT
990122405Sharti		 */
991122405Sharti		switch (which) {
992122405Sharti
993122405Sharti		  case LEAF_begemotNgControlNodeName:
994122405Sharti			return (string_get(value, snmp_nodename, -1));
995122405Sharti
996122405Sharti		  case LEAF_begemotNgResBufSiz:
997122405Sharti			value->v.integer = resbufsiz;
998122405Sharti			break;
999122405Sharti
1000122405Sharti		  case LEAF_begemotNgTimeout:
1001122405Sharti			value->v.integer = timeout;
1002122405Sharti			break;
1003122405Sharti
1004122405Sharti		  case LEAF_begemotNgDebugLevel:
1005122405Sharti			value->v.uint32 = debug_level;
1006122405Sharti			break;
1007122405Sharti
1008122405Sharti		  default:
1009122405Sharti			abort();
1010122405Sharti		}
1011122405Sharti		return (SNMP_ERR_NOERROR);
1012122405Sharti
1013122405Sharti	  case SNMP_OP_SET:
1014122405Sharti		switch (which) {
1015122405Sharti
1016122405Sharti		  case LEAF_begemotNgControlNodeName:
1017122405Sharti			/* only at initialisation */
1018122405Sharti			if (community != COMM_INITIALIZE)
1019122405Sharti				return (SNMP_ERR_NOT_WRITEABLE);
1020122405Sharti
1021122405Sharti			if (snmp_node != 0)
1022122405Sharti				return (SNMP_ERR_NOT_WRITEABLE);
1023122405Sharti
1024122405Sharti			if ((ret = string_save(value, ctx, -1, &snmp_nodename))
1025122405Sharti			    != SNMP_ERR_NOERROR)
1026122405Sharti				return (ret);
1027122405Sharti
1028122405Sharti			if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
1029122405Sharti				syslog(LOG_ERR, "NgMkSockNode: %m");
1030122405Sharti				string_rollback(ctx, &snmp_nodename);
1031122405Sharti				return (SNMP_ERR_GENERR);
1032122405Sharti			}
1033122405Sharti			snmp_node = ng_node_id(".:");
1034122405Sharti
1035122405Sharti			return (SNMP_ERR_NOERROR);
1036122405Sharti
1037122405Sharti		  case LEAF_begemotNgResBufSiz:
1038122405Sharti			ctx->scratch->int1 = resbufsiz;
1039122405Sharti			if (value->v.integer < 1024 ||
1040122405Sharti			    value->v.integer > 0x10000)
1041122405Sharti				return (SNMP_ERR_WRONG_VALUE);
1042122405Sharti			resbufsiz = value->v.integer;
1043122405Sharti			return (SNMP_ERR_NOERROR);
1044122405Sharti
1045122405Sharti		  case LEAF_begemotNgTimeout:
1046122405Sharti			ctx->scratch->int1 = timeout;
1047122405Sharti			if (value->v.integer < 10 ||
1048122405Sharti			    value->v.integer > 10000)
1049122405Sharti				return (SNMP_ERR_WRONG_VALUE);
1050122405Sharti			timeout = value->v.integer;
1051122405Sharti			return (SNMP_ERR_NOERROR);
1052122405Sharti
1053122405Sharti		  case LEAF_begemotNgDebugLevel:
1054122405Sharti			ctx->scratch->int1 = debug_level;
1055122405Sharti			debug_level = value->v.uint32;
1056122405Sharti			NgSetDebug(debug_level);
1057122405Sharti			return (SNMP_ERR_NOERROR);
1058122405Sharti		}
1059122405Sharti		abort();
1060122405Sharti
1061122405Sharti	  case SNMP_OP_ROLLBACK:
1062122405Sharti		switch (which) {
1063122405Sharti
1064122405Sharti		  case LEAF_begemotNgControlNodeName:
1065122405Sharti			string_rollback(ctx, &snmp_nodename);
1066122405Sharti			close(csock);
1067122405Sharti			close(dsock);
1068122405Sharti			snmp_node = 0;
1069122405Sharti			return (SNMP_ERR_NOERROR);
1070122405Sharti
1071122405Sharti		  case LEAF_begemotNgResBufSiz:
1072122405Sharti			resbufsiz = ctx->scratch->int1;
1073122405Sharti			return (SNMP_ERR_NOERROR);
1074122405Sharti
1075122405Sharti		  case LEAF_begemotNgTimeout:
1076122405Sharti			timeout = ctx->scratch->int1;
1077122405Sharti			return (SNMP_ERR_NOERROR);
1078122405Sharti
1079122405Sharti		  case LEAF_begemotNgDebugLevel:
1080122405Sharti			debug_level = ctx->scratch->int1;
1081122405Sharti			NgSetDebug(debug_level);
1082122405Sharti			return (SNMP_ERR_NOERROR);
1083122405Sharti		}
1084122405Sharti		abort();
1085122405Sharti
1086122405Sharti	  case SNMP_OP_COMMIT:
1087122405Sharti		switch (which) {
1088122405Sharti
1089122405Sharti		  case LEAF_begemotNgControlNodeName:
1090122405Sharti			string_commit(ctx);
1091122405Sharti			return (SNMP_ERR_NOERROR);
1092122405Sharti
1093122405Sharti		  case LEAF_begemotNgResBufSiz:
1094122405Sharti		  case LEAF_begemotNgTimeout:
1095122405Sharti		  case LEAF_begemotNgDebugLevel:
1096122405Sharti			return (SNMP_ERR_NOERROR);
1097122405Sharti		}
1098122405Sharti		abort();
1099122405Sharti	}
1100122405Sharti	abort();
1101122405Sharti}
1102122405Sharti
1103122405Shartiint
1104122405Shartiop_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
1105122405Sharti    u_int sub, u_int iidx __unused, enum snmp_op op)
1106122405Sharti{
1107122405Sharti	switch (op) {
1108122405Sharti
1109122405Sharti	  case SNMP_OP_GETNEXT:
1110122405Sharti		abort();
1111122405Sharti
1112122405Sharti	  case SNMP_OP_GET:
1113122405Sharti		value->v.uint32 = stats[value->var.subs[sub - 1] - 1];
1114122405Sharti		return (SNMP_ERR_NOERROR);
1115122405Sharti
1116122405Sharti	  case SNMP_OP_SET:
1117122405Sharti		return (SNMP_ERR_NOT_WRITEABLE);
1118122405Sharti
1119122405Sharti	  case SNMP_OP_ROLLBACK:
1120122405Sharti	  case SNMP_OP_COMMIT:
1121122405Sharti		abort();
1122122405Sharti	}
1123122405Sharti	abort();
1124122405Sharti}
1125122405Sharti
1126122405Sharti/*
1127122405Sharti * Netgraph type table
1128122405Sharti */
1129122405Shartistatic int
1130122405Shartifetch_types(void)
1131122405Sharti{
1132122405Sharti	struct ngtype *t;
1133122405Sharti	struct typelist *typelist;
1134122405Sharti	struct ng_mesg *resp;
1135122405Sharti	u_int u, i;
1136122405Sharti
1137122405Sharti	if (this_tick <= ngtype_tick)
1138122405Sharti		return (0);
1139122405Sharti
1140122405Sharti	while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
1141122405Sharti		TAILQ_REMOVE(&ngtype_list, t, link);
1142122405Sharti		free(t);
1143122405Sharti	}
1144122405Sharti
1145122405Sharti	if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE,
1146122405Sharti	    NGM_LISTTYPES, NULL, 0)) == NULL)
1147122405Sharti		return (SNMP_ERR_GENERR);
1148122405Sharti	typelist = (struct typelist *)(void *)resp->data;
1149122405Sharti
1150122405Sharti	for (u = 0; u < typelist->numtypes; u++) {
1151122405Sharti		if ((t = malloc(sizeof(*t))) == NULL) {
1152122405Sharti			free(resp);
1153122405Sharti			return (SNMP_ERR_GENERR);
1154122405Sharti		}
1155122405Sharti		strcpy(t->name, typelist->typeinfo[u].type_name);
1156122405Sharti		t->index.subs[0] = strlen(t->name);
1157122405Sharti		t->index.len = t->index.subs[0] + 1;
1158122405Sharti		for (i = 0; i < t->index.subs[0]; i++)
1159122405Sharti			t->index.subs[i + 1] = t->name[i];
1160122405Sharti
1161122405Sharti		INSERT_OBJECT_OID(t, &ngtype_list);
1162122405Sharti	}
1163122405Sharti
1164122405Sharti	ngtype_tick = this_tick;
1165122405Sharti
1166122405Sharti	free(resp);
1167122405Sharti	return (0);
1168122405Sharti}
1169122405Sharti
1170122405Sharti/*
1171122405Sharti * Try to load the netgraph type with the given name. We assume, that
1172122405Sharti * type 'type' is implemented in the kernel module 'ng_type'.
1173122405Sharti */
1174122405Shartistatic int
1175122405Shartingtype_load(const u_char *name, size_t namelen)
1176122405Sharti{
1177122405Sharti	char *mod;
1178122405Sharti	int ret;
1179122405Sharti
1180122405Sharti	if ((mod = malloc(namelen + 4)) == NULL)
1181122405Sharti		return (-1);
1182122405Sharti	strcpy(mod, "ng_");
1183122405Sharti	strncpy(mod + 3, name, namelen);
1184122405Sharti	mod[namelen + 3] = '\0';
1185122405Sharti
1186122405Sharti	ret = kldload(mod);
1187122405Sharti	free(mod);
1188122405Sharti	return (ret);
1189122405Sharti}
1190122405Sharti
1191122405Sharti/*
1192122405Sharti * Unload a netgraph type.
1193122405Sharti */
1194122405Shartistatic int
1195122405Shartingtype_unload(const u_char *name, size_t namelen)
1196122405Sharti{
1197122405Sharti	char *mod;
1198122405Sharti	int id;
1199122405Sharti
1200122405Sharti	if ((mod = malloc(namelen + 4)) == NULL)
1201122405Sharti		return (-1);
1202122405Sharti	strcpy(mod, "ng_");
1203122405Sharti	strncpy(mod + 3, name, namelen);
1204122405Sharti	mod[namelen + 3] = '\0';
1205122405Sharti
1206122405Sharti	if ((id = kldfind(mod)) == -1) {
1207122405Sharti		free(mod);
1208122405Sharti		return (-1);
1209122405Sharti	}
1210122405Sharti	free(mod);
1211122405Sharti	return (kldunload(id));
1212122405Sharti}
1213122405Sharti
1214122405Shartiint
1215122405Shartiop_ng_type(struct snmp_context *ctx, struct snmp_value *value,
1216122405Sharti    u_int sub, u_int iidx, enum snmp_op op)
1217122405Sharti{
1218122405Sharti	asn_subid_t which = value->var.subs[sub - 1];
1219122405Sharti	struct ngtype *t;
1220122405Sharti	u_char *name;
1221122405Sharti	size_t namelen;
1222122405Sharti	int status = 1;
1223122405Sharti	int ret;
1224122405Sharti
1225122405Sharti	switch (op) {
1226122405Sharti
1227122405Sharti	  case SNMP_OP_GETNEXT:
1228122405Sharti		if ((ret = fetch_types()) != 0)
1229122405Sharti			return (ret);
1230122405Sharti		if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1231122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1232122405Sharti		index_append(&value->var, sub, &t->index);
1233122405Sharti		break;
1234122405Sharti
1235122405Sharti	  case SNMP_OP_GET:
1236122405Sharti		if ((ret = fetch_types()) != 0)
1237122405Sharti			return (ret);
1238122405Sharti		if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1239122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1240122405Sharti		break;
1241122405Sharti
1242122405Sharti	  case SNMP_OP_SET:
1243122405Sharti		if (index_decode(&value->var, sub, iidx, &name, &namelen))
1244122405Sharti			return (SNMP_ERR_NO_CREATION);
1245122758Sharti		if (namelen == 0 || namelen >= NG_TYPESIZ) {
1246122405Sharti			free(name);
1247122405Sharti			return (SNMP_ERR_NO_CREATION);
1248122405Sharti		}
1249122405Sharti		if ((ret = fetch_types()) != 0) {
1250122405Sharti			free(name);
1251122405Sharti			return (ret);
1252122405Sharti		}
1253122405Sharti		t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub);
1254122405Sharti
1255122405Sharti		if (which != LEAF_begemotNgTypeStatus) {
1256122405Sharti			free(name);
1257122405Sharti			if (t != NULL)
1258122405Sharti				return (SNMP_ERR_NOT_WRITEABLE);
1259122405Sharti			return (SNMP_ERR_NO_CREATION);
1260122405Sharti		}
1261122405Sharti		if (!TRUTH_OK(value->v.integer)) {
1262122405Sharti			free(name);
1263122405Sharti			return (SNMP_ERR_WRONG_VALUE);
1264122405Sharti		}
1265122405Sharti		ctx->scratch->int1 = TRUTH_GET(value->v.integer);
1266122405Sharti		ctx->scratch->int1 |= (t != NULL) << 1;
1267122405Sharti		ctx->scratch->ptr2 = name;
1268122405Sharti		ctx->scratch->int2 = namelen;
1269122405Sharti
1270122405Sharti		if (t == NULL) {
1271122405Sharti			/* type not loaded */
1272122405Sharti			if (ctx->scratch->int1 & 1) {
1273122405Sharti				/* request to load */
1274122405Sharti				if (ngtype_load(name, namelen) == -1) {
1275122405Sharti					free(name);
1276122405Sharti					if (errno == ENOENT)
1277122405Sharti						return (SNMP_ERR_INCONS_NAME);
1278122405Sharti					else
1279122405Sharti						return (SNMP_ERR_GENERR);
1280122405Sharti				}
1281122405Sharti			}
1282122405Sharti		} else {
1283122405Sharti			/* is type loaded */
1284122405Sharti			if (!(ctx->scratch->int1 & 1)) {
1285122405Sharti				/* request to unload */
1286122405Sharti				if (ngtype_unload(name, namelen) == -1) {
1287122405Sharti					free(name);
1288122405Sharti					return (SNMP_ERR_GENERR);
1289122405Sharti				}
1290122405Sharti			}
1291122405Sharti		}
1292122405Sharti		return (SNMP_ERR_NOERROR);
1293122405Sharti
1294122405Sharti	  case SNMP_OP_ROLLBACK:
1295122405Sharti		ret = SNMP_ERR_NOERROR;
1296122405Sharti		if (!(ctx->scratch->int1 & 2)) {
1297122405Sharti			/* did not exist */
1298122405Sharti			if (ctx->scratch->int1 & 1) {
1299122405Sharti				/* request to load - unload */
1300122405Sharti				if (ngtype_unload(ctx->scratch->ptr2,
1301122405Sharti				    ctx->scratch->int2) == -1)
1302122405Sharti					ret = SNMP_ERR_UNDO_FAILED;
1303122405Sharti			}
1304122405Sharti		} else {
1305122405Sharti			/* did exist */
1306122405Sharti			if (!(ctx->scratch->int1 & 1)) {
1307122405Sharti				/* request to unload - reload */
1308122405Sharti				if (ngtype_load(ctx->scratch->ptr2,
1309122405Sharti				    ctx->scratch->int2) == -1)
1310122405Sharti					ret = SNMP_ERR_UNDO_FAILED;
1311122405Sharti			}
1312122405Sharti		}
1313122405Sharti		free(ctx->scratch->ptr2);
1314122405Sharti		return (ret);
1315122405Sharti
1316122405Sharti	  case SNMP_OP_COMMIT:
1317122405Sharti		free(ctx->scratch->ptr2);
1318122405Sharti		return (SNMP_ERR_NOERROR);
1319122405Sharti
1320122405Sharti	  default:
1321122405Sharti		abort();
1322122405Sharti	}
1323122405Sharti
1324122405Sharti	/*
1325122405Sharti	 * Come here for GET and COMMIT
1326122405Sharti	 */
1327122405Sharti	switch (which) {
1328122405Sharti
1329122405Sharti	  case LEAF_begemotNgTypeStatus:
1330122405Sharti		value->v.integer = status;
1331122405Sharti		break;
1332122405Sharti
1333122405Sharti	  default:
1334122405Sharti		abort();
1335122405Sharti	}
1336122405Sharti	return (SNMP_ERR_NOERROR);
1337122405Sharti}
1338122405Sharti
1339122405Sharti/*
1340122405Sharti * Implement the node table
1341122405Sharti */
1342122405Shartistatic int
1343122405Shartifind_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1344122405Sharti{
1345122405Sharti	ng_ID_t id = oid->subs[sub];
1346122405Sharti	struct ng_mesg *resp;
1347122405Sharti
1348122405Sharti	if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
1349122405Sharti	    NULL, 0)) == NULL)
1350122405Sharti		return (-1);
1351122405Sharti
1352122405Sharti	*info = *(struct nodeinfo *)(void *)resp->data;
1353122405Sharti	free(resp);
1354122405Sharti	return (0);
1355122405Sharti}
1356122405Sharti
1357122405Shartistatic int
1358122405Shartincmp(const void *p1, const void *p2)
1359122405Sharti{
1360122405Sharti	const struct nodeinfo *i1 = p1;
1361122405Sharti	const struct nodeinfo *i2 = p2;
1362122405Sharti
1363122405Sharti	if (i1->id < i2->id)
1364122405Sharti		return (-1);
1365122405Sharti	if (i1->id > i2->id)
1366122405Sharti		return (+1);
1367122405Sharti	return (0);
1368122405Sharti}
1369122405Sharti
1370122405Shartistatic int
1371122405Shartifind_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1372122405Sharti{
1373122405Sharti	u_int idxlen = oid->len - sub;
1374122405Sharti	struct ng_mesg *resp;
1375122405Sharti	struct namelist *list;
1376122405Sharti	ng_ID_t id;
1377122405Sharti	u_int i;
1378122405Sharti
1379122405Sharti	if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1380122405Sharti	    NULL, 0)) == NULL)
1381122405Sharti		return (-1);
1382122405Sharti	list = (struct namelist *)(void *)resp->data;
1383122405Sharti
1384122405Sharti	qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1385122405Sharti
1386122405Sharti	if (idxlen == 0) {
1387122405Sharti		if (list->numnames == 0) {
1388122405Sharti			free(resp);
1389122405Sharti			return (-1);
1390122405Sharti		}
1391122405Sharti		*info = list->nodeinfo[0];
1392122405Sharti		free(resp);
1393122405Sharti		return (0);
1394122405Sharti	}
1395122405Sharti	id = oid->subs[sub];
1396122405Sharti
1397122405Sharti	for (i = 0; i < list->numnames; i++)
1398122405Sharti		if (list->nodeinfo[i].id > id) {
1399122405Sharti			*info = list->nodeinfo[i];
1400122405Sharti			free(resp);
1401122405Sharti			return (0);
1402122405Sharti		}
1403122405Sharti
1404122405Sharti	free(resp);
1405122405Sharti	return (-1);
1406122405Sharti}
1407122405Sharti
1408122405Shartiint
1409122405Shartiop_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value,
1410122405Sharti    u_int sub, u_int iidx __unused, enum snmp_op op)
1411122405Sharti{
1412122405Sharti	asn_subid_t which = value->var.subs[sub - 1];
1413122405Sharti	u_int idxlen = value->var.len - sub;
1414122405Sharti	struct nodeinfo nodeinfo;
1415122405Sharti
1416122405Sharti	switch (op) {
1417122405Sharti
1418122405Sharti	  case SNMP_OP_GETNEXT:
1419122405Sharti		if (find_node_next(&value->var, sub, &nodeinfo) == -1)
1420122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1421122405Sharti		value->var.len = sub + 1;
1422122405Sharti		value->var.subs[sub] = nodeinfo.id;
1423122405Sharti		break;
1424122405Sharti
1425122405Sharti	  case SNMP_OP_GET:
1426122405Sharti		if (idxlen != 1)
1427122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1428122405Sharti		if (find_node(&value->var, sub, &nodeinfo) == -1)
1429122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1430122405Sharti		break;
1431122405Sharti
1432122405Sharti	  case SNMP_OP_SET:
1433122405Sharti		if (idxlen != 1)
1434122405Sharti			return (SNMP_ERR_NO_CREATION);
1435122405Sharti		if (find_node(&value->var, sub, &nodeinfo) == -1)
1436122405Sharti			return (SNMP_ERR_NO_CREATION);
1437122405Sharti		return (SNMP_ERR_NOT_WRITEABLE);
1438122405Sharti
1439122405Sharti	  case SNMP_OP_ROLLBACK:
1440122405Sharti	  case SNMP_OP_COMMIT:
1441122405Sharti	  default:
1442122405Sharti		abort();
1443122405Sharti	}
1444122405Sharti
1445122405Sharti	/*
1446122405Sharti	 * Come here for GET and COMMIT
1447122405Sharti	 */
1448122405Sharti	switch (which) {
1449122405Sharti
1450122405Sharti	  case LEAF_begemotNgNodeStatus:
1451122405Sharti		value->v.integer = 1;
1452122405Sharti		break;
1453122405Sharti	  case LEAF_begemotNgNodeName:
1454122405Sharti		return (string_get(value, nodeinfo.name, -1));
1455122405Sharti	  case LEAF_begemotNgNodeType:
1456122405Sharti		return (string_get(value, nodeinfo.type, -1));
1457122405Sharti	  case LEAF_begemotNgNodeHooks:
1458122405Sharti		value->v.uint32 = nodeinfo.hooks;
1459122405Sharti		break;
1460122405Sharti
1461122405Sharti	  default:
1462122405Sharti		abort();
1463122405Sharti	}
1464122405Sharti	return (SNMP_ERR_NOERROR);
1465122405Sharti}
1466122405Sharti
1467122405Sharti/*
1468122405Sharti * Implement the hook table
1469122405Sharti */
1470122405Shartistatic int
1471122405Shartifind_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info)
1472122405Sharti{
1473122405Sharti	struct ng_mesg *resp;
1474122405Sharti	struct hooklist *list;
1475122405Sharti	u_int i;
1476122405Sharti
1477122405Sharti	if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE,
1478122405Sharti	    NGM_LISTHOOKS, NULL, 0)) == NULL)
1479122405Sharti		return (-1);
1480122405Sharti
1481122405Sharti	list = (struct hooklist *)(void *)resp->data;
1482122405Sharti
1483122405Sharti	for (i = 0; i < list->nodeinfo.hooks; i++) {
1484122405Sharti		if (strlen(list->link[i].ourhook) == hooklen &&
1485122405Sharti		    strncmp(list->link[i].ourhook, hook, hooklen) == 0) {
1486122405Sharti			*info = list->link[i];
1487122405Sharti			free(resp);
1488122405Sharti			return (0);
1489122405Sharti		}
1490122405Sharti	}
1491122405Sharti	free(resp);
1492122405Sharti	return (-1);
1493122405Sharti}
1494122405Sharti
1495122405Shartistatic int
1496122405Shartihook_cmp(const void *p1, const void *p2)
1497122405Sharti{
1498122405Sharti	const struct linkinfo *i1 = p1;
1499122405Sharti	const struct linkinfo *i2 = p2;
1500122405Sharti
1501122405Sharti	if (strlen(i1->ourhook) < strlen(i2->ourhook))
1502122405Sharti		return (-1);
1503122405Sharti	if (strlen(i1->ourhook) > strlen(i2->ourhook))
1504122405Sharti		return (+1);
1505122405Sharti	return (strcmp(i1->ourhook, i2->ourhook));
1506122405Sharti}
1507122405Sharti
1508122405Shartistatic int
1509122405Shartifind_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo,
1510122405Sharti    struct linkinfo *linkinfo)
1511122405Sharti{
1512122405Sharti	u_int idxlen = oid->len - sub;
1513122405Sharti	struct namelist *list;
1514122405Sharti	struct ng_mesg *resp;
1515122405Sharti	struct hooklist *hooks;
1516122405Sharti	struct ng_mesg *resp1;
1517122405Sharti	u_int node_index;
1518122405Sharti	struct asn_oid idx;
1519122405Sharti	u_int i, j;
1520122405Sharti
1521122405Sharti	/*
1522122405Sharti	 * Get and sort Node list
1523122405Sharti	 */
1524122405Sharti	if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1525122405Sharti	    NULL, 0)) == NULL)
1526122405Sharti		return (-1);
1527122405Sharti	list = (struct namelist *)(void *)resp->data;
1528122405Sharti
1529122405Sharti	qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1530122405Sharti
1531122405Sharti	/*
1532122405Sharti	 * If we have no index, take the first node and return the
1533122405Sharti	 * first hook.
1534122405Sharti	 */
1535122405Sharti	if (idxlen == 0) {
1536122405Sharti		node_index = 0;
1537122405Sharti		goto return_first_hook;
1538122405Sharti	}
1539122405Sharti
1540122405Sharti	/*
1541122405Sharti	 * Locate node
1542122405Sharti	 */
1543122405Sharti	for (node_index = 0; node_index < list->numnames; node_index++)
1544122405Sharti		if (list->nodeinfo[node_index].id >= oid->subs[sub])
1545122405Sharti			break;
1546122405Sharti
1547122405Sharti	/*
1548122405Sharti	 * If we have only the node part of the index take, or
1549122405Sharti	 * there is no node with that Id, take the first hook of that node.
1550122405Sharti	 */
1551122405Sharti	if (idxlen == 1 || node_index >= list->numnames ||
1552122405Sharti	    list->nodeinfo[node_index].id > oid->subs[sub])
1553122405Sharti		goto return_first_hook;
1554122405Sharti
1555122405Sharti	/*
1556122405Sharti	 * We had an exact match on the node id and have (at last part)
1557122405Sharti	 * of the hook name index. Loop through the hooks of the node
1558122405Sharti	 * and find the next one.
1559122405Sharti	 */
1560122405Sharti	if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1561122405Sharti	    NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) {
1562122405Sharti		free(resp);
1563122405Sharti		return (-1);
1564122405Sharti	}
1565122405Sharti	hooks = (struct hooklist *)(void *)resp1->data;
1566122405Sharti	if (hooks->nodeinfo.hooks > 0) {
1567122405Sharti		qsort(hooks->link, hooks->nodeinfo.hooks,
1568122405Sharti		    sizeof(hooks->link[0]), hook_cmp);
1569122405Sharti		for (i = 0; i < hooks->nodeinfo.hooks; i++) {
1570122405Sharti			idx.len = strlen(hooks->link[i].ourhook) + 1;
1571122405Sharti			idx.subs[0] = idx.len - 1;
1572122405Sharti			for (j = 0; j < idx.len; j++)
1573122405Sharti				idx.subs[j + 1] = hooks->link[i].ourhook[j];
1574122405Sharti			if (index_compare(oid, sub + 1, &idx) < 0)
1575122405Sharti				break;
1576122405Sharti		}
1577122405Sharti		if (i < hooks->nodeinfo.hooks) {
1578122405Sharti			*nodeinfo = hooks->nodeinfo;
1579122405Sharti			*linkinfo = hooks->link[i];
1580122405Sharti
1581122405Sharti			free(resp);
1582122405Sharti			free(resp1);
1583122405Sharti			return (0);
1584122405Sharti		}
1585122405Sharti	}
1586122405Sharti
1587122405Sharti	/* no hook found larger than the index on the index node - take
1588122405Sharti	 * first hook of next node */
1589122405Sharti	free(resp1);
1590122405Sharti	node_index++;
1591122405Sharti
1592122405Sharti  return_first_hook:
1593122405Sharti	while (node_index < list->numnames) {
1594122405Sharti		if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1595122405Sharti		    NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL)
1596122405Sharti			break;
1597122405Sharti		hooks = (struct hooklist *)(void *)resp1->data;
1598122405Sharti		if (hooks->nodeinfo.hooks > 0) {
1599122405Sharti			qsort(hooks->link, hooks->nodeinfo.hooks,
1600122405Sharti			    sizeof(hooks->link[0]), hook_cmp);
1601122405Sharti
1602122405Sharti			*nodeinfo = hooks->nodeinfo;
1603122405Sharti			*linkinfo = hooks->link[0];
1604122405Sharti
1605122405Sharti			free(resp);
1606122405Sharti			free(resp1);
1607122405Sharti			return (0);
1608122405Sharti		}
1609122405Sharti
1610122405Sharti		/* if we don't have hooks, try next node */
1611122405Sharti		free(resp1);
1612122405Sharti		node_index++;
1613122405Sharti	}
1614122405Sharti
1615122405Sharti	free(resp);
1616122405Sharti	return (-1);
1617122405Sharti}
1618122405Sharti
1619122405Shartiint
1620122405Shartiop_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value,
1621122405Sharti    u_int sub, u_int iidx, enum snmp_op op)
1622122405Sharti{
1623122405Sharti	asn_subid_t which = value->var.subs[sub - 1];
1624122405Sharti	struct linkinfo linkinfo;
1625122405Sharti	struct nodeinfo nodeinfo;
1626122405Sharti	u_int32_t lid;
1627122405Sharti	u_char *hook;
1628122405Sharti	size_t hooklen;
1629122405Sharti	u_int i;
1630122405Sharti
1631122405Sharti	switch (op) {
1632122405Sharti
1633122405Sharti	  case SNMP_OP_GETNEXT:
1634122405Sharti		if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1)
1635122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1636122405Sharti
1637122405Sharti		value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook);
1638122405Sharti		value->var.subs[sub] = nodeinfo.id;
1639122405Sharti		value->var.subs[sub + 1] = strlen(linkinfo.ourhook);
1640122405Sharti		for (i = 0; i < strlen(linkinfo.ourhook); i++)
1641122405Sharti			value->var.subs[sub + i + 2] =
1642122405Sharti			    linkinfo.ourhook[i];
1643122405Sharti		break;
1644122405Sharti
1645122405Sharti	  case SNMP_OP_GET:
1646122405Sharti		if (index_decode(&value->var, sub, iidx, &lid,
1647122405Sharti		    &hook, &hooklen))
1648122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1649122405Sharti		if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1650122405Sharti			free(hook);
1651122405Sharti			return (SNMP_ERR_NOSUCHNAME);
1652122405Sharti		}
1653122405Sharti		free(hook);
1654122405Sharti		break;
1655122405Sharti
1656122405Sharti	  case SNMP_OP_SET:
1657122405Sharti		if (index_decode(&value->var, sub, iidx, &lid,
1658122405Sharti		    &hook, &hooklen))
1659122405Sharti			return (SNMP_ERR_NO_CREATION);
1660122405Sharti		if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1661122405Sharti			free(hook);
1662122405Sharti			return (SNMP_ERR_NO_CREATION);
1663122405Sharti		}
1664122405Sharti		free(hook);
1665122405Sharti		return (SNMP_ERR_NOT_WRITEABLE);
1666122405Sharti
1667122405Sharti	  case SNMP_OP_ROLLBACK:
1668122405Sharti	  case SNMP_OP_COMMIT:
1669122405Sharti	  default:
1670122405Sharti		abort();
1671122405Sharti
1672122405Sharti	}
1673122405Sharti
1674122405Sharti	switch (which) {
1675122405Sharti
1676122405Sharti	  case LEAF_begemotNgHookStatus:
1677122405Sharti		value->v.integer = 1;
1678122405Sharti		break;
1679122405Sharti	  case LEAF_begemotNgHookPeerNodeId:
1680122405Sharti		value->v.uint32 = linkinfo.nodeinfo.id;
1681122405Sharti		break;
1682122405Sharti	  case LEAF_begemotNgHookPeerHook:
1683122405Sharti		return (string_get(value, linkinfo.peerhook, -1));
1684122405Sharti	  case LEAF_begemotNgHookPeerType:
1685122405Sharti		return (string_get(value, linkinfo.nodeinfo.type, -1));
1686122405Sharti	  default:
1687122405Sharti		abort();
1688122405Sharti	}
1689122405Sharti	return (SNMP_ERR_NOERROR);
1690122405Sharti}
1691