xref: /titanic_52/usr/src/cmd/picl/plugins/sun4v/lib/snmp/pdu.c (revision 4f90901ff7a0693b0be018de6dcfff7c6e11d734)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * SNMP PDU and packet transport related routines
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include "asn1.h"
38 #include "pdu.h"
39 #include "debug.h"
40 
41 /*
42  * Static declarations
43  */
44 static int	snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
45 static oid	*snmp_oidstr_to_oid(int, char *, int, size_t *);
46 static uchar_t	*snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
47 static uchar_t	*snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
48 		    uchar_t, void *, size_t);
49 static uchar_t	*snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
50 static uchar_t	*snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
51 static void	snmp_free_null_vars(pdu_varlist_t *);
52 
53 static uchar_t *snmp_def_community = (uchar_t *)SNMP_DEF_COMMUNITY;
54 
55 /*
56  * Allocates and creates a PDU for the specified SNMP command. Currently
57  * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
58  */
59 snmp_pdu_t *
60 snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
61 {
62 	snmp_pdu_t	*pdu;
63 
64 	if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
65 	    (cmd != SNMP_MSG_GETBULK)) {
66 		return (NULL);
67 	}
68 
69 	pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
70 	if (pdu == NULL)
71 		return (NULL);
72 
73 	if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
74 		pdu->version = SNMP_VERSION_1;
75 		pdu->errstat = 0;
76 		pdu->errindex = 0;
77 	} else if (cmd == SNMP_MSG_GETBULK) {
78 		pdu->version = SNMP_VERSION_2c;
79 		pdu->non_repeaters = 0;
80 		pdu->max_repetitions = max_reps ?
81 		    max_reps : SNMP_DEF_MAX_REPETITIONS;
82 	}
83 
84 	pdu->command = cmd;
85 	pdu->reqid = snmp_get_reqid();
86 	pdu->community = snmp_def_community;
87 	pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
88 
89 	if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
90 		free((void *) pdu);
91 		return (NULL);
92 	}
93 
94 	pdu->req_pkt = NULL;
95 	pdu->req_pktsz = 0;
96 	pdu->reply_pkt = NULL;
97 	pdu->reply_pktsz = 0;
98 
99 	return (pdu);
100 }
101 
102 /*
103  * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
104  * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
105  * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
106  * as long as the number of bulk oids are not *too* many, we're safe with
107  * this limit (the typical packet size of a bulk request of 10 vars is
108  * around 250 bytes).
109  */
110 int
111 snmp_make_packet(snmp_pdu_t *pdu)
112 {
113 	uchar_t	*buf, *p;
114 	uchar_t	*msg_seq_end;
115 	uchar_t id;
116 	size_t	bufsz = SNMP_DEF_PKTBUF_SZ;
117 	size_t	seqlen;
118 
119 	if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
120 		return (-1);
121 
122 	/*
123 	 * Let's start with the ASN sequence tag. Set the length
124 	 * to 0 initially and fill it up once the message packetizing
125 	 * is complete.
126 	 */
127 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
128 	if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
129 		free((void *) buf);
130 		return (-1);
131 	}
132 	msg_seq_end = p;
133 
134 	/*
135 	 * Store the version
136 	 */
137 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
138 	if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
139 		free((void *) buf);
140 		return (-1);
141 	}
142 
143 	/*
144 	 * Store the community string
145 	 */
146 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
147 	p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
148 	if (p == NULL) {
149 		free((void *) buf);
150 		return (-1);
151 	}
152 
153 	/*
154 	 * Build the PDU
155 	 */
156 	if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
157 		free((void *) buf);
158 		return (-1);
159 	}
160 
161 	/*
162 	 * Complete the message pkt by updating the message sequence length
163 	 */
164 	seqlen = p - msg_seq_end;
165 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
166 	(void) asn_build_sequence(buf, NULL, id, seqlen);
167 
168 	/*
169 	 * Calculate packet size and return
170 	 */
171 	pdu->req_pkt = buf;
172 	pdu->req_pktsz = p - buf;
173 
174 	return (0);
175 }
176 
177 /*
178  * Makes a PDU out of a reply packet. The reply message is parsed
179  * and if the reqid of the incoming packet does not match the reqid
180  * we're waiting for, an error is returned. The PDU is allocated
181  * inside this routine and must be freed by the caller once it is no
182  * longer needed.
183  */
184 snmp_pdu_t *
185 snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
186 {
187 	snmp_pdu_t	*reply_pdu;
188 	uchar_t		*p;
189 	size_t		msgsz = reply_pktsz;
190 	uchar_t		exp_id;
191 
192 	reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
193 	if (reply_pdu == NULL)
194 		return (NULL);
195 
196 	/*
197 	 * Try to parse the ASN sequence out of the beginning of the reply
198 	 * packet. If we don't find a sequence at the beginning, something's
199 	 * wrong.
200 	 */
201 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
202 	if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
203 		snmp_free_pdu(reply_pdu);
204 		return (NULL);
205 	}
206 
207 	/*
208 	 * Now try to parse the version out of the packet
209 	 */
210 	if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
211 		snmp_free_pdu(reply_pdu);
212 		return (NULL);
213 	}
214 	if ((reply_pdu->version != SNMP_VERSION_1) &&
215 	    (reply_pdu->version != SNMP_VERSION_2c)) {
216 		snmp_free_pdu(reply_pdu);
217 		return (NULL);
218 	}
219 
220 	/*
221 	 * Parse the community string (space allocated by asn_parse_string)
222 	 */
223 	p = asn_parse_string(p, &msgsz, &reply_pdu->community,
224 	    &reply_pdu->community_len);
225 	if (p == NULL) {
226 		snmp_free_pdu(reply_pdu);
227 		return (NULL);
228 	}
229 
230 	/*
231 	 * Parse the PDU part of the message
232 	 */
233 	if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
234 		snmp_free_pdu(reply_pdu);
235 		return (NULL);
236 	}
237 
238 	return (reply_pdu);
239 }
240 
241 
242 /*
243  * Convert the OID strings into the standard PDU oid form (sequence of
244  * integer subids) and add them to the PDU's variable list. Note that
245  * this is used only for preparing the request messages (GET, GETNEXT
246  * and GETBULK), so the values of the variables are always null.
247  */
248 static int
249 snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
250 {
251 	pdu_varlist_t	*vp, *prev;
252 	pdu_varlist_t	*varblock_p = NULL;
253 	char	*p;
254 	int	i;
255 
256 	prev = NULL;
257 	p = oidstrs;
258 	for (i = 0; i < n_oids; i++) {
259 		if ((vp = calloc(1, sizeof (pdu_varlist_t))) == NULL) {
260 			snmp_free_null_vars(varblock_p);
261 			return (-1);
262 		} else if (i == 0) {
263 			varblock_p = vp;
264 		} else {
265 			prev->nextvar = vp;
266 		}
267 
268 		vp->name = snmp_oidstr_to_oid(pdu->command,
269 		    p, row, &vp->name_len);
270 		if (vp->name == NULL) {
271 			snmp_free_null_vars(varblock_p);
272 			return (-1);
273 		}
274 		vp->val.str = NULL;
275 		vp->val_len = 0;
276 		vp->type = ASN_NULL;
277 		vp->nextvar = NULL;
278 
279 		LOGVAR(TAG_NULL_VAR, vp);
280 
281 		prev = vp;
282 		p += strlen(p) + 1;
283 	}
284 
285 	/*
286 	 * append the varlist to the PDU
287 	 */
288 	if (pdu->vars == NULL)
289 		pdu->vars = varblock_p;
290 	else {
291 		for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
292 			;
293 		vp->nextvar = varblock_p;
294 	}
295 
296 	return (0);
297 }
298 
299 /*
300  * Some assumptions are in place here to eliminate unnecessary complexity.
301  * All OID strings passed are assumed to be in the numeric string form, have
302  * no leading/trailing '.' or spaces. Since PICL plugin is currently the
303  * only customer, this is quite reasonable.
304  */
305 static oid *
306 snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
307 {
308 	int	i, count;
309 	char	*p, *q;
310 	char	*oidstr_dup;
311 	oid	*objid;
312 
313 	if ((oidstr == NULL) || (n_subids == NULL))
314 		return (NULL);
315 
316 	for (count = 1, p = oidstr; p; count++, p++) {
317 		if ((p = strchr(p, '.')) == NULL)
318 			break;
319 	}
320 
321 	/*
322 	 * Add one more to count for 'row'. Need special processing
323 	 * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
324 	 * comment below.
325 	 */
326 	if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
327 	    (cmd == SNMP_MSG_GETNEXT && row >= 0)) {
328 		count++;
329 	}
330 
331 	if ((oidstr_dup = strdup(oidstr)) == NULL)
332 		return (NULL);
333 
334 	objid = (oid *) calloc(count, sizeof (oid));
335 	if (objid == NULL) {
336 		free((void *) p);
337 		return (NULL);
338 	}
339 
340 	p = oidstr_dup;
341 	for (i = 0; i < count - 1; i++) {
342 		if (q = strchr(p, '.'))
343 			*q = 0;
344 		objid[i] = (oid) strtoul(p, NULL, 10);
345 		p = q + 1;
346 	}
347 
348 	/*
349 	 * For SNMP_MSG_GET, the leaf subid will simply be the row#.
350 	 *
351 	 * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
352 	 * we pass 'row-1' as the leaf subid, to include the item that
353 	 * is of interest to us. If the row# is less than or equal to 0,
354 	 * we will simply ignore it and pass only the prefix part of the
355 	 * oidstr. For this case, our count would have been 1 less than
356 	 * usual, and we are yet to save the last subid.
357 	 *
358 	 * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
359 	 * we'll simply ignore it and pass only the prefix part of the
360 	 * oidstr. For this case, our count would have been 1 less than
361 	 * usual, and we are yet to save the last subid. If the row#
362 	 * passed is greater than or equal to 0, we'll simply pass it
363 	 * verbatim, as the leaf subid.
364 	 */
365 	switch (cmd) {
366 	case SNMP_MSG_GET:
367 		objid[i] = (oid) row;
368 		break;
369 
370 	case SNMP_MSG_GETBULK:
371 		if (row > 0)
372 			objid[i] = (oid) (row - 1);
373 		else
374 			objid[i] = (oid) strtoul(p, NULL, 10);
375 		break;
376 
377 	case SNMP_MSG_GETNEXT:
378 		if (row < 0)
379 			objid[i] = (oid) strtoul(p, NULL, 10);
380 		else
381 			objid[i] = (oid) row;
382 		break;
383 	}
384 
385 	*n_subids = count;
386 
387 	free((void *) oidstr_dup);
388 
389 	return (objid);
390 }
391 
392 /*
393  * Builds the PDU part of the snmp message packet.
394  */
395 static uchar_t *
396 snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
397 {
398 	uchar_t	*p;
399 	uchar_t	*pdu_seq_begin, *pdu_seq_end;
400 	uchar_t	*varlist_seq_begin, *varlist_seq_end;
401 	uchar_t	id;
402 	size_t	seqlen;
403 	pdu_varlist_t	*vp;
404 
405 	/*
406 	 * Build ASN sequence for the PDU command (length will be
407 	 * updated later once the entire command is completely formed)
408 	 */
409 	pdu_seq_begin = buf;
410 	p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
411 	if (p == NULL)
412 		return (NULL);
413 	pdu_seq_end = p;
414 
415 	/*
416 	 * Build the request id
417 	 */
418 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
419 	if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
420 		return (NULL);
421 
422 	/*
423 	 * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
424 	 * (same as error status and error index for other message types)
425 	 */
426 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
427 	if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
428 		return (NULL);
429 
430 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
431 	if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
432 		return (NULL);
433 
434 	/*
435 	 * Build ASN sequence for the variables list (update length
436 	 * after building the varlist)
437 	 */
438 	varlist_seq_begin = p;
439 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
440 	if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
441 		return (NULL);
442 	varlist_seq_end = p;
443 
444 	/*
445 	 * Build the variables list
446 	 */
447 	for (vp = pdu->vars; vp; vp = vp->nextvar) {
448 		p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
449 		    vp->type, vp->val.str, vp->val_len);
450 		if (p == NULL)
451 			return (NULL);
452 	}
453 
454 	/*
455 	 * Now update the varlist sequence length
456 	 */
457 	seqlen = p - varlist_seq_end;
458 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
459 	(void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
460 
461 	/*
462 	 * And finally, update the length for the PDU sequence
463 	 */
464 	seqlen = p - pdu_seq_end;
465 	(void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
466 	    seqlen);
467 
468 	return (p);
469 }
470 
471 /*
472  * Builds an object variable into the snmp message packet. Although the
473  * code is here to build variables of basic types such as integer, object id
474  * and strings, the only type of variable we ever send via snmp request
475  * messages is the ASN_NULL type.
476  */
477 static uchar_t *
478 snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
479     uchar_t val_type, void *val, size_t val_len)
480 {
481 	uchar_t	*p, *varseq_end;
482 	size_t	seqlen;
483 	uchar_t	id;
484 
485 	/*
486 	 * Each variable binding is in turn defined as a 'SEQUENCE of' by
487 	 * the SNMP PDU format, so we'll prepare the sequence and fill up
488 	 * the length later. Sigh!
489 	 */
490 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
491 	if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
492 		return (NULL);
493 	varseq_end = p;
494 
495 	/*
496 	 * Build the object id
497 	 */
498 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
499 	if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
500 		return (NULL);
501 
502 	/*
503 	 * Currently we only ever build ASN_NULL vars while sending requests,
504 	 * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
505 	 * SNMP_MSG_GETBULK.
506 	 */
507 	id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
508 	switch (val_type) {
509 	case ASN_INTEGER:
510 		p = asn_build_int(p, bufsz_p, id, *((int *)val));
511 		if (p == NULL)
512 			return (NULL);
513 		break;
514 
515 	case ASN_OBJECT_ID:
516 		p = asn_build_objid(p, bufsz_p, id, val,
517 		    val_len / sizeof (oid));
518 		if (p == NULL)
519 			return (NULL);
520 		break;
521 
522 	case ASN_OCTET_STR:
523 		p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
524 		if (p == NULL)
525 			return (NULL);
526 		break;
527 
528 	case ASN_NULL:
529 		if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
530 			return (NULL);
531 		break;
532 
533 	default:
534 		return (NULL);
535 	}
536 
537 	/*
538 	 * Rebuild the variable sequence length
539 	 */
540 	seqlen = p - varseq_end;
541 	id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
542 	(void) asn_build_sequence(buf, NULL, id, seqlen);
543 
544 	return (p);
545 }
546 
547 /*
548  * Parse the PDU portion of the incoming snmp message into the reply_pdu.
549  * Space for all structure members are allocated as needed and must be freed
550  * by the caller when these are no longer needed.
551  */
552 static uchar_t *
553 snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
554 {
555 	uchar_t	*p;
556 	uchar_t	id, exp_id;
557 	pdu_varlist_t	*newvp, *vp = NULL;
558 
559 	/*
560 	 * Parse the PDU header out of the message
561 	 */
562 	if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
563 		return (NULL);
564 	if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
565 		return (NULL);
566 	reply_pdu->command = (int)id;
567 
568 	/*
569 	 * Parse the request id and verify that this is the response
570 	 * we're expecting.
571 	 */
572 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
573 		return (NULL);
574 	if (reply_pdu->reqid != reqid)
575 		return (NULL);
576 
577 	/*
578 	 * Parse the error-status and error-index values
579 	 */
580 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
581 		return (NULL);
582 	if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
583 		return (NULL);
584 
585 	/*
586 	 * Parse the header for the variables list sequence.
587 	 */
588 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
589 	if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
590 		return (NULL);
591 
592 	while (((int)*msgsz_p) > 0) {
593 		if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
594 			return (NULL);
595 
596 		if (vp == NULL)
597 			reply_pdu->vars = newvp;
598 		else
599 			vp->nextvar = newvp;
600 
601 		vp = newvp;
602 		if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
603 			return (NULL);
604 
605 		LOGVAR(TAG_RESPONSE_VAR, vp);
606 	}
607 
608 	return (p);
609 }
610 
611 /*
612  * Allocate and parse the next variable into the varlist
613  */
614 static uchar_t *
615 snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
616 {
617 	uchar_t	*p;
618 	uchar_t	exp_id;
619 
620 	/*
621 	 * Parse this variable's sequence
622 	 */
623 	exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
624 	if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
625 		return (NULL);
626 
627 	/*
628 	 * Parse the variable's object identifier
629 	 */
630 	p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
631 	if (p == NULL)
632 		return (NULL);
633 
634 	/*
635 	 * Parse the object's value
636 	 */
637 	if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
638 		return (NULL);
639 
640 	return (p);
641 }
642 
643 void
644 snmp_free_pdu(snmp_pdu_t *pdu)
645 {
646 	pdu_varlist_t *vp, *nxt;
647 
648 	if (pdu) {
649 		if ((pdu->community) && (pdu->community != snmp_def_community))
650 			free((void *) pdu->community);
651 
652 		for (vp = pdu->vars; vp; vp = nxt) {
653 			nxt = vp->nextvar;
654 
655 			if (vp->name)
656 				free((void *) vp->name);
657 			if (vp->val.str)
658 				free((void *) vp->val.str);
659 			free((void *) vp);
660 		}
661 
662 		if (pdu->req_pkt)
663 			free((void *) pdu->req_pkt);
664 
665 		if (pdu->reply_pkt)
666 			free((void *) pdu->reply_pkt);
667 
668 		free((void *) pdu);
669 	}
670 }
671 
672 static void
673 snmp_free_null_vars(pdu_varlist_t *varblock_p)
674 {
675 	pdu_varlist_t	*vp, *nxt;
676 
677 	for (vp = varblock_p; vp; vp = nxt) {
678 		nxt = vp->nextvar;
679 
680 		if (vp->name)
681 			free(vp->name);
682 		free(vp);
683 	}
684 }
685