]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
CI: Add warning exemptions for Sun C (suncc-5.14) on Solaris 10
[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 #include <config.h>
62
63 #include "netdissect-stdinc.h"
64
65 #include <stdio.h>
66 #include <string.h>
67 #include <limits.h>
68
69 #ifdef USE_LIBSMI
70 #include <smi.h>
71 #endif
72
73 #include "netdissect-ctype.h"
74
75 #define ND_LONGJMP_FROM_TCHECK
76 #include "netdissect.h"
77 #include "extract.h"
78
79 #undef OPAQUE /* defined in <wingdi.h> */
80
81
82 /*
83 * Universal ASN.1 types
84 * (we only care about the tag values for those allowed in the Internet SMI)
85 */
86 static const char *Universal[] = {
87 "U-0",
88 "Boolean",
89 "Integer",
90 #define INTEGER 2
91 "Bitstring",
92 "String",
93 #define STRING 4
94 "Null",
95 #define ASN_NULL 5
96 "ObjID",
97 #define OBJECTID 6
98 "ObjectDes",
99 "U-8","U-9","U-10","U-11", /* 8-11 */
100 "U-12","U-13","U-14","U-15", /* 12-15 */
101 "Sequence",
102 #define SEQUENCE 16
103 "Set"
104 };
105
106 /*
107 * Application-wide ASN.1 types from the Internet SMI and their tags
108 */
109 static const char *Application[] = {
110 "IpAddress",
111 #define IPADDR 0
112 "Counter",
113 #define COUNTER 1
114 "Gauge",
115 #define GAUGE 2
116 "TimeTicks",
117 #define TIMETICKS 3
118 "Opaque",
119 #define OPAQUE 4
120 "C-5",
121 "Counter64"
122 #define COUNTER64 6
123 };
124
125 /*
126 * Context-specific ASN.1 types for the SNMP PDUs and their tags
127 */
128 static const char *Context[] = {
129 "GetRequest",
130 #define GETREQ 0
131 "GetNextRequest",
132 #define GETNEXTREQ 1
133 "GetResponse",
134 #define GETRESP 2
135 "SetRequest",
136 #define SETREQ 3
137 "Trap",
138 #define TRAP 4
139 "GetBulk",
140 #define GETBULKREQ 5
141 "Inform",
142 #define INFORMREQ 6
143 "V2Trap",
144 #define V2TRAP 7
145 "Report"
146 #define REPORT 8
147 };
148
149 #define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ)
150 #define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
151 #define WRITE_CLASS(x) (x == SETREQ)
152 #define RESPONSE_CLASS(x) (x == GETRESP)
153 #define INTERNAL_CLASS(x) (x == REPORT)
154
155 /*
156 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
157 */
158 static const char *Exceptions[] = {
159 "noSuchObject",
160 #define NOSUCHOBJECT 0
161 "noSuchInstance",
162 #define NOSUCHINSTANCE 1
163 "endOfMibView",
164 #define ENDOFMIBVIEW 2
165 };
166
167 /*
168 * Private ASN.1 types
169 * The Internet SMI does not specify any
170 */
171 static const char *Private[] = {
172 "P-0"
173 };
174
175 /*
176 * error-status values for any SNMP PDU
177 */
178 static const char *ErrorStatus[] = {
179 "noError",
180 "tooBig",
181 "noSuchName",
182 "badValue",
183 "readOnly",
184 "genErr",
185 "noAccess",
186 "wrongType",
187 "wrongLength",
188 "wrongEncoding",
189 "wrongValue",
190 "noCreation",
191 "inconsistentValue",
192 "resourceUnavailable",
193 "commitFailed",
194 "undoFailed",
195 "authorizationError",
196 "notWritable",
197 "inconsistentName"
198 };
199 #define DECODE_ErrorStatus(e) \
200 ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
201 ? ErrorStatus[e] \
202 : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
203
204 /*
205 * generic-trap values in the SNMP Trap-PDU
206 */
207 static const char *GenericTrap[] = {
208 "coldStart",
209 "warmStart",
210 "linkDown",
211 "linkUp",
212 "authenticationFailure",
213 "egpNeighborLoss",
214 "enterpriseSpecific"
215 #define GT_ENTERPRISE 6
216 };
217 #define DECODE_GenericTrap(t) \
218 ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
219 ? GenericTrap[t] \
220 : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
221
222 /*
223 * ASN.1 type class table
224 * Ties together the preceding Universal, Application, Context, and Private
225 * type definitions.
226 */
227 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
228 static const struct {
229 const char *name;
230 const char **Id;
231 int numIDs;
232 } Class[] = {
233 defineCLASS(Universal),
234 #define UNIVERSAL 0
235 defineCLASS(Application),
236 #define APPLICATION 1
237 defineCLASS(Context),
238 #define CONTEXT 2
239 defineCLASS(Private),
240 #define PRIVATE 3
241 defineCLASS(Exceptions),
242 #define EXCEPTIONS 4
243 };
244
245 /*
246 * defined forms for ASN.1 types
247 */
248 static const char *Form[] = {
249 "Primitive",
250 #define PRIMITIVE 0
251 "Constructed",
252 #define CONSTRUCTED 1
253 };
254
255 /*
256 * A structure for the OID tree for the compiled-in MIB.
257 * This is stored as a general-order tree.
258 */
259 static struct obj {
260 const char *desc; /* name of object */
261 u_char oid; /* sub-id following parent */
262 u_char type; /* object type (unused) */
263 struct obj *child, *next; /* child and next sibling pointers */
264 } *objp = NULL;
265
266 /*
267 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
268 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
269 * a value for `mibroot'.
270 *
271 * In particular, this is gross, as this is including initialized structures,
272 * and by right shouldn't be an "include" file.
273 */
274 #include "mib.h"
275
276 /*
277 * This defines a list of OIDs which will be abbreviated on output.
278 * Currently, this includes the prefixes for the Internet MIB, the
279 * private enterprises tree, and the experimental tree.
280 */
281 #define OID_FIRST_OCTET(x, y) (((x)*40) + (y)) /* X.690 8.19.4 */
282
283 #ifndef NO_ABREV_MIB
284 static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
285 #endif
286 #ifndef NO_ABREV_ENTER
287 static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
288 #endif
289 #ifndef NO_ABREV_EXPERI
290 static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
291 #endif
292 #ifndef NO_ABBREV_SNMPMODS
293 static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
294 #endif
295
296 #define OBJ_ABBREV_ENTRY(prefix, obj) \
297 { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
298 static const struct obj_abrev {
299 const char *prefix; /* prefix for this abrev */
300 struct obj *node; /* pointer into object table */
301 const uint8_t *oid; /* ASN.1 encoded OID */
302 size_t oid_len; /* length of OID */
303 } obj_abrev_list[] = {
304 #ifndef NO_ABREV_MIB
305 /* .iso.org.dod.internet.mgmt.mib */
306 OBJ_ABBREV_ENTRY("", mib),
307 #endif
308 #ifndef NO_ABREV_ENTER
309 /* .iso.org.dod.internet.private.enterprises */
310 OBJ_ABBREV_ENTRY("E:", enterprises),
311 #endif
312 #ifndef NO_ABREV_EXPERI
313 /* .iso.org.dod.internet.experimental */
314 OBJ_ABBREV_ENTRY("X:", experimental),
315 #endif
316 #ifndef NO_ABBREV_SNMPMODS
317 /* .iso.org.dod.internet.snmpV2.snmpModules */
318 OBJ_ABBREV_ENTRY("S:", snmpModules),
319 #endif
320 { 0,0,0,0 }
321 };
322
323 /*
324 * This is used in the OID print routine to walk down the object tree
325 * rooted at `mibroot'.
326 */
327 #define OBJ_PRINT(o, suppressdot) \
328 { \
329 if (objp) { \
330 do { \
331 if ((o) == objp->oid) \
332 break; \
333 } while ((objp = objp->next) != NULL); \
334 } \
335 if (objp) { \
336 ND_PRINT(suppressdot?"%s":".%s", objp->desc); \
337 objp = objp->child; \
338 } else \
339 ND_PRINT(suppressdot?"%u":".%u", (o)); \
340 }
341
342 /*
343 * This is the definition for the Any-Data-Type storage used purely for
344 * temporary internal representation while decoding an ASN.1 data stream.
345 */
346 struct be {
347 uint32_t asnlen;
348 union {
349 const uint8_t *raw;
350 int32_t integer;
351 uint32_t uns;
352 const u_char *str;
353 uint64_t uns64;
354 } data;
355 u_short id;
356 u_char form, class; /* tag info */
357 u_char type;
358 #define BE_ANY 255
359 #define BE_NONE 0
360 #define BE_NULL 1
361 #define BE_OCTET 2
362 #define BE_OID 3
363 #define BE_INT 4
364 #define BE_UNS 5
365 #define BE_STR 6
366 #define BE_SEQ 7
367 #define BE_INETADDR 8
368 #define BE_PDU 9
369 #define BE_UNS64 10
370 #define BE_NOSUCHOBJECT 128
371 #define BE_NOSUCHINST 129
372 #define BE_ENDOFMIBVIEW 130
373 };
374
375 /*
376 * SNMP versions recognized by this module
377 */
378 static const char *SnmpVersion[] = {
379 "SNMPv1",
380 #define SNMP_VERSION_1 0
381 "SNMPv2c",
382 #define SNMP_VERSION_2 1
383 "SNMPv2u",
384 #define SNMP_VERSION_2U 2
385 "SNMPv3"
386 #define SNMP_VERSION_3 3
387 };
388
389 /*
390 * Defaults for SNMP PDU components
391 */
392 #define DEF_COMMUNITY "public"
393
394 /*
395 * constants for ASN.1 decoding
396 */
397 #define OIDMUX 40
398 #define ASNLEN_INETADDR 4
399 #define ASN_SHIFT7 7
400 #define ASN_SHIFT8 8
401 #define ASN_BIT8 0x80
402 #define ASN_LONGLEN 0x80
403
404 #define ASN_ID_BITS 0x1f
405 #define ASN_FORM_BITS 0x20
406 #define ASN_FORM_SHIFT 5
407 #define ASN_CLASS_BITS 0xc0
408 #define ASN_CLASS_SHIFT 6
409
410 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
411
412 /*
413 * This decodes the next ASN.1 object in the stream pointed to by "p"
414 * (and of real-length "len") and stores the intermediate data in the
415 * provided BE object.
416 *
417 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
418 * O/w, this returns the number of bytes parsed from "p".
419 */
420 static int
421 asn1_parse(netdissect_options *ndo,
422 const u_char *p, u_int len, struct be *elem)
423 {
424 u_char form, class, id;
425 u_int i, hdr;
426
427 elem->asnlen = 0;
428 elem->type = BE_ANY;
429 if (len < 1) {
430 ND_PRINT("[nothing to parse]");
431 goto invalid;
432 }
433
434 /*
435 * it would be nice to use a bit field, but you can't depend on them.
436 * +---+---+---+---+---+---+---+---+
437 * + class |frm| id |
438 * +---+---+---+---+---+---+---+---+
439 * 7 6 5 4 3 2 1 0
440 */
441 id = GET_U_1(p) & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
442 #ifdef notdef
443 form = (GET_U_1(p) & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
444 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
445 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
446 #else
447 form = (u_char)(GET_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
448 class = (u_char)(GET_U_1(p) & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
449 #endif
450 elem->form = form;
451 elem->class = class;
452 elem->id = id;
453 p++; len--; hdr = 1;
454 /* extended tag field */
455 if (id == ASN_ID_EXT) {
456 /*
457 * The ID follows, as a sequence of octets with the
458 * 8th bit set and the remaining 7 bits being
459 * the next 7 bits of the value, terminated with
460 * an octet with the 8th bit not set.
461 *
462 * First, assemble all the octets with the 8th
463 * bit set. XXX - this doesn't handle a value
464 * that won't fit in 32 bits.
465 */
466 id = 0;
467 while (GET_U_1(p) & ASN_BIT8) {
468 if (len < 1) {
469 ND_PRINT("[Xtagfield?]");
470 goto invalid;
471 }
472 id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8);
473 len--;
474 hdr++;
475 p++;
476 }
477 if (len < 1) {
478 ND_PRINT("[Xtagfield?]");
479 goto invalid;
480 }
481 elem->id = id = (id << 7) | GET_U_1(p);
482 --len;
483 ++hdr;
484 ++p;
485 }
486 if (len < 1) {
487 ND_PRINT("[no asnlen]");
488 goto invalid;
489 }
490 elem->asnlen = GET_U_1(p);
491 p++; len--; hdr++;
492 if (elem->asnlen & ASN_BIT8) {
493 uint32_t noct = elem->asnlen % ASN_BIT8;
494 elem->asnlen = 0;
495 if (len < noct) {
496 ND_PRINT("[asnlen? %d<%d]", len, noct);
497 goto invalid;
498 }
499 for (; noct != 0; len--, hdr++, noct--) {
500 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
501 p++;
502 }
503 }
504 if (len < elem->asnlen) {
505 ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
506 goto invalid;
507 }
508 if (form >= sizeof(Form)/sizeof(Form[0])) {
509 ND_PRINT("[form?%d]", form);
510 goto invalid;
511 }
512 if (class >= sizeof(Class)/sizeof(Class[0])) {
513 ND_PRINT("[class?%c/%d]", *Form[form], class);
514 goto invalid;
515 }
516 if ((int)id >= Class[class].numIDs) {
517 ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
518 goto invalid;
519 }
520 ND_TCHECK_LEN(p, elem->asnlen);
521
522 switch (form) {
523 case PRIMITIVE:
524 switch (class) {
525 case UNIVERSAL:
526 switch (id) {
527 case STRING:
528 elem->type = BE_STR;
529 elem->data.str = p;
530 break;
531
532 case INTEGER: {
533 uint32_t data;
534 elem->type = BE_INT;
535 data = 0;
536
537 if (elem->asnlen == 0) {
538 ND_PRINT("[asnlen=0]");
539 goto invalid;
540 }
541 if (GET_U_1(p) & ASN_BIT8) /* negative */
542 data = UINT_MAX;
543 for (i = elem->asnlen; i != 0; p++, i--)
544 data = (data << ASN_SHIFT8) | GET_U_1(p);
545 elem->data.integer = data;
546 break;
547 }
548
549 case OBJECTID:
550 elem->type = BE_OID;
551 elem->data.raw = (const uint8_t *)p;
552 break;
553
554 case ASN_NULL:
555 elem->type = BE_NULL;
556 elem->data.raw = NULL;
557 break;
558
559 default:
560 elem->type = BE_OCTET;
561 elem->data.raw = (const uint8_t *)p;
562 ND_PRINT("[P/U/%s]", Class[class].Id[id]);
563 break;
564 }
565 break;
566
567 case APPLICATION:
568 switch (id) {
569 case IPADDR:
570 elem->type = BE_INETADDR;
571 elem->data.raw = (const uint8_t *)p;
572 break;
573
574 case COUNTER:
575 case GAUGE:
576 case TIMETICKS: {
577 uint32_t data;
578 elem->type = BE_UNS;
579 data = 0;
580 for (i = elem->asnlen; i != 0; p++, i--)
581 data = (data << 8) + GET_U_1(p);
582 elem->data.uns = data;
583 break;
584 }
585
586 case COUNTER64: {
587 uint64_t data64;
588 elem->type = BE_UNS64;
589 data64 = 0;
590 for (i = elem->asnlen; i != 0; p++, i--)
591 data64 = (data64 << 8) + GET_U_1(p);
592 elem->data.uns64 = data64;
593 break;
594 }
595
596 default:
597 elem->type = BE_OCTET;
598 elem->data.raw = (const uint8_t *)p;
599 ND_PRINT("[P/A/%s]",
600 Class[class].Id[id]);
601 break;
602 }
603 break;
604
605 case CONTEXT:
606 switch (id) {
607 case NOSUCHOBJECT:
608 elem->type = BE_NOSUCHOBJECT;
609 elem->data.raw = NULL;
610 break;
611
612 case NOSUCHINSTANCE:
613 elem->type = BE_NOSUCHINST;
614 elem->data.raw = NULL;
615 break;
616
617 case ENDOFMIBVIEW:
618 elem->type = BE_ENDOFMIBVIEW;
619 elem->data.raw = NULL;
620 break;
621 }
622 break;
623
624 default:
625 ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
626 elem->type = BE_OCTET;
627 elem->data.raw = (const uint8_t *)p;
628 break;
629 }
630 break;
631
632 case CONSTRUCTED:
633 switch (class) {
634 case UNIVERSAL:
635 switch (id) {
636 case SEQUENCE:
637 elem->type = BE_SEQ;
638 elem->data.raw = (const uint8_t *)p;
639 break;
640
641 default:
642 elem->type = BE_OCTET;
643 elem->data.raw = (const uint8_t *)p;
644 ND_PRINT("C/U/%s", Class[class].Id[id]);
645 break;
646 }
647 break;
648
649 case CONTEXT:
650 elem->type = BE_PDU;
651 elem->data.raw = (const uint8_t *)p;
652 break;
653
654 default:
655 elem->type = BE_OCTET;
656 elem->data.raw = (const uint8_t *)p;
657 ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
658 break;
659 }
660 break;
661 }
662 p += elem->asnlen;
663 len -= elem->asnlen;
664 return elem->asnlen + hdr;
665
666 invalid:
667 return -1;
668 }
669
670 static void
671 asn1_print_octets(netdissect_options *ndo, struct be *elem)
672 {
673 const u_char *p = (const u_char *)elem->data.raw;
674 uint32_t asnlen = elem->asnlen;
675 uint32_t i;
676
677 for (i = asnlen; i != 0; p++, i--)
678 ND_PRINT("_%.2x", GET_U_1(p));
679 }
680
681 static void
682 asn1_print_string(netdissect_options *ndo, struct be *elem)
683 {
684 int printable = 1, first = 1;
685 const u_char *p;
686 uint32_t asnlen = elem->asnlen;
687 uint32_t i;
688
689 p = elem->data.str;
690 for (i = asnlen; printable && i != 0; p++, i--)
691 printable = ND_ASCII_ISPRINT(GET_U_1(p));
692 p = elem->data.str;
693 if (printable) {
694 ND_PRINT("\"");
695 nd_printjn(ndo, p, asnlen);
696 ND_PRINT("\"");
697 } else {
698 for (i = asnlen; i != 0; p++, i--) {
699 ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
700 first = 0;
701 }
702 }
703 }
704
705 /*
706 * Display the ASN.1 object represented by the BE object.
707 * This used to be an integral part of asn1_parse() before the intermediate
708 * BE form was added.
709 */
710 static int
711 asn1_print(netdissect_options *ndo,
712 struct be *elem)
713 {
714 const u_char *p;
715 uint32_t asnlen = elem->asnlen;
716 uint32_t i;
717
718 switch (elem->type) {
719
720 case BE_OCTET:
721 asn1_print_octets(ndo, elem);
722 break;
723
724 case BE_NULL:
725 break;
726
727 case BE_OID: {
728 int first = -1;
729 uint32_t o = 0;
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("[notNotifiable]");
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;
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 while (length) {
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 }