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 */
86static 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 */
109static 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 */
128static 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 */
158static 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 */
171static const char *Private[] = {
172	"P-0"
173};
174
175/*
176 * error-status values for any SNMP PDU
177 */
178static 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 */
207static 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 */
228static 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 */
248static 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 */
259static 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
284static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
285#endif
286#ifndef NO_ABREV_ENTER
287static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
288#endif
289#ifndef NO_ABREV_EXPERI
290static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
291#endif
292#ifndef NO_ABBREV_SNMPMODS
293static 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) }
298static 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 */
346struct 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 */
378static 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 */
420static int
421asn1_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
667trunc:
668	nd_print_trunc(ndo);
669	return -1;
670}
671
672static int
673asn1_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
684trunc:
685	nd_print_trunc(ndo);
686	return -1;
687}
688
689static int
690asn1_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
717trunc:
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 */
727static int
728asn1_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
847trunc:
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 */
862static void
863asn1_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
888struct smi2be {
889    SmiBasetype basetype;
890    int be;
891};
892
893static 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
909static int
910smi_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
946static 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
959static 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
1009static 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
1036static SmiNode *
1037smi_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
1070static int
1071smi_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 */
1215static void
1216varbind_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 */
1312static void
1313snmppdu_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 */
1388static void
1389trappdu_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 */
1478static void
1479pdu_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 */
1540static void
1541scopedpdu_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 */
1596static void
1597community_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 */
1630static void
1631usm_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 */
1730static void
1731v3msg_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 */
1867void
1868snmp_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