xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c (revision 0409f346a11d84a1549a8e4e2acb42fa4561e8c2)
10d63ce2bSvenki /*
20d63ce2bSvenki  * CDDL HEADER START
30d63ce2bSvenki  *
40d63ce2bSvenki  * The contents of this file are subject to the terms of the
50d63ce2bSvenki  * Common Development and Distribution License (the "License").
60d63ce2bSvenki  * You may not use this file except in compliance with the License.
70d63ce2bSvenki  *
80d63ce2bSvenki  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90d63ce2bSvenki  * or http://www.opensolaris.org/os/licensing.
100d63ce2bSvenki  * See the License for the specific language governing permissions
110d63ce2bSvenki  * and limitations under the License.
120d63ce2bSvenki  *
130d63ce2bSvenki  * When distributing Covered Code, include this CDDL HEADER in each
140d63ce2bSvenki  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150d63ce2bSvenki  * If applicable, add the following below this CDDL HEADER, with the
160d63ce2bSvenki  * fields enclosed by brackets "[]" replaced with your own identifying
170d63ce2bSvenki  * information: Portions Copyright [yyyy] [name of copyright owner]
180d63ce2bSvenki  *
190d63ce2bSvenki  * CDDL HEADER END
200d63ce2bSvenki  */
210d63ce2bSvenki 
220d63ce2bSvenki /*
230d63ce2bSvenki  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
240d63ce2bSvenki  * Use is subject to license terms.
25*0409f346SPeter Tribble  * Copyright 2019 Peter Tribble.
260d63ce2bSvenki  */
270d63ce2bSvenki 
280d63ce2bSvenki /*
290d63ce2bSvenki  * SNMP PDU and packet transport related routines
300d63ce2bSvenki  */
310d63ce2bSvenki 
320d63ce2bSvenki #include <stdio.h>
330d63ce2bSvenki #include <stdlib.h>
340d63ce2bSvenki #include <string.h>
350d63ce2bSvenki #include <sys/types.h>
360d63ce2bSvenki #include "asn1.h"
370d63ce2bSvenki #include "pdu.h"
380d63ce2bSvenki 
390d63ce2bSvenki /*
400d63ce2bSvenki  * Static declarations
410d63ce2bSvenki  */
420d63ce2bSvenki static int	snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
430d63ce2bSvenki static oid	*snmp_oidstr_to_oid(int, char *, int, size_t *);
440d63ce2bSvenki static uchar_t	*snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
450d63ce2bSvenki static uchar_t	*snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
460d63ce2bSvenki 		    uchar_t, void *, size_t);
470d63ce2bSvenki static uchar_t	*snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
480d63ce2bSvenki static uchar_t	*snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
494f90901fSvenki static void	snmp_free_null_vars(pdu_varlist_t *);
504f90901fSvenki 
514f90901fSvenki static uchar_t *snmp_def_community = (uchar_t *)SNMP_DEF_COMMUNITY;
520d63ce2bSvenki 
530d63ce2bSvenki /*
540d63ce2bSvenki  * Allocates and creates a PDU for the specified SNMP command. Currently
550d63ce2bSvenki  * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
560d63ce2bSvenki  */
570d63ce2bSvenki snmp_pdu_t *
snmp_create_pdu(int cmd,int max_reps,char * oidstrs,int n_oids,int row)580d63ce2bSvenki snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
590d63ce2bSvenki {
600d63ce2bSvenki 	snmp_pdu_t	*pdu;
610d63ce2bSvenki 
620d63ce2bSvenki 	if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
630d63ce2bSvenki 	    (cmd != SNMP_MSG_GETBULK)) {
640d63ce2bSvenki 		return (NULL);
650d63ce2bSvenki 	}
660d63ce2bSvenki 
670d63ce2bSvenki 	pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
680d63ce2bSvenki 	if (pdu == NULL)
690d63ce2bSvenki 		return (NULL);
700d63ce2bSvenki 
710d63ce2bSvenki 	if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
720d63ce2bSvenki 		pdu->version = SNMP_VERSION_1;
730d63ce2bSvenki 		pdu->errstat = 0;
740d63ce2bSvenki 		pdu->errindex = 0;
750d63ce2bSvenki 	} else if (cmd == SNMP_MSG_GETBULK) {
760d63ce2bSvenki 		pdu->version = SNMP_VERSION_2c;
770d63ce2bSvenki 		pdu->non_repeaters = 0;
780d63ce2bSvenki 		pdu->max_repetitions = max_reps ?
790d63ce2bSvenki 		    max_reps : SNMP_DEF_MAX_REPETITIONS;
800d63ce2bSvenki 	}
810d63ce2bSvenki 
820d63ce2bSvenki 	pdu->command = cmd;
830d63ce2bSvenki 	pdu->reqid = snmp_get_reqid();
844f90901fSvenki 	pdu->community = snmp_def_community;
850d63ce2bSvenki 	pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
860d63ce2bSvenki 
870d63ce2bSvenki 	if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
880d63ce2bSvenki 		free((void *) pdu);
890d63ce2bSvenki 		return (NULL);
900d63ce2bSvenki 	}
910d63ce2bSvenki 
920d63ce2bSvenki 	pdu->req_pkt = NULL;
930d63ce2bSvenki 	pdu->req_pktsz = 0;
940d63ce2bSvenki 	pdu->reply_pkt = NULL;
950d63ce2bSvenki 	pdu->reply_pktsz = 0;
960d63ce2bSvenki 
970d63ce2bSvenki 	return (pdu);
980d63ce2bSvenki }
990d63ce2bSvenki 
1000d63ce2bSvenki /*
1010d63ce2bSvenki  * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
1020d63ce2bSvenki  * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
1030d63ce2bSvenki  * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
1040d63ce2bSvenki  * as long as the number of bulk oids are not *too* many, we're safe with
1050d63ce2bSvenki  * this limit (the typical packet size of a bulk request of 10 vars is
1060d63ce2bSvenki  * around 250 bytes).
1070d63ce2bSvenki  */
1080d63ce2bSvenki int
snmp_make_packet(snmp_pdu_t * pdu)1090d63ce2bSvenki snmp_make_packet(snmp_pdu_t *pdu)
1100d63ce2bSvenki {
1110d63ce2bSvenki 	uchar_t	*buf, *p;
1120d63ce2bSvenki 	uchar_t	*msg_seq_end;
1130d63ce2bSvenki 	uchar_t id;
1140d63ce2bSvenki 	size_t	bufsz = SNMP_DEF_PKTBUF_SZ;
1150d63ce2bSvenki 	size_t	seqlen;
1160d63ce2bSvenki 
1170d63ce2bSvenki 	if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
1180d63ce2bSvenki 		return (-1);
1190d63ce2bSvenki 
1200d63ce2bSvenki 	/*
1210d63ce2bSvenki 	 * Let's start with the ASN sequence tag. Set the length
1220d63ce2bSvenki 	 * to 0 initially and fill it up once the message packetizing
1230d63ce2bSvenki 	 * is complete.
1240d63ce2bSvenki 	 */
1250d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1260d63ce2bSvenki 	if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
1270d63ce2bSvenki 		free((void *) buf);
1280d63ce2bSvenki 		return (-1);
1290d63ce2bSvenki 	}
1300d63ce2bSvenki 	msg_seq_end = p;
1310d63ce2bSvenki 
1320d63ce2bSvenki 	/*
1330d63ce2bSvenki 	 * Store the version
1340d63ce2bSvenki 	 */
1350d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
1360d63ce2bSvenki 	if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
1370d63ce2bSvenki 		free((void *) buf);
1380d63ce2bSvenki 		return (-1);
1390d63ce2bSvenki 	}
1400d63ce2bSvenki 
1410d63ce2bSvenki 	/*
1420d63ce2bSvenki 	 * Store the community string
1430d63ce2bSvenki 	 */
1440d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
1450d63ce2bSvenki 	p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
1460d63ce2bSvenki 	if (p == NULL) {
1470d63ce2bSvenki 		free((void *) buf);
1480d63ce2bSvenki 		return (-1);
1490d63ce2bSvenki 	}
1500d63ce2bSvenki 
1510d63ce2bSvenki 	/*
1520d63ce2bSvenki 	 * Build the PDU
1530d63ce2bSvenki 	 */
1540d63ce2bSvenki 	if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
1550d63ce2bSvenki 		free((void *) buf);
1560d63ce2bSvenki 		return (-1);
1570d63ce2bSvenki 	}
1580d63ce2bSvenki 
1590d63ce2bSvenki 	/*
1600d63ce2bSvenki 	 * Complete the message pkt by updating the message sequence length
1610d63ce2bSvenki 	 */
1620d63ce2bSvenki 	seqlen = p - msg_seq_end;
1630d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1640d63ce2bSvenki 	(void) asn_build_sequence(buf, NULL, id, seqlen);
1650d63ce2bSvenki 
1660d63ce2bSvenki 	/*
1670d63ce2bSvenki 	 * Calculate packet size and return
1680d63ce2bSvenki 	 */
1690d63ce2bSvenki 	pdu->req_pkt = buf;
1700d63ce2bSvenki 	pdu->req_pktsz = p - buf;
1710d63ce2bSvenki 
1720d63ce2bSvenki 	return (0);
1730d63ce2bSvenki }
1740d63ce2bSvenki 
1750d63ce2bSvenki /*
1760d63ce2bSvenki  * Makes a PDU out of a reply packet. The reply message is parsed
1770d63ce2bSvenki  * and if the reqid of the incoming packet does not match the reqid
1780d63ce2bSvenki  * we're waiting for, an error is returned. The PDU is allocated
1790d63ce2bSvenki  * inside this routine and must be freed by the caller once it is no
1800d63ce2bSvenki  * longer needed.
1810d63ce2bSvenki  */
1820d63ce2bSvenki snmp_pdu_t *
snmp_parse_reply(int reqid,uchar_t * reply_pkt,size_t reply_pktsz)1830d63ce2bSvenki snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
1840d63ce2bSvenki {
1850d63ce2bSvenki 	snmp_pdu_t	*reply_pdu;
1860d63ce2bSvenki 	uchar_t		*p;
1870d63ce2bSvenki 	size_t		msgsz = reply_pktsz;
1880d63ce2bSvenki 	uchar_t		exp_id;
1890d63ce2bSvenki 
1900d63ce2bSvenki 	reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
1910d63ce2bSvenki 	if (reply_pdu == NULL)
1920d63ce2bSvenki 		return (NULL);
1930d63ce2bSvenki 
1940d63ce2bSvenki 	/*
1950d63ce2bSvenki 	 * Try to parse the ASN sequence out of the beginning of the reply
1960d63ce2bSvenki 	 * packet. If we don't find a sequence at the beginning, something's
1970d63ce2bSvenki 	 * wrong.
1980d63ce2bSvenki 	 */
1990d63ce2bSvenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
2000d63ce2bSvenki 	if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
2010d63ce2bSvenki 		snmp_free_pdu(reply_pdu);
2020d63ce2bSvenki 		return (NULL);
2030d63ce2bSvenki 	}
2040d63ce2bSvenki 
2050d63ce2bSvenki 	/*
2060d63ce2bSvenki 	 * Now try to parse the version out of the packet
2070d63ce2bSvenki 	 */
2080d63ce2bSvenki 	if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
2090d63ce2bSvenki 		snmp_free_pdu(reply_pdu);
2100d63ce2bSvenki 		return (NULL);
2110d63ce2bSvenki 	}
2120d63ce2bSvenki 	if ((reply_pdu->version != SNMP_VERSION_1) &&
2130d63ce2bSvenki 	    (reply_pdu->version != SNMP_VERSION_2c)) {
2140d63ce2bSvenki 		snmp_free_pdu(reply_pdu);
2150d63ce2bSvenki 		return (NULL);
2160d63ce2bSvenki 	}
2170d63ce2bSvenki 
2180d63ce2bSvenki 	/*
2190d63ce2bSvenki 	 * Parse the community string (space allocated by asn_parse_string)
2200d63ce2bSvenki 	 */
2210d63ce2bSvenki 	p = asn_parse_string(p, &msgsz, &reply_pdu->community,
2220d63ce2bSvenki 	    &reply_pdu->community_len);
2230d63ce2bSvenki 	if (p == NULL) {
2240d63ce2bSvenki 		snmp_free_pdu(reply_pdu);
2250d63ce2bSvenki 		return (NULL);
2260d63ce2bSvenki 	}
2270d63ce2bSvenki 
2280d63ce2bSvenki 	/*
2290d63ce2bSvenki 	 * Parse the PDU part of the message
2300d63ce2bSvenki 	 */
2310d63ce2bSvenki 	if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
2320d63ce2bSvenki 		snmp_free_pdu(reply_pdu);
2330d63ce2bSvenki 		return (NULL);
2340d63ce2bSvenki 	}
2350d63ce2bSvenki 
2360d63ce2bSvenki 	return (reply_pdu);
2370d63ce2bSvenki }
2380d63ce2bSvenki 
2390d63ce2bSvenki 
2400d63ce2bSvenki /*
2410d63ce2bSvenki  * Convert the OID strings into the standard PDU oid form (sequence of
2420d63ce2bSvenki  * integer subids) and add them to the PDU's variable list. Note that
2430d63ce2bSvenki  * this is used only for preparing the request messages (GET, GETNEXT
2440d63ce2bSvenki  * and GETBULK), so the values of the variables are always null.
2450d63ce2bSvenki  */
2460d63ce2bSvenki static int
snmp_add_null_vars(snmp_pdu_t * pdu,char * oidstrs,int n_oids,int row)2470d63ce2bSvenki snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
2480d63ce2bSvenki {
2490d63ce2bSvenki 	pdu_varlist_t	*vp, *prev;
2504f90901fSvenki 	pdu_varlist_t	*varblock_p = NULL;
2510d63ce2bSvenki 	char	*p;
2520d63ce2bSvenki 	int	i;
2530d63ce2bSvenki 
2540d63ce2bSvenki 	prev = NULL;
2550d63ce2bSvenki 	p = oidstrs;
2560d63ce2bSvenki 	for (i = 0; i < n_oids; i++) {
2574f90901fSvenki 		if ((vp = calloc(1, sizeof (pdu_varlist_t))) == NULL) {
2584f90901fSvenki 			snmp_free_null_vars(varblock_p);
2594f90901fSvenki 			return (-1);
2604f90901fSvenki 		} else if (i == 0) {
2614f90901fSvenki 			varblock_p = vp;
2624f90901fSvenki 		} else {
2634f90901fSvenki 			prev->nextvar = vp;
2644f90901fSvenki 		}
2654f90901fSvenki 
2660d63ce2bSvenki 		vp->name = snmp_oidstr_to_oid(pdu->command,
2670d63ce2bSvenki 		    p, row, &vp->name_len);
2680d63ce2bSvenki 		if (vp->name == NULL) {
2694f90901fSvenki 			snmp_free_null_vars(varblock_p);
2700d63ce2bSvenki 			return (-1);
2710d63ce2bSvenki 		}
2720d63ce2bSvenki 		vp->val.str = NULL;
2730d63ce2bSvenki 		vp->val_len = 0;
2740d63ce2bSvenki 		vp->type = ASN_NULL;
2754f90901fSvenki 		vp->nextvar = NULL;
2760d63ce2bSvenki 
2770d63ce2bSvenki 		prev = vp;
2780d63ce2bSvenki 		p += strlen(p) + 1;
2790d63ce2bSvenki 	}
2800d63ce2bSvenki 
2810d63ce2bSvenki 	/*
2820d63ce2bSvenki 	 * append the varlist to the PDU
2830d63ce2bSvenki 	 */
2840d63ce2bSvenki 	if (pdu->vars == NULL)
2850d63ce2bSvenki 		pdu->vars = varblock_p;
2860d63ce2bSvenki 	else {
2870d63ce2bSvenki 		for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
2880d63ce2bSvenki 			;
2890d63ce2bSvenki 		vp->nextvar = varblock_p;
2900d63ce2bSvenki 	}
2910d63ce2bSvenki 
2920d63ce2bSvenki 	return (0);
2930d63ce2bSvenki }
2944f90901fSvenki 
2950d63ce2bSvenki /*
2960d63ce2bSvenki  * Some assumptions are in place here to eliminate unnecessary complexity.
2970d63ce2bSvenki  * All OID strings passed are assumed to be in the numeric string form, have
2980d63ce2bSvenki  * no leading/trailing '.' or spaces. Since PICL plugin is currently the
2990d63ce2bSvenki  * only customer, this is quite reasonable.
3000d63ce2bSvenki  */
3010d63ce2bSvenki static oid *
snmp_oidstr_to_oid(int cmd,char * oidstr,int row,size_t * n_subids)3020d63ce2bSvenki snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
3030d63ce2bSvenki {
3040d63ce2bSvenki 	int	i, count;
3050d63ce2bSvenki 	char	*p, *q;
3060d63ce2bSvenki 	char	*oidstr_dup;
3070d63ce2bSvenki 	oid	*objid;
3080d63ce2bSvenki 
3090d63ce2bSvenki 	if ((oidstr == NULL) || (n_subids == NULL))
3100d63ce2bSvenki 		return (NULL);
3110d63ce2bSvenki 
3120d63ce2bSvenki 	for (count = 1, p = oidstr; p; count++, p++) {
3130d63ce2bSvenki 		if ((p = strchr(p, '.')) == NULL)
3140d63ce2bSvenki 			break;
3150d63ce2bSvenki 	}
3160d63ce2bSvenki 
3170d63ce2bSvenki 	/*
3180d63ce2bSvenki 	 * Add one more to count for 'row'. Need special processing
3190d63ce2bSvenki 	 * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
3200d63ce2bSvenki 	 * comment below.
3210d63ce2bSvenki 	 */
3220d63ce2bSvenki 	if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
3230d63ce2bSvenki 	    (cmd == SNMP_MSG_GETNEXT && row >= 0)) {
3240d63ce2bSvenki 		count++;
3250d63ce2bSvenki 	}
3260d63ce2bSvenki 
3270d63ce2bSvenki 	if ((oidstr_dup = strdup(oidstr)) == NULL)
3280d63ce2bSvenki 		return (NULL);
3290d63ce2bSvenki 
3300d63ce2bSvenki 	objid = (oid *) calloc(count, sizeof (oid));
3310d63ce2bSvenki 	if (objid == NULL) {
3320d63ce2bSvenki 		free((void *) p);
3330d63ce2bSvenki 		return (NULL);
3340d63ce2bSvenki 	}
3350d63ce2bSvenki 
3360d63ce2bSvenki 	p = oidstr_dup;
3370d63ce2bSvenki 	for (i = 0; i < count - 1; i++) {
3380d63ce2bSvenki 		if (q = strchr(p, '.'))
3390d63ce2bSvenki 			*q = 0;
3400d63ce2bSvenki 		objid[i] = (oid) strtoul(p, NULL, 10);
3410d63ce2bSvenki 		p = q + 1;
3420d63ce2bSvenki 	}
3430d63ce2bSvenki 
3440d63ce2bSvenki 	/*
3450d63ce2bSvenki 	 * For SNMP_MSG_GET, the leaf subid will simply be the row#.
3460d63ce2bSvenki 	 *
3470d63ce2bSvenki 	 * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
3480d63ce2bSvenki 	 * we pass 'row-1' as the leaf subid, to include the item that
3490d63ce2bSvenki 	 * is of interest to us. If the row# is less than or equal to 0,
3500d63ce2bSvenki 	 * we will simply ignore it and pass only the prefix part of the
3510d63ce2bSvenki 	 * oidstr. For this case, our count would have been 1 less than
3520d63ce2bSvenki 	 * usual, and we are yet to save the last subid.
3530d63ce2bSvenki 	 *
3540d63ce2bSvenki 	 * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
3550d63ce2bSvenki 	 * we'll simply ignore it and pass only the prefix part of the
3560d63ce2bSvenki 	 * oidstr. For this case, our count would have been 1 less than
3570d63ce2bSvenki 	 * usual, and we are yet to save the last subid. If the row#
3580d63ce2bSvenki 	 * passed is greater than or equal to 0, we'll simply pass it
3590d63ce2bSvenki 	 * verbatim, as the leaf subid.
3600d63ce2bSvenki 	 */
3610d63ce2bSvenki 	switch (cmd) {
3620d63ce2bSvenki 	case SNMP_MSG_GET:
3630d63ce2bSvenki 		objid[i] = (oid) row;
3640d63ce2bSvenki 		break;
3650d63ce2bSvenki 
3660d63ce2bSvenki 	case SNMP_MSG_GETBULK:
3670d63ce2bSvenki 		if (row > 0)
3680d63ce2bSvenki 			objid[i] = (oid) (row - 1);
3690d63ce2bSvenki 		else
3700d63ce2bSvenki 			objid[i] = (oid) strtoul(p, NULL, 10);
3710d63ce2bSvenki 		break;
3720d63ce2bSvenki 
3730d63ce2bSvenki 	case SNMP_MSG_GETNEXT:
3740d63ce2bSvenki 		if (row < 0)
3750d63ce2bSvenki 			objid[i] = (oid) strtoul(p, NULL, 10);
3760d63ce2bSvenki 		else
3770d63ce2bSvenki 			objid[i] = (oid) row;
3780d63ce2bSvenki 		break;
3790d63ce2bSvenki 	}
3800d63ce2bSvenki 
3810d63ce2bSvenki 	*n_subids = count;
3820d63ce2bSvenki 
3830d63ce2bSvenki 	free((void *) oidstr_dup);
3840d63ce2bSvenki 
3850d63ce2bSvenki 	return (objid);
3860d63ce2bSvenki }
3870d63ce2bSvenki 
3880d63ce2bSvenki /*
3890d63ce2bSvenki  * Builds the PDU part of the snmp message packet.
3900d63ce2bSvenki  */
3910d63ce2bSvenki static uchar_t *
snmp_build_pdu(snmp_pdu_t * pdu,uchar_t * buf,size_t * bufsz_p)3920d63ce2bSvenki snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
3930d63ce2bSvenki {
3940d63ce2bSvenki 	uchar_t	*p;
3950d63ce2bSvenki 	uchar_t	*pdu_seq_begin, *pdu_seq_end;
3960d63ce2bSvenki 	uchar_t	*varlist_seq_begin, *varlist_seq_end;
3970d63ce2bSvenki 	uchar_t	id;
3980d63ce2bSvenki 	size_t	seqlen;
3990d63ce2bSvenki 	pdu_varlist_t	*vp;
4000d63ce2bSvenki 
4010d63ce2bSvenki 	/*
4020d63ce2bSvenki 	 * Build ASN sequence for the PDU command (length will be
4030d63ce2bSvenki 	 * updated later once the entire command is completely formed)
4040d63ce2bSvenki 	 */
4050d63ce2bSvenki 	pdu_seq_begin = buf;
4060d63ce2bSvenki 	p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
4070d63ce2bSvenki 	if (p == NULL)
4080d63ce2bSvenki 		return (NULL);
4090d63ce2bSvenki 	pdu_seq_end = p;
4100d63ce2bSvenki 
4110d63ce2bSvenki 	/*
4120d63ce2bSvenki 	 * Build the request id
4130d63ce2bSvenki 	 */
4140d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4150d63ce2bSvenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
4160d63ce2bSvenki 		return (NULL);
4170d63ce2bSvenki 
4180d63ce2bSvenki 	/*
4190d63ce2bSvenki 	 * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
4200d63ce2bSvenki 	 * (same as error status and error index for other message types)
4210d63ce2bSvenki 	 */
4220d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4230d63ce2bSvenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
4240d63ce2bSvenki 		return (NULL);
4250d63ce2bSvenki 
4260d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4270d63ce2bSvenki 	if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
4280d63ce2bSvenki 		return (NULL);
4290d63ce2bSvenki 
4300d63ce2bSvenki 	/*
4310d63ce2bSvenki 	 * Build ASN sequence for the variables list (update length
4320d63ce2bSvenki 	 * after building the varlist)
4330d63ce2bSvenki 	 */
4340d63ce2bSvenki 	varlist_seq_begin = p;
4350d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4360d63ce2bSvenki 	if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
4370d63ce2bSvenki 		return (NULL);
4380d63ce2bSvenki 	varlist_seq_end = p;
4390d63ce2bSvenki 
4400d63ce2bSvenki 	/*
4410d63ce2bSvenki 	 * Build the variables list
4420d63ce2bSvenki 	 */
4430d63ce2bSvenki 	for (vp = pdu->vars; vp; vp = vp->nextvar) {
4440d63ce2bSvenki 		p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
4450d63ce2bSvenki 		    vp->type, vp->val.str, vp->val_len);
4460d63ce2bSvenki 		if (p == NULL)
4470d63ce2bSvenki 			return (NULL);
4480d63ce2bSvenki 	}
4490d63ce2bSvenki 
4500d63ce2bSvenki 	/*
4510d63ce2bSvenki 	 * Now update the varlist sequence length
4520d63ce2bSvenki 	 */
4530d63ce2bSvenki 	seqlen = p - varlist_seq_end;
4540d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4550d63ce2bSvenki 	(void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
4560d63ce2bSvenki 
4570d63ce2bSvenki 	/*
4580d63ce2bSvenki 	 * And finally, update the length for the PDU sequence
4590d63ce2bSvenki 	 */
4600d63ce2bSvenki 	seqlen = p - pdu_seq_end;
4610d63ce2bSvenki 	(void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
4620d63ce2bSvenki 	    seqlen);
4630d63ce2bSvenki 
4640d63ce2bSvenki 	return (p);
4650d63ce2bSvenki }
4660d63ce2bSvenki 
4670d63ce2bSvenki /*
4680d63ce2bSvenki  * Builds an object variable into the snmp message packet. Although the
4690d63ce2bSvenki  * code is here to build variables of basic types such as integer, object id
4700d63ce2bSvenki  * and strings, the only type of variable we ever send via snmp request
4710d63ce2bSvenki  * messages is the ASN_NULL type.
4720d63ce2bSvenki  */
4730d63ce2bSvenki 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)4740d63ce2bSvenki snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
4750d63ce2bSvenki     uchar_t val_type, void *val, size_t val_len)
4760d63ce2bSvenki {
4770d63ce2bSvenki 	uchar_t	*p, *varseq_end;
4780d63ce2bSvenki 	size_t	seqlen;
4790d63ce2bSvenki 	uchar_t	id;
4800d63ce2bSvenki 
4810d63ce2bSvenki 	/*
4820d63ce2bSvenki 	 * Each variable binding is in turn defined as a 'SEQUENCE of' by
4830d63ce2bSvenki 	 * the SNMP PDU format, so we'll prepare the sequence and fill up
4840d63ce2bSvenki 	 * the length later. Sigh!
4850d63ce2bSvenki 	 */
4860d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4870d63ce2bSvenki 	if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
4880d63ce2bSvenki 		return (NULL);
4890d63ce2bSvenki 	varseq_end = p;
4900d63ce2bSvenki 
4910d63ce2bSvenki 	/*
4920d63ce2bSvenki 	 * Build the object id
4930d63ce2bSvenki 	 */
4940d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
4950d63ce2bSvenki 	if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
4960d63ce2bSvenki 		return (NULL);
4970d63ce2bSvenki 
4980d63ce2bSvenki 	/*
4990d63ce2bSvenki 	 * Currently we only ever build ASN_NULL vars while sending requests,
5000d63ce2bSvenki 	 * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
5010d63ce2bSvenki 	 * SNMP_MSG_GETBULK.
5020d63ce2bSvenki 	 */
5030d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
5040d63ce2bSvenki 	switch (val_type) {
5050d63ce2bSvenki 	case ASN_INTEGER:
5060d63ce2bSvenki 		p = asn_build_int(p, bufsz_p, id, *((int *)val));
5070d63ce2bSvenki 		if (p == NULL)
5080d63ce2bSvenki 			return (NULL);
5090d63ce2bSvenki 		break;
5100d63ce2bSvenki 
5110d63ce2bSvenki 	case ASN_OBJECT_ID:
5120d63ce2bSvenki 		p = asn_build_objid(p, bufsz_p, id, val,
5130d63ce2bSvenki 		    val_len / sizeof (oid));
5140d63ce2bSvenki 		if (p == NULL)
5150d63ce2bSvenki 			return (NULL);
5160d63ce2bSvenki 		break;
5170d63ce2bSvenki 
5180d63ce2bSvenki 	case ASN_OCTET_STR:
5190d63ce2bSvenki 		p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
5200d63ce2bSvenki 		if (p == NULL)
5210d63ce2bSvenki 			return (NULL);
5220d63ce2bSvenki 		break;
5230d63ce2bSvenki 
5240d63ce2bSvenki 	case ASN_NULL:
5250d63ce2bSvenki 		if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
5260d63ce2bSvenki 			return (NULL);
5270d63ce2bSvenki 		break;
5280d63ce2bSvenki 
5290d63ce2bSvenki 	default:
5300d63ce2bSvenki 		return (NULL);
5310d63ce2bSvenki 	}
5320d63ce2bSvenki 
5330d63ce2bSvenki 	/*
5340d63ce2bSvenki 	 * Rebuild the variable sequence length
5350d63ce2bSvenki 	 */
5360d63ce2bSvenki 	seqlen = p - varseq_end;
5370d63ce2bSvenki 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5380d63ce2bSvenki 	(void) asn_build_sequence(buf, NULL, id, seqlen);
5390d63ce2bSvenki 
5400d63ce2bSvenki 	return (p);
5410d63ce2bSvenki }
5420d63ce2bSvenki 
5430d63ce2bSvenki /*
5440d63ce2bSvenki  * Parse the PDU portion of the incoming snmp message into the reply_pdu.
5450d63ce2bSvenki  * Space for all structure members are allocated as needed and must be freed
5460d63ce2bSvenki  * by the caller when these are no longer needed.
5470d63ce2bSvenki  */
5480d63ce2bSvenki static uchar_t *
snmp_parse_pdu(int reqid,uchar_t * msg,size_t * msgsz_p,snmp_pdu_t * reply_pdu)5490d63ce2bSvenki snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
5500d63ce2bSvenki {
5510d63ce2bSvenki 	uchar_t	*p;
5520d63ce2bSvenki 	uchar_t	id, exp_id;
5530d63ce2bSvenki 	pdu_varlist_t	*newvp, *vp = NULL;
5540d63ce2bSvenki 
5550d63ce2bSvenki 	/*
5560d63ce2bSvenki 	 * Parse the PDU header out of the message
5570d63ce2bSvenki 	 */
5580d63ce2bSvenki 	if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
5590d63ce2bSvenki 		return (NULL);
5600d63ce2bSvenki 	if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
5610d63ce2bSvenki 		return (NULL);
5620d63ce2bSvenki 	reply_pdu->command = (int)id;
5630d63ce2bSvenki 
5640d63ce2bSvenki 	/*
5650d63ce2bSvenki 	 * Parse the request id and verify that this is the response
5660d63ce2bSvenki 	 * we're expecting.
5670d63ce2bSvenki 	 */
5680d63ce2bSvenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
5690d63ce2bSvenki 		return (NULL);
5700d63ce2bSvenki 	if (reply_pdu->reqid != reqid)
5710d63ce2bSvenki 		return (NULL);
5720d63ce2bSvenki 
5730d63ce2bSvenki 	/*
5740d63ce2bSvenki 	 * Parse the error-status and error-index values
5750d63ce2bSvenki 	 */
5760d63ce2bSvenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
5770d63ce2bSvenki 		return (NULL);
5780d63ce2bSvenki 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
5790d63ce2bSvenki 		return (NULL);
5800d63ce2bSvenki 
5810d63ce2bSvenki 	/*
5820d63ce2bSvenki 	 * Parse the header for the variables list sequence.
5830d63ce2bSvenki 	 */
5840d63ce2bSvenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5850d63ce2bSvenki 	if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
5860d63ce2bSvenki 		return (NULL);
5870d63ce2bSvenki 
5880d63ce2bSvenki 	while (((int)*msgsz_p) > 0) {
5890d63ce2bSvenki 		if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
5900d63ce2bSvenki 			return (NULL);
5910d63ce2bSvenki 
5920d63ce2bSvenki 		if (vp == NULL)
5930d63ce2bSvenki 			reply_pdu->vars = newvp;
5940d63ce2bSvenki 		else
5950d63ce2bSvenki 			vp->nextvar = newvp;
5960d63ce2bSvenki 
5970d63ce2bSvenki 		vp = newvp;
5980d63ce2bSvenki 		if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
5990d63ce2bSvenki 			return (NULL);
6000d63ce2bSvenki 	}
6010d63ce2bSvenki 
6020d63ce2bSvenki 	return (p);
6030d63ce2bSvenki }
6040d63ce2bSvenki 
6050d63ce2bSvenki /*
6060d63ce2bSvenki  * Allocate and parse the next variable into the varlist
6070d63ce2bSvenki  */
6080d63ce2bSvenki static uchar_t *
snmp_parse_variable(uchar_t * msg,size_t * msgsz_p,pdu_varlist_t * vp)6090d63ce2bSvenki snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
6100d63ce2bSvenki {
6110d63ce2bSvenki 	uchar_t	*p;
6120d63ce2bSvenki 	uchar_t	exp_id;
6130d63ce2bSvenki 
6140d63ce2bSvenki 	/*
6150d63ce2bSvenki 	 * Parse this variable's sequence
6160d63ce2bSvenki 	 */
6170d63ce2bSvenki 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
6180d63ce2bSvenki 	if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
6190d63ce2bSvenki 		return (NULL);
6200d63ce2bSvenki 
6210d63ce2bSvenki 	/*
6220d63ce2bSvenki 	 * Parse the variable's object identifier
6230d63ce2bSvenki 	 */
6240d63ce2bSvenki 	p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
6250d63ce2bSvenki 	if (p == NULL)
6260d63ce2bSvenki 		return (NULL);
6270d63ce2bSvenki 
6280d63ce2bSvenki 	/*
6290d63ce2bSvenki 	 * Parse the object's value
6300d63ce2bSvenki 	 */
6310d63ce2bSvenki 	if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
6320d63ce2bSvenki 		return (NULL);
6330d63ce2bSvenki 
6340d63ce2bSvenki 	return (p);
6350d63ce2bSvenki }
6360d63ce2bSvenki 
6370d63ce2bSvenki void
snmp_free_pdu(snmp_pdu_t * pdu)6380d63ce2bSvenki snmp_free_pdu(snmp_pdu_t *pdu)
6390d63ce2bSvenki {
6400d63ce2bSvenki 	pdu_varlist_t *vp, *nxt;
6410d63ce2bSvenki 
6420d63ce2bSvenki 	if (pdu) {
6434f90901fSvenki 		if ((pdu->community) && (pdu->community != snmp_def_community))
6440d63ce2bSvenki 			free((void *) pdu->community);
6450d63ce2bSvenki 
6460d63ce2bSvenki 		for (vp = pdu->vars; vp; vp = nxt) {
6470d63ce2bSvenki 			nxt = vp->nextvar;
6480d63ce2bSvenki 
6490d63ce2bSvenki 			if (vp->name)
6500d63ce2bSvenki 				free((void *) vp->name);
6510d63ce2bSvenki 			if (vp->val.str)
6520d63ce2bSvenki 				free((void *) vp->val.str);
6530d63ce2bSvenki 			free((void *) vp);
6540d63ce2bSvenki 		}
6550d63ce2bSvenki 
6560d63ce2bSvenki 		if (pdu->req_pkt)
6570d63ce2bSvenki 			free((void *) pdu->req_pkt);
6580d63ce2bSvenki 
6590d63ce2bSvenki 		if (pdu->reply_pkt)
6600d63ce2bSvenki 			free((void *) pdu->reply_pkt);
6610d63ce2bSvenki 
6620d63ce2bSvenki 		free((void *) pdu);
6630d63ce2bSvenki 	}
6640d63ce2bSvenki }
6654f90901fSvenki 
6664f90901fSvenki static void
snmp_free_null_vars(pdu_varlist_t * varblock_p)6674f90901fSvenki snmp_free_null_vars(pdu_varlist_t *varblock_p)
6684f90901fSvenki {
6694f90901fSvenki 	pdu_varlist_t	*vp, *nxt;
6704f90901fSvenki 
6714f90901fSvenki 	for (vp = varblock_p; vp; vp = nxt) {
6724f90901fSvenki 		nxt = vp->nextvar;
6734f90901fSvenki 
6744f90901fSvenki 		if (vp->name)
6754f90901fSvenki 			free(vp->name);
6764f90901fSvenki 		free(vp);
6774f90901fSvenki 	}
6784f90901fSvenki }
679