xref: /freebsd/contrib/tcpdump/print-snmp.c (revision 37c8ee8847faa53432809cae2ecc11b80c4eab2f)
1 /*
2  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3  *     John Robert LoVerso. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  *
28  * This implementation has been influenced by the CMU SNMP release,
29  * by Steve Waldbusser.  However, this shares no code with that system.
30  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
31  * Earlier forms of this implementation were derived and/or inspired by an
32  * awk script originally written by C. Philip Wood of LANL (but later
33  * heavily modified by John Robert LoVerso).  The copyright notice for
34  * that work is preserved below, even though it may not rightly apply
35  * to this file.
36  *
37  * Support for SNMPv2c/SNMPv3 and the ability to link the module against
38  * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
39  *
40  * This started out as a very simple program, but the incremental decoding
41  * (into the BE structure) complicated things.
42  *
43  #			Los Alamos National Laboratory
44  #
45  #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
46  #	This software was produced under a U.S. Government contract
47  #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
48  #	operated by the	University of California for the U.S. Department
49  #	of Energy.  The U.S. Government is licensed to use, reproduce,
50  #	and distribute this software.  Permission is granted to the
51  #	public to copy and use this software without charge, provided
52  #	that this Notice and any statement of authorship are reproduced
53  #	on all copies.  Neither the Government nor the University makes
54  #	any warranty, express or implied, or assumes any liability or
55  #	responsibility for the use of this software.
56  #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
57  */
58 
59 /* \summary: Simple Network Management Protocol (SNMP) printer */
60 
61 #ifdef HAVE_CONFIG_H
62 #include <config.h>
63 #endif
64 
65 #include "netdissect-stdinc.h"
66 
67 #include <stdio.h>
68 #include <string.h>
69 
70 #ifdef USE_LIBSMI
71 #include <smi.h>
72 #endif
73 
74 #include "netdissect-ctype.h"
75 
76 #include "netdissect.h"
77 #include "extract.h"
78 
79 #undef OPAQUE  /* defined in <wingdi.h> */
80 
81 
82 /*
83  * Universal ASN.1 types
84  * (we only care about the tag values for those allowed in the Internet SMI)
85  */
86 static const char *Universal[] = {
87 	"U-0",
88 	"Boolean",
89 	"Integer",
90 #define INTEGER 2
91 	"Bitstring",
92 	"String",
93 #define STRING 4
94 	"Null",
95 #define ASN_NULL 5
96 	"ObjID",
97 #define OBJECTID 6
98 	"ObjectDes",
99 	"U-8","U-9","U-10","U-11",	/* 8-11 */
100 	"U-12","U-13","U-14","U-15",	/* 12-15 */
101 	"Sequence",
102 #define SEQUENCE 16
103 	"Set"
104 };
105 
106 /*
107  * Application-wide ASN.1 types from the Internet SMI and their tags
108  */
109 static const char *Application[] = {
110 	"IpAddress",
111 #define IPADDR 0
112 	"Counter",
113 #define COUNTER 1
114 	"Gauge",
115 #define GAUGE 2
116 	"TimeTicks",
117 #define TIMETICKS 3
118 	"Opaque",
119 #define OPAQUE 4
120 	"C-5",
121 	"Counter64"
122 #define COUNTER64 6
123 };
124 
125 /*
126  * Context-specific ASN.1 types for the SNMP PDUs and their tags
127  */
128 static const char *Context[] = {
129 	"GetRequest",
130 #define GETREQ 0
131 	"GetNextRequest",
132 #define GETNEXTREQ 1
133 	"GetResponse",
134 #define GETRESP 2
135 	"SetRequest",
136 #define SETREQ 3
137 	"Trap",
138 #define TRAP 4
139 	"GetBulk",
140 #define GETBULKREQ 5
141 	"Inform",
142 #define INFORMREQ 6
143 	"V2Trap",
144 #define V2TRAP 7
145 	"Report"
146 #define REPORT 8
147 };
148 
149 #define NOTIFY_CLASS(x)	    (x == TRAP || x == V2TRAP || x == INFORMREQ)
150 #define READ_CLASS(x)       (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
151 #define WRITE_CLASS(x)	    (x == SETREQ)
152 #define RESPONSE_CLASS(x)   (x == GETRESP)
153 #define INTERNAL_CLASS(x)   (x == REPORT)
154 
155 /*
156  * Context-specific ASN.1 types for the SNMP Exceptions and their tags
157  */
158 static const char *Exceptions[] = {
159 	"noSuchObject",
160 #define NOSUCHOBJECT 0
161 	"noSuchInstance",
162 #define NOSUCHINSTANCE 1
163 	"endOfMibView",
164 #define ENDOFMIBVIEW 2
165 };
166 
167 /*
168  * Private ASN.1 types
169  * The Internet SMI does not specify any
170  */
171 static const char *Private[] = {
172 	"P-0"
173 };
174 
175 /*
176  * error-status values for any SNMP PDU
177  */
178 static const char *ErrorStatus[] = {
179 	"noError",
180 	"tooBig",
181 	"noSuchName",
182 	"badValue",
183 	"readOnly",
184 	"genErr",
185 	"noAccess",
186 	"wrongType",
187 	"wrongLength",
188 	"wrongEncoding",
189 	"wrongValue",
190 	"noCreation",
191 	"inconsistentValue",
192 	"resourceUnavailable",
193 	"commitFailed",
194 	"undoFailed",
195 	"authorizationError",
196 	"notWritable",
197 	"inconsistentName"
198 };
199 #define DECODE_ErrorStatus(e) \
200 	( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
201 		? ErrorStatus[e] \
202 		: (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
203 
204 /*
205  * generic-trap values in the SNMP Trap-PDU
206  */
207 static const char *GenericTrap[] = {
208 	"coldStart",
209 	"warmStart",
210 	"linkDown",
211 	"linkUp",
212 	"authenticationFailure",
213 	"egpNeighborLoss",
214 	"enterpriseSpecific"
215 #define GT_ENTERPRISE 6
216 };
217 #define DECODE_GenericTrap(t) \
218 	( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
219 		? GenericTrap[t] \
220 		: (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
221 
222 /*
223  * ASN.1 type class table
224  * Ties together the preceding Universal, Application, Context, and Private
225  * type definitions.
226  */
227 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
228 static const struct {
229 	const char	*name;
230 	const char	**Id;
231 	    int	numIDs;
232     } Class[] = {
233 	defineCLASS(Universal),
234 #define	UNIVERSAL	0
235 	defineCLASS(Application),
236 #define	APPLICATION	1
237 	defineCLASS(Context),
238 #define	CONTEXT		2
239 	defineCLASS(Private),
240 #define	PRIVATE		3
241 	defineCLASS(Exceptions),
242 #define EXCEPTIONS	4
243 };
244 
245 /*
246  * defined forms for ASN.1 types
247  */
248 static const char *Form[] = {
249 	"Primitive",
250 #define PRIMITIVE	0
251 	"Constructed",
252 #define CONSTRUCTED	1
253 };
254 
255 /*
256  * A structure for the OID tree for the compiled-in MIB.
257  * This is stored as a general-order tree.
258  */
259 static struct obj {
260 	const char	*desc;		/* name of object */
261 	u_char	oid;			/* sub-id following parent */
262 	u_char	type;			/* object type (unused) */
263 	struct obj *child, *next;	/* child and next sibling pointers */
264 } *objp = NULL;
265 
266 /*
267  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
268  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
269  * a value for `mibroot'.
270  *
271  * In particular, this is gross, as this is including initialized structures,
272  * and by right shouldn't be an "include" file.
273  */
274 #include "mib.h"
275 
276 /*
277  * This defines a list of OIDs which will be abbreviated on output.
278  * Currently, this includes the prefixes for the Internet MIB, the
279  * private enterprises tree, and the experimental tree.
280  */
281 #define OID_FIRST_OCTET(x, y)	(((x)*40) + (y))	/* X.690 8.19.4 */
282 
283 #ifndef NO_ABREV_MIB
284 static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
285 #endif
286 #ifndef NO_ABREV_ENTER
287 static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
288 #endif
289 #ifndef NO_ABREV_EXPERI
290 static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
291 #endif
292 #ifndef NO_ABBREV_SNMPMODS
293 static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
294 #endif
295 
296 #define OBJ_ABBREV_ENTRY(prefix, obj) \
297 	{ prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
298 static const struct obj_abrev {
299 	const char *prefix;		/* prefix for this abrev */
300 	struct obj *node;		/* pointer into object table */
301 	const uint8_t *oid;		/* ASN.1 encoded OID */
302 	size_t oid_len;			/* length of OID */
303 } obj_abrev_list[] = {
304 #ifndef NO_ABREV_MIB
305 	/* .iso.org.dod.internet.mgmt.mib */
306 	OBJ_ABBREV_ENTRY("",	mib),
307 #endif
308 #ifndef NO_ABREV_ENTER
309 	/* .iso.org.dod.internet.private.enterprises */
310 	OBJ_ABBREV_ENTRY("E:",	enterprises),
311 #endif
312 #ifndef NO_ABREV_EXPERI
313 	/* .iso.org.dod.internet.experimental */
314 	OBJ_ABBREV_ENTRY("X:",	experimental),
315 #endif
316 #ifndef NO_ABBREV_SNMPMODS
317 	/* .iso.org.dod.internet.snmpV2.snmpModules */
318 	OBJ_ABBREV_ENTRY("S:",	snmpModules),
319 #endif
320 	{ 0,0,0,0 }
321 };
322 
323 /*
324  * This is used in the OID print routine to walk down the object tree
325  * rooted at `mibroot'.
326  */
327 #define OBJ_PRINT(o, suppressdot) \
328 { \
329 	if (objp) { \
330 		do { \
331 			if ((o) == objp->oid) \
332 				break; \
333 		} while ((objp = objp->next) != NULL); \
334 	} \
335 	if (objp) { \
336 		ND_PRINT(suppressdot?"%s":".%s", objp->desc); \
337 		objp = objp->child; \
338 	} else \
339 		ND_PRINT(suppressdot?"%u":".%u", (o)); \
340 }
341 
342 /*
343  * This is the definition for the Any-Data-Type storage used purely for
344  * temporary internal representation while decoding an ASN.1 data stream.
345  */
346 struct be {
347 	uint32_t asnlen;
348 	union {
349 		const uint8_t *raw;
350 		int32_t integer;
351 		uint32_t uns;
352 		const u_char *str;
353 		uint64_t uns64;
354 	} data;
355 	u_short id;
356 	u_char form, class;		/* tag info */
357 	u_char type;
358 #define BE_ANY		255
359 #define BE_NONE		0
360 #define BE_NULL		1
361 #define BE_OCTET	2
362 #define BE_OID		3
363 #define BE_INT		4
364 #define BE_UNS		5
365 #define BE_STR		6
366 #define BE_SEQ		7
367 #define BE_INETADDR	8
368 #define BE_PDU		9
369 #define BE_UNS64	10
370 #define BE_NOSUCHOBJECT	128
371 #define BE_NOSUCHINST	129
372 #define BE_ENDOFMIBVIEW	130
373 };
374 
375 /*
376  * SNMP versions recognized by this module
377  */
378 static const char *SnmpVersion[] = {
379 	"SNMPv1",
380 #define SNMP_VERSION_1	0
381 	"SNMPv2c",
382 #define SNMP_VERSION_2	1
383 	"SNMPv2u",
384 #define SNMP_VERSION_2U	2
385 	"SNMPv3"
386 #define SNMP_VERSION_3	3
387 };
388 
389 /*
390  * Defaults for SNMP PDU components
391  */
392 #define DEF_COMMUNITY "public"
393 
394 /*
395  * constants for ASN.1 decoding
396  */
397 #define OIDMUX 40
398 #define ASNLEN_INETADDR 4
399 #define ASN_SHIFT7 7
400 #define ASN_SHIFT8 8
401 #define ASN_BIT8 0x80
402 #define ASN_LONGLEN 0x80
403 
404 #define ASN_ID_BITS 0x1f
405 #define ASN_FORM_BITS 0x20
406 #define ASN_FORM_SHIFT 5
407 #define ASN_CLASS_BITS 0xc0
408 #define ASN_CLASS_SHIFT 6
409 
410 #define ASN_ID_EXT 0x1f		/* extension ID in tag field */
411 
412 /*
413  * This decodes the next ASN.1 object in the stream pointed to by "p"
414  * (and of real-length "len") and stores the intermediate data in the
415  * provided BE object.
416  *
417  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
418  * O/w, this returns the number of bytes parsed from "p".
419  */
420 static int
421 asn1_parse(netdissect_options *ndo,
422            const u_char *p, u_int len, struct be *elem)
423 {
424 	u_char form, class, id;
425 	u_int i, hdr;
426 
427 	elem->asnlen = 0;
428 	elem->type = BE_ANY;
429 	if (len < 1) {
430 		ND_PRINT("[nothing to parse]");
431 		return -1;
432 	}
433 
434 	/*
435 	 * it would be nice to use a bit field, but you can't depend on them.
436 	 *  +---+---+---+---+---+---+---+---+
437 	 *  + class |frm|        id         |
438 	 *  +---+---+---+---+---+---+---+---+
439 	 *    7   6   5   4   3   2   1   0
440 	 */
441 	id = GET_U_1(p) & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
442 #ifdef notdef
443 	form = (GET_U_1(p) & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
444 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
445 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
446 #else
447 	form = (u_char)(GET_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
448 	class = (u_char)(GET_U_1(p) & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
449 #endif
450 	elem->form = form;
451 	elem->class = class;
452 	elem->id = id;
453 	p++; len--; hdr = 1;
454 	/* extended tag field */
455 	if (id == ASN_ID_EXT) {
456 		/*
457 		 * The ID follows, as a sequence of octets with the
458 		 * 8th bit set and the remaining 7 bits being
459 		 * the next 7 bits of the value, terminated with
460 		 * an octet with the 8th bit not set.
461 		 *
462 		 * First, assemble all the octets with the 8th
463 		 * bit set.  XXX - this doesn't handle a value
464 		 * that won't fit in 32 bits.
465 		 */
466 		id = 0;
467 		while (GET_U_1(p) & ASN_BIT8) {
468 			if (len < 1) {
469 				ND_PRINT("[Xtagfield?]");
470 				return -1;
471 			}
472 			id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8);
473 			len--;
474 			hdr++;
475 			p++;
476 		}
477 		if (len < 1) {
478 			ND_PRINT("[Xtagfield?]");
479 			return -1;
480 		}
481 		elem->id = id = (id << 7) | GET_U_1(p);
482 		--len;
483 		++hdr;
484 		++p;
485 	}
486 	if (len < 1) {
487 		ND_PRINT("[no asnlen]");
488 		return -1;
489 	}
490 	elem->asnlen = GET_U_1(p);
491 	p++; len--; hdr++;
492 	if (elem->asnlen & ASN_BIT8) {
493 		uint32_t noct = elem->asnlen % ASN_BIT8;
494 		elem->asnlen = 0;
495 		if (len < noct) {
496 			ND_PRINT("[asnlen? %d<%d]", len, noct);
497 			return -1;
498 		}
499 		ND_TCHECK_LEN(p, noct);
500 		for (; noct != 0; len--, hdr++, noct--) {
501 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
502 			p++;
503 		}
504 	}
505 	if (len < elem->asnlen) {
506 		ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
507 		return -1;
508 	}
509 	if (form >= sizeof(Form)/sizeof(Form[0])) {
510 		ND_PRINT("[form?%d]", form);
511 		return -1;
512 	}
513 	if (class >= sizeof(Class)/sizeof(Class[0])) {
514 		ND_PRINT("[class?%c/%d]", *Form[form], class);
515 		return -1;
516 	}
517 	if ((int)id >= Class[class].numIDs) {
518 		ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
519 		return -1;
520 	}
521 	ND_TCHECK_LEN(p, elem->asnlen);
522 
523 	switch (form) {
524 	case PRIMITIVE:
525 		switch (class) {
526 		case UNIVERSAL:
527 			switch (id) {
528 			case STRING:
529 				elem->type = BE_STR;
530 				elem->data.str = p;
531 				break;
532 
533 			case INTEGER: {
534 				int32_t data;
535 				elem->type = BE_INT;
536 				data = 0;
537 
538 				if (elem->asnlen == 0) {
539 					ND_PRINT("[asnlen=0]");
540 					return -1;
541 				}
542 				if (GET_U_1(p) & ASN_BIT8)	/* negative */
543 					data = -1;
544 				for (i = elem->asnlen; i != 0; p++, i--)
545 					data = (data << ASN_SHIFT8) | GET_U_1(p);
546 				elem->data.integer = data;
547 				break;
548 			}
549 
550 			case OBJECTID:
551 				elem->type = BE_OID;
552 				elem->data.raw = (const uint8_t *)p;
553 				break;
554 
555 			case ASN_NULL:
556 				elem->type = BE_NULL;
557 				elem->data.raw = NULL;
558 				break;
559 
560 			default:
561 				elem->type = BE_OCTET;
562 				elem->data.raw = (const uint8_t *)p;
563 				ND_PRINT("[P/U/%s]", Class[class].Id[id]);
564 				break;
565 			}
566 			break;
567 
568 		case APPLICATION:
569 			switch (id) {
570 			case IPADDR:
571 				elem->type = BE_INETADDR;
572 				elem->data.raw = (const uint8_t *)p;
573 				break;
574 
575 			case COUNTER:
576 			case GAUGE:
577 			case TIMETICKS: {
578 				uint32_t data;
579 				elem->type = BE_UNS;
580 				data = 0;
581 				for (i = elem->asnlen; i != 0; p++, i--)
582 					data = (data << 8) + GET_U_1(p);
583 				elem->data.uns = data;
584 				break;
585 			}
586 
587 			case COUNTER64: {
588 				uint64_t data64;
589 			        elem->type = BE_UNS64;
590 				data64 = 0;
591 				for (i = elem->asnlen; i != 0; p++, i--)
592 					data64 = (data64 << 8) + GET_U_1(p);
593 				elem->data.uns64 = data64;
594 				break;
595 			}
596 
597 			default:
598 				elem->type = BE_OCTET;
599 				elem->data.raw = (const uint8_t *)p;
600 				ND_PRINT("[P/A/%s]",
601 					Class[class].Id[id]);
602 				break;
603 			}
604 			break;
605 
606 		case CONTEXT:
607 			switch (id) {
608 			case NOSUCHOBJECT:
609 				elem->type = BE_NOSUCHOBJECT;
610 				elem->data.raw = NULL;
611 				break;
612 
613 			case NOSUCHINSTANCE:
614 				elem->type = BE_NOSUCHINST;
615 				elem->data.raw = NULL;
616 				break;
617 
618 			case ENDOFMIBVIEW:
619 				elem->type = BE_ENDOFMIBVIEW;
620 				elem->data.raw = NULL;
621 				break;
622 			}
623 			break;
624 
625 		default:
626 			ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
627 			elem->type = BE_OCTET;
628 			elem->data.raw = (const uint8_t *)p;
629 			break;
630 		}
631 		break;
632 
633 	case CONSTRUCTED:
634 		switch (class) {
635 		case UNIVERSAL:
636 			switch (id) {
637 			case SEQUENCE:
638 				elem->type = BE_SEQ;
639 				elem->data.raw = (const uint8_t *)p;
640 				break;
641 
642 			default:
643 				elem->type = BE_OCTET;
644 				elem->data.raw = (const uint8_t *)p;
645 				ND_PRINT("C/U/%s", Class[class].Id[id]);
646 				break;
647 			}
648 			break;
649 
650 		case CONTEXT:
651 			elem->type = BE_PDU;
652 			elem->data.raw = (const uint8_t *)p;
653 			break;
654 
655 		default:
656 			elem->type = BE_OCTET;
657 			elem->data.raw = (const uint8_t *)p;
658 			ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
659 			break;
660 		}
661 		break;
662 	}
663 	p += elem->asnlen;
664 	len -= elem->asnlen;
665 	return elem->asnlen + hdr;
666 
667 trunc:
668 	nd_print_trunc(ndo);
669 	return -1;
670 }
671 
672 static int
673 asn1_print_octets(netdissect_options *ndo, struct be *elem)
674 {
675 	const u_char *p = (const u_char *)elem->data.raw;
676 	uint32_t asnlen = elem->asnlen;
677 	uint32_t i;
678 
679 	ND_TCHECK_LEN(p, asnlen);
680 	for (i = asnlen; i != 0; p++, i--)
681 		ND_PRINT("_%.2x", GET_U_1(p));
682 	return 0;
683 
684 trunc:
685 	nd_print_trunc(ndo);
686 	return -1;
687 }
688 
689 static int
690 asn1_print_string(netdissect_options *ndo, struct be *elem)
691 {
692 	int printable = 1, first = 1;
693 	const u_char *p;
694 	uint32_t asnlen = elem->asnlen;
695 	uint32_t i;
696 
697 	p = elem->data.str;
698 	ND_TCHECK_LEN(p, asnlen);
699 	for (i = asnlen; printable && i != 0; p++, i--)
700 		printable = ND_ASCII_ISPRINT(GET_U_1(p));
701 	p = elem->data.str;
702 	if (printable) {
703 		ND_PRINT("\"");
704 		if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
705 			ND_PRINT("\"");
706 			goto trunc;
707 		}
708 		ND_PRINT("\"");
709 	} else {
710 		for (i = asnlen; i != 0; p++, i--) {
711 			ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
712 			first = 0;
713 		}
714 	}
715 	return 0;
716 
717 trunc:
718 	nd_print_trunc(ndo);
719 	return -1;
720 }
721 
722 /*
723  * Display the ASN.1 object represented by the BE object.
724  * This used to be an integral part of asn1_parse() before the intermediate
725  * BE form was added.
726  */
727 static int
728 asn1_print(netdissect_options *ndo,
729            struct be *elem)
730 {
731 	const u_char *p;
732 	uint32_t asnlen = elem->asnlen;
733 	uint32_t i;
734 
735 	switch (elem->type) {
736 
737 	case BE_OCTET:
738 		if (asn1_print_octets(ndo, elem) == -1)
739 			return -1;
740 		break;
741 
742 	case BE_NULL:
743 		break;
744 
745 	case BE_OID: {
746 		int o = 0, first = -1;
747 
748 		p = (const u_char *)elem->data.raw;
749 		i = asnlen;
750 		if (!ndo->ndo_nflag && asnlen > 2) {
751 			const struct obj_abrev *a = &obj_abrev_list[0];
752 			for (; a->node; a++) {
753 				if (i < a->oid_len)
754 					continue;
755 				if (!ND_TTEST_LEN(p, a->oid_len))
756 					continue;
757 				if (memcmp(a->oid, p, a->oid_len) == 0) {
758 					objp = a->node->child;
759 					i -= a->oid_len;
760 					p += a->oid_len;
761 					ND_PRINT("%s", a->prefix);
762 					first = 1;
763 					break;
764 				}
765 			}
766 		}
767 
768 		for (; i != 0; p++, i--) {
769 			o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
770 			if (GET_U_1(p) & ASN_LONGLEN)
771 			        continue;
772 
773 			/*
774 			 * first subitem encodes two items with
775 			 * 1st*OIDMUX+2nd
776 			 * (see X.690:1997 clause 8.19 for the details)
777 			 */
778 			if (first < 0) {
779 			        int s;
780 				if (!ndo->ndo_nflag)
781 					objp = mibroot;
782 				first = 0;
783 				s = o / OIDMUX;
784 				if (s > 2) s = 2;
785 				OBJ_PRINT(s, first);
786 				o -= s * OIDMUX;
787 			}
788 			OBJ_PRINT(o, first);
789 			if (--first < 0)
790 				first = 0;
791 			o = 0;
792 		}
793 		break;
794 	}
795 
796 	case BE_INT:
797 		ND_PRINT("%d", elem->data.integer);
798 		break;
799 
800 	case BE_UNS:
801 		ND_PRINT("%u", elem->data.uns);
802 		break;
803 
804 	case BE_UNS64:
805 		ND_PRINT("%" PRIu64, elem->data.uns64);
806 		break;
807 
808 	case BE_STR:
809 		if (asn1_print_string(ndo, elem) == -1)
810 			return -1;
811 		break;
812 
813 	case BE_SEQ:
814 		ND_PRINT("Seq(%u)", elem->asnlen);
815 		break;
816 
817 	case BE_INETADDR:
818 		if (asnlen != ASNLEN_INETADDR)
819 			ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
820 		p = (const u_char *)elem->data.raw;
821 		ND_TCHECK_LEN(p, asnlen);
822 		for (i = asnlen; i != 0; p++, i--) {
823 			ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
824 		}
825 		break;
826 
827 	case BE_NOSUCHOBJECT:
828 	case BE_NOSUCHINST:
829 	case BE_ENDOFMIBVIEW:
830 		ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
831 		break;
832 
833 	case BE_PDU:
834 		ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
835 		break;
836 
837 	case BE_ANY:
838 		ND_PRINT("[BE_ANY!?]");
839 		break;
840 
841 	default:
842 		ND_PRINT("[be!?]");
843 		break;
844 	}
845 	return 0;
846 
847 trunc:
848 	nd_print_trunc(ndo);
849 	return -1;
850 }
851 
852 #ifdef notdef
853 /*
854  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
855  * This will work for any ASN.1 stream, not just an SNMP PDU.
856  *
857  * By adding newlines and spaces at the correct places, this would print in
858  * Rose-Normal-Form.
859  *
860  * This is not currently used.
861  */
862 static void
863 asn1_decode(u_char *p, u_int length)
864 {
865 	struct be elem;
866 	int i = 0;
867 
868 	while (i >= 0 && length > 0) {
869 		i = asn1_parse(ndo, p, length, &elem);
870 		if (i >= 0) {
871 			ND_PRINT(" ");
872 			if (asn1_print(ndo, &elem) < 0)
873 				return;
874 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
875 				ND_PRINT(" {");
876 				asn1_decode(elem.data.raw, elem.asnlen);
877 				ND_PRINT(" }");
878 			}
879 			length -= i;
880 			p += i;
881 		}
882 	}
883 }
884 #endif
885 
886 #ifdef USE_LIBSMI
887 
888 struct smi2be {
889     SmiBasetype basetype;
890     int be;
891 };
892 
893 static const struct smi2be smi2betab[] = {
894     { SMI_BASETYPE_INTEGER32,		BE_INT },
895     { SMI_BASETYPE_OCTETSTRING,		BE_STR },
896     { SMI_BASETYPE_OCTETSTRING,		BE_INETADDR },
897     { SMI_BASETYPE_OBJECTIDENTIFIER,	BE_OID },
898     { SMI_BASETYPE_UNSIGNED32,		BE_UNS },
899     { SMI_BASETYPE_INTEGER64,		BE_NONE },
900     { SMI_BASETYPE_UNSIGNED64,		BE_UNS64 },
901     { SMI_BASETYPE_FLOAT32,		BE_NONE },
902     { SMI_BASETYPE_FLOAT64,		BE_NONE },
903     { SMI_BASETYPE_FLOAT128,		BE_NONE },
904     { SMI_BASETYPE_ENUM,		BE_INT },
905     { SMI_BASETYPE_BITS,		BE_STR },
906     { SMI_BASETYPE_UNKNOWN,		BE_NONE }
907 };
908 
909 static int
910 smi_decode_oid(netdissect_options *ndo,
911                struct be *elem, unsigned int *oid,
912                unsigned int oidsize, unsigned int *oidlen)
913 {
914 	const u_char *p = (const u_char *)elem->data.raw;
915 	uint32_t asnlen = elem->asnlen;
916 	uint32_t i = asnlen;
917 	int o = 0, first = -1;
918 	unsigned int firstval;
919 
920 	for (*oidlen = 0; i != 0; p++, i--) {
921 	        o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
922 		if (GET_U_1(p) & ASN_LONGLEN)
923 		    continue;
924 
925 		/*
926 		 * first subitem encodes two items with 1st*OIDMUX+2nd
927 		 * (see X.690:1997 clause 8.19 for the details)
928 		 */
929 		if (first < 0) {
930 			first = 0;
931 			firstval = o / OIDMUX;
932 			if (firstval > 2) firstval = 2;
933 			o -= firstval * OIDMUX;
934 			if (*oidlen < oidsize) {
935 			    oid[(*oidlen)++] = firstval;
936 			}
937 		}
938 		if (*oidlen < oidsize) {
939 			oid[(*oidlen)++] = o;
940 		}
941 		o = 0;
942 	}
943 	return 0;
944 }
945 
946 static int smi_check_type(SmiBasetype basetype, int be)
947 {
948     int i;
949 
950     for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
951 	if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
952 	    return 1;
953 	}
954     }
955 
956     return 0;
957 }
958 
959 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
960 			     struct be *elem)
961 {
962     int ok = 1;
963 
964     switch (smiType->basetype) {
965     case SMI_BASETYPE_OBJECTIDENTIFIER:
966     case SMI_BASETYPE_OCTETSTRING:
967 	if (smiRange->minValue.value.unsigned32
968 	    == smiRange->maxValue.value.unsigned32) {
969 	    ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
970 	} else {
971 	    ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
972 		  && elem->asnlen <= smiRange->maxValue.value.unsigned32);
973 	}
974 	break;
975 
976     case SMI_BASETYPE_INTEGER32:
977 	ok = (elem->data.integer >= smiRange->minValue.value.integer32
978 	      && elem->data.integer <= smiRange->maxValue.value.integer32);
979 	break;
980 
981     case SMI_BASETYPE_UNSIGNED32:
982 	ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
983 	      && elem->data.uns <= smiRange->maxValue.value.unsigned32);
984 	break;
985 
986     case SMI_BASETYPE_UNSIGNED64:
987 	/* XXX */
988 	break;
989 
990 	/* case SMI_BASETYPE_INTEGER64: SMIng */
991 	/* case SMI_BASETYPE_FLOAT32: SMIng */
992 	/* case SMI_BASETYPE_FLOAT64: SMIng */
993 	/* case SMI_BASETYPE_FLOAT128: SMIng */
994 
995     case SMI_BASETYPE_ENUM:
996     case SMI_BASETYPE_BITS:
997     case SMI_BASETYPE_UNKNOWN:
998 	ok = 1;
999 	break;
1000 
1001     default:
1002 	ok = 0;
1003 	break;
1004     }
1005 
1006     return ok;
1007 }
1008 
1009 static int smi_check_range(SmiType *smiType, struct be *elem)
1010 {
1011         SmiRange *smiRange;
1012 	int ok = 1;
1013 
1014 	for (smiRange = smiGetFirstRange(smiType);
1015 	     smiRange;
1016 	     smiRange = smiGetNextRange(smiRange)) {
1017 
1018 	    ok = smi_check_a_range(smiType, smiRange, elem);
1019 
1020 	    if (ok) {
1021 		break;
1022 	    }
1023 	}
1024 
1025 	if (ok) {
1026 	    SmiType *parentType;
1027 	    parentType = smiGetParentType(smiType);
1028 	    if (parentType) {
1029 		ok = smi_check_range(parentType, elem);
1030 	    }
1031 	}
1032 
1033 	return ok;
1034 }
1035 
1036 static SmiNode *
1037 smi_print_variable(netdissect_options *ndo,
1038                    struct be *elem, int *status)
1039 {
1040 	unsigned int oid[128], oidlen;
1041 	SmiNode *smiNode = NULL;
1042 	unsigned int i;
1043 
1044 	if (!nd_smi_module_loaded) {
1045 		*status = asn1_print(ndo, elem);
1046 		return NULL;
1047 	}
1048 	*status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1049 	    &oidlen);
1050 	if (*status < 0)
1051 		return NULL;
1052 	smiNode = smiGetNodeByOID(oidlen, oid);
1053 	if (! smiNode) {
1054 		*status = asn1_print(ndo, elem);
1055 		return NULL;
1056 	}
1057 	if (ndo->ndo_vflag) {
1058 		ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1059 	}
1060 	ND_PRINT("%s", smiNode->name);
1061 	if (smiNode->oidlen < oidlen) {
1062 		for (i = smiNode->oidlen; i < oidlen; i++) {
1063 			ND_PRINT(".%u", oid[i]);
1064 		}
1065 	}
1066 	*status = 0;
1067 	return smiNode;
1068 }
1069 
1070 static int
1071 smi_print_value(netdissect_options *ndo,
1072                 SmiNode *smiNode, u_short pduid, struct be *elem)
1073 {
1074 	unsigned int i, oid[128], oidlen;
1075 	SmiType *smiType;
1076 	SmiNamedNumber *nn;
1077 	int done = 0;
1078 
1079 	if (! smiNode || ! (smiNode->nodekind
1080 			    & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1081 	    return asn1_print(ndo, elem);
1082 	}
1083 
1084 	if (elem->type == BE_NOSUCHOBJECT
1085 	    || elem->type == BE_NOSUCHINST
1086 	    || elem->type == BE_ENDOFMIBVIEW) {
1087 	    return asn1_print(ndo, elem);
1088 	}
1089 
1090 	if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1091 	    ND_PRINT("[notNotifyable]");
1092 	}
1093 
1094 	if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1095 	    ND_PRINT("[notReadable]");
1096 	}
1097 
1098 	if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1099 	    ND_PRINT("[notWritable]");
1100 	}
1101 
1102 	if (RESPONSE_CLASS(pduid)
1103 	    && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1104 	    ND_PRINT("[noAccess]");
1105 	}
1106 
1107 	smiType = smiGetNodeType(smiNode);
1108 	if (! smiType) {
1109 	    return asn1_print(ndo, elem);
1110 	}
1111 
1112 	if (! smi_check_type(smiType->basetype, elem->type)) {
1113 	    ND_PRINT("[wrongType]");
1114 	}
1115 
1116 	if (! smi_check_range(smiType, elem)) {
1117 	    ND_PRINT("[outOfRange]");
1118 	}
1119 
1120 	/* resolve bits to named bits */
1121 
1122 	/* check whether instance identifier is valid */
1123 
1124 	/* apply display hints (integer, octetstring) */
1125 
1126 	/* convert instance identifier to index type values */
1127 
1128 	switch (elem->type) {
1129 	case BE_OID:
1130 	        if (smiType->basetype == SMI_BASETYPE_BITS) {
1131 		        /* print bit labels */
1132 		} else {
1133 			if (nd_smi_module_loaded &&
1134 			    smi_decode_oid(ndo, elem, oid,
1135 					   sizeof(oid)/sizeof(unsigned int),
1136 					   &oidlen) == 0) {
1137 				smiNode = smiGetNodeByOID(oidlen, oid);
1138 				if (smiNode) {
1139 				        if (ndo->ndo_vflag) {
1140 						ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1141 					}
1142 					ND_PRINT("%s", smiNode->name);
1143 					if (smiNode->oidlen < oidlen) {
1144 					        for (i = smiNode->oidlen;
1145 						     i < oidlen; i++) {
1146 						        ND_PRINT(".%u", oid[i]);
1147 						}
1148 					}
1149 					done++;
1150 				}
1151 			}
1152 		}
1153 		break;
1154 
1155 	case BE_INT:
1156 	        if (smiType->basetype == SMI_BASETYPE_ENUM) {
1157 		        for (nn = smiGetFirstNamedNumber(smiType);
1158 			     nn;
1159 			     nn = smiGetNextNamedNumber(nn)) {
1160 			         if (nn->value.value.integer32
1161 				     == elem->data.integer) {
1162 				         ND_PRINT("%s", nn->name);
1163 					 ND_PRINT("(%d)", elem->data.integer);
1164 					 done++;
1165 					 break;
1166 				}
1167 			}
1168 		}
1169 		break;
1170 	}
1171 
1172 	if (! done) {
1173 		return asn1_print(ndo, elem);
1174 	}
1175 	return 0;
1176 }
1177 #endif
1178 
1179 /*
1180  * General SNMP header
1181  *	SEQUENCE {
1182  *		version INTEGER {version-1(0)},
1183  *		community OCTET STRING,
1184  *		data ANY	-- PDUs
1185  *	}
1186  * PDUs for all but Trap: (see rfc1157 from page 15 on)
1187  *	SEQUENCE {
1188  *		request-id INTEGER,
1189  *		error-status INTEGER,
1190  *		error-index INTEGER,
1191  *		varbindlist SEQUENCE OF
1192  *			SEQUENCE {
1193  *				name ObjectName,
1194  *				value ObjectValue
1195  *			}
1196  *	}
1197  * PDU for Trap:
1198  *	SEQUENCE {
1199  *		enterprise OBJECT IDENTIFIER,
1200  *		agent-addr NetworkAddress,
1201  *		generic-trap INTEGER,
1202  *		specific-trap INTEGER,
1203  *		time-stamp TimeTicks,
1204  *		varbindlist SEQUENCE OF
1205  *			SEQUENCE {
1206  *				name ObjectName,
1207  *				value ObjectValue
1208  *			}
1209  *	}
1210  */
1211 
1212 /*
1213  * Decode SNMP varBind
1214  */
1215 static void
1216 varbind_print(netdissect_options *ndo,
1217               u_short pduid, const u_char *np, u_int length)
1218 {
1219 	struct be elem;
1220 	int count = 0;
1221 #ifdef USE_LIBSMI
1222 	SmiNode *smiNode = NULL;
1223 #endif
1224 	int status;
1225 
1226 	/* Sequence of varBind */
1227 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1228 		return;
1229 	if (elem.type != BE_SEQ) {
1230 		ND_PRINT("[!SEQ of varbind]");
1231 		asn1_print(ndo, &elem);
1232 		return;
1233 	}
1234 	if ((u_int)count < length)
1235 		ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1236 	/* descend */
1237 	length = elem.asnlen;
1238 	np = (const u_char *)elem.data.raw;
1239 
1240 	while (length) {
1241 		const u_char *vbend;
1242 		u_int vblength;
1243 
1244 		ND_PRINT(" ");
1245 
1246 		/* Sequence */
1247 		if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1248 			return;
1249 		if (elem.type != BE_SEQ) {
1250 			ND_PRINT("[!varbind]");
1251 			asn1_print(ndo, &elem);
1252 			return;
1253 		}
1254 		vbend = np + count;
1255 		vblength = length - count;
1256 		/* descend */
1257 		length = elem.asnlen;
1258 		np = (const u_char *)elem.data.raw;
1259 
1260 		/* objName (OID) */
1261 		if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1262 			return;
1263 		if (elem.type != BE_OID) {
1264 			ND_PRINT("[objName!=OID]");
1265 			asn1_print(ndo, &elem);
1266 			return;
1267 		}
1268 #ifdef USE_LIBSMI
1269 		smiNode = smi_print_variable(ndo, &elem, &status);
1270 #else
1271 		status = asn1_print(ndo, &elem);
1272 #endif
1273 		if (status < 0)
1274 			return;
1275 		length -= count;
1276 		np += count;
1277 
1278 		if (pduid != GETREQ && pduid != GETNEXTREQ
1279 		    && pduid != GETBULKREQ)
1280 			ND_PRINT("=");
1281 
1282 		/* objVal (ANY) */
1283 		if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1284 			return;
1285 		if (pduid == GETREQ || pduid == GETNEXTREQ
1286 		    || pduid == GETBULKREQ) {
1287 			if (elem.type != BE_NULL) {
1288 				ND_PRINT("[objVal!=NULL]");
1289 				if (asn1_print(ndo, &elem) < 0)
1290 					return;
1291 			}
1292 		} else {
1293 		        if (elem.type != BE_NULL) {
1294 #ifdef USE_LIBSMI
1295 				status = smi_print_value(ndo, smiNode, pduid, &elem);
1296 #else
1297 				status = asn1_print(ndo, &elem);
1298 #endif
1299 			}
1300 			if (status < 0)
1301 				return;
1302 		}
1303 		length = vblength;
1304 		np = vbend;
1305 	}
1306 }
1307 
1308 /*
1309  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1310  * GetBulk, Inform, V2Trap, and Report
1311  */
1312 static void
1313 snmppdu_print(netdissect_options *ndo,
1314               u_short pduid, const u_char *np, u_int length)
1315 {
1316 	struct be elem;
1317 	int count = 0, error_status;
1318 
1319 	/* reqId (Integer) */
1320 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1321 		return;
1322 	if (elem.type != BE_INT) {
1323 		ND_PRINT("[reqId!=INT]");
1324 		asn1_print(ndo, &elem);
1325 		return;
1326 	}
1327 	if (ndo->ndo_vflag)
1328 		ND_PRINT("R=%d ", elem.data.integer);
1329 	length -= count;
1330 	np += count;
1331 
1332 	/* errorStatus (Integer) */
1333 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1334 		return;
1335 	if (elem.type != BE_INT) {
1336 		ND_PRINT("[errorStatus!=INT]");
1337 		asn1_print(ndo, &elem);
1338 		return;
1339 	}
1340 	error_status = 0;
1341 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1342 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1343 	    && elem.data.integer != 0) {
1344 		char errbuf[20];
1345 		ND_PRINT("[errorStatus(%s)!=0]",
1346 			DECODE_ErrorStatus(elem.data.integer));
1347 	} else if (pduid == GETBULKREQ) {
1348 		ND_PRINT(" N=%d", elem.data.integer);
1349 	} else if (elem.data.integer != 0) {
1350 		char errbuf[20];
1351 		ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1352 		error_status = elem.data.integer;
1353 	}
1354 	length -= count;
1355 	np += count;
1356 
1357 	/* errorIndex (Integer) */
1358 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1359 		return;
1360 	if (elem.type != BE_INT) {
1361 		ND_PRINT("[errorIndex!=INT]");
1362 		asn1_print(ndo, &elem);
1363 		return;
1364 	}
1365 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1366 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1367 	    && elem.data.integer != 0)
1368 		ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1369 	else if (pduid == GETBULKREQ)
1370 		ND_PRINT(" M=%d", elem.data.integer);
1371 	else if (elem.data.integer != 0) {
1372 		if (!error_status)
1373 			ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1374 		else
1375 			ND_PRINT("@%d", elem.data.integer);
1376 	} else if (error_status) {
1377 		ND_PRINT("[errorIndex==0]");
1378 	}
1379 	length -= count;
1380 	np += count;
1381 
1382 	varbind_print(ndo, pduid, np, length);
1383 }
1384 
1385 /*
1386  * Decode SNMP Trap PDU
1387  */
1388 static void
1389 trappdu_print(netdissect_options *ndo,
1390               const u_char *np, u_int length)
1391 {
1392 	struct be elem;
1393 	int count = 0, generic;
1394 
1395 	ND_PRINT(" ");
1396 
1397 	/* enterprise (oid) */
1398 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1399 		return;
1400 	if (elem.type != BE_OID) {
1401 		ND_PRINT("[enterprise!=OID]");
1402 		asn1_print(ndo, &elem);
1403 		return;
1404 	}
1405 	if (asn1_print(ndo, &elem) < 0)
1406 		return;
1407 	length -= count;
1408 	np += count;
1409 
1410 	ND_PRINT(" ");
1411 
1412 	/* agent-addr (inetaddr) */
1413 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1414 		return;
1415 	if (elem.type != BE_INETADDR) {
1416 		ND_PRINT("[agent-addr!=INETADDR]");
1417 		asn1_print(ndo, &elem);
1418 		return;
1419 	}
1420 	if (asn1_print(ndo, &elem) < 0)
1421 		return;
1422 	length -= count;
1423 	np += count;
1424 
1425 	/* generic-trap (Integer) */
1426 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1427 		return;
1428 	if (elem.type != BE_INT) {
1429 		ND_PRINT("[generic-trap!=INT]");
1430 		asn1_print(ndo, &elem);
1431 		return;
1432 	}
1433 	generic = elem.data.integer;
1434 	{
1435 		char buf[20];
1436 		ND_PRINT(" %s", DECODE_GenericTrap(generic));
1437 	}
1438 	length -= count;
1439 	np += count;
1440 
1441 	/* specific-trap (Integer) */
1442 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1443 		return;
1444 	if (elem.type != BE_INT) {
1445 		ND_PRINT("[specific-trap!=INT]");
1446 		asn1_print(ndo, &elem);
1447 		return;
1448 	}
1449 	if (generic != GT_ENTERPRISE) {
1450 		if (elem.data.integer != 0)
1451 			ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
1452 	} else
1453 		ND_PRINT(" s=%d", elem.data.integer);
1454 	length -= count;
1455 	np += count;
1456 
1457 	ND_PRINT(" ");
1458 
1459 	/* time-stamp (TimeTicks) */
1460 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1461 		return;
1462 	if (elem.type != BE_UNS) {			/* XXX */
1463 		ND_PRINT("[time-stamp!=TIMETICKS]");
1464 		asn1_print(ndo, &elem);
1465 		return;
1466 	}
1467 	if (asn1_print(ndo, &elem) < 0)
1468 		return;
1469 	length -= count;
1470 	np += count;
1471 
1472 	varbind_print(ndo, TRAP, np, length);
1473 }
1474 
1475 /*
1476  * Decode arbitrary SNMP PDUs.
1477  */
1478 static void
1479 pdu_print(netdissect_options *ndo,
1480           const u_char *np, u_int length, int version)
1481 {
1482 	struct be pdu;
1483 	int count = 0;
1484 
1485 	/* PDU (Context) */
1486 	if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1487 		return;
1488 	if (pdu.type != BE_PDU) {
1489 		ND_PRINT("[no PDU]");
1490 		return;
1491 	}
1492 	if ((u_int)count < length)
1493 		ND_PRINT("[%d extra after PDU]", length - count);
1494 	if (ndo->ndo_vflag) {
1495 		ND_PRINT("{ ");
1496 	}
1497 	if (asn1_print(ndo, &pdu) < 0)
1498 		return;
1499 	ND_PRINT(" ");
1500 	/* descend into PDU */
1501 	length = pdu.asnlen;
1502 	np = (const u_char *)pdu.data.raw;
1503 
1504 	if (version == SNMP_VERSION_1 &&
1505 	    (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1506 	     pdu.id == V2TRAP || pdu.id == REPORT)) {
1507 	        ND_PRINT("[v2 PDU in v1 message]");
1508 		return;
1509 	}
1510 
1511 	if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1512 		ND_PRINT("[v1 PDU in v2 message]");
1513 		return;
1514 	}
1515 
1516 	switch (pdu.id) {
1517 	case TRAP:
1518 		trappdu_print(ndo, np, length);
1519 		break;
1520 	case GETREQ:
1521 	case GETNEXTREQ:
1522 	case GETRESP:
1523 	case SETREQ:
1524 	case GETBULKREQ:
1525 	case INFORMREQ:
1526 	case V2TRAP:
1527 	case REPORT:
1528 		snmppdu_print(ndo, pdu.id, np, length);
1529 		break;
1530 	}
1531 
1532 	if (ndo->ndo_vflag) {
1533 		ND_PRINT(" } ");
1534 	}
1535 }
1536 
1537 /*
1538  * Decode a scoped SNMP PDU.
1539  */
1540 static void
1541 scopedpdu_print(netdissect_options *ndo,
1542                 const u_char *np, u_int length, int version)
1543 {
1544 	struct be elem;
1545 	int count = 0;
1546 
1547 	/* Sequence */
1548 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1549 		return;
1550 	if (elem.type != BE_SEQ) {
1551 		ND_PRINT("[!scoped PDU]");
1552 		asn1_print(ndo, &elem);
1553 		return;
1554 	}
1555 	length = elem.asnlen;
1556 	np = (const u_char *)elem.data.raw;
1557 
1558 	/* contextEngineID (OCTET STRING) */
1559 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1560 		return;
1561 	if (elem.type != BE_STR) {
1562 		ND_PRINT("[contextEngineID!=STR]");
1563 		asn1_print(ndo, &elem);
1564 		return;
1565 	}
1566 	length -= count;
1567 	np += count;
1568 
1569 	ND_PRINT("E=");
1570 	if (asn1_print_octets(ndo, &elem) == -1)
1571 		return;
1572 	ND_PRINT(" ");
1573 
1574 	/* contextName (OCTET STRING) */
1575 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1576 		return;
1577 	if (elem.type != BE_STR) {
1578 		ND_PRINT("[contextName!=STR]");
1579 		asn1_print(ndo, &elem);
1580 		return;
1581 	}
1582 	length -= count;
1583 	np += count;
1584 
1585 	ND_PRINT("C=");
1586 	if (asn1_print_string(ndo, &elem) == -1)
1587 		return;
1588 	ND_PRINT(" ");
1589 
1590 	pdu_print(ndo, np, length, version);
1591 }
1592 
1593 /*
1594  * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1595  */
1596 static void
1597 community_print(netdissect_options *ndo,
1598                 const u_char *np, u_int length, int version)
1599 {
1600 	struct be elem;
1601 	int count = 0;
1602 
1603 	/* Community (String) */
1604 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1605 		return;
1606 	if (elem.type != BE_STR) {
1607 		ND_PRINT("[comm!=STR]");
1608 		asn1_print(ndo, &elem);
1609 		return;
1610 	}
1611 	/* default community */
1612 	if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1613 	    strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1614 	            sizeof(DEF_COMMUNITY) - 1) == 0)) {
1615 		/* ! "public" */
1616 		ND_PRINT("C=");
1617 		if (asn1_print_string(ndo, &elem) == -1)
1618 			return;
1619 		ND_PRINT(" ");
1620 	}
1621 	length -= count;
1622 	np += count;
1623 
1624 	pdu_print(ndo, np, length, version);
1625 }
1626 
1627 /*
1628  * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1629  */
1630 static void
1631 usm_print(netdissect_options *ndo,
1632           const u_char *np, u_int length)
1633 {
1634         struct be elem;
1635 	int count = 0;
1636 
1637 	/* Sequence */
1638 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1639 		return;
1640 	if (elem.type != BE_SEQ) {
1641 		ND_PRINT("[!usm]");
1642 		asn1_print(ndo, &elem);
1643 		return;
1644 	}
1645 	length = elem.asnlen;
1646 	np = (const u_char *)elem.data.raw;
1647 
1648 	/* msgAuthoritativeEngineID (OCTET STRING) */
1649 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1650 		return;
1651 	if (elem.type != BE_STR) {
1652 		ND_PRINT("[msgAuthoritativeEngineID!=STR]");
1653 		asn1_print(ndo, &elem);
1654 		return;
1655 	}
1656 	length -= count;
1657 	np += count;
1658 
1659 	/* msgAuthoritativeEngineBoots (INTEGER) */
1660 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1661 		return;
1662 	if (elem.type != BE_INT) {
1663 		ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
1664 		asn1_print(ndo, &elem);
1665 		return;
1666 	}
1667 	if (ndo->ndo_vflag)
1668 		ND_PRINT("B=%d ", elem.data.integer);
1669 	length -= count;
1670 	np += count;
1671 
1672 	/* msgAuthoritativeEngineTime (INTEGER) */
1673 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1674 		return;
1675 	if (elem.type != BE_INT) {
1676 		ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
1677 		asn1_print(ndo, &elem);
1678 		return;
1679 	}
1680 	if (ndo->ndo_vflag)
1681 		ND_PRINT("T=%d ", elem.data.integer);
1682 	length -= count;
1683 	np += count;
1684 
1685 	/* msgUserName (OCTET STRING) */
1686 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1687 		return;
1688 	if (elem.type != BE_STR) {
1689 		ND_PRINT("[msgUserName!=STR]");
1690 		asn1_print(ndo, &elem);
1691 		return;
1692 	}
1693 	length -= count;
1694         np += count;
1695 
1696 	ND_PRINT("U=");
1697 	if (asn1_print_string(ndo, &elem) == -1)
1698 		return;
1699 	ND_PRINT(" ");
1700 
1701 	/* msgAuthenticationParameters (OCTET STRING) */
1702 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1703 		return;
1704 	if (elem.type != BE_STR) {
1705 		ND_PRINT("[msgAuthenticationParameters!=STR]");
1706 		asn1_print(ndo, &elem);
1707 		return;
1708 	}
1709 	length -= count;
1710         np += count;
1711 
1712 	/* msgPrivacyParameters (OCTET STRING) */
1713 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1714 		return;
1715 	if (elem.type != BE_STR) {
1716 		ND_PRINT("[msgPrivacyParameters!=STR]");
1717 		asn1_print(ndo, &elem);
1718 		return;
1719 	}
1720 	length -= count;
1721         np += count;
1722 
1723 	if ((u_int)count < length)
1724 		ND_PRINT("[%d extra after usm SEQ]", length - count);
1725 }
1726 
1727 /*
1728  * Decode SNMPv3 Message Header (SNMPv3)
1729  */
1730 static void
1731 v3msg_print(netdissect_options *ndo,
1732             const u_char *np, u_int length)
1733 {
1734 	struct be elem;
1735 	int count = 0;
1736 	u_char flags;
1737 	int model;
1738 	const u_char *xnp = np;
1739 	int xlength = length;
1740 
1741 	/* Sequence */
1742 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1743 		return;
1744 	if (elem.type != BE_SEQ) {
1745 		ND_PRINT("[!message]");
1746 		asn1_print(ndo, &elem);
1747 		return;
1748 	}
1749 	length = elem.asnlen;
1750 	np = (const u_char *)elem.data.raw;
1751 
1752 	if (ndo->ndo_vflag) {
1753 		ND_PRINT("{ ");
1754 	}
1755 
1756 	/* msgID (INTEGER) */
1757 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1758 		return;
1759 	if (elem.type != BE_INT) {
1760 		ND_PRINT("[msgID!=INT]");
1761 		asn1_print(ndo, &elem);
1762 		return;
1763 	}
1764 	length -= count;
1765 	np += count;
1766 
1767 	/* msgMaxSize (INTEGER) */
1768 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1769 		return;
1770 	if (elem.type != BE_INT) {
1771 		ND_PRINT("[msgMaxSize!=INT]");
1772 		asn1_print(ndo, &elem);
1773 		return;
1774 	}
1775 	length -= count;
1776 	np += count;
1777 
1778 	/* msgFlags (OCTET STRING) */
1779 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1780 		return;
1781 	if (elem.type != BE_STR) {
1782 		ND_PRINT("[msgFlags!=STR]");
1783 		asn1_print(ndo, &elem);
1784 		return;
1785 	}
1786 	if (elem.asnlen != 1) {
1787 		ND_PRINT("[msgFlags size %d]", elem.asnlen);
1788 		return;
1789 	}
1790 	flags = GET_U_1(elem.data.str);
1791 	if (flags != 0x00 && flags != 0x01 && flags != 0x03
1792 	    && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1793 		ND_PRINT("[msgFlags=0x%02X]", flags);
1794 		return;
1795 	}
1796 	length -= count;
1797 	np += count;
1798 
1799 	ND_PRINT("F=%s%s%s ",
1800 	          flags & 0x01 ? "a" : "",
1801 	          flags & 0x02 ? "p" : "",
1802 	          flags & 0x04 ? "r" : "");
1803 
1804 	/* msgSecurityModel (INTEGER) */
1805 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1806 		return;
1807 	if (elem.type != BE_INT) {
1808 		ND_PRINT("[msgSecurityModel!=INT]");
1809 		asn1_print(ndo, &elem);
1810 		return;
1811 	}
1812 	model = elem.data.integer;
1813 	length -= count;
1814 	np += count;
1815 
1816 	if ((u_int)count < length)
1817 		ND_PRINT("[%d extra after message SEQ]", length - count);
1818 
1819 	if (ndo->ndo_vflag) {
1820 		ND_PRINT("} ");
1821 	}
1822 
1823 	if (model == 3) {
1824 	    if (ndo->ndo_vflag) {
1825 		ND_PRINT("{ USM ");
1826 	    }
1827 	} else {
1828 	    ND_PRINT("[security model %d]", model);
1829             return;
1830 	}
1831 
1832 	np = xnp + (np - xnp);
1833 	length = xlength - (np - xnp);
1834 
1835 	/* msgSecurityParameters (OCTET STRING) */
1836 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1837 		return;
1838 	if (elem.type != BE_STR) {
1839 		ND_PRINT("[msgSecurityParameters!=STR]");
1840 		asn1_print(ndo, &elem);
1841 		return;
1842 	}
1843 	length -= count;
1844 	np += count;
1845 
1846 	if (model == 3) {
1847 	    usm_print(ndo, elem.data.str, elem.asnlen);
1848 	    if (ndo->ndo_vflag) {
1849 		ND_PRINT("} ");
1850 	    }
1851 	}
1852 
1853 	if (ndo->ndo_vflag) {
1854 	    ND_PRINT("{ ScopedPDU ");
1855 	}
1856 
1857 	scopedpdu_print(ndo, np, length, 3);
1858 
1859 	if (ndo->ndo_vflag) {
1860 		ND_PRINT("} ");
1861 	}
1862 }
1863 
1864 /*
1865  * Decode SNMP header and pass on to PDU printing routines
1866  */
1867 void
1868 snmp_print(netdissect_options *ndo,
1869            const u_char *np, u_int length)
1870 {
1871 	struct be elem;
1872 	int count = 0;
1873 	int version = 0;
1874 
1875 	ndo->ndo_protocol = "snmp";
1876 	ND_PRINT(" ");
1877 
1878 	/* initial Sequence */
1879 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1880 		return;
1881 	if (elem.type != BE_SEQ) {
1882 		ND_PRINT("[!init SEQ]");
1883 		asn1_print(ndo, &elem);
1884 		return;
1885 	}
1886 	if ((u_int)count < length)
1887 		ND_PRINT("[%d extra after iSEQ]", length - count);
1888 	/* descend */
1889 	length = elem.asnlen;
1890 	np = (const u_char *)elem.data.raw;
1891 
1892 	/* Version (INTEGER) */
1893 	if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1894 		return;
1895 	if (elem.type != BE_INT) {
1896 		ND_PRINT("[version!=INT]");
1897 		asn1_print(ndo, &elem);
1898 		return;
1899 	}
1900 
1901 	switch (elem.data.integer) {
1902 	case SNMP_VERSION_1:
1903 	case SNMP_VERSION_2:
1904 	case SNMP_VERSION_3:
1905 		if (ndo->ndo_vflag)
1906 			ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
1907 		break;
1908 	default:
1909 	        ND_PRINT("SNMP [version = %d]", elem.data.integer);
1910 		return;
1911 	}
1912 	version = elem.data.integer;
1913 	length -= count;
1914 	np += count;
1915 
1916 	switch (version) {
1917 	case SNMP_VERSION_1:
1918         case SNMP_VERSION_2:
1919 		community_print(ndo, np, length, version);
1920 		break;
1921 	case SNMP_VERSION_3:
1922 		v3msg_print(ndo, np, length);
1923 		break;
1924 	default:
1925 		ND_PRINT("[version = %d]", elem.data.integer);
1926 		break;
1927 	}
1928 
1929 	if (ndo->ndo_vflag) {
1930 		ND_PRINT("} ");
1931 	}
1932 }
1933