]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
24acaf7f4c1d0f71e162793c6f010544eee18f5c
[tcpdump] / print-snmp.c
1 /*
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3 * John Robert LoVerso. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 *
28 * This implementation has been influenced by the CMU SNMP release,
29 * by Steve Waldbusser. However, this shares no code with that system.
30 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
31 * Earlier forms of this implementation were derived and/or inspired by an
32 * awk script originally written by C. Philip Wood of LANL (but later
33 * heavily modified by John Robert LoVerso). The copyright notice for
34 * that work is preserved below, even though it may not rightly apply
35 * to this file.
36 *
37 * Support for SNMPv2c/SNMPv3 and the ability to link the module against
38 * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
39 *
40 * This started out as a very simple program, but the incremental decoding
41 * (into the BE structure) complicated things.
42 *
43 # Los Alamos National Laboratory
44 #
45 # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
46 # This software was produced under a U.S. Government contract
47 # (W-7405-ENG-36) by Los Alamos National Laboratory, which is
48 # operated by the University of California for the U.S. Department
49 # of Energy. The U.S. Government is licensed to use, reproduce,
50 # and distribute this software. Permission is granted to the
51 # public to copy and use this software without charge, provided
52 # that this Notice and any statement of authorship are reproduced
53 # on all copies. Neither the Government nor the University makes
54 # any warranty, express or implied, or assumes any liability or
55 # responsibility for the use of this software.
56 # @(#)snmp.awk.x 1.1 (LANL) 1/15/90
57 */
58
59 /* \summary: Simple Network Management Protocol (SNMP) printer */
60
61 #ifdef HAVE_CONFIG_H
62 #include <config.h>
63 #endif
64
65 #include "netdissect-stdinc.h"
66
67 #include <stdio.h>
68 #include <string.h>
69
70 #ifdef USE_LIBSMI
71 #include <smi.h>
72 #endif
73
74 #include "netdissect-ctype.h"
75
76 #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 return -1;
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 return -1;
471 }
472 id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8);
473 len--;
474 hdr++;
475 p++;
476 ND_TCHECK_1(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 int32_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 = -1;
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 o = 0, first = -1;
748
749 p = (const u_char *)elem->data.raw;
750 i = asnlen;
751 if (!ndo->ndo_nflag && asnlen > 2) {
752 const struct obj_abrev *a = &obj_abrev_list[0];
753 for (; a->node; a++) {
754 if (i < a->oid_len)
755 continue;
756 if (!ND_TTEST_LEN(p, a->oid_len))
757 continue;
758 if (memcmp(a->oid, p, a->oid_len) == 0) {
759 objp = a->node->child;
760 i -= a->oid_len;
761 p += a->oid_len;
762 ND_PRINT("%s", a->prefix);
763 first = 1;
764 break;
765 }
766 }
767 }
768
769 for (; i != 0; p++, i--) {
770 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
771 if (GET_U_1(p) & ASN_LONGLEN)
772 continue;
773
774 /*
775 * first subitem encodes two items with
776 * 1st*OIDMUX+2nd
777 * (see X.690:1997 clause 8.19 for the details)
778 */
779 if (first < 0) {
780 int s;
781 if (!ndo->ndo_nflag)
782 objp = mibroot;
783 first = 0;
784 s = o / OIDMUX;
785 if (s > 2) s = 2;
786 OBJ_PRINT(s, first);
787 o -= s * OIDMUX;
788 }
789 OBJ_PRINT(o, first);
790 if (--first < 0)
791 first = 0;
792 o = 0;
793 }
794 break;
795 }
796
797 case BE_INT:
798 ND_PRINT("%d", elem->data.integer);
799 break;
800
801 case BE_UNS:
802 ND_PRINT("%u", elem->data.uns);
803 break;
804
805 case BE_UNS64:
806 ND_PRINT("%" PRIu64, elem->data.uns64);
807 break;
808
809 case BE_STR:
810 if (asn1_print_string(ndo, elem) == -1)
811 return -1;
812 break;
813
814 case BE_SEQ:
815 ND_PRINT("Seq(%u)", elem->asnlen);
816 break;
817
818 case BE_INETADDR:
819 if (asnlen != ASNLEN_INETADDR)
820 ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
821 p = (const u_char *)elem->data.raw;
822 ND_TCHECK_LEN(p, asnlen);
823 for (i = asnlen; i != 0; p++, i--) {
824 ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
825 }
826 break;
827
828 case BE_NOSUCHOBJECT:
829 case BE_NOSUCHINST:
830 case BE_ENDOFMIBVIEW:
831 ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
832 break;
833
834 case BE_PDU:
835 ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
836 break;
837
838 case BE_ANY:
839 ND_PRINT("[BE_ANY!?]");
840 break;
841
842 default:
843 ND_PRINT("[be!?]");
844 break;
845 }
846 return 0;
847
848 trunc:
849 nd_print_trunc(ndo);
850 return -1;
851 }
852
853 #ifdef notdef
854 /*
855 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
856 * This will work for any ASN.1 stream, not just an SNMP PDU.
857 *
858 * By adding newlines and spaces at the correct places, this would print in
859 * Rose-Normal-Form.
860 *
861 * This is not currently used.
862 */
863 static void
864 asn1_decode(u_char *p, u_int length)
865 {
866 struct be elem;
867 int i = 0;
868
869 while (i >= 0 && length > 0) {
870 i = asn1_parse(ndo, p, length, &elem);
871 if (i >= 0) {
872 ND_PRINT(" ");
873 if (asn1_print(ndo, &elem) < 0)
874 return;
875 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
876 ND_PRINT(" {");
877 asn1_decode(elem.data.raw, elem.asnlen);
878 ND_PRINT(" }");
879 }
880 length -= i;
881 p += i;
882 }
883 }
884 }
885 #endif
886
887 #ifdef USE_LIBSMI
888
889 struct smi2be {
890 SmiBasetype basetype;
891 int be;
892 };
893
894 static const struct smi2be smi2betab[] = {
895 { SMI_BASETYPE_INTEGER32, BE_INT },
896 { SMI_BASETYPE_OCTETSTRING, BE_STR },
897 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
898 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
899 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
900 { SMI_BASETYPE_INTEGER64, BE_NONE },
901 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
902 { SMI_BASETYPE_FLOAT32, BE_NONE },
903 { SMI_BASETYPE_FLOAT64, BE_NONE },
904 { SMI_BASETYPE_FLOAT128, BE_NONE },
905 { SMI_BASETYPE_ENUM, BE_INT },
906 { SMI_BASETYPE_BITS, BE_STR },
907 { SMI_BASETYPE_UNKNOWN, BE_NONE }
908 };
909
910 static int
911 smi_decode_oid(netdissect_options *ndo,
912 struct be *elem, unsigned int *oid,
913 unsigned int oidsize, unsigned int *oidlen)
914 {
915 const u_char *p = (const u_char *)elem->data.raw;
916 uint32_t asnlen = elem->asnlen;
917 uint32_t i = asnlen;
918 int o = 0, first = -1;
919 unsigned int firstval;
920
921 for (*oidlen = 0; i != 0; p++, i--) {
922 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
923 if (GET_U_1(p) & ASN_LONGLEN)
924 continue;
925
926 /*
927 * first subitem encodes two items with 1st*OIDMUX+2nd
928 * (see X.690:1997 clause 8.19 for the details)
929 */
930 if (first < 0) {
931 first = 0;
932 firstval = o / OIDMUX;
933 if (firstval > 2) firstval = 2;
934 o -= firstval * OIDMUX;
935 if (*oidlen < oidsize) {
936 oid[(*oidlen)++] = firstval;
937 }
938 }
939 if (*oidlen < oidsize) {
940 oid[(*oidlen)++] = o;
941 }
942 o = 0;
943 }
944 return 0;
945 }
946
947 static int smi_check_type(SmiBasetype basetype, int be)
948 {
949 int i;
950
951 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
952 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
953 return 1;
954 }
955 }
956
957 return 0;
958 }
959
960 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
961 struct be *elem)
962 {
963 int ok = 1;
964
965 switch (smiType->basetype) {
966 case SMI_BASETYPE_OBJECTIDENTIFIER:
967 case SMI_BASETYPE_OCTETSTRING:
968 if (smiRange->minValue.value.unsigned32
969 == smiRange->maxValue.value.unsigned32) {
970 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
971 } else {
972 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
973 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
974 }
975 break;
976
977 case SMI_BASETYPE_INTEGER32:
978 ok = (elem->data.integer >= smiRange->minValue.value.integer32
979 && elem->data.integer <= smiRange->maxValue.value.integer32);
980 break;
981
982 case SMI_BASETYPE_UNSIGNED32:
983 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
984 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
985 break;
986
987 case SMI_BASETYPE_UNSIGNED64:
988 /* XXX */
989 break;
990
991 /* case SMI_BASETYPE_INTEGER64: SMIng */
992 /* case SMI_BASETYPE_FLOAT32: SMIng */
993 /* case SMI_BASETYPE_FLOAT64: SMIng */
994 /* case SMI_BASETYPE_FLOAT128: SMIng */
995
996 case SMI_BASETYPE_ENUM:
997 case SMI_BASETYPE_BITS:
998 case SMI_BASETYPE_UNKNOWN:
999 ok = 1;
1000 break;
1001
1002 default:
1003 ok = 0;
1004 break;
1005 }
1006
1007 return ok;
1008 }
1009
1010 static int smi_check_range(SmiType *smiType, struct be *elem)
1011 {
1012 SmiRange *smiRange;
1013 int ok = 1;
1014
1015 for (smiRange = smiGetFirstRange(smiType);
1016 smiRange;
1017 smiRange = smiGetNextRange(smiRange)) {
1018
1019 ok = smi_check_a_range(smiType, smiRange, elem);
1020
1021 if (ok) {
1022 break;
1023 }
1024 }
1025
1026 if (ok) {
1027 SmiType *parentType;
1028 parentType = smiGetParentType(smiType);
1029 if (parentType) {
1030 ok = smi_check_range(parentType, elem);
1031 }
1032 }
1033
1034 return ok;
1035 }
1036
1037 static SmiNode *
1038 smi_print_variable(netdissect_options *ndo,
1039 struct be *elem, int *status)
1040 {
1041 unsigned int oid[128], oidlen;
1042 SmiNode *smiNode = NULL;
1043 unsigned int i;
1044
1045 if (!nd_smi_module_loaded) {
1046 *status = asn1_print(ndo, elem);
1047 return NULL;
1048 }
1049 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1050 &oidlen);
1051 if (*status < 0)
1052 return NULL;
1053 smiNode = smiGetNodeByOID(oidlen, oid);
1054 if (! smiNode) {
1055 *status = asn1_print(ndo, elem);
1056 return NULL;
1057 }
1058 if (ndo->ndo_vflag) {
1059 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1060 }
1061 ND_PRINT("%s", smiNode->name);
1062 if (smiNode->oidlen < oidlen) {
1063 for (i = smiNode->oidlen; i < oidlen; i++) {
1064 ND_PRINT(".%u", oid[i]);
1065 }
1066 }
1067 *status = 0;
1068 return smiNode;
1069 }
1070
1071 static int
1072 smi_print_value(netdissect_options *ndo,
1073 SmiNode *smiNode, u_short pduid, struct be *elem)
1074 {
1075 unsigned int i, oid[128], oidlen;
1076 SmiType *smiType;
1077 SmiNamedNumber *nn;
1078 int done = 0;
1079
1080 if (! smiNode || ! (smiNode->nodekind
1081 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1082 return asn1_print(ndo, elem);
1083 }
1084
1085 if (elem->type == BE_NOSUCHOBJECT
1086 || elem->type == BE_NOSUCHINST
1087 || elem->type == BE_ENDOFMIBVIEW) {
1088 return asn1_print(ndo, elem);
1089 }
1090
1091 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1092 ND_PRINT("[notNotifyable]");
1093 }
1094
1095 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1096 ND_PRINT("[notReadable]");
1097 }
1098
1099 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1100 ND_PRINT("[notWritable]");
1101 }
1102
1103 if (RESPONSE_CLASS(pduid)
1104 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1105 ND_PRINT("[noAccess]");
1106 }
1107
1108 smiType = smiGetNodeType(smiNode);
1109 if (! smiType) {
1110 return asn1_print(ndo, elem);
1111 }
1112
1113 if (! smi_check_type(smiType->basetype, elem->type)) {
1114 ND_PRINT("[wrongType]");
1115 }
1116
1117 if (! smi_check_range(smiType, elem)) {
1118 ND_PRINT("[outOfRange]");
1119 }
1120
1121 /* resolve bits to named bits */
1122
1123 /* check whether instance identifier is valid */
1124
1125 /* apply display hints (integer, octetstring) */
1126
1127 /* convert instance identifier to index type values */
1128
1129 switch (elem->type) {
1130 case BE_OID:
1131 if (smiType->basetype == SMI_BASETYPE_BITS) {
1132 /* print bit labels */
1133 } else {
1134 if (nd_smi_module_loaded &&
1135 smi_decode_oid(ndo, elem, oid,
1136 sizeof(oid)/sizeof(unsigned int),
1137 &oidlen) == 0) {
1138 smiNode = smiGetNodeByOID(oidlen, oid);
1139 if (smiNode) {
1140 if (ndo->ndo_vflag) {
1141 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1142 }
1143 ND_PRINT("%s", smiNode->name);
1144 if (smiNode->oidlen < oidlen) {
1145 for (i = smiNode->oidlen;
1146 i < oidlen; i++) {
1147 ND_PRINT(".%u", oid[i]);
1148 }
1149 }
1150 done++;
1151 }
1152 }
1153 }
1154 break;
1155
1156 case BE_INT:
1157 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1158 for (nn = smiGetFirstNamedNumber(smiType);
1159 nn;
1160 nn = smiGetNextNamedNumber(nn)) {
1161 if (nn->value.value.integer32
1162 == elem->data.integer) {
1163 ND_PRINT("%s", nn->name);
1164 ND_PRINT("(%d)", elem->data.integer);
1165 done++;
1166 break;
1167 }
1168 }
1169 }
1170 break;
1171 }
1172
1173 if (! done) {
1174 return asn1_print(ndo, elem);
1175 }
1176 return 0;
1177 }
1178 #endif
1179
1180 /*
1181 * General SNMP header
1182 * SEQUENCE {
1183 * version INTEGER {version-1(0)},
1184 * community OCTET STRING,
1185 * data ANY -- PDUs
1186 * }
1187 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1188 * SEQUENCE {
1189 * request-id INTEGER,
1190 * error-status INTEGER,
1191 * error-index INTEGER,
1192 * varbindlist SEQUENCE OF
1193 * SEQUENCE {
1194 * name ObjectName,
1195 * value ObjectValue
1196 * }
1197 * }
1198 * PDU for Trap:
1199 * SEQUENCE {
1200 * enterprise OBJECT IDENTIFIER,
1201 * agent-addr NetworkAddress,
1202 * generic-trap INTEGER,
1203 * specific-trap INTEGER,
1204 * time-stamp TimeTicks,
1205 * varbindlist SEQUENCE OF
1206 * SEQUENCE {
1207 * name ObjectName,
1208 * value ObjectValue
1209 * }
1210 * }
1211 */
1212
1213 /*
1214 * Decode SNMP varBind
1215 */
1216 static void
1217 varbind_print(netdissect_options *ndo,
1218 u_short pduid, const u_char *np, u_int length)
1219 {
1220 struct be elem;
1221 int count = 0, ind;
1222 #ifdef USE_LIBSMI
1223 SmiNode *smiNode = NULL;
1224 #endif
1225 int status;
1226
1227 /* Sequence of varBind */
1228 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1229 return;
1230 if (elem.type != BE_SEQ) {
1231 ND_PRINT("[!SEQ of varbind]");
1232 asn1_print(ndo, &elem);
1233 return;
1234 }
1235 if ((u_int)count < length)
1236 ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1237 /* descend */
1238 length = elem.asnlen;
1239 np = (const u_char *)elem.data.raw;
1240
1241 for (ind = 1; length > 0; ind++) {
1242 const u_char *vbend;
1243 u_int vblength;
1244
1245 ND_PRINT(" ");
1246
1247 /* Sequence */
1248 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1249 return;
1250 if (elem.type != BE_SEQ) {
1251 ND_PRINT("[!varbind]");
1252 asn1_print(ndo, &elem);
1253 return;
1254 }
1255 vbend = np + count;
1256 vblength = length - count;
1257 /* descend */
1258 length = elem.asnlen;
1259 np = (const u_char *)elem.data.raw;
1260
1261 /* objName (OID) */
1262 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1263 return;
1264 if (elem.type != BE_OID) {
1265 ND_PRINT("[objName!=OID]");
1266 asn1_print(ndo, &elem);
1267 return;
1268 }
1269 #ifdef USE_LIBSMI
1270 smiNode = smi_print_variable(ndo, &elem, &status);
1271 #else
1272 status = asn1_print(ndo, &elem);
1273 #endif
1274 if (status < 0)
1275 return;
1276 length -= count;
1277 np += count;
1278
1279 if (pduid != GETREQ && pduid != GETNEXTREQ
1280 && pduid != GETBULKREQ)
1281 ND_PRINT("=");
1282
1283 /* objVal (ANY) */
1284 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1285 return;
1286 if (pduid == GETREQ || pduid == GETNEXTREQ
1287 || pduid == GETBULKREQ) {
1288 if (elem.type != BE_NULL) {
1289 ND_PRINT("[objVal!=NULL]");
1290 if (asn1_print(ndo, &elem) < 0)
1291 return;
1292 }
1293 } else {
1294 if (elem.type != BE_NULL) {
1295 #ifdef USE_LIBSMI
1296 status = smi_print_value(ndo, smiNode, pduid, &elem);
1297 #else
1298 status = asn1_print(ndo, &elem);
1299 #endif
1300 }
1301 if (status < 0)
1302 return;
1303 }
1304 length = vblength;
1305 np = vbend;
1306 }
1307 }
1308
1309 /*
1310 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1311 * GetBulk, Inform, V2Trap, and Report
1312 */
1313 static void
1314 snmppdu_print(netdissect_options *ndo,
1315 u_short pduid, const u_char *np, u_int length)
1316 {
1317 struct be elem;
1318 int count = 0, error_status;
1319
1320 /* reqId (Integer) */
1321 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1322 return;
1323 if (elem.type != BE_INT) {
1324 ND_PRINT("[reqId!=INT]");
1325 asn1_print(ndo, &elem);
1326 return;
1327 }
1328 if (ndo->ndo_vflag)
1329 ND_PRINT("R=%d ", elem.data.integer);
1330 length -= count;
1331 np += count;
1332
1333 /* errorStatus (Integer) */
1334 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1335 return;
1336 if (elem.type != BE_INT) {
1337 ND_PRINT("[errorStatus!=INT]");
1338 asn1_print(ndo, &elem);
1339 return;
1340 }
1341 error_status = 0;
1342 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1343 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1344 && elem.data.integer != 0) {
1345 char errbuf[20];
1346 ND_PRINT("[errorStatus(%s)!=0]",
1347 DECODE_ErrorStatus(elem.data.integer));
1348 } else if (pduid == GETBULKREQ) {
1349 ND_PRINT(" N=%d", elem.data.integer);
1350 } else if (elem.data.integer != 0) {
1351 char errbuf[20];
1352 ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1353 error_status = elem.data.integer;
1354 }
1355 length -= count;
1356 np += count;
1357
1358 /* errorIndex (Integer) */
1359 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1360 return;
1361 if (elem.type != BE_INT) {
1362 ND_PRINT("[errorIndex!=INT]");
1363 asn1_print(ndo, &elem);
1364 return;
1365 }
1366 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1367 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1368 && elem.data.integer != 0)
1369 ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1370 else if (pduid == GETBULKREQ)
1371 ND_PRINT(" M=%d", elem.data.integer);
1372 else if (elem.data.integer != 0) {
1373 if (!error_status)
1374 ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1375 else
1376 ND_PRINT("@%d", elem.data.integer);
1377 } else if (error_status) {
1378 ND_PRINT("[errorIndex==0]");
1379 }
1380 length -= count;
1381 np += count;
1382
1383 varbind_print(ndo, pduid, np, length);
1384 return;
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 return;
1476 }
1477
1478 /*
1479 * Decode arbitrary SNMP PDUs.
1480 */
1481 static void
1482 pdu_print(netdissect_options *ndo,
1483 const u_char *np, u_int length, int version)
1484 {
1485 struct be pdu;
1486 int count = 0;
1487
1488 /* PDU (Context) */
1489 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1490 return;
1491 if (pdu.type != BE_PDU) {
1492 ND_PRINT("[no PDU]");
1493 return;
1494 }
1495 if ((u_int)count < length)
1496 ND_PRINT("[%d extra after PDU]", length - count);
1497 if (ndo->ndo_vflag) {
1498 ND_PRINT("{ ");
1499 }
1500 if (asn1_print(ndo, &pdu) < 0)
1501 return;
1502 ND_PRINT(" ");
1503 /* descend into PDU */
1504 length = pdu.asnlen;
1505 np = (const u_char *)pdu.data.raw;
1506
1507 if (version == SNMP_VERSION_1 &&
1508 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1509 pdu.id == V2TRAP || pdu.id == REPORT)) {
1510 ND_PRINT("[v2 PDU in v1 message]");
1511 return;
1512 }
1513
1514 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1515 ND_PRINT("[v1 PDU in v2 message]");
1516 return;
1517 }
1518
1519 switch (pdu.id) {
1520 case TRAP:
1521 trappdu_print(ndo, np, length);
1522 break;
1523 case GETREQ:
1524 case GETNEXTREQ:
1525 case GETRESP:
1526 case SETREQ:
1527 case GETBULKREQ:
1528 case INFORMREQ:
1529 case V2TRAP:
1530 case REPORT:
1531 snmppdu_print(ndo, pdu.id, np, length);
1532 break;
1533 }
1534
1535 if (ndo->ndo_vflag) {
1536 ND_PRINT(" } ");
1537 }
1538 }
1539
1540 /*
1541 * Decode a scoped SNMP PDU.
1542 */
1543 static void
1544 scopedpdu_print(netdissect_options *ndo,
1545 const u_char *np, u_int length, int version)
1546 {
1547 struct be elem;
1548 int count = 0;
1549
1550 /* Sequence */
1551 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1552 return;
1553 if (elem.type != BE_SEQ) {
1554 ND_PRINT("[!scoped PDU]");
1555 asn1_print(ndo, &elem);
1556 return;
1557 }
1558 length = elem.asnlen;
1559 np = (const u_char *)elem.data.raw;
1560
1561 /* contextEngineID (OCTET STRING) */
1562 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1563 return;
1564 if (elem.type != BE_STR) {
1565 ND_PRINT("[contextEngineID!=STR]");
1566 asn1_print(ndo, &elem);
1567 return;
1568 }
1569 length -= count;
1570 np += count;
1571
1572 ND_PRINT("E=");
1573 if (asn1_print_octets(ndo, &elem) == -1)
1574 return;
1575 ND_PRINT(" ");
1576
1577 /* contextName (OCTET STRING) */
1578 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1579 return;
1580 if (elem.type != BE_STR) {
1581 ND_PRINT("[contextName!=STR]");
1582 asn1_print(ndo, &elem);
1583 return;
1584 }
1585 length -= count;
1586 np += count;
1587
1588 ND_PRINT("C=");
1589 if (asn1_print_string(ndo, &elem) == -1)
1590 return;
1591 ND_PRINT(" ");
1592
1593 pdu_print(ndo, np, length, version);
1594 }
1595
1596 /*
1597 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1598 */
1599 static void
1600 community_print(netdissect_options *ndo,
1601 const u_char *np, u_int length, int version)
1602 {
1603 struct be elem;
1604 int count = 0;
1605
1606 /* Community (String) */
1607 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1608 return;
1609 if (elem.type != BE_STR) {
1610 ND_PRINT("[comm!=STR]");
1611 asn1_print(ndo, &elem);
1612 return;
1613 }
1614 /* default community */
1615 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1616 strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1617 sizeof(DEF_COMMUNITY) - 1) == 0)) {
1618 /* ! "public" */
1619 ND_PRINT("C=");
1620 if (asn1_print_string(ndo, &elem) == -1)
1621 return;
1622 ND_PRINT(" ");
1623 }
1624 length -= count;
1625 np += count;
1626
1627 pdu_print(ndo, np, length, version);
1628 }
1629
1630 /*
1631 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1632 */
1633 static void
1634 usm_print(netdissect_options *ndo,
1635 const u_char *np, u_int length)
1636 {
1637 struct be elem;
1638 int count = 0;
1639
1640 /* Sequence */
1641 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1642 return;
1643 if (elem.type != BE_SEQ) {
1644 ND_PRINT("[!usm]");
1645 asn1_print(ndo, &elem);
1646 return;
1647 }
1648 length = elem.asnlen;
1649 np = (const u_char *)elem.data.raw;
1650
1651 /* msgAuthoritativeEngineID (OCTET STRING) */
1652 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1653 return;
1654 if (elem.type != BE_STR) {
1655 ND_PRINT("[msgAuthoritativeEngineID!=STR]");
1656 asn1_print(ndo, &elem);
1657 return;
1658 }
1659 length -= count;
1660 np += count;
1661
1662 /* msgAuthoritativeEngineBoots (INTEGER) */
1663 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1664 return;
1665 if (elem.type != BE_INT) {
1666 ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
1667 asn1_print(ndo, &elem);
1668 return;
1669 }
1670 if (ndo->ndo_vflag)
1671 ND_PRINT("B=%d ", elem.data.integer);
1672 length -= count;
1673 np += count;
1674
1675 /* msgAuthoritativeEngineTime (INTEGER) */
1676 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1677 return;
1678 if (elem.type != BE_INT) {
1679 ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
1680 asn1_print(ndo, &elem);
1681 return;
1682 }
1683 if (ndo->ndo_vflag)
1684 ND_PRINT("T=%d ", elem.data.integer);
1685 length -= count;
1686 np += count;
1687
1688 /* msgUserName (OCTET STRING) */
1689 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1690 return;
1691 if (elem.type != BE_STR) {
1692 ND_PRINT("[msgUserName!=STR]");
1693 asn1_print(ndo, &elem);
1694 return;
1695 }
1696 length -= count;
1697 np += count;
1698
1699 ND_PRINT("U=");
1700 if (asn1_print_string(ndo, &elem) == -1)
1701 return;
1702 ND_PRINT(" ");
1703
1704 /* msgAuthenticationParameters (OCTET STRING) */
1705 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1706 return;
1707 if (elem.type != BE_STR) {
1708 ND_PRINT("[msgAuthenticationParameters!=STR]");
1709 asn1_print(ndo, &elem);
1710 return;
1711 }
1712 length -= count;
1713 np += count;
1714
1715 /* msgPrivacyParameters (OCTET STRING) */
1716 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1717 return;
1718 if (elem.type != BE_STR) {
1719 ND_PRINT("[msgPrivacyParameters!=STR]");
1720 asn1_print(ndo, &elem);
1721 return;
1722 }
1723 length -= count;
1724 np += count;
1725
1726 if ((u_int)count < length)
1727 ND_PRINT("[%d extra after usm SEQ]", length - count);
1728 }
1729
1730 /*
1731 * Decode SNMPv3 Message Header (SNMPv3)
1732 */
1733 static void
1734 v3msg_print(netdissect_options *ndo,
1735 const u_char *np, u_int length)
1736 {
1737 struct be elem;
1738 int count = 0;
1739 u_char flags;
1740 int model;
1741 const u_char *xnp = np;
1742 int xlength = length;
1743
1744 /* Sequence */
1745 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1746 return;
1747 if (elem.type != BE_SEQ) {
1748 ND_PRINT("[!message]");
1749 asn1_print(ndo, &elem);
1750 return;
1751 }
1752 length = elem.asnlen;
1753 np = (const u_char *)elem.data.raw;
1754
1755 if (ndo->ndo_vflag) {
1756 ND_PRINT("{ ");
1757 }
1758
1759 /* msgID (INTEGER) */
1760 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1761 return;
1762 if (elem.type != BE_INT) {
1763 ND_PRINT("[msgID!=INT]");
1764 asn1_print(ndo, &elem);
1765 return;
1766 }
1767 length -= count;
1768 np += count;
1769
1770 /* msgMaxSize (INTEGER) */
1771 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1772 return;
1773 if (elem.type != BE_INT) {
1774 ND_PRINT("[msgMaxSize!=INT]");
1775 asn1_print(ndo, &elem);
1776 return;
1777 }
1778 length -= count;
1779 np += count;
1780
1781 /* msgFlags (OCTET STRING) */
1782 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1783 return;
1784 if (elem.type != BE_STR) {
1785 ND_PRINT("[msgFlags!=STR]");
1786 asn1_print(ndo, &elem);
1787 return;
1788 }
1789 if (elem.asnlen != 1) {
1790 ND_PRINT("[msgFlags size %d]", elem.asnlen);
1791 return;
1792 }
1793 flags = GET_U_1(elem.data.str);
1794 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1795 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1796 ND_PRINT("[msgFlags=0x%02X]", flags);
1797 return;
1798 }
1799 length -= count;
1800 np += count;
1801
1802 ND_PRINT("F=%s%s%s ",
1803 flags & 0x01 ? "a" : "",
1804 flags & 0x02 ? "p" : "",
1805 flags & 0x04 ? "r" : "");
1806
1807 /* msgSecurityModel (INTEGER) */
1808 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1809 return;
1810 if (elem.type != BE_INT) {
1811 ND_PRINT("[msgSecurityModel!=INT]");
1812 asn1_print(ndo, &elem);
1813 return;
1814 }
1815 model = elem.data.integer;
1816 length -= count;
1817 np += count;
1818
1819 if ((u_int)count < length)
1820 ND_PRINT("[%d extra after message SEQ]", length - count);
1821
1822 if (ndo->ndo_vflag) {
1823 ND_PRINT("} ");
1824 }
1825
1826 if (model == 3) {
1827 if (ndo->ndo_vflag) {
1828 ND_PRINT("{ USM ");
1829 }
1830 } else {
1831 ND_PRINT("[security model %d]", model);
1832 return;
1833 }
1834
1835 np = xnp + (np - xnp);
1836 length = xlength - (np - xnp);
1837
1838 /* msgSecurityParameters (OCTET STRING) */
1839 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1840 return;
1841 if (elem.type != BE_STR) {
1842 ND_PRINT("[msgSecurityParameters!=STR]");
1843 asn1_print(ndo, &elem);
1844 return;
1845 }
1846 length -= count;
1847 np += count;
1848
1849 if (model == 3) {
1850 usm_print(ndo, elem.data.str, elem.asnlen);
1851 if (ndo->ndo_vflag) {
1852 ND_PRINT("} ");
1853 }
1854 }
1855
1856 if (ndo->ndo_vflag) {
1857 ND_PRINT("{ ScopedPDU ");
1858 }
1859
1860 scopedpdu_print(ndo, np, length, 3);
1861
1862 if (ndo->ndo_vflag) {
1863 ND_PRINT("} ");
1864 }
1865 }
1866
1867 /*
1868 * Decode SNMP header and pass on to PDU printing routines
1869 */
1870 void
1871 snmp_print(netdissect_options *ndo,
1872 const u_char *np, u_int length)
1873 {
1874 struct be elem;
1875 int count = 0;
1876 int version = 0;
1877
1878 ndo->ndo_protocol = "snmp";
1879 ND_PRINT(" ");
1880
1881 /* initial Sequence */
1882 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1883 return;
1884 if (elem.type != BE_SEQ) {
1885 ND_PRINT("[!init SEQ]");
1886 asn1_print(ndo, &elem);
1887 return;
1888 }
1889 if ((u_int)count < length)
1890 ND_PRINT("[%d extra after iSEQ]", length - count);
1891 /* descend */
1892 length = elem.asnlen;
1893 np = (const u_char *)elem.data.raw;
1894
1895 /* Version (INTEGER) */
1896 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1897 return;
1898 if (elem.type != BE_INT) {
1899 ND_PRINT("[version!=INT]");
1900 asn1_print(ndo, &elem);
1901 return;
1902 }
1903
1904 switch (elem.data.integer) {
1905 case SNMP_VERSION_1:
1906 case SNMP_VERSION_2:
1907 case SNMP_VERSION_3:
1908 if (ndo->ndo_vflag)
1909 ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
1910 break;
1911 default:
1912 ND_PRINT("SNMP [version = %d]", elem.data.integer);
1913 return;
1914 }
1915 version = elem.data.integer;
1916 length -= count;
1917 np += count;
1918
1919 switch (version) {
1920 case SNMP_VERSION_1:
1921 case SNMP_VERSION_2:
1922 community_print(ndo, np, length, version);
1923 break;
1924 case SNMP_VERSION_3:
1925 v3msg_print(ndo, np, length);
1926 break;
1927 default:
1928 ND_PRINT("[version = %d]", elem.data.integer);
1929 break;
1930 }
1931
1932 if (ndo->ndo_vflag) {
1933 ND_PRINT("} ");
1934 }
1935 }