]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
Change update-test.sh to update multiple tests
[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.h"
75 #include "extract.h"
76
77 #undef OPAQUE /* defined in <wingdi.h> */
78
79 static const char tstr[] = "[|snmp]";
80
81 /*
82 * Universal ASN.1 types
83 * (we only care about the tag values for those allowed in the Internet SMI)
84 */
85 static const char *Universal[] = {
86 "U-0",
87 "Boolean",
88 "Integer",
89 #define INTEGER 2
90 "Bitstring",
91 "String",
92 #define STRING 4
93 "Null",
94 #define ASN_NULL 5
95 "ObjID",
96 #define OBJECTID 6
97 "ObjectDes",
98 "U-8","U-9","U-10","U-11", /* 8-11 */
99 "U-12","U-13","U-14","U-15", /* 12-15 */
100 "Sequence",
101 #define SEQUENCE 16
102 "Set"
103 };
104
105 /*
106 * Application-wide ASN.1 types from the Internet SMI and their tags
107 */
108 static const char *Application[] = {
109 "IpAddress",
110 #define IPADDR 0
111 "Counter",
112 #define COUNTER 1
113 "Gauge",
114 #define GAUGE 2
115 "TimeTicks",
116 #define TIMETICKS 3
117 "Opaque",
118 #define OPAQUE 4
119 "C-5",
120 "Counter64"
121 #define COUNTER64 6
122 };
123
124 /*
125 * Context-specific ASN.1 types for the SNMP PDUs and their tags
126 */
127 static const char *Context[] = {
128 "GetRequest",
129 #define GETREQ 0
130 "GetNextRequest",
131 #define GETNEXTREQ 1
132 "GetResponse",
133 #define GETRESP 2
134 "SetRequest",
135 #define SETREQ 3
136 "Trap",
137 #define TRAP 4
138 "GetBulk",
139 #define GETBULKREQ 5
140 "Inform",
141 #define INFORMREQ 6
142 "V2Trap",
143 #define V2TRAP 7
144 "Report"
145 #define REPORT 8
146 };
147
148 #define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ)
149 #define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
150 #define WRITE_CLASS(x) (x == SETREQ)
151 #define RESPONSE_CLASS(x) (x == GETRESP)
152 #define INTERNAL_CLASS(x) (x == REPORT)
153
154 /*
155 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
156 */
157 static const char *Exceptions[] = {
158 "noSuchObject",
159 #define NOSUCHOBJECT 0
160 "noSuchInstance",
161 #define NOSUCHINSTANCE 1
162 "endOfMibView",
163 #define ENDOFMIBVIEW 2
164 };
165
166 /*
167 * Private ASN.1 types
168 * The Internet SMI does not specify any
169 */
170 static const char *Private[] = {
171 "P-0"
172 };
173
174 /*
175 * error-status values for any SNMP PDU
176 */
177 static const char *ErrorStatus[] = {
178 "noError",
179 "tooBig",
180 "noSuchName",
181 "badValue",
182 "readOnly",
183 "genErr",
184 "noAccess",
185 "wrongType",
186 "wrongLength",
187 "wrongEncoding",
188 "wrongValue",
189 "noCreation",
190 "inconsistentValue",
191 "resourceUnavailable",
192 "commitFailed",
193 "undoFailed",
194 "authorizationError",
195 "notWritable",
196 "inconsistentName"
197 };
198 #define DECODE_ErrorStatus(e) \
199 ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
200 ? ErrorStatus[e] \
201 : (nd_snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
202
203 /*
204 * generic-trap values in the SNMP Trap-PDU
205 */
206 static const char *GenericTrap[] = {
207 "coldStart",
208 "warmStart",
209 "linkDown",
210 "linkUp",
211 "authenticationFailure",
212 "egpNeighborLoss",
213 "enterpriseSpecific"
214 #define GT_ENTERPRISE 6
215 };
216 #define DECODE_GenericTrap(t) \
217 ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
218 ? GenericTrap[t] \
219 : (nd_snprintf(buf, sizeof(buf), "gt=%d", t), buf))
220
221 /*
222 * ASN.1 type class table
223 * Ties together the preceding Universal, Application, Context, and Private
224 * type definitions.
225 */
226 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
227 static const struct {
228 const char *name;
229 const char **Id;
230 int numIDs;
231 } Class[] = {
232 defineCLASS(Universal),
233 #define UNIVERSAL 0
234 defineCLASS(Application),
235 #define APPLICATION 1
236 defineCLASS(Context),
237 #define CONTEXT 2
238 defineCLASS(Private),
239 #define PRIVATE 3
240 defineCLASS(Exceptions),
241 #define EXCEPTIONS 4
242 };
243
244 /*
245 * defined forms for ASN.1 types
246 */
247 static const char *Form[] = {
248 "Primitive",
249 #define PRIMITIVE 0
250 "Constructed",
251 #define CONSTRUCTED 1
252 };
253
254 /*
255 * A structure for the OID tree for the compiled-in MIB.
256 * This is stored as a general-order tree.
257 */
258 static struct obj {
259 const char *desc; /* name of object */
260 u_char oid; /* sub-id following parent */
261 u_char type; /* object type (unused) */
262 struct obj *child, *next; /* child and next sibling pointers */
263 } *objp = NULL;
264
265 /*
266 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
267 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
268 * a value for `mibroot'.
269 *
270 * In particular, this is gross, as this is including initialized structures,
271 * and by right shouldn't be an "include" file.
272 */
273 #include "mib.h"
274
275 /*
276 * This defines a list of OIDs which will be abbreviated on output.
277 * Currently, this includes the prefixes for the Internet MIB, the
278 * private enterprises tree, and the experimental tree.
279 */
280 #define OID_FIRST_OCTET(x, y) (((x)*40) + (y)) /* X.690 8.19.4 */
281
282 #ifndef NO_ABREV_MIB
283 static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
284 #endif
285 #ifndef NO_ABREV_ENTER
286 static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
287 #endif
288 #ifndef NO_ABREV_EXPERI
289 static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
290 #endif
291 #ifndef NO_ABBREV_SNMPMODS
292 static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
293 #endif
294
295 #define OBJ_ABBREV_ENTRY(prefix, obj) \
296 { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
297 static const struct obj_abrev {
298 const char *prefix; /* prefix for this abrev */
299 struct obj *node; /* pointer into object table */
300 const uint8_t *oid; /* ASN.1 encoded OID */
301 size_t oid_len; /* length of OID */
302 } obj_abrev_list[] = {
303 #ifndef NO_ABREV_MIB
304 /* .iso.org.dod.internet.mgmt.mib */
305 OBJ_ABBREV_ENTRY("", mib),
306 #endif
307 #ifndef NO_ABREV_ENTER
308 /* .iso.org.dod.internet.private.enterprises */
309 OBJ_ABBREV_ENTRY("E:", enterprises),
310 #endif
311 #ifndef NO_ABREV_EXPERI
312 /* .iso.org.dod.internet.experimental */
313 OBJ_ABBREV_ENTRY("X:", experimental),
314 #endif
315 #ifndef NO_ABBREV_SNMPMODS
316 /* .iso.org.dod.internet.snmpV2.snmpModules */
317 OBJ_ABBREV_ENTRY("S:", snmpModules),
318 #endif
319 { 0,0,0,0 }
320 };
321
322 /*
323 * This is used in the OID print routine to walk down the object tree
324 * rooted at `mibroot'.
325 */
326 #define OBJ_PRINT(o, suppressdot) \
327 { \
328 if (objp) { \
329 do { \
330 if ((o) == objp->oid) \
331 break; \
332 } while ((objp = objp->next) != NULL); \
333 } \
334 if (objp) { \
335 ND_PRINT(suppressdot?"%s":".%s", objp->desc); \
336 objp = objp->child; \
337 } else \
338 ND_PRINT(suppressdot?"%u":".%u", (o)); \
339 }
340
341 /*
342 * This is the definition for the Any-Data-Type storage used purely for
343 * temporary internal representation while decoding an ASN.1 data stream.
344 */
345 struct be {
346 uint32_t asnlen;
347 union {
348 const uint8_t *raw;
349 int32_t integer;
350 uint32_t uns;
351 const u_char *str;
352 uint64_t uns64;
353 } data;
354 u_short id;
355 u_char form, class; /* tag info */
356 u_char type;
357 #define BE_ANY 255
358 #define BE_NONE 0
359 #define BE_NULL 1
360 #define BE_OCTET 2
361 #define BE_OID 3
362 #define BE_INT 4
363 #define BE_UNS 5
364 #define BE_STR 6
365 #define BE_SEQ 7
366 #define BE_INETADDR 8
367 #define BE_PDU 9
368 #define BE_UNS64 10
369 #define BE_NOSUCHOBJECT 128
370 #define BE_NOSUCHINST 129
371 #define BE_ENDOFMIBVIEW 130
372 };
373
374 /*
375 * SNMP versions recognized by this module
376 */
377 static const char *SnmpVersion[] = {
378 "SNMPv1",
379 #define SNMP_VERSION_1 0
380 "SNMPv2c",
381 #define SNMP_VERSION_2 1
382 "SNMPv2u",
383 #define SNMP_VERSION_2U 2
384 "SNMPv3"
385 #define SNMP_VERSION_3 3
386 };
387
388 /*
389 * Defaults for SNMP PDU components
390 */
391 #define DEF_COMMUNITY "public"
392
393 /*
394 * constants for ASN.1 decoding
395 */
396 #define OIDMUX 40
397 #define ASNLEN_INETADDR 4
398 #define ASN_SHIFT7 7
399 #define ASN_SHIFT8 8
400 #define ASN_BIT8 0x80
401 #define ASN_LONGLEN 0x80
402
403 #define ASN_ID_BITS 0x1f
404 #define ASN_FORM_BITS 0x20
405 #define ASN_FORM_SHIFT 5
406 #define ASN_CLASS_BITS 0xc0
407 #define ASN_CLASS_SHIFT 6
408
409 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
410
411 /*
412 * This decodes the next ASN.1 object in the stream pointed to by "p"
413 * (and of real-length "len") and stores the intermediate data in the
414 * provided BE object.
415 *
416 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
417 * O/w, this returns the number of bytes parsed from "p".
418 */
419 static int
420 asn1_parse(netdissect_options *ndo,
421 const u_char *p, u_int len, struct be *elem)
422 {
423 u_char form, class, id;
424 u_int i, hdr;
425
426 elem->asnlen = 0;
427 elem->type = BE_ANY;
428 if (len < 1) {
429 ND_PRINT("[nothing to parse]");
430 return -1;
431 }
432 ND_TCHECK_1(p);
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 = EXTRACT_U_1(p) & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
442 #ifdef notdef
443 form = (EXTRACT_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)(EXTRACT_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
448 class = (u_char)(EXTRACT_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 ND_TCHECK_1(p);
468 while (EXTRACT_U_1(p) & ASN_BIT8) {
469 if (len < 1) {
470 ND_PRINT("[Xtagfield?]");
471 return -1;
472 }
473 id = (id << 7) | (EXTRACT_U_1(p) & ~ASN_BIT8);
474 len--;
475 hdr++;
476 p++;
477 ND_TCHECK_1(p);
478 }
479 if (len < 1) {
480 ND_PRINT("[Xtagfield?]");
481 return -1;
482 }
483 ND_TCHECK_1(p);
484 elem->id = id = (id << 7) | EXTRACT_U_1(p);
485 --len;
486 ++hdr;
487 ++p;
488 }
489 if (len < 1) {
490 ND_PRINT("[no asnlen]");
491 return -1;
492 }
493 ND_TCHECK_1(p);
494 elem->asnlen = EXTRACT_U_1(p);
495 p++; len--; hdr++;
496 if (elem->asnlen & ASN_BIT8) {
497 uint32_t noct = elem->asnlen % ASN_BIT8;
498 elem->asnlen = 0;
499 if (len < noct) {
500 ND_PRINT("[asnlen? %d<%d]", len, noct);
501 return -1;
502 }
503 ND_TCHECK_LEN(p, noct);
504 for (; noct-- > 0; len--, hdr++) {
505 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | EXTRACT_U_1(p);
506 p++;
507 }
508 }
509 if (len < elem->asnlen) {
510 ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
511 return -1;
512 }
513 if (form >= sizeof(Form)/sizeof(Form[0])) {
514 ND_PRINT("[form?%d]", form);
515 return -1;
516 }
517 if (class >= sizeof(Class)/sizeof(Class[0])) {
518 ND_PRINT("[class?%c/%d]", *Form[form], class);
519 return -1;
520 }
521 if ((int)id >= Class[class].numIDs) {
522 ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
523 return -1;
524 }
525 ND_TCHECK_LEN(p, elem->asnlen);
526
527 switch (form) {
528 case PRIMITIVE:
529 switch (class) {
530 case UNIVERSAL:
531 switch (id) {
532 case STRING:
533 elem->type = BE_STR;
534 elem->data.str = p;
535 break;
536
537 case INTEGER: {
538 int32_t data;
539 elem->type = BE_INT;
540 data = 0;
541
542 if (elem->asnlen == 0) {
543 ND_PRINT("[asnlen=0]");
544 return -1;
545 }
546 if (EXTRACT_U_1(p) & ASN_BIT8) /* negative */
547 data = -1;
548 for (i = elem->asnlen; i != 0; p++, i--)
549 data = (data << ASN_SHIFT8) | EXTRACT_U_1(p);
550 elem->data.integer = data;
551 break;
552 }
553
554 case OBJECTID:
555 elem->type = BE_OID;
556 elem->data.raw = (const uint8_t *)p;
557 break;
558
559 case ASN_NULL:
560 elem->type = BE_NULL;
561 elem->data.raw = NULL;
562 break;
563
564 default:
565 elem->type = BE_OCTET;
566 elem->data.raw = (const uint8_t *)p;
567 ND_PRINT("[P/U/%s]", Class[class].Id[id]);
568 break;
569 }
570 break;
571
572 case APPLICATION:
573 switch (id) {
574 case IPADDR:
575 elem->type = BE_INETADDR;
576 elem->data.raw = (const uint8_t *)p;
577 break;
578
579 case COUNTER:
580 case GAUGE:
581 case TIMETICKS: {
582 uint32_t data;
583 elem->type = BE_UNS;
584 data = 0;
585 for (i = elem->asnlen; i != 0; p++, i--)
586 data = (data << 8) + EXTRACT_U_1(p);
587 elem->data.uns = data;
588 break;
589 }
590
591 case COUNTER64: {
592 uint64_t data64;
593 elem->type = BE_UNS64;
594 data64 = 0;
595 for (i = elem->asnlen; i != 0; p++, i--)
596 data64 = (data64 << 8) + EXTRACT_U_1(p);
597 elem->data.uns64 = data64;
598 break;
599 }
600
601 default:
602 elem->type = BE_OCTET;
603 elem->data.raw = (const uint8_t *)p;
604 ND_PRINT("[P/A/%s]",
605 Class[class].Id[id]);
606 break;
607 }
608 break;
609
610 case CONTEXT:
611 switch (id) {
612 case NOSUCHOBJECT:
613 elem->type = BE_NOSUCHOBJECT;
614 elem->data.raw = NULL;
615 break;
616
617 case NOSUCHINSTANCE:
618 elem->type = BE_NOSUCHINST;
619 elem->data.raw = NULL;
620 break;
621
622 case ENDOFMIBVIEW:
623 elem->type = BE_ENDOFMIBVIEW;
624 elem->data.raw = NULL;
625 break;
626 }
627 break;
628
629 default:
630 ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
631 elem->type = BE_OCTET;
632 elem->data.raw = (const uint8_t *)p;
633 break;
634 }
635 break;
636
637 case CONSTRUCTED:
638 switch (class) {
639 case UNIVERSAL:
640 switch (id) {
641 case SEQUENCE:
642 elem->type = BE_SEQ;
643 elem->data.raw = (const uint8_t *)p;
644 break;
645
646 default:
647 elem->type = BE_OCTET;
648 elem->data.raw = (const uint8_t *)p;
649 ND_PRINT("C/U/%s", Class[class].Id[id]);
650 break;
651 }
652 break;
653
654 case CONTEXT:
655 elem->type = BE_PDU;
656 elem->data.raw = (const uint8_t *)p;
657 break;
658
659 default:
660 elem->type = BE_OCTET;
661 elem->data.raw = (const uint8_t *)p;
662 ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
663 break;
664 }
665 break;
666 }
667 p += elem->asnlen;
668 len -= elem->asnlen;
669 return elem->asnlen + hdr;
670
671 trunc:
672 ND_PRINT("%s", tstr);
673 return -1;
674 }
675
676 static int
677 asn1_print_octets(netdissect_options *ndo, struct be *elem)
678 {
679 const u_char *p = (const u_char *)elem->data.raw;
680 uint32_t asnlen = elem->asnlen;
681 uint32_t i;
682
683 ND_TCHECK_LEN(p, asnlen);
684 for (i = asnlen; i != 0; p++, i--)
685 ND_PRINT("_%.2x", EXTRACT_U_1(p));
686 return 0;
687
688 trunc:
689 ND_PRINT("%s", tstr);
690 return -1;
691 }
692
693 static int
694 asn1_print_string(netdissect_options *ndo, struct be *elem)
695 {
696 int printable = 1, first = 1;
697 const u_char *p;
698 uint32_t asnlen = elem->asnlen;
699 uint32_t i;
700
701 p = elem->data.str;
702 ND_TCHECK_LEN(p, asnlen);
703 for (i = asnlen; printable && i != 0; p++, i--)
704 printable = ND_ISPRINT(EXTRACT_U_1(p));
705 p = elem->data.str;
706 if (printable) {
707 ND_PRINT("\"");
708 if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
709 ND_PRINT("\"");
710 goto trunc;
711 }
712 ND_PRINT("\"");
713 } else {
714 for (i = asnlen; i != 0; p++, i--) {
715 ND_PRINT(first ? "%.2x" : "_%.2x", EXTRACT_U_1(p));
716 first = 0;
717 }
718 }
719 return 0;
720
721 trunc:
722 ND_PRINT("%s", tstr);
723 return -1;
724 }
725
726 /*
727 * Display the ASN.1 object represented by the BE object.
728 * This used to be an integral part of asn1_parse() before the intermediate
729 * BE form was added.
730 */
731 static int
732 asn1_print(netdissect_options *ndo,
733 struct be *elem)
734 {
735 const u_char *p;
736 uint32_t asnlen = elem->asnlen;
737 uint32_t i;
738
739 switch (elem->type) {
740
741 case BE_OCTET:
742 if (asn1_print_octets(ndo, elem) == -1)
743 return -1;
744 break;
745
746 case BE_NULL:
747 break;
748
749 case BE_OID: {
750 int o = 0, first = -1;
751
752 p = (const u_char *)elem->data.raw;
753 i = asnlen;
754 if (!ndo->ndo_nflag && asnlen > 2) {
755 const struct obj_abrev *a = &obj_abrev_list[0];
756 for (; a->node; a++) {
757 if (i < a->oid_len)
758 continue;
759 if (!ND_TTEST_LEN(p, a->oid_len))
760 continue;
761 if (memcmp(a->oid, p, a->oid_len) == 0) {
762 objp = a->node->child;
763 i -= a->oid_len;
764 p += a->oid_len;
765 ND_PRINT("%s", a->prefix);
766 first = 1;
767 break;
768 }
769 }
770 }
771
772 for (; i != 0; p++, i--) {
773 ND_TCHECK_1(p);
774 o = (o << ASN_SHIFT7) + (EXTRACT_U_1(p) & ~ASN_BIT8);
775 if (EXTRACT_U_1(p) & ASN_LONGLEN)
776 continue;
777
778 /*
779 * first subitem encodes two items with
780 * 1st*OIDMUX+2nd
781 * (see X.690:1997 clause 8.19 for the details)
782 */
783 if (first < 0) {
784 int s;
785 if (!ndo->ndo_nflag)
786 objp = mibroot;
787 first = 0;
788 s = o / OIDMUX;
789 if (s > 2) s = 2;
790 OBJ_PRINT(s, first);
791 o -= s * OIDMUX;
792 }
793 OBJ_PRINT(o, first);
794 if (--first < 0)
795 first = 0;
796 o = 0;
797 }
798 break;
799 }
800
801 case BE_INT:
802 ND_PRINT("%d", elem->data.integer);
803 break;
804
805 case BE_UNS:
806 ND_PRINT("%u", elem->data.uns);
807 break;
808
809 case BE_UNS64:
810 ND_PRINT("%" PRIu64, elem->data.uns64);
811 break;
812
813 case BE_STR:
814 if (asn1_print_string(ndo, elem) == -1)
815 return -1;
816 break;
817
818 case BE_SEQ:
819 ND_PRINT("Seq(%u)", elem->asnlen);
820 break;
821
822 case BE_INETADDR:
823 if (asnlen != ASNLEN_INETADDR)
824 ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
825 p = (const u_char *)elem->data.raw;
826 ND_TCHECK_LEN(p, asnlen);
827 for (i = asnlen; i != 0; p++, i--) {
828 ND_PRINT((i == asnlen) ? "%u" : ".%u", EXTRACT_U_1(p));
829 }
830 break;
831
832 case BE_NOSUCHOBJECT:
833 case BE_NOSUCHINST:
834 case BE_ENDOFMIBVIEW:
835 ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
836 break;
837
838 case BE_PDU:
839 ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
840 break;
841
842 case BE_ANY:
843 ND_PRINT("[BE_ANY!?]");
844 break;
845
846 default:
847 ND_PRINT("[be!?]");
848 break;
849 }
850 return 0;
851
852 trunc:
853 ND_PRINT("%s", tstr);
854 return -1;
855 }
856
857 #ifdef notdef
858 /*
859 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
860 * This will work for any ASN.1 stream, not just an SNMP PDU.
861 *
862 * By adding newlines and spaces at the correct places, this would print in
863 * Rose-Normal-Form.
864 *
865 * This is not currently used.
866 */
867 static void
868 asn1_decode(u_char *p, u_int length)
869 {
870 struct be elem;
871 int i = 0;
872
873 while (i >= 0 && length > 0) {
874 i = asn1_parse(ndo, p, length, &elem);
875 if (i >= 0) {
876 ND_PRINT(" ");
877 if (asn1_print(ndo, &elem) < 0)
878 return;
879 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
880 ND_PRINT(" {");
881 asn1_decode(elem.data.raw, elem.asnlen);
882 ND_PRINT(" }");
883 }
884 length -= i;
885 p += i;
886 }
887 }
888 }
889 #endif
890
891 #ifdef USE_LIBSMI
892
893 struct smi2be {
894 SmiBasetype basetype;
895 int be;
896 };
897
898 static const struct smi2be smi2betab[] = {
899 { SMI_BASETYPE_INTEGER32, BE_INT },
900 { SMI_BASETYPE_OCTETSTRING, BE_STR },
901 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
902 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
903 { SMI_BASETYPE_UNSIGNED32, BE_UNS },
904 { SMI_BASETYPE_INTEGER64, BE_NONE },
905 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
906 { SMI_BASETYPE_FLOAT32, BE_NONE },
907 { SMI_BASETYPE_FLOAT64, BE_NONE },
908 { SMI_BASETYPE_FLOAT128, BE_NONE },
909 { SMI_BASETYPE_ENUM, BE_INT },
910 { SMI_BASETYPE_BITS, BE_STR },
911 { SMI_BASETYPE_UNKNOWN, BE_NONE }
912 };
913
914 static int
915 smi_decode_oid(netdissect_options *ndo,
916 struct be *elem, unsigned int *oid,
917 unsigned int oidsize, unsigned int *oidlen)
918 {
919 const u_char *p = (const u_char *)elem->data.raw;
920 uint32_t asnlen = elem->asnlen;
921 uint32_t i = asnlen;
922 int o = 0, first = -1;
923 unsigned int firstval;
924
925 for (*oidlen = 0; i != 0; p++, i--) {
926 ND_TCHECK_1(p);
927 o = (o << ASN_SHIFT7) + (EXTRACT_U_1(p) & ~ASN_BIT8);
928 if (EXTRACT_U_1(p) & ASN_LONGLEN)
929 continue;
930
931 /*
932 * first subitem encodes two items with 1st*OIDMUX+2nd
933 * (see X.690:1997 clause 8.19 for the details)
934 */
935 if (first < 0) {
936 first = 0;
937 firstval = o / OIDMUX;
938 if (firstval > 2) firstval = 2;
939 o -= firstval * OIDMUX;
940 if (*oidlen < oidsize) {
941 oid[(*oidlen)++] = firstval;
942 }
943 }
944 if (*oidlen < oidsize) {
945 oid[(*oidlen)++] = o;
946 }
947 o = 0;
948 }
949 return 0;
950
951 trunc:
952 ND_PRINT("%s", tstr);
953 return -1;
954 }
955
956 static int smi_check_type(SmiBasetype basetype, int be)
957 {
958 int i;
959
960 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
961 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
962 return 1;
963 }
964 }
965
966 return 0;
967 }
968
969 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
970 struct be *elem)
971 {
972 int ok = 1;
973
974 switch (smiType->basetype) {
975 case SMI_BASETYPE_OBJECTIDENTIFIER:
976 case SMI_BASETYPE_OCTETSTRING:
977 if (smiRange->minValue.value.unsigned32
978 == smiRange->maxValue.value.unsigned32) {
979 ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
980 } else {
981 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
982 && elem->asnlen <= smiRange->maxValue.value.unsigned32);
983 }
984 break;
985
986 case SMI_BASETYPE_INTEGER32:
987 ok = (elem->data.integer >= smiRange->minValue.value.integer32
988 && elem->data.integer <= smiRange->maxValue.value.integer32);
989 break;
990
991 case SMI_BASETYPE_UNSIGNED32:
992 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
993 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
994 break;
995
996 case SMI_BASETYPE_UNSIGNED64:
997 /* XXX */
998 break;
999
1000 /* case SMI_BASETYPE_INTEGER64: SMIng */
1001 /* case SMI_BASETYPE_FLOAT32: SMIng */
1002 /* case SMI_BASETYPE_FLOAT64: SMIng */
1003 /* case SMI_BASETYPE_FLOAT128: SMIng */
1004
1005 case SMI_BASETYPE_ENUM:
1006 case SMI_BASETYPE_BITS:
1007 case SMI_BASETYPE_UNKNOWN:
1008 ok = 1;
1009 break;
1010
1011 default:
1012 ok = 0;
1013 break;
1014 }
1015
1016 return ok;
1017 }
1018
1019 static int smi_check_range(SmiType *smiType, struct be *elem)
1020 {
1021 SmiRange *smiRange;
1022 int ok = 1;
1023
1024 for (smiRange = smiGetFirstRange(smiType);
1025 smiRange;
1026 smiRange = smiGetNextRange(smiRange)) {
1027
1028 ok = smi_check_a_range(smiType, smiRange, elem);
1029
1030 if (ok) {
1031 break;
1032 }
1033 }
1034
1035 if (ok) {
1036 SmiType *parentType;
1037 parentType = smiGetParentType(smiType);
1038 if (parentType) {
1039 ok = smi_check_range(parentType, elem);
1040 }
1041 }
1042
1043 return ok;
1044 }
1045
1046 static SmiNode *
1047 smi_print_variable(netdissect_options *ndo,
1048 struct be *elem, int *status)
1049 {
1050 unsigned int oid[128], oidlen;
1051 SmiNode *smiNode = NULL;
1052 unsigned int i;
1053
1054 if (!nd_smi_module_loaded) {
1055 *status = asn1_print(ndo, elem);
1056 return NULL;
1057 }
1058 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1059 &oidlen);
1060 if (*status < 0)
1061 return NULL;
1062 smiNode = smiGetNodeByOID(oidlen, oid);
1063 if (! smiNode) {
1064 *status = asn1_print(ndo, elem);
1065 return NULL;
1066 }
1067 if (ndo->ndo_vflag) {
1068 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1069 }
1070 ND_PRINT("%s", smiNode->name);
1071 if (smiNode->oidlen < oidlen) {
1072 for (i = smiNode->oidlen; i < oidlen; i++) {
1073 ND_PRINT(".%u", oid[i]);
1074 }
1075 }
1076 *status = 0;
1077 return smiNode;
1078 }
1079
1080 static int
1081 smi_print_value(netdissect_options *ndo,
1082 SmiNode *smiNode, u_short pduid, struct be *elem)
1083 {
1084 unsigned int i, oid[128], oidlen;
1085 SmiType *smiType;
1086 SmiNamedNumber *nn;
1087 int done = 0;
1088
1089 if (! smiNode || ! (smiNode->nodekind
1090 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1091 return asn1_print(ndo, elem);
1092 }
1093
1094 if (elem->type == BE_NOSUCHOBJECT
1095 || elem->type == BE_NOSUCHINST
1096 || elem->type == BE_ENDOFMIBVIEW) {
1097 return asn1_print(ndo, elem);
1098 }
1099
1100 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1101 ND_PRINT("[notNotifyable]");
1102 }
1103
1104 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1105 ND_PRINT("[notReadable]");
1106 }
1107
1108 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1109 ND_PRINT("[notWritable]");
1110 }
1111
1112 if (RESPONSE_CLASS(pduid)
1113 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1114 ND_PRINT("[noAccess]");
1115 }
1116
1117 smiType = smiGetNodeType(smiNode);
1118 if (! smiType) {
1119 return asn1_print(ndo, elem);
1120 }
1121
1122 if (! smi_check_type(smiType->basetype, elem->type)) {
1123 ND_PRINT("[wrongType]");
1124 }
1125
1126 if (! smi_check_range(smiType, elem)) {
1127 ND_PRINT("[outOfRange]");
1128 }
1129
1130 /* resolve bits to named bits */
1131
1132 /* check whether instance identifier is valid */
1133
1134 /* apply display hints (integer, octetstring) */
1135
1136 /* convert instance identifier to index type values */
1137
1138 switch (elem->type) {
1139 case BE_OID:
1140 if (smiType->basetype == SMI_BASETYPE_BITS) {
1141 /* print bit labels */
1142 } else {
1143 if (nd_smi_module_loaded &&
1144 smi_decode_oid(ndo, elem, oid,
1145 sizeof(oid)/sizeof(unsigned int),
1146 &oidlen) == 0) {
1147 smiNode = smiGetNodeByOID(oidlen, oid);
1148 if (smiNode) {
1149 if (ndo->ndo_vflag) {
1150 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1151 }
1152 ND_PRINT("%s", smiNode->name);
1153 if (smiNode->oidlen < oidlen) {
1154 for (i = smiNode->oidlen;
1155 i < oidlen; i++) {
1156 ND_PRINT(".%u", oid[i]);
1157 }
1158 }
1159 done++;
1160 }
1161 }
1162 }
1163 break;
1164
1165 case BE_INT:
1166 if (smiType->basetype == SMI_BASETYPE_ENUM) {
1167 for (nn = smiGetFirstNamedNumber(smiType);
1168 nn;
1169 nn = smiGetNextNamedNumber(nn)) {
1170 if (nn->value.value.integer32
1171 == elem->data.integer) {
1172 ND_PRINT("%s", nn->name);
1173 ND_PRINT("(%d)", elem->data.integer);
1174 done++;
1175 break;
1176 }
1177 }
1178 }
1179 break;
1180 }
1181
1182 if (! done) {
1183 return asn1_print(ndo, elem);
1184 }
1185 return 0;
1186 }
1187 #endif
1188
1189 /*
1190 * General SNMP header
1191 * SEQUENCE {
1192 * version INTEGER {version-1(0)},
1193 * community OCTET STRING,
1194 * data ANY -- PDUs
1195 * }
1196 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1197 * SEQUENCE {
1198 * request-id INTEGER,
1199 * error-status INTEGER,
1200 * error-index INTEGER,
1201 * varbindlist SEQUENCE OF
1202 * SEQUENCE {
1203 * name ObjectName,
1204 * value ObjectValue
1205 * }
1206 * }
1207 * PDU for Trap:
1208 * SEQUENCE {
1209 * enterprise OBJECT IDENTIFIER,
1210 * agent-addr NetworkAddress,
1211 * generic-trap INTEGER,
1212 * specific-trap INTEGER,
1213 * time-stamp TimeTicks,
1214 * varbindlist SEQUENCE OF
1215 * SEQUENCE {
1216 * name ObjectName,
1217 * value ObjectValue
1218 * }
1219 * }
1220 */
1221
1222 /*
1223 * Decode SNMP varBind
1224 */
1225 static void
1226 varbind_print(netdissect_options *ndo,
1227 u_short pduid, const u_char *np, u_int length)
1228 {
1229 struct be elem;
1230 int count = 0, ind;
1231 #ifdef USE_LIBSMI
1232 SmiNode *smiNode = NULL;
1233 #endif
1234 int status;
1235
1236 /* Sequence of varBind */
1237 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1238 return;
1239 if (elem.type != BE_SEQ) {
1240 ND_PRINT("[!SEQ of varbind]");
1241 asn1_print(ndo, &elem);
1242 return;
1243 }
1244 if ((u_int)count < length)
1245 ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1246 /* descend */
1247 length = elem.asnlen;
1248 np = (const u_char *)elem.data.raw;
1249
1250 for (ind = 1; length > 0; ind++) {
1251 const u_char *vbend;
1252 u_int vblength;
1253
1254 ND_PRINT(" ");
1255
1256 /* Sequence */
1257 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1258 return;
1259 if (elem.type != BE_SEQ) {
1260 ND_PRINT("[!varbind]");
1261 asn1_print(ndo, &elem);
1262 return;
1263 }
1264 vbend = np + count;
1265 vblength = length - count;
1266 /* descend */
1267 length = elem.asnlen;
1268 np = (const u_char *)elem.data.raw;
1269
1270 /* objName (OID) */
1271 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1272 return;
1273 if (elem.type != BE_OID) {
1274 ND_PRINT("[objName!=OID]");
1275 asn1_print(ndo, &elem);
1276 return;
1277 }
1278 #ifdef USE_LIBSMI
1279 smiNode = smi_print_variable(ndo, &elem, &status);
1280 #else
1281 status = asn1_print(ndo, &elem);
1282 #endif
1283 if (status < 0)
1284 return;
1285 length -= count;
1286 np += count;
1287
1288 if (pduid != GETREQ && pduid != GETNEXTREQ
1289 && pduid != GETBULKREQ)
1290 ND_PRINT("=");
1291
1292 /* objVal (ANY) */
1293 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1294 return;
1295 if (pduid == GETREQ || pduid == GETNEXTREQ
1296 || pduid == GETBULKREQ) {
1297 if (elem.type != BE_NULL) {
1298 ND_PRINT("[objVal!=NULL]");
1299 if (asn1_print(ndo, &elem) < 0)
1300 return;
1301 }
1302 } else {
1303 if (elem.type != BE_NULL) {
1304 #ifdef USE_LIBSMI
1305 status = smi_print_value(ndo, smiNode, pduid, &elem);
1306 #else
1307 status = asn1_print(ndo, &elem);
1308 #endif
1309 }
1310 if (status < 0)
1311 return;
1312 }
1313 length = vblength;
1314 np = vbend;
1315 }
1316 }
1317
1318 /*
1319 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1320 * GetBulk, Inform, V2Trap, and Report
1321 */
1322 static void
1323 snmppdu_print(netdissect_options *ndo,
1324 u_short pduid, const u_char *np, u_int length)
1325 {
1326 struct be elem;
1327 int count = 0, error_status;
1328
1329 /* reqId (Integer) */
1330 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1331 return;
1332 if (elem.type != BE_INT) {
1333 ND_PRINT("[reqId!=INT]");
1334 asn1_print(ndo, &elem);
1335 return;
1336 }
1337 if (ndo->ndo_vflag)
1338 ND_PRINT("R=%d ", elem.data.integer);
1339 length -= count;
1340 np += count;
1341
1342 /* errorStatus (Integer) */
1343 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1344 return;
1345 if (elem.type != BE_INT) {
1346 ND_PRINT("[errorStatus!=INT]");
1347 asn1_print(ndo, &elem);
1348 return;
1349 }
1350 error_status = 0;
1351 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1352 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1353 && elem.data.integer != 0) {
1354 char errbuf[20];
1355 ND_PRINT("[errorStatus(%s)!=0]",
1356 DECODE_ErrorStatus(elem.data.integer));
1357 } else if (pduid == GETBULKREQ) {
1358 ND_PRINT(" N=%d", elem.data.integer);
1359 } else if (elem.data.integer != 0) {
1360 char errbuf[20];
1361 ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1362 error_status = elem.data.integer;
1363 }
1364 length -= count;
1365 np += count;
1366
1367 /* errorIndex (Integer) */
1368 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1369 return;
1370 if (elem.type != BE_INT) {
1371 ND_PRINT("[errorIndex!=INT]");
1372 asn1_print(ndo, &elem);
1373 return;
1374 }
1375 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1376 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1377 && elem.data.integer != 0)
1378 ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1379 else if (pduid == GETBULKREQ)
1380 ND_PRINT(" M=%d", elem.data.integer);
1381 else if (elem.data.integer != 0) {
1382 if (!error_status)
1383 ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1384 else
1385 ND_PRINT("@%d", elem.data.integer);
1386 } else if (error_status) {
1387 ND_PRINT("[errorIndex==0]");
1388 }
1389 length -= count;
1390 np += count;
1391
1392 varbind_print(ndo, pduid, np, length);
1393 return;
1394 }
1395
1396 /*
1397 * Decode SNMP Trap PDU
1398 */
1399 static void
1400 trappdu_print(netdissect_options *ndo,
1401 const u_char *np, u_int length)
1402 {
1403 struct be elem;
1404 int count = 0, generic;
1405
1406 ND_PRINT(" ");
1407
1408 /* enterprise (oid) */
1409 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1410 return;
1411 if (elem.type != BE_OID) {
1412 ND_PRINT("[enterprise!=OID]");
1413 asn1_print(ndo, &elem);
1414 return;
1415 }
1416 if (asn1_print(ndo, &elem) < 0)
1417 return;
1418 length -= count;
1419 np += count;
1420
1421 ND_PRINT(" ");
1422
1423 /* agent-addr (inetaddr) */
1424 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1425 return;
1426 if (elem.type != BE_INETADDR) {
1427 ND_PRINT("[agent-addr!=INETADDR]");
1428 asn1_print(ndo, &elem);
1429 return;
1430 }
1431 if (asn1_print(ndo, &elem) < 0)
1432 return;
1433 length -= count;
1434 np += count;
1435
1436 /* generic-trap (Integer) */
1437 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1438 return;
1439 if (elem.type != BE_INT) {
1440 ND_PRINT("[generic-trap!=INT]");
1441 asn1_print(ndo, &elem);
1442 return;
1443 }
1444 generic = elem.data.integer;
1445 {
1446 char buf[20];
1447 ND_PRINT(" %s", DECODE_GenericTrap(generic));
1448 }
1449 length -= count;
1450 np += count;
1451
1452 /* specific-trap (Integer) */
1453 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1454 return;
1455 if (elem.type != BE_INT) {
1456 ND_PRINT("[specific-trap!=INT]");
1457 asn1_print(ndo, &elem);
1458 return;
1459 }
1460 if (generic != GT_ENTERPRISE) {
1461 if (elem.data.integer != 0)
1462 ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
1463 } else
1464 ND_PRINT(" s=%d", elem.data.integer);
1465 length -= count;
1466 np += count;
1467
1468 ND_PRINT(" ");
1469
1470 /* time-stamp (TimeTicks) */
1471 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1472 return;
1473 if (elem.type != BE_UNS) { /* XXX */
1474 ND_PRINT("[time-stamp!=TIMETICKS]");
1475 asn1_print(ndo, &elem);
1476 return;
1477 }
1478 if (asn1_print(ndo, &elem) < 0)
1479 return;
1480 length -= count;
1481 np += count;
1482
1483 varbind_print(ndo, TRAP, np, length);
1484 return;
1485 }
1486
1487 /*
1488 * Decode arbitrary SNMP PDUs.
1489 */
1490 static void
1491 pdu_print(netdissect_options *ndo,
1492 const u_char *np, u_int length, int version)
1493 {
1494 struct be pdu;
1495 int count = 0;
1496
1497 /* PDU (Context) */
1498 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1499 return;
1500 if (pdu.type != BE_PDU) {
1501 ND_PRINT("[no PDU]");
1502 return;
1503 }
1504 if ((u_int)count < length)
1505 ND_PRINT("[%d extra after PDU]", length - count);
1506 if (ndo->ndo_vflag) {
1507 ND_PRINT("{ ");
1508 }
1509 if (asn1_print(ndo, &pdu) < 0)
1510 return;
1511 ND_PRINT(" ");
1512 /* descend into PDU */
1513 length = pdu.asnlen;
1514 np = (const u_char *)pdu.data.raw;
1515
1516 if (version == SNMP_VERSION_1 &&
1517 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1518 pdu.id == V2TRAP || pdu.id == REPORT)) {
1519 ND_PRINT("[v2 PDU in v1 message]");
1520 return;
1521 }
1522
1523 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1524 ND_PRINT("[v1 PDU in v2 message]");
1525 return;
1526 }
1527
1528 switch (pdu.id) {
1529 case TRAP:
1530 trappdu_print(ndo, np, length);
1531 break;
1532 case GETREQ:
1533 case GETNEXTREQ:
1534 case GETRESP:
1535 case SETREQ:
1536 case GETBULKREQ:
1537 case INFORMREQ:
1538 case V2TRAP:
1539 case REPORT:
1540 snmppdu_print(ndo, pdu.id, np, length);
1541 break;
1542 }
1543
1544 if (ndo->ndo_vflag) {
1545 ND_PRINT(" } ");
1546 }
1547 }
1548
1549 /*
1550 * Decode a scoped SNMP PDU.
1551 */
1552 static void
1553 scopedpdu_print(netdissect_options *ndo,
1554 const u_char *np, u_int length, int version)
1555 {
1556 struct be elem;
1557 int count = 0;
1558
1559 /* Sequence */
1560 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1561 return;
1562 if (elem.type != BE_SEQ) {
1563 ND_PRINT("[!scoped PDU]");
1564 asn1_print(ndo, &elem);
1565 return;
1566 }
1567 length = elem.asnlen;
1568 np = (const u_char *)elem.data.raw;
1569
1570 /* contextEngineID (OCTET STRING) */
1571 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1572 return;
1573 if (elem.type != BE_STR) {
1574 ND_PRINT("[contextEngineID!=STR]");
1575 asn1_print(ndo, &elem);
1576 return;
1577 }
1578 length -= count;
1579 np += count;
1580
1581 ND_PRINT("E=");
1582 if (asn1_print_octets(ndo, &elem) == -1)
1583 return;
1584 ND_PRINT(" ");
1585
1586 /* contextName (OCTET STRING) */
1587 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1588 return;
1589 if (elem.type != BE_STR) {
1590 ND_PRINT("[contextName!=STR]");
1591 asn1_print(ndo, &elem);
1592 return;
1593 }
1594 length -= count;
1595 np += count;
1596
1597 ND_PRINT("C=");
1598 if (asn1_print_string(ndo, &elem) == -1)
1599 return;
1600 ND_PRINT(" ");
1601
1602 pdu_print(ndo, np, length, version);
1603 }
1604
1605 /*
1606 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1607 */
1608 static void
1609 community_print(netdissect_options *ndo,
1610 const u_char *np, u_int length, int version)
1611 {
1612 struct be elem;
1613 int count = 0;
1614
1615 /* Community (String) */
1616 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1617 return;
1618 if (elem.type != BE_STR) {
1619 ND_PRINT("[comm!=STR]");
1620 asn1_print(ndo, &elem);
1621 return;
1622 }
1623 /* default community */
1624 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1625 strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1626 sizeof(DEF_COMMUNITY) - 1) == 0)) {
1627 /* ! "public" */
1628 ND_PRINT("C=");
1629 if (asn1_print_string(ndo, &elem) == -1)
1630 return;
1631 ND_PRINT(" ");
1632 }
1633 length -= count;
1634 np += count;
1635
1636 pdu_print(ndo, np, length, version);
1637 }
1638
1639 /*
1640 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1641 */
1642 static void
1643 usm_print(netdissect_options *ndo,
1644 const u_char *np, u_int length)
1645 {
1646 struct be elem;
1647 int count = 0;
1648
1649 /* Sequence */
1650 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1651 return;
1652 if (elem.type != BE_SEQ) {
1653 ND_PRINT("[!usm]");
1654 asn1_print(ndo, &elem);
1655 return;
1656 }
1657 length = elem.asnlen;
1658 np = (const u_char *)elem.data.raw;
1659
1660 /* msgAuthoritativeEngineID (OCTET STRING) */
1661 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1662 return;
1663 if (elem.type != BE_STR) {
1664 ND_PRINT("[msgAuthoritativeEngineID!=STR]");
1665 asn1_print(ndo, &elem);
1666 return;
1667 }
1668 length -= count;
1669 np += count;
1670
1671 /* msgAuthoritativeEngineBoots (INTEGER) */
1672 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1673 return;
1674 if (elem.type != BE_INT) {
1675 ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
1676 asn1_print(ndo, &elem);
1677 return;
1678 }
1679 if (ndo->ndo_vflag)
1680 ND_PRINT("B=%d ", elem.data.integer);
1681 length -= count;
1682 np += count;
1683
1684 /* msgAuthoritativeEngineTime (INTEGER) */
1685 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1686 return;
1687 if (elem.type != BE_INT) {
1688 ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
1689 asn1_print(ndo, &elem);
1690 return;
1691 }
1692 if (ndo->ndo_vflag)
1693 ND_PRINT("T=%d ", elem.data.integer);
1694 length -= count;
1695 np += count;
1696
1697 /* msgUserName (OCTET STRING) */
1698 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1699 return;
1700 if (elem.type != BE_STR) {
1701 ND_PRINT("[msgUserName!=STR]");
1702 asn1_print(ndo, &elem);
1703 return;
1704 }
1705 length -= count;
1706 np += count;
1707
1708 ND_PRINT("U=");
1709 if (asn1_print_string(ndo, &elem) == -1)
1710 return;
1711 ND_PRINT(" ");
1712
1713 /* msgAuthenticationParameters (OCTET STRING) */
1714 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1715 return;
1716 if (elem.type != BE_STR) {
1717 ND_PRINT("[msgAuthenticationParameters!=STR]");
1718 asn1_print(ndo, &elem);
1719 return;
1720 }
1721 length -= count;
1722 np += count;
1723
1724 /* msgPrivacyParameters (OCTET STRING) */
1725 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1726 return;
1727 if (elem.type != BE_STR) {
1728 ND_PRINT("[msgPrivacyParameters!=STR]");
1729 asn1_print(ndo, &elem);
1730 return;
1731 }
1732 length -= count;
1733 np += count;
1734
1735 if ((u_int)count < length)
1736 ND_PRINT("[%d extra after usm SEQ]", length - count);
1737 }
1738
1739 /*
1740 * Decode SNMPv3 Message Header (SNMPv3)
1741 */
1742 static void
1743 v3msg_print(netdissect_options *ndo,
1744 const u_char *np, u_int length)
1745 {
1746 struct be elem;
1747 int count = 0;
1748 u_char flags;
1749 int model;
1750 const u_char *xnp = np;
1751 int xlength = length;
1752
1753 /* Sequence */
1754 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1755 return;
1756 if (elem.type != BE_SEQ) {
1757 ND_PRINT("[!message]");
1758 asn1_print(ndo, &elem);
1759 return;
1760 }
1761 length = elem.asnlen;
1762 np = (const u_char *)elem.data.raw;
1763
1764 if (ndo->ndo_vflag) {
1765 ND_PRINT("{ ");
1766 }
1767
1768 /* msgID (INTEGER) */
1769 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1770 return;
1771 if (elem.type != BE_INT) {
1772 ND_PRINT("[msgID!=INT]");
1773 asn1_print(ndo, &elem);
1774 return;
1775 }
1776 length -= count;
1777 np += count;
1778
1779 /* msgMaxSize (INTEGER) */
1780 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1781 return;
1782 if (elem.type != BE_INT) {
1783 ND_PRINT("[msgMaxSize!=INT]");
1784 asn1_print(ndo, &elem);
1785 return;
1786 }
1787 length -= count;
1788 np += count;
1789
1790 /* msgFlags (OCTET STRING) */
1791 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1792 return;
1793 if (elem.type != BE_STR) {
1794 ND_PRINT("[msgFlags!=STR]");
1795 asn1_print(ndo, &elem);
1796 return;
1797 }
1798 if (elem.asnlen != 1) {
1799 ND_PRINT("[msgFlags size %d]", elem.asnlen);
1800 return;
1801 }
1802 flags = EXTRACT_U_1(elem.data.str);
1803 if (flags != 0x00 && flags != 0x01 && flags != 0x03
1804 && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1805 ND_PRINT("[msgFlags=0x%02X]", flags);
1806 return;
1807 }
1808 length -= count;
1809 np += count;
1810
1811 ND_PRINT("F=%s%s%s ",
1812 flags & 0x01 ? "a" : "",
1813 flags & 0x02 ? "p" : "",
1814 flags & 0x04 ? "r" : "");
1815
1816 /* msgSecurityModel (INTEGER) */
1817 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1818 return;
1819 if (elem.type != BE_INT) {
1820 ND_PRINT("[msgSecurityModel!=INT]");
1821 asn1_print(ndo, &elem);
1822 return;
1823 }
1824 model = elem.data.integer;
1825 length -= count;
1826 np += count;
1827
1828 if ((u_int)count < length)
1829 ND_PRINT("[%d extra after message SEQ]", length - count);
1830
1831 if (ndo->ndo_vflag) {
1832 ND_PRINT("} ");
1833 }
1834
1835 if (model == 3) {
1836 if (ndo->ndo_vflag) {
1837 ND_PRINT("{ USM ");
1838 }
1839 } else {
1840 ND_PRINT("[security model %d]", model);
1841 return;
1842 }
1843
1844 np = xnp + (np - xnp);
1845 length = xlength - (np - xnp);
1846
1847 /* msgSecurityParameters (OCTET STRING) */
1848 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1849 return;
1850 if (elem.type != BE_STR) {
1851 ND_PRINT("[msgSecurityParameters!=STR]");
1852 asn1_print(ndo, &elem);
1853 return;
1854 }
1855 length -= count;
1856 np += count;
1857
1858 if (model == 3) {
1859 usm_print(ndo, elem.data.str, elem.asnlen);
1860 if (ndo->ndo_vflag) {
1861 ND_PRINT("} ");
1862 }
1863 }
1864
1865 if (ndo->ndo_vflag) {
1866 ND_PRINT("{ ScopedPDU ");
1867 }
1868
1869 scopedpdu_print(ndo, np, length, 3);
1870
1871 if (ndo->ndo_vflag) {
1872 ND_PRINT("} ");
1873 }
1874 }
1875
1876 /*
1877 * Decode SNMP header and pass on to PDU printing routines
1878 */
1879 void
1880 snmp_print(netdissect_options *ndo,
1881 const u_char *np, u_int length)
1882 {
1883 struct be elem;
1884 int count = 0;
1885 int version = 0;
1886
1887 ndo->ndo_protocol = "snmp";
1888 ND_PRINT(" ");
1889
1890 /* initial Sequence */
1891 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1892 return;
1893 if (elem.type != BE_SEQ) {
1894 ND_PRINT("[!init SEQ]");
1895 asn1_print(ndo, &elem);
1896 return;
1897 }
1898 if ((u_int)count < length)
1899 ND_PRINT("[%d extra after iSEQ]", length - count);
1900 /* descend */
1901 length = elem.asnlen;
1902 np = (const u_char *)elem.data.raw;
1903
1904 /* Version (INTEGER) */
1905 if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1906 return;
1907 if (elem.type != BE_INT) {
1908 ND_PRINT("[version!=INT]");
1909 asn1_print(ndo, &elem);
1910 return;
1911 }
1912
1913 switch (elem.data.integer) {
1914 case SNMP_VERSION_1:
1915 case SNMP_VERSION_2:
1916 case SNMP_VERSION_3:
1917 if (ndo->ndo_vflag)
1918 ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
1919 break;
1920 default:
1921 ND_PRINT("SNMP [version = %d]", elem.data.integer);
1922 return;
1923 }
1924 version = elem.data.integer;
1925 length -= count;
1926 np += count;
1927
1928 switch (version) {
1929 case SNMP_VERSION_1:
1930 case SNMP_VERSION_2:
1931 community_print(ndo, np, length, version);
1932 break;
1933 case SNMP_VERSION_3:
1934 v3msg_print(ndo, np, length);
1935 break;
1936 default:
1937 ND_PRINT("[version = %d]", elem.data.integer);
1938 break;
1939 }
1940
1941 if (ndo->ndo_vflag) {
1942 ND_PRINT("} ");
1943 }
1944 }