snmp_netgraph.c revision 122758
1255670Sdes/* 2113908Sdes * Copyright (c) 2001-2003 3113908Sdes * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4113908Sdes * All rights reserved. 5113908Sdes * 6126274Sdes * Author: Harti Brandt <harti@freebsd.org> 7113908Sdes * 8113908Sdes * Redistribution of this software and documentation and use in source and 9113908Sdes * binary forms, with or without modification, are permitted provided that 10113908Sdes * the following conditions are met: 11113908Sdes * 12113908Sdes * 1. Redistributions of source code or documentation must retain the above 13113908Sdes * copyright notice, this list of conditions and the following disclaimer. 14113908Sdes * 2. Redistributions in binary form must reproduce the above copyright 15113908Sdes * notice, this list of conditions and the following disclaimer in the 16113908Sdes * documentation and/or other materials provided with the distribution. 17113908Sdes * 18113908Sdes * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS 19113908Sdes * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 20113908Sdes * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21113908Sdes * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22113908Sdes * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23113908Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24113908Sdes * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25113908Sdes * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26113908Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27113908Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28113908Sdes * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29113908Sdes * 30113908Sdes * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c 122758 2003-11-15 15:26:35Z harti $ 31113908Sdes * 32113908Sdes * Netgraph interface for SNMPd. 33113908Sdes */ 34113908Sdes#include <sys/types.h> 35113908Sdes#include <sys/param.h> 36113908Sdes#include <sys/linker.h> 37113908Sdes#include <sys/socket.h> 38204861Sdes#include <sys/syslog.h> 39113908Sdes#include <sys/queue.h> 40113908Sdes#include <sys/sysctl.h> 41113908Sdes#include <stdio.h> 42204861Sdes#include <stdlib.h> 43113908Sdes#include <errno.h> 44113908Sdes#include <unistd.h> 45113908Sdes#include <string.h> 46204861Sdes#include <netgraph.h> 47113908Sdes#include "snmpmod.h" 48113908Sdes#include "snmp_netgraph.h" 49113908Sdes#include "netgraph_tree.h" 50204861Sdes#include "netgraph_oid.h" 51113908Sdes 52113908Sdes/* maximum message size */ 53113908Sdes#define RESBUFSIZ 20000 54113908Sdes 55113908Sdes/* default node name */ 56#define NODENAME "NgSnmpd" 57 58/* my node Id */ 59ng_ID_t snmp_node; 60u_char *snmp_nodename; 61 62/* the Object Resource registration index */ 63static u_int reg_index; 64static 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 */ 69static size_t resbufsiz = RESBUFSIZ; 70static u_int timeout = 1000; 71static u_int debug_level; 72 73/* number of microseconds per clock tick */ 74static 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. */ 79struct csock_buf { 80 STAILQ_ENTRY(csock_buf) link; 81 struct ng_mesg *mesg; 82 char path[NG_PATHSIZ]; 83}; 84static 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 */ 91struct 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}; 99static SLIST_HEAD(, msgreg) msgreg_list = 100 SLIST_HEAD_INITIALIZER(msgreg_list); 101 102/* 103 * Data messages are dispatched by hook names. 104 */ 105struct 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}; 112static SLIST_HEAD(, datareg) datareg_list = 113 SLIST_HEAD_INITIALIZER(datareg_list); 114 115/* the netgraph sockets */ 116static int csock, dsock; 117static void *csock_fd, *dsock_fd; 118 119/* our module handle */ 120static struct lmodule *module; 121 122/* statistics */ 123static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1]; 124 125/* netgraph type list */ 126struct ngtype { 127 char name[NG_TYPESIZ]; 128 struct asn_oid index; 129 TAILQ_ENTRY(ngtype) link; 130}; 131TAILQ_HEAD(ngtype_list, ngtype); 132 133static struct ngtype_list ngtype_list; 134static u_int32_t ngtype_tick; 135 136 137/* 138 * Register a function to receive unsolicited messages 139 */ 140void * 141ng_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 */ 163void 164ng_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 */ 175void * 176ng_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 */ 197void 198ng_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 */ 210void 211ng_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 */ 241static void 242csock_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 */ 268static struct ng_mesg * 269csock_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 306static void 307csock_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 */ 321int 322ng_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} 327int 328ng_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} 336int 337ng_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 */ 352struct ng_mesg * 353ng_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} 419struct ng_mesg * 420ng_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} 428struct ng_mesg * 429ng_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 */ 442int 443ng_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 */ 451static void 452dsock_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 */ 501static void 502ng_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 */ 520static int 521ng_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 */ 553ng_ID_t 554ng_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} 566ng_ID_t 567ng_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} 579ng_ID_t 580ng_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} 592ng_ID_t 593ng_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 */ 608int 609ng_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} 619int 620ng_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 631int 632ng_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 647int 648ng_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 */ 673static ng_ID_t 674ng_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 */ 723ng_ID_t 724ng_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 729ng_ID_t 730ng_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)) == NULL) 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 */ 762int 763ng_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 */ 775int 776ng_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 */ 788int 789ng_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 */ 804int 805ng_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 */ 861int 862ng_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 */ 909static void 910ng_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/* 935 * Called, when the module is to be unloaded after it was successfully loaded 936 */ 937static int 938ng_fini(void) 939{ 940 struct ngtype *t; 941 942 while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { 943 TAILQ_REMOVE(&ngtype_list, t, link); 944 free(t); 945 } 946 947 if (csock_fd != NULL) 948 fd_deselect(csock_fd); 949 (void)close(csock); 950 951 if (dsock_fd != NULL) 952 fd_deselect(dsock_fd); 953 (void)close(dsock); 954 955 free(snmp_nodename); 956 957 or_unregister(reg_index); 958 959 return (0); 960} 961 962const struct snmp_module config = { 963 "This module implements access to the netgraph sub-system", 964 ng_init, 965 ng_fini, 966 ng_idle, 967 NULL, 968 NULL, 969 ng_start, 970 NULL, 971 netgraph_ctree, 972 netgraph_CTREE_SIZE, 973 NULL 974}; 975 976int 977op_ng_config(struct snmp_context *ctx, struct snmp_value *value, 978 u_int sub, u_int iidx __unused, enum snmp_op op) 979{ 980 asn_subid_t which = value->var.subs[sub - 1]; 981 int ret; 982 983 switch (op) { 984 985 case SNMP_OP_GETNEXT: 986 abort(); 987 988 case SNMP_OP_GET: 989 /* 990 * Come here for GET, GETNEXT and COMMIT 991 */ 992 switch (which) { 993 994 case LEAF_begemotNgControlNodeName: 995 return (string_get(value, snmp_nodename, -1)); 996 997 case LEAF_begemotNgResBufSiz: 998 value->v.integer = resbufsiz; 999 break; 1000 1001 case LEAF_begemotNgTimeout: 1002 value->v.integer = timeout; 1003 break; 1004 1005 case LEAF_begemotNgDebugLevel: 1006 value->v.uint32 = debug_level; 1007 break; 1008 1009 default: 1010 abort(); 1011 } 1012 return (SNMP_ERR_NOERROR); 1013 1014 case SNMP_OP_SET: 1015 switch (which) { 1016 1017 case LEAF_begemotNgControlNodeName: 1018 /* only at initialisation */ 1019 if (community != COMM_INITIALIZE) 1020 return (SNMP_ERR_NOT_WRITEABLE); 1021 1022 if (snmp_node != 0) 1023 return (SNMP_ERR_NOT_WRITEABLE); 1024 1025 if ((ret = string_save(value, ctx, -1, &snmp_nodename)) 1026 != SNMP_ERR_NOERROR) 1027 return (ret); 1028 1029 if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { 1030 syslog(LOG_ERR, "NgMkSockNode: %m"); 1031 string_rollback(ctx, &snmp_nodename); 1032 return (SNMP_ERR_GENERR); 1033 } 1034 snmp_node = ng_node_id(".:"); 1035 1036 return (SNMP_ERR_NOERROR); 1037 1038 case LEAF_begemotNgResBufSiz: 1039 ctx->scratch->int1 = resbufsiz; 1040 if (value->v.integer < 1024 || 1041 value->v.integer > 0x10000) 1042 return (SNMP_ERR_WRONG_VALUE); 1043 resbufsiz = value->v.integer; 1044 return (SNMP_ERR_NOERROR); 1045 1046 case LEAF_begemotNgTimeout: 1047 ctx->scratch->int1 = timeout; 1048 if (value->v.integer < 10 || 1049 value->v.integer > 10000) 1050 return (SNMP_ERR_WRONG_VALUE); 1051 timeout = value->v.integer; 1052 return (SNMP_ERR_NOERROR); 1053 1054 case LEAF_begemotNgDebugLevel: 1055 ctx->scratch->int1 = debug_level; 1056 debug_level = value->v.uint32; 1057 NgSetDebug(debug_level); 1058 return (SNMP_ERR_NOERROR); 1059 } 1060 abort(); 1061 1062 case SNMP_OP_ROLLBACK: 1063 switch (which) { 1064 1065 case LEAF_begemotNgControlNodeName: 1066 string_rollback(ctx, &snmp_nodename); 1067 close(csock); 1068 close(dsock); 1069 snmp_node = 0; 1070 return (SNMP_ERR_NOERROR); 1071 1072 case LEAF_begemotNgResBufSiz: 1073 resbufsiz = ctx->scratch->int1; 1074 return (SNMP_ERR_NOERROR); 1075 1076 case LEAF_begemotNgTimeout: 1077 timeout = ctx->scratch->int1; 1078 return (SNMP_ERR_NOERROR); 1079 1080 case LEAF_begemotNgDebugLevel: 1081 debug_level = ctx->scratch->int1; 1082 NgSetDebug(debug_level); 1083 return (SNMP_ERR_NOERROR); 1084 } 1085 abort(); 1086 1087 case SNMP_OP_COMMIT: 1088 switch (which) { 1089 1090 case LEAF_begemotNgControlNodeName: 1091 string_commit(ctx); 1092 return (SNMP_ERR_NOERROR); 1093 1094 case LEAF_begemotNgResBufSiz: 1095 case LEAF_begemotNgTimeout: 1096 case LEAF_begemotNgDebugLevel: 1097 return (SNMP_ERR_NOERROR); 1098 } 1099 abort(); 1100 } 1101 abort(); 1102} 1103 1104int 1105op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value, 1106 u_int sub, u_int iidx __unused, enum snmp_op op) 1107{ 1108 switch (op) { 1109 1110 case SNMP_OP_GETNEXT: 1111 abort(); 1112 1113 case SNMP_OP_GET: 1114 value->v.uint32 = stats[value->var.subs[sub - 1] - 1]; 1115 return (SNMP_ERR_NOERROR); 1116 1117 case SNMP_OP_SET: 1118 return (SNMP_ERR_NOT_WRITEABLE); 1119 1120 case SNMP_OP_ROLLBACK: 1121 case SNMP_OP_COMMIT: 1122 abort(); 1123 } 1124 abort(); 1125} 1126 1127/* 1128 * Netgraph type table 1129 */ 1130static int 1131fetch_types(void) 1132{ 1133 struct ngtype *t; 1134 struct typelist *typelist; 1135 struct ng_mesg *resp; 1136 u_int u, i; 1137 1138 if (this_tick <= ngtype_tick) 1139 return (0); 1140 1141 while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { 1142 TAILQ_REMOVE(&ngtype_list, t, link); 1143 free(t); 1144 } 1145 1146 if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, 1147 NGM_LISTTYPES, NULL, 0)) == NULL) 1148 return (SNMP_ERR_GENERR); 1149 typelist = (struct typelist *)(void *)resp->data; 1150 1151 for (u = 0; u < typelist->numtypes; u++) { 1152 if ((t = malloc(sizeof(*t))) == NULL) { 1153 free(resp); 1154 return (SNMP_ERR_GENERR); 1155 } 1156 strcpy(t->name, typelist->typeinfo[u].type_name); 1157 t->index.subs[0] = strlen(t->name); 1158 t->index.len = t->index.subs[0] + 1; 1159 for (i = 0; i < t->index.subs[0]; i++) 1160 t->index.subs[i + 1] = t->name[i]; 1161 1162 INSERT_OBJECT_OID(t, &ngtype_list); 1163 } 1164 1165 ngtype_tick = this_tick; 1166 1167 free(resp); 1168 return (0); 1169} 1170 1171/* 1172 * Try to load the netgraph type with the given name. We assume, that 1173 * type 'type' is implemented in the kernel module 'ng_type'. 1174 */ 1175static int 1176ngtype_load(const u_char *name, size_t namelen) 1177{ 1178 char *mod; 1179 int ret; 1180 1181 if ((mod = malloc(namelen + 4)) == NULL) 1182 return (-1); 1183 strcpy(mod, "ng_"); 1184 strncpy(mod + 3, name, namelen); 1185 mod[namelen + 3] = '\0'; 1186 1187 ret = kldload(mod); 1188 free(mod); 1189 return (ret); 1190} 1191 1192/* 1193 * Unload a netgraph type. 1194 */ 1195static int 1196ngtype_unload(const u_char *name, size_t namelen) 1197{ 1198 char *mod; 1199 int id; 1200 1201 if ((mod = malloc(namelen + 4)) == NULL) 1202 return (-1); 1203 strcpy(mod, "ng_"); 1204 strncpy(mod + 3, name, namelen); 1205 mod[namelen + 3] = '\0'; 1206 1207 if ((id = kldfind(mod)) == -1) { 1208 free(mod); 1209 return (-1); 1210 } 1211 free(mod); 1212 return (kldunload(id)); 1213} 1214 1215int 1216op_ng_type(struct snmp_context *ctx, struct snmp_value *value, 1217 u_int sub, u_int iidx, enum snmp_op op) 1218{ 1219 asn_subid_t which = value->var.subs[sub - 1]; 1220 struct ngtype *t; 1221 u_char *name; 1222 size_t namelen; 1223 int status = 1; 1224 int ret; 1225 1226 switch (op) { 1227 1228 case SNMP_OP_GETNEXT: 1229 if ((ret = fetch_types()) != 0) 1230 return (ret); 1231 if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) 1232 return (SNMP_ERR_NOSUCHNAME); 1233 index_append(&value->var, sub, &t->index); 1234 break; 1235 1236 case SNMP_OP_GET: 1237 if ((ret = fetch_types()) != 0) 1238 return (ret); 1239 if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) 1240 return (SNMP_ERR_NOSUCHNAME); 1241 break; 1242 1243 case SNMP_OP_SET: 1244 if (index_decode(&value->var, sub, iidx, &name, &namelen)) 1245 return (SNMP_ERR_NO_CREATION); 1246 if (namelen == 0 || namelen >= NG_TYPESIZ) { 1247 free(name); 1248 return (SNMP_ERR_NO_CREATION); 1249 } 1250 if ((ret = fetch_types()) != 0) { 1251 free(name); 1252 return (ret); 1253 } 1254 t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub); 1255 1256 if (which != LEAF_begemotNgTypeStatus) { 1257 free(name); 1258 if (t != NULL) 1259 return (SNMP_ERR_NOT_WRITEABLE); 1260 return (SNMP_ERR_NO_CREATION); 1261 } 1262 if (!TRUTH_OK(value->v.integer)) { 1263 free(name); 1264 return (SNMP_ERR_WRONG_VALUE); 1265 } 1266 ctx->scratch->int1 = TRUTH_GET(value->v.integer); 1267 ctx->scratch->int1 |= (t != NULL) << 1; 1268 ctx->scratch->ptr2 = name; 1269 ctx->scratch->int2 = namelen; 1270 1271 if (t == NULL) { 1272 /* type not loaded */ 1273 if (ctx->scratch->int1 & 1) { 1274 /* request to load */ 1275 if (ngtype_load(name, namelen) == -1) { 1276 free(name); 1277 if (errno == ENOENT) 1278 return (SNMP_ERR_INCONS_NAME); 1279 else 1280 return (SNMP_ERR_GENERR); 1281 } 1282 } 1283 } else { 1284 /* is type loaded */ 1285 if (!(ctx->scratch->int1 & 1)) { 1286 /* request to unload */ 1287 if (ngtype_unload(name, namelen) == -1) { 1288 free(name); 1289 return (SNMP_ERR_GENERR); 1290 } 1291 } 1292 } 1293 return (SNMP_ERR_NOERROR); 1294 1295 case SNMP_OP_ROLLBACK: 1296 ret = SNMP_ERR_NOERROR; 1297 if (!(ctx->scratch->int1 & 2)) { 1298 /* did not exist */ 1299 if (ctx->scratch->int1 & 1) { 1300 /* request to load - unload */ 1301 if (ngtype_unload(ctx->scratch->ptr2, 1302 ctx->scratch->int2) == -1) 1303 ret = SNMP_ERR_UNDO_FAILED; 1304 } 1305 } else { 1306 /* did exist */ 1307 if (!(ctx->scratch->int1 & 1)) { 1308 /* request to unload - reload */ 1309 if (ngtype_load(ctx->scratch->ptr2, 1310 ctx->scratch->int2) == -1) 1311 ret = SNMP_ERR_UNDO_FAILED; 1312 } 1313 } 1314 free(ctx->scratch->ptr2); 1315 return (ret); 1316 1317 case SNMP_OP_COMMIT: 1318 free(ctx->scratch->ptr2); 1319 return (SNMP_ERR_NOERROR); 1320 1321 default: 1322 abort(); 1323 } 1324 1325 /* 1326 * Come here for GET and COMMIT 1327 */ 1328 switch (which) { 1329 1330 case LEAF_begemotNgTypeStatus: 1331 value->v.integer = status; 1332 break; 1333 1334 default: 1335 abort(); 1336 } 1337 return (SNMP_ERR_NOERROR); 1338} 1339 1340/* 1341 * Implement the node table 1342 */ 1343static int 1344find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) 1345{ 1346 ng_ID_t id = oid->subs[sub]; 1347 struct ng_mesg *resp; 1348 1349 if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, 1350 NULL, 0)) == NULL) 1351 return (-1); 1352 1353 *info = *(struct nodeinfo *)(void *)resp->data; 1354 free(resp); 1355 return (0); 1356} 1357 1358static int 1359ncmp(const void *p1, const void *p2) 1360{ 1361 const struct nodeinfo *i1 = p1; 1362 const struct nodeinfo *i2 = p2; 1363 1364 if (i1->id < i2->id) 1365 return (-1); 1366 if (i1->id > i2->id) 1367 return (+1); 1368 return (0); 1369} 1370 1371static int 1372find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) 1373{ 1374 u_int idxlen = oid->len - sub; 1375 struct ng_mesg *resp; 1376 struct namelist *list; 1377 ng_ID_t id; 1378 u_int i; 1379 1380 if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, 1381 NULL, 0)) == NULL) 1382 return (-1); 1383 list = (struct namelist *)(void *)resp->data; 1384 1385 qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); 1386 1387 if (idxlen == 0) { 1388 if (list->numnames == 0) { 1389 free(resp); 1390 return (-1); 1391 } 1392 *info = list->nodeinfo[0]; 1393 free(resp); 1394 return (0); 1395 } 1396 id = oid->subs[sub]; 1397 1398 for (i = 0; i < list->numnames; i++) 1399 if (list->nodeinfo[i].id > id) { 1400 *info = list->nodeinfo[i]; 1401 free(resp); 1402 return (0); 1403 } 1404 1405 free(resp); 1406 return (-1); 1407} 1408 1409int 1410op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value, 1411 u_int sub, u_int iidx __unused, enum snmp_op op) 1412{ 1413 asn_subid_t which = value->var.subs[sub - 1]; 1414 u_int idxlen = value->var.len - sub; 1415 struct nodeinfo nodeinfo; 1416 1417 switch (op) { 1418 1419 case SNMP_OP_GETNEXT: 1420 if (find_node_next(&value->var, sub, &nodeinfo) == -1) 1421 return (SNMP_ERR_NOSUCHNAME); 1422 value->var.len = sub + 1; 1423 value->var.subs[sub] = nodeinfo.id; 1424 break; 1425 1426 case SNMP_OP_GET: 1427 if (idxlen != 1) 1428 return (SNMP_ERR_NOSUCHNAME); 1429 if (find_node(&value->var, sub, &nodeinfo) == -1) 1430 return (SNMP_ERR_NOSUCHNAME); 1431 break; 1432 1433 case SNMP_OP_SET: 1434 if (idxlen != 1) 1435 return (SNMP_ERR_NO_CREATION); 1436 if (find_node(&value->var, sub, &nodeinfo) == -1) 1437 return (SNMP_ERR_NO_CREATION); 1438 return (SNMP_ERR_NOT_WRITEABLE); 1439 1440 case SNMP_OP_ROLLBACK: 1441 case SNMP_OP_COMMIT: 1442 default: 1443 abort(); 1444 } 1445 1446 /* 1447 * Come here for GET and COMMIT 1448 */ 1449 switch (which) { 1450 1451 case LEAF_begemotNgNodeStatus: 1452 value->v.integer = 1; 1453 break; 1454 case LEAF_begemotNgNodeName: 1455 return (string_get(value, nodeinfo.name, -1)); 1456 case LEAF_begemotNgNodeType: 1457 return (string_get(value, nodeinfo.type, -1)); 1458 case LEAF_begemotNgNodeHooks: 1459 value->v.uint32 = nodeinfo.hooks; 1460 break; 1461 1462 default: 1463 abort(); 1464 } 1465 return (SNMP_ERR_NOERROR); 1466} 1467 1468/* 1469 * Implement the hook table 1470 */ 1471static int 1472find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info) 1473{ 1474 struct ng_mesg *resp; 1475 struct hooklist *list; 1476 u_int i; 1477 1478 if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, 1479 NGM_LISTHOOKS, NULL, 0)) == NULL) 1480 return (-1); 1481 1482 list = (struct hooklist *)(void *)resp->data; 1483 1484 for (i = 0; i < list->nodeinfo.hooks; i++) { 1485 if (strlen(list->link[i].ourhook) == hooklen && 1486 strncmp(list->link[i].ourhook, hook, hooklen) == 0) { 1487 *info = list->link[i]; 1488 free(resp); 1489 return (0); 1490 } 1491 } 1492 free(resp); 1493 return (-1); 1494} 1495 1496static int 1497hook_cmp(const void *p1, const void *p2) 1498{ 1499 const struct linkinfo *i1 = p1; 1500 const struct linkinfo *i2 = p2; 1501 1502 if (strlen(i1->ourhook) < strlen(i2->ourhook)) 1503 return (-1); 1504 if (strlen(i1->ourhook) > strlen(i2->ourhook)) 1505 return (+1); 1506 return (strcmp(i1->ourhook, i2->ourhook)); 1507} 1508 1509static int 1510find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo, 1511 struct linkinfo *linkinfo) 1512{ 1513 u_int idxlen = oid->len - sub; 1514 struct namelist *list; 1515 struct ng_mesg *resp; 1516 struct hooklist *hooks; 1517 struct ng_mesg *resp1; 1518 u_int node_index; 1519 struct asn_oid idx; 1520 u_int i, j; 1521 1522 /* 1523 * Get and sort Node list 1524 */ 1525 if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, 1526 NULL, 0)) == NULL) 1527 return (-1); 1528 list = (struct namelist *)(void *)resp->data; 1529 1530 qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); 1531 1532 /* 1533 * If we have no index, take the first node and return the 1534 * first hook. 1535 */ 1536 if (idxlen == 0) { 1537 node_index = 0; 1538 goto return_first_hook; 1539 } 1540 1541 /* 1542 * Locate node 1543 */ 1544 for (node_index = 0; node_index < list->numnames; node_index++) 1545 if (list->nodeinfo[node_index].id >= oid->subs[sub]) 1546 break; 1547 1548 /* 1549 * If we have only the node part of the index take, or 1550 * there is no node with that Id, take the first hook of that node. 1551 */ 1552 if (idxlen == 1 || node_index >= list->numnames || 1553 list->nodeinfo[node_index].id > oid->subs[sub]) 1554 goto return_first_hook; 1555 1556 /* 1557 * We had an exact match on the node id and have (at last part) 1558 * of the hook name index. Loop through the hooks of the node 1559 * and find the next one. 1560 */ 1561 if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, 1562 NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { 1563 free(resp); 1564 return (-1); 1565 } 1566 hooks = (struct hooklist *)(void *)resp1->data; 1567 if (hooks->nodeinfo.hooks > 0) { 1568 qsort(hooks->link, hooks->nodeinfo.hooks, 1569 sizeof(hooks->link[0]), hook_cmp); 1570 for (i = 0; i < hooks->nodeinfo.hooks; i++) { 1571 idx.len = strlen(hooks->link[i].ourhook) + 1; 1572 idx.subs[0] = idx.len - 1; 1573 for (j = 0; j < idx.len; j++) 1574 idx.subs[j + 1] = hooks->link[i].ourhook[j]; 1575 if (index_compare(oid, sub + 1, &idx) < 0) 1576 break; 1577 } 1578 if (i < hooks->nodeinfo.hooks) { 1579 *nodeinfo = hooks->nodeinfo; 1580 *linkinfo = hooks->link[i]; 1581 1582 free(resp); 1583 free(resp1); 1584 return (0); 1585 } 1586 } 1587 1588 /* no hook found larger than the index on the index node - take 1589 * first hook of next node */ 1590 free(resp1); 1591 node_index++; 1592 1593 return_first_hook: 1594 while (node_index < list->numnames) { 1595 if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, 1596 NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) 1597 break; 1598 hooks = (struct hooklist *)(void *)resp1->data; 1599 if (hooks->nodeinfo.hooks > 0) { 1600 qsort(hooks->link, hooks->nodeinfo.hooks, 1601 sizeof(hooks->link[0]), hook_cmp); 1602 1603 *nodeinfo = hooks->nodeinfo; 1604 *linkinfo = hooks->link[0]; 1605 1606 free(resp); 1607 free(resp1); 1608 return (0); 1609 } 1610 1611 /* if we don't have hooks, try next node */ 1612 free(resp1); 1613 node_index++; 1614 } 1615 1616 free(resp); 1617 return (-1); 1618} 1619 1620int 1621op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value, 1622 u_int sub, u_int iidx, enum snmp_op op) 1623{ 1624 asn_subid_t which = value->var.subs[sub - 1]; 1625 struct linkinfo linkinfo; 1626 struct nodeinfo nodeinfo; 1627 u_int32_t lid; 1628 u_char *hook; 1629 size_t hooklen; 1630 u_int i; 1631 1632 switch (op) { 1633 1634 case SNMP_OP_GETNEXT: 1635 if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1) 1636 return (SNMP_ERR_NOSUCHNAME); 1637 1638 value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook); 1639 value->var.subs[sub] = nodeinfo.id; 1640 value->var.subs[sub + 1] = strlen(linkinfo.ourhook); 1641 for (i = 0; i < strlen(linkinfo.ourhook); i++) 1642 value->var.subs[sub + i + 2] = 1643 linkinfo.ourhook[i]; 1644 break; 1645 1646 case SNMP_OP_GET: 1647 if (index_decode(&value->var, sub, iidx, &lid, 1648 &hook, &hooklen)) 1649 return (SNMP_ERR_NOSUCHNAME); 1650 if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { 1651 free(hook); 1652 return (SNMP_ERR_NOSUCHNAME); 1653 } 1654 free(hook); 1655 break; 1656 1657 case SNMP_OP_SET: 1658 if (index_decode(&value->var, sub, iidx, &lid, 1659 &hook, &hooklen)) 1660 return (SNMP_ERR_NO_CREATION); 1661 if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { 1662 free(hook); 1663 return (SNMP_ERR_NO_CREATION); 1664 } 1665 free(hook); 1666 return (SNMP_ERR_NOT_WRITEABLE); 1667 1668 case SNMP_OP_ROLLBACK: 1669 case SNMP_OP_COMMIT: 1670 default: 1671 abort(); 1672 1673 } 1674 1675 switch (which) { 1676 1677 case LEAF_begemotNgHookStatus: 1678 value->v.integer = 1; 1679 break; 1680 case LEAF_begemotNgHookPeerNodeId: 1681 value->v.uint32 = linkinfo.nodeinfo.id; 1682 break; 1683 case LEAF_begemotNgHookPeerHook: 1684 return (string_get(value, linkinfo.peerhook, -1)); 1685 case LEAF_begemotNgHookPeerType: 1686 return (string_get(value, linkinfo.nodeinfo.type, -1)); 1687 default: 1688 abort(); 1689 } 1690 return (SNMP_ERR_NOERROR); 1691} 1692