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