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