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 * 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 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 * 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 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 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 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 * 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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