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