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