summaryrefslogtreecommitdiffstats
path: root/lib/agentx.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/agentx.c400
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(&notification_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(&notification_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 */