diff options
Diffstat (limited to '')
-rw-r--r-- | lib/agentx.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/lib/agentx.c b/lib/agentx.c new file mode 100644 index 0000000..45f14c2 --- /dev/null +++ b/lib/agentx.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* SNMP support + * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx> + */ + +#include <zebra.h> + +#ifdef SNMP_AGENTX +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/agent/snmp_vars.h> + +#include "command.h" +#include "smux.h" +#include "memory.h" +#include "linklist.h" +#include "lib/version.h" +#include "lib_errors.h" +#include "hook.h" +#include "libfrr.h" +#include "xref.h" + +XREF_SETUP(); + +DEFINE_HOOK(agentx_enabled, (), ()); + +static bool agentx_enabled = false; + +static struct event_loop *agentx_tm; +static struct event *timeout_thr = NULL; +static struct list *events = NULL; + +static void agentx_events_update(void); + +static void agentx_timeout(struct event *t) +{ + snmp_timeout(); + run_alarms(); + netsnmp_check_outstanding_agent_requests(); + agentx_events_update(); +} + +static void agentx_read(struct event *t) +{ + fd_set fds; + int flags, new_flags = 0; + int nonblock = false; + struct listnode *ln = EVENT_ARG(t); + struct event **thr = listgetdata(ln); + XFREE(MTYPE_TMP, thr); + list_delete_node(events, ln); + + /* fix for non blocking socket */ + flags = fcntl(EVENT_FD(t), F_GETFL, 0); + if (-1 == flags) { + flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)", + strerror(errno), errno); + return; + } + + if (flags & O_NONBLOCK) + nonblock = true; + else + new_flags = fcntl(EVENT_FD(t), F_SETFL, flags | O_NONBLOCK); + + if (new_flags == -1) + flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)", + strerror(errno), errno); + + FD_ZERO(&fds); + FD_SET(EVENT_FD(t), &fds); + snmp_read(&fds); + + /* Reset the flag */ + if (!nonblock) { + new_flags = fcntl(EVENT_FD(t), F_SETFL, flags); + + if (new_flags == -1) + flog_err( + EC_LIB_SYSTEM_CALL, + "Failed to set snmp fd back to original settings: %s(%d)", + strerror(errno), errno); + } + + netsnmp_check_outstanding_agent_requests(); + agentx_events_update(); +} + +static void agentx_events_update(void) +{ + int maxfd = 0; + int block = 1; + struct timeval timeout = {.tv_sec = 0, .tv_usec = 0}; + fd_set fds; + struct listnode *ln; + struct event **thr; + int fd, thr_fd; + + event_cancel(&timeout_thr); + + FD_ZERO(&fds); + snmp_select_info(&maxfd, &fds, &timeout, &block); + + if (!block) { + event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout, + &timeout_thr); + } + + ln = listhead(events); + thr = ln ? listgetdata(ln) : NULL; + thr_fd = thr ? EVENT_FD(*thr) : -1; + + /* "two-pointer" / two-list simultaneous iteration + * ln/thr/thr_fd point to the next existing event listener to hit while + * fd counts to catch up */ + for (fd = 0; fd < maxfd; fd++) { + /* caught up */ + if (thr_fd == fd) { + struct listnode *nextln = listnextnode(ln); + if (!FD_ISSET(fd, &fds)) { + event_cancel(thr); + XFREE(MTYPE_TMP, thr); + list_delete_node(events, ln); + } + ln = nextln; + thr = ln ? listgetdata(ln) : NULL; + thr_fd = thr ? EVENT_FD(*thr) : -1; + } + /* need listener, but haven't hit one where it would be */ + else if (FD_ISSET(fd, &fds)) { + struct listnode *newln; + + thr = XCALLOC(MTYPE_TMP, sizeof(struct event *)); + newln = listnode_add_before(events, ln, thr); + event_add_read(agentx_tm, agentx_read, newln, fd, thr); + } + } + + /* leftover event listeners at this point have fd > maxfd, delete them + */ + while (ln) { + struct listnode *nextln = listnextnode(ln); + thr = listgetdata(ln); + event_cancel(thr); + XFREE(MTYPE_TMP, thr); + list_delete_node(events, ln); + ln = nextln; + } +} + +/* AgentX node. */ +static int config_write_agentx(struct vty *vty); +static struct cmd_node agentx_node = { + .name = "smux", + .node = SMUX_NODE, + .prompt = "", + .config_write = config_write_agentx, +}; + +/* Logging NetSNMP messages */ +static int agentx_log_callback(int major, int minor, void *serverarg, + void *clientarg) +{ + struct snmp_log_message *slm = (struct snmp_log_message *)serverarg; + char *msg = XSTRDUP(MTYPE_TMP, slm->msg); + if (msg) + msg[strlen(msg) - 1] = '\0'; + switch (slm->priority) { + case LOG_EMERG: + flog_err(EC_LIB_SNMP, "snmp[emerg]: %s", msg ? msg : slm->msg); + break; + case LOG_ALERT: + flog_err(EC_LIB_SNMP, "snmp[alert]: %s", msg ? msg : slm->msg); + break; + case LOG_CRIT: + flog_err(EC_LIB_SNMP, "snmp[crit]: %s", msg ? msg : slm->msg); + break; + case LOG_ERR: + flog_err(EC_LIB_SNMP, "snmp[err]: %s", msg ? msg : slm->msg); + break; + case LOG_WARNING: + flog_warn(EC_LIB_SNMP, "snmp[warning]: %s", + msg ? msg : slm->msg); + break; + case LOG_NOTICE: + zlog_notice("snmp[notice]: %s", msg ? msg : slm->msg); + break; + case LOG_INFO: + zlog_info("snmp[info]: %s", msg ? msg : slm->msg); + break; + case LOG_DEBUG: + zlog_debug("snmp[debug]: %s", msg ? msg : slm->msg); + break; + } + XFREE(MTYPE_TMP, msg); + return SNMP_ERR_NOERROR; +} + +static int config_write_agentx(struct vty *vty) +{ + if (agentx_enabled) + vty_out(vty, "agentx\n"); + return 1; +} + +DEFUN (agentx_enable, + agentx_enable_cmd, + "agentx", + "SNMP AgentX protocol settings\n") +{ + if (!agentx_enabled) { + init_snmp(FRR_SMUX_NAME); + events = list_new(); + agentx_events_update(); + agentx_enabled = true; + hook_call(agentx_enabled); + } + + return CMD_SUCCESS; +} + +DEFUN (no_agentx, + no_agentx_cmd, + "no agentx", + NO_STR + "SNMP AgentX protocol settings\n") +{ + if (!agentx_enabled) + return CMD_SUCCESS; + vty_out(vty, "SNMP AgentX support cannot be disabled once enabled\n"); + return CMD_WARNING_CONFIG_FAILED; +} + +static int smux_disable(void) +{ + agentx_enabled = false; + + return 0; +} + +bool smux_enabled(void) +{ + return agentx_enabled; +} + +void smux_init(struct event_loop *tm) +{ + agentx_tm = tm; + + netsnmp_enable_subagent(); + snmp_disable_log(); + snmp_enable_calllog(); + snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, + agentx_log_callback, NULL); + init_agent(FRR_SMUX_NAME); + + install_node(&agentx_node); + install_element(CONFIG_NODE, &agentx_enable_cmd); + install_element(CONFIG_NODE, &no_agentx_cmd); + + hook_register(frr_early_fini, smux_disable); +} + +void smux_agentx_enable(void) +{ + if (!agentx_enabled) { + init_snmp(FRR_SMUX_NAME); + events = list_new(); + agentx_events_update(); + agentx_enabled = true; + } +} + +void smux_register_mib(const char *descr, struct variable *var, size_t width, + int num, oid name[], size_t namelen) +{ + register_mib(descr, var, width, num, name, namelen); +} + +void smux_trap(struct variable *vp, size_t vp_len, const oid *ename, + size_t enamelen, const oid *name, size_t namelen, + const oid *iname, size_t inamelen, + const struct trap_object *trapobj, size_t trapobjlen, + uint8_t sptrap) +{ + struct index_oid trap_index[1]; + + /* copy the single index into the multi-index format */ + oid_copy(trap_index[0].indexname, iname, inamelen); + trap_index[0].indexlen = inamelen; + + smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen, + trap_index, array_size(trap_index), trapobj, + trapobjlen, sptrap); +} + +int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename, + size_t enamelen, const oid *name, size_t namelen, + struct index_oid *iname, size_t index_len, + const struct trap_object *trapobj, size_t trapobjlen, + uint8_t sptrap) +{ + oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; + size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid); + oid notification_oid[MAX_OID_LEN]; + size_t notification_oid_len; + unsigned int i; + + netsnmp_variable_list *notification_vars = NULL; + if (!agentx_enabled) + return 0; + + /* snmpTrapOID */ + oid_copy(notification_oid, ename, enamelen); + notification_oid[enamelen] = sptrap; + notification_oid_len = enamelen + 1; + snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, + objid_snmptrap_len, ASN_OBJECT_ID, + (uint8_t *)notification_oid, + notification_oid_len * sizeof(oid)); + + /* Provided bindings */ + for (i = 0; i < trapobjlen; i++) { + unsigned int j; + oid oid[MAX_OID_LEN]; + size_t oid_len, onamelen; + uint8_t *val; + size_t val_len; + WriteMethod *wm = NULL; + struct variable cvp; + unsigned int iindex; + /* + * this allows the behaviour of smux_trap with a singe index + * for all objects to be maintained whilst allowing traps which + * have different indices per object to be supported + */ + iindex = (index_len == 1) ? 0 : i; + + /* Make OID. */ + if (trapobj[i].namelen > 0) { + /* Columnar object */ + onamelen = trapobj[i].namelen; + oid_copy(oid, name, namelen); + oid_copy(oid + namelen, trapobj[i].name, onamelen); + oid_copy(oid + namelen + onamelen, + iname[iindex].indexname, + iname[iindex].indexlen); + oid_len = namelen + onamelen + iname[iindex].indexlen; + } else { + /* Scalar object */ + onamelen = trapobj[i].namelen * (-1); + oid_copy(oid, name, namelen); + oid_copy(oid + namelen, trapobj[i].name, onamelen); + oid[onamelen + namelen] = 0; + oid_len = namelen + onamelen + 1; + } + + /* Locate the appropriate function and type in the MIB registry. + */ + for (j = 0; j < vp_len; j++) { + if (oid_compare(trapobj[i].name, onamelen, vp[j].name, + vp[j].namelen) + != 0) + continue; + /* We found the appropriate variable in the MIB + * registry. */ + oid_copy(cvp.name, name, namelen); + oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen); + cvp.namelen = namelen + vp[j].namelen; + cvp.type = vp[j].type; + cvp.magic = vp[j].magic; + cvp.acl = vp[j].acl; + cvp.findVar = vp[j].findVar; + + /* Grab the result. */ + val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len, + &wm); + if (!val) + break; + snmp_varlist_add_variable(¬ification_vars, oid, + oid_len, vp[j].type, val, + val_len); + break; + } + } + + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + agentx_events_update(); + return 1; +} + +void smux_events_update(void) +{ + agentx_events_update(); +} + +#endif /* SNMP_AGENTX */ |