]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
remove redundant ND_TCHECK, let GET_ routines handle checks
[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 }
477 if (len < 1) {
478 ND_PRINT("[Xtagfield?]");
479 return -1;
480 }
481 elem->id = id = (id << 7) | GET_U_1(p);
482 --len;
483 ++hdr;
484 ++p;
485 }
486 if (len < 1) {
487 ND_PRINT("[no asnlen]");
488 return -1;
489 }
490 elem->asnlen = GET_U_1(p);
491 p++; len--; hdr++;
492 if (elem->asnlen & ASN_BIT8) {
493 uint32_t noct = elem->asnlen % ASN_BIT8;
494 elem->asnlen = 0;
495 if (len < noct) {
496 ND_PRINT("[asnlen? %d<%d]", len, noct);
497 return -1;
498 }
499 ND_TCHECK_LEN(p, noct);
500 for (; noct != 0; len--, hdr++, noct--) {
501 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
502 p++;
503 }
504 }
505 if (len < elem->asnlen) {
506 ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
507 return -1;
508 }
509 if (form >= sizeof(Form)/sizeof(Form[0])) {
510 ND_PRINT("[form?%d]", form);
511 return -1;
512 }
513 if (class >= sizeof(Class)/sizeof(Class[0])) {
514 ND_PRINT("[class?%c/%d]", *Form[form], class);
515 return -1;
516 }
517 if ((int)id >= Class[class].numIDs) {
518 ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
519 return -1;
520 }
521 ND_TCHECK_LEN(p, elem->asnlen);
522
523 switch (form) {
524 case PRIMITIVE:
525 switch (class) {
526 case UNIVERSAL:
527 switch (id) {
528 case STRING:
529 elem->type = BE_STR;
530 elem->data.str = p;
531 break;
532
533 case INTEGER: {
534 int32_t data;
535 elem->type = BE_INT;
536 data = 0;
537
538 if (elem->asnlen == 0) {
539 ND_PRINT("[asnlen=0]");
540 return -1;
541 }
542 if (GET_U_1(p) & ASN_BIT8) /* negative */
543 data = -1;
544 for (i = elem->asnlen; i != 0; p++, i--)
545 data = (data << ASN_SHIFT8) | GET_U_1(p);
546 elem->data.integer = data;
547 break;
548 }
549
550 case OBJECTID:
551 elem->type = BE_OID;
552 elem->data.raw = (const uint8_t *)p;
553 break;
554
555 case ASN_NULL:
556 elem->type = BE_NULL;
557 elem->data.raw = NULL;
558 break;
559
560 default:
561 elem->type = BE_OCTET;
562 elem->data.raw = (const uint8_t *)p;
563 ND_PRINT("[P/U/%s]", Class[class].Id[id]);
564 break;
565 }
566 break;
567
568 case APPLICATION:
569 switch (id) {
570 case IPADDR:
571 elem->type = BE_INETADDR;
572 elem->data.raw = (const uint8_t *)p;
573 break;
574
575 case COUNTER:
576 case GAUGE:
577 case TIMETICKS: {
578 uint32_t data;
579 elem->type = BE_UNS;
580 data = 0;
581 for (i = elem->asnlen; i != 0; p++, i--)
582 data = (data << 8) + GET_U_1(p);
583 elem->data.uns = data;
584 break;
585 }
586
587 case COUNTER64: {
588 uint64_t data64;
589 elem->type = BE_UNS64;
590 data64 = 0;
591 for (i = elem->asnlen; i != 0; p++, i--)
592 data64 = (data64 << 8) + GET_U_1(p);
593 elem->data.uns64 = data64;
594 break;
595 }
596
597 default:
598 elem->type = BE_OCTET;
599 elem->data.raw = (const uint8_t *)p;
600 ND_PRINT("[P/A/%s]",
601 Class[class].Id[id]);
602 break;
603 }
604 break;
605
606 case CONTEXT:
607 switch (id) {
608 case NOSUCHOBJECT:
609 elem->type = BE_NOSUCHOBJECT;
610 elem->data.raw = NULL;
611 break;
612
613 case NOSUCHINSTANCE:
614 elem->type = BE_NOSUCHINST;
615 elem->data.raw = NULL;
616 break;
617
618 case ENDOFMIBVIEW:
619 elem->type = BE_ENDOFMIBVIEW;
620 elem->data.raw = NULL;
621 break;
622 }
623 break;
624
625 default:
626 ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
627 elem->type = BE_OCTET;
628 elem->data.raw = (const uint8_t *)p;
629 break;
630 }
631 break;
632
633 case CONSTRUCTED:
634 switch (class) {
635 case UNIVERSAL:
636 switch (id) {
637 case SEQUENCE:
638 elem->type = BE_SEQ;
639 elem->data.raw = (const uint8_t *)p;
640 break;
641
642 default:
643 elem->type = BE_OCTET;
644 elem->data.raw = (const uint8_t *)p;
645 ND_PRINT("C/U/%s", Class[class].Id[id]);
646 break;
647 }
648 break;
649
650 case CONTEXT:
651 elem->type = BE_PDU;
652 elem->data.raw = (const uint8_t *)p;
653 break;
654
655 default:
656 elem->type = BE_OCTET;
657 elem->data.raw = (const uint8_t *)p;
658 ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
659 break;
660 }
661 break;
662 }
663 p += elem->asnlen;
664 len -= elem->asnlen;
665 return elem->asnlen + hdr;
666
667 trunc:
668 nd_print_trunc(ndo);
669 return -1;
670 }
671
672 static int
673 asn1_print_octets(netdissect_options *ndo, struct be *elem)
674 {
675 const u_char *p = (const u_char *)elem->data.raw;
676 uint32_t asnlen = elem->asnlen;
677 uint32_t i;
678
679 ND_TCHECK_LEN(p, asnlen);
680 for (i = asnlen; i != 0; p++, i--)
681 ND_PRINT("_%.2x", GET_U_1(p));
682 return 0;
683
684 trunc:
685 nd_print_trunc(ndo);
686 return -1;
687 }
688
689 static int
690 asn1_print_string(netdissect_options *ndo, struct be *elem)
691 {
692 int printable = 1, first = 1;
693 const u_char *p;
694 uint32_t asnlen = elem->asnlen;
695 uint32_t i;
696
697 p = elem->data.str;
698 ND_TCHECK_LEN(p, asnlen);
699 for (i = asnlen; printable && i != 0; p++, i--)
700 printable = ND_ASCII_ISPRINT(GET_U_1(p));
701 p = elem->data.str;
702 if (printable) {
703 ND_PRINT("\"");
704 if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
705 ND_PRINT("\"");
706 goto trunc;
707 }
708 ND_PRINT("\"");
709 } else {
710 for (i = asnlen; i != 0; p++, i--) {
711 ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
712 first = 0;
713 }
714 }
715 return 0;
716
717 trunc:
718 nd_print_trunc(ndo);
719 return -1;
720 }
721
722 /*
723 * Display the ASN.1 object represented by the BE object.
724 * This used to be an integral part of asn1_parse() before the intermediate
725 * BE form was added.
726 */
727 static int
728 asn1_print(netdissect_options *ndo,
729 struct be *elem)
730 {
731 const u_char *p;
732 uint32_t asnlen = elem->asnlen;
733 uint32_t i;
734
735 switch (elem->type) {
736
737 case BE_OCTET:
738 if (asn1_print_octets(ndo, elem) == -1)
739 return -1;
740 break;
741
742 case BE_NULL:
743 break;
744
745 case BE_OID: {
746 int o = 0, first = -1;
747
748 p = (const u_char *)elem->data.raw;
749 i = asnlen;
750 if (!ndo->ndo_nflag && asnlen > 2) {
751 const struct obj_abrev *a = &obj_abrev_list[0];
752 for (; a->node; a++) {
753 if (i < a->oid_len)
754 continue;
755 if (!ND_TTEST_LEN(p, a->oid_len))
756 continue;
757 if (memcmp(a->oid, p, a->oid_len) == 0) {
758 objp = a->node->child;
759 i -= a->oid_len;
760 p += a->oid_len;
761 ND_PRINT("%s", a->prefix);
762 first = 1;
763 break;
764 }
765 }
766 }
767
768 for (; i != 0; p++, i--) {
769 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
770 if (GET_U_1(p) & ASN_LONGLEN)
771 continue;
772
773 /*
774 * first subitem encodes two items with
775 * 1st*OIDMUX+2nd
776 * (see X.690:1997 clause 8.19 for the details)
777 */
778 if (first < 0) {
779 int s;
780 if (!ndo->ndo_nflag)
781 objp = mibroot;
782 first = 0;
783 s = o / OIDMUX;
784 if (s > 2) s = 2;
785 OBJ_PRINT(s, first);
786 o -= s * OIDMUX;
787 }
788 OBJ_PRINT(o, first);
789 if (--first < 0)
790 first = 0;
791 o = 0;
792 }
793 break;
794 }
795
796 case BE_INT:
797 ND_PRINT("%d", elem->data.integer);
798 break;
799
800 case BE_UNS:
801 ND_PRINT("%u", elem->data.uns);
802 break;
803
804 case BE_UNS64:
805 ND_PRINT("%" PRIu64, elem->data.uns64);
806 break;
807
808 case BE_STR:
809 if (asn1_print_string(ndo, elem) == -1)
810 return -1;
811 break;
812
813 case BE_SEQ:
814 ND_PRINT("Seq(%u)", elem->asnlen);
815 break;
816
817 case BE_INETADDR:
818 if (asnlen != ASNLEN_INETADDR)
819 ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
820 p = (const u_char *)elem->data.raw;
821 ND_TCHECK_LEN(p, asnlen);
822 for (i = asnlen; i != 0; p++, i--) {
823 ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
824 }
825 break;
826
827 case BE_NOSUCHOBJECT:
828 case BE_NOSUCHINST:
829 case BE_ENDOFMIBVIEW:
830 ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
831 break;
832
833 case BE_PDU:
834 ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
835 break;
836
837 case BE_ANY:
838 ND_PRINT("[BE_ANY!?]");
839 break;
840
841 default:
842 ND_PRINT("[be!?]");
843 break;
844 }
845 return 0;
846
847 trunc:
848 nd_print_trunc(ndo);
849 return -1;
850 }
851
852 #ifdef notdef
853 /*
854 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
855 * This will work for any ASN.1 stream, not just an SNMP PDU.
856 *
857 * By adding newlines and spaces at the correct places, this would print in
858 * Rose-Normal-Form.
859 *
860 * This is not currently used.
861 */
862 static void
863 asn1_decode(u_char *p, u_int length)
864 {
865 struct be elem;
866 int i = 0;
867
868 while (i >= 0 && length > 0) {
869 i = asn1_parse(ndo, p, length, &elem);
870 if (i >= 0) {
871 ND_PRINT(" ");
872 if (asn1_print(ndo, &elem) < 0)
873 return;
874 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
875 ND_PRINT(" {");
876 asn1_decode(elem.data.raw, elem.asnlen);
877 ND_PRINT(" }");
878 }
879 length -= i;
880 p += i;
881 }
882 }
883 }
884 #endif
885
886 #ifdef USE_LIBSMI
887
888 struct smi2be {
889 SmiBasetype basetype;
890 int be;
891 };
892
893 static const struct smi2be smi2betab[] = {
894 { SMI_BASETYPE_INTEGER32, BE_INT },
895 { SMI_BASETYPE_OCTETSTRING, BE_STR },
896 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
897 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
898 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
899 { SMI_BASETYPE_INTEGER64, BE_NONE },
900 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
901 { SMI_BASETYPE_FLOAT32, BE_NONE },
902 { SMI_BASETYPE_FLOAT64, BE_NONE },
903 { SMI_BASETYPE_FLOAT128, BE_NONE },
904 { SMI_BASETYPE_ENUM, BE_INT },
905 { SMI_BASETYPE_BITS, BE_STR },
906 { SMI_BASETYPE_UNKNOWN, BE_NONE }
907 };
908
909 static int
910 smi_decode_oid(netdissect_options *ndo,
911 struct be *elem, unsigned int *oid,
912 unsigned int oidsize, unsigned int *oidlen)
913 {
914 const u_char *p = (const u_char *)elem->data.raw;
915 uint32_t asnlen = elem->asnlen;
916 uint32_t i = asnlen;
917 int o = 0, first = -1;
918 unsigned int firstval;
919
920 for (*oidlen = 0; i != 0; p++, i--) {
921 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
922 if (GET_U_1(p) & ASN_LONGLEN)
923 continue;
924
925 /*
926 * first subitem encodes two items with 1st*OIDMUX+2nd
927 * (see X.690:1997 clause 8.19 for the details)
928 */
929 if (first < 0) {
930 first = 0;
931 firstval = o / OIDMUX;
932 if (firstval > 2) firstval = 2;
933 o -= firstval * OIDMUX;
934 if (*oidlen < oidsize) {
935 oid[(*oidlen)++] = firstval;
936 }
937 }
938 if (*oidlen < oidsize) {
939 oid[(*oidlen)++] = o;
940 }
941 o = 0;
942 }
943 return 0;
944 }
945
946 static int smi_check_type(SmiBasetype basetype, int be)
947 {
948 int i;
949
950 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
951 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
952 return 1;
953 }
954 }
955
956 return 0;
957 }
958
959 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
960 struct be *elem)
961 {
962 int ok = 1;
963
964 switch (smiType->basetype) {
965 case SMI_BASETYPE_OBJECTIDENTIFIER:
966 case SMI_BASETYPE_OCTETSTRING:
967 if (smiRange->minValue.value.unsigned32
968 == smiRange->maxValue.value.unsigned32) {
969 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
970 } else {
971 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
972 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
973 }
974 break;
975
976 case SMI_BASETYPE_INTEGER32:
977 ok = (elem->data.integer >= smiRange->minValue.value.integer32
978 && elem->data.integer <= smiRange->maxValue.value.integer32);
979 break;
980
981 case SMI_BASETYPE_UNSIGNED32:
982 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
983 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
984 break;
985
986 case SMI_BASETYPE_UNSIGNED64:
987 /* XXX */
988 break;
989
990 /* case SMI_BASETYPE_INTEGER64: SMIng */
991 /* case SMI_BASETYPE_FLOAT32: SMIng */
992 /* case SMI_BASETYPE_FLOAT64: SMIng */
993 /* case SMI_BASETYPE_FLOAT128: SMIng */
994
995 case SMI_BASETYPE_ENUM:
996 case SMI_BASETYPE_BITS:
997 case SMI_BASETYPE_UNKNOWN:
998 ok = 1;
999 break;
1000
1001 default:
1002 ok = 0;
1003 break;
1004 }
1005
1006 return ok;
1007 }
1008
1009 static int smi_check_range(SmiType *smiType, struct be *elem)
1010 {
1011 SmiRange *smiRange;
1012 int ok = 1;
1013
1014 for (smiRange = smiGetFirstRange(smiType);
1015 smiRange;
1016 smiRange = smiGetNextRange(smiRange)) {
1017
1018 ok = smi_check_a_range(smiType, smiRange, elem);
1019
1020 if (ok) {
1021 break;
1022 }
1023 }
1024
1025 if (ok) {
1026 SmiType *parentType;
1027 parentType = smiGetParentType(smiType);
1028 if (parentType) {
1029 ok = smi_check_range(parentType, elem);
1030 }
1031 }
1032
1033 return ok;
1034 }
1035
1036 static SmiNode *
1037 smi_print_variable(netdissect_options *ndo,
1038 struct be *elem, int *status)
1039 {
1040 unsigned int oid[128], oidlen;
1041 SmiNode *smiNode = NULL;
1042 unsigned int i;
1043
1044 if (!nd_smi_module_loaded) {
1045 *status = asn1_print(ndo, elem);
1046 return NULL;
1047 }
1048 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1049 &oidlen);
1050 if (*status < 0)
1051 return NULL;
1052 smiNode = smiGetNodeByOID(oidlen, oid);
1053 if (! smiNode) {
1054 *status = asn1_print(ndo, elem);
1055 return NULL;
1056 }
1057 if (ndo->ndo_vflag) {
1058 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1059 }
1060 ND_PRINT("%s", smiNode->name);
1061 if (smiNode->oidlen < oidlen) {
1062 for (i = smiNode->oidlen; i < oidlen; i++) {
1063 ND_PRINT(".%u", oid[i]);
1064 }
1065 }
1066 *status = 0;
1067 return smiNode;
1068 }
1069
1070 static int
1071 smi_print_value(netdissect_options *ndo,
1072 SmiNode *smiNode, u_short pduid, struct be *elem)
1073 {
1074 unsigned int i, oid[128], oidlen;
1075 SmiType *smiType;
1076 SmiNamedNumber *nn;
1077 int done = 0;
1078
1079 if (! smiNode || ! (smiNode->nodekind
1080 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1081 return asn1_print(ndo, elem);
1082 }
1083
1084 if (elem->type == BE_NOSUCHOBJECT
1085 || elem->type == BE_NOSUCHINST
1086 || elem->type == BE_ENDOFMIBVIEW) {
1087 return asn1_print(ndo, elem);
1088 }
1089
1090 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1091 ND_PRINT("[notNotifyable]");
1092 }
1093
1094 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1095 ND_PRINT("[notReadable]");
1096 }
1097
1098 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1099 ND_PRINT("[notWritable]");
1100 }
1101
1102 if (RESPONSE_CLASS(pduid)
1103 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1104 ND_PRINT("[noAccess]");
1105 }
1106
1107 smiType = smiGetNodeType(smiNode);
1108 if (! smiType) {
1109 return asn1_print(ndo, elem);
1110 }
1111
1112 if (! smi_check_type(smiType->basetype, elem->type)) {
1113 ND_PRINT("[wrongType]");
1114 }
1115
1116 if (! smi_check_range(smiType, elem)) {
1117 ND_PRINT("[outOfRange]");
1118 }
1119
1120 /* resolve bits to named bits */
1121
1122 /* check whether instance identifier is valid */
1123
1124 /* apply display hints (integer, octetstring) */
1125
1126 /* convert instance identifier to index type values */
1127
1128 switch (elem->type) {
1129 case BE_OID:
1130 if (smiType->basetype == SMI_BASETYPE_BITS) {
1131 /* print bit labels */
1132 } else {
1133 if (nd_smi_module_loaded &&
1134 smi_decode_oid(ndo, elem, oid,
1135 sizeof(oid)/sizeof(unsigned int),
1136 &oidlen) == 0) {
1137 smiNode = smiGetNodeByOID(oidlen, oid);
1138 if (smiNode) {
1139 if (ndo->ndo_vflag) {
1140 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1141 }
1142 ND_PRINT("%s", smiNode->name);
1143 if (smiNode->oidlen < oidlen) {
1144 for (i = smiNode->oidlen;
1145 i < oidlen; i++) {
1146 ND_PRINT(".%u", oid[i]);
1147 }
1148 }
1149 done++;
1150 }
1151 }
1152 }
1153 break;
1154
1155 case BE_INT:
1156 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1157 for (nn = smiGetFirstNamedNumber(smiType);
1158 nn;
1159 nn = smiGetNextNamedNumber(nn)) {
1160 if (nn->value.value.integer32
1161 == elem->data.integer) {
1162 ND_PRINT("%s", nn->name);
1163 ND_PRINT("(%d)", elem->data.integer);
1164 done++;
1165 break;
1166 }
1167 }
1168 }
1169 break;
1170 }
1171
1172 if (! done) {
1173 return asn1_print(ndo, elem);
1174 }
1175 return 0;
1176 }
1177 #endif
1178
1179 /*
1180 * General SNMP header
1181 * SEQUENCE {
1182 * version INTEGER {version-1(0)},
1183 * community OCTET STRING,
1184 * data ANY -- PDUs
1185 * }
1186 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1187 * SEQUENCE {
1188 * request-id INTEGER,
1189 * error-status INTEGER,
1190 * error-index INTEGER,
1191 * varbindlist SEQUENCE OF
1192 * SEQUENCE {
1193 * name ObjectName,
1194 * value ObjectValue
1195 * }
1196 * }
1197 * PDU for Trap:
1198 * SEQUENCE {
1199 * enterprise OBJECT IDENTIFIER,
1200 * agent-addr NetworkAddress,
1201 * generic-trap INTEGER,
1202 * specific-trap INTEGER,
1203 * time-stamp TimeTicks,
1204 * varbindlist SEQUENCE OF
1205 * SEQUENCE {
1206 * name ObjectName,
1207 * value ObjectValue
1208 * }
1209 * }
1210 */
1211
1212 /*
1213 * Decode SNMP varBind
1214 */
1215 static void
1216 varbind_print(netdissect_options *ndo,
1217 u_short pduid, const u_char *np, u_int length)
1218 {
1219 struct be elem;
1220 int count = 0, ind;
1221 #ifdef USE_LIBSMI
1222 SmiNode *smiNode = NULL;
1223 #endif
1224 int status;
1225
1226 /* Sequence of varBind */
1227 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1228 return;
1229 if (elem.type != BE_SEQ) {
1230 ND_PRINT("[!SEQ of varbind]");
1231 asn1_print(ndo, &elem);
1232 return;
1233 }
1234 if ((u_int)count < length)
1235 ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1236 /* descend */
1237 length = elem.asnlen;
1238 np = (const u_char *)elem.data.raw;
1239
1240 for (ind = 1; length > 0; ind++) {
1241 const u_char *vbend;
1242 u_int vblength;
1243
1244 ND_PRINT(" ");
1245
1246 /* Sequence */
1247 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1248 return;
1249 if (elem.type != BE_SEQ) {
1250 ND_PRINT("[!varbind]");
1251 asn1_print(ndo, &elem);
1252 return;
1253 }
1254 vbend = np + count;
1255 vblength = length - count;
1256 /* descend */
1257 length = elem.asnlen;
1258 np = (const u_char *)elem.data.raw;
1259
1260 /* objName (OID) */
1261 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1262 return;
1263 if (elem.type != BE_OID) {
1264 ND_PRINT("[objName!=OID]");
1265 asn1_print(ndo, &elem);
1266 return;
1267 }
1268 #ifdef USE_LIBSMI
1269 smiNode = smi_print_variable(ndo, &elem, &status);
1270 #else
1271 status = asn1_print(ndo, &elem);
1272 #endif
1273 if (status < 0)
1274 return;
1275 length -= count;
1276 np += count;
1277
1278 if (pduid != GETREQ && pduid != GETNEXTREQ
1279 && pduid != GETBULKREQ)
1280 ND_PRINT("=");
1281
1282 /* objVal (ANY) */
1283 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1284 return;
1285 if (pduid == GETREQ || pduid == GETNEXTREQ
1286 || pduid == GETBULKREQ) {
1287 if (elem.type != BE_NULL) {
1288 ND_PRINT("[objVal!=NULL]");
1289 if (asn1_print(ndo, &elem) < 0)
1290 return;
1291 }
1292 } else {
1293 if (elem.type != BE_NULL) {
1294 #ifdef USE_LIBSMI
1295 status = smi_print_value(ndo, smiNode, pduid, &elem);
1296 #else
1297 status = asn1_print(ndo, &elem);
1298 #endif
1299 }
1300 if (status < 0)
1301 return;
1302 }
1303 length = vblength;
1304 np = vbend;
1305 }
1306 }
1307
1308 /*
1309 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1310 * GetBulk, Inform, V2Trap, and Report
1311 */
1312 static void
1313 snmppdu_print(netdissect_options *ndo,
1314 u_short pduid, const u_char *np, u_int length)
1315 {
1316 struct be elem;
1317 int count = 0, error_status;
1318
1319 /* reqId (Integer) */
1320 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1321 return;
1322 if (elem.type != BE_INT) {
1323 ND_PRINT("[reqId!=INT]");
1324 asn1_print(ndo, &elem);
1325 return;
1326 }
1327 if (ndo->ndo_vflag)
1328 ND_PRINT("R=%d ", elem.data.integer);
1329 length -= count;
1330 np += count;
1331
1332 /* errorStatus (Integer) */
1333 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1334 return;
1335 if (elem.type != BE_INT) {
1336 ND_PRINT("[errorStatus!=INT]");
1337 asn1_print(ndo, &elem);
1338 return;
1339 }
1340 error_status = 0;
1341 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1342 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1343 && elem.data.integer != 0) {
1344 char errbuf[20];
1345 ND_PRINT("[errorStatus(%s)!=0]",
1346 DECODE_ErrorStatus(elem.data.integer));
1347 } else if (pduid == GETBULKREQ) {
1348 ND_PRINT(" N=%d", elem.data.integer);
1349 } else if (elem.data.integer != 0) {
1350 char errbuf[20];
1351 ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1352 error_status = elem.data.integer;
1353 }
1354 length -= count;
1355 np += count;
1356
1357 /* errorIndex (Integer) */
1358 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1359 return;
1360 if (elem.type != BE_INT) {
1361 ND_PRINT("[errorIndex!=INT]");
1362 asn1_print(ndo, &elem);
1363 return;
1364 }
1365 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1366 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1367 && elem.data.integer != 0)
1368 ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1369 else if (pduid == GETBULKREQ)
1370 ND_PRINT(" M=%d", elem.data.integer);
1371 else if (elem.data.integer != 0) {
1372 if (!error_status)
1373 ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1374 else
1375 ND_PRINT("@%d", elem.data.integer);
1376 } else if (error_status) {
1377 ND_PRINT("[errorIndex==0]");
1378 }
1379 length -= count;
1380 np += count;
1381
1382 varbind_print(ndo, pduid, np, length);
1383 return;
1384 }
1385
1386 /*
1387 * Decode SNMP Trap PDU
1388 */
1389 static void
1390 trappdu_print(netdissect_options *ndo,
1391 const u_char *np, u_int length)
1392 {
1393 struct be elem;
1394 int count = 0, generic;
1395
1396 ND_PRINT(" ");
1397
1398 /* enterprise (oid) */
1399 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1400 return;
1401 if (elem.type != BE_OID) {
1402 ND_PRINT("[enterprise!=OID]");
1403 asn1_print(ndo, &elem);
1404 return;
1405 }
1406 if (asn1_print(ndo, &elem) < 0)
1407 return;
1408 length -= count;
1409 np += count;
1410
1411 ND_PRINT(" ");
1412
1413 /* agent-addr (inetaddr) */
1414 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1415 return;
1416 if (elem.type != BE_INETADDR) {
1417 ND_PRINT("[agent-addr!=INETADDR]");
1418 asn1_print(ndo, &elem);
1419 return;
1420 }
1421 if (asn1_print(ndo, &elem) < 0)
1422 return;
1423 length -= count;
1424 np += count;
1425
1426 /* generic-trap (Integer) */
1427 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1428 return;
1429 if (elem.type != BE_INT) {
1430 ND_PRINT("[generic-trap!=INT]");
1431 asn1_print(ndo, &elem);
1432 return;
1433 }
1434 generic = elem.data.integer;
1435 {
1436 char buf[20];
1437 ND_PRINT(" %s", DECODE_GenericTrap(generic));
1438 }
1439 length -= count;
1440 np += count;
1441
1442 /* specific-trap (Integer) */
1443 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1444 return;
1445 if (elem.type != BE_INT) {
1446 ND_PRINT("[specific-trap!=INT]");
1447 asn1_print(ndo, &elem);
1448 return;
1449 }
1450 if (generic != GT_ENTERPRISE) {
1451 if (elem.data.integer != 0)
1452 ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
1453 } else
1454 ND_PRINT(" s=%d", elem.data.integer);
1455 length -= count;
1456 np += count;
1457
1458 ND_PRINT(" ");
1459
1460 /* time-stamp (TimeTicks) */
1461 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1462 return;
1463 if (elem.type != BE_UNS) { /* XXX */
1464 ND_PRINT("[time-stamp!=TIMETICKS]");
1465 asn1_print(ndo, &elem);
1466 return;
1467 }
1468 if (asn1_print(ndo, &elem) < 0)
1469 return;
1470 length -= count;
1471 np += count;
1472
1473 varbind_print(ndo, TRAP, np, length);
1474 return;
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 }