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