xref: /openbsd/usr.sbin/tcpdump/print-snmp.c (revision e6c7c102)
1 /*	$OpenBSD: print-snmp.c,v 1.30 2024/04/23 13:34:51 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
5  *     John Robert LoVerso. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *
30  * This implementation has been influenced by the CMU SNMP release,
31  * by Steve Waldbusser.  However, this shares no code with that system.
32  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
33  * Earlier forms of this implementation were derived and/or inspired by an
34  * awk script originally written by C. Philip Wood of LANL (but later
35  * heavily modified by John Robert LoVerso).  The copyright notice for
36  * that work is preserved below, even though it may not rightly apply
37  * to this file.
38  *
39  * This started out as a very simple program, but the incremental decoding
40  * (into the BE structure) complicated things.
41  *
42  #			Los Alamos National Laboratory
43  #
44  #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
45  #	This software was produced under a U.S. Government contract
46  #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
47  #	operated by the	University of California for the U.S. Department
48  #	of Energy.  The U.S. Government is licensed to use, reproduce,
49  #	and distribute this software.  Permission is granted to the
50  #	public to copy and use this software without charge, provided
51  #	that this Notice and any statement of authorship are reproduced
52  #	on all copies.  Neither the Government nor the University makes
53  #	any warranty, express or implied, or assumes any liability or
54  #	responsibility for the use of this software.
55  #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
56  */
57 
58 #include <sys/time.h>
59 
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <string.h>
63 
64 #include "interface.h"
65 #include "addrtoname.h"
66 
67 /*
68  * Universal ASN.1 types
69  * (we only care about the tag values for those allowed in the Internet SMI)
70  */
71 char *Universal[] = {
72 	"U-0",
73 	"Boolean",
74 	"Integer",
75 #define INTEGER 2
76 	"Bitstring",
77 	"String",
78 #define STRING 4
79 	"Null",
80 #define ASN_NULL 5
81 	"ObjID",
82 #define OBJECTID 6
83 	"ObjectDes",
84 	"U-8","U-9","U-10","U-11",	/* 8-11 */
85 	"U-12","U-13","U-14","U-15",	/* 12-15 */
86 	"Sequence",
87 #define SEQUENCE 16
88 	"Set"
89 };
90 
91 /*
92  * Application-wide ASN.1 types from the Internet SMI and their tags
93  */
94 char *Application[] = {
95 	"IpAddress",
96 #define IPADDR 0
97 	"Counter",
98 #define COUNTER 1
99 	"Gauge",
100 #define GAUGE 2
101 	"TimeTicks",
102 #define TIMETICKS 3
103 	"Opaque",
104 #define OPAQUE 4
105 	"NsapAddress",
106 #define NSAPADDR 5
107 	"Counter64",
108 #define COUNTER64 6
109 	"UInteger32" /* Defined in RFC1442, removed since RFC1902 */
110 #define UINTEGER32 7
111 };
112 
113 /*
114  * Context-specific ASN.1 types for the SNMP PDUs and their tags
115  */
116 char *Context[] = {
117 	"GetRequest",
118 #define GETREQ 0
119 	"GetNextRequest",
120 #define GETNEXTREQ 1
121 	"GetResponse",
122 #define GETRESP 2
123 	"SetRequest",
124 #define SETREQ 3
125 	"Trap",
126 #define TRAP 4
127 	"GetBulkReq",
128 #define GETBULKREQ 5
129 	"InformReq",
130 #define INFORMREQ 6
131 	"TrapV2",
132 #define TRAPV2 7
133 	"Report"
134 #define REPORT 8
135 };
136 
137 char *ContextVarbind[] = {
138 	"noSuchObject",
139 #define NOSUCHOBJECT 0
140 	"noSuchInstance",
141 #define NOSUCHINSTANCE 1
142 	"endOfMibView"
143 #define ENDOFMIBVIEW 2
144 };
145 
146 /*
147  * Private ASN.1 types
148  * The Internet SMI does not specify any
149  */
150 char *Private[] = {
151 	"P-0"
152 };
153 
154 /*
155  * error-status values for any SNMP PDU
156  */
157 char *ErrorStatus[] = {
158 	"noError",
159 	"tooBig",
160 	"noSuchName",
161 	"badValue",
162 	"readOnly",
163 	"genErr",
164 	"noAccess",
165 	"wrongType",
166 	"wrongLength",
167 	"wrongEnc",
168 	"wrongValue",
169 	"noCreation",
170 	"inconValue",
171 	"resUnavail",
172 	"commitFailed",
173 	"undoFailed",
174 	"authError",
175 	"notWritable",
176 	"inconName"
177 };
178 #define DECODE_ErrorStatus(e) \
179 	( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
180 	? ErrorStatus[e] : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
181 
182 /*
183  * generic-trap values in the SNMP Trap-PDU
184  */
185 char *GenericTrap[] = {
186 	"coldStart",
187 	"warmStart",
188 	"linkDown",
189 	"linkUp",
190 	"authenticationFailure",
191 	"egpNeighborLoss",
192 	"enterpriseSpecific"
193 #define GT_ENTERPRISE 6
194 };
195 #define DECODE_GenericTrap(t) \
196 	( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
197 	? GenericTrap[t] : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
198 
199 /*
200  * ASN.1 type class table
201  * Ties together the preceding Universal, Application, Context, and Private
202  * type definitions.
203  */
204 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
205 struct {
206 	char	*name;
207 	char	**Id;
208 	int	numIDs;
209     } Class[] = {
210 	defineCLASS(Universal),
211 #define	UNIVERSAL	0
212 	defineCLASS(Application),
213 #define	APPLICATION	1
214 	defineCLASS(Context),
215 #define	CONTEXT		2
216 	defineCLASS(Private),
217 #define	PRIVATE		3
218 };
219 
220 /*
221  * defined forms for ASN.1 types
222  */
223 char *Form[] = {
224 	"Primitive",
225 #define PRIMITIVE	0
226 	"Constructed",
227 #define CONSTRUCTED	1
228 };
229 
230 /*
231  * A structure for the OID tree for the compiled-in MIB.
232  * This is stored as a general-order tree.
233  */
234 struct obj {
235 	char	*desc;			/* name of object */
236 	u_int	oid;			/* sub-id following parent */
237 	u_char	type;			/* object type (unused) */
238 	struct obj *child, *next;	/* child and next sibling pointers */
239 } *objp = NULL;
240 
241 /*
242  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
243  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
244  * a value for `mibroot'.
245  *
246  * In particular, this is gross, as this is including initialized structures,
247  * and by right shouldn't be an "include" file.
248  */
249 #include "mib.h"
250 
251 /*
252  * This defines a list of OIDs which will be abbreviated on output.
253  * Currently, this includes the prefixes for the Internet MIB, the
254  * private enterprises tree, and the experimental tree.
255  */
256 struct obj_abrev {
257 	char *prefix;			/* prefix for this abrev */
258 	struct obj *node;		/* pointer into object table */
259 	char *oid;			/* ASN.1 encoded OID */
260 } obj_abrev_list[] = {
261 #ifndef NO_ABREV_MIB
262 	/* .iso.org.dod.internet.mgmt.mib */
263 	{ "",	&_mib_obj,		"\53\6\1\2\1" },
264 #endif
265 #ifndef NO_ABREV_ENTER
266 	/* .iso.org.dod.internet.private.enterprises */
267 	{ "E:",	&_enterprises_obj,	"\53\6\1\4\1" },
268 #endif
269 #ifndef NO_ABREV_EXPERI
270 	/* .iso.org.dod.internet.experimental */
271 	{ "X:",	&_experimental_obj,	"\53\6\1\3" },
272 #endif
273 #ifndef NO_ABREV_SNMPMIBOBJECTS
274 	/* .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects */
275 	{ "S:", &_snmpmibobjects_obj,	"\53\6\1\6\3\1\1" },
276 #endif
277 	{ 0,0,0 }
278 };
279 
280 /*
281  * This is used in the OID print routine to walk down the object tree
282  * rooted at `mibroot'.
283  */
284 #define OBJ_PRINT(o, suppressdot) \
285 { \
286 	if (objp) { \
287 		do { \
288 			if ((o) == objp->oid) \
289 				break; \
290 		} while ((objp = objp->next) != NULL); \
291 	} \
292 	if (objp) { \
293 		printf(suppressdot?"%s":".%s", objp->desc); \
294 		objp = objp->child; \
295 	} else \
296 		printf(suppressdot?"%u":".%u", (o)); \
297 }
298 
299 /*
300  * This is the definition for the Any-Data-Type storage used purely for
301  * temporary internal representation while decoding an ASN.1 data stream.
302  */
303 struct be {
304 	u_int32_t asnlen;
305 	union {
306 		caddr_t raw;
307 		int32_t integer;
308 		u_int32_t uns;
309 		u_int64_t uns64;
310 		const u_char *str;
311 	} data;
312 	u_short id;
313 	u_char form, class;		/* tag info */
314 	u_char type;
315 #define BE_ANY		255
316 #define BE_NONE		0
317 #define BE_NULL		1
318 #define BE_OCTET	2
319 #define BE_OID		3
320 #define BE_INT		4
321 #define BE_UNS		5
322 #define BE_STR		6
323 #define BE_SEQ		7
324 #define BE_INETADDR	8
325 #define BE_PDU		9
326 #define BE_UNS64	10
327 #define BE_VB		11
328 };
329 
330 
331 /*
332  * SNMP components
333  */
334 static int snmp3_print_usmparams(const u_char *, u_int);
335 
336 enum snmp_version {
337 	SNMP_V1 = 0,
338 	SNMP_V2C = 1,
339 	SNMP_V3 = 3
340 };
341 #define SNMP3_AUTH(f) (f & 0x01)
342 #define SNMP3_PRIV(f) (f & 0x02)
343 #define SNMP3_REPORT(f) (f & 0x04)
344 
345 struct snmp3_sm {
346 	int id;
347 	char *name;
348 	int (*parse_params)(const u_char *, u_int);
349 } snmp3_securitymodel[] = {
350 	{3, "USM", snmp3_print_usmparams},
351 	{0, NULL, NULL}
352 };
353 
354 /*
355  * constants for ASN.1 decoding
356  */
357 #define OIDMUX 40
358 #define ASNLEN_INETADDR 4
359 #define ASN_SHIFT7 7
360 #define ASN_SHIFT8 8
361 #define ASN_BIT8 0x80
362 #define ASN_LONGLEN 0x80
363 
364 #define ASN_ID_BITS 0x1f
365 #define ASN_FORM_BITS 0x20
366 #define ASN_FORM_SHIFT 5
367 #define ASN_CLASS_BITS 0xc0
368 #define ASN_CLASS_SHIFT 6
369 
370 #define ASN_ID_EXT 0x1f		/* extension ID in tag field */
371 
372 /*
373  * truncated==1 means the packet was complete, but we don't have all of
374  * it to decode.
375  */
376 static int truncated;
377 
378 /*
379  * This decodes the next ASN.1 object in the stream pointed to by "p"
380  * (and of real-length "len") and stores the intermediate data in the
381  * provided BE object.
382  *
383  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
384  * O/w, this returns the number of bytes parsed from "p".
385  */
386 static int
asn1_parse(const u_char * p,u_int len,struct be * elem)387 asn1_parse(const u_char *p, u_int len, struct be *elem)
388 {
389 	u_char form, class, id;
390 	int i, hdr;
391 
392 	elem->asnlen = 0;
393 	elem->type = BE_ANY;
394 	if (len < 1) {
395 		if (truncated)
396 			printf("[|snmp]");
397 		else
398 			printf("[nothing to parse]");
399 		return -1;
400 	}
401 
402 	/*
403 	 * it would be nice to use a bit field, but you can't depend on them.
404 	 *  +---+---+---+---+---+---+---+---+
405 	 *  + class |frm|        id         |
406 	 *  +---+---+---+---+---+---+---+---+
407 	 *    7   6   5   4   3   2   1   0
408 	 */
409 	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
410 #ifdef notdef
411 	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
412 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
413 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
414 #else
415 	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
416 	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
417 #endif
418 	elem->form = form;
419 	elem->class = class;
420 	elem->id = id;
421 	if (vflag > 1)
422 		printf("|%.2x", *p);
423 	p++; len--; hdr = 1;
424 	/* extended tag field */
425 	if (id == ASN_ID_EXT) {
426 		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
427 			if (vflag > 1)
428 				printf("|%.2x", *p);
429 			id = (id << 7) | (*p & ~ASN_BIT8);
430 		}
431 		if (len == 0 && *p & ASN_BIT8) {
432 			if (truncated)
433 				printf("[|snmp]");
434 			else
435 				printf("[Xtagfield?]");
436 			return -1;
437 		}
438 		elem->id = id = (id << 7) | *p;
439 		--len;
440 		++hdr;
441 		++p;
442 	}
443 	if (len < 1) {
444 		if (truncated)
445 			printf("[|snmp]");
446 		else
447 			printf("[no asnlen]");
448 		return -1;
449 	}
450 	elem->asnlen = *p;
451 	if (vflag > 1)
452 		printf("|%.2x", *p);
453 	p++; len--; hdr++;
454 	if (elem->asnlen & ASN_BIT8) {
455 		int noct = elem->asnlen % ASN_BIT8;
456 		elem->asnlen = 0;
457 		if (len < noct) {
458 			if (truncated)
459 				printf("[|snmp]");
460 			else
461 				printf("[asnlen? %d<%d]", len, noct);
462 			return -1;
463 		}
464 		for (; noct-- > 0; len--, hdr++) {
465 			if (vflag > 1)
466 				printf("|%.2x", *p);
467 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
468 		}
469 	}
470 	if (len < elem->asnlen) {
471 		if (!truncated) {
472 			printf("[len%d<asnlen%u]", len, elem->asnlen);
473 			return -1;
474 		}
475 		/* maybe should check at least 4? */
476 		elem->asnlen = len;
477 	}
478 	if (form >= sizeof(Form)/sizeof(Form[0])) {
479 		if (truncated)
480 			printf("[|snmp]");
481 		else
482 			printf("[form?%d]", form);
483 		return -1;
484 	}
485 	if (class >= sizeof(Class)/sizeof(Class[0])) {
486 		if (truncated)
487 			printf("[|snmp]");
488 		else
489 			printf("[class?%c/%d]", *Form[form], class);
490 		return -1;
491 	}
492 	if ((int)id >= Class[class].numIDs) {
493 		if (truncated)
494 			printf("[|snmp]");
495 		else
496 			printf("[id?%c/%s/%d]", *Form[form],
497 			    Class[class].name, id);
498 		return -1;
499 	}
500 
501 	switch (form) {
502 	case PRIMITIVE:
503 		switch (class) {
504 		case UNIVERSAL:
505 			switch (id) {
506 			case STRING:
507 				elem->type = BE_STR;
508 				elem->data.str = p;
509 				break;
510 
511 			case INTEGER: {
512 				int32_t data;
513 				elem->type = BE_INT;
514 				data = 0;
515 
516 				if (*p & ASN_BIT8)	/* negative */
517 					data = -1;
518 				for (i = elem->asnlen; i-- > 0; p++)
519 					data = (data << ASN_SHIFT8) | *p;
520 				elem->data.integer = data;
521 				break;
522 			}
523 
524 			case OBJECTID:
525 				elem->type = BE_OID;
526 				elem->data.raw = (caddr_t)p;
527 				break;
528 
529 			case ASN_NULL:
530 				elem->type = BE_NULL;
531 				elem->data.raw = NULL;
532 				break;
533 
534 			default:
535 				elem->type = BE_OCTET;
536 				elem->data.raw = (caddr_t)p;
537 				printf("[P/U/%s]",
538 					Class[class].Id[id]);
539 				break;
540 			}
541 			break;
542 
543 		case APPLICATION:
544 			switch (id) {
545 			case IPADDR:
546 				elem->type = BE_INETADDR;
547 				elem->data.raw = (caddr_t)p;
548 				break;
549 
550 			case COUNTER:
551 			case GAUGE:
552 			case TIMETICKS:
553 			case OPAQUE:
554 			case NSAPADDR:
555 			case UINTEGER32: {
556 				u_int32_t data;
557 				elem->type = BE_UNS;
558 				data = 0;
559 				for (i = elem->asnlen; i-- > 0; p++)
560 					data = (data << 8) + *p;
561 				elem->data.uns = data;
562 				break;
563 			}
564 
565 			case COUNTER64: {
566 				u_int64_t data;
567 				elem->type = BE_UNS64;
568 				data = 0;
569 				for (i = elem->asnlen; i-- > 0; p++)
570 					data = (data << 8) + *p;
571 				elem->data.uns64 = data;
572 				break;
573 			}
574 
575 			default:
576 				elem->type = BE_OCTET;
577 				elem->data.raw = (caddr_t)p;
578 				printf("[P/A/%s]",
579 					Class[class].Id[id]);
580 				break;
581 			}
582 			break;
583 
584 		case CONTEXT:
585 			switch (id) {
586 			case NOSUCHOBJECT:
587 			case NOSUCHINSTANCE:
588 			case ENDOFMIBVIEW:
589 				elem->type = BE_VB;
590 				elem->data.raw = NULL;
591 				break;
592 			default:
593 				elem->type = BE_OCTET;
594 				elem->data.raw = (caddr_t)p;
595 				printf("[P/C/%d]", id);
596 				break;
597 			}
598 			break;
599 
600 		default:
601 			elem->type = BE_OCTET;
602 			elem->data.raw = (caddr_t)p;
603 			printf("[P/%s/%s]",
604 				Class[class].name, Class[class].Id[id]);
605 			break;
606 		}
607 		break;
608 
609 	case CONSTRUCTED:
610 		switch (class) {
611 		case UNIVERSAL:
612 			switch (id) {
613 			case SEQUENCE:
614 				elem->type = BE_SEQ;
615 				elem->data.raw = (caddr_t)p;
616 				break;
617 
618 			default:
619 				elem->type = BE_OCTET;
620 				elem->data.raw = (caddr_t)p;
621 				printf("C/U/%s", Class[class].Id[id]);
622 				break;
623 			}
624 			break;
625 
626 		case CONTEXT:
627 			elem->type = BE_PDU;
628 			elem->data.raw = (caddr_t)p;
629 			break;
630 
631 		default:
632 			elem->type = BE_OCTET;
633 			elem->data.raw = (caddr_t)p;
634 			printf("C/%s/%s",
635 				Class[class].name, Class[class].Id[id]);
636 			break;
637 		}
638 		break;
639 	}
640 	p += elem->asnlen;
641 	len -= elem->asnlen;
642 	return elem->asnlen + hdr;
643 }
644 
645 /*
646  * Display the ASN.1 object represented by the BE object.
647  * This used to be an integral part of asn1_parse() before the intermediate
648  * BE form was added.
649  */
650 static void
asn1_print(struct be * elem)651 asn1_print(struct be *elem)
652 {
653 	u_char *p = (u_char *)elem->data.raw;
654 	u_int32_t asnlen = elem->asnlen;
655 	int i;
656 
657 	switch (elem->type) {
658 	case BE_OCTET:
659 		for (i = asnlen; i-- > 0; p++)
660 			printf("_%.2x", *p);
661 		break;
662 	case BE_NULL:
663 		break;
664 	case BE_OID: {
665 		int o = 0, first = -1, i = asnlen;
666 
667 		if (!nflag && asnlen > 2) {
668 			struct obj_abrev *a = &obj_abrev_list[0];
669 			for (; a->node; a++) {
670 				if (!memcmp(a->oid, (char *)p,
671 				    strlen(a->oid))) {
672 					objp = a->node->child;
673 					i -= strlen(a->oid);
674 					p += strlen(a->oid);
675 					printf("%s", a->prefix);
676 					first = 1;
677 					break;
678 				}
679 			}
680 		}
681 		for (; i-- > 0; p++) {
682 			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
683 			if (*p & ASN_LONGLEN)
684 				continue;
685 
686 			/*
687 			 * first subitem encodes two items with 1st*OIDMUX+2nd
688 			 */
689 			if (first < 0) {
690 				if (!nflag)
691 					objp = mibroot;
692 				first = 0;
693 				OBJ_PRINT(o/OIDMUX, first);
694 				o %= OIDMUX;
695 			}
696 			OBJ_PRINT(o, first);
697 			if (--first < 0)
698 				first = 0;
699 			o = 0;
700 		}
701 		break;
702 	}
703 	case BE_INT:
704 		printf("%d", elem->data.integer);
705 		break;
706 	case BE_UNS:
707 		printf("%d", elem->data.uns);
708 		break;
709 	case BE_UNS64:
710 		printf("%lld", elem->data.uns64);
711 		break;
712 	case BE_STR: {
713 		int printable = 1, first = 1;
714 		const u_char *p = elem->data.str;
715 		for (i = asnlen; printable && i-- > 0; p++)
716 			printable = isprint(*p) || isspace(*p);
717 		p = elem->data.str;
718 		if (printable) {
719 			putchar('"');
720 			(void)fn_print(p, p + asnlen);
721 			putchar('"');
722 		} else
723 			for (i = asnlen; i-- > 0; p++) {
724 				printf(first ? "%.2x" : "_%.2x", *p);
725 				first = 0;
726 			}
727 		break;
728 	}
729 	case BE_SEQ:
730 		printf("Seq(%u)", elem->asnlen);
731 		break;
732 	case BE_INETADDR: {
733 		char sep;
734 		if (asnlen != ASNLEN_INETADDR)
735 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
736 		sep='[';
737 		for (i = asnlen; i-- > 0; p++) {
738 			printf("%c%u", sep, *p);
739 			sep='.';
740 		}
741 		putchar(']');
742 		break;
743 	}
744 	case BE_PDU:
745 		printf("%s(%u)",
746 			Class[CONTEXT].Id[elem->id], elem->asnlen);
747 		break;
748 	case BE_VB:
749 		if (elem->id >= sizeof(ContextVarbind)/sizeof(ContextVarbind[0]))
750 			break;
751 		printf("%s", ContextVarbind[elem->id]);
752 		break;
753 	case BE_ANY:
754 		printf("[BE_ANY!?]");
755 		break;
756 	default:
757 		printf("[be!?]");
758 		break;
759 	}
760 }
761 
762 #ifdef notdef
763 /*
764  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
765  * This will work for any ASN.1 stream, not just an SNMP PDU.
766  *
767  * By adding newlines and spaces at the correct places, this would print in
768  * Rose-Normal-Form.
769  *
770  * This is not currently used.
771  */
772 static void
asn1_decode(u_char * p,u_int length)773 asn1_decode(u_char *p, u_int length)
774 {
775 	struct be elem;
776 	int i = 0;
777 
778 	while (i >= 0 && length > 0) {
779 		i = asn1_parse(p, length, &elem);
780 		if (i >= 0) {
781 			printf(" ");
782 			asn1_print(&elem);
783 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
784 				printf(" {");
785 				asn1_decode(elem.data.raw, elem.asnlen);
786 				printf(" }");
787 			}
788 			length -= i;
789 			p += i;
790 		}
791 	}
792 }
793 #endif
794 
795 /*
796  * General SNMP header
797  *	SEQUENCE {
798  *		version INTEGER {version-1(0)},
799  *		community OCTET STRING,
800  *		data ANY	-- PDUs
801  *	}
802  * PDUs for all but Trap: (see rfc1157 from page 15 on)
803  *	SEQUENCE {
804  *		request-id INTEGER,
805  *		error-status INTEGER,
806  *		error-index INTEGER,
807  *		varbindlist SEQUENCE OF
808  *			SEQUENCE {
809  *				name ObjectName,
810  *				value ObjectValue
811  *			}
812  *	}
813  * PDU for Trap:
814  *	SEQUENCE {
815  *		enterprise OBJECT IDENTIFIER,
816  *		agent-addr NetworkAddress,
817  *		generic-trap INTEGER,
818  *		specific-trap INTEGER,
819  *		time-stamp TimeTicks,
820  *		varbindlist SEQUENCE OF
821  *			SEQUENCE {
822  *				name ObjectName,
823  *				value ObjectValue
824  *			}
825  *	}
826  */
827 
828 /*
829  * Decode SNMP varBind
830  */
831 static void
varbind_print(u_char pduid,const u_char * np,u_int length,int error)832 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
833 {
834 	struct be elem;
835 	int count = 0, ind;
836 
837 	/* Sequence of varBind */
838 	if ((count = asn1_parse(np, length, &elem)) < 0)
839 		return;
840 	if (elem.type != BE_SEQ) {
841 		printf("[!SEQ of varbind]");
842 		asn1_print(&elem);
843 		return;
844 	}
845 	if (count < length)
846 		printf("[%d extra after SEQ of varbind]", length - count);
847 	/* descend */
848 	length = elem.asnlen;
849 	np = (u_char *)elem.data.raw;
850 
851 	for (ind = 1; length > 0; ind++) {
852 		const u_char *vbend;
853 		u_int vblength;
854 
855 		putchar(' ');
856 		/* Sequence */
857 		if ((count = asn1_parse(np, length, &elem)) < 0)
858 			return;
859 		if (elem.type != BE_SEQ) {
860 			printf("[!varbind]");
861 			asn1_print(&elem);
862 			return;
863 		}
864 		vbend = np + count;
865 		vblength = length - count;
866 		/* descend */
867 		length = elem.asnlen;
868 		np = (u_char *)elem.data.raw;
869 
870 		/* objName (OID) */
871 		if ((count = asn1_parse(np, length, &elem)) < 0)
872 			return;
873 		if (elem.type != BE_OID) {
874 			printf("[objName!=OID]");
875 			asn1_print(&elem);
876 			return;
877 		}
878 		if (!error || ind == error)
879 			asn1_print(&elem);
880 		length -= count;
881 		np += count;
882 
883 		if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
884 				printf("=");
885 
886 		/* objVal (ANY) */
887 		if ((count = asn1_parse(np, length, &elem)) < 0)
888 			return;
889 		if (pduid == GETREQ || pduid == GETNEXTREQ || pduid == GETBULKREQ) {
890 			if (elem.type != BE_NULL) {
891 				printf("[objVal!=NULL]");
892 				asn1_print(&elem);
893 			}
894 		} else {
895 			if (error && ind == error && elem.type != BE_NULL)
896 				printf("[err objVal!=NULL]");
897 			if (!error || ind == error)
898 				asn1_print(&elem);
899 		}
900 
901 		length = vblength;
902 		np = vbend;
903 	}
904 }
905 
906 /*
907  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
908  */
909 static void
snmppdu_print(u_char pduid,const u_char * np,u_int length)910 snmppdu_print(u_char pduid, const u_char *np, u_int length)
911 {
912 	struct be elem;
913 	int count = 0, error;
914 
915 	/* reqId (Integer) */
916 	if ((count = asn1_parse(np, length, &elem)) < 0)
917 		return;
918 	if (elem.type != BE_INT) {
919 		printf("[reqId!=INT]");
920 		asn1_print(&elem);
921 		return;
922 	}
923 	if (vflag)
924 		printf(" reqId=%d", elem.data.integer);
925 	length -= count;
926 	np += count;
927 
928 	/* errorStatus (Integer) */
929 	if ((count = asn1_parse(np, length, &elem)) < 0)
930 		return;
931 	if (elem.type != BE_INT) {
932 		printf("[errorStatus!=INT]");
933 		asn1_print(&elem);
934 		return;
935 	}
936 	error = 0;
937 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
938 	    && elem.data.integer != 0) {
939 		char errbuf[20];
940 		printf("[errorStatus(%s)!=0]",
941 			DECODE_ErrorStatus(elem.data.integer));
942 	} else if (pduid == GETBULKREQ)
943 		printf(" non-repeaters=%d", elem.data.integer);
944 	else if (elem.data.integer != 0) {
945 		char errbuf[20];
946 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
947 		error = elem.data.integer;
948 	}
949 	length -= count;
950 	np += count;
951 
952 	/* errorIndex (Integer) */
953 	if ((count = asn1_parse(np, length, &elem)) < 0)
954 		return;
955 	if (elem.type != BE_INT) {
956 		printf("[errorIndex!=INT]");
957 		asn1_print(&elem);
958 		return;
959 	}
960 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
961 	    && elem.data.integer != 0)
962 		printf("[errorIndex(%d)!=0]", elem.data.integer);
963 	else if (pduid == GETBULKREQ)
964 		printf(" max-repetitions=%d", elem.data.integer);
965 	else if (elem.data.integer != 0) {
966 		if (!error)
967 			printf("[errorIndex(%d) w/o errorStatus]",
968 				elem.data.integer);
969 		else {
970 			printf("@%d", elem.data.integer);
971 			error = elem.data.integer;
972 		}
973 	} else if (error) {
974 		printf("[errorIndex==0]");
975 		error = 0;
976 	}
977 	length -= count;
978 	np += count;
979 
980 	varbind_print(pduid, np, length, error);
981 	return;
982 }
983 
984 /*
985  * Decode SNMP Trap PDU
986  */
987 static void
trap_print(const u_char * np,u_int length)988 trap_print(const u_char *np, u_int length)
989 {
990 	struct be elem;
991 	int count = 0, generic;
992 
993 	putchar(' ');
994 
995 	/* enterprise (oid) */
996 	if ((count = asn1_parse(np, length, &elem)) < 0)
997 		return;
998 	if (elem.type != BE_OID) {
999 		printf("[enterprise!=OID]");
1000 		asn1_print(&elem);
1001 		return;
1002 	}
1003 	asn1_print(&elem);
1004 	length -= count;
1005 	np += count;
1006 
1007 	putchar(' ');
1008 
1009 	/* agent-addr (inetaddr) */
1010 	if ((count = asn1_parse(np, length, &elem)) < 0)
1011 		return;
1012 	if (elem.type != BE_INETADDR) {
1013 		printf("[agent-addr!=INETADDR]");
1014 		asn1_print(&elem);
1015 		return;
1016 	}
1017 	asn1_print(&elem);
1018 	length -= count;
1019 	np += count;
1020 
1021 	/* generic-trap (Integer) */
1022 	if ((count = asn1_parse(np, length, &elem)) < 0)
1023 		return;
1024 	if (elem.type != BE_INT) {
1025 		printf("[generic-trap!=INT]");
1026 		asn1_print(&elem);
1027 		return;
1028 	}
1029 	generic = elem.data.integer;
1030 	{
1031 		char buf[20];
1032 		printf(" %s", DECODE_GenericTrap(generic));
1033 	}
1034 	length -= count;
1035 	np += count;
1036 
1037 	/* specific-trap (Integer) */
1038 	if ((count = asn1_parse(np, length, &elem)) < 0)
1039 		return;
1040 	if (elem.type != BE_INT) {
1041 		printf("[specific-trap!=INT]");
1042 		asn1_print(&elem);
1043 		return;
1044 	}
1045 	if (generic != GT_ENTERPRISE) {
1046 		if (elem.data.integer != 0)
1047 			printf("[specific-trap(%d)!=0]", elem.data.integer);
1048 	} else
1049 		printf(" s=%d", elem.data.integer);
1050 	length -= count;
1051 	np += count;
1052 
1053 	putchar(' ');
1054 
1055 	/* time-stamp (TimeTicks) */
1056 	if ((count = asn1_parse(np, length, &elem)) < 0)
1057 		return;
1058 	if (elem.type != BE_UNS) {			/* XXX */
1059 		printf("[time-stamp!=TIMETICKS]");
1060 		asn1_print(&elem);
1061 		return;
1062 	}
1063 	asn1_print(&elem);
1064 	length -= count;
1065 	np += count;
1066 
1067 	varbind_print (TRAP, np, length, 0);
1068 	return;
1069 }
1070 
1071 /*
1072  * Decode SNMP header and pass on to PDU printing routines
1073  */
1074 static void
snmp12_print(const u_char * np,u_int length)1075 snmp12_print(const u_char *np, u_int length)
1076 {
1077 	struct be elem;
1078 	int count;
1079 
1080 	/* Community (String) */
1081 	if ((count = asn1_parse(np, length, &elem)) < 0)
1082 		return;
1083 	if (elem.type != BE_STR) {
1084 		printf("[comm!=STR]");
1085 		asn1_print(&elem);
1086 		return;
1087 	}
1088 	printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1089 	length -= count;
1090 	np += count;
1091 
1092 	/* PDU (Context) */
1093 	if ((count = asn1_parse(np, length, &elem)) < 0)
1094 		return;
1095 	if (elem.type != BE_PDU) {
1096 		printf("[no PDU]");
1097 		return;
1098 	}
1099 	if (count < length)
1100 		printf("[%d extra after PDU]", length - count);
1101 	asn1_print(&elem);
1102 	/* descend into PDU */
1103 	length = elem.asnlen;
1104 	np = (u_char *)elem.data.raw;
1105 
1106 	switch (elem.id) {
1107 	case TRAP:
1108 		trap_print(np, length);
1109 		break;
1110 	case GETREQ:
1111 	case GETNEXTREQ:
1112 	case GETRESP:
1113 	case SETREQ:
1114 	case GETBULKREQ:
1115 	case INFORMREQ:
1116 	case TRAPV2:
1117 	case REPORT:
1118 		snmppdu_print(elem.id, np, length);
1119 		break;
1120 	}
1121 	return;
1122 }
1123 
1124 static int
snmp3_print_usmparams(const u_char * np,u_int length)1125 snmp3_print_usmparams(const u_char *np, u_int length)
1126 {
1127 	struct be elem;
1128 	int count;
1129 	int i;
1130 
1131 	if ((count = asn1_parse(np, length, &elem)) < 0)
1132 		return -1;
1133 	if (elem.type != BE_SEQ) {
1134 		printf("[!usmSM SEQ]");
1135 		asn1_print(&elem);
1136 		return -1;
1137 	}
1138 	if (count < length) {
1139 		printf("[%d extra after usmSM]", length - count);
1140 		return -1;
1141 	}
1142 	/* descend */
1143 	length = elem.asnlen;
1144 	np = (u_char *)elem.data.raw;
1145 
1146 	/* msgAuthoritativeEngineID */
1147 	if ((count = asn1_parse(np, length, &elem)) < 0)
1148 		return -1;
1149 	if (elem.type != BE_STR) {
1150 		printf("[umsEID!=STR]");
1151 		asn1_print(&elem);
1152 		return -1;
1153 	}
1154 	if (vflag && elem.asnlen > 0) {
1155 		printf("umsEID=0x");
1156 		for (i = 0; i < elem.asnlen; i++)
1157 			printf("%02hhX", elem.data.str[i]);
1158 		putchar(' ');
1159 	}
1160 	length -= count;
1161 	np += count;
1162 
1163 	/* msgAuthoritativeEngineBoots */
1164 	if ((count = asn1_parse(np, length, &elem)) < 0)
1165 		return -1;
1166 	if (elem.type != BE_INT) {
1167 		printf("[EBoots!=INT]");
1168 		asn1_print(&elem);
1169 		return -1;
1170 	}
1171 	if (vflag)
1172 		printf("EBoots=%d ", elem.data.integer);
1173 	length -= count;
1174 	np += count;
1175 
1176 	/* msgAuthoritativeEngineTime */
1177 	if ((count = asn1_parse(np, length, &elem)) < 0)
1178 		return -1;
1179 	if (elem.type != BE_INT) {
1180 		printf("[ETime!=INT]");
1181 		asn1_print(&elem);
1182 		return -1;
1183 	}
1184 	if (vflag)
1185 		printf("ETime=%d ", elem.data.integer);
1186 	length -= count;
1187 	np += count;
1188 
1189 	if ((count = asn1_parse(np, length, &elem)) < 0)
1190 		return -1;
1191 	if (elem.type != BE_STR) {
1192 		printf("[User!=STR]");
1193 		asn1_print(&elem);
1194 		return -1;
1195 	}
1196 	if (elem.asnlen > 0) {
1197 		printf("User=");
1198 		asn1_print(&elem);
1199 		putchar(' ');
1200 	}
1201 	length -= count;
1202 	np += count;
1203 
1204 	/* msgAuthenticationParameters */
1205 	if ((count = asn1_parse(np, length, &elem)) < 0)
1206 		return -1;
1207 	if (elem.type != BE_STR) {
1208 		printf("[AuthParam!=STR]");
1209 		asn1_print(&elem);
1210 		return -1;
1211 	}
1212 	/* Can't validate msgAuthenticationParameters without pass */
1213 	length -= count;
1214 	np += count;
1215 
1216 	/* msgPrivacyParameters */
1217 	if ((count = asn1_parse(np, length, &elem)) < 0)
1218 		return -1;
1219 	if (elem.type != BE_STR) {
1220 		printf("[PrivParam!=STR]");
1221 		asn1_print(&elem);
1222 		return -1;
1223 	}
1224 	/* Salt is not useful if we can't decrypt */
1225 	if (length - count != 0) {
1226 		printf("[%d extra after usmSM]", length - count);
1227 		return -1;
1228 	}
1229 	return 0;
1230 }
1231 
1232 static void
snmp3_print(const u_char * np,u_int length)1233 snmp3_print(const u_char *np, u_int length)
1234 {
1235 	struct be elem;
1236 	struct snmp3_sm *sm = NULL;
1237 	int count;
1238 	int sublen;
1239 	int i;
1240 	int authpriv;
1241 	u_char *subnp;
1242 
1243 	/* Header sequence */
1244 	if ((count = asn1_parse(np, length, &elem)) < 0)
1245 		return;
1246 	if (elem.type != BE_SEQ) {
1247 		printf("[!header SEQ]");
1248 		asn1_print(&elem);
1249 		return;
1250 	}
1251 	np += count;
1252 	length -= count;
1253 	/* descend */
1254 	/* msgID */
1255 	sublen = elem.asnlen;
1256 	subnp = (u_char *)elem.data.raw;
1257 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1258 		return;
1259 	if (elem.type != BE_INT) {
1260 		printf("[msgID!=INT]");
1261 		asn1_print(&elem);
1262 		return;
1263 	}
1264 	if (vflag)
1265 		printf("msgID=%d ", elem.data.integer);
1266 	sublen -= count;
1267 	subnp += count;
1268 
1269 	/* msgMaxSize */
1270 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1271 		return;
1272 	if (elem.type != BE_INT) {
1273 		printf("[msgMS!=INT]");
1274 		asn1_print(&elem);
1275 		return;
1276 	}
1277 	if (vflag)
1278 		printf("msgMS=%d ", elem.data.integer);
1279 	sublen -= count;
1280 	subnp += count;
1281 
1282 	/* msgFlags */
1283 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1284 		return;
1285 	if (elem.type != BE_STR) {
1286 		printf("[msgFl!=STR]");
1287 		asn1_print(&elem);
1288 		return;
1289 	}
1290 	if (elem.asnlen != 1)
1291 		printf("[%d extra after msgFl]", elem.asnlen - 1);
1292 	authpriv = *elem.data.str & 0x3;
1293 	if (vflag && (*elem.data.str & 0x7) != 0) {
1294 		printf("(%suth%sPriv%s) ",
1295 		    SNMP3_AUTH(*elem.data.str) ? "a" : "noA",
1296 		    SNMP3_PRIV(*elem.data.str) ? "" : "No",
1297 		    SNMP3_REPORT(*elem.data.str) ? "|Reportable" : ""
1298 		);
1299 	}
1300 	sublen -= count;
1301 	subnp += count;
1302 
1303 	/* msgSecurityModel */
1304 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1305 		return;
1306 	if (elem.type != BE_INT) {
1307 		printf("[msgSM!=INT]");
1308 		asn1_print(&elem);
1309 		return;
1310 	}
1311 	for (i = 0; snmp3_securitymodel[i].id != 0; i++) {
1312 		if (snmp3_securitymodel[i].id == elem.data.integer) {
1313 			sm = &(snmp3_securitymodel[i]);
1314 			break;
1315 		}
1316 	}
1317 	if (vflag) {
1318 		if (sm != NULL && nflag == 0)
1319 			printf("msgSM=%s ", sm->name);
1320 		else
1321 			printf("msgSM=%d ", elem.data.integer);
1322 	}
1323 	if (sublen - count != 0) {
1324 		printf("[%d extra after header]", sublen - count);
1325 		return;
1326 	}
1327 
1328 	/* ascend */
1329 	if ((count = asn1_parse(np, length, &elem)) < 0)
1330 		return;
1331 	if (elem.type != BE_STR) {
1332 		printf("msgSP!=STR]");
1333 		asn1_print(&elem);
1334 		return;
1335 	}
1336 	if (sm != NULL && sm->parse_params != NULL) {
1337 		if (sm->parse_params(elem.data.raw, elem.asnlen) == -1)
1338 			return;
1339 	}
1340 	length -= count;
1341 	np += count;
1342 
1343 	if (SNMP3_PRIV(authpriv) != 0) {
1344 		printf("[encrypted PDU]");
1345 		return;
1346 	}
1347 
1348 	/* msgData */
1349 	if ((count = asn1_parse(np, length, &elem)) < 0)
1350 		return;
1351 	if (elem.type != BE_SEQ) {
1352 		printf("[ScPDU!=SEQ]");
1353 		asn1_print(&elem);
1354 		return;
1355 	}
1356 	if (count < length)
1357 		printf("[%d extra after ScPDU]", length - count);
1358 	/* descend */
1359 	length = elem.asnlen;
1360 	np = (u_char *)elem.data.raw;
1361 
1362 	/* contextEngineID */
1363 	if ((count = asn1_parse(np, length, &elem)) < 0)
1364 		return;
1365 	if (elem.type != BE_STR) {
1366 		printf("ctxEID!=STR]");
1367 		asn1_print(&elem);
1368 		return;
1369 	}
1370 	if (vflag && elem.asnlen > 0) {
1371 		printf("ctxEID=0x");
1372 		for (i = 0; i < elem.asnlen; i++)
1373 			printf("%02hhX", elem.data.str[i]);
1374 		putchar(' ');
1375 	}
1376 	length -= count;
1377 	np += count;
1378 
1379 	/* contextName */
1380 	if ((count = asn1_parse(np, length, &elem)) < 0)
1381 		return;
1382 	if (elem.type != BE_STR) {
1383 		printf("[ctxEName!=STR]");
1384 		asn1_print(&elem);
1385 		return;
1386 	}
1387 	if (vflag && elem.asnlen > 0) {
1388 		printf("ctxName=");
1389 		asn1_print(&elem);
1390 		putchar(' ');
1391 	}
1392 	length -= count;
1393 	np += count;
1394 
1395 	/* Data */
1396 	if ((count = asn1_parse(np, length, &elem)) < 0)
1397 		return;
1398 	if (elem.type != BE_PDU) {
1399 		printf("[data!=PDU]");
1400 		asn1_print(&elem);
1401 		return;
1402 	}
1403 	if (count < length)
1404 		printf("[%d extra after PDU]", length - count);
1405 	asn1_print(&elem);
1406 	/* descend into PDU */
1407 	length = elem.asnlen;
1408 	np = (u_char *)elem.data.raw;
1409 	switch (elem.id) {
1410 	case TRAP:
1411 		trap_print(np, length);
1412 		break;
1413 	case GETREQ:
1414 	case GETNEXTREQ:
1415 	case GETRESP:
1416 	case SETREQ:
1417 	case GETBULKREQ:
1418 	case INFORMREQ:
1419 	case TRAPV2:
1420 	case REPORT:
1421 		snmppdu_print(elem.id, np, length);
1422 		break;
1423 	}
1424 }
1425 
1426 void
snmp_print(const u_char * np,u_int length)1427 snmp_print(const u_char *np, u_int length)
1428 {
1429 	struct be elem;
1430 	int count = 0;
1431 
1432 	truncated = 0;
1433 
1434 	/* truncated packet? */
1435 	if (np + length > snapend) {
1436 		truncated = 1;
1437 		length = snapend - np;
1438 	}
1439 
1440 	/* initial Sequence */
1441 	if ((count = asn1_parse(np, length, &elem)) < 0)
1442 		return;
1443 	if (elem.type != BE_SEQ) {
1444 		printf("[!init SEQ]");
1445 		asn1_print(&elem);
1446 		return;
1447 	}
1448 	if (count < length)
1449 		printf("[%d extra after iSEQ]", length - count);
1450 	/* descend */
1451 	length = elem.asnlen;
1452 	np = (u_char *)elem.data.raw;
1453 	/* Version (Integer) */
1454 	if ((count = asn1_parse(np, length, &elem)) < 0)
1455 		return;
1456 	if (elem.type != BE_INT) {
1457 		printf("[version!=INT]");
1458 		asn1_print(&elem);
1459 		return;
1460 	}
1461 	length -= count;
1462 	np += count;
1463 	switch (elem.data.integer) {
1464 	case SNMP_V1:
1465 	case SNMP_V2C:
1466 		if (vflag)
1467 			printf("SNMPv%s ", elem.data.integer == SNMP_V1 ?
1468 			    "1" : "2c");
1469 		snmp12_print(np, length);
1470 		return;
1471 	case SNMP_V3:
1472 		if (vflag)
1473 			printf("SNMPv3 ");
1474 		snmp3_print(np, length);
1475 		return;
1476 	default:
1477 		printf("[snmp version(%d)]", elem.data.integer);
1478 		return;
1479 	}
1480 }
1481