]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
Move the printer summaries from INSTALL.txt to each printer
[tcpdump] / print-snmp.c
1 /*
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3 * John Robert LoVerso. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 *
28 * This implementation has been influenced by the CMU SNMP release,
29 * by Steve Waldbusser. However, this shares no code with that system.
30 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
31 * Earlier forms of this implementation were derived and/or inspired by an
32 * awk script originally written by C. Philip Wood of LANL (but later
33 * heavily modified by John Robert LoVerso). The copyright notice for
34 * that work is preserved below, even though it may not rightly apply
35 * to this file.
36 *
37 * Support for SNMPv2c/SNMPv3 and the ability to link the module against
38 * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
39 *
40 * This started out as a very simple program, but the incremental decoding
41 * (into the BE structure) complicated things.
42 *
43 # Los Alamos National Laboratory
44 #
45 # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
46 # This software was produced under a U.S. Government contract
47 # (W-7405-ENG-36) by Los Alamos National Laboratory, which is
48 # operated by the University of California for the U.S. Department
49 # of Energy. The U.S. Government is licensed to use, reproduce,
50 # and distribute this software. Permission is granted to the
51 # public to copy and use this software without charge, provided
52 # that this Notice and any statement of authorship are reproduced
53 # on all copies. Neither the Government nor the University makes
54 # any warranty, express or implied, or assumes any liability or
55 # responsibility for the use of this software.
56 # @(#)snmp.awk.x 1.1 (LANL) 1/15/90
57 */
58
59 /* \summary: Simple Network Management Protocol (SNMP) printer */
60
61 #ifdef HAVE_CONFIG_H
62 #include "config.h"
63 #endif
64
65 #include <netdissect-stdinc.h>
66
67 #include <stdio.h>
68 #include <string.h>
69
70 #ifdef USE_LIBSMI
71 #include <smi.h>
72 #endif
73
74 #include "netdissect.h"
75
76 #undef OPAQUE /* defined in <wingdi.h> */
77
78 static const char tstr[] = "[|snmp]";
79
80 /*
81 * Universal ASN.1 types
82 * (we only care about the tag values for those allowed in the Internet SMI)
83 */
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"
102 };
103
104 /*
105 * Application-wide ASN.1 types from the Internet SMI and their tags
106 */
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
121 };
122
123 /*
124 * Context-specific ASN.1 types for the SNMP PDUs and their tags
125 */
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
145 };
146
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)
152
153 /*
154 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
155 */
156 static const char *Exceptions[] = {
157 "noSuchObject",
158 #define NOSUCHOBJECT 0
159 "noSuchInstance",
160 #define NOSUCHINSTANCE 1
161 "endOfMibView",
162 #define ENDOFMIBVIEW 2
163 };
164
165 /*
166 * Private ASN.1 types
167 * The Internet SMI does not specify any
168 */
169 static const char *Private[] = {
170 "P-0"
171 };
172
173 /*
174 * error-status values for any SNMP PDU
175 */
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"
196 };
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))
201
202 /*
203 * generic-trap values in the SNMP Trap-PDU
204 */
205 static const char *GenericTrap[] = {
206 "coldStart",
207 "warmStart",
208 "linkDown",
209 "linkUp",
210 "authenticationFailure",
211 "egpNeighborLoss",
212 "enterpriseSpecific"
213 #define GT_ENTERPRISE 6
214 };
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))
219
220 /*
221 * ASN.1 type class table
222 * Ties together the preceding Universal, Application, Context, and Private
223 * type definitions.
224 */
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
241 };
242
243 /*
244 * defined forms for ASN.1 types
245 */
246 static const char *Form[] = {
247 "Primitive",
248 #define PRIMITIVE 0
249 "Constructed",
250 #define CONSTRUCTED 1
251 };
252
253 /*
254 * A structure for the OID tree for the compiled-in MIB.
255 * This is stored as a general-order tree.
256 */
257 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;
263
264 /*
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'.
268 *
269 * In particular, this is gross, as this is including initialized structures,
270 * and by right shouldn't be an "include" file.
271 */
272 #include "mib.h"
273
274 /*
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.
278 */
279 static const struct obj_abrev {
280 const char *prefix; /* prefix for this abrev */
281 struct obj *node; /* pointer into object table */
282 const char *oid; /* ASN.1 encoded OID */
283 } obj_abrev_list[] = {
284 #ifndef NO_ABREV_MIB
285 /* .iso.org.dod.internet.mgmt.mib */
286 { "", &_mib_obj, "\53\6\1\2\1" },
287 #endif
288 #ifndef NO_ABREV_ENTER
289 /* .iso.org.dod.internet.private.enterprises */
290 { "E:", &_enterprises_obj, "\53\6\1\4\1" },
291 #endif
292 #ifndef NO_ABREV_EXPERI
293 /* .iso.org.dod.internet.experimental */
294 { "X:", &_experimental_obj, "\53\6\1\3" },
295 #endif
296 #ifndef NO_ABBREV_SNMPMODS
297 /* .iso.org.dod.internet.snmpV2.snmpModules */
298 { "S:", &_snmpModules_obj, "\53\6\1\6\3" },
299 #endif
300 { 0,0,0 }
301 };
302
303 /*
304 * This is used in the OID print routine to walk down the object tree
305 * rooted at `mibroot'.
306 */
307 #define OBJ_PRINT(o, suppressdot) \
308 { \
309 if (objp) { \
310 do { \
311 if ((o) == objp->oid) \
312 break; \
313 } while ((objp = objp->next) != NULL); \
314 } \
315 if (objp) { \
316 ND_PRINT((ndo, suppressdot?"%s":".%s", objp->desc)); \
317 objp = objp->child; \
318 } else \
319 ND_PRINT((ndo, suppressdot?"%u":".%u", (o))); \
320 }
321
322 /*
323 * This is the definition for the Any-Data-Type storage used purely for
324 * temporary internal representation while decoding an ASN.1 data stream.
325 */
326 struct be {
327 uint32_t asnlen;
328 union {
329 const uint8_t *raw;
330 int32_t integer;
331 uint32_t uns;
332 const u_char *str;
333 struct {
334 uint32_t high;
335 uint32_t low;
336 } uns64;
337 } data;
338 u_short id;
339 u_char form, class; /* tag info */
340 u_char type;
341 #define BE_ANY 255
342 #define BE_NONE 0
343 #define BE_NULL 1
344 #define BE_OCTET 2
345 #define BE_OID 3
346 #define BE_INT 4
347 #define BE_UNS 5
348 #define BE_STR 6
349 #define BE_SEQ 7
350 #define BE_INETADDR 8
351 #define BE_PDU 9
352 #define BE_UNS64 10
353 #define BE_NOSUCHOBJECT 128
354 #define BE_NOSUCHINST 129
355 #define BE_ENDOFMIBVIEW 130
356 };
357
358 /*
359 * SNMP versions recognized by this module
360 */
361 static const char *SnmpVersion[] = {
362 "SNMPv1",
363 #define SNMP_VERSION_1 0
364 "SNMPv2c",
365 #define SNMP_VERSION_2 1
366 "SNMPv2u",
367 #define SNMP_VERSION_2U 2
368 "SNMPv3"
369 #define SNMP_VERSION_3 3
370 };
371
372 /*
373 * Defaults for SNMP PDU components
374 */
375 #define DEF_COMMUNITY "public"
376
377 /*
378 * constants for ASN.1 decoding
379 */
380 #define OIDMUX 40
381 #define ASNLEN_INETADDR 4
382 #define ASN_SHIFT7 7
383 #define ASN_SHIFT8 8
384 #define ASN_BIT8 0x80
385 #define ASN_LONGLEN 0x80
386
387 #define ASN_ID_BITS 0x1f
388 #define ASN_FORM_BITS 0x20
389 #define ASN_FORM_SHIFT 5
390 #define ASN_CLASS_BITS 0xc0
391 #define ASN_CLASS_SHIFT 6
392
393 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
394
395 /*
396 * This decodes the next ASN.1 object in the stream pointed to by "p"
397 * (and of real-length "len") and stores the intermediate data in the
398 * provided BE object.
399 *
400 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
401 * O/w, this returns the number of bytes parsed from "p".
402 */
403 static int
404 asn1_parse(netdissect_options *ndo,
405 register const u_char *p, u_int len, struct be *elem)
406 {
407 u_char form, class, id;
408 int i, hdr;
409
410 elem->asnlen = 0;
411 elem->type = BE_ANY;
412 if (len < 1) {
413 ND_PRINT((ndo, "[nothing to parse]"));
414 return -1;
415 }
416 ND_TCHECK(*p);
417
418 /*
419 * it would be nice to use a bit field, but you can't depend on them.
420 * +---+---+---+---+---+---+---+---+
421 * + class |frm| id |
422 * +---+---+---+---+---+---+---+---+
423 * 7 6 5 4 3 2 1 0
424 */
425 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
426 #ifdef notdef
427 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
428 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
429 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
430 #else
431 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
432 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
433 #endif
434 elem->form = form;
435 elem->class = class;
436 elem->id = id;
437 p++; len--; hdr = 1;
438 /* extended tag field */
439 if (id == ASN_ID_EXT) {
440 /*
441 * The ID follows, as a sequence of octets with the
442 * 8th bit set and the remaining 7 bits being
443 * the next 7 bits of the value, terminated with
444 * an octet with the 8th bit not set.
445 *
446 * First, assemble all the octets with the 8th
447 * bit set. XXX - this doesn't handle a value
448 * that won't fit in 32 bits.
449 */
450 for (id = 0; *p & ASN_BIT8; len--, hdr++, p++) {
451 if (len < 1) {
452 ND_PRINT((ndo, "[Xtagfield?]"));
453 return -1;
454 }
455 ND_TCHECK(*p);
456 id = (id << 7) | (*p & ~ASN_BIT8);
457 }
458 if (len < 1) {
459 ND_PRINT((ndo, "[Xtagfield?]"));
460 return -1;
461 }
462 ND_TCHECK(*p);
463 elem->id = id = (id << 7) | *p;
464 --len;
465 ++hdr;
466 ++p;
467 }
468 if (len < 1) {
469 ND_PRINT((ndo, "[no asnlen]"));
470 return -1;
471 }
472 ND_TCHECK(*p);
473 elem->asnlen = *p;
474 p++; len--; hdr++;
475 if (elem->asnlen & ASN_BIT8) {
476 uint32_t noct = elem->asnlen % ASN_BIT8;
477 elem->asnlen = 0;
478 if (len < noct) {
479 ND_PRINT((ndo, "[asnlen? %d<%d]", len, noct));
480 return -1;
481 }
482 ND_TCHECK2(*p, noct);
483 for (; noct-- > 0; len--, hdr++)
484 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
485 }
486 if (len < elem->asnlen) {
487 ND_PRINT((ndo, "[len%d<asnlen%u]", len, elem->asnlen));
488 return -1;
489 }
490 if (form >= sizeof(Form)/sizeof(Form[0])) {
491 ND_PRINT((ndo, "[form?%d]", form));
492 return -1;
493 }
494 if (class >= sizeof(Class)/sizeof(Class[0])) {
495 ND_PRINT((ndo, "[class?%c/%d]", *Form[form], class));
496 return -1;
497 }
498 if ((int)id >= Class[class].numIDs) {
499 ND_PRINT((ndo, "[id?%c/%s/%d]", *Form[form], Class[class].name, id));
500 return -1;
501 }
502
503 switch (form) {
504 case PRIMITIVE:
505 switch (class) {
506 case UNIVERSAL:
507 switch (id) {
508 case STRING:
509 elem->type = BE_STR;
510 elem->data.str = p;
511 break;
512
513 case INTEGER: {
514 register int32_t data;
515 elem->type = BE_INT;
516 data = 0;
517
518 ND_TCHECK2(*p, elem->asnlen);
519 if (*p & ASN_BIT8) /* negative */
520 data = -1;
521 for (i = elem->asnlen; i-- > 0; p++)
522 data = (data << ASN_SHIFT8) | *p;
523 elem->data.integer = data;
524 break;
525 }
526
527 case OBJECTID:
528 elem->type = BE_OID;
529 elem->data.raw = (const uint8_t *)p;
530 break;
531
532 case ASN_NULL:
533 elem->type = BE_NULL;
534 elem->data.raw = NULL;
535 break;
536
537 default:
538 elem->type = BE_OCTET;
539 elem->data.raw = (const uint8_t *)p;
540 ND_PRINT((ndo, "[P/U/%s]", Class[class].Id[id]));
541 break;
542 }
543 break;
544
545 case APPLICATION:
546 switch (id) {
547 case IPADDR:
548 elem->type = BE_INETADDR;
549 elem->data.raw = (const uint8_t *)p;
550 break;
551
552 case COUNTER:
553 case GAUGE:
554 case TIMETICKS: {
555 register uint32_t data;
556 ND_TCHECK2(*p, elem->asnlen);
557 elem->type = BE_UNS;
558 data = 0;
559 for (i = elem->asnlen; i-- > 0; p++)
560 data = (data << 8) + *p;
561 elem->data.uns = data;
562 break;
563 }
564
565 case COUNTER64: {
566 register uint32_t high, low;
567 ND_TCHECK2(*p, elem->asnlen);
568 elem->type = BE_UNS64;
569 high = 0, low = 0;
570 for (i = elem->asnlen; i-- > 0; p++) {
571 high = (high << 8) |
572 ((low & 0xFF000000) >> 24);
573 low = (low << 8) | *p;
574 }
575 elem->data.uns64.high = high;
576 elem->data.uns64.low = low;
577 break;
578 }
579
580 default:
581 elem->type = BE_OCTET;
582 elem->data.raw = (const uint8_t *)p;
583 ND_PRINT((ndo, "[P/A/%s]",
584 Class[class].Id[id]));
585 break;
586 }
587 break;
588
589 case CONTEXT:
590 switch (id) {
591 case NOSUCHOBJECT:
592 elem->type = BE_NOSUCHOBJECT;
593 elem->data.raw = NULL;
594 break;
595
596 case NOSUCHINSTANCE:
597 elem->type = BE_NOSUCHINST;
598 elem->data.raw = NULL;
599 break;
600
601 case ENDOFMIBVIEW:
602 elem->type = BE_ENDOFMIBVIEW;
603 elem->data.raw = NULL;
604 break;
605 }
606 break;
607
608 default:
609 ND_PRINT((ndo, "[P/%s/%s]", Class[class].name, Class[class].Id[id]));
610 ND_TCHECK2(*p, elem->asnlen);
611 elem->type = BE_OCTET;
612 elem->data.raw = (const uint8_t *)p;
613 break;
614 }
615 break;
616
617 case CONSTRUCTED:
618 switch (class) {
619 case UNIVERSAL:
620 switch (id) {
621 case SEQUENCE:
622 elem->type = BE_SEQ;
623 elem->data.raw = (const uint8_t *)p;
624 break;
625
626 default:
627 elem->type = BE_OCTET;
628 elem->data.raw = (const uint8_t *)p;
629 ND_PRINT((ndo, "C/U/%s", Class[class].Id[id]));
630 break;
631 }
632 break;
633
634 case CONTEXT:
635 elem->type = BE_PDU;
636 elem->data.raw = (const uint8_t *)p;
637 break;
638
639 default:
640 elem->type = BE_OCTET;
641 elem->data.raw = (const uint8_t *)p;
642 ND_PRINT((ndo, "C/%s/%s", Class[class].name, Class[class].Id[id]));
643 break;
644 }
645 break;
646 }
647 p += elem->asnlen;
648 len -= elem->asnlen;
649 return elem->asnlen + hdr;
650
651 trunc:
652 ND_PRINT((ndo, "%s", tstr));
653 return -1;
654 }
655
656 /*
657 * Display the ASN.1 object represented by the BE object.
658 * This used to be an integral part of asn1_parse() before the intermediate
659 * BE form was added.
660 */
661 static int
662 asn1_print(netdissect_options *ndo,
663 struct be *elem)
664 {
665 const u_char *p = (const u_char *)elem->data.raw;
666 uint32_t asnlen = elem->asnlen;
667 uint32_t i;
668
669 switch (elem->type) {
670
671 case BE_OCTET:
672 ND_TCHECK2(*p, asnlen);
673 for (i = asnlen; i-- > 0; p++)
674 ND_PRINT((ndo, "_%.2x", *p));
675 break;
676
677 case BE_NULL:
678 break;
679
680 case BE_OID: {
681 int o = 0, first = -1;
682
683 i = asnlen;
684 if (!nd_smi_module_loaded) {
685 if (!ndo->ndo_nflag && asnlen > 2) {
686 const struct obj_abrev *a = &obj_abrev_list[0];
687 size_t a_len = strlen(a->oid);
688 for (; a->node; a++) {
689 ND_TCHECK2(*p, a_len);
690 if (memcmp(a->oid, p, a_len) == 0) {
691 objp = a->node->child;
692 i -= strlen(a->oid);
693 p += strlen(a->oid);
694 ND_PRINT((ndo, "%s", a->prefix));
695 first = 1;
696 break;
697 }
698 }
699 }
700
701 for (; i-- > 0; p++) {
702 ND_TCHECK(*p);
703 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
704 if (*p & ASN_LONGLEN)
705 continue;
706
707 /*
708 * first subitem encodes two items with
709 * 1st*OIDMUX+2nd
710 * (see X.690:1997 clause 8.19 for the details)
711 */
712 if (first < 0) {
713 int s;
714 if (!ndo->ndo_nflag)
715 objp = mibroot;
716 first = 0;
717 s = o / OIDMUX;
718 if (s > 2) s = 2;
719 OBJ_PRINT(s, first);
720 o -= s * OIDMUX;
721 }
722 OBJ_PRINT(o, first);
723 if (--first < 0)
724 first = 0;
725 o = 0;
726 }
727 }
728 break;
729 }
730
731 case BE_INT:
732 ND_PRINT((ndo, "%d", elem->data.integer));
733 break;
734
735 case BE_UNS:
736 ND_PRINT((ndo, "%u", elem->data.uns));
737 break;
738
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;
746 }
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;
756 }
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;
774 }
775 *cpf = j + '0';
776 }
777 ND_PRINT((ndo, "%s", first));
778 break;
779 }
780
781 case BE_STR: {
782 register int printable = 1, first = 1;
783
784 p = elem->data.str;
785 ND_TCHECK2(*p, asnlen);
786 for (i = asnlen; printable && i-- > 0; p++)
787 printable = ND_ISPRINT(*p);
788 p = elem->data.str;
789 if (printable) {
790 ND_PRINT((ndo, "\""));
791 if (fn_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
792 ND_PRINT((ndo, "\""));
793 goto trunc;
794 }
795 ND_PRINT((ndo, "\""));
796 } else
797 for (i = asnlen; i-- > 0; p++) {
798 ND_PRINT((ndo, first ? "%.2x" : "_%.2x", *p));
799 first = 0;
800 }
801 break;
802 }
803
804 case BE_SEQ:
805 ND_PRINT((ndo, "Seq(%u)", elem->asnlen));
806 break;
807
808 case BE_INETADDR:
809 if (asnlen != ASNLEN_INETADDR)
810 ND_PRINT((ndo, "[inetaddr len!=%d]", ASNLEN_INETADDR));
811 ND_TCHECK2(*p, asnlen);
812 for (i = asnlen; i-- != 0; p++) {
813 ND_PRINT((ndo, (i == asnlen-1) ? "%u" : ".%u", *p));
814 }
815 break;
816
817 case BE_NOSUCHOBJECT:
818 case BE_NOSUCHINST:
819 case BE_ENDOFMIBVIEW:
820 ND_PRINT((ndo, "[%s]", Class[EXCEPTIONS].Id[elem->id]));
821 break;
822
823 case BE_PDU:
824 ND_PRINT((ndo, "%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen));
825 break;
826
827 case BE_ANY:
828 ND_PRINT((ndo, "[BE_ANY!?]"));
829 break;
830
831 default:
832 ND_PRINT((ndo, "[be!?]"));
833 break;
834 }
835 return 0;
836
837 trunc:
838 ND_PRINT((ndo, "%s", tstr));
839 return -1;
840 }
841
842 #ifdef notdef
843 /*
844 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
845 * This will work for any ASN.1 stream, not just an SNMP PDU.
846 *
847 * By adding newlines and spaces at the correct places, this would print in
848 * Rose-Normal-Form.
849 *
850 * This is not currently used.
851 */
852 static void
853 asn1_decode(u_char *p, u_int length)
854 {
855 struct be elem;
856 int i = 0;
857
858 while (i >= 0 && length > 0) {
859 i = asn1_parse(ndo, p, length, &elem);
860 if (i >= 0) {
861 ND_PRINT((ndo, " "));
862 if (asn1_print(ndo, &elem) < 0)
863 return;
864 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
865 ND_PRINT((ndo, " {"));
866 asn1_decode(elem.data.raw, elem.asnlen);
867 ND_PRINT((ndo, " }"));
868 }
869 length -= i;
870 p += i;
871 }
872 }
873 }
874 #endif
875
876 #ifdef USE_LIBSMI
877
878 struct smi2be {
879 SmiBasetype basetype;
880 int be;
881 };
882
883 static const struct smi2be smi2betab[] = {
884 { SMI_BASETYPE_INTEGER32, BE_INT },
885 { SMI_BASETYPE_OCTETSTRING, BE_STR },
886 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
887 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
888 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
889 { SMI_BASETYPE_INTEGER64, BE_NONE },
890 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
891 { SMI_BASETYPE_FLOAT32, BE_NONE },
892 { SMI_BASETYPE_FLOAT64, BE_NONE },
893 { SMI_BASETYPE_FLOAT128, BE_NONE },
894 { SMI_BASETYPE_ENUM, BE_INT },
895 { SMI_BASETYPE_BITS, BE_STR },
896 { SMI_BASETYPE_UNKNOWN, BE_NONE }
897 };
898
899 static int
900 smi_decode_oid(netdissect_options *ndo,
901 struct be *elem, unsigned int *oid,
902 unsigned int oidsize, unsigned int *oidlen)
903 {
904 const u_char *p = (const u_char *)elem->data.raw;
905 uint32_t asnlen = elem->asnlen;
906 int o = 0, first = -1, i = asnlen;
907 unsigned int firstval;
908
909 if (nd_smi_module_loaded) {
910 for (*oidlen = 0; i-- > 0; p++) {
911 ND_TCHECK(*p);
912 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
913 if (*p & ASN_LONGLEN)
914 continue;
915
916 /*
917 * first subitem encodes two items with 1st*OIDMUX+2nd
918 * (see X.690:1997 clause 8.19 for the details)
919 */
920 if (first < 0) {
921 first = 0;
922 firstval = o / OIDMUX;
923 if (firstval > 2) firstval = 2;
924 o -= firstval * OIDMUX;
925 if (*oidlen < oidsize) {
926 oid[(*oidlen)++] = firstval;
927 }
928 }
929 if (*oidlen < oidsize) {
930 oid[(*oidlen)++] = o;
931 }
932 o = 0;
933 }
934 }
935 return 0;
936
937 trunc:
938 ND_PRINT((ndo, "%s", tstr));
939 return -1;
940 }
941
942 static int smi_check_type(SmiBasetype basetype, int be)
943 {
944 int i;
945
946 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
947 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
948 return 1;
949 }
950 }
951
952 return 0;
953 }
954
955 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
956 struct be *elem)
957 {
958 int ok = 1;
959
960 switch (smiType->basetype) {
961 case SMI_BASETYPE_OBJECTIDENTIFIER:
962 case SMI_BASETYPE_OCTETSTRING:
963 if (smiRange->minValue.value.unsigned32
964 == smiRange->maxValue.value.unsigned32) {
965 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
966 } else {
967 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
968 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
969 }
970 break;
971
972 case SMI_BASETYPE_INTEGER32:
973 ok = (elem->data.integer >= smiRange->minValue.value.integer32
974 && elem->data.integer <= smiRange->maxValue.value.integer32);
975 break;
976
977 case SMI_BASETYPE_UNSIGNED32:
978 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
979 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
980 break;
981
982 case SMI_BASETYPE_UNSIGNED64:
983 /* XXX */
984 break;
985
986 /* case SMI_BASETYPE_INTEGER64: SMIng */
987 /* case SMI_BASETYPE_FLOAT32: SMIng */
988 /* case SMI_BASETYPE_FLOAT64: SMIng */
989 /* case SMI_BASETYPE_FLOAT128: SMIng */
990
991 case SMI_BASETYPE_ENUM:
992 case SMI_BASETYPE_BITS:
993 case SMI_BASETYPE_UNKNOWN:
994 ok = 1;
995 break;
996
997 default:
998 ok = 0;
999 break;
1000 }
1001
1002 return ok;
1003 }
1004
1005 static int smi_check_range(SmiType *smiType, struct be *elem)
1006 {
1007 SmiRange *smiRange;
1008 int ok = 1;
1009
1010 for (smiRange = smiGetFirstRange(smiType);
1011 smiRange;
1012 smiRange = smiGetNextRange(smiRange)) {
1013
1014 ok = smi_check_a_range(smiType, smiRange, elem);
1015
1016 if (ok) {
1017 break;
1018 }
1019 }
1020
1021 if (ok) {
1022 SmiType *parentType;
1023 parentType = smiGetParentType(smiType);
1024 if (parentType) {
1025 ok = smi_check_range(parentType, elem);
1026 }
1027 }
1028
1029 return ok;
1030 }
1031
1032 static SmiNode *
1033 smi_print_variable(netdissect_options *ndo,
1034 struct be *elem, int *status)
1035 {
1036 unsigned int oid[128], oidlen;
1037 SmiNode *smiNode = NULL;
1038 unsigned int i;
1039
1040 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1041 &oidlen);
1042 if (*status < 0)
1043 return NULL;
1044 smiNode = smiGetNodeByOID(oidlen, oid);
1045 if (! smiNode) {
1046 *status = asn1_print(ndo, elem);
1047 return NULL;
1048 }
1049 if (ndo->ndo_vflag) {
1050 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1051 }
1052 ND_PRINT((ndo, "%s", smiNode->name));
1053 if (smiNode->oidlen < oidlen) {
1054 for (i = smiNode->oidlen; i < oidlen; i++) {
1055 ND_PRINT((ndo, ".%u", oid[i]));
1056 }
1057 }
1058 *status = 0;
1059 return smiNode;
1060 }
1061
1062 static int
1063 smi_print_value(netdissect_options *ndo,
1064 SmiNode *smiNode, u_short pduid, struct be *elem)
1065 {
1066 unsigned int i, oid[128], oidlen;
1067 SmiType *smiType;
1068 SmiNamedNumber *nn;
1069 int done = 0;
1070
1071 if (! smiNode || ! (smiNode->nodekind
1072 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1073 return asn1_print(ndo, elem);
1074 }
1075
1076 if (elem->type == BE_NOSUCHOBJECT
1077 || elem->type == BE_NOSUCHINST
1078 || elem->type == BE_ENDOFMIBVIEW) {
1079 return asn1_print(ndo, elem);
1080 }
1081
1082 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1083 ND_PRINT((ndo, "[notNotifyable]"));
1084 }
1085
1086 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1087 ND_PRINT((ndo, "[notReadable]"));
1088 }
1089
1090 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1091 ND_PRINT((ndo, "[notWritable]"));
1092 }
1093
1094 if (RESPONSE_CLASS(pduid)
1095 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1096 ND_PRINT((ndo, "[noAccess]"));
1097 }
1098
1099 smiType = smiGetNodeType(smiNode);
1100 if (! smiType) {
1101 return asn1_print(ndo, elem);
1102 }
1103
1104 if (! smi_check_type(smiType->basetype, elem->type)) {
1105 ND_PRINT((ndo, "[wrongType]"));
1106 }
1107
1108 if (! smi_check_range(smiType, elem)) {
1109 ND_PRINT((ndo, "[outOfRange]"));
1110 }
1111
1112 /* resolve bits to named bits */
1113
1114 /* check whether instance identifier is valid */
1115
1116 /* apply display hints (integer, octetstring) */
1117
1118 /* convert instance identifier to index type values */
1119
1120 switch (elem->type) {
1121 case BE_OID:
1122 if (smiType->basetype == SMI_BASETYPE_BITS) {
1123 /* print bit labels */
1124 } else {
1125 smi_decode_oid(ndo, elem, oid,
1126 sizeof(oid)/sizeof(unsigned int),
1127 &oidlen);
1128 smiNode = smiGetNodeByOID(oidlen, oid);
1129 if (smiNode) {
1130 if (ndo->ndo_vflag) {
1131 ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name));
1132 }
1133 ND_PRINT((ndo, "%s", smiNode->name));
1134 if (smiNode->oidlen < oidlen) {
1135 for (i = smiNode->oidlen;
1136 i < oidlen; i++) {
1137 ND_PRINT((ndo, ".%u", oid[i]));
1138 }
1139 }
1140 done++;
1141 }
1142 }
1143 break;
1144
1145 case BE_INT:
1146 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1147 for (nn = smiGetFirstNamedNumber(smiType);
1148 nn;
1149 nn = smiGetNextNamedNumber(nn)) {
1150 if (nn->value.value.integer32
1151 == elem->data.integer) {
1152 ND_PRINT((ndo, "%s", nn->name));
1153 ND_PRINT((ndo, "(%d)", elem->data.integer));
1154 done++;
1155 break;
1156 }
1157 }
1158 }
1159 break;
1160 }
1161
1162 if (! done) {
1163 return asn1_print(ndo, elem);
1164 }
1165 return 0;
1166 }
1167 #endif
1168
1169 /*
1170 * General SNMP header
1171 * SEQUENCE {
1172 * version INTEGER {version-1(0)},
1173 * community OCTET STRING,
1174 * data ANY -- PDUs
1175 * }
1176 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1177 * SEQUENCE {
1178 * request-id INTEGER,
1179 * error-status INTEGER,
1180 * error-index INTEGER,
1181 * varbindlist SEQUENCE OF
1182 * SEQUENCE {
1183 * name ObjectName,
1184 * value ObjectValue
1185 * }
1186 * }
1187 * PDU for Trap:
1188 * SEQUENCE {
1189 * enterprise OBJECT IDENTIFIER,
1190 * agent-addr NetworkAddress,
1191 * generic-trap INTEGER,
1192 * specific-trap INTEGER,
1193 * time-stamp TimeTicks,
1194 * varbindlist SEQUENCE OF
1195 * SEQUENCE {
1196 * name ObjectName,
1197 * value ObjectValue
1198 * }
1199 * }
1200 */
1201
1202 /*
1203 * Decode SNMP varBind
1204 */
1205 static void
1206 varbind_print(netdissect_options *ndo,
1207 u_short pduid, const u_char *np, u_int length)
1208 {
1209 struct be elem;
1210 int count = 0, ind;
1211 #ifdef USE_LIBSMI
1212 SmiNode *smiNode = NULL;
1213 #endif
1214 int status;
1215
1216 /* Sequence of varBind */
1217 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1218 return;
1219 if (elem.type != BE_SEQ) {
1220 ND_PRINT((ndo, "[!SEQ of varbind]"));
1221 asn1_print(ndo, &elem);
1222 return;
1223 }
1224 if ((u_int)count < length)
1225 ND_PRINT((ndo, "[%d extra after SEQ of varbind]", length - count));
1226 /* descend */
1227 length = elem.asnlen;
1228 np = (const u_char *)elem.data.raw;
1229
1230 for (ind = 1; length > 0; ind++) {
1231 const u_char *vbend;
1232 u_int vblength;
1233
1234 ND_PRINT((ndo, " "));
1235
1236 /* Sequence */
1237 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1238 return;
1239 if (elem.type != BE_SEQ) {
1240 ND_PRINT((ndo, "[!varbind]"));
1241 asn1_print(ndo, &elem);
1242 return;
1243 }
1244 vbend = np + count;
1245 vblength = length - count;
1246 /* descend */
1247 length = elem.asnlen;
1248 np = (const u_char *)elem.data.raw;
1249
1250 /* objName (OID) */
1251 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1252 return;
1253 if (elem.type != BE_OID) {
1254 ND_PRINT((ndo, "[objName!=OID]"));
1255 asn1_print(ndo, &elem);
1256 return;
1257 }
1258 #ifdef USE_LIBSMI
1259 smiNode = smi_print_variable(ndo, &elem, &status);
1260 #else
1261 status = asn1_print(ndo, &elem);
1262 #endif
1263 if (status < 0)
1264 return;
1265 length -= count;
1266 np += count;
1267
1268 if (pduid != GETREQ && pduid != GETNEXTREQ
1269 && pduid != GETBULKREQ)
1270 ND_PRINT((ndo, "="));
1271
1272 /* objVal (ANY) */
1273 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1274 return;
1275 if (pduid == GETREQ || pduid == GETNEXTREQ
1276 || pduid == GETBULKREQ) {
1277 if (elem.type != BE_NULL) {
1278 ND_PRINT((ndo, "[objVal!=NULL]"));
1279 if (asn1_print(ndo, &elem) < 0)
1280 return;
1281 }
1282 } else {
1283 if (elem.type != BE_NULL) {
1284 #ifdef USE_LIBSMI
1285 status = smi_print_value(ndo, smiNode, pduid, &elem);
1286 #else
1287 status = asn1_print(ndo, &elem);
1288 #endif
1289 }
1290 if (status < 0)
1291 return;
1292 }
1293 length = vblength;
1294 np = vbend;
1295 }
1296 }
1297
1298 /*
1299 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1300 * GetBulk, Inform, V2Trap, and Report
1301 */
1302 static void
1303 snmppdu_print(netdissect_options *ndo,
1304 u_short pduid, const u_char *np, u_int length)
1305 {
1306 struct be elem;
1307 int count = 0, error_status;
1308
1309 /* reqId (Integer) */
1310 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1311 return;
1312 if (elem.type != BE_INT) {
1313 ND_PRINT((ndo, "[reqId!=INT]"));
1314 asn1_print(ndo, &elem);
1315 return;
1316 }
1317 if (ndo->ndo_vflag)
1318 ND_PRINT((ndo, "R=%d ", elem.data.integer));
1319 length -= count;
1320 np += count;
1321
1322 /* errorStatus (Integer) */
1323 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1324 return;
1325 if (elem.type != BE_INT) {
1326 ND_PRINT((ndo, "[errorStatus!=INT]"));
1327 asn1_print(ndo, &elem);
1328 return;
1329 }
1330 error_status = 0;
1331 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1332 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1333 && elem.data.integer != 0) {
1334 char errbuf[20];
1335 ND_PRINT((ndo, "[errorStatus(%s)!=0]",
1336 DECODE_ErrorStatus(elem.data.integer)));
1337 } else if (pduid == GETBULKREQ) {
1338 ND_PRINT((ndo, " N=%d", elem.data.integer));
1339 } else if (elem.data.integer != 0) {
1340 char errbuf[20];
1341 ND_PRINT((ndo, " %s", DECODE_ErrorStatus(elem.data.integer)));
1342 error_status = elem.data.integer;
1343 }
1344 length -= count;
1345 np += count;
1346
1347 /* errorIndex (Integer) */
1348 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1349 return;
1350 if (elem.type != BE_INT) {
1351 ND_PRINT((ndo, "[errorIndex!=INT]"));
1352 asn1_print(ndo, &elem);
1353 return;
1354 }
1355 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1356 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1357 && elem.data.integer != 0)
1358 ND_PRINT((ndo, "[errorIndex(%d)!=0]", elem.data.integer));
1359 else if (pduid == GETBULKREQ)
1360 ND_PRINT((ndo, " M=%d", elem.data.integer));
1361 else if (elem.data.integer != 0) {
1362 if (!error_status)
1363 ND_PRINT((ndo, "[errorIndex(%d) w/o errorStatus]", elem.data.integer));
1364 else
1365 ND_PRINT((ndo, "@%d", elem.data.integer));
1366 } else if (error_status) {
1367 ND_PRINT((ndo, "[errorIndex==0]"));
1368 }
1369 length -= count;
1370 np += count;
1371
1372 varbind_print(ndo, pduid, np, length);
1373 return;
1374 }
1375
1376 /*
1377 * Decode SNMP Trap PDU
1378 */
1379 static void
1380 trappdu_print(netdissect_options *ndo,
1381 const u_char *np, u_int length)
1382 {
1383 struct be elem;
1384 int count = 0, generic;
1385
1386 ND_PRINT((ndo, " "));
1387
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;
1395 }
1396 if (asn1_print(ndo, &elem) < 0)
1397 return;
1398 length -= count;
1399 np += count;
1400
1401 ND_PRINT((ndo, " "));
1402
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;
1410 }
1411 if (asn1_print(ndo, &elem) < 0)
1412 return;
1413 length -= count;
1414 np += count;
1415
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;
1423 }
1424 generic = elem.data.integer;
1425 {
1426 char buf[20];
1427 ND_PRINT((ndo, " %s", DECODE_GenericTrap(generic)));
1428 }
1429 length -= count;
1430 np += count;
1431
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;
1439 }
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;
1447
1448 ND_PRINT((ndo, " "));
1449
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;
1457 }
1458 if (asn1_print(ndo, &elem) < 0)
1459 return;
1460 length -= count;
1461 np += count;
1462
1463 varbind_print(ndo, TRAP, np, length);
1464 return;
1465 }
1466
1467 /*
1468 * Decode arbitrary SNMP PDUs.
1469 */
1470 static void
1471 pdu_print(netdissect_options *ndo,
1472 const u_char *np, u_int length, int version)
1473 {
1474 struct be pdu;
1475 int count = 0;
1476
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;
1483 }
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, "{ "));
1488 }
1489 if (asn1_print(ndo, &pdu) < 0)
1490 return;
1491 ND_PRINT((ndo, " "));
1492 /* descend into PDU */
1493 length = pdu.asnlen;
1494 np = (const u_char *)pdu.data.raw;
1495
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;
1501 }
1502
1503 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1504 ND_PRINT((ndo, "[v1 PDU in v2 message]"));
1505 return;
1506 }
1507
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;
1522 }
1523
1524 if (ndo->ndo_vflag) {
1525 ND_PRINT((ndo, " } "));
1526 }
1527 }
1528
1529 /*
1530 * Decode a scoped SNMP PDU.
1531 */
1532 static void
1533 scopedpdu_print(netdissect_options *ndo,
1534 const u_char *np, u_int length, int version)
1535 {
1536 struct be elem;
1537 int i, count = 0;
1538
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;
1546 }
1547 length = elem.asnlen;
1548 np = (const u_char *)elem.data.raw;
1549
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;
1557 }
1558 length -= count;
1559 np += count;
1560
1561 ND_PRINT((ndo, "E= "));
1562 for (i = 0; i < (int)elem.asnlen; i++) {
1563 ND_PRINT((ndo, "0x%02X", elem.data.str[i]));
1564 }
1565 ND_PRINT((ndo, " "));
1566
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;
1574 }
1575 length -= count;
1576 np += count;
1577
1578 ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str));
1579
1580 pdu_print(ndo, np, length, version);
1581 }
1582
1583 /*
1584 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1585 */
1586 static void
1587 community_print(netdissect_options *ndo,
1588 const u_char *np, u_int length, int version)
1589 {
1590 struct be elem;
1591 int count = 0;
1592
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;
1600 }
1601 /* default community */
1602 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1603 strncmp((const 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;
1609
1610 pdu_print(ndo, np, length, version);
1611 }
1612
1613 /*
1614 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1615 */
1616 static void
1617 usm_print(netdissect_options *ndo,
1618 const u_char *np, u_int length)
1619 {
1620 struct be elem;
1621 int count = 0;
1622
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;
1630 }
1631 length = elem.asnlen;
1632 np = (const u_char *)elem.data.raw;
1633
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;
1641 }
1642 length -= count;
1643 np += count;
1644
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;
1652 }
1653 if (ndo->ndo_vflag)
1654 ND_PRINT((ndo, "B=%d ", elem.data.integer));
1655 length -= count;
1656 np += count;
1657
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;
1665 }
1666 if (ndo->ndo_vflag)
1667 ND_PRINT((ndo, "T=%d ", elem.data.integer));
1668 length -= count;
1669 np += count;
1670
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;
1678 }
1679 length -= count;
1680 np += count;
1681
1682 ND_PRINT((ndo, "U=%.*s ", (int)elem.asnlen, elem.data.str));
1683
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;
1691 }
1692 length -= count;
1693 np += count;
1694
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;
1702 }
1703 length -= count;
1704 np += count;
1705
1706 if ((u_int)count < length)
1707 ND_PRINT((ndo, "[%d extra after usm SEQ]", length - count));
1708 }
1709
1710 /*
1711 * Decode SNMPv3 Message Header (SNMPv3)
1712 */
1713 static void
1714 v3msg_print(netdissect_options *ndo,
1715 const u_char *np, u_int length)
1716 {
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;
1723
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;
1731 }
1732 length = elem.asnlen;
1733 np = (const u_char *)elem.data.raw;
1734
1735 if (ndo->ndo_vflag) {
1736 ND_PRINT((ndo, "{ "));
1737 }
1738
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;
1746 }
1747 length -= count;
1748 np += count;
1749
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;
1757 }
1758 length -= count;
1759 np += count;
1760
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;
1768 }
1769 if (elem.asnlen != 1) {
1770 ND_PRINT((ndo, "[msgFlags size %d]", elem.asnlen));
1771 return;
1772 }
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;
1778 }
1779 length -= count;
1780 np += count;
1781
1782 ND_PRINT((ndo, "F=%s%s%s ",
1783 flags & 0x01 ? "a" : "",
1784 flags & 0x02 ? "p" : "",
1785 flags & 0x04 ? "r" : ""));
1786
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;
1794 }
1795 model = elem.data.integer;
1796 length -= count;
1797 np += count;
1798
1799 if ((u_int)count < length)
1800 ND_PRINT((ndo, "[%d extra after message SEQ]", length - count));
1801
1802 if (ndo->ndo_vflag) {
1803 ND_PRINT((ndo, "} "));
1804 }
1805
1806 if (model == 3) {
1807 if (ndo->ndo_vflag) {
1808 ND_PRINT((ndo, "{ USM "));
1809 }
1810 } else {
1811 ND_PRINT((ndo, "[security model %d]", model));
1812 return;
1813 }
1814
1815 np = xnp + (np - xnp);
1816 length = xlength - (np - xnp);
1817
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;
1825 }
1826 length -= count;
1827 np += count;
1828
1829 if (model == 3) {
1830 usm_print(ndo, elem.data.str, elem.asnlen);
1831 if (ndo->ndo_vflag) {
1832 ND_PRINT((ndo, "} "));
1833 }
1834 }
1835
1836 if (ndo->ndo_vflag) {
1837 ND_PRINT((ndo, "{ ScopedPDU "));
1838 }
1839
1840 scopedpdu_print(ndo, np, length, 3);
1841
1842 if (ndo->ndo_vflag) {
1843 ND_PRINT((ndo, "} "));
1844 }
1845 }
1846
1847 /*
1848 * Decode SNMP header and pass on to PDU printing routines
1849 */
1850 void
1851 snmp_print(netdissect_options *ndo,
1852 const u_char *np, u_int length)
1853 {
1854 struct be elem;
1855 int count = 0;
1856 int version = 0;
1857
1858 ND_PRINT((ndo, " "));
1859
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;
1867 }
1868 if ((u_int)count < length)
1869 ND_PRINT((ndo, "[%d extra after iSEQ]", length - count));
1870 /* descend */
1871 length = elem.asnlen;
1872 np = (const u_char *)elem.data.raw;
1873
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;
1881 }
1882
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;
1893 }
1894 version = elem.data.integer;
1895 length -= count;
1896 np += count;
1897
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;
1909 }
1910
1911 if (ndo->ndo_vflag) {
1912 ND_PRINT((ndo, "} "));
1913 }
1914 }