]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
Fix spelling
[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 #include <limits.h>
70
71 #ifdef USE_LIBSMI
72 #include <smi.h>
73 #endif
74
75 #include "netdissect-ctype.h"
76
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 return -1;
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 return -1;
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 return -1;
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 return -1;
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 return -1;
499 }
500 ND_TCHECK_LEN(p, noct);
501 for (; noct != 0; len--, hdr++, noct--) {
502 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
503 p++;
504 }
505 }
506 if (len < elem->asnlen) {
507 ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
508 return -1;
509 }
510 if (form >= sizeof(Form)/sizeof(Form[0])) {
511 ND_PRINT("[form?%d]", form);
512 return -1;
513 }
514 if (class >= sizeof(Class)/sizeof(Class[0])) {
515 ND_PRINT("[class?%c/%d]", *Form[form], class);
516 return -1;
517 }
518 if ((int)id >= Class[class].numIDs) {
519 ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
520 return -1;
521 }
522 ND_TCHECK_LEN(p, elem->asnlen);
523
524 switch (form) {
525 case PRIMITIVE:
526 switch (class) {
527 case UNIVERSAL:
528 switch (id) {
529 case STRING:
530 elem->type = BE_STR;
531 elem->data.str = p;
532 break;
533
534 case INTEGER: {
535 uint32_t data;
536 elem->type = BE_INT;
537 data = 0;
538
539 if (elem->asnlen == 0) {
540 ND_PRINT("[asnlen=0]");
541 return -1;
542 }
543 if (GET_U_1(p) & ASN_BIT8) /* negative */
544 data = UINT_MAX;
545 for (i = elem->asnlen; i != 0; p++, i--)
546 data = (data << ASN_SHIFT8) | GET_U_1(p);
547 elem->data.integer = data;
548 break;
549 }
550
551 case OBJECTID:
552 elem->type = BE_OID;
553 elem->data.raw = (const uint8_t *)p;
554 break;
555
556 case ASN_NULL:
557 elem->type = BE_NULL;
558 elem->data.raw = NULL;
559 break;
560
561 default:
562 elem->type = BE_OCTET;
563 elem->data.raw = (const uint8_t *)p;
564 ND_PRINT("[P/U/%s]", Class[class].Id[id]);
565 break;
566 }
567 break;
568
569 case APPLICATION:
570 switch (id) {
571 case IPADDR:
572 elem->type = BE_INETADDR;
573 elem->data.raw = (const uint8_t *)p;
574 break;
575
576 case COUNTER:
577 case GAUGE:
578 case TIMETICKS: {
579 uint32_t data;
580 elem->type = BE_UNS;
581 data = 0;
582 for (i = elem->asnlen; i != 0; p++, i--)
583 data = (data << 8) + GET_U_1(p);
584 elem->data.uns = data;
585 break;
586 }
587
588 case COUNTER64: {
589 uint64_t data64;
590 elem->type = BE_UNS64;
591 data64 = 0;
592 for (i = elem->asnlen; i != 0; p++, i--)
593 data64 = (data64 << 8) + GET_U_1(p);
594 elem->data.uns64 = data64;
595 break;
596 }
597
598 default:
599 elem->type = BE_OCTET;
600 elem->data.raw = (const uint8_t *)p;
601 ND_PRINT("[P/A/%s]",
602 Class[class].Id[id]);
603 break;
604 }
605 break;
606
607 case CONTEXT:
608 switch (id) {
609 case NOSUCHOBJECT:
610 elem->type = BE_NOSUCHOBJECT;
611 elem->data.raw = NULL;
612 break;
613
614 case NOSUCHINSTANCE:
615 elem->type = BE_NOSUCHINST;
616 elem->data.raw = NULL;
617 break;
618
619 case ENDOFMIBVIEW:
620 elem->type = BE_ENDOFMIBVIEW;
621 elem->data.raw = NULL;
622 break;
623 }
624 break;
625
626 default:
627 ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
628 elem->type = BE_OCTET;
629 elem->data.raw = (const uint8_t *)p;
630 break;
631 }
632 break;
633
634 case CONSTRUCTED:
635 switch (class) {
636 case UNIVERSAL:
637 switch (id) {
638 case SEQUENCE:
639 elem->type = BE_SEQ;
640 elem->data.raw = (const uint8_t *)p;
641 break;
642
643 default:
644 elem->type = BE_OCTET;
645 elem->data.raw = (const uint8_t *)p;
646 ND_PRINT("C/U/%s", Class[class].Id[id]);
647 break;
648 }
649 break;
650
651 case CONTEXT:
652 elem->type = BE_PDU;
653 elem->data.raw = (const uint8_t *)p;
654 break;
655
656 default:
657 elem->type = BE_OCTET;
658 elem->data.raw = (const uint8_t *)p;
659 ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
660 break;
661 }
662 break;
663 }
664 p += elem->asnlen;
665 len -= elem->asnlen;
666 return elem->asnlen + hdr;
667
668 trunc:
669 nd_print_trunc(ndo);
670 return -1;
671 }
672
673 static int
674 asn1_print_octets(netdissect_options *ndo, struct be *elem)
675 {
676 const u_char *p = (const u_char *)elem->data.raw;
677 uint32_t asnlen = elem->asnlen;
678 uint32_t i;
679
680 ND_TCHECK_LEN(p, asnlen);
681 for (i = asnlen; i != 0; p++, i--)
682 ND_PRINT("_%.2x", GET_U_1(p));
683 return 0;
684
685 trunc:
686 nd_print_trunc(ndo);
687 return -1;
688 }
689
690 static int
691 asn1_print_string(netdissect_options *ndo, struct be *elem)
692 {
693 int printable = 1, first = 1;
694 const u_char *p;
695 uint32_t asnlen = elem->asnlen;
696 uint32_t i;
697
698 p = elem->data.str;
699 ND_TCHECK_LEN(p, asnlen);
700 for (i = asnlen; printable && i != 0; p++, i--)
701 printable = ND_ASCII_ISPRINT(GET_U_1(p));
702 p = elem->data.str;
703 if (printable) {
704 ND_PRINT("\"");
705 if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
706 ND_PRINT("\"");
707 goto trunc;
708 }
709 ND_PRINT("\"");
710 } else {
711 for (i = asnlen; i != 0; p++, i--) {
712 ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
713 first = 0;
714 }
715 }
716 return 0;
717
718 trunc:
719 nd_print_trunc(ndo);
720 return -1;
721 }
722
723 /*
724 * Display the ASN.1 object represented by the BE object.
725 * This used to be an integral part of asn1_parse() before the intermediate
726 * BE form was added.
727 */
728 static int
729 asn1_print(netdissect_options *ndo,
730 struct be *elem)
731 {
732 const u_char *p;
733 uint32_t asnlen = elem->asnlen;
734 uint32_t i;
735
736 switch (elem->type) {
737
738 case BE_OCTET:
739 if (asn1_print_octets(ndo, elem) == -1)
740 return -1;
741 break;
742
743 case BE_NULL:
744 break;
745
746 case BE_OID: {
747 int first = -1;
748 uint32_t o = 0;
749
750 p = (const u_char *)elem->data.raw;
751 i = asnlen;
752 if (!ndo->ndo_nflag && asnlen > 2) {
753 const struct obj_abrev *a = &obj_abrev_list[0];
754 for (; a->node; a++) {
755 if (i < a->oid_len)
756 continue;
757 if (!ND_TTEST_LEN(p, a->oid_len))
758 continue;
759 if (memcmp(a->oid, p, a->oid_len) == 0) {
760 objp = a->node->child;
761 i -= a->oid_len;
762 p += a->oid_len;
763 ND_PRINT("%s", a->prefix);
764 first = 1;
765 break;
766 }
767 }
768 }
769
770 for (; i != 0; p++, i--) {
771 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
772 if (GET_U_1(p) & ASN_LONGLEN)
773 continue;
774
775 /*
776 * first subitem encodes two items with
777 * 1st*OIDMUX+2nd
778 * (see X.690:1997 clause 8.19 for the details)
779 */
780 if (first < 0) {
781 int s;
782 if (!ndo->ndo_nflag)
783 objp = mibroot;
784 first = 0;
785 s = o / OIDMUX;
786 if (s > 2) s = 2;
787 OBJ_PRINT(s, first);
788 o -= s * OIDMUX;
789 }
790 OBJ_PRINT(o, first);
791 if (--first < 0)
792 first = 0;
793 o = 0;
794 }
795 break;
796 }
797
798 case BE_INT:
799 ND_PRINT("%d", elem->data.integer);
800 break;
801
802 case BE_UNS:
803 ND_PRINT("%u", elem->data.uns);
804 break;
805
806 case BE_UNS64:
807 ND_PRINT("%" PRIu64, elem->data.uns64);
808 break;
809
810 case BE_STR:
811 if (asn1_print_string(ndo, elem) == -1)
812 return -1;
813 break;
814
815 case BE_SEQ:
816 ND_PRINT("Seq(%u)", elem->asnlen);
817 break;
818
819 case BE_INETADDR:
820 if (asnlen != ASNLEN_INETADDR)
821 ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
822 p = (const u_char *)elem->data.raw;
823 ND_TCHECK_LEN(p, asnlen);
824 for (i = asnlen; i != 0; p++, i--) {
825 ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
826 }
827 break;
828
829 case BE_NOSUCHOBJECT:
830 case BE_NOSUCHINST:
831 case BE_ENDOFMIBVIEW:
832 ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
833 break;
834
835 case BE_PDU:
836 ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
837 break;
838
839 case BE_ANY:
840 ND_PRINT("[BE_ANY!?]");
841 break;
842
843 default:
844 ND_PRINT("[be!?]");
845 break;
846 }
847 return 0;
848
849 trunc:
850 nd_print_trunc(ndo);
851 return -1;
852 }
853
854 #ifdef notdef
855 /*
856 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
857 * This will work for any ASN.1 stream, not just an SNMP PDU.
858 *
859 * By adding newlines and spaces at the correct places, this would print in
860 * Rose-Normal-Form.
861 *
862 * This is not currently used.
863 */
864 static void
865 asn1_decode(u_char *p, u_int length)
866 {
867 struct be elem;
868 int i = 0;
869
870 while (i >= 0 && length > 0) {
871 i = asn1_parse(ndo, p, length, &elem);
872 if (i >= 0) {
873 ND_PRINT(" ");
874 if (asn1_print(ndo, &elem) < 0)
875 return;
876 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
877 ND_PRINT(" {");
878 asn1_decode(elem.data.raw, elem.asnlen);
879 ND_PRINT(" }");
880 }
881 length -= i;
882 p += i;
883 }
884 }
885 }
886 #endif
887
888 #ifdef USE_LIBSMI
889
890 struct smi2be {
891 SmiBasetype basetype;
892 int be;
893 };
894
895 static const struct smi2be smi2betab[] = {
896 { SMI_BASETYPE_INTEGER32, BE_INT },
897 { SMI_BASETYPE_OCTETSTRING, BE_STR },
898 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
899 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
900 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
901 { SMI_BASETYPE_INTEGER64, BE_NONE },
902 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
903 { SMI_BASETYPE_FLOAT32, BE_NONE },
904 { SMI_BASETYPE_FLOAT64, BE_NONE },
905 { SMI_BASETYPE_FLOAT128, BE_NONE },
906 { SMI_BASETYPE_ENUM, BE_INT },
907 { SMI_BASETYPE_BITS, BE_STR },
908 { SMI_BASETYPE_UNKNOWN, BE_NONE }
909 };
910
911 static int
912 smi_decode_oid(netdissect_options *ndo,
913 struct be *elem, unsigned int *oid,
914 unsigned int oidsize, unsigned int *oidlen)
915 {
916 const u_char *p = (const u_char *)elem->data.raw;
917 uint32_t asnlen = elem->asnlen;
918 uint32_t i = asnlen;
919 int o = 0, first = -1;
920 unsigned int firstval;
921
922 for (*oidlen = 0; i != 0; p++, i--) {
923 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
924 if (GET_U_1(p) & ASN_LONGLEN)
925 continue;
926
927 /*
928 * first subitem encodes two items with 1st*OIDMUX+2nd
929 * (see X.690:1997 clause 8.19 for the details)
930 */
931 if (first < 0) {
932 first = 0;
933 firstval = o / OIDMUX;
934 if (firstval > 2) firstval = 2;
935 o -= firstval * OIDMUX;
936 if (*oidlen < oidsize) {
937 oid[(*oidlen)++] = firstval;
938 }
939 }
940 if (*oidlen < oidsize) {
941 oid[(*oidlen)++] = o;
942 }
943 o = 0;
944 }
945 return 0;
946 }
947
948 static int smi_check_type(SmiBasetype basetype, int be)
949 {
950 int i;
951
952 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
953 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
954 return 1;
955 }
956 }
957
958 return 0;
959 }
960
961 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
962 struct be *elem)
963 {
964 int ok = 1;
965
966 switch (smiType->basetype) {
967 case SMI_BASETYPE_OBJECTIDENTIFIER:
968 case SMI_BASETYPE_OCTETSTRING:
969 if (smiRange->minValue.value.unsigned32
970 == smiRange->maxValue.value.unsigned32) {
971 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
972 } else {
973 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
974 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
975 }
976 break;
977
978 case SMI_BASETYPE_INTEGER32:
979 ok = (elem->data.integer >= smiRange->minValue.value.integer32
980 && elem->data.integer <= smiRange->maxValue.value.integer32);
981 break;
982
983 case SMI_BASETYPE_UNSIGNED32:
984 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
985 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
986 break;
987
988 case SMI_BASETYPE_UNSIGNED64:
989 /* XXX */
990 break;
991
992 /* case SMI_BASETYPE_INTEGER64: SMIng */
993 /* case SMI_BASETYPE_FLOAT32: SMIng */
994 /* case SMI_BASETYPE_FLOAT64: SMIng */
995 /* case SMI_BASETYPE_FLOAT128: SMIng */
996
997 case SMI_BASETYPE_ENUM:
998 case SMI_BASETYPE_BITS:
999 case SMI_BASETYPE_UNKNOWN:
1000 ok = 1;
1001 break;
1002
1003 default:
1004 ok = 0;
1005 break;
1006 }
1007
1008 return ok;
1009 }
1010
1011 static int smi_check_range(SmiType *smiType, struct be *elem)
1012 {
1013 SmiRange *smiRange;
1014 int ok = 1;
1015
1016 for (smiRange = smiGetFirstRange(smiType);
1017 smiRange;
1018 smiRange = smiGetNextRange(smiRange)) {
1019
1020 ok = smi_check_a_range(smiType, smiRange, elem);
1021
1022 if (ok) {
1023 break;
1024 }
1025 }
1026
1027 if (ok) {
1028 SmiType *parentType;
1029 parentType = smiGetParentType(smiType);
1030 if (parentType) {
1031 ok = smi_check_range(parentType, elem);
1032 }
1033 }
1034
1035 return ok;
1036 }
1037
1038 static SmiNode *
1039 smi_print_variable(netdissect_options *ndo,
1040 struct be *elem, int *status)
1041 {
1042 unsigned int oid[128], oidlen;
1043 SmiNode *smiNode = NULL;
1044 unsigned int i;
1045
1046 if (!nd_smi_module_loaded) {
1047 *status = asn1_print(ndo, elem);
1048 return NULL;
1049 }
1050 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1051 &oidlen);
1052 if (*status < 0)
1053 return NULL;
1054 smiNode = smiGetNodeByOID(oidlen, oid);
1055 if (! smiNode) {
1056 *status = asn1_print(ndo, elem);
1057 return NULL;
1058 }
1059 if (ndo->ndo_vflag) {
1060 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1061 }
1062 ND_PRINT("%s", smiNode->name);
1063 if (smiNode->oidlen < oidlen) {
1064 for (i = smiNode->oidlen; i < oidlen; i++) {
1065 ND_PRINT(".%u", oid[i]);
1066 }
1067 }
1068 *status = 0;
1069 return smiNode;
1070 }
1071
1072 static int
1073 smi_print_value(netdissect_options *ndo,
1074 SmiNode *smiNode, u_short pduid, struct be *elem)
1075 {
1076 unsigned int i, oid[128], oidlen;
1077 SmiType *smiType;
1078 SmiNamedNumber *nn;
1079 int done = 0;
1080
1081 if (! smiNode || ! (smiNode->nodekind
1082 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1083 return asn1_print(ndo, elem);
1084 }
1085
1086 if (elem->type == BE_NOSUCHOBJECT
1087 || elem->type == BE_NOSUCHINST
1088 || elem->type == BE_ENDOFMIBVIEW) {
1089 return asn1_print(ndo, elem);
1090 }
1091
1092 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1093 ND_PRINT("[notNotifiable]");
1094 }
1095
1096 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1097 ND_PRINT("[notReadable]");
1098 }
1099
1100 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1101 ND_PRINT("[notWritable]");
1102 }
1103
1104 if (RESPONSE_CLASS(pduid)
1105 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1106 ND_PRINT("[noAccess]");
1107 }
1108
1109 smiType = smiGetNodeType(smiNode);
1110 if (! smiType) {
1111 return asn1_print(ndo, elem);
1112 }
1113
1114 if (! smi_check_type(smiType->basetype, elem->type)) {
1115 ND_PRINT("[wrongType]");
1116 }
1117
1118 if (! smi_check_range(smiType, elem)) {
1119 ND_PRINT("[outOfRange]");
1120 }
1121
1122 /* resolve bits to named bits */
1123
1124 /* check whether instance identifier is valid */
1125
1126 /* apply display hints (integer, octetstring) */
1127
1128 /* convert instance identifier to index type values */
1129
1130 switch (elem->type) {
1131 case BE_OID:
1132 if (smiType->basetype == SMI_BASETYPE_BITS) {
1133 /* print bit labels */
1134 } else {
1135 if (nd_smi_module_loaded &&
1136 smi_decode_oid(ndo, elem, oid,
1137 sizeof(oid)/sizeof(unsigned int),
1138 &oidlen) == 0) {
1139 smiNode = smiGetNodeByOID(oidlen, oid);
1140 if (smiNode) {
1141 if (ndo->ndo_vflag) {
1142 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1143 }
1144 ND_PRINT("%s", smiNode->name);
1145 if (smiNode->oidlen < oidlen) {
1146 for (i = smiNode->oidlen;
1147 i < oidlen; i++) {
1148 ND_PRINT(".%u", oid[i]);
1149 }
1150 }
1151 done++;
1152 }
1153 }
1154 }
1155 break;
1156
1157 case BE_INT:
1158 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1159 for (nn = smiGetFirstNamedNumber(smiType);
1160 nn;
1161 nn = smiGetNextNamedNumber(nn)) {
1162 if (nn->value.value.integer32
1163 == elem->data.integer) {
1164 ND_PRINT("%s", nn->name);
1165 ND_PRINT("(%d)", elem->data.integer);
1166 done++;
1167 break;
1168 }
1169 }
1170 }
1171 break;
1172 }
1173
1174 if (! done) {
1175 return asn1_print(ndo, elem);
1176 }
1177 return 0;
1178 }
1179 #endif
1180
1181 /*
1182 * General SNMP header
1183 * SEQUENCE {
1184 * version INTEGER {version-1(0)},
1185 * community OCTET STRING,
1186 * data ANY -- PDUs
1187 * }
1188 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1189 * SEQUENCE {
1190 * request-id INTEGER,
1191 * error-status INTEGER,
1192 * error-index INTEGER,
1193 * varbindlist SEQUENCE OF
1194 * SEQUENCE {
1195 * name ObjectName,
1196 * value ObjectValue
1197 * }
1198 * }
1199 * PDU for Trap:
1200 * SEQUENCE {
1201 * enterprise OBJECT IDENTIFIER,
1202 * agent-addr NetworkAddress,
1203 * generic-trap INTEGER,
1204 * specific-trap INTEGER,
1205 * time-stamp TimeTicks,
1206 * varbindlist SEQUENCE OF
1207 * SEQUENCE {
1208 * name ObjectName,
1209 * value ObjectValue
1210 * }
1211 * }
1212 */
1213
1214 /*
1215 * Decode SNMP varBind
1216 */
1217 static void
1218 varbind_print(netdissect_options *ndo,
1219 u_short pduid, const u_char *np, u_int length)
1220 {
1221 struct be elem;
1222 int count = 0;
1223 #ifdef USE_LIBSMI
1224 SmiNode *smiNode = NULL;
1225 #endif
1226 int status;
1227
1228 /* Sequence of varBind */
1229 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1230 return;
1231 if (elem.type != BE_SEQ) {
1232 ND_PRINT("[!SEQ of varbind]");
1233 asn1_print(ndo, &elem);
1234 return;
1235 }
1236 if ((u_int)count < length)
1237 ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1238 /* descend */
1239 length = elem.asnlen;
1240 np = (const u_char *)elem.data.raw;
1241
1242 while (length) {
1243 const u_char *vbend;
1244 u_int vblength;
1245
1246 ND_PRINT(" ");
1247
1248 /* Sequence */
1249 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1250 return;
1251 if (elem.type != BE_SEQ) {
1252 ND_PRINT("[!varbind]");
1253 asn1_print(ndo, &elem);
1254 return;
1255 }
1256 vbend = np + count;
1257 vblength = length - count;
1258 /* descend */
1259 length = elem.asnlen;
1260 np = (const u_char *)elem.data.raw;
1261
1262 /* objName (OID) */
1263 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1264 return;
1265 if (elem.type != BE_OID) {
1266 ND_PRINT("[objName!=OID]");
1267 asn1_print(ndo, &elem);
1268 return;
1269 }
1270 #ifdef USE_LIBSMI
1271 smiNode = smi_print_variable(ndo, &elem, &status);
1272 #else
1273 status = asn1_print(ndo, &elem);
1274 #endif
1275 if (status < 0)
1276 return;
1277 length -= count;
1278 np += count;
1279
1280 if (pduid != GETREQ && pduid != GETNEXTREQ
1281 && pduid != GETBULKREQ)
1282 ND_PRINT("=");
1283
1284 /* objVal (ANY) */
1285 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1286 return;
1287 if (pduid == GETREQ || pduid == GETNEXTREQ
1288 || pduid == GETBULKREQ) {
1289 if (elem.type != BE_NULL) {
1290 ND_PRINT("[objVal!=NULL]");
1291 if (asn1_print(ndo, &elem) < 0)
1292 return;
1293 }
1294 } else {
1295 if (elem.type != BE_NULL) {
1296 #ifdef USE_LIBSMI
1297 status = smi_print_value(ndo, smiNode, pduid, &elem);
1298 #else
1299 status = asn1_print(ndo, &elem);
1300 #endif
1301 }
1302 if (status < 0)
1303 return;
1304 }
1305 length = vblength;
1306 np = vbend;
1307 }
1308 }
1309
1310 /*
1311 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1312 * GetBulk, Inform, V2Trap, and Report
1313 */
1314 static void
1315 snmppdu_print(netdissect_options *ndo,
1316 u_short pduid, const u_char *np, u_int length)
1317 {
1318 struct be elem;
1319 int count = 0, error_status;
1320
1321 /* reqId (Integer) */
1322 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1323 return;
1324 if (elem.type != BE_INT) {
1325 ND_PRINT("[reqId!=INT]");
1326 asn1_print(ndo, &elem);
1327 return;
1328 }
1329 if (ndo->ndo_vflag)
1330 ND_PRINT("R=%d ", elem.data.integer);
1331 length -= count;
1332 np += count;
1333
1334 /* errorStatus (Integer) */
1335 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1336 return;
1337 if (elem.type != BE_INT) {
1338 ND_PRINT("[errorStatus!=INT]");
1339 asn1_print(ndo, &elem);
1340 return;
1341 }
1342 error_status = 0;
1343 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1344 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1345 && elem.data.integer != 0) {
1346 char errbuf[20];
1347 ND_PRINT("[errorStatus(%s)!=0]",
1348 DECODE_ErrorStatus(elem.data.integer));
1349 } else if (pduid == GETBULKREQ) {
1350 ND_PRINT(" N=%d", elem.data.integer);
1351 } else if (elem.data.integer != 0) {
1352 char errbuf[20];
1353 ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1354 error_status = elem.data.integer;
1355 }
1356 length -= count;
1357 np += count;
1358
1359 /* errorIndex (Integer) */
1360 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1361 return;
1362 if (elem.type != BE_INT) {
1363 ND_PRINT("[errorIndex!=INT]");
1364 asn1_print(ndo, &elem);
1365 return;
1366 }
1367 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1368 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1369 && elem.data.integer != 0)
1370 ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1371 else if (pduid == GETBULKREQ)
1372 ND_PRINT(" M=%d", elem.data.integer);
1373 else if (elem.data.integer != 0) {
1374 if (!error_status)
1375 ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1376 else
1377 ND_PRINT("@%d", elem.data.integer);
1378 } else if (error_status) {
1379 ND_PRINT("[errorIndex==0]");
1380 }
1381 length -= count;
1382 np += count;
1383
1384 varbind_print(ndo, pduid, np, length);
1385 }
1386
1387 /*
1388 * Decode SNMP Trap PDU
1389 */
1390 static void
1391 trappdu_print(netdissect_options *ndo,
1392 const u_char *np, u_int length)
1393 {
1394 struct be elem;
1395 int count = 0, generic;
1396
1397 ND_PRINT(" ");
1398
1399 /* enterprise (oid) */
1400 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1401 return;
1402 if (elem.type != BE_OID) {
1403 ND_PRINT("[enterprise!=OID]");
1404 asn1_print(ndo, &elem);
1405 return;
1406 }
1407 if (asn1_print(ndo, &elem) < 0)
1408 return;
1409 length -= count;
1410 np += count;
1411
1412 ND_PRINT(" ");
1413
1414 /* agent-addr (inetaddr) */
1415 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1416 return;
1417 if (elem.type != BE_INETADDR) {
1418 ND_PRINT("[agent-addr!=INETADDR]");
1419 asn1_print(ndo, &elem);
1420 return;
1421 }
1422 if (asn1_print(ndo, &elem) < 0)
1423 return;
1424 length -= count;
1425 np += count;
1426
1427 /* generic-trap (Integer) */
1428 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1429 return;
1430 if (elem.type != BE_INT) {
1431 ND_PRINT("[generic-trap!=INT]");
1432 asn1_print(ndo, &elem);
1433 return;
1434 }
1435 generic = elem.data.integer;
1436 {
1437 char buf[20];
1438 ND_PRINT(" %s", DECODE_GenericTrap(generic));
1439 }
1440 length -= count;
1441 np += count;
1442
1443 /* specific-trap (Integer) */
1444 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1445 return;
1446 if (elem.type != BE_INT) {
1447 ND_PRINT("[specific-trap!=INT]");
1448 asn1_print(ndo, &elem);
1449 return;
1450 }
1451 if (generic != GT_ENTERPRISE) {
1452 if (elem.data.integer != 0)
1453 ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
1454 } else
1455 ND_PRINT(" s=%d", elem.data.integer);
1456 length -= count;
1457 np += count;
1458
1459 ND_PRINT(" ");
1460
1461 /* time-stamp (TimeTicks) */
1462 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1463 return;
1464 if (elem.type != BE_UNS) { /* XXX */
1465 ND_PRINT("[time-stamp!=TIMETICKS]");
1466 asn1_print(ndo, &elem);
1467 return;
1468 }
1469 if (asn1_print(ndo, &elem) < 0)
1470 return;
1471 length -= count;
1472 np += count;
1473
1474 varbind_print(ndo, TRAP, np, length);
1475 }
1476
1477 /*
1478 * Decode arbitrary SNMP PDUs.
1479 */
1480 static void
1481 pdu_print(netdissect_options *ndo,
1482 const u_char *np, u_int length, int version)
1483 {
1484 struct be pdu;
1485 int count = 0;
1486
1487 /* PDU (Context) */
1488 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1489 return;
1490 if (pdu.type != BE_PDU) {
1491 ND_PRINT("[no PDU]");
1492 return;
1493 }
1494 if ((u_int)count < length)
1495 ND_PRINT("[%d extra after PDU]", length - count);
1496 if (ndo->ndo_vflag) {
1497 ND_PRINT("{ ");
1498 }
1499 if (asn1_print(ndo, &pdu) < 0)
1500 return;
1501 ND_PRINT(" ");
1502 /* descend into PDU */
1503 length = pdu.asnlen;
1504 np = (const u_char *)pdu.data.raw;
1505
1506 if (version == SNMP_VERSION_1 &&
1507 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1508 pdu.id == V2TRAP || pdu.id == REPORT)) {
1509 ND_PRINT("[v2 PDU in v1 message]");
1510 return;
1511 }
1512
1513 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1514 ND_PRINT("[v1 PDU in v2 message]");
1515 return;
1516 }
1517
1518 switch (pdu.id) {
1519 case TRAP:
1520 trappdu_print(ndo, np, length);
1521 break;
1522 case GETREQ:
1523 case GETNEXTREQ:
1524 case GETRESP:
1525 case SETREQ:
1526 case GETBULKREQ:
1527 case INFORMREQ:
1528 case V2TRAP:
1529 case REPORT:
1530 snmppdu_print(ndo, pdu.id, np, length);
1531 break;
1532 }
1533
1534 if (ndo->ndo_vflag) {
1535 ND_PRINT(" } ");
1536 }
1537 }
1538
1539 /*
1540 * Decode a scoped SNMP PDU.
1541 */
1542 static void
1543 scopedpdu_print(netdissect_options *ndo,
1544 const u_char *np, u_int length, int version)
1545 {
1546 struct be elem;
1547 int count = 0;
1548
1549 /* Sequence */
1550 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1551 return;
1552 if (elem.type != BE_SEQ) {
1553 ND_PRINT("[!scoped PDU]");
1554 asn1_print(ndo, &elem);
1555 return;
1556 }
1557 length = elem.asnlen;
1558 np = (const u_char *)elem.data.raw;
1559
1560 /* contextEngineID (OCTET STRING) */
1561 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1562 return;
1563 if (elem.type != BE_STR) {
1564 ND_PRINT("[contextEngineID!=STR]");
1565 asn1_print(ndo, &elem);
1566 return;
1567 }
1568 length -= count;
1569 np += count;
1570
1571 ND_PRINT("E=");
1572 if (asn1_print_octets(ndo, &elem) == -1)
1573 return;
1574 ND_PRINT(" ");
1575
1576 /* contextName (OCTET STRING) */
1577 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1578 return;
1579 if (elem.type != BE_STR) {
1580 ND_PRINT("[contextName!=STR]");
1581 asn1_print(ndo, &elem);
1582 return;
1583 }
1584 length -= count;
1585 np += count;
1586
1587 ND_PRINT("C=");
1588 if (asn1_print_string(ndo, &elem) == -1)
1589 return;
1590 ND_PRINT(" ");
1591
1592 pdu_print(ndo, np, length, version);
1593 }
1594
1595 /*
1596 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1597 */
1598 static void
1599 community_print(netdissect_options *ndo,
1600 const u_char *np, u_int length, int version)
1601 {
1602 struct be elem;
1603 int count = 0;
1604
1605 /* Community (String) */
1606 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1607 return;
1608 if (elem.type != BE_STR) {
1609 ND_PRINT("[comm!=STR]");
1610 asn1_print(ndo, &elem);
1611 return;
1612 }
1613 /* default community */
1614 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1615 strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1616 sizeof(DEF_COMMUNITY) - 1) == 0)) {
1617 /* ! "public" */
1618 ND_PRINT("C=");
1619 if (asn1_print_string(ndo, &elem) == -1)
1620 return;
1621 ND_PRINT(" ");
1622 }
1623 length -= count;
1624 np += count;
1625
1626 pdu_print(ndo, np, length, version);
1627 }
1628
1629 /*
1630 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1631 */
1632 static void
1633 usm_print(netdissect_options *ndo,
1634 const u_char *np, u_int length)
1635 {
1636 struct be elem;
1637 int count = 0;
1638
1639 /* Sequence */
1640 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1641 return;
1642 if (elem.type != BE_SEQ) {
1643 ND_PRINT("[!usm]");
1644 asn1_print(ndo, &elem);
1645 return;
1646 }
1647 length = elem.asnlen;
1648 np = (const u_char *)elem.data.raw;
1649
1650 /* msgAuthoritativeEngineID (OCTET STRING) */
1651 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1652 return;
1653 if (elem.type != BE_STR) {
1654 ND_PRINT("[msgAuthoritativeEngineID!=STR]");
1655 asn1_print(ndo, &elem);
1656 return;
1657 }
1658 length -= count;
1659 np += count;
1660
1661 /* msgAuthoritativeEngineBoots (INTEGER) */
1662 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1663 return;
1664 if (elem.type != BE_INT) {
1665 ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
1666 asn1_print(ndo, &elem);
1667 return;
1668 }
1669 if (ndo->ndo_vflag)
1670 ND_PRINT("B=%d ", elem.data.integer);
1671 length -= count;
1672 np += count;
1673
1674 /* msgAuthoritativeEngineTime (INTEGER) */
1675 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1676 return;
1677 if (elem.type != BE_INT) {
1678 ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
1679 asn1_print(ndo, &elem);
1680 return;
1681 }
1682 if (ndo->ndo_vflag)
1683 ND_PRINT("T=%d ", elem.data.integer);
1684 length -= count;
1685 np += count;
1686
1687 /* msgUserName (OCTET STRING) */
1688 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1689 return;
1690 if (elem.type != BE_STR) {
1691 ND_PRINT("[msgUserName!=STR]");
1692 asn1_print(ndo, &elem);
1693 return;
1694 }
1695 length -= count;
1696 np += count;
1697
1698 ND_PRINT("U=");
1699 if (asn1_print_string(ndo, &elem) == -1)
1700 return;
1701 ND_PRINT(" ");
1702
1703 /* msgAuthenticationParameters (OCTET STRING) */
1704 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1705 return;
1706 if (elem.type != BE_STR) {
1707 ND_PRINT("[msgAuthenticationParameters!=STR]");
1708 asn1_print(ndo, &elem);
1709 return;
1710 }
1711 length -= count;
1712 np += count;
1713
1714 /* msgPrivacyParameters (OCTET STRING) */
1715 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1716 return;
1717 if (elem.type != BE_STR) {
1718 ND_PRINT("[msgPrivacyParameters!=STR]");
1719 asn1_print(ndo, &elem);
1720 return;
1721 }
1722 length -= count;
1723 np += count;
1724
1725 if ((u_int)count < length)
1726 ND_PRINT("[%d extra after usm SEQ]", length - count);
1727 }
1728
1729 /*
1730 * Decode SNMPv3 Message Header (SNMPv3)
1731 */
1732 static void
1733 v3msg_print(netdissect_options *ndo,
1734 const u_char *np, u_int length)
1735 {
1736 struct be elem;
1737 int count = 0;
1738 u_char flags;
1739 int model;
1740 const u_char *xnp = np;
1741 int xlength = length;
1742
1743 /* Sequence */
1744 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1745 return;
1746 if (elem.type != BE_SEQ) {
1747 ND_PRINT("[!message]");
1748 asn1_print(ndo, &elem);
1749 return;
1750 }
1751 length = elem.asnlen;
1752 np = (const u_char *)elem.data.raw;
1753
1754 if (ndo->ndo_vflag) {
1755 ND_PRINT("{ ");
1756 }
1757
1758 /* msgID (INTEGER) */
1759 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1760 return;
1761 if (elem.type != BE_INT) {
1762 ND_PRINT("[msgID!=INT]");
1763 asn1_print(ndo, &elem);
1764 return;
1765 }
1766 length -= count;
1767 np += count;
1768
1769 /* msgMaxSize (INTEGER) */
1770 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1771 return;
1772 if (elem.type != BE_INT) {
1773 ND_PRINT("[msgMaxSize!=INT]");
1774 asn1_print(ndo, &elem);
1775 return;
1776 }
1777 length -= count;
1778 np += count;
1779
1780 /* msgFlags (OCTET STRING) */
1781 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1782 return;
1783 if (elem.type != BE_STR) {
1784 ND_PRINT("[msgFlags!=STR]");
1785 asn1_print(ndo, &elem);
1786 return;
1787 }
1788 if (elem.asnlen != 1) {
1789 ND_PRINT("[msgFlags size %d]", elem.asnlen);
1790 return;
1791 }
1792 flags = GET_U_1(elem.data.str);
1793 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1794 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1795 ND_PRINT("[msgFlags=0x%02X]", flags);
1796 return;
1797 }
1798 length -= count;
1799 np += count;
1800
1801 ND_PRINT("F=%s%s%s ",
1802 flags & 0x01 ? "a" : "",
1803 flags & 0x02 ? "p" : "",
1804 flags & 0x04 ? "r" : "");
1805
1806 /* msgSecurityModel (INTEGER) */
1807 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1808 return;
1809 if (elem.type != BE_INT) {
1810 ND_PRINT("[msgSecurityModel!=INT]");
1811 asn1_print(ndo, &elem);
1812 return;
1813 }
1814 model = elem.data.integer;
1815 length -= count;
1816 np += count;
1817
1818 if ((u_int)count < length)
1819 ND_PRINT("[%d extra after message SEQ]", length - count);
1820
1821 if (ndo->ndo_vflag) {
1822 ND_PRINT("} ");
1823 }
1824
1825 if (model == 3) {
1826 if (ndo->ndo_vflag) {
1827 ND_PRINT("{ USM ");
1828 }
1829 } else {
1830 ND_PRINT("[security model %d]", model);
1831 return;
1832 }
1833
1834 np = xnp + (np - xnp);
1835 length = xlength - (np - xnp);
1836
1837 /* msgSecurityParameters (OCTET STRING) */
1838 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1839 return;
1840 if (elem.type != BE_STR) {
1841 ND_PRINT("[msgSecurityParameters!=STR]");
1842 asn1_print(ndo, &elem);
1843 return;
1844 }
1845 length -= count;
1846 np += count;
1847
1848 if (model == 3) {
1849 usm_print(ndo, elem.data.str, elem.asnlen);
1850 if (ndo->ndo_vflag) {
1851 ND_PRINT("} ");
1852 }
1853 }
1854
1855 if (ndo->ndo_vflag) {
1856 ND_PRINT("{ ScopedPDU ");
1857 }
1858
1859 scopedpdu_print(ndo, np, length, 3);
1860
1861 if (ndo->ndo_vflag) {
1862 ND_PRINT("} ");
1863 }
1864 }
1865
1866 /*
1867 * Decode SNMP header and pass on to PDU printing routines
1868 */
1869 void
1870 snmp_print(netdissect_options *ndo,
1871 const u_char *np, u_int length)
1872 {
1873 struct be elem;
1874 int count = 0;
1875 int version = 0;
1876
1877 ndo->ndo_protocol = "snmp";
1878 ND_PRINT(" ");
1879
1880 /* initial Sequence */
1881 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1882 return;
1883 if (elem.type != BE_SEQ) {
1884 ND_PRINT("[!init SEQ]");
1885 asn1_print(ndo, &elem);
1886 return;
1887 }
1888 if ((u_int)count < length)
1889 ND_PRINT("[%d extra after iSEQ]", length - count);
1890 /* descend */
1891 length = elem.asnlen;
1892 np = (const u_char *)elem.data.raw;
1893
1894 /* Version (INTEGER) */
1895 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1896 return;
1897 if (elem.type != BE_INT) {
1898 ND_PRINT("[version!=INT]");
1899 asn1_print(ndo, &elem);
1900 return;
1901 }
1902
1903 switch (elem.data.integer) {
1904 case SNMP_VERSION_1:
1905 case SNMP_VERSION_2:
1906 case SNMP_VERSION_3:
1907 if (ndo->ndo_vflag)
1908 ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
1909 break;
1910 default:
1911 ND_PRINT("SNMP [version = %d]", elem.data.integer);
1912 return;
1913 }
1914 version = elem.data.integer;
1915 length -= count;
1916 np += count;
1917
1918 switch (version) {
1919 case SNMP_VERSION_1:
1920 case SNMP_VERSION_2:
1921 community_print(ndo, np, length, version);
1922 break;
1923 case SNMP_VERSION_3:
1924 v3msg_print(ndo, np, length);
1925 break;
1926 default:
1927 ND_PRINT("[version = %d]", elem.data.integer);
1928 break;
1929 }
1930
1931 if (ndo->ndo_vflag) {
1932 ND_PRINT("} ");
1933 }
1934 }