]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
The official #define for 32-bit and 64-bit Windows is _WIN32.
[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 "interface.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;
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 = 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 = 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)
1356 ND_PRINT((ndo, "[errorIndex(%d) w/o errorStatus]", elem.data.integer));
1357 else {
1358 ND_PRINT((ndo, "@%d", elem.data.integer));
1359 error = elem.data.integer;
1360 }
1361 } else if (error) {
1362 ND_PRINT((ndo, "[errorIndex==0]"));
1363 error = 0;
1364 }
1365 length -= count;
1366 np += count;
1367
1368 varbind_print(ndo, pduid, np, length);
1369 return;
1370 }
1371
1372 /*
1373 * Decode SNMP Trap PDU
1374 */
1375 static void
1376 trappdu_print(netdissect_options *ndo,
1377 const u_char *np, u_int length)
1378 {
1379 struct be elem;
1380 int count = 0, generic;
1381
1382 ND_PRINT((ndo, " "));
1383
1384 /* enterprise (oid) */
1385 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1386 return;
1387 if (elem.type != BE_OID) {
1388 ND_PRINT((ndo, "[enterprise!=OID]"));
1389 asn1_print(ndo, &elem);
1390 return;
1391 }
1392 if (asn1_print(ndo, &elem) < 0)
1393 return;
1394 length -= count;
1395 np += count;
1396
1397 ND_PRINT((ndo, " "));
1398
1399 /* agent-addr (inetaddr) */
1400 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1401 return;
1402 if (elem.type != BE_INETADDR) {
1403 ND_PRINT((ndo, "[agent-addr!=INETADDR]"));
1404 asn1_print(ndo, &elem);
1405 return;
1406 }
1407 if (asn1_print(ndo, &elem) < 0)
1408 return;
1409 length -= count;
1410 np += count;
1411
1412 /* generic-trap (Integer) */
1413 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1414 return;
1415 if (elem.type != BE_INT) {
1416 ND_PRINT((ndo, "[generic-trap!=INT]"));
1417 asn1_print(ndo, &elem);
1418 return;
1419 }
1420 generic = elem.data.integer;
1421 {
1422 char buf[20];
1423 ND_PRINT((ndo, " %s", DECODE_GenericTrap(generic)));
1424 }
1425 length -= count;
1426 np += count;
1427
1428 /* specific-trap (Integer) */
1429 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1430 return;
1431 if (elem.type != BE_INT) {
1432 ND_PRINT((ndo, "[specific-trap!=INT]"));
1433 asn1_print(ndo, &elem);
1434 return;
1435 }
1436 if (generic != GT_ENTERPRISE) {
1437 if (elem.data.integer != 0)
1438 ND_PRINT((ndo, "[specific-trap(%d)!=0]", elem.data.integer));
1439 } else
1440 ND_PRINT((ndo, " s=%d", elem.data.integer));
1441 length -= count;
1442 np += count;
1443
1444 ND_PRINT((ndo, " "));
1445
1446 /* time-stamp (TimeTicks) */
1447 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1448 return;
1449 if (elem.type != BE_UNS) { /* XXX */
1450 ND_PRINT((ndo, "[time-stamp!=TIMETICKS]"));
1451 asn1_print(ndo, &elem);
1452 return;
1453 }
1454 if (asn1_print(ndo, &elem) < 0)
1455 return;
1456 length -= count;
1457 np += count;
1458
1459 varbind_print(ndo, TRAP, np, length);
1460 return;
1461 }
1462
1463 /*
1464 * Decode arbitrary SNMP PDUs.
1465 */
1466 static void
1467 pdu_print(netdissect_options *ndo,
1468 const u_char *np, u_int length, int version)
1469 {
1470 struct be pdu;
1471 int count = 0;
1472
1473 /* PDU (Context) */
1474 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1475 return;
1476 if (pdu.type != BE_PDU) {
1477 ND_PRINT((ndo, "[no PDU]"));
1478 return;
1479 }
1480 if ((u_int)count < length)
1481 ND_PRINT((ndo, "[%d extra after PDU]", length - count));
1482 if (ndo->ndo_vflag) {
1483 ND_PRINT((ndo, "{ "));
1484 }
1485 if (asn1_print(ndo, &pdu) < 0)
1486 return;
1487 ND_PRINT((ndo, " "));
1488 /* descend into PDU */
1489 length = pdu.asnlen;
1490 np = (const u_char *)pdu.data.raw;
1491
1492 if (version == SNMP_VERSION_1 &&
1493 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1494 pdu.id == V2TRAP || pdu.id == REPORT)) {
1495 ND_PRINT((ndo, "[v2 PDU in v1 message]"));
1496 return;
1497 }
1498
1499 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1500 ND_PRINT((ndo, "[v1 PDU in v2 message]"));
1501 return;
1502 }
1503
1504 switch (pdu.id) {
1505 case TRAP:
1506 trappdu_print(ndo, np, length);
1507 break;
1508 case GETREQ:
1509 case GETNEXTREQ:
1510 case GETRESP:
1511 case SETREQ:
1512 case GETBULKREQ:
1513 case INFORMREQ:
1514 case V2TRAP:
1515 case REPORT:
1516 snmppdu_print(ndo, pdu.id, np, length);
1517 break;
1518 }
1519
1520 if (ndo->ndo_vflag) {
1521 ND_PRINT((ndo, " } "));
1522 }
1523 }
1524
1525 /*
1526 * Decode a scoped SNMP PDU.
1527 */
1528 static void
1529 scopedpdu_print(netdissect_options *ndo,
1530 const u_char *np, u_int length, int version)
1531 {
1532 struct be elem;
1533 int i, count = 0;
1534
1535 /* Sequence */
1536 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1537 return;
1538 if (elem.type != BE_SEQ) {
1539 ND_PRINT((ndo, "[!scoped PDU]"));
1540 asn1_print(ndo, &elem);
1541 return;
1542 }
1543 length = elem.asnlen;
1544 np = (const u_char *)elem.data.raw;
1545
1546 /* contextEngineID (OCTET STRING) */
1547 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1548 return;
1549 if (elem.type != BE_STR) {
1550 ND_PRINT((ndo, "[contextEngineID!=STR]"));
1551 asn1_print(ndo, &elem);
1552 return;
1553 }
1554 length -= count;
1555 np += count;
1556
1557 ND_PRINT((ndo, "E= "));
1558 for (i = 0; i < (int)elem.asnlen; i++) {
1559 ND_PRINT((ndo, "0x%02X", elem.data.str[i]));
1560 }
1561 ND_PRINT((ndo, " "));
1562
1563 /* contextName (OCTET STRING) */
1564 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1565 return;
1566 if (elem.type != BE_STR) {
1567 ND_PRINT((ndo, "[contextName!=STR]"));
1568 asn1_print(ndo, &elem);
1569 return;
1570 }
1571 length -= count;
1572 np += count;
1573
1574 ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str));
1575
1576 pdu_print(ndo, np, length, version);
1577 }
1578
1579 /*
1580 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1581 */
1582 static void
1583 community_print(netdissect_options *ndo,
1584 const u_char *np, u_int length, int version)
1585 {
1586 struct be elem;
1587 int count = 0;
1588
1589 /* Community (String) */
1590 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1591 return;
1592 if (elem.type != BE_STR) {
1593 ND_PRINT((ndo, "[comm!=STR]"));
1594 asn1_print(ndo, &elem);
1595 return;
1596 }
1597 /* default community */
1598 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1599 strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1600 sizeof(DEF_COMMUNITY) - 1) == 0))
1601 /* ! "public" */
1602 ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str));
1603 length -= count;
1604 np += count;
1605
1606 pdu_print(ndo, np, length, version);
1607 }
1608
1609 /*
1610 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1611 */
1612 static void
1613 usm_print(netdissect_options *ndo,
1614 const u_char *np, u_int length)
1615 {
1616 struct be elem;
1617 int count = 0;
1618
1619 /* Sequence */
1620 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1621 return;
1622 if (elem.type != BE_SEQ) {
1623 ND_PRINT((ndo, "[!usm]"));
1624 asn1_print(ndo, &elem);
1625 return;
1626 }
1627 length = elem.asnlen;
1628 np = (const u_char *)elem.data.raw;
1629
1630 /* msgAuthoritativeEngineID (OCTET STRING) */
1631 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1632 return;
1633 if (elem.type != BE_STR) {
1634 ND_PRINT((ndo, "[msgAuthoritativeEngineID!=STR]"));
1635 asn1_print(ndo, &elem);
1636 return;
1637 }
1638 length -= count;
1639 np += count;
1640
1641 /* msgAuthoritativeEngineBoots (INTEGER) */
1642 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1643 return;
1644 if (elem.type != BE_INT) {
1645 ND_PRINT((ndo, "[msgAuthoritativeEngineBoots!=INT]"));
1646 asn1_print(ndo, &elem);
1647 return;
1648 }
1649 if (ndo->ndo_vflag)
1650 ND_PRINT((ndo, "B=%d ", elem.data.integer));
1651 length -= count;
1652 np += count;
1653
1654 /* msgAuthoritativeEngineTime (INTEGER) */
1655 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1656 return;
1657 if (elem.type != BE_INT) {
1658 ND_PRINT((ndo, "[msgAuthoritativeEngineTime!=INT]"));
1659 asn1_print(ndo, &elem);
1660 return;
1661 }
1662 if (ndo->ndo_vflag)
1663 ND_PRINT((ndo, "T=%d ", elem.data.integer));
1664 length -= count;
1665 np += count;
1666
1667 /* msgUserName (OCTET STRING) */
1668 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1669 return;
1670 if (elem.type != BE_STR) {
1671 ND_PRINT((ndo, "[msgUserName!=STR]"));
1672 asn1_print(ndo, &elem);
1673 return;
1674 }
1675 length -= count;
1676 np += count;
1677
1678 ND_PRINT((ndo, "U=%.*s ", (int)elem.asnlen, elem.data.str));
1679
1680 /* msgAuthenticationParameters (OCTET STRING) */
1681 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1682 return;
1683 if (elem.type != BE_STR) {
1684 ND_PRINT((ndo, "[msgAuthenticationParameters!=STR]"));
1685 asn1_print(ndo, &elem);
1686 return;
1687 }
1688 length -= count;
1689 np += count;
1690
1691 /* msgPrivacyParameters (OCTET STRING) */
1692 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1693 return;
1694 if (elem.type != BE_STR) {
1695 ND_PRINT((ndo, "[msgPrivacyParameters!=STR]"));
1696 asn1_print(ndo, &elem);
1697 return;
1698 }
1699 length -= count;
1700 np += count;
1701
1702 if ((u_int)count < length)
1703 ND_PRINT((ndo, "[%d extra after usm SEQ]", length - count));
1704 }
1705
1706 /*
1707 * Decode SNMPv3 Message Header (SNMPv3)
1708 */
1709 static void
1710 v3msg_print(netdissect_options *ndo,
1711 const u_char *np, u_int length)
1712 {
1713 struct be elem;
1714 int count = 0;
1715 u_char flags;
1716 int model;
1717 const u_char *xnp = np;
1718 int xlength = length;
1719
1720 /* Sequence */
1721 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1722 return;
1723 if (elem.type != BE_SEQ) {
1724 ND_PRINT((ndo, "[!message]"));
1725 asn1_print(ndo, &elem);
1726 return;
1727 }
1728 length = elem.asnlen;
1729 np = (const u_char *)elem.data.raw;
1730
1731 if (ndo->ndo_vflag) {
1732 ND_PRINT((ndo, "{ "));
1733 }
1734
1735 /* msgID (INTEGER) */
1736 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1737 return;
1738 if (elem.type != BE_INT) {
1739 ND_PRINT((ndo, "[msgID!=INT]"));
1740 asn1_print(ndo, &elem);
1741 return;
1742 }
1743 length -= count;
1744 np += count;
1745
1746 /* msgMaxSize (INTEGER) */
1747 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1748 return;
1749 if (elem.type != BE_INT) {
1750 ND_PRINT((ndo, "[msgMaxSize!=INT]"));
1751 asn1_print(ndo, &elem);
1752 return;
1753 }
1754 length -= count;
1755 np += count;
1756
1757 /* msgFlags (OCTET STRING) */
1758 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1759 return;
1760 if (elem.type != BE_STR) {
1761 ND_PRINT((ndo, "[msgFlags!=STR]"));
1762 asn1_print(ndo, &elem);
1763 return;
1764 }
1765 if (elem.asnlen != 1) {
1766 ND_PRINT((ndo, "[msgFlags size %d]", elem.asnlen));
1767 return;
1768 }
1769 flags = elem.data.str[0];
1770 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1771 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1772 ND_PRINT((ndo, "[msgFlags=0x%02X]", flags));
1773 return;
1774 }
1775 length -= count;
1776 np += count;
1777
1778 ND_PRINT((ndo, "F=%s%s%s ",
1779 flags & 0x01 ? "a" : "",
1780 flags & 0x02 ? "p" : "",
1781 flags & 0x04 ? "r" : ""));
1782
1783 /* msgSecurityModel (INTEGER) */
1784 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1785 return;
1786 if (elem.type != BE_INT) {
1787 ND_PRINT((ndo, "[msgSecurityModel!=INT]"));
1788 asn1_print(ndo, &elem);
1789 return;
1790 }
1791 model = elem.data.integer;
1792 length -= count;
1793 np += count;
1794
1795 if ((u_int)count < length)
1796 ND_PRINT((ndo, "[%d extra after message SEQ]", length - count));
1797
1798 if (ndo->ndo_vflag) {
1799 ND_PRINT((ndo, "} "));
1800 }
1801
1802 if (model == 3) {
1803 if (ndo->ndo_vflag) {
1804 ND_PRINT((ndo, "{ USM "));
1805 }
1806 } else {
1807 ND_PRINT((ndo, "[security model %d]", model));
1808 return;
1809 }
1810
1811 np = xnp + (np - xnp);
1812 length = xlength - (np - xnp);
1813
1814 /* msgSecurityParameters (OCTET STRING) */
1815 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1816 return;
1817 if (elem.type != BE_STR) {
1818 ND_PRINT((ndo, "[msgSecurityParameters!=STR]"));
1819 asn1_print(ndo, &elem);
1820 return;
1821 }
1822 length -= count;
1823 np += count;
1824
1825 if (model == 3) {
1826 usm_print(ndo, elem.data.str, elem.asnlen);
1827 if (ndo->ndo_vflag) {
1828 ND_PRINT((ndo, "} "));
1829 }
1830 }
1831
1832 if (ndo->ndo_vflag) {
1833 ND_PRINT((ndo, "{ ScopedPDU "));
1834 }
1835
1836 scopedpdu_print(ndo, np, length, 3);
1837
1838 if (ndo->ndo_vflag) {
1839 ND_PRINT((ndo, "} "));
1840 }
1841 }
1842
1843 /*
1844 * Decode SNMP header and pass on to PDU printing routines
1845 */
1846 void
1847 snmp_print(netdissect_options *ndo,
1848 const u_char *np, u_int length)
1849 {
1850 struct be elem;
1851 int count = 0;
1852 int version = 0;
1853
1854 ND_PRINT((ndo, " "));
1855
1856 /* initial Sequence */
1857 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1858 return;
1859 if (elem.type != BE_SEQ) {
1860 ND_PRINT((ndo, "[!init SEQ]"));
1861 asn1_print(ndo, &elem);
1862 return;
1863 }
1864 if ((u_int)count < length)
1865 ND_PRINT((ndo, "[%d extra after iSEQ]", length - count));
1866 /* descend */
1867 length = elem.asnlen;
1868 np = (const u_char *)elem.data.raw;
1869
1870 /* Version (INTEGER) */
1871 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1872 return;
1873 if (elem.type != BE_INT) {
1874 ND_PRINT((ndo, "[version!=INT]"));
1875 asn1_print(ndo, &elem);
1876 return;
1877 }
1878
1879 switch (elem.data.integer) {
1880 case SNMP_VERSION_1:
1881 case SNMP_VERSION_2:
1882 case SNMP_VERSION_3:
1883 if (ndo->ndo_vflag)
1884 ND_PRINT((ndo, "{ %s ", SnmpVersion[elem.data.integer]));
1885 break;
1886 default:
1887 ND_PRINT((ndo, "[version = %d]", elem.data.integer));
1888 return;
1889 }
1890 version = elem.data.integer;
1891 length -= count;
1892 np += count;
1893
1894 switch (version) {
1895 case SNMP_VERSION_1:
1896 case SNMP_VERSION_2:
1897 community_print(ndo, np, length, version);
1898 break;
1899 case SNMP_VERSION_3:
1900 v3msg_print(ndo, np, length);
1901 break;
1902 default:
1903 ND_PRINT((ndo, "[version = %d]", elem.data.integer));
1904 break;
1905 }
1906
1907 if (ndo->ndo_vflag) {
1908 ND_PRINT((ndo, "} "));
1909 }
1910 }