| /* SNMP support |
| * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx> |
| * |
| * This file is part of GNU Zebra. |
| * |
| * GNU Zebra is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * GNU Zebra is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GNU Zebra; see the file COPYING. If not, write to the Free |
| * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include <zebra.h> |
| |
| #if defined HAVE_SNMP && defined 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" |
| |
| static int agentx_enabled = 0; |
| |
| static struct thread_master *agentx_tm; |
| static struct thread *timeout_thr = NULL; |
| static struct list *events = NULL; |
| |
| static void agentx_events_update(void); |
| |
| static int |
| agentx_timeout(struct thread *t) |
| { |
| timeout_thr = NULL; |
| |
| snmp_timeout (); |
| run_alarms (); |
| netsnmp_check_outstanding_agent_requests (); |
| agentx_events_update (); |
| return 0; |
| } |
| |
| static int |
| agentx_read(struct thread *t) |
| { |
| fd_set fds; |
| struct listnode *ln = THREAD_ARG (t); |
| list_delete_node (events, ln); |
| |
| FD_ZERO (&fds); |
| FD_SET (THREAD_FD (t), &fds); |
| snmp_read (&fds); |
| |
| netsnmp_check_outstanding_agent_requests (); |
| agentx_events_update (); |
| return 0; |
| } |
| |
| 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 thread *thr; |
| int fd, thr_fd; |
| |
| THREAD_OFF (timeout_thr); |
| |
| FD_ZERO (&fds); |
| snmp_select_info (&maxfd, &fds, &timeout, &block); |
| |
| if (!block) |
| timeout_thr = thread_add_timer_tv (agentx_tm, agentx_timeout, NULL, &timeout); |
| |
| ln = listhead (events); |
| thr = ln ? listgetdata (ln) : NULL; |
| thr_fd = thr ? THREAD_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)) |
| { |
| thread_cancel (thr); |
| list_delete_node (events, ln); |
| } |
| ln = nextln; |
| thr = ln ? listgetdata (ln) : NULL; |
| thr_fd = thr ? THREAD_FD (thr) : -1; |
| } |
| /* need listener, but haven't hit one where it would be */ |
| else if (FD_ISSET (fd, &fds)) |
| { |
| struct listnode *newln; |
| thr = thread_add_read (agentx_tm, agentx_read, NULL, fd); |
| newln = listnode_add_before (events, ln, thr); |
| thr->arg = newln; |
| } |
| } |
| |
| /* leftover event listeners at this point have fd > maxfd, delete them */ |
| while (ln) |
| { |
| struct listnode *nextln = listnextnode (ln); |
| thread_cancel (listgetdata (ln)); |
| list_delete_node (events, ln); |
| ln = nextln; |
| } |
| } |
| |
| /* AgentX node. */ |
| static struct cmd_node agentx_node = |
| { |
| SMUX_NODE, |
| "" /* AgentX has no interface. */ |
| }; |
| |
| /* 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 = strdup (slm->msg); |
| if (msg) msg[strlen(msg)-1] = '\0'; |
| switch (slm->priority) |
| { |
| case LOG_EMERG: zlog_err ("snmp[emerg]: %s", msg?msg:slm->msg); break; |
| case LOG_ALERT: zlog_err ("snmp[alert]: %s", msg?msg:slm->msg); break; |
| case LOG_CRIT: zlog_err ("snmp[crit]: %s", msg?msg:slm->msg); break; |
| case LOG_ERR: zlog_err ("snmp[err]: %s", msg?msg:slm->msg); break; |
| case LOG_WARNING: zlog_warn ("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; |
| } |
| free(msg); |
| return SNMP_ERR_NOERROR; |
| } |
| |
| static int |
| config_write_agentx (struct vty *vty) |
| { |
| if (agentx_enabled) |
| vty_out (vty, "agentx%s", VTY_NEWLINE); |
| return 0; |
| } |
| |
| DEFUN (agentx_enable, |
| agentx_enable_cmd, |
| "agentx", |
| "SNMP AgentX protocol settings\n" |
| "SNMP AgentX settings\n") |
| { |
| if (!agentx_enabled) |
| { |
| init_snmp("quagga"); |
| events = list_new(); |
| agentx_events_update (); |
| agentx_enabled = 1; |
| return CMD_SUCCESS; |
| } |
| vty_out (vty, "SNMP AgentX already enabled%s", VTY_NEWLINE); |
| return CMD_WARNING; |
| } |
| |
| DEFUN (no_agentx, |
| no_agentx_cmd, |
| "no agentx", |
| NO_STR |
| "SNMP AgentX protocol settings\n" |
| "SNMP AgentX settings\n") |
| { |
| if (!agentx_enabled) return CMD_SUCCESS; |
| vty_out (vty, "SNMP AgentX support cannot be disabled once enabled%s", VTY_NEWLINE); |
| return CMD_WARNING; |
| } |
| |
| void |
| smux_init (struct thread_master *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 ("quagga"); |
| |
| install_node (&agentx_node, config_write_agentx); |
| install_element (CONFIG_NODE, &agentx_enable_cmd); |
| install_element (CONFIG_NODE, &no_agentx_cmd); |
| } |
| |
| 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); |
| } |
| |
| int |
| 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, |
| u_char 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, |
| (u_char *) 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; |
| u_char *val; |
| size_t val_len; |
| WriteMethod *wm = NULL; |
| struct variable cvp; |
| |
| /* 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, inamelen); |
| oid_len = namelen + onamelen + inamelen; |
| } |
| 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; |
| } |
| |
| #endif /* HAVE_SNMP */ |