]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
patches to help build on Linux 2.2
[tcpdump] / print-snmp.c
1 /*
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by John Robert LoVerso.
11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * This implementation has been influenced by the CMU SNMP release,
16 * by Steve Waldbusser. However, this shares no code with that system.
17 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
18 * Earlier forms of this implementation were derived and/or inspired by an
19 * awk script originally written by C. Philip Wood of LANL (but later
20 * heavily modified by John Robert LoVerso). The copyright notice for
21 * that work is preserved below, even though it may not rightly apply
22 * to this file.
23 *
24 * This started out as a very simple program, but the incremental decoding
25 * (into the BE structure) complicated things.
26 *
27 # Los Alamos National Laboratory
28 #
29 # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
30 # This software was produced under a U.S. Government contract
31 # (W-7405-ENG-36) by Los Alamos National Laboratory, which is
32 # operated by the University of California for the U.S. Department
33 # of Energy. The U.S. Government is licensed to use, reproduce,
34 # and distribute this software. Permission is granted to the
35 # public to copy and use this software without charge, provided
36 # that this Notice and any statement of authorship are reproduced
37 # on all copies. Neither the Government nor the University makes
38 # any warranty, express or implied, or assumes any liability or
39 # responsibility for the use of this software.
40 # @(#)snmp.awk.x 1.1 (LANL) 1/15/90
41 */
42
43 #ifndef lint
44 static const char rcsid[] =
45 "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.35 1999-10-17 21:37:15 mcr Exp $ (LBL)";
46 #endif
47
48 #include <sys/param.h>
49 #include <sys/time.h>
50
51 #include <ctype.h>
52 #ifdef HAVE_MEMORY_H
53 #include <memory.h>
54 #endif
55 #include <stdio.h>
56 #include <string.h>
57
58 #include "interface.h"
59 #include "addrtoname.h"
60
61 /*
62 * Universal ASN.1 types
63 * (we only care about the tag values for those allowed in the Internet SMI)
64 */
65 char *Universal[] = {
66 "U-0",
67 "Boolean",
68 "Integer",
69 #define INTEGER 2
70 "Bitstring",
71 "String",
72 #define STRING 4
73 "Null",
74 #define ASN_NULL 5
75 "ObjID",
76 #define OBJECTID 6
77 "ObjectDes",
78 "U-8","U-9","U-10","U-11", /* 8-11 */
79 "U-12","U-13","U-14","U-15", /* 12-15 */
80 "Sequence",
81 #define SEQUENCE 16
82 "Set"
83 };
84
85 /*
86 * Application-wide ASN.1 types from the Internet SMI and their tags
87 */
88 char *Application[] = {
89 "IpAddress",
90 #define IPADDR 0
91 "Counter",
92 #define COUNTER 1
93 "Gauge",
94 #define GAUGE 2
95 "TimeTicks",
96 #define TIMETICKS 3
97 "Opaque"
98 };
99
100 /*
101 * Context-specific ASN.1 types for the SNMP PDUs and their tags
102 */
103 char *Context[] = {
104 "GetRequest",
105 #define GETREQ 0
106 "GetNextRequest",
107 #define GETNEXTREQ 1
108 "GetResponse",
109 #define GETRESP 2
110 "SetRequest",
111 #define SETREQ 3
112 "Trap"
113 #define TRAP 4
114 };
115
116 /*
117 * Private ASN.1 types
118 * The Internet SMI does not specify any
119 */
120 char *Private[] = {
121 "P-0"
122 };
123
124 /*
125 * error-status values for any SNMP PDU
126 */
127 char *ErrorStatus[] = {
128 "noError",
129 "tooBig",
130 "noSuchName",
131 "badValue",
132 "readOnly",
133 "genErr"
134 };
135 #define DECODE_ErrorStatus(e) \
136 ( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
137 ? ErrorStatus[e] : (sprintf(errbuf, "err=%u", e), errbuf))
138
139 /*
140 * generic-trap values in the SNMP Trap-PDU
141 */
142 char *GenericTrap[] = {
143 "coldStart",
144 "warmStart",
145 "linkDown",
146 "linkUp",
147 "authenticationFailure",
148 "egpNeighborLoss",
149 "enterpriseSpecific"
150 #define GT_ENTERPRISE 7
151 };
152 #define DECODE_GenericTrap(t) \
153 ( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
154 ? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf))
155
156 /*
157 * ASN.1 type class table
158 * Ties together the preceding Universal, Application, Context, and Private
159 * type definitions.
160 */
161 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
162 struct {
163 char *name;
164 char **Id;
165 int numIDs;
166 } Class[] = {
167 defineCLASS(Universal),
168 #define UNIVERSAL 0
169 defineCLASS(Application),
170 #define APPLICATION 1
171 defineCLASS(Context),
172 #define CONTEXT 2
173 defineCLASS(Private),
174 #define PRIVATE 3
175 };
176
177 /*
178 * defined forms for ASN.1 types
179 */
180 char *Form[] = {
181 "Primitive",
182 #define PRIMITIVE 0
183 "Constructed",
184 #define CONSTRUCTED 1
185 };
186
187 /*
188 * A structure for the OID tree for the compiled-in MIB.
189 * This is stored as a general-order tree.
190 */
191 struct obj {
192 char *desc; /* name of object */
193 u_char oid; /* sub-id following parent */
194 u_char type; /* object type (unused) */
195 struct obj *child, *next; /* child and next sibling pointers */
196 } *objp = NULL;
197
198 /*
199 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
200 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
201 * a value for `mibroot'.
202 *
203 * In particular, this is gross, as this is including initialized structures,
204 * and by right shouldn't be an "include" file.
205 */
206 #include "mib.h"
207
208 /*
209 * This defines a list of OIDs which will be abbreviated on output.
210 * Currently, this includes the prefixes for the Internet MIB, the
211 * private enterprises tree, and the experimental tree.
212 */
213 struct obj_abrev {
214 char *prefix; /* prefix for this abrev */
215 struct obj *node; /* pointer into object table */
216 char *oid; /* ASN.1 encoded OID */
217 } obj_abrev_list[] = {
218 #ifndef NO_ABREV_MIB
219 /* .iso.org.dod.internet.mgmt.mib */
220 { "", &_mib_obj, "\53\6\1\2\1" },
221 #endif
222 #ifndef NO_ABREV_ENTER
223 /* .iso.org.dod.internet.private.enterprises */
224 { "E:", &_enterprises_obj, "\53\6\1\4\1" },
225 #endif
226 #ifndef NO_ABREV_EXPERI
227 /* .iso.org.dod.internet.experimental */
228 { "X:", &_experimental_obj, "\53\6\1\3" },
229 #endif
230 { 0,0,0 }
231 };
232
233 /*
234 * This is used in the OID print routine to walk down the object tree
235 * rooted at `mibroot'.
236 */
237 #define OBJ_PRINT(o, suppressdot) \
238 { \
239 if (objp) { \
240 do { \
241 if ((o) == objp->oid) \
242 break; \
243 } while ((objp = objp->next) != NULL); \
244 } \
245 if (objp) { \
246 printf(suppressdot?"%s":".%s", objp->desc); \
247 objp = objp->child; \
248 } else \
249 printf(suppressdot?"%u":".%u", (o)); \
250 }
251
252 /*
253 * This is the definition for the Any-Data-Type storage used purely for
254 * temporary internal representation while decoding an ASN.1 data stream.
255 */
256 struct be {
257 u_int32_t asnlen;
258 union {
259 caddr_t raw;
260 int32_t integer;
261 u_int32_t uns;
262 const u_char *str;
263 } data;
264 u_short id;
265 u_char form, class; /* tag info */
266 u_char type;
267 #define BE_ANY 255
268 #define BE_NONE 0
269 #define BE_NULL 1
270 #define BE_OCTET 2
271 #define BE_OID 3
272 #define BE_INT 4
273 #define BE_UNS 5
274 #define BE_STR 6
275 #define BE_SEQ 7
276 #define BE_INETADDR 8
277 #define BE_PDU 9
278 };
279
280 /*
281 * Defaults for SNMP PDU components
282 */
283 #define DEF_COMMUNITY "public"
284 #define DEF_VERSION 0
285
286 /*
287 * constants for ASN.1 decoding
288 */
289 #define OIDMUX 40
290 #define ASNLEN_INETADDR 4
291 #define ASN_SHIFT7 7
292 #define ASN_SHIFT8 8
293 #define ASN_BIT8 0x80
294 #define ASN_LONGLEN 0x80
295
296 #define ASN_ID_BITS 0x1f
297 #define ASN_FORM_BITS 0x20
298 #define ASN_FORM_SHIFT 5
299 #define ASN_CLASS_BITS 0xc0
300 #define ASN_CLASS_SHIFT 6
301
302 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
303
304 /*
305 * truncated==1 means the packet was complete, but we don't have all of
306 * it to decode.
307 */
308 static int truncated;
309 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
310
311 /*
312 * This decodes the next ASN.1 object in the stream pointed to by "p"
313 * (and of real-length "len") and stores the intermediate data in the
314 * provided BE object.
315 *
316 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
317 * O/w, this returns the number of bytes parsed from "p".
318 */
319 static int
320 asn1_parse(register const u_char *p, u_int len, struct be *elem)
321 {
322 u_char form, class, id;
323 int i, hdr;
324
325 elem->asnlen = 0;
326 elem->type = BE_ANY;
327 if (len < 1) {
328 ifNotTruncated puts("[nothing to parse], stdout");
329 return -1;
330 }
331
332 /*
333 * it would be nice to use a bit field, but you can't depend on them.
334 * +---+---+---+---+---+---+---+---+
335 * + class |frm| id |
336 * +---+---+---+---+---+---+---+---+
337 * 7 6 5 4 3 2 1 0
338 */
339 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
340 #ifdef notdef
341 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
342 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
343 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
344 #else
345 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
346 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
347 #endif
348 elem->form = form;
349 elem->class = class;
350 elem->id = id;
351 if (vflag)
352 printf("|%.2x", *p);
353 p++; len--; hdr = 1;
354 /* extended tag field */
355 if (id == ASN_ID_EXT) {
356 for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
357 if (vflag)
358 printf("|%.2x", *p);
359 id = (id << 7) | (*p & ~ASN_BIT8);
360 }
361 if (len == 0 && *p & ASN_BIT8) {
362 ifNotTruncated fputs("[Xtagfield?]", stdout);
363 return -1;
364 }
365 elem->id = id = (id << 7) | *p;
366 --len;
367 ++hdr;
368 ++p;
369 }
370 if (len < 1) {
371 ifNotTruncated fputs("[no asnlen]", stdout);
372 return -1;
373 }
374 elem->asnlen = *p;
375 if (vflag)
376 printf("|%.2x", *p);
377 p++; len--; hdr++;
378 if (elem->asnlen & ASN_BIT8) {
379 int noct = elem->asnlen % ASN_BIT8;
380 elem->asnlen = 0;
381 if (len < noct) {
382 ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
383 return -1;
384 }
385 for (; noct-- > 0; len--, hdr++) {
386 if (vflag)
387 printf("|%.2x", *p);
388 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
389 }
390 }
391 if (len < elem->asnlen) {
392 if (!truncated) {
393 printf("[len%d<asnlen%u]", len, elem->asnlen);
394 return -1;
395 }
396 /* maybe should check at least 4? */
397 elem->asnlen = len;
398 }
399 if (form >= sizeof(Form)/sizeof(Form[0])) {
400 ifNotTruncated printf("[form?%d]", form);
401 return -1;
402 }
403 if (class >= sizeof(Class)/sizeof(Class[0])) {
404 ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
405 return -1;
406 }
407 if ((int)id >= Class[class].numIDs) {
408 ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
409 Class[class].name, id);
410 return -1;
411 }
412
413 switch (form) {
414 case PRIMITIVE:
415 switch (class) {
416 case UNIVERSAL:
417 switch (id) {
418 case STRING:
419 elem->type = BE_STR;
420 elem->data.str = p;
421 break;
422
423 case INTEGER: {
424 register int32_t data;
425 elem->type = BE_INT;
426 data = 0;
427
428 if (*p & ASN_BIT8) /* negative */
429 data = -1;
430 for (i = elem->asnlen; i-- > 0; p++)
431 data = (data << ASN_SHIFT8) | *p;
432 elem->data.integer = data;
433 break;
434 }
435
436 case OBJECTID:
437 elem->type = BE_OID;
438 elem->data.raw = (caddr_t)p;
439 break;
440
441 case ASN_NULL:
442 elem->type = BE_NULL;
443 elem->data.raw = NULL;
444 break;
445
446 default:
447 elem->type = BE_OCTET;
448 elem->data.raw = (caddr_t)p;
449 printf("[P/U/%s]",
450 Class[class].Id[id]);
451 break;
452 }
453 break;
454
455 case APPLICATION:
456 switch (id) {
457 case IPADDR:
458 elem->type = BE_INETADDR;
459 elem->data.raw = (caddr_t)p;
460 break;
461
462 case COUNTER:
463 case GAUGE:
464 case TIMETICKS: {
465 register u_int32_t data;
466 elem->type = BE_UNS;
467 data = 0;
468 for (i = elem->asnlen; i-- > 0; p++)
469 data = (data << 8) + *p;
470 elem->data.uns = data;
471 break;
472 }
473
474 default:
475 elem->type = BE_OCTET;
476 elem->data.raw = (caddr_t)p;
477 printf("[P/A/%s]",
478 Class[class].Id[id]);
479 break;
480 }
481 break;
482
483 default:
484 elem->type = BE_OCTET;
485 elem->data.raw = (caddr_t)p;
486 printf("[P/%s/%s]",
487 Class[class].name, Class[class].Id[id]);
488 break;
489 }
490 break;
491
492 case CONSTRUCTED:
493 switch (class) {
494 case UNIVERSAL:
495 switch (id) {
496 case SEQUENCE:
497 elem->type = BE_SEQ;
498 elem->data.raw = (caddr_t)p;
499 break;
500
501 default:
502 elem->type = BE_OCTET;
503 elem->data.raw = (caddr_t)p;
504 printf("C/U/%s", Class[class].Id[id]);
505 break;
506 }
507 break;
508
509 case CONTEXT:
510 elem->type = BE_PDU;
511 elem->data.raw = (caddr_t)p;
512 break;
513
514 default:
515 elem->type = BE_OCTET;
516 elem->data.raw = (caddr_t)p;
517 printf("C/%s/%s",
518 Class[class].name, Class[class].Id[id]);
519 break;
520 }
521 break;
522 }
523 p += elem->asnlen;
524 len -= elem->asnlen;
525 return elem->asnlen + hdr;
526 }
527
528 /*
529 * Display the ASN.1 object represented by the BE object.
530 * This used to be an integral part of asn1_parse() before the intermediate
531 * BE form was added.
532 */
533 static void
534 asn1_print(struct be *elem)
535 {
536 u_char *p = (u_char *)elem->data.raw;
537 u_int32_t asnlen = elem->asnlen;
538 int i;
539
540 switch (elem->type) {
541
542 case BE_OCTET:
543 for (i = asnlen; i-- > 0; p++);
544 printf("_%.2x", *p);
545 break;
546
547 case BE_NULL:
548 break;
549
550 case BE_OID: {
551 int o = 0, first = -1, i = asnlen;
552
553 if (!nflag && asnlen > 2) {
554 struct obj_abrev *a = &obj_abrev_list[0];
555 for (; a->node; a++) {
556 if (!memcmp(a->oid, (char *)p,
557 strlen(a->oid))) {
558 objp = a->node->child;
559 i -= strlen(a->oid);
560 p += strlen(a->oid);
561 fputs(a->prefix, stdout);
562 first = 1;
563 break;
564 }
565 }
566 }
567 for (; i-- > 0; p++) {
568 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
569 if (*p & ASN_LONGLEN)
570 continue;
571
572 /*
573 * first subitem encodes two items with 1st*OIDMUX+2nd
574 */
575 if (first < 0) {
576 if (!nflag)
577 objp = mibroot;
578 first = 0;
579 OBJ_PRINT(o/OIDMUX, first);
580 o %= OIDMUX;
581 }
582 OBJ_PRINT(o, first);
583 if (--first < 0)
584 first = 0;
585 o = 0;
586 }
587 break;
588 }
589
590 case BE_INT:
591 printf("%d", elem->data.integer);
592 break;
593
594 case BE_UNS:
595 printf("%d", elem->data.uns);
596 break;
597
598 case BE_STR: {
599 register int printable = 1, first = 1;
600 const u_char *p = elem->data.str;
601 for (i = asnlen; printable && i-- > 0; p++)
602 printable = isprint(*p) || isspace(*p);
603 p = elem->data.str;
604 if (printable) {
605 putchar('"');
606 (void)fn_print(p, p + asnlen);
607 putchar('"');
608 } else
609 for (i = asnlen; i-- > 0; p++) {
610 printf(first ? "%.2x" : "_%.2x", *p);
611 first = 0;
612 }
613 break;
614 }
615
616 case BE_SEQ:
617 printf("Seq(%u)", elem->asnlen);
618 break;
619
620 case BE_INETADDR: {
621 char sep;
622 if (asnlen != ASNLEN_INETADDR)
623 printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
624 sep='[';
625 for (i = asnlen; i-- > 0; p++) {
626 printf("%c%u", sep, *p);
627 sep='.';
628 }
629 putchar(']');
630 break;
631 }
632
633 case BE_PDU:
634 printf("%s(%u)",
635 Class[CONTEXT].Id[elem->id], elem->asnlen);
636 break;
637
638 case BE_ANY:
639 fputs("[BE_ANY!?]", stdout);
640 break;
641
642 default:
643 fputs("[be!?]", stdout);
644 break;
645 }
646 }
647
648 #ifdef notdef
649 /*
650 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
651 * This will work for any ASN.1 stream, not just an SNMP PDU.
652 *
653 * By adding newlines and spaces at the correct places, this would print in
654 * Rose-Normal-Form.
655 *
656 * This is not currently used.
657 */
658 static void
659 asn1_decode(u_char *p, u_int length)
660 {
661 struct be elem;
662 int i = 0;
663
664 while (i >= 0 && length > 0) {
665 i = asn1_parse(p, length, &elem);
666 if (i >= 0) {
667 fputs(" ", stdout);
668 asn1_print(&elem);
669 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
670 fputs(" {", stdout);
671 asn1_decode(elem.data.raw, elem.asnlen);
672 fputs(" }", stdout);
673 }
674 length -= i;
675 p += i;
676 }
677 }
678 }
679 #endif
680
681 /*
682 * General SNMP header
683 * SEQUENCE {
684 * version INTEGER {version-1(0)},
685 * community OCTET STRING,
686 * data ANY -- PDUs
687 * }
688 * PDUs for all but Trap: (see rfc1157 from page 15 on)
689 * SEQUENCE {
690 * request-id INTEGER,
691 * error-status INTEGER,
692 * error-index INTEGER,
693 * varbindlist SEQUENCE OF
694 * SEQUENCE {
695 * name ObjectName,
696 * value ObjectValue
697 * }
698 * }
699 * PDU for Trap:
700 * SEQUENCE {
701 * enterprise OBJECT IDENTIFIER,
702 * agent-addr NetworkAddress,
703 * generic-trap INTEGER,
704 * specific-trap INTEGER,
705 * time-stamp TimeTicks,
706 * varbindlist SEQUENCE OF
707 * SEQUENCE {
708 * name ObjectName,
709 * value ObjectValue
710 * }
711 * }
712 */
713
714 /*
715 * Decode SNMP varBind
716 */
717 static void
718 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
719 {
720 struct be elem;
721 int count = 0, ind;
722
723 /* Sequence of varBind */
724 if ((count = asn1_parse(np, length, &elem)) < 0)
725 return;
726 if (elem.type != BE_SEQ) {
727 fputs("[!SEQ of varbind]", stdout);
728 asn1_print(&elem);
729 return;
730 }
731 if (count < length)
732 printf("[%d extra after SEQ of varbind]", length - count);
733 /* descend */
734 length = elem.asnlen;
735 np = (u_char *)elem.data.raw;
736
737 for (ind = 1; length > 0; ind++) {
738 const u_char *vbend;
739 u_int vblength;
740
741 if (!error || ind == error)
742 fputs(" ", stdout);
743
744 /* Sequence */
745 if ((count = asn1_parse(np, length, &elem)) < 0)
746 return;
747 if (elem.type != BE_SEQ) {
748 fputs("[!varbind]", stdout);
749 asn1_print(&elem);
750 return;
751 }
752 vbend = np + count;
753 vblength = length - count;
754 /* descend */
755 length = elem.asnlen;
756 np = (u_char *)elem.data.raw;
757
758 /* objName (OID) */
759 if ((count = asn1_parse(np, length, &elem)) < 0)
760 return;
761 if (elem.type != BE_OID) {
762 fputs("[objName!=OID]", stdout);
763 asn1_print(&elem);
764 return;
765 }
766 if (!error || ind == error)
767 asn1_print(&elem);
768 length -= count;
769 np += count;
770
771 if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
772 fputs("=", stdout);
773
774 /* objVal (ANY) */
775 if ((count = asn1_parse(np, length, &elem)) < 0)
776 return;
777 if (pduid == GETREQ || pduid == GETNEXTREQ) {
778 if (elem.type != BE_NULL) {
779 fputs("[objVal!=NULL]", stdout);
780 asn1_print(&elem);
781 }
782 } else
783 if (error && ind == error && elem.type != BE_NULL)
784 fputs("[err objVal!=NULL]", stdout);
785 if (!error || ind == error)
786 asn1_print(&elem);
787
788 length = vblength;
789 np = vbend;
790 }
791 }
792
793 /*
794 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
795 */
796 static void
797 snmppdu_print(u_char pduid, const u_char *np, u_int length)
798 {
799 struct be elem;
800 int count = 0, error;
801
802 /* reqId (Integer) */
803 if ((count = asn1_parse(np, length, &elem)) < 0)
804 return;
805 if (elem.type != BE_INT) {
806 fputs("[reqId!=INT]", stdout);
807 asn1_print(&elem);
808 return;
809 }
810 /* ignore the reqId */
811 length -= count;
812 np += count;
813
814 /* errorStatus (Integer) */
815 if ((count = asn1_parse(np, length, &elem)) < 0)
816 return;
817 if (elem.type != BE_INT) {
818 fputs("[errorStatus!=INT]", stdout);
819 asn1_print(&elem);
820 return;
821 }
822 error = 0;
823 if ((pduid == GETREQ || pduid == GETNEXTREQ)
824 && elem.data.integer != 0) {
825 char errbuf[10];
826 printf("[errorStatus(%s)!=0]",
827 DECODE_ErrorStatus(elem.data.integer));
828 } else if (elem.data.integer != 0) {
829 char errbuf[10];
830 printf(" %s", DECODE_ErrorStatus(elem.data.integer));
831 error = elem.data.integer;
832 }
833 length -= count;
834 np += count;
835
836 /* errorIndex (Integer) */
837 if ((count = asn1_parse(np, length, &elem)) < 0)
838 return;
839 if (elem.type != BE_INT) {
840 fputs("[errorIndex!=INT]", stdout);
841 asn1_print(&elem);
842 return;
843 }
844 if ((pduid == GETREQ || pduid == GETNEXTREQ)
845 && elem.data.integer != 0)
846 printf("[errorIndex(%d)!=0]", elem.data.integer);
847 else if (elem.data.integer != 0) {
848 if (!error)
849 printf("[errorIndex(%d) w/o errorStatus]",
850 elem.data.integer);
851 else {
852 printf("@%d", elem.data.integer);
853 error = elem.data.integer;
854 }
855 } else if (error) {
856 fputs("[errorIndex==0]", stdout);
857 error = 0;
858 }
859 length -= count;
860 np += count;
861
862 varbind_print(pduid, np, length, error);
863 return;
864 }
865
866 /*
867 * Decode SNMP Trap PDU
868 */
869 static void
870 trap_print(const u_char *np, u_int length)
871 {
872 struct be elem;
873 int count = 0, generic;
874
875 putchar(' ');
876
877 /* enterprise (oid) */
878 if ((count = asn1_parse(np, length, &elem)) < 0)
879 return;
880 if (elem.type != BE_OID) {
881 fputs("[enterprise!=OID]", stdout);
882 asn1_print(&elem);
883 return;
884 }
885 asn1_print(&elem);
886 length -= count;
887 np += count;
888
889 putchar(' ');
890
891 /* agent-addr (inetaddr) */
892 if ((count = asn1_parse(np, length, &elem)) < 0)
893 return;
894 if (elem.type != BE_INETADDR) {
895 fputs("[agent-addr!=INETADDR]", stdout);
896 asn1_print(&elem);
897 return;
898 }
899 asn1_print(&elem);
900 length -= count;
901 np += count;
902
903 /* generic-trap (Integer) */
904 if ((count = asn1_parse(np, length, &elem)) < 0)
905 return;
906 if (elem.type != BE_INT) {
907 fputs("[generic-trap!=INT]", stdout);
908 asn1_print(&elem);
909 return;
910 }
911 generic = elem.data.integer;
912 {
913 char buf[10];
914 printf(" %s", DECODE_GenericTrap(generic));
915 }
916 length -= count;
917 np += count;
918
919 /* specific-trap (Integer) */
920 if ((count = asn1_parse(np, length, &elem)) < 0)
921 return;
922 if (elem.type != BE_INT) {
923 fputs("[specific-trap!=INT]", stdout);
924 asn1_print(&elem);
925 return;
926 }
927 if (generic != GT_ENTERPRISE) {
928 if (elem.data.integer != 0)
929 printf("[specific-trap(%d)!=0]", elem.data.integer);
930 } else
931 printf(" s=%d", elem.data.integer);
932 length -= count;
933 np += count;
934
935 putchar(' ');
936
937 /* time-stamp (TimeTicks) */
938 if ((count = asn1_parse(np, length, &elem)) < 0)
939 return;
940 if (elem.type != BE_UNS) { /* XXX */
941 fputs("[time-stamp!=TIMETICKS]", stdout);
942 asn1_print(&elem);
943 return;
944 }
945 asn1_print(&elem);
946 length -= count;
947 np += count;
948
949 varbind_print (TRAP, np, length, 0);
950 return;
951 }
952
953 /*
954 * Decode SNMP header and pass on to PDU printing routines
955 */
956 void
957 snmp_print(const u_char *np, u_int length)
958 {
959 struct be elem, pdu;
960 int count = 0;
961
962 truncated = 0;
963
964 /* truncated packet? */
965 if (np + length > snapend) {
966 truncated = 1;
967 length = snapend - np;
968 }
969
970 putchar(' ');
971
972 /* initial Sequence */
973 if ((count = asn1_parse(np, length, &elem)) < 0)
974 return;
975 if (elem.type != BE_SEQ) {
976 fputs("[!init SEQ]", stdout);
977 asn1_print(&elem);
978 return;
979 }
980 if (count < length)
981 printf("[%d extra after iSEQ]", length - count);
982 /* descend */
983 length = elem.asnlen;
984 np = (u_char *)elem.data.raw;
985 /* Version (Integer) */
986 if ((count = asn1_parse(np, length, &elem)) < 0)
987 return;
988 if (elem.type != BE_INT) {
989 fputs("[version!=INT]", stdout);
990 asn1_print(&elem);
991 return;
992 }
993 /* only handle version==0 */
994 if (elem.data.integer != DEF_VERSION) {
995 printf("[version(%d)!=0]", elem.data.integer);
996 return;
997 }
998 length -= count;
999 np += count;
1000
1001 /* Community (String) */
1002 if ((count = asn1_parse(np, length, &elem)) < 0)
1003 return;
1004 if (elem.type != BE_STR) {
1005 fputs("[comm!=STR]", stdout);
1006 asn1_print(&elem);
1007 return;
1008 }
1009 /* default community */
1010 if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1011 sizeof(DEF_COMMUNITY) - 1))
1012 /* ! "public" */
1013 printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1014 length -= count;
1015 np += count;
1016
1017 /* PDU (Context) */
1018 if ((count = asn1_parse(np, length, &pdu)) < 0)
1019 return;
1020 if (pdu.type != BE_PDU) {
1021 fputs("[no PDU]", stdout);
1022 return;
1023 }
1024 if (count < length)
1025 printf("[%d extra after PDU]", length - count);
1026 asn1_print(&pdu);
1027 /* descend into PDU */
1028 length = pdu.asnlen;
1029 np = (u_char *)pdu.data.raw;
1030
1031 switch (pdu.id) {
1032 case TRAP:
1033 trap_print(np, length);
1034 break;
1035 case GETREQ:
1036 case GETNEXTREQ:
1037 case GETRESP:
1038 case SETREQ:
1039 snmppdu_print(pdu.id, np, length);
1040 break;
1041 }
1042 return;
1043 }