diff --git a/configure.ac b/configure.ac index 96e201cc4210..715b1b7ef732 100755 --- a/configure.ac +++ b/configure.ac @@ -382,7 +382,7 @@ AC_ARG_ENABLE(bgp-vnc, AC_ARG_WITH(rfp-path, AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC])) AC_ARG_ENABLE(snmp, - AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) + AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx])) AC_ARG_ENABLE(zeromq, AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)])) AC_ARG_WITH(libpam, @@ -1496,14 +1496,13 @@ int main(void); yes) SNMP_METHOD=agentx ;; - smux|agentx) + agentx) SNMP_METHOD="${enable_snmp}" ;; *) - AC_MSG_ERROR([--enable-snmp given with an unknown method (${enable_snmp}). Use smux or agentx]) + AC_MSG_ERROR([--enable-snmp given with an unknown method (${enable_snmp}). Use yes or agentx]) ;; esac - AH_TEMPLATE([SNMP_SMUX], [Use SNMP SMUX to interface with snmpd]) AH_TEMPLATE([SNMP_AGENTX], [Use SNMP AgentX to interface with snmpd]) AC_DEFINE_UNQUOTED(AS_TR_CPP(SNMP_${SNMP_METHOD}),,SNMP method to interface with snmpd) fi diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst index 38918ff250f3..5579969c0e00 100644 --- a/doc/user/snmp.rst +++ b/doc/user/snmp.rst @@ -7,8 +7,8 @@ SNMP Support :abbr:`SNMP (Simple Network Managing Protocol)` is a widely implemented feature for collecting network information from router and/or host. FRR itself does not support SNMP agent (server daemon) functionality but is able to connect to -a SNMP agent using the SMUX protocol (:rfc:`1227`) or the AgentX protocol -(:rfc:`2741`) and make the routing protocol MIBs available through it. +a SNMP agent using the the AgentX protocol (:rfc:`2741`) and make the +routing protocol MIBs available through it. Note that SNMP Support needs to be enabled at compile-time and loaded as module on daemon startup. Refer to :ref:`loadable-module-support` on the latter. @@ -18,16 +18,10 @@ on daemon startup. Refer to :ref:`loadable-module-support` on the latter. Getting and installing an SNMP agent ==================================== -There are several SNMP agent which support SMUX or AgentX. We recommend to use +The supported SNMP agent is AgentX. We recommend to use the latest version of `net-snmp` which was formerly known as `ucd-snmp`. It is free and open software and available at `http://www.net-snmp.org/ `_ -and as binary package for most Linux distributions. `net-snmp` has to be -compiled with `--with-mib-modules=agentx` to be able to accept connections from -FRR using AgentX protocol or with `--with-mib-modules=smux` to use SMUX -protocol. - -Nowadays, SMUX is a legacy protocol. The AgentX protocol should be preferred -for any new deployment. Both protocols have the same coverage. +and as binary package for most Linux distributions. .. _agentx-configuration: @@ -42,7 +36,7 @@ master SNMP agent (snmpd) and each of the FRR daemons must be configured. In :file:`/etc/snmp/snmpd.conf`, the ``master agentx`` directive should be added. In each of the FRR daemons, ``agentx`` command will enable AgentX support. -:file:`/etc/snmp/snmpd.conf`: +:file:`/etc/snmp/zebra.conf`: :: @@ -97,105 +91,6 @@ need to configure FRR to use another transport, you can configure it through agentXSocket tcp:192.168.15.12:705 -.. _smux-configuration: - -SMUX configuration -================== - -To enable SMUX protocol support, FRR must have been build with the -:option:`--enable-snmp` option. - -A separate connection has then to be established between the SNMP agent (snmpd) -and each of the FRR daemons. This connections each use different OID numbers -and passwords. Be aware that this OID number is not the one that is used in -queries by clients, it is solely used for the intercommunication of the -daemons. - -In the following example the ospfd daemon will be connected to the snmpd daemon -using the password "frr_ospfd". For testing it is recommending to take exactly -the below snmpd.conf as wrong access restrictions can be hard to debug. - -:file:`/etc/snmp/snmpd.conf`: - -:: - - # - # example access restrictions setup - # - com2sec readonly default public - group MyROGroup v1 readonly - view all included .1 80 - access MyROGroup "" any noauth exact all none none - # - # the following line is relevant for FRR - # - smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd - -:file:`/etc/frr/ospf`: - -:: - - ! ... the rest of ospfd.conf has been omitted for clarity ... - ! - smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd - ! - - -After restarting snmpd and frr, a successful connection can be verified in the -syslog and by querying the SNMP daemon: - -:: - - snmpd[12300]: [smux_accept] accepted fd 12 from 127.0.0.1:36255 - snmpd[12300]: accepted smux peer: \\ - oid GNOME-PRODUCT-ZEBRA-MIB::ospfd, frr-0.96.5 - - # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1 - OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109 - - -Be warned that the current version (5.1.1) of the Net-SNMP daemon writes a line -for every SNMP connect to the syslog which can lead to enormous log file sizes. -If that is a problem you should consider to patch snmpd and comment out the -troublesome `snmp_log()` line in the function `netsnmp_agent_check_packet()` in -`agent/snmp_agent.c`. - -MIB and command reference -========================= - -The following OID numbers are used for the interprocess communication of snmpd and -the FRR daemons with SMUX only.:: - - . (OIDs below .iso.org.dod.internet.private.enterprises) - zebra .1.3.6.1.4.1.3317.1.2.1 .gnome.gnomeProducts.zebra.zserv - bgpd .1.3.6.1.4.1.3317.1.2.2 .gnome.gnomeProducts.zebra.bgpd - ripd .1.3.6.1.4.1.3317.1.2.3 .gnome.gnomeProducts.zebra.ripd - ospfd .1.3.6.1.4.1.3317.1.2.5 .gnome.gnomeProducts.zebra.ospfd - ospf6d .1.3.6.1.4.1.3317.1.2.6 .gnome.gnomeProducts.zebra.ospf6d - - -Sadly, SNMP has not been implemented in all daemons yet. The following -OID numbers are used for querying the SNMP daemon by a client::: - - zebra .1.3.6.1.2.1.4.24 .iso.org.dot.internet.mgmt.mib-2.ip.ipForward - ospfd .1.3.6.1.2.1.14 .iso.org.dot.internet.mgmt.mib-2.ospf - bgpd .1.3.6.1.2.1.15 .iso.org.dot.internet.mgmt.mib-2.bgp - ripd .1.3.6.1.2.1.23 .iso.org.dot.internet.mgmt.mib-2.rip2 - ospf6d .1.3.6.1.3.102 .iso.org.dod.internet.experimental.ospfv3 - - -The following syntax is understood by the FRR daemons for configuring SNMP -using SMUX: - -.. index:: smux peer OID -.. clicmd:: smux peer OID -.. index:: no smux peer OID -.. clicmd:: no smux peer OID -.. index:: smux peer OID PASSWORD -.. clicmd:: smux peer OID PASSWORD -.. index:: no smux peer OID PASSWORD -.. clicmd:: no smux peer OID PASSWORD - Here is the syntax for using AgentX: .. index:: agentx diff --git a/lib/smux.c b/lib/smux.c deleted file mode 100644 index 51abfccba369..000000000000 --- a/lib/smux.c +++ /dev/null @@ -1,1416 +0,0 @@ -/* SNMP support - * Copyright (C) 1999 Kunihiro Ishiguro - * - * 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 this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#ifdef SNMP_SMUX -#include -#include - -#include "log.h" -#include "thread.h" -#include "linklist.h" -#include "command.h" -#include -#include "memory.h" -#include "sockunion.h" -#include "smux.h" - -#define SMUX_PORT_DEFAULT 199 - -#define SMUXMAXPKTSIZE 1500 -#define SMUXMAXSTRLEN 256 - -#define SMUX_OPEN (ASN_APPLICATION | ASN_CONSTRUCTOR | 0) -#define SMUX_CLOSE (ASN_APPLICATION | ASN_PRIMITIVE | 1) -#define SMUX_RREQ (ASN_APPLICATION | ASN_CONSTRUCTOR | 2) -#define SMUX_RRSP (ASN_APPLICATION | ASN_PRIMITIVE | 3) -#define SMUX_SOUT (ASN_APPLICATION | ASN_PRIMITIVE | 4) - -#define SMUX_GET (ASN_CONTEXT | ASN_CONSTRUCTOR | 0) -#define SMUX_GETNEXT (ASN_CONTEXT | ASN_CONSTRUCTOR | 1) -#define SMUX_GETRSP (ASN_CONTEXT | ASN_CONSTRUCTOR | 2) -#define SMUX_SET (ASN_CONTEXT | ASN_CONSTRUCTOR | 3) -#define SMUX_TRAP (ASN_CONTEXT | ASN_CONSTRUCTOR | 4) - -#define SMUX_MAX_FAILURE 3 - -/* SNMP tree. */ -struct subtree { - /* Tree's oid. */ - oid name[MAX_OID_LEN]; - uint8_t name_len; - - /* List of the variables. */ - struct variable *variables; - - /* Length of the variables list. */ - int variables_num; - - /* Width of the variables list. */ - int variables_width; - - /* Registered flag. */ - int registered; -}; - -#define min(A,B) ((A) < (B) ? (A) : (B)) - -enum smux_event { SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ }; - -void smux_event(enum smux_event, int); - - -/* SMUX socket. */ -int smux_sock = -1; - -/* SMUX subtree list. */ -struct list *treelist; - -/* SMUX oid. */ -oid *smux_oid = NULL; -size_t smux_oid_len; - -/* SMUX password. */ -char *smux_passwd = NULL; - -/* SMUX read threads. */ -struct thread *smux_read_thread; - -/* SMUX connect thrads. */ -struct thread *smux_connect_thread; - -/* SMUX debug flag. */ -int debug_smux = 0; - -/* SMUX failure count. */ -int fail = 0; - -/* SMUX node. */ -static struct cmd_node smux_node = { - SMUX_NODE, "" /* SMUX has no interface. */ -}; - -/* thread master */ -static struct thread_master *smux_master; - -static int oid_compare_part(oid *o1, int o1_len, oid *o2, int o2_len) -{ - int i; - - for (i = 0; i < min(o1_len, o2_len); i++) { - if (o1[i] < o2[i]) - return -1; - else if (o1[i] > o2[i]) - return 1; - } - if (o1_len < o2_len) - return -1; - - return 0; -} - -static void smux_oid_dump(const char *prefix, const oid *oid, size_t oid_len) -{ - unsigned int i; - int first = 1; - char buf[MAX_OID_LEN * 3]; - - buf[0] = '\0'; - - for (i = 0; i < oid_len; i++) { - sprintf(buf + strlen(buf), "%s%d", first ? "" : ".", - (int)oid[i]); - first = 0; - } - zlog_debug("%s: %s", prefix, buf); -} - -static int smux_socket(void) -{ - int ret; - struct addrinfo hints, *res0, *res; - int gai; - int sock = 0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - gai = getaddrinfo(NULL, "smux", &hints, &res0); - if (gai == EAI_SERVICE) { - char servbuf[NI_MAXSERV]; - sprintf(servbuf, "%d", SMUX_PORT_DEFAULT); - servbuf[sizeof(servbuf) - 1] = '\0'; - gai = getaddrinfo(NULL, servbuf, &hints, &res0); - } - if (gai) { - zlog_warn("Cannot locate loopback service smux"); - return -1; - } - for (res = res0; res; res = res->ai_next) { - if (res->ai_family != AF_INET && res->ai_family != AF_INET6) - continue; - - sock = socket(res->ai_family, res->ai_socktype, - res->ai_protocol); - if (sock < 0) - continue; - sockopt_reuseaddr(sock); - sockopt_reuseport(sock); - ret = connect(sock, res->ai_addr, res->ai_addrlen); - if (ret < 0) { - close(sock); - sock = -1; - continue; - } - break; - } - freeaddrinfo(res0); - if (sock < 0) - zlog_warn("Can't connect to SNMP agent with SMUX"); - return sock; -} - -static void smux_getresp_send(oid objid[], size_t objid_len, long reqid, - long errstat, long errindex, uint8_t val_type, - void *arg, size_t arg_len) -{ - uint8_t buf[BUFSIZ]; - uint8_t *ptr, *h1, *h1e, *h2, *h2e; - size_t len, length; - - ptr = buf; - len = BUFSIZ; - length = len; - - if (debug_smux) { - zlog_debug("SMUX GETRSP send"); - zlog_debug("SMUX GETRSP reqid: %ld", reqid); - } - - h1 = ptr; - /* Place holder h1 for complete sequence */ - ptr = asn_build_sequence(ptr, &len, (uint8_t)SMUX_GETRSP, 0); - h1e = ptr; - - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &reqid, - sizeof(reqid)); - - if (debug_smux) - zlog_debug("SMUX GETRSP errstat: %ld", errstat); - - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - &errstat, sizeof(errstat)); - if (debug_smux) - zlog_debug("SMUX GETRSP errindex: %ld", errindex); - - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - &errindex, sizeof(errindex)); - - h2 = ptr; - /* Place holder h2 for one variable */ - ptr = asn_build_sequence(ptr, &len, - (uint8_t)(ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); - h2e = ptr; - - ptr = snmp_build_var_op(ptr, objid, &objid_len, val_type, arg_len, arg, - &len); - - /* Now variable size is known, fill in size */ - asn_build_sequence(h2, &length, - (uint8_t)(ASN_SEQUENCE | ASN_CONSTRUCTOR), - ptr - h2e); - - /* Fill in size of whole sequence */ - asn_build_sequence(h1, &length, (uint8_t)SMUX_GETRSP, ptr - h1e); - - if (debug_smux) - zlog_debug("SMUX getresp send: %td", (ptr - buf)); - - send(smux_sock, buf, (ptr - buf), 0); -} - -static uint8_t *smux_var(uint8_t *ptr, size_t len, oid objid[], - size_t *objid_len, size_t *var_val_len, - uint8_t *var_val_type, void **var_value) -{ - uint8_t type; - uint8_t val_type; - size_t val_len; - uint8_t *val; - - if (debug_smux) - zlog_debug("SMUX var parse: len %zd", len); - - /* Parse header. */ - ptr = asn_parse_header(ptr, &len, &type); - - if (debug_smux) { - zlog_debug("SMUX var parse: type %d len %zd", type, len); - zlog_debug("SMUX var parse: type must be %d", - (ASN_SEQUENCE | ASN_CONSTRUCTOR)); - } - - /* Parse var option. */ - *objid_len = MAX_OID_LEN; - ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type, &val_len, - &val, &len); - - if (var_val_len) - *var_val_len = val_len; - - if (var_value) - *var_value = (void *)val; - - if (var_val_type) - *var_val_type = val_type; - - /* Requested object id length is objid_len. */ - if (debug_smux) - smux_oid_dump("Request OID", objid, *objid_len); - - if (debug_smux) - zlog_debug("SMUX val_type: %d", val_type); - - /* Check request value type. */ - if (debug_smux) - switch (val_type) { - case ASN_NULL: - /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set - to - ASN_NULL. */ - zlog_debug("ASN_NULL"); - break; - - case ASN_INTEGER: - zlog_debug("ASN_INTEGER"); - break; - case ASN_COUNTER: - case ASN_GAUGE: - case ASN_TIMETICKS: - case ASN_UINTEGER: - zlog_debug("ASN_COUNTER"); - break; - case ASN_COUNTER64: - zlog_debug("ASN_COUNTER64"); - break; - case ASN_IPADDRESS: - zlog_debug("ASN_IPADDRESS"); - break; - case ASN_OCTET_STR: - zlog_debug("ASN_OCTET_STR"); - break; - case ASN_OPAQUE: - case ASN_NSAP: - case ASN_OBJECT_ID: - zlog_debug("ASN_OPAQUE"); - break; - case SNMP_NOSUCHOBJECT: - zlog_debug("SNMP_NOSUCHOBJECT"); - break; - case SNMP_NOSUCHINSTANCE: - zlog_debug("SNMP_NOSUCHINSTANCE"); - break; - case SNMP_ENDOFMIBVIEW: - zlog_debug("SNMP_ENDOFMIBVIEW"); - break; - case ASN_BIT_STR: - zlog_debug("ASN_BIT_STR"); - break; - default: - zlog_debug("Unknown type"); - break; - } - return ptr; -} - -/* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on - ucd-snmp smux and as such suppose, that the peer receives in the message - only one variable. Fortunately, IBM seems to do the same in AIX. */ - -static int smux_set(oid *reqid, size_t *reqid_len, uint8_t val_type, void *val, - size_t val_len, int action) -{ - int j; - struct subtree *subtree; - struct variable *v; - int subresult; - oid *suffix; - size_t suffix_len; - int result; - uint8_t *statP = NULL; - WriteMethod *write_method = NULL; - struct listnode *node, *nnode; - - /* Check */ - for (ALL_LIST_ELEMENTS(treelist, node, nnode, subtree)) { - subresult = oid_compare_part(reqid, *reqid_len, subtree->name, - subtree->name_len); - - /* Subtree matched. */ - if (subresult == 0) { - /* Prepare suffix. */ - suffix = reqid + subtree->name_len; - suffix_len = *reqid_len - subtree->name_len; - result = subresult; - - /* Check variables. */ - for (j = 0; j < subtree->variables_num; j++) { - v = &subtree->variables[j]; - - /* Always check suffix */ - result = oid_compare_part(suffix, suffix_len, - v->name, v->namelen); - - /* This is exact match so result must be zero. - */ - if (result == 0) { - if (debug_smux) - zlog_debug( - "SMUX function call index is %d", - v->magic); - - statP = (*v->findVar)( - v, suffix, &suffix_len, 1, - &val_len, &write_method); - - if (write_method) { - return (*write_method)( - action, val, val_type, - val_len, statP, suffix, - suffix_len); - } else { - return SNMP_ERR_READONLY; - } - } - - /* If above execution is failed or oid is small - (so - there is no further match). */ - if (result < 0) - return SNMP_ERR_NOSUCHNAME; - } - } - } - return SNMP_ERR_NOSUCHNAME; -} - -static int smux_get(oid *reqid, size_t *reqid_len, int exact, uint8_t *val_type, - void **val, size_t *val_len) -{ - int j; - struct subtree *subtree; - struct variable *v; - int subresult; - oid *suffix; - size_t suffix_len; - int result; - WriteMethod *write_method = NULL; - struct listnode *node, *nnode; - - /* Check */ - for (ALL_LIST_ELEMENTS(treelist, node, nnode, subtree)) { - subresult = oid_compare_part(reqid, *reqid_len, subtree->name, - subtree->name_len); - - /* Subtree matched. */ - if (subresult == 0) { - /* Prepare suffix. */ - suffix = reqid + subtree->name_len; - suffix_len = *reqid_len - subtree->name_len; - result = subresult; - - /* Check variables. */ - for (j = 0; j < subtree->variables_num; j++) { - v = &subtree->variables[j]; - - /* Always check suffix */ - result = oid_compare_part(suffix, suffix_len, - v->name, v->namelen); - - /* This is exact match so result must be zero. - */ - if (result == 0) { - if (debug_smux) - zlog_debug( - "SMUX function call index is %d", - v->magic); - - *val = (*v->findVar)( - v, suffix, &suffix_len, exact, - val_len, &write_method); - - /* There is no instance. */ - if (*val == NULL) - return SNMP_NOSUCHINSTANCE; - - /* Call is suceed. */ - *val_type = v->type; - - return 0; - } - - /* If above execution is failed or oid is small - (so - there is no further match). */ - if (result < 0) - return SNMP_ERR_NOSUCHNAME; - } - } - } - return SNMP_ERR_NOSUCHNAME; -} - -static int smux_getnext(oid *reqid, size_t *reqid_len, int exact, - uint8_t *val_type, void **val, size_t *val_len) -{ - int j; - oid save[MAX_OID_LEN]; - int savelen = 0; - struct subtree *subtree; - struct variable *v; - int subresult; - oid *suffix; - size_t suffix_len; - int result; - WriteMethod *write_method = NULL; - struct listnode *node, *nnode; - - - /* Save incoming request. */ - oid_copy(save, reqid, *reqid_len); - savelen = *reqid_len; - - /* Check */ - for (ALL_LIST_ELEMENTS(treelist, node, nnode, subtree)) { - subresult = oid_compare_part(reqid, *reqid_len, subtree->name, - subtree->name_len); - - /* If request is in the tree. The agent has to make sure we - only receive requests we have registered for. */ - /* Unfortunately, that's not true. In fact, a SMUX subagent has - to - behave as if it manages the whole SNMP MIB tree itself. It's - the - duty of the master agent to collect the best answer and - return it - to the manager. See RFC 1227 chapter 3.1.6 for the glory - details - :-). ucd-snmp really behaves bad here as it actually might - ask - multiple times for the same GETNEXT request as it throws away - the - answer when it expects it in a different subtree and might - come - back later with the very same request. --jochen */ - - if (subresult <= 0) { - /* Prepare suffix. */ - suffix = reqid + subtree->name_len; - suffix_len = *reqid_len - subtree->name_len; - if (subresult < 0) { - oid_copy(reqid, subtree->name, - subtree->name_len); - *reqid_len = subtree->name_len; - } - for (j = 0; j < subtree->variables_num; j++) { - result = subresult; - v = &subtree->variables[j]; - - /* Next then check result >= 0. */ - if (result == 0) - result = oid_compare_part( - suffix, suffix_len, v->name, - v->namelen); - - if (result <= 0) { - if (debug_smux) - zlog_debug( - "SMUX function call index is %d", - v->magic); - if (result < 0) { - oid_copy(suffix, v->name, - v->namelen); - suffix_len = v->namelen; - } - *val = (*v->findVar)( - v, suffix, &suffix_len, exact, - val_len, &write_method); - *reqid_len = - suffix_len + subtree->name_len; - if (*val) { - *val_type = v->type; - return 0; - } - } - } - } - } - memcpy(reqid, save, savelen * sizeof(oid)); - *reqid_len = savelen; - - return SNMP_ERR_NOSUCHNAME; -} - -/* GET message header. */ -static uint8_t *smux_parse_get_header(uint8_t *ptr, size_t *len, long *reqid) -{ - uint8_t type; - long errstat; - long errindex; - - /* Request ID. */ - ptr = asn_parse_int(ptr, len, &type, reqid, sizeof(*reqid)); - - if (debug_smux) - zlog_debug("SMUX GET reqid: %d len: %d", (int)*reqid, - (int)*len); - - /* Error status. */ - ptr = asn_parse_int(ptr, len, &type, &errstat, sizeof(errstat)); - - if (debug_smux) - zlog_debug("SMUX GET errstat %ld len: %zd", errstat, *len); - - /* Error index. */ - ptr = asn_parse_int(ptr, len, &type, &errindex, sizeof(errindex)); - - if (debug_smux) - zlog_debug("SMUX GET errindex %ld len: %zd", errindex, *len); - - return ptr; -} - -static void smux_parse_set(uint8_t *ptr, size_t len, int action) -{ - long reqid; - oid oid[MAX_OID_LEN]; - size_t oid_len; - uint8_t val_type; - void *val; - size_t val_len; - int ret; - - if (debug_smux) - zlog_debug("SMUX SET(%s) message parse: len %zd", - (RESERVE1 == action) - ? "RESERVE1" - : ((FREE == action) ? "FREE" : "COMMIT"), - len); - - /* Parse SET message header. */ - ptr = smux_parse_get_header(ptr, &len, &reqid); - - /* Parse SET message object ID. */ - ptr = smux_var(ptr, len, oid, &oid_len, &val_len, &val_type, &val); - - ret = smux_set(oid, &oid_len, val_type, val, val_len, action); - if (debug_smux) - zlog_debug("SMUX SET ret %d", ret); - - /* Return result. */ - if (RESERVE1 == action) - smux_getresp_send(oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, - 0); -} - -static void smux_parse_get(uint8_t *ptr, size_t len, int exact) -{ - long reqid; - oid oid[MAX_OID_LEN]; - size_t oid_len; - uint8_t val_type; - void *val; - size_t val_len; - int ret; - - if (debug_smux) - zlog_debug("SMUX GET message parse: len %zd", len); - - /* Parse GET message header. */ - ptr = smux_parse_get_header(ptr, &len, &reqid); - - /* Parse GET message object ID. We needn't the value come */ - ptr = smux_var(ptr, len, oid, &oid_len, NULL, NULL, NULL); - - /* Traditional getstatptr. */ - if (exact) - ret = smux_get(oid, &oid_len, exact, &val_type, &val, &val_len); - else - ret = smux_getnext(oid, &oid_len, exact, &val_type, &val, - &val_len); - - /* Return result. */ - if (ret == 0) - smux_getresp_send(oid, oid_len, reqid, 0, 0, val_type, val, - val_len); - else - smux_getresp_send(oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, - 0); -} - -/* Parse SMUX_CLOSE message. */ -static void smux_parse_close(uint8_t *ptr, int len) -{ - long reason = 0; - - while (len--) { - reason = (reason << 8) | (long)*ptr; - ptr++; - } - zlog_info("SMUX_CLOSE with reason: %ld", reason); -} - -/* SMUX_RRSP message. */ -static void smux_parse_rrsp(uint8_t *ptr, size_t len) -{ - uint8_t val; - long errstat; - - ptr = asn_parse_int(ptr, &len, &val, &errstat, sizeof(errstat)); - - if (debug_smux) - zlog_debug("SMUX_RRSP value: %d errstat: %ld", val, errstat); -} - -/* Parse SMUX message. */ -static int smux_parse(uint8_t *ptr, size_t len) -{ - /* This buffer we'll use for SOUT message. We could allocate it with - malloc and save only static pointer/lenght, but IMHO static - buffer is a faster solusion. */ - static uint8_t sout_save_buff[SMUXMAXPKTSIZE]; - static int sout_save_len = 0; - - int len_income = len; /* see note below: YYY */ - uint8_t type; - uint8_t rollback; - - rollback = ptr[2]; /* important only for SMUX_SOUT */ - -process_rest: /* see note below: YYY */ - - /* Parse SMUX message type and subsequent length. */ - ptr = asn_parse_header(ptr, &len, &type); - - if (debug_smux) - zlog_debug("SMUX message received type: %d rest len: %zd", type, - len); - - switch (type) { - case SMUX_OPEN: - /* Open must be not send from SNMP agent. */ - zlog_warn("SMUX_OPEN received: resetting connection."); - return -1; - break; - case SMUX_RREQ: - /* SMUX_RREQ message is invalid for us. */ - zlog_warn("SMUX_RREQ received: resetting connection."); - return -1; - break; - case SMUX_SOUT: - /* SMUX_SOUT message is now valied for us. */ - if (debug_smux) - zlog_debug("SMUX_SOUT(%s)", - rollback ? "rollback" : "commit"); - - if (sout_save_len > 0) { - smux_parse_set(sout_save_buff, sout_save_len, - rollback ? FREE : COMMIT); - sout_save_len = 0; - } else - zlog_warn("SMUX_SOUT sout_save_len=%d - invalid", - (int)sout_save_len); - - if (len_income > 3) { - /* YYY: this strange code has to solve the "slow peer" - problem: When agent sends SMUX_SOUT message it - doesn't - wait any responce and may send some next message to - subagent. Then the peer in 'smux_read()' will recieve - from socket the 'concatenated' buffer, contaning both - SMUX_SOUT message and the next one - (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: - if - the buffer is longer than 3 ( length of SMUX_SOUT ), - we - must process the rest of it. This effect may be - observed - if 'debug_smux' is set to '1' */ - ptr++; - len = len_income - 3; - goto process_rest; - } - break; - case SMUX_GETRSP: - /* SMUX_GETRSP message is invalid for us. */ - zlog_warn("SMUX_GETRSP received: resetting connection."); - return -1; - break; - case SMUX_CLOSE: - /* Close SMUX connection. */ - if (debug_smux) - zlog_debug("SMUX_CLOSE"); - smux_parse_close(ptr, len); - return -1; - break; - case SMUX_RRSP: - /* This is response for register message. */ - if (debug_smux) - zlog_debug("SMUX_RRSP"); - smux_parse_rrsp(ptr, len); - break; - case SMUX_GET: - /* Exact request for object id. */ - if (debug_smux) - zlog_debug("SMUX_GET"); - smux_parse_get(ptr, len, 1); - break; - case SMUX_GETNEXT: - /* Next request for object id. */ - if (debug_smux) - zlog_debug("SMUX_GETNEXT"); - smux_parse_get(ptr, len, 0); - break; - case SMUX_SET: - /* SMUX_SET is supported with some limitations. */ - if (debug_smux) - zlog_debug("SMUX_SET"); - - /* save the data for future SMUX_SOUT */ - memcpy(sout_save_buff, ptr, len); - sout_save_len = len; - smux_parse_set(ptr, len, RESERVE1); - break; - default: - zlog_info("Unknown type: %d", type); - break; - } - return 0; -} - -/* SMUX message read function. */ -static int smux_read(struct thread *t) -{ - int sock; - int len; - uint8_t buf[SMUXMAXPKTSIZE]; - int ret; - - /* Clear thread. */ - sock = THREAD_FD(t); - smux_read_thread = NULL; - - if (debug_smux) - zlog_debug("SMUX read start"); - - /* Read message from SMUX socket. */ - len = recv(sock, buf, SMUXMAXPKTSIZE, 0); - - if (len < 0) { - zlog_warn("Can't read all SMUX packet: %s", - safe_strerror(errno)); - close(sock); - smux_sock = -1; - smux_event(SMUX_CONNECT, 0); - return -1; - } - - if (len == 0) { - zlog_warn("SMUX connection closed: %d", sock); - close(sock); - smux_sock = -1; - smux_event(SMUX_CONNECT, 0); - return -1; - } - - if (debug_smux) - zlog_debug("SMUX read len: %d", len); - - /* Parse the message. */ - ret = smux_parse(buf, len); - - if (ret < 0) { - close(sock); - smux_sock = -1; - smux_event(SMUX_CONNECT, 0); - return -1; - } - - /* Regiser read thread. */ - smux_event(SMUX_READ, sock); - - return 0; -} - -static int smux_open(int sock) -{ - uint8_t buf[BUFSIZ]; - uint8_t *ptr; - size_t len; - long version; - const char progname[] = FRR_SMUX_NAME "-" FRR_VERSION; - - if (debug_smux) { - smux_oid_dump("SMUX open oid", smux_oid, smux_oid_len); - zlog_debug("SMUX open progname: %s", progname); - zlog_debug("SMUX open password: %s", smux_passwd); - } - - ptr = buf; - len = BUFSIZ; - - /* SMUX Header. As placeholder. */ - ptr = asn_build_header(ptr, &len, (uint8_t)SMUX_OPEN, 0); - - /* SMUX Open. */ - version = 0; - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - &version, sizeof(version)); - - /* SMUX connection oid. */ - ptr = asn_build_objid( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), - smux_oid, smux_oid_len); - - /* SMUX connection description. */ - ptr = asn_build_string( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), - (const uint8_t *)progname, strlen(progname)); - - /* SMUX connection password. */ - ptr = asn_build_string( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), - (uint8_t *)smux_passwd, strlen(smux_passwd)); - - /* Fill in real SMUX header. We exclude ASN header size (2). */ - len = BUFSIZ; - asn_build_header(buf, &len, (uint8_t)SMUX_OPEN, (ptr - buf) - 2); - - return send(sock, buf, (ptr - buf), 0); -} - -/* `ename` is ignored. Instead of using the provided enterprise OID, - the SMUX peer is used. This keep compatibility with the previous - versions of Quagga. - - All other fields are used as they are intended. */ -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, - uint8_t sptrap) -{ - unsigned int i; - uint8_t buf[BUFSIZ]; - uint8_t *ptr; - size_t len, length; - struct in_addr addr; - unsigned long val; - uint8_t *h1, *h1e; - - ptr = buf; - len = BUFSIZ; - length = len; - - /* When SMUX connection is not established. */ - if (smux_sock < 0) - return 0; - - /* SMUX header. */ - ptr = asn_build_header(ptr, &len, (uint8_t)SMUX_TRAP, 0); - - /* Sub agent enterprise oid. */ - ptr = asn_build_objid( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), - smux_oid, smux_oid_len); - - /* IP address. */ - addr.s_addr = 0; - ptr = asn_build_string( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS), - (uint8_t *)&addr, sizeof(addr)); - - /* Generic trap integer. */ - val = SNMP_TRAP_ENTERPRISESPECIFIC; - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - (long *)&val, sizeof(val)); - - /* Specific trap integer. */ - val = sptrap; - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - (long *)&val, sizeof(val)); - - /* Timeticks timestamp. */ - val = 0; - ptr = asn_build_unsigned_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS), &val, - sizeof(val)); - - /* Variables. */ - h1 = ptr; - ptr = asn_build_sequence(ptr, &len, - (uint8_t)(ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); - - - /* Iteration for each objects. */ - h1e = ptr; - for (i = 0; i < trapobjlen; i++) { - int ret; - oid oid[MAX_OID_LEN]; - size_t oid_len; - void *val; - size_t val_len; - uint8_t val_type; - - /* Make OID. */ - if (trapobj[i].namelen > 0) { - oid_copy(oid, name, namelen); - oid_copy(oid + namelen, trapobj[i].name, - trapobj[i].namelen); - oid_copy(oid + namelen + trapobj[i].namelen, iname, - inamelen); - oid_len = namelen + trapobj[i].namelen + inamelen; - } else { - oid_copy(oid, name, namelen); - oid_copy(oid + namelen, trapobj[i].name, - trapobj[i].namelen * (-1)); - oid_len = namelen + trapobj[i].namelen * (-1); - } - - if (debug_smux) { - smux_oid_dump("Trap", name, namelen); - if (trapobj[i].namelen < 0) - smux_oid_dump("Trap", trapobj[i].name, - (-1) * (trapobj[i].namelen)); - else { - smux_oid_dump("Trap", trapobj[i].name, - (trapobj[i].namelen)); - smux_oid_dump("Trap", iname, inamelen); - } - smux_oid_dump("Trap", oid, oid_len); - zlog_info("BUFSIZ: %d // oid_len: %lu", BUFSIZ, - (unsigned long)oid_len); - } - - ret = smux_get(oid, &oid_len, 1, &val_type, &val, &val_len); - - if (debug_smux) - zlog_debug("smux_get result %d", ret); - - if (ret == 0) - ptr = snmp_build_var_op(ptr, oid, &oid_len, val_type, - val_len, val, &len); - } - - /* Now variable size is known, fill in size */ - asn_build_sequence(h1, &length, - (uint8_t)(ASN_SEQUENCE | ASN_CONSTRUCTOR), - ptr - h1e); - - /* Fill in size of whole sequence */ - len = BUFSIZ; - asn_build_header(buf, &len, (uint8_t)SMUX_TRAP, (ptr - buf) - 2); - - return send(smux_sock, buf, (ptr - buf), 0); -} - -static int smux_register(int sock) -{ - uint8_t buf[BUFSIZ]; - uint8_t *ptr; - int ret; - size_t len; - long priority; - long operation; - struct subtree *subtree; - struct listnode *node, *nnode; - - ret = 0; - - for (ALL_LIST_ELEMENTS(treelist, node, nnode, subtree)) { - ptr = buf; - len = BUFSIZ; - - /* SMUX RReq Header. */ - ptr = asn_build_header(ptr, &len, (uint8_t)SMUX_RREQ, 0); - - /* Register MIB tree. */ - ptr = asn_build_objid(ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE - | ASN_OBJECT_ID), - subtree->name, subtree->name_len); - - /* Priority. */ - priority = -1; - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - &priority, sizeof(priority)); - - /* Operation. */ - operation = 2; /* Register R/W */ - ptr = asn_build_int( - ptr, &len, - (uint8_t)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), - &operation, sizeof(operation)); - - if (debug_smux) { - smux_oid_dump("SMUX register oid", subtree->name, - subtree->name_len); - zlog_debug("SMUX register priority: %ld", priority); - zlog_debug("SMUX register operation: %ld", operation); - } - - len = BUFSIZ; - asn_build_header(buf, &len, (uint8_t)SMUX_RREQ, - (ptr - buf) - 2); - ret = send(sock, buf, (ptr - buf), 0); - if (ret < 0) - return ret; - } - return ret; -} - -/* Try to connect to SNMP agent. */ -static int smux_connect(struct thread *t) -{ - int ret; - - if (debug_smux) - zlog_debug("SMUX connect try %d", fail + 1); - - /* Clear thread poner of myself. */ - smux_connect_thread = NULL; - - /* Make socket. Try to connect. */ - smux_sock = smux_socket(); - if (smux_sock < 0) { - if (++fail < SMUX_MAX_FAILURE) - smux_event(SMUX_CONNECT, 0); - return 0; - } - - /* Send OPEN PDU. */ - ret = smux_open(smux_sock); - if (ret < 0) { - zlog_warn("SMUX open message send failed: %s", - safe_strerror(errno)); - close(smux_sock); - smux_sock = -1; - if (++fail < SMUX_MAX_FAILURE) - smux_event(SMUX_CONNECT, 0); - return -1; - } - - /* Send any outstanding register PDUs. */ - ret = smux_register(smux_sock); - if (ret < 0) { - zlog_warn("SMUX register message send failed: %s", - safe_strerror(errno)); - close(smux_sock); - smux_sock = -1; - if (++fail < SMUX_MAX_FAILURE) - smux_event(SMUX_CONNECT, 0); - return -1; - } - - /* Everything goes fine. */ - smux_event(SMUX_READ, smux_sock); - - return 0; -} - -/* Clear all SMUX related resources. */ -static void smux_stop(void) -{ - if (smux_read_thread) { - thread_cancel(smux_read_thread); - smux_read_thread = NULL; - } - - if (smux_connect_thread) { - thread_cancel(smux_connect_thread); - smux_connect_thread = NULL; - } - - if (smux_sock >= 0) { - close(smux_sock); - smux_sock = -1; - } -} - - -void smux_event(enum smux_event event, int sock) -{ - switch (event) { - case SMUX_SCHEDULE: - smux_connect_thread = NULL; - thread_add_event(smux_master, smux_connect, NULL, 0, - &smux_connect_thread); - break; - case SMUX_CONNECT: - smux_connect_thread = NULL; - thread_add_timer(smux_master, smux_connect, NULL, 10, - &smux_connect_thread); - break; - case SMUX_READ: - smux_read_thread = NULL; - thread_add_read(smux_master, smux_read, NULL, sock, - &smux_read_thread); - break; - default: - break; - } -} - -static int smux_str2oid(const char *str, oid *oid, size_t *oid_len) -{ - int len; - int val; - - len = 0; - val = 0; - *oid_len = 0; - - if (*str == '.') - str++; - if (*str == '\0') - return 0; - - while (1) { - if (!isdigit(*str)) - return -1; - - while (isdigit(*str)) { - val *= 10; - val += (*str - '0'); - str++; - } - - if (*str == '\0') - break; - if (*str != '.') - return -1; - - oid[len++] = val; - val = 0; - str++; - } - - oid[len++] = val; - *oid_len = len; - - return 0; -} - -static oid *smux_oid_dup(oid *objid, size_t objid_len) -{ - oid *new; - - new = XMALLOC(MTYPE_TMP, sizeof(oid) * objid_len); - oid_copy(new, objid, objid_len); - - return new; -} - -static int smux_peer_oid(struct vty *vty, const char *oid_str, - const char *passwd_str) -{ - int ret; - oid oid[MAX_OID_LEN]; - size_t oid_len; - - ret = smux_str2oid(oid_str, oid, &oid_len); - if (ret != 0) { - vty_out(vty, "object ID malformed\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (smux_oid) { - free(smux_oid); - smux_oid = NULL; - } - - /* careful, smux_passwd might point to string constant */ - if (smux_passwd) { - free(smux_passwd); - smux_passwd = NULL; - } - - smux_oid = smux_oid_dup(oid, oid_len); - smux_oid_len = oid_len; - - if (passwd_str) - smux_passwd = strdup(passwd_str); - else - smux_passwd = strdup(""); - - return 0; -} - -static int smux_peer_default(void) -{ - if (smux_oid) { - free(smux_oid); - smux_oid = NULL; - } - - /* careful, smux_passwd might be pointing at string constant */ - if (smux_passwd) { - free(smux_passwd); - smux_passwd = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN (smux_peer, - smux_peer_cmd, - "smux peer OID", - "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n" - "Object ID used in SMUX peering\n") -{ - int idx_oid = 2; - if (smux_peer_oid(vty, argv[idx_oid]->arg, NULL) == 0) { - smux_start(); - return CMD_SUCCESS; - } else - return CMD_WARNING_CONFIG_FAILED; -} - -DEFUN (smux_peer_password, - smux_peer_password_cmd, - "smux peer OID PASSWORD", - "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n" - "SMUX peering object ID\n" - "SMUX peering password\n") -{ - int idx_oid = 2; - if (smux_peer_oid(vty, argv[idx_oid]->arg, argv[3]->rg) == 0) { - smux_start(); - return CMD_SUCCESS; - } else - return CMD_WARNING_CONFIG_FAILED; -} - -DEFUN (no_smux_peer, - no_smux_peer_cmd, - "no smux peer [OID [PASSWORD]]", - NO_STR - "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n" - "SMUX peering object ID\n" - "SMUX peering password\n") -{ - smux_stop(); - return smux_peer_default(); -} - -static int config_write_smux(struct vty *vty) -{ - int first = 1; - unsigned int i; - - if (smux_oid) { - vty_out(vty, "smux peer "); - for (i = 0; i < smux_oid_len; i++) { - vty_out(vty, "%s%d", first ? "" : ".", - (int)smux_oid[i]); - first = 0; - } - vty_out(vty, " %s\n", smux_passwd); - } - return 0; -} - -/* Register subtree to smux master tree. */ -void smux_register_mib(const char *descr, struct variable *var, size_t width, - int num, oid name[], size_t namelen) -{ - struct subtree *tree; - - tree = (struct subtree *)malloc(sizeof(struct subtree)); - oid_copy(tree->name, name, namelen); - tree->name_len = namelen; - tree->variables = var; - tree->variables_num = num; - tree->variables_width = width; - tree->registered = 0; - listnode_add_sort(treelist, tree); -} - -/* Compare function to keep treelist sorted */ -static int smux_tree_cmp(struct subtree *tree1, struct subtree *tree2) -{ - return oid_compare(tree1->name, tree1->name_len, tree2->name, - tree2->name_len); -} - -/* Initialize some values then schedule first SMUX connection. */ -void smux_init(struct thread_master *tm) -{ - assert(tm); - /* copy callers thread master */ - smux_master = tm; - - /* Make MIB tree. */ - treelist = list_new(); - treelist->cmp = (int (*)(void *, void *))smux_tree_cmp; - - /* Install commands. */ - install_node(&smux_node, config_write_smux); - - install_element(CONFIG_NODE, &smux_peer_cmd); - install_element(CONFIG_NODE, &smux_peer_password_cmd); - install_element(CONFIG_NODE, &no_smux_peer_cmd); - install_element(CONFIG_NODE, &no_smux_peer_oid_cmd); - install_element(CONFIG_NODE, &no_smux_peer_oid_password_cmd); -} - -void smux_start(void) -{ - /* Close any existing connections. */ - smux_stop(); - - /* Schedule first connection. */ - smux_event(SMUX_SCHEDULE, 0); -} -#endif /* SNMP_SMUX */ diff --git a/lib/subdir.am b/lib/subdir.am index b938dbcea34a..50cfd70a571f 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -150,7 +150,6 @@ pkginclude_HEADERS += \ lib/sha256.h \ lib/sigevent.h \ lib/skiplist.h \ - lib/smux.h \ lib/sockopt.h \ lib/sockunion.h \ lib/spf_backoff.h \ @@ -199,7 +198,6 @@ lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0 lib_libfrrsnmp_la_LIBADD = lib/libfrr.la $(SNMP_LIBS) lib_libfrrsnmp_la_SOURCES = \ lib/agentx.c \ - lib/smux.c \ lib/snmp.c \ # end