Remove building with NOCRYPTO option
[minix.git] / external / bsd / tcpdump / dist / print-snmp.c
blob67a8b6b6977f6b8ffa63d2e2d1175d1638ddb909
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 #include <sys/cdefs.h>
60 #ifndef lint
61 __RCSID("$NetBSD: print-snmp.c,v 1.4 2014/11/20 03:05:03 christos Exp $");
62 #endif
64 #define NETDISSECT_REWORKED
65 #ifdef HAVE_CONFIG_H
66 #include "config.h"
67 #endif
69 #include <tcpdump-stdinc.h>
71 #include <stdio.h>
72 #include <string.h>
74 #ifdef USE_LIBSMI
75 #include <smi.h>
76 #endif
78 #include "interface.h"
80 #undef OPAQUE /* defined in <wingdi.h> */
82 static const char tstr[] = "[|snmp]";
85 * Universal ASN.1 types
86 * (we only care about the tag values for those allowed in the Internet SMI)
88 static const char *Universal[] = {
89 "U-0",
90 "Boolean",
91 "Integer",
92 #define INTEGER 2
93 "Bitstring",
94 "String",
95 #define STRING 4
96 "Null",
97 #define ASN_NULL 5
98 "ObjID",
99 #define OBJECTID 6
100 "ObjectDes",
101 "U-8","U-9","U-10","U-11", /* 8-11 */
102 "U-12","U-13","U-14","U-15", /* 12-15 */
103 "Sequence",
104 #define SEQUENCE 16
105 "Set"
109 * Application-wide ASN.1 types from the Internet SMI and their tags
111 static const char *Application[] = {
112 "IpAddress",
113 #define IPADDR 0
114 "Counter",
115 #define COUNTER 1
116 "Gauge",
117 #define GAUGE 2
118 "TimeTicks",
119 #define TIMETICKS 3
120 "Opaque",
121 #define OPAQUE 4
122 "C-5",
123 "Counter64"
124 #define COUNTER64 6
128 * Context-specific ASN.1 types for the SNMP PDUs and their tags
130 static const char *Context[] = {
131 "GetRequest",
132 #define GETREQ 0
133 "GetNextRequest",
134 #define GETNEXTREQ 1
135 "GetResponse",
136 #define GETRESP 2
137 "SetRequest",
138 #define SETREQ 3
139 "Trap",
140 #define TRAP 4
141 "GetBulk",
142 #define GETBULKREQ 5
143 "Inform",
144 #define INFORMREQ 6
145 "V2Trap",
146 #define V2TRAP 7
147 "Report"
148 #define REPORT 8
151 #define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ)
152 #define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
153 #define WRITE_CLASS(x) (x == SETREQ)
154 #define RESPONSE_CLASS(x) (x == GETRESP)
155 #define INTERNAL_CLASS(x) (x == REPORT)
158 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
160 static const char *Exceptions[] = {
161 "noSuchObject",
162 #define NOSUCHOBJECT 0
163 "noSuchInstance",
164 #define NOSUCHINSTANCE 1
165 "endOfMibView",
166 #define ENDOFMIBVIEW 2
170 * Private ASN.1 types
171 * The Internet SMI does not specify any
173 static const char *Private[] = {
174 "P-0"
178 * error-status values for any SNMP PDU
180 static const char *ErrorStatus[] = {
181 "noError",
182 "tooBig",
183 "noSuchName",
184 "badValue",
185 "readOnly",
186 "genErr",
187 "noAccess",
188 "wrongType",
189 "wrongLength",
190 "wrongEncoding",
191 "wrongValue",
192 "noCreation",
193 "inconsistentValue",
194 "resourceUnavailable",
195 "commitFailed",
196 "undoFailed",
197 "authorizationError",
198 "notWritable",
199 "inconsistentName"
201 #define DECODE_ErrorStatus(e) \
202 ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
203 ? ErrorStatus[e] \
204 : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
207 * generic-trap values in the SNMP Trap-PDU
209 static const char *GenericTrap[] = {
210 "coldStart",
211 "warmStart",
212 "linkDown",
213 "linkUp",
214 "authenticationFailure",
215 "egpNeighborLoss",
216 "enterpriseSpecific"
217 #define GT_ENTERPRISE 6
219 #define DECODE_GenericTrap(t) \
220 ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
221 ? GenericTrap[t] \
222 : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
225 * ASN.1 type class table
226 * Ties together the preceding Universal, Application, Context, and Private
227 * type definitions.
229 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
230 static const struct {
231 const char *name;
232 const char **Id;
233 int numIDs;
234 } Class[] = {
235 defineCLASS(Universal),
236 #define UNIVERSAL 0
237 defineCLASS(Application),
238 #define APPLICATION 1
239 defineCLASS(Context),
240 #define CONTEXT 2
241 defineCLASS(Private),
242 #define PRIVATE 3
243 defineCLASS(Exceptions),
244 #define EXCEPTIONS 4
248 * defined forms for ASN.1 types
250 static const char *Form[] = {
251 "Primitive",
252 #define PRIMITIVE 0
253 "Constructed",
254 #define CONSTRUCTED 1
258 * A structure for the OID tree for the compiled-in MIB.
259 * This is stored as a general-order tree.
261 struct obj {
262 const char *desc; /* name of object */
263 u_char oid; /* sub-id following parent */
264 u_char type; /* object type (unused) */
265 struct obj *child, *next; /* child and next sibling pointers */
266 } *objp = NULL;
269 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
270 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
271 * a value for `mibroot'.
273 * In particular, this is gross, as this is including initialized structures,
274 * and by right shouldn't be an "include" file.
276 #include "mib.h"
279 * This defines a list of OIDs which will be abbreviated on output.
280 * Currently, this includes the prefixes for the Internet MIB, the
281 * private enterprises tree, and the experimental tree.
283 static const struct obj_abrev {
284 const char *prefix; /* prefix for this abrev */
285 struct obj *node; /* pointer into object table */
286 const char *oid; /* ASN.1 encoded OID */
287 } obj_abrev_list[] = {
288 #ifndef NO_ABREV_MIB
289 /* .iso.org.dod.internet.mgmt.mib */
290 { "", &_mib_obj, "\53\6\1\2\1" },
291 #endif
292 #ifndef NO_ABREV_ENTER
293 /* .iso.org.dod.internet.private.enterprises */
294 { "E:", &_enterprises_obj, "\53\6\1\4\1" },
295 #endif
296 #ifndef NO_ABREV_EXPERI
297 /* .iso.org.dod.internet.experimental */
298 { "X:", &_experimental_obj, "\53\6\1\3" },
299 #endif
300 #ifndef NO_ABBREV_SNMPMODS
301 /* .iso.org.dod.internet.snmpV2.snmpModules */
302 { "S:", &_snmpModules_obj, "\53\6\1\6\3" },
303 #endif
304 { 0,0,0 }
308 * This is used in the OID print routine to walk down the object tree
309 * rooted at `mibroot'.
311 #define OBJ_PRINT(o, suppressdot) \
313 if (objp) { \
314 do { \
315 if ((o) == objp->oid) \
316 break; \
317 } while ((objp = objp->next) != NULL); \
319 if (objp) { \
320 ND_PRINT((ndo, suppressdot?"%s":".%s", objp->desc)); \
321 objp = objp->child; \
322 } else \
323 ND_PRINT((ndo, suppressdot?"%u":".%u", (o))); \
327 * This is the definition for the Any-Data-Type storage used purely for
328 * temporary internal representation while decoding an ASN.1 data stream.
330 struct be {
331 uint32_t asnlen;
332 union {
333 caddr_t raw;
334 int32_t integer;
335 uint32_t uns;
336 const u_char *str;
337 struct {
338 uint32_t high;
339 uint32_t low;
340 } uns64;
341 } data;
342 u_short id;
343 u_char form, class; /* tag info */
344 u_char type;
345 #define BE_ANY 255
346 #define BE_NONE 0
347 #define BE_NULL 1
348 #define BE_OCTET 2
349 #define BE_OID 3
350 #define BE_INT 4
351 #define BE_UNS 5
352 #define BE_STR 6
353 #define BE_SEQ 7
354 #define BE_INETADDR 8
355 #define BE_PDU 9
356 #define BE_UNS64 10
357 #define BE_NOSUCHOBJECT 128
358 #define BE_NOSUCHINST 129
359 #define BE_ENDOFMIBVIEW 130
363 * SNMP versions recognized by this module
365 static const char *SnmpVersion[] = {
366 "SNMPv1",
367 #define SNMP_VERSION_1 0
368 "SNMPv2c",
369 #define SNMP_VERSION_2 1
370 "SNMPv2u",
371 #define SNMP_VERSION_2U 2
372 "SNMPv3"
373 #define SNMP_VERSION_3 3
377 * Defaults for SNMP PDU components
379 #define DEF_COMMUNITY "public"
382 * constants for ASN.1 decoding
384 #define OIDMUX 40
385 #define ASNLEN_INETADDR 4
386 #define ASN_SHIFT7 7
387 #define ASN_SHIFT8 8
388 #define ASN_BIT8 0x80
389 #define ASN_LONGLEN 0x80
391 #define ASN_ID_BITS 0x1f
392 #define ASN_FORM_BITS 0x20
393 #define ASN_FORM_SHIFT 5
394 #define ASN_CLASS_BITS 0xc0
395 #define ASN_CLASS_SHIFT 6
397 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
400 * This decodes the next ASN.1 object in the stream pointed to by "p"
401 * (and of real-length "len") and stores the intermediate data in the
402 * provided BE object.
404 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
405 * O/w, this returns the number of bytes parsed from "p".
407 static int
408 asn1_parse(netdissect_options *ndo,
409 register const u_char *p, u_int len, struct be *elem)
411 u_char form, class, id;
412 int i, hdr;
414 elem->asnlen = 0;
415 elem->type = BE_ANY;
416 if (len < 1) {
417 ND_PRINT((ndo, "[nothing to parse]"));
418 return -1;
420 ND_TCHECK(*p);
423 * it would be nice to use a bit field, but you can't depend on them.
424 * +---+---+---+---+---+---+---+---+
425 * + class |frm| id |
426 * +---+---+---+---+---+---+---+---+
427 * 7 6 5 4 3 2 1 0
429 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
430 #ifdef notdef
431 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
432 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
433 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
434 #else
435 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
436 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
437 #endif
438 elem->form = form;
439 elem->class = class;
440 elem->id = id;
441 p++; len--; hdr = 1;
442 /* extended tag field */
443 if (id == ASN_ID_EXT) {
445 * The ID follows, as a sequence of octets with the
446 * 8th bit set and the remaining 7 bits being
447 * the next 7 bits of the value, terminated with
448 * an octet with the 8th bit not set.
450 * First, assemble all the octets with the 8th
451 * bit set. XXX - this doesn't handle a value
452 * that won't fit in 32 bits.
454 for (id = 0; *p & ASN_BIT8; len--, hdr++, p++) {
455 if (len < 1) {
456 ND_PRINT((ndo, "[Xtagfield?]"));
457 return -1;
459 ND_TCHECK(*p);
460 id = (id << 7) | (*p & ~ASN_BIT8);
462 if (len < 1) {
463 ND_PRINT((ndo, "[Xtagfield?]"));
464 return -1;
466 ND_TCHECK(*p);
467 elem->id = id = (id << 7) | *p;
468 --len;
469 ++hdr;
470 ++p;
472 if (len < 1) {
473 ND_PRINT((ndo, "[no asnlen]"));
474 return -1;
476 ND_TCHECK(*p);
477 elem->asnlen = *p;
478 p++; len--; hdr++;
479 if (elem->asnlen & ASN_BIT8) {
480 uint32_t noct = elem->asnlen % ASN_BIT8;
481 elem->asnlen = 0;
482 if (len < noct) {
483 ND_PRINT((ndo, "[asnlen? %d<%d]", len, noct));
484 return -1;
486 ND_TCHECK2(*p, noct);
487 for (; noct-- > 0; len--, hdr++)
488 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
490 if (len < elem->asnlen) {
491 ND_PRINT((ndo, "[len%d<asnlen%u]", len, elem->asnlen));
492 return -1;
494 if (form >= sizeof(Form)/sizeof(Form[0])) {
495 ND_PRINT((ndo, "[form?%d]", form));
496 return -1;
498 if (class >= sizeof(Class)/sizeof(Class[0])) {
499 ND_PRINT((ndo, "[class?%c/%d]", *Form[form], class));
500 return -1;
502 if ((int)id >= Class[class].numIDs) {
503 ND_PRINT((ndo, "[id?%c/%s/%d]", *Form[form], Class[class].name, id));
504 return -1;
507 switch (form) {
508 case PRIMITIVE:
509 switch (class) {
510 case UNIVERSAL:
511 switch (id) {
512 case STRING:
513 elem->type = BE_STR;
514 elem->data.str = p;
515 break;
517 case INTEGER: {
518 register int32_t data;
519 elem->type = BE_INT;
520 data = 0;
522 ND_TCHECK2(*p, elem->asnlen);
523 if (*p & ASN_BIT8) /* negative */
524 data = -1;
525 for (i = elem->asnlen; i-- > 0; p++)
526 data = (data << ASN_SHIFT8) | *p;
527 elem->data.integer = data;
528 break;
531 case OBJECTID:
532 elem->type = BE_OID;
533 elem->data.raw = (caddr_t)p;
534 break;
536 case ASN_NULL:
537 elem->type = BE_NULL;
538 elem->data.raw = NULL;
539 break;
541 default:
542 elem->type = BE_OCTET;
543 elem->data.raw = (caddr_t)p;
544 ND_PRINT((ndo, "[P/U/%s]", Class[class].Id[id]));
545 break;
547 break;
549 case APPLICATION:
550 switch (id) {
551 case IPADDR:
552 elem->type = BE_INETADDR;
553 elem->data.raw = (caddr_t)p;
554 break;
556 case COUNTER:
557 case GAUGE:
558 case TIMETICKS: {
559 register uint32_t data;
560 ND_TCHECK2(*p, elem->asnlen);
561 elem->type = BE_UNS;
562 data = 0;
563 for (i = elem->asnlen; i-- > 0; p++)
564 data = (data << 8) + *p;
565 elem->data.uns = data;
566 break;
569 case COUNTER64: {
570 register uint32_t high, low;
571 ND_TCHECK2(*p, elem->asnlen);
572 elem->type = BE_UNS64;
573 high = 0, low = 0;
574 for (i = elem->asnlen; i-- > 0; p++) {
575 high = (high << 8) |
576 ((low & 0xFF000000) >> 24);
577 low = (low << 8) | *p;
579 elem->data.uns64.high = high;
580 elem->data.uns64.low = low;
581 break;
584 default:
585 elem->type = BE_OCTET;
586 elem->data.raw = (caddr_t)p;
587 ND_PRINT((ndo, "[P/A/%s]",
588 Class[class].Id[id]));
589 break;
591 break;
593 case CONTEXT:
594 switch (id) {
595 case NOSUCHOBJECT:
596 elem->type = BE_NOSUCHOBJECT;
597 elem->data.raw = NULL;
598 break;
600 case NOSUCHINSTANCE:
601 elem->type = BE_NOSUCHINST;
602 elem->data.raw = NULL;
603 break;
605 case ENDOFMIBVIEW:
606 elem->type = BE_ENDOFMIBVIEW;
607 elem->data.raw = NULL;
608 break;
610 break;
612 default:
613 ND_PRINT((ndo, "[P/%s/%s]", Class[class].name, Class[class].Id[id]));
614 ND_TCHECK2(*p, elem->asnlen);
615 elem->type = BE_OCTET;
616 elem->data.raw = (caddr_t)p;
617 break;
619 break;
621 case CONSTRUCTED:
622 switch (class) {
623 case UNIVERSAL:
624 switch (id) {
625 case SEQUENCE:
626 elem->type = BE_SEQ;
627 elem->data.raw = (caddr_t)p;
628 break;
630 default:
631 elem->type = BE_OCTET;
632 elem->data.raw = (caddr_t)p;
633 ND_PRINT((ndo, "C/U/%s", Class[class].Id[id]));
634 break;
636 break;
638 case CONTEXT:
639 elem->type = BE_PDU;
640 elem->data.raw = (caddr_t)p;
641 break;
643 default:
644 elem->type = BE_OCTET;
645 elem->data.raw = (caddr_t)p;
646 ND_PRINT((ndo, "C/%s/%s", Class[class].name, Class[class].Id[id]));
647 break;
649 break;
651 p += elem->asnlen;
652 len -= elem->asnlen;
653 return elem->asnlen + hdr;
655 trunc:
656 ND_PRINT((ndo, "%s", tstr));
657 return -1;
661 * Display the ASN.1 object represented by the BE object.
662 * This used to be an integral part of asn1_parse() before the intermediate
663 * BE form was added.
665 static int
666 asn1_print(netdissect_options *ndo,
667 struct be *elem)
669 u_char *p = (u_char *)elem->data.raw;
670 uint32_t asnlen = elem->asnlen;
671 uint32_t i;
673 switch (elem->type) {
675 case BE_OCTET:
676 ND_TCHECK2(*p, asnlen);
677 for (i = asnlen; i-- > 0; p++)
678 ND_PRINT((ndo, "_%.2x", *p));
679 break;
681 case BE_NULL:
682 break;
684 case BE_OID: {
685 int o = 0, first = -1, i = asnlen;
687 if (!ndo->ndo_sflag && !ndo->ndo_nflag && asnlen > 2) {
688 const struct obj_abrev *a = &obj_abrev_list[0];
689 size_t a_len = strlen(a->oid);
690 for (; a->node; a++) {
691 ND_TCHECK2(*p, a_len);
692 if (memcmp(a->oid, (char *)p, a_len) == 0) {
693 objp = a->node->child;
694 i -= strlen(a->oid);
695 p += strlen(a->oid);
696 ND_PRINT((ndo, "%s", a->prefix));
697 first = 1;
698 break;
703 for (; !ndo->ndo_sflag && i-- > 0; p++) {
704 ND_TCHECK(*p);
705 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
706 if (*p & ASN_LONGLEN)
707 continue;
710 * first subitem encodes two items with 1st*OIDMUX+2nd
711 * (see X.690:1997 clause 8.19 for the details)
713 if (first < 0) {
714 int s;
715 if (!ndo->ndo_nflag)
716 objp = mibroot;
717 first = 0;
718 s = o / OIDMUX;
719 if (s > 2) s = 2;
720 OBJ_PRINT(s, first);
721 o -= s * OIDMUX;
723 OBJ_PRINT(o, first);
724 if (--first < 0)
725 first = 0;
726 o = 0;
728 break;
731 case BE_INT:
732 ND_PRINT((ndo, "%d", elem->data.integer));
733 break;
735 case BE_UNS:
736 ND_PRINT((ndo, "%u", elem->data.uns));
737 break;
739 case BE_UNS64: { /* idea borrowed from by Marshall Rose */
740 double d;
741 int j, carry;
742 char *cpf, *cpl, last[6], first[30];
743 if (elem->data.uns64.high == 0) {
744 ND_PRINT((ndo, "%u", elem->data.uns64.low));
745 break;
747 d = elem->data.uns64.high * 4294967296.0; /* 2^32 */
748 if (elem->data.uns64.high <= 0x1fffff) {
749 d += elem->data.uns64.low;
750 #if 0 /*is looks illegal, but what is the intention?*/
751 ND_PRINT((ndo, "%.f", d));
752 #else
753 ND_PRINT((ndo, "%f", d));
754 #endif
755 break;
757 d += (elem->data.uns64.low & 0xfffff000);
758 #if 0 /*is looks illegal, but what is the intention?*/
759 snprintf(first, sizeof(first), "%.f", d);
760 #else
761 snprintf(first, sizeof(first), "%f", d);
762 #endif
763 snprintf(last, sizeof(last), "%5.5d",
764 elem->data.uns64.low & 0xfff);
765 for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
766 cpl >= last;
767 cpf--, cpl--) {
768 j = carry + (*cpf - '0') + (*cpl - '0');
769 if (j > 9) {
770 j -= 10;
771 carry = 1;
772 } else {
773 carry = 0;
775 *cpf = j + '0';
777 ND_PRINT((ndo, "%s", first));
778 break;
781 case BE_STR: {
782 register int printable = 1, first = 1;
783 const u_char *p = elem->data.str;
784 ND_TCHECK2(*p, asnlen);
785 for (i = asnlen; printable && i-- > 0; p++)
786 printable = ND_ISPRINT(*p);
787 p = elem->data.str;
788 if (printable) {
789 ND_PRINT((ndo, "\""));
790 if (fn_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
791 ND_PRINT((ndo, "\""));
792 goto trunc;
794 ND_PRINT((ndo, "\""));
795 } else
796 for (i = asnlen; i-- > 0; p++) {
797 ND_PRINT((ndo, first ? "%.2x" : "_%.2x", *p));
798 first = 0;
800 break;
803 case BE_SEQ:
804 ND_PRINT((ndo, "Seq(%u)", elem->asnlen));
805 break;
807 case BE_INETADDR:
808 if (asnlen != ASNLEN_INETADDR)
809 ND_PRINT((ndo, "[inetaddr len!=%d]", ASNLEN_INETADDR));
810 ND_TCHECK2(*p, asnlen);
811 for (i = asnlen; i-- != 0; p++) {
812 ND_PRINT((ndo, (i == asnlen-1) ? "%u" : ".%u", *p));
814 break;
816 case BE_NOSUCHOBJECT:
817 case BE_NOSUCHINST:
818 case BE_ENDOFMIBVIEW:
819 ND_PRINT((ndo, "[%s]", Class[EXCEPTIONS].Id[elem->id]));
820 break;
822 case BE_PDU:
823 ND_PRINT((ndo, "%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen));
824 break;
826 case BE_ANY:
827 ND_PRINT((ndo, "[BE_ANY!?]"));
828 break;
830 default:
831 ND_PRINT((ndo, "[be!?]"));
832 break;
834 return 0;
836 trunc:
837 ND_PRINT((ndo, "%s", tstr));
838 return -1;
841 #ifdef notdef
843 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
844 * This will work for any ASN.1 stream, not just an SNMP PDU.
846 * By adding newlines and spaces at the correct places, this would print in
847 * Rose-Normal-Form.
849 * This is not currently used.
851 static void
852 asn1_decode(u_char *p, u_int length)
854 struct be elem;
855 int i = 0;
857 while (i >= 0 && length > 0) {
858 i = asn1_parse(ndo, p, length, &elem);
859 if (i >= 0) {
860 ND_PRINT((ndo, " "));
861 if (asn1_print(ndo, &elem) < 0)
862 return;
863 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
864 ND_PRINT((ndo, " {"));
865 asn1_decode(elem.data.raw, elem.asnlen);
866 ND_PRINT((ndo, " }"));
868 length -= i;
869 p += i;
873 #endif
875 #ifdef USE_LIBSMI
877 struct smi2be {
878 SmiBasetype basetype;
879 int be;
882 static const struct smi2be smi2betab[] = {
883 { SMI_BASETYPE_INTEGER32, BE_INT },
884 { SMI_BASETYPE_OCTETSTRING, BE_STR },
885 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
886 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
887 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
888 { SMI_BASETYPE_INTEGER64, BE_NONE },
889 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
890 { SMI_BASETYPE_FLOAT32, BE_NONE },
891 { SMI_BASETYPE_FLOAT64, BE_NONE },
892 { SMI_BASETYPE_FLOAT128, BE_NONE },
893 { SMI_BASETYPE_ENUM, BE_INT },
894 { SMI_BASETYPE_BITS, BE_STR },
895 { SMI_BASETYPE_UNKNOWN, BE_NONE }
898 static int
899 smi_decode_oid(netdissect_options *ndo,
900 struct be *elem, unsigned int *oid,
901 unsigned int oidsize, unsigned int *oidlen)
903 u_char *p = (u_char *)elem->data.raw;
904 uint32_t asnlen = elem->asnlen;
905 int o = 0, first = -1, i = asnlen;
906 unsigned int firstval;
908 for (*oidlen = 0; ndo->ndo_sflag && i-- > 0; p++) {
909 ND_TCHECK(*p);
910 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
911 if (*p & ASN_LONGLEN)
912 continue;
915 * first subitem encodes two items with 1st*OIDMUX+2nd
916 * (see X.690:1997 clause 8.19 for the details)
918 if (first < 0) {
919 first = 0;
920 firstval = o / OIDMUX;
921 if (firstval > 2) firstval = 2;
922 o -= firstval * OIDMUX;
923 if (*oidlen < oidsize) {
924 oid[(*oidlen)++] = firstval;
927 if (*oidlen < oidsize) {
928 oid[(*oidlen)++] = o;
930 o = 0;
932 return 0;
934 trunc:
935 ND_PRINT((ndo, "%s", tstr));
936 return -1;
939 static int smi_check_type(SmiBasetype basetype, int be)
941 int i;
943 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
944 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
945 return 1;
949 return 0;
952 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
953 struct be *elem)
955 int ok = 1;
957 switch (smiType->basetype) {
958 case SMI_BASETYPE_OBJECTIDENTIFIER:
959 case SMI_BASETYPE_OCTETSTRING:
960 if (smiRange->minValue.value.unsigned32
961 == smiRange->maxValue.value.unsigned32) {
962 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
963 } else {
964 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
965 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
967 break;
969 case SMI_BASETYPE_INTEGER32:
970 ok = (elem->data.integer >= smiRange->minValue.value.integer32
971 && elem->data.integer <= smiRange->maxValue.value.integer32);
972 break;
974 case SMI_BASETYPE_UNSIGNED32:
975 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
976 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
977 break;
979 case SMI_BASETYPE_UNSIGNED64:
980 /* XXX */
981 break;
983 /* case SMI_BASETYPE_INTEGER64: SMIng */
984 /* case SMI_BASETYPE_FLOAT32: SMIng */
985 /* case SMI_BASETYPE_FLOAT64: SMIng */
986 /* case SMI_BASETYPE_FLOAT128: SMIng */
988 case SMI_BASETYPE_ENUM:
989 case SMI_BASETYPE_BITS:
990 case SMI_BASETYPE_UNKNOWN:
991 ok = 1;
992 break;
994 default:
995 ok = 0;
996 break;
999 return ok;
1002 static int smi_check_range(SmiType *smiType, struct be *elem)
1004 SmiRange *smiRange;
1005 int ok = 1;
1007 for (smiRange = smiGetFirstRange(smiType);
1008 smiRange;
1009 smiRange = smiGetNextRange(smiRange)) {
1011 ok = smi_check_a_range(smiType, smiRange, elem);
1013 if (ok) {
1014 break;
1018 if (ok) {
1019 SmiType *parentType;
1020 parentType = smiGetParentType(smiType);
1021 if (parentType) {
1022 ok = smi_check_range(parentType, elem);
1026 return ok;
1029 static SmiNode *
1030 smi_print_variable(netdissect_options *ndo,
1031 struct be *elem, int *status)
1033 unsigned int oid[128], oidlen;
1034 SmiNode *smiNode = NULL;
1035 unsigned int i;
1037 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1038 &oidlen);
1039 if (*status < 0)
1040 return NULL;
1041 smiNode = smiGetNodeByOID(oidlen, oid);
1042 if (! smiNode) {
1043 *status = asn1_print(ndo, elem);
1044 return NULL;
1046 if (ndo->ndo_vflag) {
1047 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1049 ND_PRINT((ndo, "%s", smiNode->name));
1050 if (smiNode->oidlen < oidlen) {
1051 for (i = smiNode->oidlen; i < oidlen; i++) {
1052 ND_PRINT((ndo, ".%u", oid[i]));
1055 *status = 0;
1056 return smiNode;
1059 static int
1060 smi_print_value(netdissect_options *ndo,
1061 SmiNode *smiNode, u_char pduid, struct be *elem)
1063 unsigned int i, oid[128], oidlen;
1064 SmiType *smiType;
1065 SmiNamedNumber *nn;
1066 int done = 0;
1068 if (! smiNode || ! (smiNode->nodekind
1069 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1070 return asn1_print(ndo, elem);
1073 if (elem->type == BE_NOSUCHOBJECT
1074 || elem->type == BE_NOSUCHINST
1075 || elem->type == BE_ENDOFMIBVIEW) {
1076 return asn1_print(ndo, elem);
1079 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1080 ND_PRINT((ndo, "[notNotifyable]"));
1083 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1084 ND_PRINT((ndo, "[notReadable]"));
1087 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1088 ND_PRINT((ndo, "[notWritable]"));
1091 if (RESPONSE_CLASS(pduid)
1092 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1093 ND_PRINT((ndo, "[noAccess]"));
1096 smiType = smiGetNodeType(smiNode);
1097 if (! smiType) {
1098 return asn1_print(ndo, elem);
1101 if (! smi_check_type(smiType->basetype, elem->type)) {
1102 ND_PRINT((ndo, "[wrongType]"));
1105 if (! smi_check_range(smiType, elem)) {
1106 ND_PRINT((ndo, "[outOfRange]"));
1109 /* resolve bits to named bits */
1111 /* check whether instance identifier is valid */
1113 /* apply display hints (integer, octetstring) */
1115 /* convert instance identifier to index type values */
1117 switch (elem->type) {
1118 case BE_OID:
1119 if (smiType->basetype == SMI_BASETYPE_BITS) {
1120 /* print bit labels */
1121 } else {
1122 smi_decode_oid(ndo, elem, oid,
1123 sizeof(oid)/sizeof(unsigned int),
1124 &oidlen);
1125 smiNode = smiGetNodeByOID(oidlen, oid);
1126 if (smiNode) {
1127 if (ndo->ndo_vflag) {
1128 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1130 ND_PRINT((ndo, "%s", smiNode->name));
1131 if (smiNode->oidlen < oidlen) {
1132 for (i = smiNode->oidlen;
1133 i < oidlen; i++) {
1134 ND_PRINT((ndo, ".%u", oid[i]));
1137 done++;
1140 break;
1142 case BE_INT:
1143 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1144 for (nn = smiGetFirstNamedNumber(smiType);
1146 nn = smiGetNextNamedNumber(nn)) {
1147 if (nn->value.value.integer32
1148 == elem->data.integer) {
1149 ND_PRINT((ndo, "%s", nn->name));
1150 ND_PRINT((ndo, "(%d)", elem->data.integer));
1151 done++;
1152 break;
1156 break;
1159 if (! done) {
1160 return asn1_print(ndo, elem);
1162 return 0;
1164 #endif
1167 * General SNMP header
1168 * SEQUENCE {
1169 * version INTEGER {version-1(0)},
1170 * community OCTET STRING,
1171 * data ANY -- PDUs
1173 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1174 * SEQUENCE {
1175 * request-id INTEGER,
1176 * error-status INTEGER,
1177 * error-index INTEGER,
1178 * varbindlist SEQUENCE OF
1179 * SEQUENCE {
1180 * name ObjectName,
1181 * value ObjectValue
1184 * PDU for Trap:
1185 * SEQUENCE {
1186 * enterprise OBJECT IDENTIFIER,
1187 * agent-addr NetworkAddress,
1188 * generic-trap INTEGER,
1189 * specific-trap INTEGER,
1190 * time-stamp TimeTicks,
1191 * varbindlist SEQUENCE OF
1192 * SEQUENCE {
1193 * name ObjectName,
1194 * value ObjectValue
1200 * Decode SNMP varBind
1202 static void
1203 varbind_print(netdissect_options *ndo,
1204 u_char pduid, const u_char *np, u_int length)
1206 struct be elem;
1207 int count = 0, ind;
1208 #ifdef USE_LIBSMI
1209 SmiNode *smiNode = NULL;
1210 #endif
1211 int status;
1213 /* Sequence of varBind */
1214 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1215 return;
1216 if (elem.type != BE_SEQ) {
1217 ND_PRINT((ndo, "[!SEQ of varbind]"));
1218 asn1_print(ndo, &elem);
1219 return;
1221 if ((u_int)count < length)
1222 ND_PRINT((ndo, "[%d extra after SEQ of varbind]", length - count));
1223 /* descend */
1224 length = elem.asnlen;
1225 np = (u_char *)elem.data.raw;
1227 for (ind = 1; length > 0; ind++) {
1228 const u_char *vbend;
1229 u_int vblength;
1231 ND_PRINT((ndo, " "));
1233 /* Sequence */
1234 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1235 return;
1236 if (elem.type != BE_SEQ) {
1237 ND_PRINT((ndo, "[!varbind]"));
1238 asn1_print(ndo, &elem);
1239 return;
1241 vbend = np + count;
1242 vblength = length - count;
1243 /* descend */
1244 length = elem.asnlen;
1245 np = (u_char *)elem.data.raw;
1247 /* objName (OID) */
1248 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1249 return;
1250 if (elem.type != BE_OID) {
1251 ND_PRINT((ndo, "[objName!=OID]"));
1252 asn1_print(ndo, &elem);
1253 return;
1255 #ifdef USE_LIBSMI
1256 smiNode = smi_print_variable(ndo, &elem, &status);
1257 #else
1258 status = asn1_print(ndo, &elem);
1259 #endif
1260 if (status < 0)
1261 return;
1262 length -= count;
1263 np += count;
1265 if (pduid != GETREQ && pduid != GETNEXTREQ
1266 && pduid != GETBULKREQ)
1267 ND_PRINT((ndo, "="));
1269 /* objVal (ANY) */
1270 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1271 return;
1272 if (pduid == GETREQ || pduid == GETNEXTREQ
1273 || pduid == GETBULKREQ) {
1274 if (elem.type != BE_NULL) {
1275 ND_PRINT((ndo, "[objVal!=NULL]"));
1276 if (asn1_print(ndo, &elem) < 0)
1277 return;
1279 } else {
1280 if (elem.type != BE_NULL) {
1281 #ifdef USE_LIBSMI
1282 status = smi_print_value(ndo, smiNode, pduid, &elem);
1283 #else
1284 status = asn1_print(ndo, &elem);
1285 #endif
1287 if (status < 0)
1288 return;
1290 length = vblength;
1291 np = vbend;
1296 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1297 * GetBulk, Inform, V2Trap, and Report
1299 static void
1300 snmppdu_print(netdissect_options *ndo,
1301 u_short pduid, const u_char *np, u_int length)
1303 struct be elem;
1304 int count = 0, error;
1306 /* reqId (Integer) */
1307 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1308 return;
1309 if (elem.type != BE_INT) {
1310 ND_PRINT((ndo, "[reqId!=INT]"));
1311 asn1_print(ndo, &elem);
1312 return;
1314 if (ndo->ndo_vflag)
1315 ND_PRINT((ndo, "R=%d ", elem.data.integer));
1316 length -= count;
1317 np += count;
1319 /* errorStatus (Integer) */
1320 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1321 return;
1322 if (elem.type != BE_INT) {
1323 ND_PRINT((ndo, "[errorStatus!=INT]"));
1324 asn1_print(ndo, &elem);
1325 return;
1327 error = 0;
1328 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1329 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1330 && elem.data.integer != 0) {
1331 char errbuf[20];
1332 ND_PRINT((ndo, "[errorStatus(%s)!=0]",
1333 DECODE_ErrorStatus(elem.data.integer)));
1334 } else if (pduid == GETBULKREQ) {
1335 ND_PRINT((ndo, " N=%d", elem.data.integer));
1336 } else if (elem.data.integer != 0) {
1337 char errbuf[20];
1338 ND_PRINT((ndo, " %s", DECODE_ErrorStatus(elem.data.integer)));
1339 error = elem.data.integer;
1341 length -= count;
1342 np += count;
1344 /* errorIndex (Integer) */
1345 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1346 return;
1347 if (elem.type != BE_INT) {
1348 ND_PRINT((ndo, "[errorIndex!=INT]"));
1349 asn1_print(ndo, &elem);
1350 return;
1352 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1353 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1354 && elem.data.integer != 0)
1355 ND_PRINT((ndo, "[errorIndex(%d)!=0]", elem.data.integer));
1356 else if (pduid == GETBULKREQ)
1357 ND_PRINT((ndo, " M=%d", elem.data.integer));
1358 else if (elem.data.integer != 0) {
1359 if (!error)
1360 ND_PRINT((ndo, "[errorIndex(%d) w/o errorStatus]", elem.data.integer));
1361 else {
1362 ND_PRINT((ndo, "@%d", elem.data.integer));
1363 error = elem.data.integer;
1365 } else if (error) {
1366 ND_PRINT((ndo, "[errorIndex==0]"));
1367 error = 0;
1369 length -= count;
1370 np += count;
1372 varbind_print(ndo, pduid, np, length);
1373 return;
1377 * Decode SNMP Trap PDU
1379 static void
1380 trappdu_print(netdissect_options *ndo,
1381 const u_char *np, u_int length)
1383 struct be elem;
1384 int count = 0, generic;
1386 ND_PRINT((ndo, " "));
1388 /* enterprise (oid) */
1389 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1390 return;
1391 if (elem.type != BE_OID) {
1392 ND_PRINT((ndo, "[enterprise!=OID]"));
1393 asn1_print(ndo, &elem);
1394 return;
1396 if (asn1_print(ndo, &elem) < 0)
1397 return;
1398 length -= count;
1399 np += count;
1401 ND_PRINT((ndo, " "));
1403 /* agent-addr (inetaddr) */
1404 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1405 return;
1406 if (elem.type != BE_INETADDR) {
1407 ND_PRINT((ndo, "[agent-addr!=INETADDR]"));
1408 asn1_print(ndo, &elem);
1409 return;
1411 if (asn1_print(ndo, &elem) < 0)
1412 return;
1413 length -= count;
1414 np += count;
1416 /* generic-trap (Integer) */
1417 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1418 return;
1419 if (elem.type != BE_INT) {
1420 ND_PRINT((ndo, "[generic-trap!=INT]"));
1421 asn1_print(ndo, &elem);
1422 return;
1424 generic = elem.data.integer;
1426 char buf[20];
1427 ND_PRINT((ndo, " %s", DECODE_GenericTrap(generic)));
1429 length -= count;
1430 np += count;
1432 /* specific-trap (Integer) */
1433 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1434 return;
1435 if (elem.type != BE_INT) {
1436 ND_PRINT((ndo, "[specific-trap!=INT]"));
1437 asn1_print(ndo, &elem);
1438 return;
1440 if (generic != GT_ENTERPRISE) {
1441 if (elem.data.integer != 0)
1442 ND_PRINT((ndo, "[specific-trap(%d)!=0]", elem.data.integer));
1443 } else
1444 ND_PRINT((ndo, " s=%d", elem.data.integer));
1445 length -= count;
1446 np += count;
1448 ND_PRINT((ndo, " "));
1450 /* time-stamp (TimeTicks) */
1451 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1452 return;
1453 if (elem.type != BE_UNS) { /* XXX */
1454 ND_PRINT((ndo, "[time-stamp!=TIMETICKS]"));
1455 asn1_print(ndo, &elem);
1456 return;
1458 if (asn1_print(ndo, &elem) < 0)
1459 return;
1460 length -= count;
1461 np += count;
1463 varbind_print(ndo, TRAP, np, length);
1464 return;
1468 * Decode arbitrary SNMP PDUs.
1470 static void
1471 pdu_print(netdissect_options *ndo,
1472 const u_char *np, u_int length, int version)
1474 struct be pdu;
1475 int count = 0;
1477 /* PDU (Context) */
1478 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1479 return;
1480 if (pdu.type != BE_PDU) {
1481 ND_PRINT((ndo, "[no PDU]"));
1482 return;
1484 if ((u_int)count < length)
1485 ND_PRINT((ndo, "[%d extra after PDU]", length - count));
1486 if (ndo->ndo_vflag) {
1487 ND_PRINT((ndo, "{ "));
1489 if (asn1_print(ndo, &pdu) < 0)
1490 return;
1491 ND_PRINT((ndo, " "));
1492 /* descend into PDU */
1493 length = pdu.asnlen;
1494 np = (u_char *)pdu.data.raw;
1496 if (version == SNMP_VERSION_1 &&
1497 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1498 pdu.id == V2TRAP || pdu.id == REPORT)) {
1499 ND_PRINT((ndo, "[v2 PDU in v1 message]"));
1500 return;
1503 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1504 ND_PRINT((ndo, "[v1 PDU in v2 message]"));
1505 return;
1508 switch (pdu.id) {
1509 case TRAP:
1510 trappdu_print(ndo, np, length);
1511 break;
1512 case GETREQ:
1513 case GETNEXTREQ:
1514 case GETRESP:
1515 case SETREQ:
1516 case GETBULKREQ:
1517 case INFORMREQ:
1518 case V2TRAP:
1519 case REPORT:
1520 snmppdu_print(ndo, pdu.id, np, length);
1521 break;
1524 if (ndo->ndo_vflag) {
1525 ND_PRINT((ndo, " } "));
1530 * Decode a scoped SNMP PDU.
1532 static void
1533 scopedpdu_print(netdissect_options *ndo,
1534 const u_char *np, u_int length, int version)
1536 struct be elem;
1537 int i, count = 0;
1539 /* Sequence */
1540 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1541 return;
1542 if (elem.type != BE_SEQ) {
1543 ND_PRINT((ndo, "[!scoped PDU]"));
1544 asn1_print(ndo, &elem);
1545 return;
1547 length = elem.asnlen;
1548 np = (u_char *)elem.data.raw;
1550 /* contextEngineID (OCTET STRING) */
1551 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1552 return;
1553 if (elem.type != BE_STR) {
1554 ND_PRINT((ndo, "[contextEngineID!=STR]"));
1555 asn1_print(ndo, &elem);
1556 return;
1558 length -= count;
1559 np += count;
1561 ND_PRINT((ndo, "E= "));
1562 for (i = 0; i < (int)elem.asnlen; i++) {
1563 ND_PRINT((ndo, "0x%02X", elem.data.str[i]));
1565 ND_PRINT((ndo, " "));
1567 /* contextName (OCTET STRING) */
1568 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1569 return;
1570 if (elem.type != BE_STR) {
1571 ND_PRINT((ndo, "[contextName!=STR]"));
1572 asn1_print(ndo, &elem);
1573 return;
1575 length -= count;
1576 np += count;
1578 ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str));
1580 pdu_print(ndo, np, length, version);
1584 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1586 static void
1587 community_print(netdissect_options *ndo,
1588 const u_char *np, u_int length, int version)
1590 struct be elem;
1591 int count = 0;
1593 /* Community (String) */
1594 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1595 return;
1596 if (elem.type != BE_STR) {
1597 ND_PRINT((ndo, "[comm!=STR]"));
1598 asn1_print(ndo, &elem);
1599 return;
1601 /* default community */
1602 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1603 strncmp((char *)elem.data.str, DEF_COMMUNITY,
1604 sizeof(DEF_COMMUNITY) - 1) == 0))
1605 /* ! "public" */
1606 ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str));
1607 length -= count;
1608 np += count;
1610 pdu_print(ndo, np, length, version);
1614 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1616 static void
1617 usm_print(netdissect_options *ndo,
1618 const u_char *np, u_int length)
1620 struct be elem;
1621 int count = 0;
1623 /* Sequence */
1624 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1625 return;
1626 if (elem.type != BE_SEQ) {
1627 ND_PRINT((ndo, "[!usm]"));
1628 asn1_print(ndo, &elem);
1629 return;
1631 length = elem.asnlen;
1632 np = (u_char *)elem.data.raw;
1634 /* msgAuthoritativeEngineID (OCTET STRING) */
1635 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1636 return;
1637 if (elem.type != BE_STR) {
1638 ND_PRINT((ndo, "[msgAuthoritativeEngineID!=STR]"));
1639 asn1_print(ndo, &elem);
1640 return;
1642 length -= count;
1643 np += count;
1645 /* msgAuthoritativeEngineBoots (INTEGER) */
1646 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1647 return;
1648 if (elem.type != BE_INT) {
1649 ND_PRINT((ndo, "[msgAuthoritativeEngineBoots!=INT]"));
1650 asn1_print(ndo, &elem);
1651 return;
1653 if (ndo->ndo_vflag)
1654 ND_PRINT((ndo, "B=%d ", elem.data.integer));
1655 length -= count;
1656 np += count;
1658 /* msgAuthoritativeEngineTime (INTEGER) */
1659 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1660 return;
1661 if (elem.type != BE_INT) {
1662 ND_PRINT((ndo, "[msgAuthoritativeEngineTime!=INT]"));
1663 asn1_print(ndo, &elem);
1664 return;
1666 if (ndo->ndo_vflag)
1667 ND_PRINT((ndo, "T=%d ", elem.data.integer));
1668 length -= count;
1669 np += count;
1671 /* msgUserName (OCTET STRING) */
1672 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1673 return;
1674 if (elem.type != BE_STR) {
1675 ND_PRINT((ndo, "[msgUserName!=STR]"));
1676 asn1_print(ndo, &elem);
1677 return;
1679 length -= count;
1680 np += count;
1682 ND_PRINT((ndo, "U=%.*s ", (int)elem.asnlen, elem.data.str));
1684 /* msgAuthenticationParameters (OCTET STRING) */
1685 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1686 return;
1687 if (elem.type != BE_STR) {
1688 ND_PRINT((ndo, "[msgAuthenticationParameters!=STR]"));
1689 asn1_print(ndo, &elem);
1690 return;
1692 length -= count;
1693 np += count;
1695 /* msgPrivacyParameters (OCTET STRING) */
1696 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1697 return;
1698 if (elem.type != BE_STR) {
1699 ND_PRINT((ndo, "[msgPrivacyParameters!=STR]"));
1700 asn1_print(ndo, &elem);
1701 return;
1703 length -= count;
1704 np += count;
1706 if ((u_int)count < length)
1707 ND_PRINT((ndo, "[%d extra after usm SEQ]", length - count));
1711 * Decode SNMPv3 Message Header (SNMPv3)
1713 static void
1714 v3msg_print(netdissect_options *ndo,
1715 const u_char *np, u_int length)
1717 struct be elem;
1718 int count = 0;
1719 u_char flags;
1720 int model;
1721 const u_char *xnp = np;
1722 int xlength = length;
1724 /* Sequence */
1725 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1726 return;
1727 if (elem.type != BE_SEQ) {
1728 ND_PRINT((ndo, "[!message]"));
1729 asn1_print(ndo, &elem);
1730 return;
1732 length = elem.asnlen;
1733 np = (u_char *)elem.data.raw;
1735 if (ndo->ndo_vflag) {
1736 ND_PRINT((ndo, "{ "));
1739 /* msgID (INTEGER) */
1740 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1741 return;
1742 if (elem.type != BE_INT) {
1743 ND_PRINT((ndo, "[msgID!=INT]"));
1744 asn1_print(ndo, &elem);
1745 return;
1747 length -= count;
1748 np += count;
1750 /* msgMaxSize (INTEGER) */
1751 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1752 return;
1753 if (elem.type != BE_INT) {
1754 ND_PRINT((ndo, "[msgMaxSize!=INT]"));
1755 asn1_print(ndo, &elem);
1756 return;
1758 length -= count;
1759 np += count;
1761 /* msgFlags (OCTET STRING) */
1762 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1763 return;
1764 if (elem.type != BE_STR) {
1765 ND_PRINT((ndo, "[msgFlags!=STR]"));
1766 asn1_print(ndo, &elem);
1767 return;
1769 if (elem.asnlen != 1) {
1770 ND_PRINT((ndo, "[msgFlags size %d]", elem.asnlen));
1771 return;
1773 flags = elem.data.str[0];
1774 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1775 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1776 ND_PRINT((ndo, "[msgFlags=0x%02X]", flags));
1777 return;
1779 length -= count;
1780 np += count;
1782 ND_PRINT((ndo, "F=%s%s%s ",
1783 flags & 0x01 ? "a" : "",
1784 flags & 0x02 ? "p" : "",
1785 flags & 0x04 ? "r" : ""));
1787 /* msgSecurityModel (INTEGER) */
1788 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1789 return;
1790 if (elem.type != BE_INT) {
1791 ND_PRINT((ndo, "[msgSecurityModel!=INT]"));
1792 asn1_print(ndo, &elem);
1793 return;
1795 model = elem.data.integer;
1796 length -= count;
1797 np += count;
1799 if ((u_int)count < length)
1800 ND_PRINT((ndo, "[%d extra after message SEQ]", length - count));
1802 if (ndo->ndo_vflag) {
1803 ND_PRINT((ndo, "} "));
1806 if (model == 3) {
1807 if (ndo->ndo_vflag) {
1808 ND_PRINT((ndo, "{ USM "));
1810 } else {
1811 ND_PRINT((ndo, "[security model %d]", model));
1812 return;
1815 np = xnp + (np - xnp);
1816 length = xlength - (np - xnp);
1818 /* msgSecurityParameters (OCTET STRING) */
1819 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1820 return;
1821 if (elem.type != BE_STR) {
1822 ND_PRINT((ndo, "[msgSecurityParameters!=STR]"));
1823 asn1_print(ndo, &elem);
1824 return;
1826 length -= count;
1827 np += count;
1829 if (model == 3) {
1830 usm_print(ndo, elem.data.str, elem.asnlen);
1831 if (ndo->ndo_vflag) {
1832 ND_PRINT((ndo, "} "));
1836 if (ndo->ndo_vflag) {
1837 ND_PRINT((ndo, "{ ScopedPDU "));
1840 scopedpdu_print(ndo, np, length, 3);
1842 if (ndo->ndo_vflag) {
1843 ND_PRINT((ndo, "} "));
1848 * Decode SNMP header and pass on to PDU printing routines
1850 void
1851 snmp_print(netdissect_options *ndo,
1852 const u_char *np, u_int length)
1854 struct be elem;
1855 int count = 0;
1856 int version = 0;
1858 ND_PRINT((ndo, " "));
1860 /* initial Sequence */
1861 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1862 return;
1863 if (elem.type != BE_SEQ) {
1864 ND_PRINT((ndo, "[!init SEQ]"));
1865 asn1_print(ndo, &elem);
1866 return;
1868 if ((u_int)count < length)
1869 ND_PRINT((ndo, "[%d extra after iSEQ]", length - count));
1870 /* descend */
1871 length = elem.asnlen;
1872 np = (u_char *)elem.data.raw;
1874 /* Version (INTEGER) */
1875 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1876 return;
1877 if (elem.type != BE_INT) {
1878 ND_PRINT((ndo, "[version!=INT]"));
1879 asn1_print(ndo, &elem);
1880 return;
1883 switch (elem.data.integer) {
1884 case SNMP_VERSION_1:
1885 case SNMP_VERSION_2:
1886 case SNMP_VERSION_3:
1887 if (ndo->ndo_vflag)
1888 ND_PRINT((ndo, "{ %s ", SnmpVersion[elem.data.integer]));
1889 break;
1890 default:
1891 ND_PRINT((ndo, "[version = %d]", elem.data.integer));
1892 return;
1894 version = elem.data.integer;
1895 length -= count;
1896 np += count;
1898 switch (version) {
1899 case SNMP_VERSION_1:
1900 case SNMP_VERSION_2:
1901 community_print(ndo, np, length, version);
1902 break;
1903 case SNMP_VERSION_3:
1904 v3msg_print(ndo, np, length);
1905 break;
1906 default:
1907 ND_PRINT((ndo, "[version = %d]", elem.data.integer));
1908 break;
1911 if (ndo->ndo_vflag) {
1912 ND_PRINT((ndo, "} "));