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