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