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