diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c')
-rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c | 1690 |
1 files changed, 0 insertions, 1690 deletions
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c deleted file mode 100644 index d9d136a..0000000 --- a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c +++ /dev/null @@ -1,1690 +0,0 @@ -/* - * Copyright (c) 2001-2003 - * Fraunhofer Institute for Open Communication Systems (FhG Fokus). - * All rights reserved. - * - * Author: Harti Brandt <harti@freebsd.org> - * - * Redistribution of this software and documentation and use in source and - * binary forms, with or without modification, are permitted provided that - * the following conditions are met: - * - * 1. Redistributions of source code or documentation must retain the above - * copyright notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS - * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - * - * Netgraph interface for SNMPd. - */ -#include <sys/types.h> -#include <sys/param.h> -#include <sys/linker.h> -#include <sys/socket.h> -#include <sys/syslog.h> -#include <sys/queue.h> -#include <sys/sysctl.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <string.h> -#include <netgraph.h> -#include <bsnmp/snmpmod.h> -#include "snmp_netgraph.h" -#include "netgraph_tree.h" -#include "netgraph_oid.h" - -/* maximum message size */ -#define RESBUFSIZ 20000 - -/* default node name */ -#define NODENAME "NgSnmpd" - -/* my node Id */ -ng_ID_t snmp_node; -u_char *snmp_nodename; - -/* the Object Resource registration index */ -static u_int reg_index; -static const struct asn_oid oid_begemotNg = OIDX_begemotNg; - -/* configuration */ -/* this must be smaller than int32_t because the functions in libnetgraph - * falsely return an int */ -static size_t resbufsiz = RESBUFSIZ; -static u_int timeout = 1000; -static u_int debug_level; - -/* number of microseconds per clock tick */ -static struct clockinfo clockinfo; - -/* Csock buffers. Communication on the csock is asynchronuous. This means - * if we wait for a specific response, we may get other messages. Put these - * into a queue and execute them when we are idle. */ -struct csock_buf { - STAILQ_ENTRY(csock_buf) link; - struct ng_mesg *mesg; - char path[NG_PATHSIZ]; -}; -static STAILQ_HEAD(, csock_buf) csock_bufs = - STAILQ_HEAD_INITIALIZER(csock_bufs); - -/* - * We dispatch unsolicieted messages by node cookies and ids. - * So we must keep a list of hook names and dispatch functions. - */ -struct msgreg { - u_int32_t cookie; - ng_ID_t id; - ng_cookie_f *func; - void *arg; - const struct lmodule *mod; - SLIST_ENTRY(msgreg) link; -}; -static SLIST_HEAD(, msgreg) msgreg_list = - SLIST_HEAD_INITIALIZER(msgreg_list); - -/* - * Data messages are dispatched by hook names. - */ -struct datareg { - char hook[NG_HOOKSIZ]; - ng_hook_f *func; - void *arg; - const struct lmodule *mod; - SLIST_ENTRY(datareg) link; -}; -static SLIST_HEAD(, datareg) datareg_list = - SLIST_HEAD_INITIALIZER(datareg_list); - -/* the netgraph sockets */ -static int csock, dsock; -static void *csock_fd, *dsock_fd; - -/* our module handle */ -static struct lmodule *module; - -/* statistics */ -static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1]; - -/* netgraph type list */ -struct ngtype { - char name[NG_TYPESIZ]; - struct asn_oid index; - TAILQ_ENTRY(ngtype) link; -}; -TAILQ_HEAD(ngtype_list, ngtype); - -static struct ngtype_list ngtype_list; -static uint64_t ngtype_tick; - - -/* - * Register a function to receive unsolicited messages - */ -void * -ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id, - ng_cookie_f *func, void *arg) -{ - struct msgreg *d; - - if ((d = malloc(sizeof(*d))) == NULL) - return (NULL); - - d->cookie = cookie; - d->id = id; - d->func = func; - d->arg = arg; - d->mod = mod; - - SLIST_INSERT_HEAD(&msgreg_list, d, link); - - return (d); -} - -/* - * Remove a registration. - */ -void -ng_unregister_cookie(void *dd) -{ - struct msgreg *d = dd; - - SLIST_REMOVE(&msgreg_list, d, msgreg, link); - free(d); -} - -/* - * Register a function for hook data. - */ -void * -ng_register_hook(const struct lmodule *mod, const char *hook, - ng_hook_f *func, void *arg) -{ - struct datareg *d; - - if ((d = malloc(sizeof(*d))) == NULL) - return (NULL); - - strcpy(d->hook, hook); - d->func = func; - d->arg = arg; - d->mod = mod; - - SLIST_INSERT_HEAD(&datareg_list, d, link); - - return (d); -} - -/* - * Unregister a hook function - */ -void -ng_unregister_hook(void *dd) -{ - struct datareg *d = dd; - - SLIST_REMOVE(&datareg_list, d, datareg, link); - free(d); -} - -/* - * Unregister all hooks and cookies for that module. Note: doesn't disconnect - * any hooks! - */ -void -ng_unregister_module(const struct lmodule *mod) -{ - struct msgreg *m, *m1; - struct datareg *d, *d1; - - m = SLIST_FIRST(&msgreg_list); - while (m != NULL) { - m1 = SLIST_NEXT(m, link); - if (m->mod == mod) { - SLIST_REMOVE(&msgreg_list, m, msgreg, link); - free(m); - } - m = m1; - } - - d = SLIST_FIRST(&datareg_list); - while (d != NULL) { - d1 = SLIST_NEXT(d, link); - if (d->mod == mod) { - SLIST_REMOVE(&datareg_list, d, datareg, link); - free(d); - } - d = d1; - } -} - -/* - * Dispatch a message to the correct module and delete it. More than one - * module can get a message. - */ -static void -csock_handle(struct ng_mesg *mesg, const char *path) -{ - struct msgreg *d, *d1; - u_int id; - int len; - - if (sscanf(path, "[%x]:%n", &id, &len) != 1 || - (u_int)len != strlen(path)) { - syslog(LOG_ERR, "cannot parse message path '%s'", path); - id = 0; - } - - d = SLIST_FIRST(&msgreg_list); - while (d != NULL) { - d1 = SLIST_NEXT(d, link); - if (d->cookie == mesg->header.typecookie && - (d->id == 0 || d->id == id || id == 0)) - (*d->func)(mesg, path, id, d->arg); - d = d1; - } - free(mesg); -} - -/* - * Input from the control socket. - */ -static struct ng_mesg * -csock_read(char *path) -{ - struct ng_mesg *mesg; - int ret, err; - - if ((mesg = malloc(resbufsiz + 1)) == NULL) { - stats[LEAF_begemotNgNoMems]++; - syslog(LOG_CRIT, "out of memory"); - errno = ENOMEM; - return (NULL); - } - if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) { - err = errno; - free(mesg); - if (errno == EWOULDBLOCK) { - errno = err; - return (NULL); - } - stats[LEAF_begemotNgMsgReadErrs]++; - syslog(LOG_WARNING, "read from csock: %m"); - errno = err; - return (NULL); - } - if (ret == 0) { - syslog(LOG_DEBUG, "node closed -- exiting"); - exit(0); - } - if ((size_t)ret > resbufsiz) { - stats[LEAF_begemotNgTooLargeMsgs]++; - syslog(LOG_WARNING, "ng message too large"); - free(mesg); - errno = EFBIG; - return (NULL); - } - return (mesg); -} - -static void -csock_input(int fd __unused, void *udata __unused) -{ - struct ng_mesg *mesg; - char path[NG_PATHSIZ]; - - if ((mesg = csock_read(path)) == NULL) - return; - - csock_handle(mesg, path); -} - -/* - * Write a message to a node. - */ -int -ng_output(const char *path, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen)); -} -int -ng_output_node(const char *node, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - char path[NG_PATHSIZ]; - - sprintf(path, "%s:", node); - return (ng_output(path, cookie, opcode, arg, arglen)); -} -int -ng_output_id(ng_ID_t node, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - char path[NG_PATHSIZ]; - - sprintf(path, "[%x]:", node); - return (ng_output(path, cookie, opcode, arg, arglen)); -} - - - -/* - * Execute a synchronuous dialog with the csock. All message we receive, that - * do not match our request, are queue until the next call to the IDLE function. - */ -struct ng_mesg * -ng_dialog(const char *path, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - int token, err; - struct ng_mesg *mesg; - char rpath[NG_PATHSIZ]; - struct csock_buf *b; - struct timeval end, tv; - - if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0) - return (NULL); - - if (csock_fd) - fd_suspend(csock_fd); - - gettimeofday(&end, NULL); - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - timeradd(&end, &tv, &end); - for (;;) { - mesg = NULL; - gettimeofday(&tv, NULL); - if (timercmp(&tv, &end, >=)) { - block: - syslog(LOG_WARNING, "no response for request %u/%u", - cookie, opcode); - errno = EWOULDBLOCK; - break; - } - timersub(&end, &tv, &tv); - if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick) - goto block; - - if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) - syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m"); - if ((mesg = csock_read(rpath)) == NULL) { - if (errno == EWOULDBLOCK) - continue; - break; - } - if (mesg->header.token == (u_int)token) - break; - if ((b = malloc(sizeof(*b))) == NULL) { - stats[LEAF_begemotNgNoMems]++; - syslog(LOG_ERR, "out of memory"); - free(mesg); - continue; - } - b->mesg = mesg; - strcpy(b->path, rpath); - STAILQ_INSERT_TAIL(&csock_bufs, b, link); - } - - tv.tv_sec = 0; - tv.tv_usec = 0; - if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) - syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m"); - - if (csock_fd) { - err = errno; - fd_resume(csock_fd); - errno = err; - } - - return (mesg); -} -struct ng_mesg * -ng_dialog_node(const char *node, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - char path[NG_PATHSIZ]; - - sprintf(path, "%s:", node); - return (ng_dialog(path, cookie, opcode, arg, arglen)); -} -struct ng_mesg * -ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode, - const void *arg, size_t arglen) -{ - char path[NG_PATHSIZ]; - - sprintf(path, "[%x]:", id); - return (ng_dialog(path, cookie, opcode, arg, arglen)); -} - - -/* - * Send a data message to a given hook. - */ -int -ng_send_data(const char *hook, const void *sndbuf, size_t sndlen) -{ - return (NgSendData(dsock, hook, sndbuf, sndlen)); -} - -/* - * Input from a data socket. Dispatch to the function for that hook. - */ -static void -dsock_input(int fd __unused, void *udata __unused) -{ - u_char *resbuf, embuf[100]; - ssize_t len; - char hook[NG_HOOKSIZ]; - struct datareg *d, *d1; - - if ((resbuf = malloc(resbufsiz + 1)) == NULL) { - stats[LEAF_begemotNgNoMems]++; - syslog(LOG_CRIT, "out of memory"); - (void)NgRecvData(fd, embuf, sizeof(embuf), hook); - errno = ENOMEM; - return; - } - if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) { - stats[LEAF_begemotNgDataReadErrs]++; - syslog(LOG_ERR, "reading message: %m"); - free(resbuf); - return; - } - if (len == 0) { - free(resbuf); - return; - } - if ((size_t)len == resbufsiz + 1) { - stats[LEAF_begemotNgTooLargeDatas]++; - syslog(LOG_WARNING, "message too long"); - free(resbuf); - return; - } - - /* - * Dispatch message. Maybe dispatched to more than one function. - */ - d = SLIST_FIRST(&datareg_list); - while (d != NULL) { - d1 = SLIST_NEXT(d, link); - if (strcmp(hook, d->hook) == 0) - (*d->func)(hook, resbuf, len, d->arg); - d = d1; - } - - free(resbuf); -} - -/* - * The SNMP daemon is about to wait for an event. Look whether we have - * netgraph messages waiting. If yes, drain the queue. - */ -static void -ng_idle(void) -{ - struct csock_buf *b; - - /* execute waiting csock_bufs */ - while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) { - STAILQ_REMOVE_HEAD(&csock_bufs, link); - csock_handle(b->mesg, b->path); - free(b); - } -} - -/* - * Called when the module is loaded. Returning a non-zero value means, - * rejecting the initialisation. - * - * We make the netgraph socket. - */ -static int -ng_init(struct lmodule *mod, int argc, char *argv[]) -{ - int name[2]; - size_t len; - - module = mod; - - if (argc == 0) { - if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL) - return (ENOMEM); - strcpy(snmp_nodename, NODENAME); - } else { - if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL) - return (ENOMEM); - strlcpy(snmp_nodename, argv[0], NG_NODESIZ); - } - - /* fetch clockinfo (for the number of microseconds per tick) */ - name[0] = CTL_KERN; - name[1] = KERN_CLOCKRATE; - len = sizeof(clockinfo); - if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1) - return (errno); - - TAILQ_INIT(&ngtype_list); - - return (0); -} - -/* - * Get the node Id/name/type of a node. - */ -ng_ID_t -ng_node_id(const char *path) -{ - struct ng_mesg *resp; - ng_ID_t id; - - if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO, - NULL, 0)) == NULL) - return (0); - id = ((struct nodeinfo *)(void *)resp->data)->id; - free(resp); - return (id); -} -ng_ID_t -ng_node_id_node(const char *node) -{ - struct ng_mesg *resp; - ng_ID_t id; - - if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO, - NULL, 0)) == NULL) - return (0); - id = ((struct nodeinfo *)(void *)resp->data)->id; - free(resp); - return (id); -} -ng_ID_t -ng_node_name(ng_ID_t id, char *name) -{ - struct ng_mesg *resp; - - if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, - NULL, 0)) == NULL) - return (0); - strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name); - free(resp); - return (id); - -} -ng_ID_t -ng_node_type(ng_ID_t id, char *type) -{ - struct ng_mesg *resp; - - if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, - NULL, 0)) == NULL) - return (0); - strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type); - free(resp); - return (id); -} - -/* - * Connect our node to some other node - */ -int -ng_connect_node(const char *node, const char *ourhook, const char *peerhook) -{ - struct ngm_connect conn; - - snprintf(conn.path, NG_PATHSIZ, "%s:", node); - strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); - strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); - return (NgSendMsg(csock, ".:", - NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); -} -int -ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook) -{ - struct ngm_connect conn; - - snprintf(conn.path, NG_PATHSIZ, "[%x]:", id); - strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); - strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); - return (NgSendMsg(csock, ".:", - NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); -} - -int -ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, - const char *peerhook) -{ - struct ngm_connect conn; - char path[NG_PATHSIZ]; - - snprintf(path, NG_PATHSIZ, "[%x]:", id); - - snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); - strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); - strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); - return (NgSendMsg(csock, path, - NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); -} - -int -ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, - const char *peerhook) -{ - struct ngm_connect conn; - char path[NG_PATHSIZ]; - ng_ID_t tee; - - if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0) - return (-1); - - snprintf(path, NG_PATHSIZ, "[%x]:", tee); - - snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); - strlcpy(conn.ourhook, "right", NG_HOOKSIZ); - strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); - return (NgSendMsg(csock, path, - NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); -} - -/* - * Ensure that a node of type 'type' is connected to 'hook' of 'node' - * and return its node id. tee nodes between node and the target node - * are skipped. If the type is wrong, or the hook is a dead-end return 0. - * If type is NULL, it is not checked. - */ -static ng_ID_t -ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook, - int skip_tee) -{ - struct ng_mesg *resp; - struct hooklist *hooklist; - u_int i; - - if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, - NULL, 0)) == NULL) { - syslog(LOG_ERR, "get hook list: %m"); - exit(1); - } - hooklist = (struct hooklist *)(void *)resp->data; - - for (i = 0; i < hooklist->nodeinfo.hooks; i++) - if (strcmp(hooklist->link[i].ourhook, hook) == 0) - break; - - if (i == hooklist->nodeinfo.hooks) { - free(resp); - return (0); - } - - node = hooklist->link[i].nodeinfo.id; - - if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { - if (strcmp(hooklist->link[i].peerhook, "left") == 0) - node = ng_next_node_id(node, type, "right"); - else if (strcmp(hooklist->link[i].peerhook, "right") == 0) - node = ng_next_node_id(node, type, "left"); - else if (type != NULL && - strcmp(hooklist->link[i].nodeinfo.type, type) != 0) - node = 0; - - } else if (type != NULL && - strcmp(hooklist->link[i].nodeinfo.type, type) != 0) - node = 0; - - free(resp); - - return (node); -} - -/* - * Ensure that a node of type 'type' is connected to 'hook' of 'node' - * and return its node id. tee nodes between node and the target node - * are skipped. If the type is wrong, or the hook is a dead-end return 0. - * If type is NULL, it is not checked. - */ -ng_ID_t -ng_next_node_id(ng_ID_t node, const char *type, const char *hook) -{ - return (ng_next_node_id_internal(node, type, hook, 1)); -} - -ng_ID_t -ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type, - const char *hook, const char *peerhook) -{ - char path[NG_PATHSIZ]; - struct ngm_mkpeer mkpeer; - struct ngm_name name; - - strlcpy(mkpeer.type, type, NG_TYPESIZ); - strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ); - strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ); - - sprintf(path, "[%x]:", id); - if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, - &mkpeer, sizeof(mkpeer)) == -1) - return (0); - - if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0) - return (0); - - if (nodename != NULL) { - strcpy(name.name, nodename); - sprintf(path, "[%x]:", id); - if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME, - &name, sizeof(name)) == -1) - return (0); - } - return (id); -} - -/* - * SHutdown node - */ -int -ng_shutdown_id(ng_ID_t id) -{ - char path[NG_PATHSIZ]; - - snprintf(path, NG_PATHSIZ, "[%x]:", id); - return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, - NGM_SHUTDOWN, NULL, 0)); -} - -/* - * Disconnect one of our hooks - */ -int -ng_rmhook(const char *ourhook) -{ - struct ngm_rmhook rmhook; - - strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ); - return (NgSendMsg(csock, ".:", - NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); -} - -/* - * Disconnect a hook of a node - */ -int -ng_rmhook_id(ng_ID_t id, const char *hook) -{ - struct ngm_rmhook rmhook; - char path[NG_PATHSIZ]; - - strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ); - snprintf(path, NG_PATHSIZ, "[%x]:", id); - return (NgSendMsg(csock, path, - NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); -} - -/* - * Disconnect a hook and shutdown all tee nodes that were connected to that - * hook. - */ -int -ng_rmhook_tee_id(ng_ID_t node, const char *hook) -{ - struct ng_mesg *resp; - struct hooklist *hooklist; - u_int i; - int first = 1; - ng_ID_t next_node; - const char *next_hook; - - again: - /* if we have just shutdown a tee node, which had no other hooks - * connected, the node id may already be wrong here. */ - if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, - NULL, 0)) == NULL) - return (0); - - hooklist = (struct hooklist *)(void *)resp->data; - - for (i = 0; i < hooklist->nodeinfo.hooks; i++) - if (strcmp(hooklist->link[i].ourhook, hook) == 0) - break; - - if (i == hooklist->nodeinfo.hooks) { - free(resp); - return (0); - } - - next_node = 0; - next_hook = NULL; - if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { - if (strcmp(hooklist->link[i].peerhook, "left") == 0) { - next_node = hooklist->link[i].nodeinfo.id; - next_hook = "right"; - } else if (strcmp(hooklist->link[i].peerhook, "right") == 0) { - next_node = hooklist->link[i].nodeinfo.id; - next_hook = "left"; - } - } - free(resp); - - if (first) { - ng_rmhook_id(node, hook); - first = 0; - } else { - ng_shutdown_id(node); - } - if ((node = next_node) == 0) - return (0); - hook = next_hook; - - goto again; -} - -/* - * Get the peer hook of a hook on a given node. Skip any tee nodes in between - */ -int -ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook) -{ - struct ng_mesg *resp; - struct hooklist *hooklist; - u_int i; - int ret; - - if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, - NULL, 0)) == NULL) { - syslog(LOG_ERR, "get hook list: %m"); - exit(1); - } - hooklist = (struct hooklist *)(void *)resp->data; - - for (i = 0; i < hooklist->nodeinfo.hooks; i++) - if (strcmp(hooklist->link[i].ourhook, hook) == 0) - break; - - if (i == hooklist->nodeinfo.hooks) { - free(resp); - return (-1); - } - - node = hooklist->link[i].nodeinfo.id; - - ret = 0; - if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { - if (strcmp(hooklist->link[i].peerhook, "left") == 0) - ret = ng_peer_hook_id(node, "right", peerhook); - else if (strcmp(hooklist->link[i].peerhook, "right") == 0) - ret = ng_peer_hook_id(node, "left", peerhook); - else - strcpy(peerhook, hooklist->link[i].peerhook); - - } else - strcpy(peerhook, hooklist->link[i].peerhook); - - free(resp); - - return (ret); -} - - -/* - * Now the module is started. Select on the sockets, so that we can get - * unsolicited input. - */ -static void -ng_start(void) -{ - if (snmp_node == 0) { - if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { - syslog(LOG_ERR, "NgMkSockNode: %m"); - exit(1); - } - snmp_node = ng_node_id(".:"); - } - - if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) { - syslog(LOG_ERR, "fd_select failed on csock: %m"); - return; - } - if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) { - syslog(LOG_ERR, "fd_select failed on dsock: %m"); - return; - } - - reg_index = or_register(&oid_begemotNg, - "The MIB for the NetGraph access module for SNMP.", module); -} - -/* - * Called, when the module is to be unloaded after it was successfully loaded - */ -static int -ng_fini(void) -{ - struct ngtype *t; - - while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { - TAILQ_REMOVE(&ngtype_list, t, link); - free(t); - } - - if (csock_fd != NULL) - fd_deselect(csock_fd); - (void)close(csock); - - if (dsock_fd != NULL) - fd_deselect(dsock_fd); - (void)close(dsock); - - free(snmp_nodename); - - or_unregister(reg_index); - - return (0); -} - -const struct snmp_module config = { - "This module implements access to the netgraph sub-system", - ng_init, - ng_fini, - ng_idle, - NULL, - NULL, - ng_start, - NULL, - netgraph_ctree, - netgraph_CTREE_SIZE, - NULL -}; - -int -op_ng_config(struct snmp_context *ctx, struct snmp_value *value, - u_int sub, u_int iidx __unused, enum snmp_op op) -{ - asn_subid_t which = value->var.subs[sub - 1]; - int ret; - - switch (op) { - - case SNMP_OP_GETNEXT: - abort(); - - case SNMP_OP_GET: - /* - * Come here for GET, GETNEXT and COMMIT - */ - switch (which) { - - case LEAF_begemotNgControlNodeName: - return (string_get(value, snmp_nodename, -1)); - - case LEAF_begemotNgResBufSiz: - value->v.integer = resbufsiz; - break; - - case LEAF_begemotNgTimeout: - value->v.integer = timeout; - break; - - case LEAF_begemotNgDebugLevel: - value->v.uint32 = debug_level; - break; - - default: - abort(); - } - return (SNMP_ERR_NOERROR); - - case SNMP_OP_SET: - switch (which) { - - case LEAF_begemotNgControlNodeName: - /* only at initialisation */ - if (community != COMM_INITIALIZE) - return (SNMP_ERR_NOT_WRITEABLE); - - if (snmp_node != 0) - return (SNMP_ERR_NOT_WRITEABLE); - - if ((ret = string_save(value, ctx, -1, &snmp_nodename)) - != SNMP_ERR_NOERROR) - return (ret); - - if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { - syslog(LOG_ERR, "NgMkSockNode: %m"); - string_rollback(ctx, &snmp_nodename); - return (SNMP_ERR_GENERR); - } - snmp_node = ng_node_id(".:"); - - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgResBufSiz: - ctx->scratch->int1 = resbufsiz; - if (value->v.integer < 1024 || - value->v.integer > 0x10000) - return (SNMP_ERR_WRONG_VALUE); - resbufsiz = value->v.integer; - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgTimeout: - ctx->scratch->int1 = timeout; - if (value->v.integer < 10 || - value->v.integer > 10000) - return (SNMP_ERR_WRONG_VALUE); - timeout = value->v.integer; - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgDebugLevel: - ctx->scratch->int1 = debug_level; - debug_level = value->v.uint32; - NgSetDebug(debug_level); - return (SNMP_ERR_NOERROR); - } - abort(); - - case SNMP_OP_ROLLBACK: - switch (which) { - - case LEAF_begemotNgControlNodeName: - string_rollback(ctx, &snmp_nodename); - close(csock); - close(dsock); - snmp_node = 0; - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgResBufSiz: - resbufsiz = ctx->scratch->int1; - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgTimeout: - timeout = ctx->scratch->int1; - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgDebugLevel: - debug_level = ctx->scratch->int1; - NgSetDebug(debug_level); - return (SNMP_ERR_NOERROR); - } - abort(); - - case SNMP_OP_COMMIT: - switch (which) { - - case LEAF_begemotNgControlNodeName: - string_commit(ctx); - return (SNMP_ERR_NOERROR); - - case LEAF_begemotNgResBufSiz: - case LEAF_begemotNgTimeout: - case LEAF_begemotNgDebugLevel: - return (SNMP_ERR_NOERROR); - } - abort(); - } - abort(); -} - -int -op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value, - u_int sub, u_int iidx __unused, enum snmp_op op) -{ - switch (op) { - - case SNMP_OP_GETNEXT: - abort(); - - case SNMP_OP_GET: - value->v.uint32 = stats[value->var.subs[sub - 1] - 1]; - return (SNMP_ERR_NOERROR); - - case SNMP_OP_SET: - return (SNMP_ERR_NOT_WRITEABLE); - - case SNMP_OP_ROLLBACK: - case SNMP_OP_COMMIT: - abort(); - } - abort(); -} - -/* - * Netgraph type table - */ -static int -fetch_types(void) -{ - struct ngtype *t; - struct typelist *typelist; - struct ng_mesg *resp; - u_int u, i; - - if (this_tick <= ngtype_tick) - return (0); - - while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { - TAILQ_REMOVE(&ngtype_list, t, link); - free(t); - } - - if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, - NGM_LISTTYPES, NULL, 0)) == NULL) - return (SNMP_ERR_GENERR); - typelist = (struct typelist *)(void *)resp->data; - - for (u = 0; u < typelist->numtypes; u++) { - if ((t = malloc(sizeof(*t))) == NULL) { - free(resp); - return (SNMP_ERR_GENERR); - } - strcpy(t->name, typelist->typeinfo[u].type_name); - t->index.subs[0] = strlen(t->name); - t->index.len = t->index.subs[0] + 1; - for (i = 0; i < t->index.subs[0]; i++) - t->index.subs[i + 1] = t->name[i]; - - INSERT_OBJECT_OID(t, &ngtype_list); - } - - ngtype_tick = this_tick; - - free(resp); - return (0); -} - -/* - * Try to load the netgraph type with the given name. We assume, that - * type 'type' is implemented in the kernel module 'ng_type'. - */ -static int -ngtype_load(const u_char *name, size_t namelen) -{ - char *mod; - int ret; - - if ((mod = malloc(namelen + 4)) == NULL) - return (-1); - strcpy(mod, "ng_"); - strncpy(mod + 3, name, namelen); - mod[namelen + 3] = '\0'; - - ret = kldload(mod); - free(mod); - return (ret); -} - -/* - * Unload a netgraph type. - */ -static int -ngtype_unload(const u_char *name, size_t namelen) -{ - char *mod; - int id; - - if ((mod = malloc(namelen + 4)) == NULL) - return (-1); - strcpy(mod, "ng_"); - strncpy(mod + 3, name, namelen); - mod[namelen + 3] = '\0'; - - if ((id = kldfind(mod)) == -1) { - free(mod); - return (-1); - } - free(mod); - return (kldunload(id)); -} - -int -op_ng_type(struct snmp_context *ctx, struct snmp_value *value, - u_int sub, u_int iidx, enum snmp_op op) -{ - asn_subid_t which = value->var.subs[sub - 1]; - struct ngtype *t; - u_char *name; - size_t namelen; - int status = 1; - int ret; - - switch (op) { - - case SNMP_OP_GETNEXT: - if ((ret = fetch_types()) != 0) - return (ret); - if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) - return (SNMP_ERR_NOSUCHNAME); - index_append(&value->var, sub, &t->index); - break; - - case SNMP_OP_GET: - if ((ret = fetch_types()) != 0) - return (ret); - if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) - return (SNMP_ERR_NOSUCHNAME); - break; - - case SNMP_OP_SET: - if (index_decode(&value->var, sub, iidx, &name, &namelen)) - return (SNMP_ERR_NO_CREATION); - if (namelen == 0 || namelen >= NG_TYPESIZ) { - free(name); - return (SNMP_ERR_NO_CREATION); - } - if ((ret = fetch_types()) != 0) { - free(name); - return (ret); - } - t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub); - - if (which != LEAF_begemotNgTypeStatus) { - free(name); - if (t != NULL) - return (SNMP_ERR_NOT_WRITEABLE); - return (SNMP_ERR_NO_CREATION); - } - if (!TRUTH_OK(value->v.integer)) { - free(name); - return (SNMP_ERR_WRONG_VALUE); - } - ctx->scratch->int1 = TRUTH_GET(value->v.integer); - ctx->scratch->int1 |= (t != NULL) << 1; - ctx->scratch->ptr2 = name; - ctx->scratch->int2 = namelen; - - if (t == NULL) { - /* type not loaded */ - if (ctx->scratch->int1 & 1) { - /* request to load */ - if (ngtype_load(name, namelen) == -1) { - free(name); - if (errno == ENOENT) - return (SNMP_ERR_INCONS_NAME); - else - return (SNMP_ERR_GENERR); - } - } - } else { - /* is type loaded */ - if (!(ctx->scratch->int1 & 1)) { - /* request to unload */ - if (ngtype_unload(name, namelen) == -1) { - free(name); - return (SNMP_ERR_GENERR); - } - } - } - return (SNMP_ERR_NOERROR); - - case SNMP_OP_ROLLBACK: - ret = SNMP_ERR_NOERROR; - if (!(ctx->scratch->int1 & 2)) { - /* did not exist */ - if (ctx->scratch->int1 & 1) { - /* request to load - unload */ - if (ngtype_unload(ctx->scratch->ptr2, - ctx->scratch->int2) == -1) - ret = SNMP_ERR_UNDO_FAILED; - } - } else { - /* did exist */ - if (!(ctx->scratch->int1 & 1)) { - /* request to unload - reload */ - if (ngtype_load(ctx->scratch->ptr2, - ctx->scratch->int2) == -1) - ret = SNMP_ERR_UNDO_FAILED; - } - } - free(ctx->scratch->ptr2); - return (ret); - - case SNMP_OP_COMMIT: - free(ctx->scratch->ptr2); - return (SNMP_ERR_NOERROR); - - default: - abort(); - } - - /* - * Come here for GET and COMMIT - */ - switch (which) { - - case LEAF_begemotNgTypeStatus: - value->v.integer = status; - break; - - default: - abort(); - } - return (SNMP_ERR_NOERROR); -} - -/* - * Implement the node table - */ -static int -find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) -{ - ng_ID_t id = oid->subs[sub]; - struct ng_mesg *resp; - - if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, - NULL, 0)) == NULL) - return (-1); - - *info = *(struct nodeinfo *)(void *)resp->data; - free(resp); - return (0); -} - -static int -ncmp(const void *p1, const void *p2) -{ - const struct nodeinfo *i1 = p1; - const struct nodeinfo *i2 = p2; - - if (i1->id < i2->id) - return (-1); - if (i1->id > i2->id) - return (+1); - return (0); -} - -static int -find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) -{ - u_int idxlen = oid->len - sub; - struct ng_mesg *resp; - struct namelist *list; - ng_ID_t id; - u_int i; - - if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, - NULL, 0)) == NULL) - return (-1); - list = (struct namelist *)(void *)resp->data; - - qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); - - if (idxlen == 0) { - if (list->numnames == 0) { - free(resp); - return (-1); - } - *info = list->nodeinfo[0]; - free(resp); - return (0); - } - id = oid->subs[sub]; - - for (i = 0; i < list->numnames; i++) - if (list->nodeinfo[i].id > id) { - *info = list->nodeinfo[i]; - free(resp); - return (0); - } - - free(resp); - return (-1); -} - -int -op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value, - u_int sub, u_int iidx __unused, enum snmp_op op) -{ - asn_subid_t which = value->var.subs[sub - 1]; - u_int idxlen = value->var.len - sub; - struct nodeinfo nodeinfo; - - switch (op) { - - case SNMP_OP_GETNEXT: - if (find_node_next(&value->var, sub, &nodeinfo) == -1) - return (SNMP_ERR_NOSUCHNAME); - value->var.len = sub + 1; - value->var.subs[sub] = nodeinfo.id; - break; - - case SNMP_OP_GET: - if (idxlen != 1) - return (SNMP_ERR_NOSUCHNAME); - if (find_node(&value->var, sub, &nodeinfo) == -1) - return (SNMP_ERR_NOSUCHNAME); - break; - - case SNMP_OP_SET: - if (idxlen != 1) - return (SNMP_ERR_NO_CREATION); - if (find_node(&value->var, sub, &nodeinfo) == -1) - return (SNMP_ERR_NO_CREATION); - return (SNMP_ERR_NOT_WRITEABLE); - - case SNMP_OP_ROLLBACK: - case SNMP_OP_COMMIT: - default: - abort(); - } - - /* - * Come here for GET and COMMIT - */ - switch (which) { - - case LEAF_begemotNgNodeStatus: - value->v.integer = 1; - break; - case LEAF_begemotNgNodeName: - return (string_get(value, nodeinfo.name, -1)); - case LEAF_begemotNgNodeType: - return (string_get(value, nodeinfo.type, -1)); - case LEAF_begemotNgNodeHooks: - value->v.uint32 = nodeinfo.hooks; - break; - - default: - abort(); - } - return (SNMP_ERR_NOERROR); -} - -/* - * Implement the hook table - */ -static int -find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info) -{ - struct ng_mesg *resp; - struct hooklist *list; - u_int i; - - if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, - NGM_LISTHOOKS, NULL, 0)) == NULL) - return (-1); - - list = (struct hooklist *)(void *)resp->data; - - for (i = 0; i < list->nodeinfo.hooks; i++) { - if (strlen(list->link[i].ourhook) == hooklen && - strncmp(list->link[i].ourhook, hook, hooklen) == 0) { - *info = list->link[i]; - free(resp); - return (0); - } - } - free(resp); - return (-1); -} - -static int -hook_cmp(const void *p1, const void *p2) -{ - const struct linkinfo *i1 = p1; - const struct linkinfo *i2 = p2; - - if (strlen(i1->ourhook) < strlen(i2->ourhook)) - return (-1); - if (strlen(i1->ourhook) > strlen(i2->ourhook)) - return (+1); - return (strcmp(i1->ourhook, i2->ourhook)); -} - -static int -find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo, - struct linkinfo *linkinfo) -{ - u_int idxlen = oid->len - sub; - struct namelist *list; - struct ng_mesg *resp; - struct hooklist *hooks; - struct ng_mesg *resp1; - u_int node_index; - struct asn_oid idx; - u_int i, j; - - /* - * Get and sort Node list - */ - if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, - NULL, 0)) == NULL) - return (-1); - list = (struct namelist *)(void *)resp->data; - - qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); - - /* - * If we have no index, take the first node and return the - * first hook. - */ - if (idxlen == 0) { - node_index = 0; - goto return_first_hook; - } - - /* - * Locate node - */ - for (node_index = 0; node_index < list->numnames; node_index++) - if (list->nodeinfo[node_index].id >= oid->subs[sub]) - break; - - /* - * If we have only the node part of the index take, or - * there is no node with that Id, take the first hook of that node. - */ - if (idxlen == 1 || node_index >= list->numnames || - list->nodeinfo[node_index].id > oid->subs[sub]) - goto return_first_hook; - - /* - * We had an exact match on the node id and have (at last part) - * of the hook name index. Loop through the hooks of the node - * and find the next one. - */ - if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, - NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { - free(resp); - return (-1); - } - hooks = (struct hooklist *)(void *)resp1->data; - if (hooks->nodeinfo.hooks > 0) { - qsort(hooks->link, hooks->nodeinfo.hooks, - sizeof(hooks->link[0]), hook_cmp); - for (i = 0; i < hooks->nodeinfo.hooks; i++) { - idx.len = strlen(hooks->link[i].ourhook) + 1; - idx.subs[0] = idx.len - 1; - for (j = 0; j < idx.len; j++) - idx.subs[j + 1] = hooks->link[i].ourhook[j]; - if (index_compare(oid, sub + 1, &idx) < 0) - break; - } - if (i < hooks->nodeinfo.hooks) { - *nodeinfo = hooks->nodeinfo; - *linkinfo = hooks->link[i]; - - free(resp); - free(resp1); - return (0); - } - } - - /* no hook found larger than the index on the index node - take - * first hook of next node */ - free(resp1); - node_index++; - - return_first_hook: - while (node_index < list->numnames) { - if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, - NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) - break; - hooks = (struct hooklist *)(void *)resp1->data; - if (hooks->nodeinfo.hooks > 0) { - qsort(hooks->link, hooks->nodeinfo.hooks, - sizeof(hooks->link[0]), hook_cmp); - - *nodeinfo = hooks->nodeinfo; - *linkinfo = hooks->link[0]; - - free(resp); - free(resp1); - return (0); - } - - /* if we don't have hooks, try next node */ - free(resp1); - node_index++; - } - - free(resp); - return (-1); -} - -int -op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value, - u_int sub, u_int iidx, enum snmp_op op) -{ - asn_subid_t which = value->var.subs[sub - 1]; - struct linkinfo linkinfo; - struct nodeinfo nodeinfo; - u_int32_t lid; - u_char *hook; - size_t hooklen; - u_int i; - - switch (op) { - - case SNMP_OP_GETNEXT: - if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1) - return (SNMP_ERR_NOSUCHNAME); - - value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook); - value->var.subs[sub] = nodeinfo.id; - value->var.subs[sub + 1] = strlen(linkinfo.ourhook); - for (i = 0; i < strlen(linkinfo.ourhook); i++) - value->var.subs[sub + i + 2] = - linkinfo.ourhook[i]; - break; - - case SNMP_OP_GET: - if (index_decode(&value->var, sub, iidx, &lid, - &hook, &hooklen)) - return (SNMP_ERR_NOSUCHNAME); - if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { - free(hook); - return (SNMP_ERR_NOSUCHNAME); - } - free(hook); - break; - - case SNMP_OP_SET: - if (index_decode(&value->var, sub, iidx, &lid, - &hook, &hooklen)) - return (SNMP_ERR_NO_CREATION); - if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { - free(hook); - return (SNMP_ERR_NO_CREATION); - } - free(hook); - return (SNMP_ERR_NOT_WRITEABLE); - - case SNMP_OP_ROLLBACK: - case SNMP_OP_COMMIT: - default: - abort(); - - } - - switch (which) { - - case LEAF_begemotNgHookStatus: - value->v.integer = 1; - break; - case LEAF_begemotNgHookPeerNodeId: - value->v.uint32 = linkinfo.nodeinfo.id; - break; - case LEAF_begemotNgHookPeerHook: - return (string_get(value, linkinfo.peerhook, -1)); - case LEAF_begemotNgHookPeerType: - return (string_get(value, linkinfo.nodeinfo.type, -1)); - default: - abort(); - } - return (SNMP_ERR_NOERROR); -} |