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