mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 03:13:44 +00:00 
			
		
		
		
	The following types are nonstandard: - u_char - u_short - u_int - u_long - u_int8_t - u_int16_t - u_int32_t Replace them with the C99 standard types: - uint8_t - unsigned short - unsigned int - unsigned long - uint8_t - uint16_t - uint32_t Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
		
			
				
	
	
		
			1417 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1417 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SNMP support
 | 
						|
 * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
 | 
						|
 *
 | 
						|
 * 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 <zebra.h>
 | 
						|
 | 
						|
#ifdef SNMP_SMUX
 | 
						|
#include <net-snmp/net-snmp-config.h>
 | 
						|
#include <net-snmp/net-snmp-includes.h>
 | 
						|
 | 
						|
#include "log.h"
 | 
						|
#include "thread.h"
 | 
						|
#include "linklist.h"
 | 
						|
#include "command.h"
 | 
						|
#include <lib/version.h>
 | 
						|
#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 */
 |