]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / bsnmpd / modules / snmp_netgraph / snmp_netgraph.c
1 /*
2  * Copyright (c) 2001-2003
3  *      Fraunhofer Institute for Open Communication Systems (FhG Fokus).
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  * $FreeBSD$
31  *
32  * Netgraph interface for SNMPd.
33  */
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/linker.h>
37 #include <sys/socket.h>
38 #include <sys/syslog.h>
39 #include <sys/queue.h>
40 #include <sys/sysctl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <netgraph.h>
47 #include <bsnmp/snmpmod.h>
48 #include "snmp_netgraph.h"
49 #include "netgraph_tree.h"
50 #include "netgraph_oid.h"
51
52 /* maximum message size */
53 #define RESBUFSIZ       20000
54
55 /* default node name */
56 #define NODENAME        "NgSnmpd"
57
58 /* my node Id */
59 ng_ID_t snmp_node;
60 u_char *snmp_nodename;
61
62 /* the Object Resource registration index */
63 static u_int reg_index;
64 static const struct asn_oid oid_begemotNg = OIDX_begemotNg;
65
66 /* configuration */
67 /* this must be smaller than int32_t because the functions in libnetgraph
68  * falsely return an int */
69 static size_t resbufsiz = RESBUFSIZ;
70 static u_int timeout = 1000;
71 static u_int debug_level;
72
73 /* number of microseconds per clock tick */
74 static struct clockinfo clockinfo;
75
76 /* Csock buffers. Communication on the csock is asynchronuous. This means
77  * if we wait for a specific response, we may get other messages. Put these
78  * into a queue and execute them when we are idle. */
79 struct csock_buf {
80         STAILQ_ENTRY(csock_buf) link;
81         struct ng_mesg *mesg;
82         char path[NG_PATHSIZ];
83 };
84 static STAILQ_HEAD(, csock_buf) csock_bufs =
85         STAILQ_HEAD_INITIALIZER(csock_bufs);
86
87 /*
88  * We dispatch unsolicieted messages by node cookies and ids.
89  * So we must keep a list of hook names and dispatch functions.
90  */
91 struct msgreg {
92         u_int32_t       cookie;
93         ng_ID_t         id;
94         ng_cookie_f     *func;
95         void            *arg;
96         const struct lmodule *mod;
97         SLIST_ENTRY(msgreg) link;
98 };
99 static SLIST_HEAD(, msgreg) msgreg_list =
100         SLIST_HEAD_INITIALIZER(msgreg_list);
101
102 /*
103  * Data messages are dispatched by hook names.
104  */
105 struct datareg {
106         char            hook[NG_HOOKSIZ];
107         ng_hook_f       *func;
108         void            *arg;
109         const struct lmodule *mod;
110         SLIST_ENTRY(datareg) link;
111 };
112 static SLIST_HEAD(, datareg) datareg_list =
113         SLIST_HEAD_INITIALIZER(datareg_list);
114
115 /* the netgraph sockets */
116 static int csock, dsock;
117 static void *csock_fd, *dsock_fd;
118
119 /* our module handle */
120 static struct lmodule *module;
121
122 /* statistics */
123 static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1];
124
125 /* netgraph type list */
126 struct ngtype {
127         char            name[NG_TYPESIZ];
128         struct asn_oid  index;
129         TAILQ_ENTRY(ngtype) link;
130 };
131 TAILQ_HEAD(ngtype_list, ngtype);
132
133 static struct ngtype_list ngtype_list;
134 static uint64_t ngtype_tick;
135
136
137 /*
138  * Register a function to receive unsolicited messages
139  */
140 void *
141 ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id,
142     ng_cookie_f *func, void *arg)
143 {
144         struct msgreg *d;
145
146         if ((d = malloc(sizeof(*d))) == NULL)
147                 return (NULL);
148
149         d->cookie = cookie;
150         d->id = id;
151         d->func = func;
152         d->arg = arg;
153         d->mod = mod;
154
155         SLIST_INSERT_HEAD(&msgreg_list, d, link);
156
157         return (d);
158 }
159
160 /*
161  * Remove a registration.
162  */
163 void
164 ng_unregister_cookie(void *dd)
165 {
166         struct msgreg *d = dd;
167
168         SLIST_REMOVE(&msgreg_list, d, msgreg, link);
169         free(d);
170 }
171
172 /*
173  * Register a function for hook data.
174  */
175 void *
176 ng_register_hook(const struct lmodule *mod, const char *hook, 
177     ng_hook_f *func, void *arg)
178 {
179         struct datareg *d;
180
181         if ((d = malloc(sizeof(*d))) == NULL)
182                 return (NULL);
183
184         strcpy(d->hook, hook);
185         d->func = func;
186         d->arg = arg;
187         d->mod = mod;
188
189         SLIST_INSERT_HEAD(&datareg_list, d, link);
190
191         return (d);
192 }
193
194 /*
195  * Unregister a hook function
196  */
197 void
198 ng_unregister_hook(void *dd)
199 {
200         struct datareg *d = dd;
201
202         SLIST_REMOVE(&datareg_list, d, datareg, link);
203         free(d);
204 }
205
206 /*
207  * Unregister all hooks and cookies for that module. Note: doesn't disconnect
208  * any hooks!
209  */
210 void
211 ng_unregister_module(const struct lmodule *mod)
212 {
213         struct msgreg *m, *m1;
214         struct datareg *d, *d1;
215
216         m = SLIST_FIRST(&msgreg_list);
217         while (m != NULL) {
218                 m1 = SLIST_NEXT(m, link);
219                 if (m->mod == mod) {
220                         SLIST_REMOVE(&msgreg_list, m, msgreg, link);
221                         free(m);
222                 }
223                 m = m1;
224         }
225
226         d = SLIST_FIRST(&datareg_list);
227         while (d != NULL) {
228                 d1 = SLIST_NEXT(d, link);
229                 if (d->mod == mod) {
230                         SLIST_REMOVE(&datareg_list, d, datareg, link);
231                         free(d);
232                 }
233                 d = d1;
234         }
235 }
236
237 /*
238  * Dispatch a message to the correct module and delete it. More than one
239  * module can get a message.
240  */
241 static void
242 csock_handle(struct ng_mesg *mesg, const char *path)
243 {
244         struct msgreg *d, *d1;
245         u_int id;
246         int len;
247
248         if (sscanf(path, "[%x]:%n", &id, &len) != 1 ||
249             (u_int)len != strlen(path)) {
250                 syslog(LOG_ERR, "cannot parse message path '%s'", path);
251                 id = 0;
252         }
253
254         d = SLIST_FIRST(&msgreg_list);
255         while (d != NULL) {
256                 d1 = SLIST_NEXT(d, link);
257                 if (d->cookie == mesg->header.typecookie &&
258                     (d->id == 0 || d->id == id || id == 0))
259                         (*d->func)(mesg, path, id, d->arg);
260                 d = d1;
261         }
262         free(mesg);
263 }
264
265 /*
266  * Input from the control socket.
267  */
268 static struct ng_mesg *
269 csock_read(char *path)
270 {
271         struct ng_mesg *mesg;
272         int ret, err;
273
274         if ((mesg = malloc(resbufsiz + 1)) == NULL) {
275                 stats[LEAF_begemotNgNoMems]++;
276                 syslog(LOG_CRIT, "out of memory");
277                 errno = ENOMEM;
278                 return (NULL);
279         }
280         if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) {
281                 err = errno;
282                 free(mesg);
283                 if (errno == EWOULDBLOCK) {
284                         errno = err;
285                         return (NULL);
286                 }
287                 stats[LEAF_begemotNgMsgReadErrs]++;
288                 syslog(LOG_WARNING, "read from csock: %m");
289                 errno = err;
290                 return (NULL);
291         }
292         if (ret == 0) {
293                 syslog(LOG_DEBUG, "node closed -- exiting");
294                 exit(0);
295         }
296         if ((size_t)ret > resbufsiz) {
297                 stats[LEAF_begemotNgTooLargeMsgs]++;
298                 syslog(LOG_WARNING, "ng message too large");
299                 free(mesg);
300                 errno = EFBIG;
301                 return (NULL);
302         }
303         return (mesg);
304 }
305
306 static void
307 csock_input(int fd __unused, void *udata __unused)
308 {
309         struct ng_mesg *mesg;
310         char path[NG_PATHSIZ];
311
312         if ((mesg = csock_read(path)) == NULL)
313                 return;
314
315         csock_handle(mesg, path);
316 }
317
318 /*
319  * Write a message to a node.
320  */
321 int
322 ng_output(const char *path, u_int cookie, u_int opcode,
323     const void *arg, size_t arglen)
324 {
325         return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen));
326 }
327 int
328 ng_output_node(const char *node, u_int cookie, u_int opcode,
329     const void *arg, size_t arglen)
330 {
331         char path[NG_PATHSIZ];
332
333         sprintf(path, "%s:", node);
334         return (ng_output(path, cookie, opcode, arg, arglen));
335 }
336 int
337 ng_output_id(ng_ID_t node, u_int cookie, u_int opcode,
338     const void *arg, size_t arglen)
339 {
340         char path[NG_PATHSIZ];
341
342         sprintf(path, "[%x]:", node);
343         return (ng_output(path, cookie, opcode, arg, arglen));
344 }
345
346
347
348 /*
349  * Execute a synchronuous dialog with the csock. All message we receive, that
350  * do not match our request, are queue until the next call to the IDLE function.
351  */
352 struct ng_mesg *
353 ng_dialog(const char *path, u_int cookie, u_int opcode,
354     const void *arg, size_t arglen)
355 {
356         int token, err;
357         struct ng_mesg *mesg;
358         char rpath[NG_PATHSIZ];
359         struct csock_buf *b;
360         struct timeval end, tv;
361
362         if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0)
363                 return (NULL);
364
365         if (csock_fd)
366                 fd_suspend(csock_fd);
367
368         gettimeofday(&end, NULL);
369         tv.tv_sec = timeout / 1000;
370         tv.tv_usec = (timeout % 1000) * 1000;
371         timeradd(&end, &tv, &end);
372         for (;;) {
373                 mesg = NULL;
374                 gettimeofday(&tv, NULL);
375                 if (timercmp(&tv, &end, >=)) {
376   block:
377                         syslog(LOG_WARNING, "no response for request %u/%u",
378                             cookie, opcode);
379                         errno = EWOULDBLOCK;
380                         break;
381                 }
382                 timersub(&end, &tv, &tv);
383                 if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick)
384                         goto block;
385
386                 if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
387                         syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m");
388                 if ((mesg = csock_read(rpath)) == NULL) {
389                         if (errno == EWOULDBLOCK)
390                                 continue;
391                         break;
392                 }
393                 if (mesg->header.token == (u_int)token)
394                         break;
395                 if ((b = malloc(sizeof(*b))) == NULL) {
396                         stats[LEAF_begemotNgNoMems]++;
397                         syslog(LOG_ERR, "out of memory");
398                         free(mesg);
399                         continue;
400                 }
401                 b->mesg = mesg;
402                 strcpy(b->path, rpath);
403                 STAILQ_INSERT_TAIL(&csock_bufs, b, link);
404         }
405
406         tv.tv_sec = 0;
407         tv.tv_usec = 0;
408         if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
409                 syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m");
410
411         if (csock_fd) {
412                 err = errno;
413                 fd_resume(csock_fd);
414                 errno = err;
415         }
416
417         return (mesg);
418 }
419 struct ng_mesg *
420 ng_dialog_node(const char *node, u_int cookie, u_int opcode,
421     const void *arg, size_t arglen)
422 {
423         char path[NG_PATHSIZ];
424
425         sprintf(path, "%s:", node);
426         return (ng_dialog(path, cookie, opcode, arg, arglen));
427 }
428 struct ng_mesg *
429 ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,
430     const void *arg, size_t arglen)
431 {
432         char path[NG_PATHSIZ];
433
434         sprintf(path, "[%x]:", id);
435         return (ng_dialog(path, cookie, opcode, arg, arglen));
436 }
437
438
439 /*
440  * Send a data message to a given hook.
441  */
442 int
443 ng_send_data(const char *hook, const void *sndbuf, size_t sndlen)
444 {
445         return (NgSendData(dsock, hook, sndbuf, sndlen));
446 }
447
448 /*
449  * Input from a data socket. Dispatch to the function for that hook.
450  */
451 static void
452 dsock_input(int fd __unused, void *udata __unused)
453 {
454         u_char *resbuf, embuf[100];
455         ssize_t len;
456         char hook[NG_HOOKSIZ];
457         struct datareg *d, *d1;
458
459         if ((resbuf = malloc(resbufsiz + 1)) == NULL) {
460                 stats[LEAF_begemotNgNoMems]++;
461                 syslog(LOG_CRIT, "out of memory");
462                 (void)NgRecvData(fd, embuf, sizeof(embuf), hook);
463                 errno = ENOMEM;
464                 return;
465         }
466         if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) {
467                 stats[LEAF_begemotNgDataReadErrs]++;
468                 syslog(LOG_ERR, "reading message: %m");
469                 free(resbuf);
470                 return;
471         }
472         if (len == 0) {
473                 free(resbuf);
474                 return;
475         }
476         if ((size_t)len == resbufsiz + 1) {
477                 stats[LEAF_begemotNgTooLargeDatas]++;
478                 syslog(LOG_WARNING, "message too long");
479                 free(resbuf);
480                 return;
481         }
482
483         /*
484          * Dispatch message. Maybe dispatched to more than one function.
485          */
486         d = SLIST_FIRST(&datareg_list);
487         while (d != NULL) {
488                 d1 = SLIST_NEXT(d, link);
489                 if (strcmp(hook, d->hook) == 0)
490                         (*d->func)(hook, resbuf, len, d->arg);
491                 d = d1;
492         }
493
494         free(resbuf);
495 }
496
497 /*
498  * The SNMP daemon is about to wait for an event. Look whether we have
499  * netgraph messages waiting. If yes, drain the queue.
500  */
501 static void
502 ng_idle(void)
503 {
504         struct csock_buf *b;
505
506         /* execute waiting csock_bufs */
507         while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) {
508                 STAILQ_REMOVE_HEAD(&csock_bufs, link);
509                 csock_handle(b->mesg, b->path);
510                 free(b);
511         }
512 }
513
514 /*
515  * Called when the module is loaded. Returning a non-zero value means,
516  * rejecting the initialisation.
517  *
518  * We make the netgraph socket.
519  */
520 static int
521 ng_init(struct lmodule *mod, int argc, char *argv[])
522 {
523         int name[2];
524         size_t len;
525
526         module = mod;
527
528         if (argc == 0) {
529                 if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL)
530                         return (ENOMEM);
531                 strcpy(snmp_nodename, NODENAME);
532         } else {
533                 if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL)
534                         return (ENOMEM);
535                 strlcpy(snmp_nodename, argv[0], NG_NODESIZ);
536         }
537
538         /* fetch clockinfo (for the number of microseconds per tick) */
539         name[0] = CTL_KERN;
540         name[1] = KERN_CLOCKRATE;
541         len = sizeof(clockinfo);
542         if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1)
543                 return (errno);
544
545         TAILQ_INIT(&ngtype_list);
546
547         return (0);
548 }
549
550 /*
551  * Get the node Id/name/type of a node.
552  */
553 ng_ID_t
554 ng_node_id(const char *path)
555 {
556         struct ng_mesg *resp;
557         ng_ID_t id;
558
559         if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO,
560             NULL, 0)) == NULL)
561                 return (0);
562         id = ((struct nodeinfo *)(void *)resp->data)->id;
563         free(resp);
564         return (id);
565 }
566 ng_ID_t
567 ng_node_id_node(const char *node)
568 {
569         struct ng_mesg *resp;
570         ng_ID_t id;
571
572         if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO,
573             NULL, 0)) == NULL)
574                 return (0);
575         id = ((struct nodeinfo *)(void *)resp->data)->id;
576         free(resp);
577         return (id);
578 }
579 ng_ID_t
580 ng_node_name(ng_ID_t id, char *name)
581 {
582         struct ng_mesg *resp;
583
584         if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
585             NULL, 0)) == NULL)
586                 return (0);
587         strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name);
588         free(resp);
589         return (id);
590
591 }
592 ng_ID_t
593 ng_node_type(ng_ID_t id, char *type)
594 {
595         struct ng_mesg *resp;
596
597         if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
598             NULL, 0)) == NULL)
599                 return (0);
600         strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type);
601         free(resp);
602         return (id);
603 }
604
605 /*
606  * Connect our node to some other node
607  */
608 int
609 ng_connect_node(const char *node, const char *ourhook, const char *peerhook)
610 {
611         struct ngm_connect conn;
612
613         snprintf(conn.path, NG_PATHSIZ, "%s:", node);
614         strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
615         strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
616         return (NgSendMsg(csock, ".:",
617             NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
618 }
619 int
620 ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook)
621 {
622         struct ngm_connect conn;
623
624         snprintf(conn.path, NG_PATHSIZ, "[%x]:", id);
625         strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
626         strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
627         return (NgSendMsg(csock, ".:",
628             NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
629 }
630
631 int
632 ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
633     const char *peerhook)
634 {
635         struct ngm_connect conn;
636         char path[NG_PATHSIZ];
637
638         snprintf(path, NG_PATHSIZ, "[%x]:", id);
639
640         snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
641         strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
642         strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
643         return (NgSendMsg(csock, path,
644             NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
645 }
646
647 int
648 ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
649     const char *peerhook)
650 {
651         struct ngm_connect conn;
652         char path[NG_PATHSIZ];
653         ng_ID_t tee;
654
655         if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0)
656                 return (-1);
657
658         snprintf(path, NG_PATHSIZ, "[%x]:", tee);
659
660         snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
661         strlcpy(conn.ourhook, "right", NG_HOOKSIZ);
662         strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
663         return (NgSendMsg(csock, path,
664             NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
665 }
666
667 /*
668  * Ensure that a node of type 'type' is connected to 'hook' of 'node'
669  * and return its node id. tee nodes between node and the target node
670  * are skipped. If the type is wrong, or the hook is a dead-end return 0.
671  * If type is NULL, it is not checked.
672  */
673 static ng_ID_t
674 ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook,
675     int skip_tee)
676 {
677         struct ng_mesg *resp;
678         struct hooklist *hooklist;
679         u_int i;
680
681         if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
682             NULL, 0)) == NULL) {
683                 syslog(LOG_ERR, "get hook list: %m");
684                 exit(1);
685         }
686         hooklist = (struct hooklist *)(void *)resp->data;
687
688         for (i = 0; i < hooklist->nodeinfo.hooks; i++)
689                 if (strcmp(hooklist->link[i].ourhook, hook) == 0)
690                         break;
691
692         if (i == hooklist->nodeinfo.hooks) {
693                 free(resp);
694                 return (0);
695         }
696
697         node = hooklist->link[i].nodeinfo.id;
698
699         if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
700                 if (strcmp(hooklist->link[i].peerhook, "left") == 0)
701                         node = ng_next_node_id(node, type, "right");
702                 else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
703                         node = ng_next_node_id(node, type, "left");
704                 else if (type != NULL &&
705                     strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
706                         node = 0;
707
708         } else if (type != NULL &&
709             strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
710                 node = 0;
711
712         free(resp);
713
714         return (node);
715 }
716
717 /*
718  * Ensure that a node of type 'type' is connected to 'hook' of 'node'
719  * and return its node id. tee nodes between node and the target node
720  * are skipped. If the type is wrong, or the hook is a dead-end return 0.
721  * If type is NULL, it is not checked.
722  */
723 ng_ID_t
724 ng_next_node_id(ng_ID_t node, const char *type, const char *hook)
725 {
726         return (ng_next_node_id_internal(node, type, hook, 1));
727 }
728
729 ng_ID_t
730 ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type,
731     const char *hook, const char *peerhook)
732 {
733         char path[NG_PATHSIZ];
734         struct ngm_mkpeer mkpeer;
735         struct ngm_name name;
736
737         strlcpy(mkpeer.type, type, NG_TYPESIZ);
738         strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ);
739         strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ);
740
741         sprintf(path, "[%x]:", id);
742         if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,
743             &mkpeer, sizeof(mkpeer)) == -1)
744                 return (0);
745
746         if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0)
747                 return (0);
748
749         if (nodename != NULL) {
750                 strcpy(name.name, nodename);
751                 sprintf(path, "[%x]:", id);
752                 if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME,
753                     &name, sizeof(name)) == -1)
754                         return (0);
755         }
756         return (id);
757 }
758
759 /*
760  * SHutdown node
761  */
762 int
763 ng_shutdown_id(ng_ID_t id)
764 {
765         char path[NG_PATHSIZ];
766
767         snprintf(path, NG_PATHSIZ, "[%x]:", id);
768         return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
769             NGM_SHUTDOWN, NULL, 0));
770 }
771
772 /*
773  * Disconnect one of our hooks
774  */
775 int
776 ng_rmhook(const char *ourhook)
777 {
778         struct ngm_rmhook rmhook;
779
780         strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ);
781         return (NgSendMsg(csock, ".:",
782             NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
783 }
784
785 /*
786  * Disconnect a hook of a node
787  */
788 int
789 ng_rmhook_id(ng_ID_t id, const char *hook)
790 {
791         struct ngm_rmhook rmhook;
792         char path[NG_PATHSIZ];
793
794         strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ);
795         snprintf(path, NG_PATHSIZ, "[%x]:", id);
796         return (NgSendMsg(csock, path,
797             NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
798 }
799
800 /*
801  * Disconnect a hook and shutdown all tee nodes that were connected to that
802  * hook.
803  */
804 int
805 ng_rmhook_tee_id(ng_ID_t node, const char *hook)
806 {
807         struct ng_mesg *resp;
808         struct hooklist *hooklist;
809         u_int i;
810         int first = 1;
811         ng_ID_t next_node;
812         const char *next_hook;
813
814   again:
815         /* if we have just shutdown a tee node, which had no other hooks
816          * connected, the node id may already be wrong here. */
817         if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
818             NULL, 0)) == NULL)
819                 return (0);
820
821         hooklist = (struct hooklist *)(void *)resp->data;
822
823         for (i = 0; i < hooklist->nodeinfo.hooks; i++)
824                 if (strcmp(hooklist->link[i].ourhook, hook) == 0)
825                         break;
826
827         if (i == hooklist->nodeinfo.hooks) {
828                 free(resp);
829                 return (0);
830         }
831
832         next_node = 0;
833         next_hook = NULL;
834         if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
835                 if (strcmp(hooklist->link[i].peerhook, "left") == 0) {
836                         next_node = hooklist->link[i].nodeinfo.id;
837                         next_hook = "right";
838                 } else if (strcmp(hooklist->link[i].peerhook, "right") == 0) {
839                         next_node = hooklist->link[i].nodeinfo.id;
840                         next_hook = "left";
841                 }
842         }
843         free(resp);
844
845         if (first) {
846                 ng_rmhook_id(node, hook);
847                 first = 0;
848         } else {
849                 ng_shutdown_id(node);
850         }
851         if ((node = next_node) == 0)
852                 return (0);
853         hook = next_hook;
854
855         goto again;
856 }
857
858 /*
859  * Get the peer hook of a hook on a given node. Skip any tee nodes in between
860  */
861 int
862 ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook)
863 {
864         struct ng_mesg *resp;
865         struct hooklist *hooklist;
866         u_int i;
867         int ret;
868
869         if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
870             NULL, 0)) == NULL) {
871                 syslog(LOG_ERR, "get hook list: %m");
872                 exit(1);
873         }
874         hooklist = (struct hooklist *)(void *)resp->data;
875
876         for (i = 0; i < hooklist->nodeinfo.hooks; i++)
877                 if (strcmp(hooklist->link[i].ourhook, hook) == 0)
878                         break;
879
880         if (i == hooklist->nodeinfo.hooks) {
881                 free(resp);
882                 return (-1);
883         }
884
885         node = hooklist->link[i].nodeinfo.id;
886
887         ret = 0;
888         if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
889                 if (strcmp(hooklist->link[i].peerhook, "left") == 0)
890                         ret = ng_peer_hook_id(node, "right", peerhook);
891                 else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
892                         ret = ng_peer_hook_id(node, "left", peerhook);
893                 else
894                         strcpy(peerhook, hooklist->link[i].peerhook);
895
896         } else 
897                 strcpy(peerhook, hooklist->link[i].peerhook);
898
899         free(resp);
900
901         return (ret);
902 }
903
904
905 /*
906  * Now the module is started. Select on the sockets, so that we can get 
907  * unsolicited input.
908  */
909 static void
910 ng_start(void)
911 {
912         if (snmp_node == 0) {
913                 if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
914                         syslog(LOG_ERR, "NgMkSockNode: %m");
915                         exit(1);
916                 }
917                 snmp_node = ng_node_id(".:");
918         }
919
920         if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) {
921                 syslog(LOG_ERR, "fd_select failed on csock: %m");
922                 return;
923         }
924         if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) {
925                 syslog(LOG_ERR, "fd_select failed on dsock: %m");
926                 return;
927         }
928
929         reg_index = or_register(&oid_begemotNg, 
930             "The MIB for the NetGraph access module for SNMP.", module);
931 }
932
933 /*
934  * Called, when the module is to be unloaded after it was successfully loaded
935  */
936 static int
937 ng_fini(void)
938 {
939         struct ngtype *t;
940
941         while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
942                 TAILQ_REMOVE(&ngtype_list, t, link);
943                 free(t);
944         }
945
946         if (csock_fd != NULL)
947                 fd_deselect(csock_fd);
948         (void)close(csock);
949
950         if (dsock_fd != NULL)
951                 fd_deselect(dsock_fd);
952         (void)close(dsock);
953
954         free(snmp_nodename);
955
956         or_unregister(reg_index);
957
958         return (0);
959 }
960
961 const struct snmp_module config = {
962         "This module implements access to the netgraph sub-system",
963         ng_init,
964         ng_fini,
965         ng_idle,
966         NULL,
967         NULL,
968         ng_start,
969         NULL,
970         netgraph_ctree,
971         netgraph_CTREE_SIZE,
972         NULL
973 };
974
975 int
976 op_ng_config(struct snmp_context *ctx, struct snmp_value *value,
977     u_int sub, u_int iidx __unused, enum snmp_op op)
978 {
979         asn_subid_t which = value->var.subs[sub - 1];
980         int ret;
981
982         switch (op) {
983
984           case SNMP_OP_GETNEXT:
985                 abort();
986
987           case SNMP_OP_GET:
988                 /*
989                  * Come here for GET, GETNEXT and COMMIT
990                  */
991                 switch (which) {
992
993                   case LEAF_begemotNgControlNodeName:
994                         return (string_get(value, snmp_nodename, -1));
995
996                   case LEAF_begemotNgResBufSiz:
997                         value->v.integer = resbufsiz;
998                         break;
999
1000                   case LEAF_begemotNgTimeout:
1001                         value->v.integer = timeout;
1002                         break;
1003
1004                   case LEAF_begemotNgDebugLevel:
1005                         value->v.uint32 = debug_level;
1006                         break;
1007
1008                   default:
1009                         abort();
1010                 }
1011                 return (SNMP_ERR_NOERROR);
1012
1013           case SNMP_OP_SET:
1014                 switch (which) {
1015
1016                   case LEAF_begemotNgControlNodeName:
1017                         /* only at initialisation */
1018                         if (community != COMM_INITIALIZE)
1019                                 return (SNMP_ERR_NOT_WRITEABLE);
1020
1021                         if (snmp_node != 0)
1022                                 return (SNMP_ERR_NOT_WRITEABLE);
1023
1024                         if ((ret = string_save(value, ctx, -1, &snmp_nodename))
1025                             != SNMP_ERR_NOERROR)
1026                                 return (ret);
1027
1028                         if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
1029                                 syslog(LOG_ERR, "NgMkSockNode: %m");
1030                                 string_rollback(ctx, &snmp_nodename);
1031                                 return (SNMP_ERR_GENERR);
1032                         }
1033                         snmp_node = ng_node_id(".:");
1034
1035                         return (SNMP_ERR_NOERROR);
1036
1037                   case LEAF_begemotNgResBufSiz:
1038                         ctx->scratch->int1 = resbufsiz;
1039                         if (value->v.integer < 1024 ||
1040                             value->v.integer > 0x10000)
1041                                 return (SNMP_ERR_WRONG_VALUE);
1042                         resbufsiz = value->v.integer;
1043                         return (SNMP_ERR_NOERROR);
1044
1045                   case LEAF_begemotNgTimeout:
1046                         ctx->scratch->int1 = timeout;
1047                         if (value->v.integer < 10 ||
1048                             value->v.integer > 10000)
1049                                 return (SNMP_ERR_WRONG_VALUE);
1050                         timeout = value->v.integer;
1051                         return (SNMP_ERR_NOERROR);
1052
1053                   case LEAF_begemotNgDebugLevel:
1054                         ctx->scratch->int1 = debug_level;
1055                         debug_level = value->v.uint32;
1056                         NgSetDebug(debug_level);
1057                         return (SNMP_ERR_NOERROR);
1058                 }
1059                 abort();
1060
1061           case SNMP_OP_ROLLBACK:
1062                 switch (which) {
1063
1064                   case LEAF_begemotNgControlNodeName:
1065                         string_rollback(ctx, &snmp_nodename);
1066                         close(csock);
1067                         close(dsock);
1068                         snmp_node = 0;
1069                         return (SNMP_ERR_NOERROR);
1070
1071                   case LEAF_begemotNgResBufSiz:
1072                         resbufsiz = ctx->scratch->int1;
1073                         return (SNMP_ERR_NOERROR);
1074
1075                   case LEAF_begemotNgTimeout:
1076                         timeout = ctx->scratch->int1;
1077                         return (SNMP_ERR_NOERROR);
1078
1079                   case LEAF_begemotNgDebugLevel:
1080                         debug_level = ctx->scratch->int1;
1081                         NgSetDebug(debug_level);
1082                         return (SNMP_ERR_NOERROR);
1083                 }
1084                 abort();
1085
1086           case SNMP_OP_COMMIT:
1087                 switch (which) {
1088
1089                   case LEAF_begemotNgControlNodeName:
1090                         string_commit(ctx);
1091                         return (SNMP_ERR_NOERROR);
1092
1093                   case LEAF_begemotNgResBufSiz:
1094                   case LEAF_begemotNgTimeout:
1095                   case LEAF_begemotNgDebugLevel:
1096                         return (SNMP_ERR_NOERROR);
1097                 }
1098                 abort();
1099         }
1100         abort();
1101 }
1102
1103 int
1104 op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
1105     u_int sub, u_int iidx __unused, enum snmp_op op)
1106 {
1107         switch (op) {
1108
1109           case SNMP_OP_GETNEXT:
1110                 abort();
1111
1112           case SNMP_OP_GET:
1113                 value->v.uint32 = stats[value->var.subs[sub - 1] - 1];
1114                 return (SNMP_ERR_NOERROR);
1115
1116           case SNMP_OP_SET:
1117                 return (SNMP_ERR_NOT_WRITEABLE);
1118
1119           case SNMP_OP_ROLLBACK:
1120           case SNMP_OP_COMMIT:
1121                 abort();
1122         }
1123         abort();
1124 }
1125
1126 /*
1127  * Netgraph type table
1128  */
1129 static int
1130 fetch_types(void)
1131 {
1132         struct ngtype *t;
1133         struct typelist *typelist;
1134         struct ng_mesg *resp;
1135         u_int u, i;
1136
1137         if (this_tick <= ngtype_tick)
1138                 return (0);
1139
1140         while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
1141                 TAILQ_REMOVE(&ngtype_list, t, link);
1142                 free(t);
1143         }
1144
1145         if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE,
1146             NGM_LISTTYPES, NULL, 0)) == NULL)
1147                 return (SNMP_ERR_GENERR);
1148         typelist = (struct typelist *)(void *)resp->data;
1149
1150         for (u = 0; u < typelist->numtypes; u++) {
1151                 if ((t = malloc(sizeof(*t))) == NULL) {
1152                         free(resp);
1153                         return (SNMP_ERR_GENERR);
1154                 }
1155                 strcpy(t->name, typelist->typeinfo[u].type_name);
1156                 t->index.subs[0] = strlen(t->name);
1157                 t->index.len = t->index.subs[0] + 1;
1158                 for (i = 0; i < t->index.subs[0]; i++)
1159                         t->index.subs[i + 1] = t->name[i];
1160
1161                 INSERT_OBJECT_OID(t, &ngtype_list);
1162         }
1163
1164         ngtype_tick = this_tick;
1165
1166         free(resp);
1167         return (0);
1168 }
1169
1170 /*
1171  * Try to load the netgraph type with the given name. We assume, that
1172  * type 'type' is implemented in the kernel module 'ng_type'.
1173  */
1174 static int
1175 ngtype_load(const u_char *name, size_t namelen)
1176 {
1177         char *mod;
1178         int ret;
1179
1180         if ((mod = malloc(namelen + 4)) == NULL)
1181                 return (-1);
1182         strcpy(mod, "ng_");
1183         strncpy(mod + 3, name, namelen);
1184         mod[namelen + 3] = '\0';
1185
1186         ret = kldload(mod);
1187         free(mod);
1188         return (ret);
1189 }
1190
1191 /*
1192  * Unload a netgraph type.
1193  */
1194 static int
1195 ngtype_unload(const u_char *name, size_t namelen)
1196 {
1197         char *mod;
1198         int id;
1199
1200         if ((mod = malloc(namelen + 4)) == NULL)
1201                 return (-1);
1202         strcpy(mod, "ng_");
1203         strncpy(mod + 3, name, namelen);
1204         mod[namelen + 3] = '\0';
1205
1206         if ((id = kldfind(mod)) == -1) {
1207                 free(mod);
1208                 return (-1);
1209         }
1210         free(mod);
1211         return (kldunload(id));
1212 }
1213
1214 int
1215 op_ng_type(struct snmp_context *ctx, struct snmp_value *value,
1216     u_int sub, u_int iidx, enum snmp_op op)
1217 {
1218         asn_subid_t which = value->var.subs[sub - 1];
1219         struct ngtype *t;
1220         u_char *name;
1221         size_t namelen;
1222         int status = 1;
1223         int ret;
1224
1225         switch (op) {
1226
1227           case SNMP_OP_GETNEXT:
1228                 if ((ret = fetch_types()) != 0)
1229                         return (ret);
1230                 if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1231                         return (SNMP_ERR_NOSUCHNAME);
1232                 index_append(&value->var, sub, &t->index);
1233                 break;
1234
1235           case SNMP_OP_GET:
1236                 if ((ret = fetch_types()) != 0)
1237                         return (ret);
1238                 if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1239                         return (SNMP_ERR_NOSUCHNAME);
1240                 break;
1241
1242           case SNMP_OP_SET:
1243                 if (index_decode(&value->var, sub, iidx, &name, &namelen))
1244                         return (SNMP_ERR_NO_CREATION);
1245                 if (namelen == 0 || namelen >= NG_TYPESIZ) {
1246                         free(name);
1247                         return (SNMP_ERR_NO_CREATION);
1248                 }
1249                 if ((ret = fetch_types()) != 0) {
1250                         free(name);
1251                         return (ret);
1252                 }
1253                 t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub);
1254
1255                 if (which != LEAF_begemotNgTypeStatus) {
1256                         free(name);
1257                         if (t != NULL)
1258                                 return (SNMP_ERR_NOT_WRITEABLE);
1259                         return (SNMP_ERR_NO_CREATION);
1260                 }
1261                 if (!TRUTH_OK(value->v.integer)) {
1262                         free(name);
1263                         return (SNMP_ERR_WRONG_VALUE);
1264                 }
1265                 ctx->scratch->int1 = TRUTH_GET(value->v.integer);
1266                 ctx->scratch->int1 |= (t != NULL) << 1;
1267                 ctx->scratch->ptr2 = name;
1268                 ctx->scratch->int2 = namelen;
1269
1270                 if (t == NULL) {
1271                         /* type not loaded */
1272                         if (ctx->scratch->int1 & 1) {
1273                                 /* request to load */
1274                                 if (ngtype_load(name, namelen) == -1) {
1275                                         free(name);
1276                                         if (errno == ENOENT)
1277                                                 return (SNMP_ERR_INCONS_NAME);
1278                                         else
1279                                                 return (SNMP_ERR_GENERR);
1280                                 }
1281                         }
1282                 } else {
1283                         /* is type loaded */
1284                         if (!(ctx->scratch->int1 & 1)) {
1285                                 /* request to unload */
1286                                 if (ngtype_unload(name, namelen) == -1) {
1287                                         free(name);
1288                                         return (SNMP_ERR_GENERR);
1289                                 }
1290                         }
1291                 }
1292                 return (SNMP_ERR_NOERROR);
1293
1294           case SNMP_OP_ROLLBACK:
1295                 ret = SNMP_ERR_NOERROR;
1296                 if (!(ctx->scratch->int1 & 2)) {
1297                         /* did not exist */
1298                         if (ctx->scratch->int1 & 1) {
1299                                 /* request to load - unload */
1300                                 if (ngtype_unload(ctx->scratch->ptr2,
1301                                     ctx->scratch->int2) == -1)
1302                                         ret = SNMP_ERR_UNDO_FAILED;
1303                         }
1304                 } else {
1305                         /* did exist */
1306                         if (!(ctx->scratch->int1 & 1)) {
1307                                 /* request to unload - reload */
1308                                 if (ngtype_load(ctx->scratch->ptr2,
1309                                     ctx->scratch->int2) == -1)
1310                                         ret = SNMP_ERR_UNDO_FAILED;
1311                         }
1312                 }
1313                 free(ctx->scratch->ptr2);
1314                 return (ret);
1315
1316           case SNMP_OP_COMMIT:
1317                 free(ctx->scratch->ptr2);
1318                 return (SNMP_ERR_NOERROR);
1319
1320           default:
1321                 abort();
1322         }
1323
1324         /*
1325          * Come here for GET and COMMIT
1326          */
1327         switch (which) {
1328
1329           case LEAF_begemotNgTypeStatus:
1330                 value->v.integer = status;
1331                 break;
1332
1333           default:
1334                 abort();
1335         }
1336         return (SNMP_ERR_NOERROR);
1337 }
1338
1339 /*
1340  * Implement the node table
1341  */
1342 static int
1343 find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1344 {
1345         ng_ID_t id = oid->subs[sub];
1346         struct ng_mesg *resp;
1347
1348         if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
1349             NULL, 0)) == NULL)
1350                 return (-1);
1351
1352         *info = *(struct nodeinfo *)(void *)resp->data;
1353         free(resp);
1354         return (0);
1355 }
1356
1357 static int
1358 ncmp(const void *p1, const void *p2)
1359 {
1360         const struct nodeinfo *i1 = p1;
1361         const struct nodeinfo *i2 = p2;
1362
1363         if (i1->id < i2->id)
1364                 return (-1);
1365         if (i1->id > i2->id)
1366                 return (+1);
1367         return (0);
1368 }
1369
1370 static int
1371 find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1372 {
1373         u_int idxlen = oid->len - sub;
1374         struct ng_mesg *resp;
1375         struct namelist *list;
1376         ng_ID_t id;
1377         u_int i;
1378
1379         if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1380             NULL, 0)) == NULL)
1381                 return (-1);
1382         list = (struct namelist *)(void *)resp->data;
1383
1384         qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1385
1386         if (idxlen == 0) {
1387                 if (list->numnames == 0) {
1388                         free(resp);
1389                         return (-1);
1390                 }
1391                 *info = list->nodeinfo[0];
1392                 free(resp);
1393                 return (0);
1394         }
1395         id = oid->subs[sub];
1396
1397         for (i = 0; i < list->numnames; i++)
1398                 if (list->nodeinfo[i].id > id) {
1399                         *info = list->nodeinfo[i];
1400                         free(resp);
1401                         return (0);
1402                 }
1403
1404         free(resp);
1405         return (-1);
1406 }
1407
1408 int
1409 op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value,
1410     u_int sub, u_int iidx __unused, enum snmp_op op)
1411 {
1412         asn_subid_t which = value->var.subs[sub - 1];
1413         u_int idxlen = value->var.len - sub;
1414         struct nodeinfo nodeinfo;
1415
1416         switch (op) {
1417
1418           case SNMP_OP_GETNEXT:
1419                 if (find_node_next(&value->var, sub, &nodeinfo) == -1)
1420                         return (SNMP_ERR_NOSUCHNAME);
1421                 value->var.len = sub + 1;
1422                 value->var.subs[sub] = nodeinfo.id;
1423                 break;
1424
1425           case SNMP_OP_GET:
1426                 if (idxlen != 1)
1427                         return (SNMP_ERR_NOSUCHNAME);
1428                 if (find_node(&value->var, sub, &nodeinfo) == -1)
1429                         return (SNMP_ERR_NOSUCHNAME);
1430                 break;
1431
1432           case SNMP_OP_SET:
1433                 if (idxlen != 1)
1434                         return (SNMP_ERR_NO_CREATION);
1435                 if (find_node(&value->var, sub, &nodeinfo) == -1)
1436                         return (SNMP_ERR_NO_CREATION);
1437                 return (SNMP_ERR_NOT_WRITEABLE);
1438
1439           case SNMP_OP_ROLLBACK:
1440           case SNMP_OP_COMMIT:
1441           default:
1442                 abort();
1443         }
1444
1445         /*
1446          * Come here for GET and COMMIT
1447          */
1448         switch (which) {
1449
1450           case LEAF_begemotNgNodeStatus:
1451                 value->v.integer = 1;
1452                 break;
1453           case LEAF_begemotNgNodeName:
1454                 return (string_get(value, nodeinfo.name, -1));
1455           case LEAF_begemotNgNodeType:
1456                 return (string_get(value, nodeinfo.type, -1));
1457           case LEAF_begemotNgNodeHooks:
1458                 value->v.uint32 = nodeinfo.hooks;
1459                 break;
1460
1461           default:
1462                 abort();
1463         }
1464         return (SNMP_ERR_NOERROR);
1465 }
1466
1467 /*
1468  * Implement the hook table
1469  */
1470 static int
1471 find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info)
1472 {
1473         struct ng_mesg *resp;
1474         struct hooklist *list;
1475         u_int i;
1476
1477         if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE,
1478             NGM_LISTHOOKS, NULL, 0)) == NULL)
1479                 return (-1);
1480
1481         list = (struct hooklist *)(void *)resp->data;
1482
1483         for (i = 0; i < list->nodeinfo.hooks; i++) {
1484                 if (strlen(list->link[i].ourhook) == hooklen &&
1485                     strncmp(list->link[i].ourhook, hook, hooklen) == 0) {
1486                         *info = list->link[i];
1487                         free(resp);
1488                         return (0);
1489                 }
1490         }
1491         free(resp);
1492         return (-1);
1493 }
1494
1495 static int
1496 hook_cmp(const void *p1, const void *p2)
1497 {
1498         const struct linkinfo *i1 = p1;
1499         const struct linkinfo *i2 = p2;
1500
1501         if (strlen(i1->ourhook) < strlen(i2->ourhook))
1502                 return (-1);
1503         if (strlen(i1->ourhook) > strlen(i2->ourhook))
1504                 return (+1);
1505         return (strcmp(i1->ourhook, i2->ourhook));
1506 }
1507
1508 static int
1509 find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo,
1510     struct linkinfo *linkinfo)
1511 {
1512         u_int idxlen = oid->len - sub;
1513         struct namelist *list;
1514         struct ng_mesg *resp;
1515         struct hooklist *hooks;
1516         struct ng_mesg *resp1;
1517         u_int node_index;
1518         struct asn_oid idx;
1519         u_int i, j;
1520
1521         /*
1522          * Get and sort Node list
1523          */
1524         if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1525             NULL, 0)) == NULL)
1526                 return (-1);
1527         list = (struct namelist *)(void *)resp->data;
1528
1529         qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1530
1531         /*
1532          * If we have no index, take the first node and return the
1533          * first hook.
1534          */
1535         if (idxlen == 0) {
1536                 node_index = 0;
1537                 goto return_first_hook;
1538         }
1539
1540         /*
1541          * Locate node
1542          */
1543         for (node_index = 0; node_index < list->numnames; node_index++)
1544                 if (list->nodeinfo[node_index].id >= oid->subs[sub])
1545                         break;
1546
1547         /*
1548          * If we have only the node part of the index take, or
1549          * there is no node with that Id, take the first hook of that node.
1550          */
1551         if (idxlen == 1 || node_index >= list->numnames ||
1552             list->nodeinfo[node_index].id > oid->subs[sub])
1553                 goto return_first_hook;
1554
1555         /*
1556          * We had an exact match on the node id and have (at last part)
1557          * of the hook name index. Loop through the hooks of the node
1558          * and find the next one.
1559          */
1560         if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1561             NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) {
1562                 free(resp);
1563                 return (-1);
1564         }
1565         hooks = (struct hooklist *)(void *)resp1->data;
1566         if (hooks->nodeinfo.hooks > 0) {
1567                 qsort(hooks->link, hooks->nodeinfo.hooks,
1568                     sizeof(hooks->link[0]), hook_cmp);
1569                 for (i = 0; i < hooks->nodeinfo.hooks; i++) {
1570                         idx.len = strlen(hooks->link[i].ourhook) + 1;
1571                         idx.subs[0] = idx.len - 1;
1572                         for (j = 0; j < idx.len; j++)
1573                                 idx.subs[j + 1] = hooks->link[i].ourhook[j];
1574                         if (index_compare(oid, sub + 1, &idx) < 0)
1575                                 break;
1576                 }
1577                 if (i < hooks->nodeinfo.hooks) {
1578                         *nodeinfo = hooks->nodeinfo;
1579                         *linkinfo = hooks->link[i];
1580
1581                         free(resp);
1582                         free(resp1);
1583                         return (0);
1584                 }
1585         }
1586
1587         /* no hook found larger than the index on the index node - take
1588          * first hook of next node */
1589         free(resp1);
1590         node_index++;
1591
1592   return_first_hook:
1593         while (node_index < list->numnames) {
1594                 if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1595                     NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL)
1596                         break;
1597                 hooks = (struct hooklist *)(void *)resp1->data;
1598                 if (hooks->nodeinfo.hooks > 0) {
1599                         qsort(hooks->link, hooks->nodeinfo.hooks,
1600                             sizeof(hooks->link[0]), hook_cmp);
1601
1602                         *nodeinfo = hooks->nodeinfo;
1603                         *linkinfo = hooks->link[0];
1604
1605                         free(resp);
1606                         free(resp1);
1607                         return (0);
1608                 }
1609
1610                 /* if we don't have hooks, try next node */
1611                 free(resp1);
1612                 node_index++;
1613         }
1614
1615         free(resp);
1616         return (-1);
1617 }
1618
1619 int
1620 op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value,
1621     u_int sub, u_int iidx, enum snmp_op op)
1622 {
1623         asn_subid_t which = value->var.subs[sub - 1];
1624         struct linkinfo linkinfo;
1625         struct nodeinfo nodeinfo;
1626         u_int32_t lid;
1627         u_char *hook;
1628         size_t hooklen;
1629         u_int i;
1630
1631         switch (op) {
1632
1633           case SNMP_OP_GETNEXT:
1634                 if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1)
1635                         return (SNMP_ERR_NOSUCHNAME);
1636
1637                 value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook);
1638                 value->var.subs[sub] = nodeinfo.id;
1639                 value->var.subs[sub + 1] = strlen(linkinfo.ourhook);
1640                 for (i = 0; i < strlen(linkinfo.ourhook); i++)
1641                         value->var.subs[sub + i + 2] =
1642                             linkinfo.ourhook[i];
1643                 break;
1644
1645           case SNMP_OP_GET:
1646                 if (index_decode(&value->var, sub, iidx, &lid,
1647                     &hook, &hooklen))
1648                         return (SNMP_ERR_NOSUCHNAME);
1649                 if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1650                         free(hook);
1651                         return (SNMP_ERR_NOSUCHNAME);
1652                 }
1653                 free(hook);
1654                 break;
1655
1656           case SNMP_OP_SET:
1657                 if (index_decode(&value->var, sub, iidx, &lid,
1658                     &hook, &hooklen))
1659                         return (SNMP_ERR_NO_CREATION);
1660                 if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1661                         free(hook);
1662                         return (SNMP_ERR_NO_CREATION);
1663                 }
1664                 free(hook);
1665                 return (SNMP_ERR_NOT_WRITEABLE);
1666
1667           case SNMP_OP_ROLLBACK:
1668           case SNMP_OP_COMMIT:
1669           default:
1670                 abort();
1671
1672         }
1673
1674         switch (which) {
1675
1676           case LEAF_begemotNgHookStatus:
1677                 value->v.integer = 1;
1678                 break;
1679           case LEAF_begemotNgHookPeerNodeId:
1680                 value->v.uint32 = linkinfo.nodeinfo.id;
1681                 break;
1682           case LEAF_begemotNgHookPeerHook:
1683                 return (string_get(value, linkinfo.peerhook, -1));
1684           case LEAF_begemotNgHookPeerType:
1685                 return (string_get(value, linkinfo.nodeinfo.type, -1));
1686           default:
1687                 abort();
1688         }
1689         return (SNMP_ERR_NOERROR);
1690 }