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: stable/9/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c 152268 2005-11-10 10:12:01Z harti $
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 *
ng_register_cookie(const struct lmodule * mod,u_int32_t cookie,ng_ID_t id,ng_cookie_f * func,void * arg)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
ng_unregister_cookie(void * dd)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 *
ng_register_hook(const struct lmodule * mod,const char * hook,ng_hook_f * func,void * arg)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
ng_unregister_hook(void * dd)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
ng_unregister_module(const struct lmodule * mod)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
csock_handle(struct ng_mesg * mesg,const char * path)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 *
csock_read(char * path)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
csock_input(int fd __unused,void * udata __unused)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
ng_output(const char * path,u_int cookie,u_int opcode,const void * arg,size_t arglen)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
ng_output_node(const char * node,u_int cookie,u_int opcode,const void * arg,size_t arglen)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
ng_output_id(ng_ID_t node,u_int cookie,u_int opcode,const void * arg,size_t arglen)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 *
ng_dialog(const char * path,u_int cookie,u_int opcode,const void * arg,size_t arglen)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 *
ng_dialog_node(const char * node,u_int cookie,u_int opcode,const void * arg,size_t arglen)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 *
ng_dialog_id(ng_ID_t id,u_int cookie,u_int opcode,const void * arg,size_t arglen)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
ng_send_data(const char * hook,const void * sndbuf,size_t sndlen)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
dsock_input(int fd __unused,void * udata __unused)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
ng_idle(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
ng_init(struct lmodule * mod,int argc,char * argv[])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
ng_node_id(const char * path)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
ng_node_id_node(const char * node)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
ng_node_name(ng_ID_t id,char * name)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
ng_node_type(ng_ID_t id,char * type)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
ng_connect_node(const char * node,const char * ourhook,const char * peerhook)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
ng_connect_id(ng_ID_t id,const char * ourhook,const char * peerhook)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
ng_connect2_id(ng_ID_t id,ng_ID_t peer,const char * ourhook,const char * peerhook)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
ng_connect2_tee_id(ng_ID_t id,ng_ID_t peer,const char * ourhook,const char * peerhook)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
ng_next_node_id_internal(ng_ID_t node,const char * type,const char * hook,int skip_tee)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
ng_next_node_id(ng_ID_t node,const char * type,const char * hook)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
ng_mkpeer_id(ng_ID_t id,const char * nodename,const char * type,const char * hook,const char * peerhook)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
ng_shutdown_id(ng_ID_t id)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
ng_rmhook(const char * ourhook)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
ng_rmhook_id(ng_ID_t id,const char * hook)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
ng_rmhook_tee_id(ng_ID_t node,const char * hook)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
ng_peer_hook_id(ng_ID_t node,const char * hook,char * peerhook)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
ng_start(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
ng_fini(void)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
op_ng_config(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)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
op_ng_stats(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)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
fetch_types(void)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
ngtype_load(const u_char * name,size_t namelen)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
ngtype_unload(const u_char * name,size_t namelen)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
op_ng_type(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx,enum snmp_op op)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
find_node(const struct asn_oid * oid,u_int sub,struct nodeinfo * info)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
ncmp(const void * p1,const void * p2)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
find_node_next(const struct asn_oid * oid,u_int sub,struct nodeinfo * info)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
op_ng_node(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)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
find_hook(int32_t id,const u_char * hook,size_t hooklen,struct linkinfo * info)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
hook_cmp(const void * p1,const void * p2)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
find_hook_next(const struct asn_oid * oid,u_int sub,struct nodeinfo * nodeinfo,struct linkinfo * linkinfo)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
op_ng_hook(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx,enum snmp_op op)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 }
1691