xref: /onnv-gate/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c (revision 4085:f5fd2bc7debd)
13941Svenki /*
23941Svenki  * CDDL HEADER START
33941Svenki  *
43941Svenki  * The contents of this file are subject to the terms of the
53941Svenki  * Common Development and Distribution License (the "License").
63941Svenki  * You may not use this file except in compliance with the License.
73941Svenki  *
83941Svenki  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93941Svenki  * or http://www.opensolaris.org/os/licensing.
103941Svenki  * See the License for the specific language governing permissions
113941Svenki  * and limitations under the License.
123941Svenki  *
133941Svenki  * When distributing Covered Code, include this CDDL HEADER in each
143941Svenki  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153941Svenki  * If applicable, add the following below this CDDL HEADER, with the
163941Svenki  * fields enclosed by brackets "[]" replaced with your own identifying
173941Svenki  * information: Portions Copyright [yyyy] [name of copyright owner]
183941Svenki  *
193941Svenki  * CDDL HEADER END
203941Svenki  */
213941Svenki 
223941Svenki /*
233941Svenki  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243941Svenki  * Use is subject to license terms.
253941Svenki  */
263941Svenki 
273941Svenki #pragma ident	"%Z%%M%	%I%	%E% SMI"
283941Svenki 
293941Svenki /*
303941Svenki  * SNMP PDU and packet transport related routines
313941Svenki  */
323941Svenki 
333941Svenki #include <stdio.h>
343941Svenki #include <stdlib.h>
353941Svenki #include <string.h>
363941Svenki #include <sys/types.h>
373941Svenki #include "asn1.h"
383941Svenki #include "pdu.h"
393941Svenki #include "debug.h"
403941Svenki 
413941Svenki /*
423941Svenki  * Static declarations
433941Svenki  */
443941Svenki static int	snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
453941Svenki static oid	*snmp_oidstr_to_oid(int, char *, int, size_t *);
463941Svenki static uchar_t	*snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
473941Svenki static uchar_t	*snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
483941Svenki 		    uchar_t, void *, size_t);
493941Svenki static uchar_t	*snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
503941Svenki static uchar_t	*snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
51*4085Svenki static void	snmp_free_null_vars(pdu_varlist_t *);
52*4085Svenki 
53*4085Svenki static uchar_t *snmp_def_community = (uchar_t *)SNMP_DEF_COMMUNITY;
543941Svenki 
553941Svenki /*
563941Svenki  * Allocates and creates a PDU for the specified SNMP command. Currently
573941Svenki  * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
583941Svenki  */
593941Svenki snmp_pdu_t *
snmp_create_pdu(int cmd,int max_reps,char * oidstrs,int n_oids,int row)603941Svenki snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
613941Svenki {
623941Svenki 	snmp_pdu_t	*pdu;
633941Svenki 
643941Svenki 	if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
653941Svenki 	    (cmd != SNMP_MSG_GETBULK)) {
663941Svenki 		return (NULL);
673941Svenki 	}
683941Svenki 
693941Svenki 	pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
703941Svenki 	if (pdu == NULL)
713941Svenki 		return (NULL);
723941Svenki 
733941Svenki 	if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
743941Svenki 		pdu->version = SNMP_VERSION_1;
753941Svenki 		pdu->errstat = 0;
763941Svenki 		pdu->errindex = 0;
773941Svenki 	} else if (cmd == SNMP_MSG_GETBULK) {
783941Svenki 		pdu->version = SNMP_VERSION_2c;
793941Svenki 		pdu->non_repeaters = 0;
803941Svenki 		pdu->max_repetitions = max_reps ?
813941Svenki 		    max_reps : SNMP_DEF_MAX_REPETITIONS;
823941Svenki 	}
833941Svenki 
843941Svenki 	pdu->command = cmd;
853941Svenki 	pdu->reqid = snmp_get_reqid();
86*4085Svenki 	pdu->community = snmp_def_community;
873941Svenki 	pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
883941Svenki 
893941Svenki 	if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
903941Svenki 		free((void *) pdu);
913941Svenki 		return (NULL);
923941Svenki 	}
933941Svenki 
943941Svenki 	pdu->req_pkt = NULL;
953941Svenki 	pdu->req_pktsz = 0;
963941Svenki 	pdu->reply_pkt = NULL;
973941Svenki 	pdu->reply_pktsz = 0;
983941Svenki 
993941Svenki 	return (pdu);
1003941Svenki }
1013941Svenki 
1023941Svenki /*
1033941Svenki  * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
1043941Svenki  * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
1053941Svenki  * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
1063941Svenki  * as long as the number of bulk oids are not *too* many, we're safe with
1073941Svenki  * this limit (the typical packet size of a bulk request of 10 vars is
1083941Svenki  * around 250 bytes).
1093941Svenki  */
1103941Svenki int
snmp_make_packet(snmp_pdu_t * pdu)1113941Svenki snmp_make_packet(snmp_pdu_t *pdu)
1123941Svenki {
1133941Svenki 	uchar_t	*buf, *p;
1143941Svenki 	uchar_t	*msg_seq_end;
1153941Svenki 	uchar_t id;
1163941Svenki 	size_t	bufsz = SNMP_DEF_PKTBUF_SZ;
1173941Svenki 	size_t	seqlen;
1183941Svenki 
1193941Svenki 	if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
1203941Svenki 		return (-1);
1213941Svenki 
1223941Svenki 	/*
1233941Svenki 	 * Let's start with the ASN sequence tag. Set the length
1243941Svenki 	 * to 0 initially and fill it up once the message packetizing
1253941Svenki 	 * is complete.
1263941Svenki 	 */
1273941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1283941Svenki 	if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
1293941Svenki 		free((void *) buf);
1303941Svenki 		return (-1);
1313941Svenki 	}
1323941Svenki 	msg_seq_end = p;
1333941Svenki 
1343941Svenki 	/*
1353941Svenki 	 * Store the version
1363941Svenki 	 */
1373941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
1383941Svenki 	if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
1393941Svenki 		free((void *) buf);
1403941Svenki 		return (-1);
1413941Svenki 	}
1423941Svenki 
1433941Svenki 	/*
1443941Svenki 	 * Store the community string
1453941Svenki 	 */
1463941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
1473941Svenki 	p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
1483941Svenki 	if (p == NULL) {
1493941Svenki 		free((void *) buf);
1503941Svenki 		return (-1);
1513941Svenki 	}
1523941Svenki 
1533941Svenki 	/*
1543941Svenki 	 * Build the PDU
1553941Svenki 	 */
1563941Svenki 	if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
1573941Svenki 		free((void *) buf);
1583941Svenki 		return (-1);
1593941Svenki 	}
1603941Svenki 
1613941Svenki 	/*
1623941Svenki 	 * Complete the message pkt by updating the message sequence length
1633941Svenki 	 */
1643941Svenki 	seqlen = p - msg_seq_end;
1653941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1663941Svenki 	(void) asn_build_sequence(buf, NULL, id, seqlen);
1673941Svenki 
1683941Svenki 	/*
1693941Svenki 	 * Calculate packet size and return
1703941Svenki 	 */
1713941Svenki 	pdu->req_pkt = buf;
1723941Svenki 	pdu->req_pktsz = p - buf;
1733941Svenki 
1743941Svenki 	return (0);
1753941Svenki }
1763941Svenki 
1773941Svenki /*
1783941Svenki  * Makes a PDU out of a reply packet. The reply message is parsed
1793941Svenki  * and if the reqid of the incoming packet does not match the reqid
1803941Svenki  * we're waiting for, an error is returned. The PDU is allocated
1813941Svenki  * inside this routine and must be freed by the caller once it is no
1823941Svenki  * longer needed.
1833941Svenki  */
1843941Svenki snmp_pdu_t *
snmp_parse_reply(int reqid,uchar_t * reply_pkt,size_t reply_pktsz)1853941Svenki snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
1863941Svenki {
1873941Svenki 	snmp_pdu_t	*reply_pdu;
1883941Svenki 	uchar_t		*p;
1893941Svenki 	size_t		msgsz = reply_pktsz;
1903941Svenki 	uchar_t		exp_id;
1913941Svenki 
1923941Svenki 	reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
1933941Svenki 	if (reply_pdu == NULL)
1943941Svenki 		return (NULL);
1953941Svenki 
1963941Svenki 	/*
1973941Svenki 	 * Try to parse the ASN sequence out of the beginning of the reply
1983941Svenki 	 * packet. If we don't find a sequence at the beginning, something's
1993941Svenki 	 * wrong.
2003941Svenki 	 */
2013941Svenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
2023941Svenki 	if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
2033941Svenki 		snmp_free_pdu(reply_pdu);
2043941Svenki 		return (NULL);
2053941Svenki 	}
2063941Svenki 
2073941Svenki 	/*
2083941Svenki 	 * Now try to parse the version out of the packet
2093941Svenki 	 */
2103941Svenki 	if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
2113941Svenki 		snmp_free_pdu(reply_pdu);
2123941Svenki 		return (NULL);
2133941Svenki 	}
2143941Svenki 	if ((reply_pdu->version != SNMP_VERSION_1) &&
2153941Svenki 	    (reply_pdu->version != SNMP_VERSION_2c)) {
2163941Svenki 		snmp_free_pdu(reply_pdu);
2173941Svenki 		return (NULL);
2183941Svenki 	}
2193941Svenki 
2203941Svenki 	/*
2213941Svenki 	 * Parse the community string (space allocated by asn_parse_string)
2223941Svenki 	 */
2233941Svenki 	p = asn_parse_string(p, &msgsz, &reply_pdu->community,
2243941Svenki 	    &reply_pdu->community_len);
2253941Svenki 	if (p == NULL) {
2263941Svenki 		snmp_free_pdu(reply_pdu);
2273941Svenki 		return (NULL);
2283941Svenki 	}
2293941Svenki 
2303941Svenki 	/*
2313941Svenki 	 * Parse the PDU part of the message
2323941Svenki 	 */
2333941Svenki 	if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
2343941Svenki 		snmp_free_pdu(reply_pdu);
2353941Svenki 		return (NULL);
2363941Svenki 	}
2373941Svenki 
2383941Svenki 	return (reply_pdu);
2393941Svenki }
2403941Svenki 
2413941Svenki 
2423941Svenki /*
2433941Svenki  * Convert the OID strings into the standard PDU oid form (sequence of
2443941Svenki  * integer subids) and add them to the PDU's variable list. Note that
2453941Svenki  * this is used only for preparing the request messages (GET, GETNEXT
2463941Svenki  * and GETBULK), so the values of the variables are always null.
2473941Svenki  */
2483941Svenki static int
snmp_add_null_vars(snmp_pdu_t * pdu,char * oidstrs,int n_oids,int row)2493941Svenki snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
2503941Svenki {
2513941Svenki 	pdu_varlist_t	*vp, *prev;
252*4085Svenki 	pdu_varlist_t	*varblock_p = NULL;
2533941Svenki 	char	*p;
2543941Svenki 	int	i;
2553941Svenki 
2563941Svenki 	prev = NULL;
2573941Svenki 	p = oidstrs;
2583941Svenki 	for (i = 0; i < n_oids; i++) {
259*4085Svenki 		if ((vp = calloc(1, sizeof (pdu_varlist_t))) == NULL) {
260*4085Svenki 			snmp_free_null_vars(varblock_p);
261*4085Svenki 			return (-1);
262*4085Svenki 		} else if (i == 0) {
263*4085Svenki 			varblock_p = vp;
264*4085Svenki 		} else {
265*4085Svenki 			prev->nextvar = vp;
266*4085Svenki 		}
267*4085Svenki 
2683941Svenki 		vp->name = snmp_oidstr_to_oid(pdu->command,
2693941Svenki 		    p, row, &vp->name_len);
2703941Svenki 		if (vp->name == NULL) {
271*4085Svenki 			snmp_free_null_vars(varblock_p);
2723941Svenki 			return (-1);
2733941Svenki 		}
2743941Svenki 		vp->val.str = NULL;
2753941Svenki 		vp->val_len = 0;
2763941Svenki 		vp->type = ASN_NULL;
277*4085Svenki 		vp->nextvar = NULL;
2783941Svenki 
2793941Svenki 		LOGVAR(TAG_NULL_VAR, vp);
2803941Svenki 
2813941Svenki 		prev = vp;
2823941Svenki 		p += strlen(p) + 1;
2833941Svenki 	}
2843941Svenki 
2853941Svenki 	/*
2863941Svenki 	 * append the varlist to the PDU
2873941Svenki 	 */
2883941Svenki 	if (pdu->vars == NULL)
2893941Svenki 		pdu->vars = varblock_p;
2903941Svenki 	else {
2913941Svenki 		for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
2923941Svenki 			;
2933941Svenki 		vp->nextvar = varblock_p;
2943941Svenki 	}
2953941Svenki 
2963941Svenki 	return (0);
2973941Svenki }
298*4085Svenki 
2993941Svenki /*
3003941Svenki  * Some assumptions are in place here to eliminate unnecessary complexity.
3013941Svenki  * All OID strings passed are assumed to be in the numeric string form, have
3023941Svenki  * no leading/trailing '.' or spaces. Since PICL plugin is currently the
3033941Svenki  * only customer, this is quite reasonable.
3043941Svenki  */
3053941Svenki static oid *
snmp_oidstr_to_oid(int cmd,char * oidstr,int row,size_t * n_subids)3063941Svenki snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
3073941Svenki {
3083941Svenki 	int	i, count;
3093941Svenki 	char	*p, *q;
3103941Svenki 	char	*oidstr_dup;
3113941Svenki 	oid	*objid;
3123941Svenki 
3133941Svenki 	if ((oidstr == NULL) || (n_subids == NULL))
3143941Svenki 		return (NULL);
3153941Svenki 
3163941Svenki 	for (count = 1, p = oidstr; p; count++, p++) {
3173941Svenki 		if ((p = strchr(p, '.')) == NULL)
3183941Svenki 			break;
3193941Svenki 	}
3203941Svenki 
3213941Svenki 	/*
3223941Svenki 	 * Add one more to count for 'row'. Need special processing
3233941Svenki 	 * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
3243941Svenki 	 * comment below.
3253941Svenki 	 */
3263941Svenki 	if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
3273941Svenki 	    (cmd == SNMP_MSG_GETNEXT && row >= 0)) {
3283941Svenki 		count++;
3293941Svenki 	}
3303941Svenki 
3313941Svenki 	if ((oidstr_dup = strdup(oidstr)) == NULL)
3323941Svenki 		return (NULL);
3333941Svenki 
3343941Svenki 	objid = (oid *) calloc(count, sizeof (oid));
3353941Svenki 	if (objid == NULL) {
3363941Svenki 		free((void *) p);
3373941Svenki 		return (NULL);
3383941Svenki 	}
3393941Svenki 
3403941Svenki 	p = oidstr_dup;
3413941Svenki 	for (i = 0; i < count - 1; i++) {
3423941Svenki 		if (q = strchr(p, '.'))
3433941Svenki 			*q = 0;
3443941Svenki 		objid[i] = (oid) strtoul(p, NULL, 10);
3453941Svenki 		p = q + 1;
3463941Svenki 	}
3473941Svenki 
3483941Svenki 	/*
3493941Svenki 	 * For SNMP_MSG_GET, the leaf subid will simply be the row#.
3503941Svenki 	 *
3513941Svenki 	 * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
3523941Svenki 	 * we pass 'row-1' as the leaf subid, to include the item that
3533941Svenki 	 * is of interest to us. If the row# is less than or equal to 0,
3543941Svenki 	 * we will simply ignore it and pass only the prefix part of the
3553941Svenki 	 * oidstr. For this case, our count would have been 1 less than
3563941Svenki 	 * usual, and we are yet to save the last subid.
3573941Svenki 	 *
3583941Svenki 	 * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
3593941Svenki 	 * we'll simply ignore it and pass only the prefix part of the
3603941Svenki 	 * oidstr. For this case, our count would have been 1 less than
3613941Svenki 	 * usual, and we are yet to save the last subid. If the row#
3623941Svenki 	 * passed is greater than or equal to 0, we'll simply pass it
3633941Svenki 	 * verbatim, as the leaf subid.
3643941Svenki 	 */
3653941Svenki 	switch (cmd) {
3663941Svenki 	case SNMP_MSG_GET:
3673941Svenki 		objid[i] = (oid) row;
3683941Svenki 		break;
3693941Svenki 
3703941Svenki 	case SNMP_MSG_GETBULK:
3713941Svenki 		if (row > 0)
3723941Svenki 			objid[i] = (oid) (row - 1);
3733941Svenki 		else
3743941Svenki 			objid[i] = (oid) strtoul(p, NULL, 10);
3753941Svenki 		break;
3763941Svenki 
3773941Svenki 	case SNMP_MSG_GETNEXT:
3783941Svenki 		if (row < 0)
3793941Svenki 			objid[i] = (oid) strtoul(p, NULL, 10);
3803941Svenki 		else
3813941Svenki 			objid[i] = (oid) row;
3823941Svenki 		break;
3833941Svenki 	}
3843941Svenki 
3853941Svenki 	*n_subids = count;
3863941Svenki 
3873941Svenki 	free((void *) oidstr_dup);
3883941Svenki 
3893941Svenki 	return (objid);
3903941Svenki }
3913941Svenki 
3923941Svenki /*
3933941Svenki  * Builds the PDU part of the snmp message packet.
3943941Svenki  */
3953941Svenki static uchar_t *
snmp_build_pdu(snmp_pdu_t * pdu,uchar_t * buf,size_t * bufsz_p)3963941Svenki snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
3973941Svenki {
3983941Svenki 	uchar_t	*p;
3993941Svenki 	uchar_t	*pdu_seq_begin, *pdu_seq_end;
4003941Svenki 	uchar_t	*varlist_seq_begin, *varlist_seq_end;
4013941Svenki 	uchar_t	id;
4023941Svenki 	size_t	seqlen;
4033941Svenki 	pdu_varlist_t	*vp;
4043941Svenki 
4053941Svenki 	/*
4063941Svenki 	 * Build ASN sequence for the PDU command (length will be
4073941Svenki 	 * updated later once the entire command is completely formed)
4083941Svenki 	 */
4093941Svenki 	pdu_seq_begin = buf;
4103941Svenki 	p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
4113941Svenki 	if (p == NULL)
4123941Svenki 		return (NULL);
4133941Svenki 	pdu_seq_end = p;
4143941Svenki 
4153941Svenki 	/*
4163941Svenki 	 * Build the request id
4173941Svenki 	 */
4183941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4193941Svenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
4203941Svenki 		return (NULL);
4213941Svenki 
4223941Svenki 	/*
4233941Svenki 	 * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
4243941Svenki 	 * (same as error status and error index for other message types)
4253941Svenki 	 */
4263941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4273941Svenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
4283941Svenki 		return (NULL);
4293941Svenki 
4303941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4313941Svenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
4323941Svenki 		return (NULL);
4333941Svenki 
4343941Svenki 	/*
4353941Svenki 	 * Build ASN sequence for the variables list (update length
4363941Svenki 	 * after building the varlist)
4373941Svenki 	 */
4383941Svenki 	varlist_seq_begin = p;
4393941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4403941Svenki 	if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
4413941Svenki 		return (NULL);
4423941Svenki 	varlist_seq_end = p;
4433941Svenki 
4443941Svenki 	/*
4453941Svenki 	 * Build the variables list
4463941Svenki 	 */
4473941Svenki 	for (vp = pdu->vars; vp; vp = vp->nextvar) {
4483941Svenki 		p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
4493941Svenki 		    vp->type, vp->val.str, vp->val_len);
4503941Svenki 		if (p == NULL)
4513941Svenki 			return (NULL);
4523941Svenki 	}
4533941Svenki 
4543941Svenki 	/*
4553941Svenki 	 * Now update the varlist sequence length
4563941Svenki 	 */
4573941Svenki 	seqlen = p - varlist_seq_end;
4583941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4593941Svenki 	(void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
4603941Svenki 
4613941Svenki 	/*
4623941Svenki 	 * And finally, update the length for the PDU sequence
4633941Svenki 	 */
4643941Svenki 	seqlen = p - pdu_seq_end;
4653941Svenki 	(void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
4663941Svenki 	    seqlen);
4673941Svenki 
4683941Svenki 	return (p);
4693941Svenki }
4703941Svenki 
4713941Svenki /*
4723941Svenki  * Builds an object variable into the snmp message packet. Although the
4733941Svenki  * code is here to build variables of basic types such as integer, object id
4743941Svenki  * and strings, the only type of variable we ever send via snmp request
4753941Svenki  * messages is the ASN_NULL type.
4763941Svenki  */
4773941Svenki static uchar_t *
snmp_build_variable(uchar_t * buf,size_t * bufsz_p,oid * name,size_t name_len,uchar_t val_type,void * val,size_t val_len)4783941Svenki snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
4793941Svenki     uchar_t val_type, void *val, size_t val_len)
4803941Svenki {
4813941Svenki 	uchar_t	*p, *varseq_end;
4823941Svenki 	size_t	seqlen;
4833941Svenki 	uchar_t	id;
4843941Svenki 
4853941Svenki 	/*
4863941Svenki 	 * Each variable binding is in turn defined as a 'SEQUENCE of' by
4873941Svenki 	 * the SNMP PDU format, so we'll prepare the sequence and fill up
4883941Svenki 	 * the length later. Sigh!
4893941Svenki 	 */
4903941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4913941Svenki 	if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
4923941Svenki 		return (NULL);
4933941Svenki 	varseq_end = p;
4943941Svenki 
4953941Svenki 	/*
4963941Svenki 	 * Build the object id
4973941Svenki 	 */
4983941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
4993941Svenki 	if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
5003941Svenki 		return (NULL);
5013941Svenki 
5023941Svenki 	/*
5033941Svenki 	 * Currently we only ever build ASN_NULL vars while sending requests,
5043941Svenki 	 * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
5053941Svenki 	 * SNMP_MSG_GETBULK.
5063941Svenki 	 */
5073941Svenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
5083941Svenki 	switch (val_type) {
5093941Svenki 	case ASN_INTEGER:
5103941Svenki 		p = asn_build_int(p, bufsz_p, id, *((int *)val));
5113941Svenki 		if (p == NULL)
5123941Svenki 			return (NULL);
5133941Svenki 		break;
5143941Svenki 
5153941Svenki 	case ASN_OBJECT_ID:
5163941Svenki 		p = asn_build_objid(p, bufsz_p, id, val,
5173941Svenki 		    val_len / sizeof (oid));
5183941Svenki 		if (p == NULL)
5193941Svenki 			return (NULL);
5203941Svenki 		break;
5213941Svenki 
5223941Svenki 	case ASN_OCTET_STR:
5233941Svenki 		p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
5243941Svenki 		if (p == NULL)
5253941Svenki 			return (NULL);
5263941Svenki 		break;
5273941Svenki 
5283941Svenki 	case ASN_NULL:
5293941Svenki 		if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
5303941Svenki 			return (NULL);
5313941Svenki 		break;
5323941Svenki 
5333941Svenki 	default:
5343941Svenki 		return (NULL);
5353941Svenki 	}
5363941Svenki 
5373941Svenki 	/*
5383941Svenki 	 * Rebuild the variable sequence length
5393941Svenki 	 */
5403941Svenki 	seqlen = p - varseq_end;
5413941Svenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5423941Svenki 	(void) asn_build_sequence(buf, NULL, id, seqlen);
5433941Svenki 
5443941Svenki 	return (p);
5453941Svenki }
5463941Svenki 
5473941Svenki /*
5483941Svenki  * Parse the PDU portion of the incoming snmp message into the reply_pdu.
5493941Svenki  * Space for all structure members are allocated as needed and must be freed
5503941Svenki  * by the caller when these are no longer needed.
5513941Svenki  */
5523941Svenki static uchar_t *
snmp_parse_pdu(int reqid,uchar_t * msg,size_t * msgsz_p,snmp_pdu_t * reply_pdu)5533941Svenki snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
5543941Svenki {
5553941Svenki 	uchar_t	*p;
5563941Svenki 	uchar_t	id, exp_id;
5573941Svenki 	pdu_varlist_t	*newvp, *vp = NULL;
5583941Svenki 
5593941Svenki 	/*
5603941Svenki 	 * Parse the PDU header out of the message
5613941Svenki 	 */
5623941Svenki 	if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
5633941Svenki 		return (NULL);
5643941Svenki 	if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
5653941Svenki 		return (NULL);
5663941Svenki 	reply_pdu->command = (int)id;
5673941Svenki 
5683941Svenki 	/*
5693941Svenki 	 * Parse the request id and verify that this is the response
5703941Svenki 	 * we're expecting.
5713941Svenki 	 */
5723941Svenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
5733941Svenki 		return (NULL);
5743941Svenki 	if (reply_pdu->reqid != reqid)
5753941Svenki 		return (NULL);
5763941Svenki 
5773941Svenki 	/*
5783941Svenki 	 * Parse the error-status and error-index values
5793941Svenki 	 */
5803941Svenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
5813941Svenki 		return (NULL);
5823941Svenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
5833941Svenki 		return (NULL);
5843941Svenki 
5853941Svenki 	/*
5863941Svenki 	 * Parse the header for the variables list sequence.
5873941Svenki 	 */
5883941Svenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5893941Svenki 	if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
5903941Svenki 		return (NULL);
5913941Svenki 
5923941Svenki 	while (((int)*msgsz_p) > 0) {
5933941Svenki 		if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
5943941Svenki 			return (NULL);
5953941Svenki 
5963941Svenki 		if (vp == NULL)
5973941Svenki 			reply_pdu->vars = newvp;
5983941Svenki 		else
5993941Svenki 			vp->nextvar = newvp;
6003941Svenki 
6013941Svenki 		vp = newvp;
6023941Svenki 		if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
6033941Svenki 			return (NULL);
6043941Svenki 
6053941Svenki 		LOGVAR(TAG_RESPONSE_VAR, vp);
6063941Svenki 	}
6073941Svenki 
6083941Svenki 	return (p);
6093941Svenki }
6103941Svenki 
6113941Svenki /*
6123941Svenki  * Allocate and parse the next variable into the varlist
6133941Svenki  */
6143941Svenki static uchar_t *
snmp_parse_variable(uchar_t * msg,size_t * msgsz_p,pdu_varlist_t * vp)6153941Svenki snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
6163941Svenki {
6173941Svenki 	uchar_t	*p;
6183941Svenki 	uchar_t	exp_id;
6193941Svenki 
6203941Svenki 	/*
6213941Svenki 	 * Parse this variable's sequence
6223941Svenki 	 */
6233941Svenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
6243941Svenki 	if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
6253941Svenki 		return (NULL);
6263941Svenki 
6273941Svenki 	/*
6283941Svenki 	 * Parse the variable's object identifier
6293941Svenki 	 */
6303941Svenki 	p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
6313941Svenki 	if (p == NULL)
6323941Svenki 		return (NULL);
6333941Svenki 
6343941Svenki 	/*
6353941Svenki 	 * Parse the object's value
6363941Svenki 	 */
6373941Svenki 	if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
6383941Svenki 		return (NULL);
6393941Svenki 
6403941Svenki 	return (p);
6413941Svenki }
6423941Svenki 
6433941Svenki void
snmp_free_pdu(snmp_pdu_t * pdu)6443941Svenki snmp_free_pdu(snmp_pdu_t *pdu)
6453941Svenki {
6463941Svenki 	pdu_varlist_t *vp, *nxt;
6473941Svenki 
6483941Svenki 	if (pdu) {
649*4085Svenki 		if ((pdu->community) && (pdu->community != snmp_def_community))
6503941Svenki 			free((void *) pdu->community);
6513941Svenki 
6523941Svenki 		for (vp = pdu->vars; vp; vp = nxt) {
6533941Svenki 			nxt = vp->nextvar;
6543941Svenki 
6553941Svenki 			if (vp->name)
6563941Svenki 				free((void *) vp->name);
6573941Svenki 			if (vp->val.str)
6583941Svenki 				free((void *) vp->val.str);
6593941Svenki 			free((void *) vp);
6603941Svenki 		}
6613941Svenki 
6623941Svenki 		if (pdu->req_pkt)
6633941Svenki 			free((void *) pdu->req_pkt);
6643941Svenki 
6653941Svenki 		if (pdu->reply_pkt)
6663941Svenki 			free((void *) pdu->reply_pkt);
6673941Svenki 
6683941Svenki 		free((void *) pdu);
6693941Svenki 	}
6703941Svenki }
671*4085Svenki 
672*4085Svenki static void
snmp_free_null_vars(pdu_varlist_t * varblock_p)673*4085Svenki snmp_free_null_vars(pdu_varlist_t *varblock_p)
674*4085Svenki {
675*4085Svenki 	pdu_varlist_t	*vp, *nxt;
676*4085Svenki 
677*4085Svenki 	for (vp = varblock_p; vp; vp = nxt) {
678*4085Svenki 		nxt = vp->nextvar;
679*4085Svenki 
680*4085Svenki 		if (vp->name)
681*4085Svenki 			free(vp->name);
682*4085Svenki 		free(vp);
683*4085Svenki 	}
684*4085Svenki }
685