Path: blob/main/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c
105686 views
/*1* Copyright (c) 2001-20032* Fraunhofer Institute for Open Communication Systems (FhG Fokus).3* All rights reserved.4*5* Author: Harti Brandt <[email protected]>6*7* Redistribution of this software and documentation and use in source and8* binary forms, with or without modification, are permitted provided that9* the following conditions are met:10*11* 1. Redistributions of source code or documentation must retain the above12* copyright notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS18* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,19* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND20* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL21* FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,22* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT23* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,24* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF25* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING26* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,27* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.28*29* Netgraph interface for SNMPd.30*/31#include <sys/types.h>32#include <sys/param.h>33#include <sys/linker.h>34#include <sys/socket.h>35#include <sys/syslog.h>36#include <sys/queue.h>37#include <sys/sysctl.h>38#include <stdio.h>39#include <stdlib.h>40#include <errno.h>41#include <unistd.h>42#include <string.h>43#include <netgraph.h>44#include <bsnmp/snmpmod.h>45#include "snmp_netgraph.h"46#include "netgraph_tree.h"47#include "netgraph_oid.h"4849/* maximum message size */50#define RESBUFSIZ 200005152/* default node name */53#define NODENAME "NgSnmpd"5455/* my node Id */56ng_ID_t snmp_node;57u_char *snmp_nodename;5859/* the Object Resource registration index */60static u_int reg_index;61static const struct asn_oid oid_begemotNg = OIDX_begemotNg;6263/* configuration */64/* this must be smaller than int32_t because the functions in libnetgraph65* falsely return an int */66static size_t resbufsiz = RESBUFSIZ;67static u_int timeout = 1000;68static u_int debug_level;6970/* number of microseconds per clock tick */71static struct clockinfo clockinfo;7273/* Csock buffers. Communication on the csock is asynchronuous. This means74* if we wait for a specific response, we may get other messages. Put these75* into a queue and execute them when we are idle. */76struct csock_buf {77STAILQ_ENTRY(csock_buf) link;78struct ng_mesg *mesg;79char path[NG_PATHSIZ];80};81static STAILQ_HEAD(, csock_buf) csock_bufs =82STAILQ_HEAD_INITIALIZER(csock_bufs);8384/*85* We dispatch unsolicieted messages by node cookies and ids.86* So we must keep a list of hook names and dispatch functions.87*/88struct msgreg {89u_int32_t cookie;90ng_ID_t id;91ng_cookie_f *func;92void *arg;93const struct lmodule *mod;94SLIST_ENTRY(msgreg) link;95};96static SLIST_HEAD(, msgreg) msgreg_list =97SLIST_HEAD_INITIALIZER(msgreg_list);9899/*100* Data messages are dispatched by hook names.101*/102struct datareg {103char hook[NG_HOOKSIZ];104ng_hook_f *func;105void *arg;106const struct lmodule *mod;107SLIST_ENTRY(datareg) link;108};109static SLIST_HEAD(, datareg) datareg_list =110SLIST_HEAD_INITIALIZER(datareg_list);111112/* the netgraph sockets */113static int csock, dsock;114static void *csock_fd, *dsock_fd;115116/* our module handle */117static struct lmodule *module;118119/* statistics */120static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1];121122/* netgraph type list */123struct ngtype {124char name[NG_TYPESIZ];125struct asn_oid index;126TAILQ_ENTRY(ngtype) link;127};128TAILQ_HEAD(ngtype_list, ngtype);129130static struct ngtype_list ngtype_list;131static uint64_t ngtype_tick;132133134/*135* Register a function to receive unsolicited messages136*/137void *138ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id,139ng_cookie_f *func, void *arg)140{141struct msgreg *d;142143if ((d = malloc(sizeof(*d))) == NULL)144return (NULL);145146d->cookie = cookie;147d->id = id;148d->func = func;149d->arg = arg;150d->mod = mod;151152SLIST_INSERT_HEAD(&msgreg_list, d, link);153154return (d);155}156157/*158* Remove a registration.159*/160void161ng_unregister_cookie(void *dd)162{163struct msgreg *d = dd;164165SLIST_REMOVE(&msgreg_list, d, msgreg, link);166free(d);167}168169/*170* Register a function for hook data.171*/172void *173ng_register_hook(const struct lmodule *mod, const char *hook,174ng_hook_f *func, void *arg)175{176struct datareg *d;177178if ((d = malloc(sizeof(*d))) == NULL)179return (NULL);180181strcpy(d->hook, hook);182d->func = func;183d->arg = arg;184d->mod = mod;185186SLIST_INSERT_HEAD(&datareg_list, d, link);187188return (d);189}190191/*192* Unregister a hook function193*/194void195ng_unregister_hook(void *dd)196{197struct datareg *d = dd;198199SLIST_REMOVE(&datareg_list, d, datareg, link);200free(d);201}202203/*204* Unregister all hooks and cookies for that module. Note: doesn't disconnect205* any hooks!206*/207void208ng_unregister_module(const struct lmodule *mod)209{210struct msgreg *m, *m1;211struct datareg *d, *d1;212213m = SLIST_FIRST(&msgreg_list);214while (m != NULL) {215m1 = SLIST_NEXT(m, link);216if (m->mod == mod) {217SLIST_REMOVE(&msgreg_list, m, msgreg, link);218free(m);219}220m = m1;221}222223d = SLIST_FIRST(&datareg_list);224while (d != NULL) {225d1 = SLIST_NEXT(d, link);226if (d->mod == mod) {227SLIST_REMOVE(&datareg_list, d, datareg, link);228free(d);229}230d = d1;231}232}233234/*235* Dispatch a message to the correct module and delete it. More than one236* module can get a message.237*/238static void239csock_handle(struct ng_mesg *mesg, const char *path)240{241struct msgreg *d, *d1;242u_int id;243int len;244245if (sscanf(path, "[%x]:%n", &id, &len) != 1 ||246(u_int)len != strlen(path)) {247syslog(LOG_ERR, "cannot parse message path '%s'", path);248id = 0;249}250251d = SLIST_FIRST(&msgreg_list);252while (d != NULL) {253d1 = SLIST_NEXT(d, link);254if (d->cookie == mesg->header.typecookie &&255(d->id == 0 || d->id == id || id == 0))256(*d->func)(mesg, path, id, d->arg);257d = d1;258}259free(mesg);260}261262/*263* Input from the control socket.264*/265static struct ng_mesg *266csock_read(char *path)267{268struct ng_mesg *mesg;269int ret, err;270271if ((mesg = malloc(resbufsiz + 1)) == NULL) {272stats[LEAF_begemotNgNoMems]++;273syslog(LOG_CRIT, "out of memory");274errno = ENOMEM;275return (NULL);276}277if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) {278err = errno;279free(mesg);280if (errno == EWOULDBLOCK) {281errno = err;282return (NULL);283}284stats[LEAF_begemotNgMsgReadErrs]++;285syslog(LOG_WARNING, "read from csock: %m");286errno = err;287return (NULL);288}289if (ret == 0) {290syslog(LOG_DEBUG, "node closed -- exiting");291exit(0);292}293if ((size_t)ret > resbufsiz) {294stats[LEAF_begemotNgTooLargeMsgs]++;295syslog(LOG_WARNING, "ng message too large");296free(mesg);297errno = EFBIG;298return (NULL);299}300return (mesg);301}302303static void304csock_input(int fd __unused, void *udata __unused)305{306struct ng_mesg *mesg;307char path[NG_PATHSIZ];308309if ((mesg = csock_read(path)) == NULL)310return;311312csock_handle(mesg, path);313}314315/*316* Write a message to a node.317*/318int319ng_output(const char *path, u_int cookie, u_int opcode,320const void *arg, size_t arglen)321{322return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen));323}324int325ng_output_node(const char *node, u_int cookie, u_int opcode,326const void *arg, size_t arglen)327{328char path[NG_PATHSIZ];329330sprintf(path, "%s:", node);331return (ng_output(path, cookie, opcode, arg, arglen));332}333int334ng_output_id(ng_ID_t node, u_int cookie, u_int opcode,335const void *arg, size_t arglen)336{337char path[NG_PATHSIZ];338339sprintf(path, "[%x]:", node);340return (ng_output(path, cookie, opcode, arg, arglen));341}342343344345/*346* Execute a synchronuous dialog with the csock. All message we receive, that347* do not match our request, are queue until the next call to the IDLE function.348*/349struct ng_mesg *350ng_dialog(const char *path, u_int cookie, u_int opcode,351const void *arg, size_t arglen)352{353int token, err;354struct ng_mesg *mesg;355char rpath[NG_PATHSIZ];356struct csock_buf *b;357struct timeval end, tv;358359if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0)360return (NULL);361362if (csock_fd)363fd_suspend(csock_fd);364365gettimeofday(&end, NULL);366tv.tv_sec = timeout / 1000;367tv.tv_usec = (timeout % 1000) * 1000;368timeradd(&end, &tv, &end);369for (;;) {370mesg = NULL;371gettimeofday(&tv, NULL);372if (timercmp(&tv, &end, >=)) {373block:374syslog(LOG_WARNING, "no response for request %u/%u",375cookie, opcode);376errno = EWOULDBLOCK;377break;378}379timersub(&end, &tv, &tv);380if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick)381goto block;382383if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)384syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m");385if ((mesg = csock_read(rpath)) == NULL) {386if (errno == EWOULDBLOCK)387continue;388break;389}390if (mesg->header.token == (u_int)token)391break;392if ((b = malloc(sizeof(*b))) == NULL) {393stats[LEAF_begemotNgNoMems]++;394syslog(LOG_ERR, "out of memory");395free(mesg);396continue;397}398b->mesg = mesg;399strcpy(b->path, rpath);400STAILQ_INSERT_TAIL(&csock_bufs, b, link);401}402403tv.tv_sec = 0;404tv.tv_usec = 0;405if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)406syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m");407408if (csock_fd) {409err = errno;410fd_resume(csock_fd);411errno = err;412}413414return (mesg);415}416struct ng_mesg *417ng_dialog_node(const char *node, u_int cookie, u_int opcode,418const void *arg, size_t arglen)419{420char path[NG_PATHSIZ];421422sprintf(path, "%s:", node);423return (ng_dialog(path, cookie, opcode, arg, arglen));424}425struct ng_mesg *426ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,427const void *arg, size_t arglen)428{429char path[NG_PATHSIZ];430431sprintf(path, "[%x]:", id);432return (ng_dialog(path, cookie, opcode, arg, arglen));433}434435436/*437* Send a data message to a given hook.438*/439int440ng_send_data(const char *hook, const void *sndbuf, size_t sndlen)441{442return (NgSendData(dsock, hook, sndbuf, sndlen));443}444445/*446* Input from a data socket. Dispatch to the function for that hook.447*/448static void449dsock_input(int fd __unused, void *udata __unused)450{451u_char *resbuf, embuf[100];452ssize_t len;453char hook[NG_HOOKSIZ];454struct datareg *d, *d1;455456if ((resbuf = malloc(resbufsiz + 1)) == NULL) {457stats[LEAF_begemotNgNoMems]++;458syslog(LOG_CRIT, "out of memory");459(void)NgRecvData(fd, embuf, sizeof(embuf), hook);460errno = ENOMEM;461return;462}463if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) {464stats[LEAF_begemotNgDataReadErrs]++;465syslog(LOG_ERR, "reading message: %m");466free(resbuf);467return;468}469if (len == 0) {470free(resbuf);471return;472}473if ((size_t)len == resbufsiz + 1) {474stats[LEAF_begemotNgTooLargeDatas]++;475syslog(LOG_WARNING, "message too long");476free(resbuf);477return;478}479480/*481* Dispatch message. Maybe dispatched to more than one function.482*/483d = SLIST_FIRST(&datareg_list);484while (d != NULL) {485d1 = SLIST_NEXT(d, link);486if (strcmp(hook, d->hook) == 0)487(*d->func)(hook, resbuf, len, d->arg);488d = d1;489}490491free(resbuf);492}493494/*495* The SNMP daemon is about to wait for an event. Look whether we have496* netgraph messages waiting. If yes, drain the queue.497*/498static void499ng_idle(void)500{501struct csock_buf *b;502503/* execute waiting csock_bufs */504while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) {505STAILQ_REMOVE_HEAD(&csock_bufs, link);506csock_handle(b->mesg, b->path);507free(b);508}509}510511/*512* Called when the module is loaded. Returning a non-zero value means,513* rejecting the initialisation.514*515* We make the netgraph socket.516*/517static int518ng_init(struct lmodule *mod, int argc, char *argv[])519{520int name[2];521size_t len;522523module = mod;524525if (argc == 0) {526if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL)527return (ENOMEM);528strcpy(snmp_nodename, NODENAME);529} else {530if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL)531return (ENOMEM);532strlcpy(snmp_nodename, argv[0], NG_NODESIZ);533}534535/* fetch clockinfo (for the number of microseconds per tick) */536name[0] = CTL_KERN;537name[1] = KERN_CLOCKRATE;538len = sizeof(clockinfo);539if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1)540return (errno);541542TAILQ_INIT(&ngtype_list);543544return (0);545}546547/*548* Get the node Id/name/type of a node.549*/550ng_ID_t551ng_node_id(const char *path)552{553struct ng_mesg *resp;554ng_ID_t id;555556if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO,557NULL, 0)) == NULL)558return (0);559id = ((struct nodeinfo *)(void *)resp->data)->id;560free(resp);561return (id);562}563ng_ID_t564ng_node_id_node(const char *node)565{566struct ng_mesg *resp;567ng_ID_t id;568569if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO,570NULL, 0)) == NULL)571return (0);572id = ((struct nodeinfo *)(void *)resp->data)->id;573free(resp);574return (id);575}576ng_ID_t577ng_node_name(ng_ID_t id, char *name)578{579struct ng_mesg *resp;580581if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,582NULL, 0)) == NULL)583return (0);584strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name);585free(resp);586return (id);587588}589ng_ID_t590ng_node_type(ng_ID_t id, char *type)591{592struct ng_mesg *resp;593594if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,595NULL, 0)) == NULL)596return (0);597strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type);598free(resp);599return (id);600}601602/*603* Connect our node to some other node604*/605int606ng_connect_node(const char *node, const char *ourhook, const char *peerhook)607{608struct ngm_connect conn;609610snprintf(conn.path, NG_PATHSIZ, "%s:", node);611strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);612strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);613return (NgSendMsg(csock, ".:",614NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));615}616int617ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook)618{619struct ngm_connect conn;620621snprintf(conn.path, NG_PATHSIZ, "[%x]:", id);622strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);623strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);624return (NgSendMsg(csock, ".:",625NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));626}627628int629ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,630const char *peerhook)631{632struct ngm_connect conn;633char path[NG_PATHSIZ];634635snprintf(path, NG_PATHSIZ, "[%x]:", id);636637snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);638strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);639strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);640return (NgSendMsg(csock, path,641NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));642}643644int645ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,646const char *peerhook)647{648struct ngm_connect conn;649char path[NG_PATHSIZ];650ng_ID_t tee;651652if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0)653return (-1);654655snprintf(path, NG_PATHSIZ, "[%x]:", tee);656657snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);658strlcpy(conn.ourhook, "right", NG_HOOKSIZ);659strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);660return (NgSendMsg(csock, path,661NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));662}663664/*665* Ensure that a node of type 'type' is connected to 'hook' of 'node'666* and return its node id. tee nodes between node and the target node667* are skipped. If the type is wrong, or the hook is a dead-end return 0.668* If type is NULL, it is not checked.669*/670static ng_ID_t671ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook,672int skip_tee)673{674struct ng_mesg *resp;675struct hooklist *hooklist;676u_int i;677678if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,679NULL, 0)) == NULL) {680syslog(LOG_ERR, "get hook list: %m");681exit(1);682}683hooklist = (struct hooklist *)(void *)resp->data;684685for (i = 0; i < hooklist->nodeinfo.hooks; i++)686if (strcmp(hooklist->link[i].ourhook, hook) == 0)687break;688689if (i == hooklist->nodeinfo.hooks) {690free(resp);691return (0);692}693694node = hooklist->link[i].nodeinfo.id;695696if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {697if (strcmp(hooklist->link[i].peerhook, "left") == 0)698node = ng_next_node_id(node, type, "right");699else if (strcmp(hooklist->link[i].peerhook, "right") == 0)700node = ng_next_node_id(node, type, "left");701else if (type != NULL &&702strcmp(hooklist->link[i].nodeinfo.type, type) != 0)703node = 0;704705} else if (type != NULL &&706strcmp(hooklist->link[i].nodeinfo.type, type) != 0)707node = 0;708709free(resp);710711return (node);712}713714/*715* Ensure that a node of type 'type' is connected to 'hook' of 'node'716* and return its node id. tee nodes between node and the target node717* are skipped. If the type is wrong, or the hook is a dead-end return 0.718* If type is NULL, it is not checked.719*/720ng_ID_t721ng_next_node_id(ng_ID_t node, const char *type, const char *hook)722{723return (ng_next_node_id_internal(node, type, hook, 1));724}725726ng_ID_t727ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type,728const char *hook, const char *peerhook)729{730char path[NG_PATHSIZ];731struct ngm_mkpeer mkpeer;732struct ngm_name name;733734strlcpy(mkpeer.type, type, NG_TYPESIZ);735strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ);736strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ);737738sprintf(path, "[%x]:", id);739if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,740&mkpeer, sizeof(mkpeer)) == -1)741return (0);742743if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0)744return (0);745746if (nodename != NULL) {747strcpy(name.name, nodename);748sprintf(path, "[%x]:", id);749if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME,750&name, sizeof(name)) == -1)751return (0);752}753return (id);754}755756/*757* SHutdown node758*/759int760ng_shutdown_id(ng_ID_t id)761{762char path[NG_PATHSIZ];763764snprintf(path, NG_PATHSIZ, "[%x]:", id);765return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,766NGM_SHUTDOWN, NULL, 0));767}768769/*770* Disconnect one of our hooks771*/772int773ng_rmhook(const char *ourhook)774{775struct ngm_rmhook rmhook;776777strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ);778return (NgSendMsg(csock, ".:",779NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));780}781782/*783* Disconnect a hook of a node784*/785int786ng_rmhook_id(ng_ID_t id, const char *hook)787{788struct ngm_rmhook rmhook;789char path[NG_PATHSIZ];790791strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ);792snprintf(path, NG_PATHSIZ, "[%x]:", id);793return (NgSendMsg(csock, path,794NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));795}796797/*798* Disconnect a hook and shutdown all tee nodes that were connected to that799* hook.800*/801int802ng_rmhook_tee_id(ng_ID_t node, const char *hook)803{804struct ng_mesg *resp;805struct hooklist *hooklist;806u_int i;807int first = 1;808ng_ID_t next_node;809const char *next_hook;810811again:812/* if we have just shutdown a tee node, which had no other hooks813* connected, the node id may already be wrong here. */814if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,815NULL, 0)) == NULL)816return (0);817818hooklist = (struct hooklist *)(void *)resp->data;819820for (i = 0; i < hooklist->nodeinfo.hooks; i++)821if (strcmp(hooklist->link[i].ourhook, hook) == 0)822break;823824if (i == hooklist->nodeinfo.hooks) {825free(resp);826return (0);827}828829next_node = 0;830next_hook = NULL;831if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {832if (strcmp(hooklist->link[i].peerhook, "left") == 0) {833next_node = hooklist->link[i].nodeinfo.id;834next_hook = "right";835} else if (strcmp(hooklist->link[i].peerhook, "right") == 0) {836next_node = hooklist->link[i].nodeinfo.id;837next_hook = "left";838}839}840free(resp);841842if (first) {843ng_rmhook_id(node, hook);844first = 0;845} else {846ng_shutdown_id(node);847}848if ((node = next_node) == 0)849return (0);850hook = next_hook;851852goto again;853}854855/*856* Get the peer hook of a hook on a given node. Skip any tee nodes in between857*/858int859ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook)860{861struct ng_mesg *resp;862struct hooklist *hooklist;863u_int i;864int ret;865866if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,867NULL, 0)) == NULL) {868syslog(LOG_ERR, "get hook list: %m");869exit(1);870}871hooklist = (struct hooklist *)(void *)resp->data;872873for (i = 0; i < hooklist->nodeinfo.hooks; i++)874if (strcmp(hooklist->link[i].ourhook, hook) == 0)875break;876877if (i == hooklist->nodeinfo.hooks) {878free(resp);879return (-1);880}881882node = hooklist->link[i].nodeinfo.id;883884ret = 0;885if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {886if (strcmp(hooklist->link[i].peerhook, "left") == 0)887ret = ng_peer_hook_id(node, "right", peerhook);888else if (strcmp(hooklist->link[i].peerhook, "right") == 0)889ret = ng_peer_hook_id(node, "left", peerhook);890else891strcpy(peerhook, hooklist->link[i].peerhook);892893} else894strcpy(peerhook, hooklist->link[i].peerhook);895896free(resp);897898return (ret);899}900901902/*903* Now the module is started. Select on the sockets, so that we can get904* unsolicited input.905*/906static void907ng_start(void)908{909if (snmp_node == 0) {910if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {911syslog(LOG_ERR, "NgMkSockNode: %m");912exit(1);913}914snmp_node = ng_node_id(".:");915}916917if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) {918syslog(LOG_ERR, "fd_select failed on csock: %m");919return;920}921if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) {922syslog(LOG_ERR, "fd_select failed on dsock: %m");923return;924}925926reg_index = or_register(&oid_begemotNg,927"The MIB for the NetGraph access module for SNMP.", module);928}929930/*931* Called, when the module is to be unloaded after it was successfully loaded932*/933static int934ng_fini(void)935{936struct ngtype *t;937938while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {939TAILQ_REMOVE(&ngtype_list, t, link);940free(t);941}942943if (csock_fd != NULL)944fd_deselect(csock_fd);945(void)close(csock);946947if (dsock_fd != NULL)948fd_deselect(dsock_fd);949(void)close(dsock);950951free(snmp_nodename);952953or_unregister(reg_index);954955return (0);956}957958const struct snmp_module config = {959"This module implements access to the netgraph sub-system",960ng_init,961ng_fini,962ng_idle,963NULL,964NULL,965ng_start,966NULL,967netgraph_ctree,968netgraph_CTREE_SIZE,969NULL970};971972int973op_ng_config(struct snmp_context *ctx, struct snmp_value *value,974u_int sub, u_int iidx __unused, enum snmp_op op)975{976asn_subid_t which = value->var.subs[sub - 1];977int ret;978979switch (op) {980981case SNMP_OP_GETNEXT:982abort();983984case SNMP_OP_GET:985/*986* Come here for GET, GETNEXT and COMMIT987*/988switch (which) {989990case LEAF_begemotNgControlNodeName:991return (string_get(value, snmp_nodename, -1));992993case LEAF_begemotNgResBufSiz:994value->v.integer = resbufsiz;995break;996997case LEAF_begemotNgTimeout:998value->v.integer = timeout;999break;10001001case LEAF_begemotNgDebugLevel:1002value->v.uint32 = debug_level;1003break;10041005default:1006abort();1007}1008return (SNMP_ERR_NOERROR);10091010case SNMP_OP_SET:1011switch (which) {10121013case LEAF_begemotNgControlNodeName:1014/* only at initialisation */1015if (community != COMM_INITIALIZE)1016return (SNMP_ERR_NOT_WRITEABLE);10171018if (snmp_node != 0)1019return (SNMP_ERR_NOT_WRITEABLE);10201021if ((ret = string_save(value, ctx, -1, &snmp_nodename))1022!= SNMP_ERR_NOERROR)1023return (ret);10241025if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {1026syslog(LOG_ERR, "NgMkSockNode: %m");1027string_rollback(ctx, &snmp_nodename);1028return (SNMP_ERR_GENERR);1029}1030snmp_node = ng_node_id(".:");10311032return (SNMP_ERR_NOERROR);10331034case LEAF_begemotNgResBufSiz:1035ctx->scratch->int1 = resbufsiz;1036if (value->v.integer < 1024 ||1037value->v.integer > 0x10000)1038return (SNMP_ERR_WRONG_VALUE);1039resbufsiz = value->v.integer;1040return (SNMP_ERR_NOERROR);10411042case LEAF_begemotNgTimeout:1043ctx->scratch->int1 = timeout;1044if (value->v.integer < 10 ||1045value->v.integer > 10000)1046return (SNMP_ERR_WRONG_VALUE);1047timeout = value->v.integer;1048return (SNMP_ERR_NOERROR);10491050case LEAF_begemotNgDebugLevel:1051ctx->scratch->int1 = debug_level;1052debug_level = value->v.uint32;1053NgSetDebug(debug_level);1054return (SNMP_ERR_NOERROR);1055}1056abort();10571058case SNMP_OP_ROLLBACK:1059switch (which) {10601061case LEAF_begemotNgControlNodeName:1062string_rollback(ctx, &snmp_nodename);1063close(csock);1064close(dsock);1065snmp_node = 0;1066return (SNMP_ERR_NOERROR);10671068case LEAF_begemotNgResBufSiz:1069resbufsiz = ctx->scratch->int1;1070return (SNMP_ERR_NOERROR);10711072case LEAF_begemotNgTimeout:1073timeout = ctx->scratch->int1;1074return (SNMP_ERR_NOERROR);10751076case LEAF_begemotNgDebugLevel:1077debug_level = ctx->scratch->int1;1078NgSetDebug(debug_level);1079return (SNMP_ERR_NOERROR);1080}1081abort();10821083case SNMP_OP_COMMIT:1084switch (which) {10851086case LEAF_begemotNgControlNodeName:1087string_commit(ctx);1088return (SNMP_ERR_NOERROR);10891090case LEAF_begemotNgResBufSiz:1091case LEAF_begemotNgTimeout:1092case LEAF_begemotNgDebugLevel:1093return (SNMP_ERR_NOERROR);1094}1095abort();1096}1097abort();1098}10991100int1101op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value,1102u_int sub, u_int iidx __unused, enum snmp_op op)1103{1104switch (op) {11051106case SNMP_OP_GETNEXT:1107abort();11081109case SNMP_OP_GET:1110value->v.uint32 = stats[value->var.subs[sub - 1] - 1];1111return (SNMP_ERR_NOERROR);11121113case SNMP_OP_SET:1114return (SNMP_ERR_NOT_WRITEABLE);11151116case SNMP_OP_ROLLBACK:1117case SNMP_OP_COMMIT:1118abort();1119}1120abort();1121}11221123/*1124* Netgraph type table1125*/1126static int1127fetch_types(void)1128{1129struct ngtype *t;1130struct typelist *typelist;1131struct ng_mesg *resp;1132u_int u, i;11331134if (this_tick <= ngtype_tick)1135return (0);11361137while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {1138TAILQ_REMOVE(&ngtype_list, t, link);1139free(t);1140}11411142if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE,1143NGM_LISTTYPES, NULL, 0)) == NULL)1144return (SNMP_ERR_GENERR);1145typelist = (struct typelist *)(void *)resp->data;11461147for (u = 0; u < typelist->numtypes; u++) {1148if ((t = malloc(sizeof(*t))) == NULL) {1149free(resp);1150return (SNMP_ERR_GENERR);1151}1152strcpy(t->name, typelist->typeinfo[u].type_name);1153t->index.subs[0] = strlen(t->name);1154t->index.len = t->index.subs[0] + 1;1155for (i = 0; i < t->index.subs[0]; i++)1156t->index.subs[i + 1] = t->name[i];11571158INSERT_OBJECT_OID(t, &ngtype_list);1159}11601161ngtype_tick = this_tick;11621163free(resp);1164return (0);1165}11661167/*1168* Try to load the netgraph type with the given name. We assume, that1169* type 'type' is implemented in the kernel module 'ng_type'.1170*/1171static int1172ngtype_load(const u_char *name, size_t namelen)1173{1174char *mod;1175int ret;11761177if ((mod = malloc(namelen + 4)) == NULL)1178return (-1);1179strcpy(mod, "ng_");1180strncpy(mod + 3, name, namelen);1181mod[namelen + 3] = '\0';11821183ret = kldload(mod);1184free(mod);1185return (ret);1186}11871188/*1189* Unload a netgraph type.1190*/1191static int1192ngtype_unload(const u_char *name, size_t namelen)1193{1194char *mod;1195int id;11961197if ((mod = malloc(namelen + 4)) == NULL)1198return (-1);1199strcpy(mod, "ng_");1200strncpy(mod + 3, name, namelen);1201mod[namelen + 3] = '\0';12021203if ((id = kldfind(mod)) == -1) {1204free(mod);1205return (-1);1206}1207free(mod);1208return (kldunload(id));1209}12101211int1212op_ng_type(struct snmp_context *ctx, struct snmp_value *value,1213u_int sub, u_int iidx, enum snmp_op op)1214{1215asn_subid_t which = value->var.subs[sub - 1];1216struct ngtype *t;1217u_char *name;1218size_t namelen;1219int status = 1;1220int ret;12211222switch (op) {12231224case SNMP_OP_GETNEXT:1225if ((ret = fetch_types()) != 0)1226return (ret);1227if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)1228return (SNMP_ERR_NOSUCHNAME);1229index_append(&value->var, sub, &t->index);1230break;12311232case SNMP_OP_GET:1233if ((ret = fetch_types()) != 0)1234return (ret);1235if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)1236return (SNMP_ERR_NOSUCHNAME);1237break;12381239case SNMP_OP_SET:1240if (index_decode(&value->var, sub, iidx, &name, &namelen))1241return (SNMP_ERR_NO_CREATION);1242if (namelen == 0 || namelen >= NG_TYPESIZ) {1243free(name);1244return (SNMP_ERR_NO_CREATION);1245}1246if ((ret = fetch_types()) != 0) {1247free(name);1248return (ret);1249}1250t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub);12511252if (which != LEAF_begemotNgTypeStatus) {1253free(name);1254if (t != NULL)1255return (SNMP_ERR_NOT_WRITEABLE);1256return (SNMP_ERR_NO_CREATION);1257}1258if (!TRUTH_OK(value->v.integer)) {1259free(name);1260return (SNMP_ERR_WRONG_VALUE);1261}1262ctx->scratch->int1 = TRUTH_GET(value->v.integer);1263ctx->scratch->int1 |= (t != NULL) << 1;1264ctx->scratch->ptr2 = name;1265ctx->scratch->int2 = namelen;12661267if (t == NULL) {1268/* type not loaded */1269if (ctx->scratch->int1 & 1) {1270/* request to load */1271if (ngtype_load(name, namelen) == -1) {1272free(name);1273if (errno == ENOENT)1274return (SNMP_ERR_INCONS_NAME);1275else1276return (SNMP_ERR_GENERR);1277}1278}1279} else {1280/* is type loaded */1281if (!(ctx->scratch->int1 & 1)) {1282/* request to unload */1283if (ngtype_unload(name, namelen) == -1) {1284free(name);1285return (SNMP_ERR_GENERR);1286}1287}1288}1289return (SNMP_ERR_NOERROR);12901291case SNMP_OP_ROLLBACK:1292ret = SNMP_ERR_NOERROR;1293if (!(ctx->scratch->int1 & 2)) {1294/* did not exist */1295if (ctx->scratch->int1 & 1) {1296/* request to load - unload */1297if (ngtype_unload(ctx->scratch->ptr2,1298ctx->scratch->int2) == -1)1299ret = SNMP_ERR_UNDO_FAILED;1300}1301} else {1302/* did exist */1303if (!(ctx->scratch->int1 & 1)) {1304/* request to unload - reload */1305if (ngtype_load(ctx->scratch->ptr2,1306ctx->scratch->int2) == -1)1307ret = SNMP_ERR_UNDO_FAILED;1308}1309}1310free(ctx->scratch->ptr2);1311return (ret);13121313case SNMP_OP_COMMIT:1314free(ctx->scratch->ptr2);1315return (SNMP_ERR_NOERROR);13161317default:1318abort();1319}13201321/*1322* Come here for GET and COMMIT1323*/1324switch (which) {13251326case LEAF_begemotNgTypeStatus:1327value->v.integer = status;1328break;13291330default:1331abort();1332}1333return (SNMP_ERR_NOERROR);1334}13351336/*1337* Implement the node table1338*/1339static int1340find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)1341{1342ng_ID_t id = oid->subs[sub];1343struct ng_mesg *resp;13441345if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,1346NULL, 0)) == NULL)1347return (-1);13481349*info = *(struct nodeinfo *)(void *)resp->data;1350free(resp);1351return (0);1352}13531354static int1355ncmp(const void *p1, const void *p2)1356{1357const struct nodeinfo *i1 = p1;1358const struct nodeinfo *i2 = p2;13591360if (i1->id < i2->id)1361return (-1);1362if (i1->id > i2->id)1363return (+1);1364return (0);1365}13661367static int1368find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)1369{1370u_int idxlen = oid->len - sub;1371struct ng_mesg *resp;1372struct namelist *list;1373ng_ID_t id;1374u_int i;13751376if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,1377NULL, 0)) == NULL)1378return (-1);1379list = (struct namelist *)(void *)resp->data;13801381qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);13821383if (idxlen == 0) {1384if (list->numnames == 0) {1385free(resp);1386return (-1);1387}1388*info = list->nodeinfo[0];1389free(resp);1390return (0);1391}1392id = oid->subs[sub];13931394for (i = 0; i < list->numnames; i++)1395if (list->nodeinfo[i].id > id) {1396*info = list->nodeinfo[i];1397free(resp);1398return (0);1399}14001401free(resp);1402return (-1);1403}14041405int1406op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value,1407u_int sub, u_int iidx __unused, enum snmp_op op)1408{1409asn_subid_t which = value->var.subs[sub - 1];1410u_int idxlen = value->var.len - sub;1411struct nodeinfo nodeinfo;14121413switch (op) {14141415case SNMP_OP_GETNEXT:1416if (find_node_next(&value->var, sub, &nodeinfo) == -1)1417return (SNMP_ERR_NOSUCHNAME);1418value->var.len = sub + 1;1419value->var.subs[sub] = nodeinfo.id;1420break;14211422case SNMP_OP_GET:1423if (idxlen != 1)1424return (SNMP_ERR_NOSUCHNAME);1425if (find_node(&value->var, sub, &nodeinfo) == -1)1426return (SNMP_ERR_NOSUCHNAME);1427break;14281429case SNMP_OP_SET:1430if (idxlen != 1)1431return (SNMP_ERR_NO_CREATION);1432if (find_node(&value->var, sub, &nodeinfo) == -1)1433return (SNMP_ERR_NO_CREATION);1434return (SNMP_ERR_NOT_WRITEABLE);14351436case SNMP_OP_ROLLBACK:1437case SNMP_OP_COMMIT:1438default:1439abort();1440}14411442/*1443* Come here for GET and COMMIT1444*/1445switch (which) {14461447case LEAF_begemotNgNodeStatus:1448value->v.integer = 1;1449break;1450case LEAF_begemotNgNodeName:1451return (string_get(value, nodeinfo.name, -1));1452case LEAF_begemotNgNodeType:1453return (string_get(value, nodeinfo.type, -1));1454case LEAF_begemotNgNodeHooks:1455value->v.uint32 = nodeinfo.hooks;1456break;14571458default:1459abort();1460}1461return (SNMP_ERR_NOERROR);1462}14631464/*1465* Implement the hook table1466*/1467static int1468find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info)1469{1470struct ng_mesg *resp;1471struct hooklist *list;1472u_int i;14731474if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE,1475NGM_LISTHOOKS, NULL, 0)) == NULL)1476return (-1);14771478list = (struct hooklist *)(void *)resp->data;14791480for (i = 0; i < list->nodeinfo.hooks; i++) {1481if (strlen(list->link[i].ourhook) == hooklen &&1482strncmp(list->link[i].ourhook, hook, hooklen) == 0) {1483*info = list->link[i];1484free(resp);1485return (0);1486}1487}1488free(resp);1489return (-1);1490}14911492static int1493hook_cmp(const void *p1, const void *p2)1494{1495const struct linkinfo *i1 = p1;1496const struct linkinfo *i2 = p2;14971498if (strlen(i1->ourhook) < strlen(i2->ourhook))1499return (-1);1500if (strlen(i1->ourhook) > strlen(i2->ourhook))1501return (+1);1502return (strcmp(i1->ourhook, i2->ourhook));1503}15041505static int1506find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo,1507struct linkinfo *linkinfo)1508{1509u_int idxlen = oid->len - sub;1510struct namelist *list;1511struct ng_mesg *resp;1512struct hooklist *hooks;1513struct ng_mesg *resp1;1514u_int node_index;1515struct asn_oid idx;1516u_int i, j;15171518/*1519* Get and sort Node list1520*/1521if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,1522NULL, 0)) == NULL)1523return (-1);1524list = (struct namelist *)(void *)resp->data;15251526qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);15271528/*1529* If we have no index, take the first node and return the1530* first hook.1531*/1532if (idxlen == 0) {1533node_index = 0;1534goto return_first_hook;1535}15361537/*1538* Locate node1539*/1540for (node_index = 0; node_index < list->numnames; node_index++)1541if (list->nodeinfo[node_index].id >= oid->subs[sub])1542break;15431544/*1545* If we have only the node part of the index take, or1546* there is no node with that Id, take the first hook of that node.1547*/1548if (idxlen == 1 || node_index >= list->numnames ||1549list->nodeinfo[node_index].id > oid->subs[sub])1550goto return_first_hook;15511552/*1553* We had an exact match on the node id and have (at last part)1554* of the hook name index. Loop through the hooks of the node1555* and find the next one.1556*/1557if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,1558NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) {1559free(resp);1560return (-1);1561}1562hooks = (struct hooklist *)(void *)resp1->data;1563if (hooks->nodeinfo.hooks > 0) {1564qsort(hooks->link, hooks->nodeinfo.hooks,1565sizeof(hooks->link[0]), hook_cmp);1566for (i = 0; i < hooks->nodeinfo.hooks; i++) {1567idx.len = strlen(hooks->link[i].ourhook) + 1;1568idx.subs[0] = idx.len - 1;1569for (j = 0; j < idx.len; j++)1570idx.subs[j + 1] = hooks->link[i].ourhook[j];1571if (index_compare(oid, sub + 1, &idx) < 0)1572break;1573}1574if (i < hooks->nodeinfo.hooks) {1575*nodeinfo = hooks->nodeinfo;1576*linkinfo = hooks->link[i];15771578free(resp);1579free(resp1);1580return (0);1581}1582}15831584/* no hook found larger than the index on the index node - take1585* first hook of next node */1586free(resp1);1587node_index++;15881589return_first_hook:1590while (node_index < list->numnames) {1591if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,1592NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL)1593break;1594hooks = (struct hooklist *)(void *)resp1->data;1595if (hooks->nodeinfo.hooks > 0) {1596qsort(hooks->link, hooks->nodeinfo.hooks,1597sizeof(hooks->link[0]), hook_cmp);15981599*nodeinfo = hooks->nodeinfo;1600*linkinfo = hooks->link[0];16011602free(resp);1603free(resp1);1604return (0);1605}16061607/* if we don't have hooks, try next node */1608free(resp1);1609node_index++;1610}16111612free(resp);1613return (-1);1614}16151616int1617op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value,1618u_int sub, u_int iidx, enum snmp_op op)1619{1620asn_subid_t which = value->var.subs[sub - 1];1621struct linkinfo linkinfo;1622struct nodeinfo nodeinfo;1623u_int32_t lid;1624u_char *hook;1625size_t hooklen;1626u_int i;16271628switch (op) {16291630case SNMP_OP_GETNEXT:1631if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1)1632return (SNMP_ERR_NOSUCHNAME);16331634value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook);1635value->var.subs[sub] = nodeinfo.id;1636value->var.subs[sub + 1] = strlen(linkinfo.ourhook);1637for (i = 0; i < strlen(linkinfo.ourhook); i++)1638value->var.subs[sub + i + 2] =1639linkinfo.ourhook[i];1640break;16411642case SNMP_OP_GET:1643if (index_decode(&value->var, sub, iidx, &lid,1644&hook, &hooklen))1645return (SNMP_ERR_NOSUCHNAME);1646if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {1647free(hook);1648return (SNMP_ERR_NOSUCHNAME);1649}1650free(hook);1651break;16521653case SNMP_OP_SET:1654if (index_decode(&value->var, sub, iidx, &lid,1655&hook, &hooklen))1656return (SNMP_ERR_NO_CREATION);1657if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {1658free(hook);1659return (SNMP_ERR_NO_CREATION);1660}1661free(hook);1662return (SNMP_ERR_NOT_WRITEABLE);16631664case SNMP_OP_ROLLBACK:1665case SNMP_OP_COMMIT:1666default:1667abort();16681669}16701671switch (which) {16721673case LEAF_begemotNgHookStatus:1674value->v.integer = 1;1675break;1676case LEAF_begemotNgHookPeerNodeId:1677value->v.uint32 = linkinfo.nodeinfo.id;1678break;1679case LEAF_begemotNgHookPeerHook:1680return (string_get(value, linkinfo.peerhook, -1));1681case LEAF_begemotNgHookPeerType:1682return (string_get(value, linkinfo.nodeinfo.type, -1));1683default:1684abort();1685}1686return (SNMP_ERR_NOERROR);1687}168816891690