lib: remove unused libfruutils
[unleashed.git] / contrib / tcpdump / print-snmp.c
blob1b096dcfe579289b90be9521dafaad86df1e136e
1 /*
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3 * John Robert LoVerso. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
37 * Support for SNMPv2c/SNMPv3 and the ability to link the module against
38 * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
40 * This started out as a very simple program, but the incremental decoding
41 * (into the BE structure) complicated things.
43 # Los Alamos National Laboratory
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
59 /* \summary: Simple Network Management Protocol (SNMP) printer */
61 #ifdef HAVE_CONFIG_H
62 #include "config.h"
63 #endif
65 #include <netdissect-stdinc.h>
67 #include <stdio.h>
68 #include <string.h>
70 #ifdef USE_LIBSMI
71 #include <smi.h>
72 #endif
74 #include "netdissect.h"
76 #undef OPAQUE /* defined in <wingdi.h> */
78 static const char tstr[] = "[|snmp]";
81 * Universal ASN.1 types
82 * (we only care about the tag values for those allowed in the Internet SMI)
84 static const char *Universal[] = {
85 "U-0",
86 "Boolean",
87 "Integer",
88 #define INTEGER 2
89 "Bitstring",
90 "String",
91 #define STRING 4
92 "Null",
93 #define ASN_NULL 5
94 "ObjID",
95 #define OBJECTID 6
96 "ObjectDes",
97 "U-8","U-9","U-10","U-11", /* 8-11 */
98 "U-12","U-13","U-14","U-15", /* 12-15 */
99 "Sequence",
100 #define SEQUENCE 16
101 "Set"
105 * Application-wide ASN.1 types from the Internet SMI and their tags
107 static const char *Application[] = {
108 "IpAddress",
109 #define IPADDR 0
110 "Counter",
111 #define COUNTER 1
112 "Gauge",
113 #define GAUGE 2
114 "TimeTicks",
115 #define TIMETICKS 3
116 "Opaque",
117 #define OPAQUE 4
118 "C-5",
119 "Counter64"
120 #define COUNTER64 6
124 * Context-specific ASN.1 types for the SNMP PDUs and their tags
126 static const char *Context[] = {
127 "GetRequest",
128 #define GETREQ 0
129 "GetNextRequest",
130 #define GETNEXTREQ 1
131 "GetResponse",
132 #define GETRESP 2
133 "SetRequest",
134 #define SETREQ 3
135 "Trap",
136 #define TRAP 4
137 "GetBulk",
138 #define GETBULKREQ 5
139 "Inform",
140 #define INFORMREQ 6
141 "V2Trap",
142 #define V2TRAP 7
143 "Report"
144 #define REPORT 8
147 #define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ)
148 #define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
149 #define WRITE_CLASS(x) (x == SETREQ)
150 #define RESPONSE_CLASS(x) (x == GETRESP)
151 #define INTERNAL_CLASS(x) (x == REPORT)
154 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
156 static const char *Exceptions[] = {
157 "noSuchObject",
158 #define NOSUCHOBJECT 0
159 "noSuchInstance",
160 #define NOSUCHINSTANCE 1
161 "endOfMibView",
162 #define ENDOFMIBVIEW 2
166 * Private ASN.1 types
167 * The Internet SMI does not specify any
169 static const char *Private[] = {
170 "P-0"
174 * error-status values for any SNMP PDU
176 static const char *ErrorStatus[] = {
177 "noError",
178 "tooBig",
179 "noSuchName",
180 "badValue",
181 "readOnly",
182 "genErr",
183 "noAccess",
184 "wrongType",
185 "wrongLength",
186 "wrongEncoding",
187 "wrongValue",
188 "noCreation",
189 "inconsistentValue",
190 "resourceUnavailable",
191 "commitFailed",
192 "undoFailed",
193 "authorizationError",
194 "notWritable",
195 "inconsistentName"
197 #define DECODE_ErrorStatus(e) \
198 ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
199 ? ErrorStatus[e] \
200 : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
203 * generic-trap values in the SNMP Trap-PDU
205 static const char *GenericTrap[] = {
206 "coldStart",
207 "warmStart",
208 "linkDown",
209 "linkUp",
210 "authenticationFailure",
211 "egpNeighborLoss",
212 "enterpriseSpecific"
213 #define GT_ENTERPRISE 6
215 #define DECODE_GenericTrap(t) \
216 ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
217 ? GenericTrap[t] \
218 : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
221 * ASN.1 type class table
222 * Ties together the preceding Universal, Application, Context, and Private
223 * type definitions.
225 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
226 static const struct {
227 const char *name;
228 const char **Id;
229 int numIDs;
230 } Class[] = {
231 defineCLASS(Universal),
232 #define UNIVERSAL 0
233 defineCLASS(Application),
234 #define APPLICATION 1
235 defineCLASS(Context),
236 #define CONTEXT 2
237 defineCLASS(Private),
238 #define PRIVATE 3
239 defineCLASS(Exceptions),
240 #define EXCEPTIONS 4
244 * defined forms for ASN.1 types
246 static const char *Form[] = {
247 "Primitive",
248 #define PRIMITIVE 0
249 "Constructed",
250 #define CONSTRUCTED 1
254 * A structure for the OID tree for the compiled-in MIB.
255 * This is stored as a general-order tree.
257 static struct obj {
258 const char *desc; /* name of object */
259 u_char oid; /* sub-id following parent */
260 u_char type; /* object type (unused) */
261 struct obj *child, *next; /* child and next sibling pointers */
262 } *objp = NULL;
265 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
266 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
267 * a value for `mibroot'.
269 * In particular, this is gross, as this is including initialized structures,
270 * and by right shouldn't be an "include" file.
272 #include "mib.h"
275 * This defines a list of OIDs which will be abbreviated on output.
276 * Currently, this includes the prefixes for the Internet MIB, the
277 * private enterprises tree, and the experimental tree.
279 #define OID_FIRST_OCTET(x, y) (((x)*40) + (y)) /* X.690 8.19.4 */
281 #ifndef NO_ABREV_MIB
282 static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
283 #endif
284 #ifndef NO_ABREV_ENTER
285 static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
286 #endif
287 #ifndef NO_ABREV_EXPERI
288 static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
289 #endif
290 #ifndef NO_ABBREV_SNMPMODS
291 static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
292 #endif
294 #define OBJ_ABBREV_ENTRY(prefix, obj) \
295 { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
296 static const struct obj_abrev {
297 const char *prefix; /* prefix for this abrev */
298 struct obj *node; /* pointer into object table */
299 const uint8_t *oid; /* ASN.1 encoded OID */
300 size_t oid_len; /* length of OID */
301 } obj_abrev_list[] = {
302 #ifndef NO_ABREV_MIB
303 /* .iso.org.dod.internet.mgmt.mib */
304 OBJ_ABBREV_ENTRY("", mib),
305 #endif
306 #ifndef NO_ABREV_ENTER
307 /* .iso.org.dod.internet.private.enterprises */
308 OBJ_ABBREV_ENTRY("E:", enterprises),
309 #endif
310 #ifndef NO_ABREV_EXPERI
311 /* .iso.org.dod.internet.experimental */
312 OBJ_ABBREV_ENTRY("X:", experimental),
313 #endif
314 #ifndef NO_ABBREV_SNMPMODS
315 /* .iso.org.dod.internet.snmpV2.snmpModules */
316 OBJ_ABBREV_ENTRY("S:", snmpModules),
317 #endif
318 { 0,0,0,0 }
322 * This is used in the OID print routine to walk down the object tree
323 * rooted at `mibroot'.
325 #define OBJ_PRINT(o, suppressdot) \
327 if (objp) { \
328 do { \
329 if ((o) == objp->oid) \
330 break; \
331 } while ((objp = objp->next) != NULL); \
333 if (objp) { \
334 ND_PRINT((ndo, suppressdot?"%s":".%s", objp->desc)); \
335 objp = objp->child; \
336 } else \
337 ND_PRINT((ndo, suppressdot?"%u":".%u", (o))); \
341 * This is the definition for the Any-Data-Type storage used purely for
342 * temporary internal representation while decoding an ASN.1 data stream.
344 struct be {
345 uint32_t asnlen;
346 union {
347 const uint8_t *raw;
348 int32_t integer;
349 uint32_t uns;
350 const u_char *str;
351 uint64_t uns64;
352 } data;
353 u_short id;
354 u_char form, class; /* tag info */
355 u_char type;
356 #define BE_ANY 255
357 #define BE_NONE 0
358 #define BE_NULL 1
359 #define BE_OCTET 2
360 #define BE_OID 3
361 #define BE_INT 4
362 #define BE_UNS 5
363 #define BE_STR 6
364 #define BE_SEQ 7
365 #define BE_INETADDR 8
366 #define BE_PDU 9
367 #define BE_UNS64 10
368 #define BE_NOSUCHOBJECT 128
369 #define BE_NOSUCHINST 129
370 #define BE_ENDOFMIBVIEW 130
374 * SNMP versions recognized by this module
376 static const char *SnmpVersion[] = {
377 "SNMPv1",
378 #define SNMP_VERSION_1 0
379 "SNMPv2c",
380 #define SNMP_VERSION_2 1
381 "SNMPv2u",
382 #define SNMP_VERSION_2U 2
383 "SNMPv3"
384 #define SNMP_VERSION_3 3
388 * Defaults for SNMP PDU components
390 #define DEF_COMMUNITY "public"
393 * constants for ASN.1 decoding
395 #define OIDMUX 40
396 #define ASNLEN_INETADDR 4
397 #define ASN_SHIFT7 7
398 #define ASN_SHIFT8 8
399 #define ASN_BIT8 0x80
400 #define ASN_LONGLEN 0x80
402 #define ASN_ID_BITS 0x1f
403 #define ASN_FORM_BITS 0x20
404 #define ASN_FORM_SHIFT 5
405 #define ASN_CLASS_BITS 0xc0
406 #define ASN_CLASS_SHIFT 6
408 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
411 * This decodes the next ASN.1 object in the stream pointed to by "p"
412 * (and of real-length "len") and stores the intermediate data in the
413 * provided BE object.
415 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
416 * O/w, this returns the number of bytes parsed from "p".
418 static int
419 asn1_parse(netdissect_options *ndo,
420 register const u_char *p, u_int len, struct be *elem)
422 u_char form, class, id;
423 int i, hdr;
425 elem->asnlen = 0;
426 elem->type = BE_ANY;
427 if (len < 1) {
428 ND_PRINT((ndo, "[nothing to parse]"));
429 return -1;
431 ND_TCHECK(*p);
434 * it would be nice to use a bit field, but you can't depend on them.
435 * +---+---+---+---+---+---+---+---+
436 * + class |frm| id |
437 * +---+---+---+---+---+---+---+---+
438 * 7 6 5 4 3 2 1 0
440 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
441 #ifdef notdef
442 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
443 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
444 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
445 #else
446 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
447 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
448 #endif
449 elem->form = form;
450 elem->class = class;
451 elem->id = id;
452 p++; len--; hdr = 1;
453 /* extended tag field */
454 if (id == ASN_ID_EXT) {
456 * The ID follows, as a sequence of octets with the
457 * 8th bit set and the remaining 7 bits being
458 * the next 7 bits of the value, terminated with
459 * an octet with the 8th bit not set.
461 * First, assemble all the octets with the 8th
462 * bit set. XXX - this doesn't handle a value
463 * that won't fit in 32 bits.
465 id = 0;
466 ND_TCHECK(*p);
467 while (*p & ASN_BIT8) {
468 if (len < 1) {
469 ND_PRINT((ndo, "[Xtagfield?]"));
470 return -1;
472 id = (id << 7) | (*p & ~ASN_BIT8);
473 len--;
474 hdr++;
475 p++;
476 ND_TCHECK(*p);
478 if (len < 1) {
479 ND_PRINT((ndo, "[Xtagfield?]"));
480 return -1;
482 ND_TCHECK(*p);
483 elem->id = id = (id << 7) | *p;
484 --len;
485 ++hdr;
486 ++p;
488 if (len < 1) {
489 ND_PRINT((ndo, "[no asnlen]"));
490 return -1;
492 ND_TCHECK(*p);
493 elem->asnlen = *p;
494 p++; len--; hdr++;
495 if (elem->asnlen & ASN_BIT8) {
496 uint32_t noct = elem->asnlen % ASN_BIT8;
497 elem->asnlen = 0;
498 if (len < noct) {
499 ND_PRINT((ndo, "[asnlen? %d<%d]", len, noct));
500 return -1;
502 ND_TCHECK2(*p, noct);
503 for (; noct-- > 0; len--, hdr++)
504 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
506 if (len < elem->asnlen) {
507 ND_PRINT((ndo, "[len%d<asnlen%u]", len, elem->asnlen));
508 return -1;
510 if (form >= sizeof(Form)/sizeof(Form[0])) {
511 ND_PRINT((ndo, "[form?%d]", form));
512 return -1;
514 if (class >= sizeof(Class)/sizeof(Class[0])) {
515 ND_PRINT((ndo, "[class?%c/%d]", *Form[form], class));
516 return -1;
518 if ((int)id >= Class[class].numIDs) {
519 ND_PRINT((ndo, "[id?%c/%s/%d]", *Form[form], Class[class].name, id));
520 return -1;
522 ND_TCHECK2(*p, elem->asnlen);
524 switch (form) {
525 case PRIMITIVE:
526 switch (class) {
527 case UNIVERSAL:
528 switch (id) {
529 case STRING:
530 elem->type = BE_STR;
531 elem->data.str = p;
532 break;
534 case INTEGER: {
535 register int32_t data;
536 elem->type = BE_INT;
537 data = 0;
539 if (elem->asnlen == 0) {
540 ND_PRINT((ndo, "[asnlen=0]"));
541 return -1;
543 if (*p & ASN_BIT8) /* negative */
544 data = -1;
545 for (i = elem->asnlen; i-- > 0; p++)
546 data = (data << ASN_SHIFT8) | *p;
547 elem->data.integer = data;
548 break;
551 case OBJECTID:
552 elem->type = BE_OID;
553 elem->data.raw = (const uint8_t *)p;
554 break;
556 case ASN_NULL:
557 elem->type = BE_NULL;
558 elem->data.raw = NULL;
559 break;
561 default:
562 elem->type = BE_OCTET;
563 elem->data.raw = (const uint8_t *)p;
564 ND_PRINT((ndo, "[P/U/%s]", Class[class].Id[id]));
565 break;
567 break;
569 case APPLICATION:
570 switch (id) {
571 case IPADDR:
572 elem->type = BE_INETADDR;
573 elem->data.raw = (const uint8_t *)p;
574 break;
576 case COUNTER:
577 case GAUGE:
578 case TIMETICKS: {
579 register uint32_t data;
580 elem->type = BE_UNS;
581 data = 0;
582 for (i = elem->asnlen; i-- > 0; p++)
583 data = (data << 8) + *p;
584 elem->data.uns = data;
585 break;
588 case COUNTER64: {
589 register uint64_t data64;
590 elem->type = BE_UNS64;
591 data64 = 0;
592 for (i = elem->asnlen; i-- > 0; p++)
593 data64 = (data64 << 8) + *p;
594 elem->data.uns64 = data64;
595 break;
598 default:
599 elem->type = BE_OCTET;
600 elem->data.raw = (const uint8_t *)p;
601 ND_PRINT((ndo, "[P/A/%s]",
602 Class[class].Id[id]));
603 break;
605 break;
607 case CONTEXT:
608 switch (id) {
609 case NOSUCHOBJECT:
610 elem->type = BE_NOSUCHOBJECT;
611 elem->data.raw = NULL;
612 break;
614 case NOSUCHINSTANCE:
615 elem->type = BE_NOSUCHINST;
616 elem->data.raw = NULL;
617 break;
619 case ENDOFMIBVIEW:
620 elem->type = BE_ENDOFMIBVIEW;
621 elem->data.raw = NULL;
622 break;
624 break;
626 default:
627 ND_PRINT((ndo, "[P/%s/%s]", Class[class].name, Class[class].Id[id]));
628 elem->type = BE_OCTET;
629 elem->data.raw = (const uint8_t *)p;
630 break;
632 break;
634 case CONSTRUCTED:
635 switch (class) {
636 case UNIVERSAL:
637 switch (id) {
638 case SEQUENCE:
639 elem->type = BE_SEQ;
640 elem->data.raw = (const uint8_t *)p;
641 break;
643 default:
644 elem->type = BE_OCTET;
645 elem->data.raw = (const uint8_t *)p;
646 ND_PRINT((ndo, "C/U/%s", Class[class].Id[id]));
647 break;
649 break;
651 case CONTEXT:
652 elem->type = BE_PDU;
653 elem->data.raw = (const uint8_t *)p;
654 break;
656 default:
657 elem->type = BE_OCTET;
658 elem->data.raw = (const uint8_t *)p;
659 ND_PRINT((ndo, "C/%s/%s", Class[class].name, Class[class].Id[id]));
660 break;
662 break;
664 p += elem->asnlen;
665 len -= elem->asnlen;
666 return elem->asnlen + hdr;
668 trunc:
669 ND_PRINT((ndo, "%s", tstr));
670 return -1;
673 static int
674 asn1_print_octets(netdissect_options *ndo, struct be *elem)
676 const u_char *p = (const u_char *)elem->data.raw;
677 uint32_t asnlen = elem->asnlen;
678 uint32_t i;
680 ND_TCHECK2(*p, asnlen);
681 for (i = asnlen; i-- > 0; p++)
682 ND_PRINT((ndo, "_%.2x", *p));
683 return 0;
685 trunc:
686 ND_PRINT((ndo, "%s", tstr));
687 return -1;
690 static int
691 asn1_print_string(netdissect_options *ndo, struct be *elem)
693 register int printable = 1, first = 1;
694 const u_char *p;
695 uint32_t asnlen = elem->asnlen;
696 uint32_t i;
698 p = elem->data.str;
699 ND_TCHECK2(*p, asnlen);
700 for (i = asnlen; printable && i-- > 0; p++)
701 printable = ND_ISPRINT(*p);
702 p = elem->data.str;
703 if (printable) {
704 ND_PRINT((ndo, "\""));
705 if (fn_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
706 ND_PRINT((ndo, "\""));
707 goto trunc;
709 ND_PRINT((ndo, "\""));
710 } else {
711 for (i = asnlen; i-- > 0; p++) {
712 ND_PRINT((ndo, first ? "%.2x" : "_%.2x", *p));
713 first = 0;
716 return 0;
718 trunc:
719 ND_PRINT((ndo, "%s", tstr));
720 return -1;
724 * Display the ASN.1 object represented by the BE object.
725 * This used to be an integral part of asn1_parse() before the intermediate
726 * BE form was added.
728 static int
729 asn1_print(netdissect_options *ndo,
730 struct be *elem)
732 const u_char *p;
733 uint32_t asnlen = elem->asnlen;
734 uint32_t i;
736 switch (elem->type) {
738 case BE_OCTET:
739 if (asn1_print_octets(ndo, elem) == -1)
740 return -1;
741 break;
743 case BE_NULL:
744 break;
746 case BE_OID: {
747 int o = 0, first = -1;
749 p = (const u_char *)elem->data.raw;
750 i = asnlen;
751 if (!ndo->ndo_nflag && asnlen > 2) {
752 const struct obj_abrev *a = &obj_abrev_list[0];
753 for (; a->node; a++) {
754 if (i < a->oid_len)
755 continue;
756 if (!ND_TTEST2(*p, a->oid_len))
757 continue;
758 if (memcmp(a->oid, p, a->oid_len) == 0) {
759 objp = a->node->child;
760 i -= a->oid_len;
761 p += a->oid_len;
762 ND_PRINT((ndo, "%s", a->prefix));
763 first = 1;
764 break;
769 for (; i-- > 0; p++) {
770 ND_TCHECK(*p);
771 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
772 if (*p & ASN_LONGLEN)
773 continue;
776 * first subitem encodes two items with
777 * 1st*OIDMUX+2nd
778 * (see X.690:1997 clause 8.19 for the details)
780 if (first < 0) {
781 int s;
782 if (!ndo->ndo_nflag)
783 objp = mibroot;
784 first = 0;
785 s = o / OIDMUX;
786 if (s > 2) s = 2;
787 OBJ_PRINT(s, first);
788 o -= s * OIDMUX;
790 OBJ_PRINT(o, first);
791 if (--first < 0)
792 first = 0;
793 o = 0;
795 break;
798 case BE_INT:
799 ND_PRINT((ndo, "%d", elem->data.integer));
800 break;
802 case BE_UNS:
803 ND_PRINT((ndo, "%u", elem->data.uns));
804 break;
806 case BE_UNS64:
807 ND_PRINT((ndo, "%" PRIu64, elem->data.uns64));
808 break;
810 case BE_STR:
811 if (asn1_print_string(ndo, elem) == -1)
812 return -1;
813 break;
815 case BE_SEQ:
816 ND_PRINT((ndo, "Seq(%u)", elem->asnlen));
817 break;
819 case BE_INETADDR:
820 if (asnlen != ASNLEN_INETADDR)
821 ND_PRINT((ndo, "[inetaddr len!=%d]", ASNLEN_INETADDR));
822 p = (const u_char *)elem->data.raw;
823 ND_TCHECK2(*p, asnlen);
824 for (i = asnlen; i-- != 0; p++) {
825 ND_PRINT((ndo, (i == asnlen-1) ? "%u" : ".%u", *p));
827 break;
829 case BE_NOSUCHOBJECT:
830 case BE_NOSUCHINST:
831 case BE_ENDOFMIBVIEW:
832 ND_PRINT((ndo, "[%s]", Class[EXCEPTIONS].Id[elem->id]));
833 break;
835 case BE_PDU:
836 ND_PRINT((ndo, "%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen));
837 break;
839 case BE_ANY:
840 ND_PRINT((ndo, "[BE_ANY!?]"));
841 break;
843 default:
844 ND_PRINT((ndo, "[be!?]"));
845 break;
847 return 0;
849 trunc:
850 ND_PRINT((ndo, "%s", tstr));
851 return -1;
854 #ifdef notdef
856 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
857 * This will work for any ASN.1 stream, not just an SNMP PDU.
859 * By adding newlines and spaces at the correct places, this would print in
860 * Rose-Normal-Form.
862 * This is not currently used.
864 static void
865 asn1_decode(u_char *p, u_int length)
867 struct be elem;
868 int i = 0;
870 while (i >= 0 && length > 0) {
871 i = asn1_parse(ndo, p, length, &elem);
872 if (i >= 0) {
873 ND_PRINT((ndo, " "));
874 if (asn1_print(ndo, &elem) < 0)
875 return;
876 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
877 ND_PRINT((ndo, " {"));
878 asn1_decode(elem.data.raw, elem.asnlen);
879 ND_PRINT((ndo, " }"));
881 length -= i;
882 p += i;
886 #endif
888 #ifdef USE_LIBSMI
890 struct smi2be {
891 SmiBasetype basetype;
892 int be;
895 static const struct smi2be smi2betab[] = {
896 { SMI_BASETYPE_INTEGER32, BE_INT },
897 { SMI_BASETYPE_OCTETSTRING, BE_STR },
898 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
899 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
900 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
901 { SMI_BASETYPE_INTEGER64, BE_NONE },
902 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
903 { SMI_BASETYPE_FLOAT32, BE_NONE },
904 { SMI_BASETYPE_FLOAT64, BE_NONE },
905 { SMI_BASETYPE_FLOAT128, BE_NONE },
906 { SMI_BASETYPE_ENUM, BE_INT },
907 { SMI_BASETYPE_BITS, BE_STR },
908 { SMI_BASETYPE_UNKNOWN, BE_NONE }
911 static int
912 smi_decode_oid(netdissect_options *ndo,
913 struct be *elem, unsigned int *oid,
914 unsigned int oidsize, unsigned int *oidlen)
916 const u_char *p = (const u_char *)elem->data.raw;
917 uint32_t asnlen = elem->asnlen;
918 int o = 0, first = -1, i = asnlen;
919 unsigned int firstval;
921 for (*oidlen = 0; i-- > 0; p++) {
922 ND_TCHECK(*p);
923 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
924 if (*p & ASN_LONGLEN)
925 continue;
928 * first subitem encodes two items with 1st*OIDMUX+2nd
929 * (see X.690:1997 clause 8.19 for the details)
931 if (first < 0) {
932 first = 0;
933 firstval = o / OIDMUX;
934 if (firstval > 2) firstval = 2;
935 o -= firstval * OIDMUX;
936 if (*oidlen < oidsize) {
937 oid[(*oidlen)++] = firstval;
940 if (*oidlen < oidsize) {
941 oid[(*oidlen)++] = o;
943 o = 0;
945 return 0;
947 trunc:
948 ND_PRINT((ndo, "%s", tstr));
949 return -1;
952 static int smi_check_type(SmiBasetype basetype, int be)
954 int i;
956 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
957 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
958 return 1;
962 return 0;
965 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
966 struct be *elem)
968 int ok = 1;
970 switch (smiType->basetype) {
971 case SMI_BASETYPE_OBJECTIDENTIFIER:
972 case SMI_BASETYPE_OCTETSTRING:
973 if (smiRange->minValue.value.unsigned32
974 == smiRange->maxValue.value.unsigned32) {
975 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
976 } else {
977 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
978 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
980 break;
982 case SMI_BASETYPE_INTEGER32:
983 ok = (elem->data.integer >= smiRange->minValue.value.integer32
984 && elem->data.integer <= smiRange->maxValue.value.integer32);
985 break;
987 case SMI_BASETYPE_UNSIGNED32:
988 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
989 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
990 break;
992 case SMI_BASETYPE_UNSIGNED64:
993 /* XXX */
994 break;
996 /* case SMI_BASETYPE_INTEGER64: SMIng */
997 /* case SMI_BASETYPE_FLOAT32: SMIng */
998 /* case SMI_BASETYPE_FLOAT64: SMIng */
999 /* case SMI_BASETYPE_FLOAT128: SMIng */
1001 case SMI_BASETYPE_ENUM:
1002 case SMI_BASETYPE_BITS:
1003 case SMI_BASETYPE_UNKNOWN:
1004 ok = 1;
1005 break;
1007 default:
1008 ok = 0;
1009 break;
1012 return ok;
1015 static int smi_check_range(SmiType *smiType, struct be *elem)
1017 SmiRange *smiRange;
1018 int ok = 1;
1020 for (smiRange = smiGetFirstRange(smiType);
1021 smiRange;
1022 smiRange = smiGetNextRange(smiRange)) {
1024 ok = smi_check_a_range(smiType, smiRange, elem);
1026 if (ok) {
1027 break;
1031 if (ok) {
1032 SmiType *parentType;
1033 parentType = smiGetParentType(smiType);
1034 if (parentType) {
1035 ok = smi_check_range(parentType, elem);
1039 return ok;
1042 static SmiNode *
1043 smi_print_variable(netdissect_options *ndo,
1044 struct be *elem, int *status)
1046 unsigned int oid[128], oidlen;
1047 SmiNode *smiNode = NULL;
1048 unsigned int i;
1050 if (!nd_smi_module_loaded) {
1051 *status = asn1_print(ndo, elem);
1052 return NULL;
1054 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1055 &oidlen);
1056 if (*status < 0)
1057 return NULL;
1058 smiNode = smiGetNodeByOID(oidlen, oid);
1059 if (! smiNode) {
1060 *status = asn1_print(ndo, elem);
1061 return NULL;
1063 if (ndo->ndo_vflag) {
1064 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1066 ND_PRINT((ndo, "%s", smiNode->name));
1067 if (smiNode->oidlen < oidlen) {
1068 for (i = smiNode->oidlen; i < oidlen; i++) {
1069 ND_PRINT((ndo, ".%u", oid[i]));
1072 *status = 0;
1073 return smiNode;
1076 static int
1077 smi_print_value(netdissect_options *ndo,
1078 SmiNode *smiNode, u_short pduid, struct be *elem)
1080 unsigned int i, oid[128], oidlen;
1081 SmiType *smiType;
1082 SmiNamedNumber *nn;
1083 int done = 0;
1085 if (! smiNode || ! (smiNode->nodekind
1086 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1087 return asn1_print(ndo, elem);
1090 if (elem->type == BE_NOSUCHOBJECT
1091 || elem->type == BE_NOSUCHINST
1092 || elem->type == BE_ENDOFMIBVIEW) {
1093 return asn1_print(ndo, elem);
1096 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1097 ND_PRINT((ndo, "[notNotifyable]"));
1100 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1101 ND_PRINT((ndo, "[notReadable]"));
1104 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1105 ND_PRINT((ndo, "[notWritable]"));
1108 if (RESPONSE_CLASS(pduid)
1109 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1110 ND_PRINT((ndo, "[noAccess]"));
1113 smiType = smiGetNodeType(smiNode);
1114 if (! smiType) {
1115 return asn1_print(ndo, elem);
1118 if (! smi_check_type(smiType->basetype, elem->type)) {
1119 ND_PRINT((ndo, "[wrongType]"));
1122 if (! smi_check_range(smiType, elem)) {
1123 ND_PRINT((ndo, "[outOfRange]"));
1126 /* resolve bits to named bits */
1128 /* check whether instance identifier is valid */
1130 /* apply display hints (integer, octetstring) */
1132 /* convert instance identifier to index type values */
1134 switch (elem->type) {
1135 case BE_OID:
1136 if (smiType->basetype == SMI_BASETYPE_BITS) {
1137 /* print bit labels */
1138 } else {
1139 if (nd_smi_module_loaded &&
1140 smi_decode_oid(ndo, elem, oid,
1141 sizeof(oid)/sizeof(unsigned int),
1142 &oidlen) == 0) {
1143 smiNode = smiGetNodeByOID(oidlen, oid);
1144 if (smiNode) {
1145 if (ndo->ndo_vflag) {
1146 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1148 ND_PRINT((ndo, "%s", smiNode->name));
1149 if (smiNode->oidlen < oidlen) {
1150 for (i = smiNode->oidlen;
1151 i < oidlen; i++) {
1152 ND_PRINT((ndo, ".%u", oid[i]));
1155 done++;
1159 break;
1161 case BE_INT:
1162 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1163 for (nn = smiGetFirstNamedNumber(smiType);
1165 nn = smiGetNextNamedNumber(nn)) {
1166 if (nn->value.value.integer32
1167 == elem->data.integer) {
1168 ND_PRINT((ndo, "%s", nn->name));
1169 ND_PRINT((ndo, "(%d)", elem->data.integer));
1170 done++;
1171 break;
1175 break;
1178 if (! done) {
1179 return asn1_print(ndo, elem);
1181 return 0;
1183 #endif
1186 * General SNMP header
1187 * SEQUENCE {
1188 * version INTEGER {version-1(0)},
1189 * community OCTET STRING,
1190 * data ANY -- PDUs
1192 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1193 * SEQUENCE {
1194 * request-id INTEGER,
1195 * error-status INTEGER,
1196 * error-index INTEGER,
1197 * varbindlist SEQUENCE OF
1198 * SEQUENCE {
1199 * name ObjectName,
1200 * value ObjectValue
1203 * PDU for Trap:
1204 * SEQUENCE {
1205 * enterprise OBJECT IDENTIFIER,
1206 * agent-addr NetworkAddress,
1207 * generic-trap INTEGER,
1208 * specific-trap INTEGER,
1209 * time-stamp TimeTicks,
1210 * varbindlist SEQUENCE OF
1211 * SEQUENCE {
1212 * name ObjectName,
1213 * value ObjectValue
1219 * Decode SNMP varBind
1221 static void
1222 varbind_print(netdissect_options *ndo,
1223 u_short pduid, const u_char *np, u_int length)
1225 struct be elem;
1226 int count = 0, ind;
1227 #ifdef USE_LIBSMI
1228 SmiNode *smiNode = NULL;
1229 #endif
1230 int status;
1232 /* Sequence of varBind */
1233 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1234 return;
1235 if (elem.type != BE_SEQ) {
1236 ND_PRINT((ndo, "[!SEQ of varbind]"));
1237 asn1_print(ndo, &elem);
1238 return;
1240 if ((u_int)count < length)
1241 ND_PRINT((ndo, "[%d extra after SEQ of varbind]", length - count));
1242 /* descend */
1243 length = elem.asnlen;
1244 np = (const u_char *)elem.data.raw;
1246 for (ind = 1; length > 0; ind++) {
1247 const u_char *vbend;
1248 u_int vblength;
1250 ND_PRINT((ndo, " "));
1252 /* Sequence */
1253 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1254 return;
1255 if (elem.type != BE_SEQ) {
1256 ND_PRINT((ndo, "[!varbind]"));
1257 asn1_print(ndo, &elem);
1258 return;
1260 vbend = np + count;
1261 vblength = length - count;
1262 /* descend */
1263 length = elem.asnlen;
1264 np = (const u_char *)elem.data.raw;
1266 /* objName (OID) */
1267 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1268 return;
1269 if (elem.type != BE_OID) {
1270 ND_PRINT((ndo, "[objName!=OID]"));
1271 asn1_print(ndo, &elem);
1272 return;
1274 #ifdef USE_LIBSMI
1275 smiNode = smi_print_variable(ndo, &elem, &status);
1276 #else
1277 status = asn1_print(ndo, &elem);
1278 #endif
1279 if (status < 0)
1280 return;
1281 length -= count;
1282 np += count;
1284 if (pduid != GETREQ && pduid != GETNEXTREQ
1285 && pduid != GETBULKREQ)
1286 ND_PRINT((ndo, "="));
1288 /* objVal (ANY) */
1289 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1290 return;
1291 if (pduid == GETREQ || pduid == GETNEXTREQ
1292 || pduid == GETBULKREQ) {
1293 if (elem.type != BE_NULL) {
1294 ND_PRINT((ndo, "[objVal!=NULL]"));
1295 if (asn1_print(ndo, &elem) < 0)
1296 return;
1298 } else {
1299 if (elem.type != BE_NULL) {
1300 #ifdef USE_LIBSMI
1301 status = smi_print_value(ndo, smiNode, pduid, &elem);
1302 #else
1303 status = asn1_print(ndo, &elem);
1304 #endif
1306 if (status < 0)
1307 return;
1309 length = vblength;
1310 np = vbend;
1315 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1316 * GetBulk, Inform, V2Trap, and Report
1318 static void
1319 snmppdu_print(netdissect_options *ndo,
1320 u_short pduid, const u_char *np, u_int length)
1322 struct be elem;
1323 int count = 0, error_status;
1325 /* reqId (Integer) */
1326 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1327 return;
1328 if (elem.type != BE_INT) {
1329 ND_PRINT((ndo, "[reqId!=INT]"));
1330 asn1_print(ndo, &elem);
1331 return;
1333 if (ndo->ndo_vflag)
1334 ND_PRINT((ndo, "R=%d ", elem.data.integer));
1335 length -= count;
1336 np += count;
1338 /* errorStatus (Integer) */
1339 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1340 return;
1341 if (elem.type != BE_INT) {
1342 ND_PRINT((ndo, "[errorStatus!=INT]"));
1343 asn1_print(ndo, &elem);
1344 return;
1346 error_status = 0;
1347 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1348 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1349 && elem.data.integer != 0) {
1350 char errbuf[20];
1351 ND_PRINT((ndo, "[errorStatus(%s)!=0]",
1352 DECODE_ErrorStatus(elem.data.integer)));
1353 } else if (pduid == GETBULKREQ) {
1354 ND_PRINT((ndo, " N=%d", elem.data.integer));
1355 } else if (elem.data.integer != 0) {
1356 char errbuf[20];
1357 ND_PRINT((ndo, " %s", DECODE_ErrorStatus(elem.data.integer)));
1358 error_status = elem.data.integer;
1360 length -= count;
1361 np += count;
1363 /* errorIndex (Integer) */
1364 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1365 return;
1366 if (elem.type != BE_INT) {
1367 ND_PRINT((ndo, "[errorIndex!=INT]"));
1368 asn1_print(ndo, &elem);
1369 return;
1371 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1372 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1373 && elem.data.integer != 0)
1374 ND_PRINT((ndo, "[errorIndex(%d)!=0]", elem.data.integer));
1375 else if (pduid == GETBULKREQ)
1376 ND_PRINT((ndo, " M=%d", elem.data.integer));
1377 else if (elem.data.integer != 0) {
1378 if (!error_status)
1379 ND_PRINT((ndo, "[errorIndex(%d) w/o errorStatus]", elem.data.integer));
1380 else
1381 ND_PRINT((ndo, "@%d", elem.data.integer));
1382 } else if (error_status) {
1383 ND_PRINT((ndo, "[errorIndex==0]"));
1385 length -= count;
1386 np += count;
1388 varbind_print(ndo, pduid, np, length);
1389 return;
1393 * Decode SNMP Trap PDU
1395 static void
1396 trappdu_print(netdissect_options *ndo,
1397 const u_char *np, u_int length)
1399 struct be elem;
1400 int count = 0, generic;
1402 ND_PRINT((ndo, " "));
1404 /* enterprise (oid) */
1405 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1406 return;
1407 if (elem.type != BE_OID) {
1408 ND_PRINT((ndo, "[enterprise!=OID]"));
1409 asn1_print(ndo, &elem);
1410 return;
1412 if (asn1_print(ndo, &elem) < 0)
1413 return;
1414 length -= count;
1415 np += count;
1417 ND_PRINT((ndo, " "));
1419 /* agent-addr (inetaddr) */
1420 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1421 return;
1422 if (elem.type != BE_INETADDR) {
1423 ND_PRINT((ndo, "[agent-addr!=INETADDR]"));
1424 asn1_print(ndo, &elem);
1425 return;
1427 if (asn1_print(ndo, &elem) < 0)
1428 return;
1429 length -= count;
1430 np += count;
1432 /* generic-trap (Integer) */
1433 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1434 return;
1435 if (elem.type != BE_INT) {
1436 ND_PRINT((ndo, "[generic-trap!=INT]"));
1437 asn1_print(ndo, &elem);
1438 return;
1440 generic = elem.data.integer;
1442 char buf[20];
1443 ND_PRINT((ndo, " %s", DECODE_GenericTrap(generic)));
1445 length -= count;
1446 np += count;
1448 /* specific-trap (Integer) */
1449 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1450 return;
1451 if (elem.type != BE_INT) {
1452 ND_PRINT((ndo, "[specific-trap!=INT]"));
1453 asn1_print(ndo, &elem);
1454 return;
1456 if (generic != GT_ENTERPRISE) {
1457 if (elem.data.integer != 0)
1458 ND_PRINT((ndo, "[specific-trap(%d)!=0]", elem.data.integer));
1459 } else
1460 ND_PRINT((ndo, " s=%d", elem.data.integer));
1461 length -= count;
1462 np += count;
1464 ND_PRINT((ndo, " "));
1466 /* time-stamp (TimeTicks) */
1467 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1468 return;
1469 if (elem.type != BE_UNS) { /* XXX */
1470 ND_PRINT((ndo, "[time-stamp!=TIMETICKS]"));
1471 asn1_print(ndo, &elem);
1472 return;
1474 if (asn1_print(ndo, &elem) < 0)
1475 return;
1476 length -= count;
1477 np += count;
1479 varbind_print(ndo, TRAP, np, length);
1480 return;
1484 * Decode arbitrary SNMP PDUs.
1486 static void
1487 pdu_print(netdissect_options *ndo,
1488 const u_char *np, u_int length, int version)
1490 struct be pdu;
1491 int count = 0;
1493 /* PDU (Context) */
1494 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1495 return;
1496 if (pdu.type != BE_PDU) {
1497 ND_PRINT((ndo, "[no PDU]"));
1498 return;
1500 if ((u_int)count < length)
1501 ND_PRINT((ndo, "[%d extra after PDU]", length - count));
1502 if (ndo->ndo_vflag) {
1503 ND_PRINT((ndo, "{ "));
1505 if (asn1_print(ndo, &pdu) < 0)
1506 return;
1507 ND_PRINT((ndo, " "));
1508 /* descend into PDU */
1509 length = pdu.asnlen;
1510 np = (const u_char *)pdu.data.raw;
1512 if (version == SNMP_VERSION_1 &&
1513 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1514 pdu.id == V2TRAP || pdu.id == REPORT)) {
1515 ND_PRINT((ndo, "[v2 PDU in v1 message]"));
1516 return;
1519 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1520 ND_PRINT((ndo, "[v1 PDU in v2 message]"));
1521 return;
1524 switch (pdu.id) {
1525 case TRAP:
1526 trappdu_print(ndo, np, length);
1527 break;
1528 case GETREQ:
1529 case GETNEXTREQ:
1530 case GETRESP:
1531 case SETREQ:
1532 case GETBULKREQ:
1533 case INFORMREQ:
1534 case V2TRAP:
1535 case REPORT:
1536 snmppdu_print(ndo, pdu.id, np, length);
1537 break;
1540 if (ndo->ndo_vflag) {
1541 ND_PRINT((ndo, " } "));
1546 * Decode a scoped SNMP PDU.
1548 static void
1549 scopedpdu_print(netdissect_options *ndo,
1550 const u_char *np, u_int length, int version)
1552 struct be elem;
1553 int count = 0;
1555 /* Sequence */
1556 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1557 return;
1558 if (elem.type != BE_SEQ) {
1559 ND_PRINT((ndo, "[!scoped PDU]"));
1560 asn1_print(ndo, &elem);
1561 return;
1563 length = elem.asnlen;
1564 np = (const u_char *)elem.data.raw;
1566 /* contextEngineID (OCTET STRING) */
1567 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1568 return;
1569 if (elem.type != BE_STR) {
1570 ND_PRINT((ndo, "[contextEngineID!=STR]"));
1571 asn1_print(ndo, &elem);
1572 return;
1574 length -= count;
1575 np += count;
1577 ND_PRINT((ndo, "E="));
1578 if (asn1_print_octets(ndo, &elem) == -1)
1579 return;
1580 ND_PRINT((ndo, " "));
1582 /* contextName (OCTET STRING) */
1583 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1584 return;
1585 if (elem.type != BE_STR) {
1586 ND_PRINT((ndo, "[contextName!=STR]"));
1587 asn1_print(ndo, &elem);
1588 return;
1590 length -= count;
1591 np += count;
1593 ND_PRINT((ndo, "C="));
1594 if (asn1_print_string(ndo, &elem) == -1)
1595 return;
1596 ND_PRINT((ndo, " "));
1598 pdu_print(ndo, np, length, version);
1602 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1604 static void
1605 community_print(netdissect_options *ndo,
1606 const u_char *np, u_int length, int version)
1608 struct be elem;
1609 int count = 0;
1611 /* Community (String) */
1612 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1613 return;
1614 if (elem.type != BE_STR) {
1615 ND_PRINT((ndo, "[comm!=STR]"));
1616 asn1_print(ndo, &elem);
1617 return;
1619 /* default community */
1620 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1621 strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1622 sizeof(DEF_COMMUNITY) - 1) == 0)) {
1623 /* ! "public" */
1624 ND_PRINT((ndo, "C="));
1625 if (asn1_print_string(ndo, &elem) == -1)
1626 return;
1627 ND_PRINT((ndo, " "));
1629 length -= count;
1630 np += count;
1632 pdu_print(ndo, np, length, version);
1636 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1638 static void
1639 usm_print(netdissect_options *ndo,
1640 const u_char *np, u_int length)
1642 struct be elem;
1643 int count = 0;
1645 /* Sequence */
1646 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1647 return;
1648 if (elem.type != BE_SEQ) {
1649 ND_PRINT((ndo, "[!usm]"));
1650 asn1_print(ndo, &elem);
1651 return;
1653 length = elem.asnlen;
1654 np = (const u_char *)elem.data.raw;
1656 /* msgAuthoritativeEngineID (OCTET STRING) */
1657 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1658 return;
1659 if (elem.type != BE_STR) {
1660 ND_PRINT((ndo, "[msgAuthoritativeEngineID!=STR]"));
1661 asn1_print(ndo, &elem);
1662 return;
1664 length -= count;
1665 np += count;
1667 /* msgAuthoritativeEngineBoots (INTEGER) */
1668 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1669 return;
1670 if (elem.type != BE_INT) {
1671 ND_PRINT((ndo, "[msgAuthoritativeEngineBoots!=INT]"));
1672 asn1_print(ndo, &elem);
1673 return;
1675 if (ndo->ndo_vflag)
1676 ND_PRINT((ndo, "B=%d ", elem.data.integer));
1677 length -= count;
1678 np += count;
1680 /* msgAuthoritativeEngineTime (INTEGER) */
1681 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1682 return;
1683 if (elem.type != BE_INT) {
1684 ND_PRINT((ndo, "[msgAuthoritativeEngineTime!=INT]"));
1685 asn1_print(ndo, &elem);
1686 return;
1688 if (ndo->ndo_vflag)
1689 ND_PRINT((ndo, "T=%d ", elem.data.integer));
1690 length -= count;
1691 np += count;
1693 /* msgUserName (OCTET STRING) */
1694 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1695 return;
1696 if (elem.type != BE_STR) {
1697 ND_PRINT((ndo, "[msgUserName!=STR]"));
1698 asn1_print(ndo, &elem);
1699 return;
1701 length -= count;
1702 np += count;
1704 ND_PRINT((ndo, "U="));
1705 if (asn1_print_string(ndo, &elem) == -1)
1706 return;
1707 ND_PRINT((ndo, " "));
1709 /* msgAuthenticationParameters (OCTET STRING) */
1710 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1711 return;
1712 if (elem.type != BE_STR) {
1713 ND_PRINT((ndo, "[msgAuthenticationParameters!=STR]"));
1714 asn1_print(ndo, &elem);
1715 return;
1717 length -= count;
1718 np += count;
1720 /* msgPrivacyParameters (OCTET STRING) */
1721 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1722 return;
1723 if (elem.type != BE_STR) {
1724 ND_PRINT((ndo, "[msgPrivacyParameters!=STR]"));
1725 asn1_print(ndo, &elem);
1726 return;
1728 length -= count;
1729 np += count;
1731 if ((u_int)count < length)
1732 ND_PRINT((ndo, "[%d extra after usm SEQ]", length - count));
1736 * Decode SNMPv3 Message Header (SNMPv3)
1738 static void
1739 v3msg_print(netdissect_options *ndo,
1740 const u_char *np, u_int length)
1742 struct be elem;
1743 int count = 0;
1744 u_char flags;
1745 int model;
1746 const u_char *xnp = np;
1747 int xlength = length;
1749 /* Sequence */
1750 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1751 return;
1752 if (elem.type != BE_SEQ) {
1753 ND_PRINT((ndo, "[!message]"));
1754 asn1_print(ndo, &elem);
1755 return;
1757 length = elem.asnlen;
1758 np = (const u_char *)elem.data.raw;
1760 if (ndo->ndo_vflag) {
1761 ND_PRINT((ndo, "{ "));
1764 /* msgID (INTEGER) */
1765 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1766 return;
1767 if (elem.type != BE_INT) {
1768 ND_PRINT((ndo, "[msgID!=INT]"));
1769 asn1_print(ndo, &elem);
1770 return;
1772 length -= count;
1773 np += count;
1775 /* msgMaxSize (INTEGER) */
1776 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1777 return;
1778 if (elem.type != BE_INT) {
1779 ND_PRINT((ndo, "[msgMaxSize!=INT]"));
1780 asn1_print(ndo, &elem);
1781 return;
1783 length -= count;
1784 np += count;
1786 /* msgFlags (OCTET STRING) */
1787 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1788 return;
1789 if (elem.type != BE_STR) {
1790 ND_PRINT((ndo, "[msgFlags!=STR]"));
1791 asn1_print(ndo, &elem);
1792 return;
1794 if (elem.asnlen != 1) {
1795 ND_PRINT((ndo, "[msgFlags size %d]", elem.asnlen));
1796 return;
1798 flags = elem.data.str[0];
1799 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1800 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1801 ND_PRINT((ndo, "[msgFlags=0x%02X]", flags));
1802 return;
1804 length -= count;
1805 np += count;
1807 ND_PRINT((ndo, "F=%s%s%s ",
1808 flags & 0x01 ? "a" : "",
1809 flags & 0x02 ? "p" : "",
1810 flags & 0x04 ? "r" : ""));
1812 /* msgSecurityModel (INTEGER) */
1813 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1814 return;
1815 if (elem.type != BE_INT) {
1816 ND_PRINT((ndo, "[msgSecurityModel!=INT]"));
1817 asn1_print(ndo, &elem);
1818 return;
1820 model = elem.data.integer;
1821 length -= count;
1822 np += count;
1824 if ((u_int)count < length)
1825 ND_PRINT((ndo, "[%d extra after message SEQ]", length - count));
1827 if (ndo->ndo_vflag) {
1828 ND_PRINT((ndo, "} "));
1831 if (model == 3) {
1832 if (ndo->ndo_vflag) {
1833 ND_PRINT((ndo, "{ USM "));
1835 } else {
1836 ND_PRINT((ndo, "[security model %d]", model));
1837 return;
1840 np = xnp + (np - xnp);
1841 length = xlength - (np - xnp);
1843 /* msgSecurityParameters (OCTET STRING) */
1844 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1845 return;
1846 if (elem.type != BE_STR) {
1847 ND_PRINT((ndo, "[msgSecurityParameters!=STR]"));
1848 asn1_print(ndo, &elem);
1849 return;
1851 length -= count;
1852 np += count;
1854 if (model == 3) {
1855 usm_print(ndo, elem.data.str, elem.asnlen);
1856 if (ndo->ndo_vflag) {
1857 ND_PRINT((ndo, "} "));
1861 if (ndo->ndo_vflag) {
1862 ND_PRINT((ndo, "{ ScopedPDU "));
1865 scopedpdu_print(ndo, np, length, 3);
1867 if (ndo->ndo_vflag) {
1868 ND_PRINT((ndo, "} "));
1873 * Decode SNMP header and pass on to PDU printing routines
1875 void
1876 snmp_print(netdissect_options *ndo,
1877 const u_char *np, u_int length)
1879 struct be elem;
1880 int count = 0;
1881 int version = 0;
1883 ND_PRINT((ndo, " "));
1885 /* initial Sequence */
1886 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1887 return;
1888 if (elem.type != BE_SEQ) {
1889 ND_PRINT((ndo, "[!init SEQ]"));
1890 asn1_print(ndo, &elem);
1891 return;
1893 if ((u_int)count < length)
1894 ND_PRINT((ndo, "[%d extra after iSEQ]", length - count));
1895 /* descend */
1896 length = elem.asnlen;
1897 np = (const u_char *)elem.data.raw;
1899 /* Version (INTEGER) */
1900 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1901 return;
1902 if (elem.type != BE_INT) {
1903 ND_PRINT((ndo, "[version!=INT]"));
1904 asn1_print(ndo, &elem);
1905 return;
1908 switch (elem.data.integer) {
1909 case SNMP_VERSION_1:
1910 case SNMP_VERSION_2:
1911 case SNMP_VERSION_3:
1912 if (ndo->ndo_vflag)
1913 ND_PRINT((ndo, "{ %s ", SnmpVersion[elem.data.integer]));
1914 break;
1915 default:
1916 ND_PRINT((ndo, "SNMP [version = %d]", elem.data.integer));
1917 return;
1919 version = elem.data.integer;
1920 length -= count;
1921 np += count;
1923 switch (version) {
1924 case SNMP_VERSION_1:
1925 case SNMP_VERSION_2:
1926 community_print(ndo, np, length, version);
1927 break;
1928 case SNMP_VERSION_3:
1929 v3msg_print(ndo, np, length);
1930 break;
1931 default:
1932 ND_PRINT((ndo, "[version = %d]", elem.data.integer));
1933 break;
1936 if (ndo->ndo_vflag) {
1937 ND_PRINT((ndo, "} "));