]> The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
SNMP v2c patches from http://www.ibr.cs.tu-bs.de/ietf/snmpv3/tcpdump.shtml
[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.34 1999-10-17 21:12:42 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 #define OPAQUE 4
99 "C-5",
100 "Counter64"
101 #define COUNTER64 6
102 };
103
104 /*
105 * Context-specific ASN.1 types for the SNMP PDUs and their tags
106 */
107 char *Context[] = {
108 "GetRequest",
109 #define GETREQ 0
110 "GetNextRequest",
111 #define GETNEXTREQ 1
112 "GetResponse",
113 #define GETRESP 2
114 "SetRequest",
115 #define SETREQ 3
116 "Trap",
117 #define TRAP 4
118 "GetBulk",
119 #define GETBULKREQ 5
120 "Inform",
121 #define INFORMREQ 6
122 "V2Trap",
123 #define V2TRAP 7
124 "Report"
125 #define REPORT 8
126 };
127
128 /*
129 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
130 */
131 char *Exceptions[] = {
132 "noSuchObject",
133 #define NOSUCHOBJECT 0
134 "noSuchInstance",
135 #define NOSUCHINSTANCE 1
136 "endOfMibView",
137 #define ENDOFMIBVIEW 2
138 };
139
140 /*
141 * Private ASN.1 types
142 * The Internet SMI does not specify any
143 */
144 char *Private[] = {
145 "P-0"
146 };
147
148 /*
149 * error-status values for any SNMP PDU
150 */
151 char *ErrorStatus[] = {
152 "noError",
153 "tooBig",
154 "noSuchName",
155 "badValue",
156 "readOnly",
157 "genErr",
158 "noAccess",
159 "wrongType",
160 "wrongLength",
161 "wrongEncoding",
162 "wrongValue",
163 "noCreation",
164 "inconsistentValue",
165 "resourceUnavailable",
166 "commitFailed",
167 "undoFailed",
168 "authorizationError",
169 "notWritable",
170 "inconsistentName"
171 };
172 #define DECODE_ErrorStatus(e) \
173 ( e >= 0 && e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
174 ? ErrorStatus[e] : (sprintf(errbuf, "err=%u", e), errbuf))
175
176 /*
177 * generic-trap values in the SNMP Trap-PDU
178 */
179 char *GenericTrap[] = {
180 "coldStart",
181 "warmStart",
182 "linkDown",
183 "linkUp",
184 "authenticationFailure",
185 "egpNeighborLoss",
186 "enterpriseSpecific"
187 #define GT_ENTERPRISE 7
188 };
189 #define DECODE_GenericTrap(t) \
190 ( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
191 ? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf))
192
193 /*
194 * ASN.1 type class table
195 * Ties together the preceding Universal, Application, Context, and Private
196 * type definitions.
197 */
198 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
199 struct {
200 char *name;
201 char **Id;
202 int numIDs;
203 } Class[] = {
204 defineCLASS(Universal),
205 #define UNIVERSAL 0
206 defineCLASS(Application),
207 #define APPLICATION 1
208 defineCLASS(Context),
209 #define CONTEXT 2
210 defineCLASS(Private),
211 #define PRIVATE 3
212 defineCLASS(Exceptions),
213 #define EXCEPTIONS 4
214 };
215
216 /*
217 * defined forms for ASN.1 types
218 */
219 char *Form[] = {
220 "Primitive",
221 #define PRIMITIVE 0
222 "Constructed",
223 #define CONSTRUCTED 1
224 };
225
226 /*
227 * A structure for the OID tree for the compiled-in MIB.
228 * This is stored as a general-order tree.
229 */
230 struct obj {
231 char *desc; /* name of object */
232 u_char oid; /* sub-id following parent */
233 u_char type; /* object type (unused) */
234 struct obj *child, *next; /* child and next sibling pointers */
235 } *objp = NULL;
236
237 /*
238 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
239 * RFC-1156 format files into "makemib". "mib.h" MUST define at least
240 * a value for `mibroot'.
241 *
242 * In particular, this is gross, as this is including initialized structures,
243 * and by right shouldn't be an "include" file.
244 */
245 #include "mib.h"
246
247 /*
248 * This defines a list of OIDs which will be abbreviated on output.
249 * Currently, this includes the prefixes for the Internet MIB, the
250 * private enterprises tree, and the experimental tree.
251 */
252 struct obj_abrev {
253 char *prefix; /* prefix for this abrev */
254 struct obj *node; /* pointer into object table */
255 char *oid; /* ASN.1 encoded OID */
256 } obj_abrev_list[] = {
257 #ifndef NO_ABREV_MIB
258 /* .iso.org.dod.internet.mgmt.mib */
259 { "", &_mib_obj, "\53\6\1\2\1" },
260 #endif
261 #ifndef NO_ABREV_ENTER
262 /* .iso.org.dod.internet.private.enterprises */
263 { "E:", &_enterprises_obj, "\53\6\1\4\1" },
264 #endif
265 #ifndef NO_ABREV_EXPERI
266 /* .iso.org.dod.internet.experimental */
267 { "X:", &_experimental_obj, "\53\6\1\3" },
268 #endif
269 { 0,0,0 }
270 };
271
272 /*
273 * This is used in the OID print routine to walk down the object tree
274 * rooted at `mibroot'.
275 */
276 #define OBJ_PRINT(o, suppressdot) \
277 { \
278 if (objp) { \
279 do { \
280 if ((o) == objp->oid) \
281 break; \
282 } while ((objp = objp->next) != NULL); \
283 } \
284 if (objp) { \
285 printf(suppressdot?"%s":".%s", objp->desc); \
286 objp = objp->child; \
287 } else \
288 printf(suppressdot?"%u":".%u", (o)); \
289 }
290
291 /*
292 * This is the definition for the Any-Data-Type storage used purely for
293 * temporary internal representation while decoding an ASN.1 data stream.
294 */
295 struct be {
296 u_int32_t asnlen;
297 union {
298 caddr_t raw;
299 int32_t integer;
300 u_int32_t uns;
301 const u_char *str;
302 struct {
303 u_int32_t high;
304 u_int32_t low;
305 } uns64;
306 } data;
307 u_short id;
308 u_char form, class; /* tag info */
309 u_char type;
310 #define BE_ANY 255
311 #define BE_NONE 0
312 #define BE_NULL 1
313 #define BE_OCTET 2
314 #define BE_OID 3
315 #define BE_INT 4
316 #define BE_UNS 5
317 #define BE_STR 6
318 #define BE_SEQ 7
319 #define BE_INETADDR 8
320 #define BE_PDU 9
321 #define BE_UNS64 10
322 #define BE_NOSUCHOBJECT 128
323 #define BE_NOSUCHINST 129
324 #define BE_ENDOFMIBVIEW 130
325 };
326
327 /*
328 * SNMP versions recognized by this module
329 */
330 char *SnmpVersion[] = {
331 "SNMPv1",
332 #define SNMP_VERSION_1 0
333 "SNMPv2"
334 #define SNMP_VERSION_2 1
335 };
336 #define DECODE_SnmpVersion(v) \
337 ( v >= 0 && v < sizeof(SnmpVersion)/sizeof(SnmpVersion[0]) \
338 ? SnmpVersion[v] : (sprintf(versionbuf, "version=%u", v), versionbuf))
339
340 /*
341 * Defaults for SNMP PDU components
342 */
343 #define DEF_COMMUNITY "public"
344
345 /*
346 * constants for ASN.1 decoding
347 */
348 #define OIDMUX 40
349 #define ASNLEN_INETADDR 4
350 #define ASN_SHIFT7 7
351 #define ASN_SHIFT8 8
352 #define ASN_BIT8 0x80
353 #define ASN_LONGLEN 0x80
354
355 #define ASN_ID_BITS 0x1f
356 #define ASN_FORM_BITS 0x20
357 #define ASN_FORM_SHIFT 5
358 #define ASN_CLASS_BITS 0xc0
359 #define ASN_CLASS_SHIFT 6
360
361 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
362
363 /*
364 * truncated==1 means the packet was complete, but we don't have all of
365 * it to decode.
366 */
367 static int truncated;
368 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
369
370 /*
371 * This decodes the next ASN.1 object in the stream pointed to by "p"
372 * (and of real-length "len") and stores the intermediate data in the
373 * provided BE object.
374 *
375 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
376 * O/w, this returns the number of bytes parsed from "p".
377 */
378 static int
379 asn1_parse(register const u_char *p, u_int len, struct be *elem)
380 {
381 u_char form, class, id;
382 int i, hdr;
383
384 elem->asnlen = 0;
385 elem->type = BE_ANY;
386 if (len < 1) {
387 ifNotTruncated puts("[nothing to parse], stdout");
388 return -1;
389 }
390
391 /*
392 * it would be nice to use a bit field, but you can't depend on them.
393 * +---+---+---+---+---+---+---+---+
394 * + class |frm| id |
395 * +---+---+---+---+---+---+---+---+
396 * 7 6 5 4 3 2 1 0
397 */
398 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
399 #ifdef notdef
400 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
401 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
402 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
403 #else
404 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
405 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
406 #endif
407 elem->form = form;
408 elem->class = class;
409 elem->id = id;
410 if (vflag)
411 printf("|%.2x", *p);
412 p++; len--; hdr = 1;
413 /* extended tag field */
414 if (id == ASN_ID_EXT) {
415 for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
416 if (vflag)
417 printf("|%.2x", *p);
418 id = (id << 7) | (*p & ~ASN_BIT8);
419 }
420 if (len == 0 && *p & ASN_BIT8) {
421 ifNotTruncated fputs("[Xtagfield?]", stdout);
422 return -1;
423 }
424 elem->id = id = (id << 7) | *p;
425 --len;
426 ++hdr;
427 ++p;
428 }
429 if (len < 1) {
430 ifNotTruncated fputs("[no asnlen]", stdout);
431 return -1;
432 }
433 elem->asnlen = *p;
434 if (vflag)
435 printf("|%.2x", *p);
436 p++; len--; hdr++;
437 if (elem->asnlen & ASN_BIT8) {
438 int noct = elem->asnlen % ASN_BIT8;
439 elem->asnlen = 0;
440 if (len < noct) {
441 ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
442 return -1;
443 }
444 for (; noct-- > 0; len--, hdr++) {
445 if (vflag)
446 printf("|%.2x", *p);
447 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
448 }
449 }
450 if (len < elem->asnlen) {
451 if (!truncated) {
452 printf("[len%d<asnlen%u]", len, elem->asnlen);
453 return -1;
454 }
455 /* maybe should check at least 4? */
456 elem->asnlen = len;
457 }
458 if (form >= sizeof(Form)/sizeof(Form[0])) {
459 ifNotTruncated printf("[form?%d]", form);
460 return -1;
461 }
462 if (class >= sizeof(Class)/sizeof(Class[0])) {
463 ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
464 return -1;
465 }
466 if ((int)id >= Class[class].numIDs) {
467 ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
468 Class[class].name, id);
469 return -1;
470 }
471
472 switch (form) {
473 case PRIMITIVE:
474 switch (class) {
475 case UNIVERSAL:
476 switch (id) {
477 case STRING:
478 elem->type = BE_STR;
479 elem->data.str = p;
480 break;
481
482 case INTEGER: {
483 register int32_t data;
484 elem->type = BE_INT;
485 data = 0;
486
487 if (*p & ASN_BIT8) /* negative */
488 data = -1;
489 for (i = elem->asnlen; i-- > 0; p++)
490 data = (data << ASN_SHIFT8) | *p;
491 elem->data.integer = data;
492 break;
493 }
494
495 case OBJECTID:
496 elem->type = BE_OID;
497 elem->data.raw = (caddr_t)p;
498 break;
499
500 case ASN_NULL:
501 elem->type = BE_NULL;
502 elem->data.raw = NULL;
503 break;
504
505 default:
506 elem->type = BE_OCTET;
507 elem->data.raw = (caddr_t)p;
508 printf("[P/U/%s]",
509 Class[class].Id[id]);
510 break;
511 }
512 break;
513
514 case APPLICATION:
515 switch (id) {
516 case IPADDR:
517 elem->type = BE_INETADDR;
518 elem->data.raw = (caddr_t)p;
519 break;
520
521 case COUNTER:
522 case GAUGE:
523 case TIMETICKS: {
524 register u_int32_t data;
525 elem->type = BE_UNS;
526 data = 0;
527 for (i = elem->asnlen; i-- > 0; p++)
528 data = (data << 8) + *p;
529 elem->data.uns = data;
530 break;
531 }
532
533 case COUNTER64: {
534 register u_int32_t high, low;
535 elem->type = BE_UNS64;
536 high = 0, low = 0;
537 for (i = elem->asnlen; i-- > 0; p++) {
538 high = (high << 8) |
539 ((low & 0xFF000000) >> 24);
540 low = (low << 8) | *p;
541 }
542 elem->data.uns64.high = high;
543 elem->data.uns64.low = low;
544 break;
545 }
546
547 default:
548 elem->type = BE_OCTET;
549 elem->data.raw = (caddr_t)p;
550 printf("[P/A/%s]",
551 Class[class].Id[id]);
552 break;
553 }
554 break;
555
556 case CONTEXT:
557 switch (id) {
558 case NOSUCHOBJECT:
559 elem->type = BE_NOSUCHOBJECT;
560 elem->data.raw = NULL;
561 break;
562
563 case NOSUCHINSTANCE:
564 elem->type = BE_NOSUCHINST;
565 elem->data.raw = NULL;
566 break;
567
568 case ENDOFMIBVIEW:
569 elem->type = BE_ENDOFMIBVIEW;
570 elem->data.raw = NULL;
571 break;
572 }
573 break;
574
575 default:
576 elem->type = BE_OCTET;
577 elem->data.raw = (caddr_t)p;
578 printf("[P/%s/%s]",
579 Class[class].name, Class[class].Id[id]);
580 break;
581 }
582 break;
583
584 case CONSTRUCTED:
585 switch (class) {
586 case UNIVERSAL:
587 switch (id) {
588 case SEQUENCE:
589 elem->type = BE_SEQ;
590 elem->data.raw = (caddr_t)p;
591 break;
592
593 default:
594 elem->type = BE_OCTET;
595 elem->data.raw = (caddr_t)p;
596 printf("C/U/%s", Class[class].Id[id]);
597 break;
598 }
599 break;
600
601 case CONTEXT:
602 elem->type = BE_PDU;
603 elem->data.raw = (caddr_t)p;
604 break;
605
606 default:
607 elem->type = BE_OCTET;
608 elem->data.raw = (caddr_t)p;
609 printf("C/%s/%s",
610 Class[class].name, Class[class].Id[id]);
611 break;
612 }
613 break;
614 }
615 p += elem->asnlen;
616 len -= elem->asnlen;
617 return elem->asnlen + hdr;
618 }
619
620 /*
621 * Display the ASN.1 object represented by the BE object.
622 * This used to be an integral part of asn1_parse() before the intermediate
623 * BE form was added.
624 */
625 static void
626 asn1_print(struct be *elem)
627 {
628 u_char *p = (u_char *)elem->data.raw;
629 u_int32_t asnlen = elem->asnlen;
630 int i;
631
632 switch (elem->type) {
633
634 case BE_OCTET:
635 for (i = asnlen; i-- > 0; p++);
636 printf("_%.2x", *p);
637 break;
638
639 case BE_NULL:
640 break;
641
642 case BE_OID: {
643 int o = 0, first = -1, i = asnlen;
644
645 if (!nflag && asnlen > 2) {
646 struct obj_abrev *a = &obj_abrev_list[0];
647 for (; a->node; a++) {
648 if (!memcmp(a->oid, (char *)p,
649 strlen(a->oid))) {
650 objp = a->node->child;
651 i -= strlen(a->oid);
652 p += strlen(a->oid);
653 fputs(a->prefix, stdout);
654 first = 1;
655 break;
656 }
657 }
658 }
659 for (; i-- > 0; p++) {
660 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
661 if (*p & ASN_LONGLEN)
662 continue;
663
664 /*
665 * first subitem encodes two items with 1st*OIDMUX+2nd
666 */
667 if (first < 0) {
668 if (!nflag)
669 objp = mibroot;
670 first = 0;
671 OBJ_PRINT(o/OIDMUX, first);
672 o %= OIDMUX;
673 }
674 OBJ_PRINT(o, first);
675 if (--first < 0)
676 first = 0;
677 o = 0;
678 }
679 break;
680 }
681
682 case BE_INT:
683 printf("%d", elem->data.integer);
684 break;
685
686 case BE_UNS:
687 printf("%d", elem->data.uns);
688 break;
689
690 case BE_UNS64: { /* idea borrowed from by Marshall Rose */
691 double d;
692 int j, carry;
693 char *cpf, *cpl, last[6], first[30];
694 if (elem->data.uns64.high == 0) {
695 printf("%u", elem->data.uns64.low);
696 break;
697 }
698 d = elem->data.uns64.high * 4294967296.0; /* 2^32 */
699 if (elem->data.uns64.high <= 0x1fffff) {
700 d += elem->data.uns64.low;
701 printf("%.f", d);
702 break;
703 }
704 d += (elem->data.uns64.low & 0xfffff000);
705 sprintf(first, "%.f", d);
706 sprintf(last, "%5.5d", elem->data.uns64.low & 0xfff);
707 for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
708 cpl >= last;
709 cpf--, cpl--) {
710 j = carry + (*cpf - '0') + (*cpl - '0');
711 if (j > 9) {
712 j -= 10;
713 carry = 1;
714 } else {
715 carry = 0;
716 }
717 *cpf = j + '0';
718 }
719 fputs(first, stdout);
720 break;
721 }
722
723 case BE_STR: {
724 register int printable = 1, first = 1;
725 const u_char *p = elem->data.str;
726 for (i = asnlen; printable && i-- > 0; p++)
727 printable = isprint(*p) || isspace(*p);
728 p = elem->data.str;
729 if (printable) {
730 putchar('"');
731 (void)fn_print(p, p + asnlen);
732 putchar('"');
733 } else
734 for (i = asnlen; i-- > 0; p++) {
735 printf(first ? "%.2x" : "_%.2x", *p);
736 first = 0;
737 }
738 break;
739 }
740
741 case BE_SEQ:
742 printf("Seq(%u)", elem->asnlen);
743 break;
744
745 case BE_INETADDR: {
746 char sep;
747 if (asnlen != ASNLEN_INETADDR)
748 printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
749 sep='[';
750 for (i = asnlen; i-- > 0; p++) {
751 printf("%c%u", sep, *p);
752 sep='.';
753 }
754 putchar(']');
755 break;
756 }
757
758 case BE_NOSUCHOBJECT:
759 case BE_NOSUCHINST:
760 case BE_ENDOFMIBVIEW:
761 printf("[%s]", Class[EXCEPTIONS].Id[elem->id]);
762 break;
763
764 case BE_PDU:
765 printf("%s(%u)",
766 Class[CONTEXT].Id[elem->id], elem->asnlen);
767 break;
768
769 case BE_ANY:
770 fputs("[BE_ANY!?]", stdout);
771 break;
772
773 default:
774 fputs("[be!?]", stdout);
775 break;
776 }
777 }
778
779 #ifdef notdef
780 /*
781 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
782 * This will work for any ASN.1 stream, not just an SNMP PDU.
783 *
784 * By adding newlines and spaces at the correct places, this would print in
785 * Rose-Normal-Form.
786 *
787 * This is not currently used.
788 */
789 static void
790 asn1_decode(u_char *p, u_int length)
791 {
792 struct be elem;
793 int i = 0;
794
795 while (i >= 0 && length > 0) {
796 i = asn1_parse(p, length, &elem);
797 if (i >= 0) {
798 fputs(" ", stdout);
799 asn1_print(&elem);
800 if (elem.type == BE_SEQ || elem.type == BE_PDU) {
801 fputs(" {", stdout);
802 asn1_decode(elem.data.raw, elem.asnlen);
803 fputs(" }", stdout);
804 }
805 length -= i;
806 p += i;
807 }
808 }
809 }
810 #endif
811
812 /*
813 * General SNMP header
814 * SEQUENCE {
815 * version INTEGER {version-1(0)},
816 * community OCTET STRING,
817 * data ANY -- PDUs
818 * }
819 * PDUs for all but Trap: (see rfc1157 from page 15 on)
820 * SEQUENCE {
821 * request-id INTEGER,
822 * error-status INTEGER,
823 * error-index INTEGER,
824 * varbindlist SEQUENCE OF
825 * SEQUENCE {
826 * name ObjectName,
827 * value ObjectValue
828 * }
829 * }
830 * PDU for Trap:
831 * SEQUENCE {
832 * enterprise OBJECT IDENTIFIER,
833 * agent-addr NetworkAddress,
834 * generic-trap INTEGER,
835 * specific-trap INTEGER,
836 * time-stamp TimeTicks,
837 * varbindlist SEQUENCE OF
838 * SEQUENCE {
839 * name ObjectName,
840 * value ObjectValue
841 * }
842 * }
843 */
844
845 /*
846 * Decode SNMP varBind
847 */
848 static void
849 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
850 {
851 struct be elem;
852 int count = 0, ind;
853
854 /* Sequence of varBind */
855 if ((count = asn1_parse(np, length, &elem)) < 0)
856 return;
857 if (elem.type != BE_SEQ) {
858 fputs("[!SEQ of varbind]", stdout);
859 asn1_print(&elem);
860 return;
861 }
862 if (count < length)
863 printf("[%d extra after SEQ of varbind]", length - count);
864 /* descend */
865 length = elem.asnlen;
866 np = (u_char *)elem.data.raw;
867
868 for (ind = 1; length > 0; ind++) {
869 const u_char *vbend;
870 u_int vblength;
871
872 if (!error || ind == error)
873 fputs(" ", stdout);
874
875 /* Sequence */
876 if ((count = asn1_parse(np, length, &elem)) < 0)
877 return;
878 if (elem.type != BE_SEQ) {
879 fputs("[!varbind]", stdout);
880 asn1_print(&elem);
881 return;
882 }
883 vbend = np + count;
884 vblength = length - count;
885 /* descend */
886 length = elem.asnlen;
887 np = (u_char *)elem.data.raw;
888
889 /* objName (OID) */
890 if ((count = asn1_parse(np, length, &elem)) < 0)
891 return;
892 if (elem.type != BE_OID) {
893 fputs("[objName!=OID]", stdout);
894 asn1_print(&elem);
895 return;
896 }
897 if (!error || ind == error)
898 asn1_print(&elem);
899 length -= count;
900 np += count;
901
902 if (pduid != GETREQ && pduid != GETNEXTREQ
903 && pduid != GETBULKREQ && !error)
904 fputs("=", stdout);
905
906 /* objVal (ANY) */
907 if ((count = asn1_parse(np, length, &elem)) < 0)
908 return;
909 if (pduid == GETREQ || pduid == GETNEXTREQ
910 || pduid == GETBULKREQ) {
911 if (elem.type != BE_NULL) {
912 fputs("[objVal!=NULL]", stdout);
913 asn1_print(&elem);
914 }
915 } else
916 if (error && ind == error && elem.type != BE_NULL)
917 fputs("[err objVal!=NULL]", stdout);
918 if (!error || ind == error)
919 asn1_print(&elem);
920
921 length = vblength;
922 np = vbend;
923 }
924 }
925
926 /*
927 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
928 * GetBulk, Inform, V2Trap, and Report
929 */
930 static void
931 snmppdu_print(u_char pduid, const u_char *np, u_int length)
932 {
933 struct be elem;
934 int count = 0, error;
935
936 /* reqId (Integer) */
937 if ((count = asn1_parse(np, length, &elem)) < 0)
938 return;
939 if (elem.type != BE_INT) {
940 fputs("[reqId!=INT]", stdout);
941 asn1_print(&elem);
942 return;
943 }
944 /* ignore the reqId */
945 length -= count;
946 np += count;
947
948 /* errorStatus (Integer) */
949 if ((count = asn1_parse(np, length, &elem)) < 0)
950 return;
951 if (elem.type != BE_INT) {
952 fputs("[errorStatus!=INT]", stdout);
953 asn1_print(&elem);
954 return;
955 }
956 error = 0;
957 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
958 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
959 && elem.data.integer != 0) {
960 char errbuf[10];
961 printf("[errorStatus(%s)!=0]",
962 DECODE_ErrorStatus(elem.data.integer));
963 } else if (pduid == GETBULKREQ) {
964 printf(" N=%d", elem.data.integer);
965 } else if (elem.data.integer != 0) {
966 char errbuf[10];
967 printf(" %s", DECODE_ErrorStatus(elem.data.integer));
968 error = elem.data.integer;
969 }
970 length -= count;
971 np += count;
972
973 /* errorIndex (Integer) */
974 if ((count = asn1_parse(np, length, &elem)) < 0)
975 return;
976 if (elem.type != BE_INT) {
977 fputs("[errorIndex!=INT]", stdout);
978 asn1_print(&elem);
979 return;
980 }
981 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
982 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
983 && elem.data.integer != 0)
984 printf("[errorIndex(%d)!=0]", elem.data.integer);
985 else if (pduid == GETBULKREQ)
986 printf(" M=%d", elem.data.integer);
987 else if (elem.data.integer != 0) {
988 if (!error)
989 printf("[errorIndex(%d) w/o errorStatus]",
990 elem.data.integer);
991 else {
992 printf("@%d", elem.data.integer);
993 error = elem.data.integer;
994 }
995 } else if (error) {
996 fputs("[errorIndex==0]", stdout);
997 error = 0;
998 }
999 length -= count;
1000 np += count;
1001
1002 varbind_print(pduid, np, length, error);
1003 return;
1004 }
1005
1006 /*
1007 * Decode SNMP Trap PDU
1008 */
1009 static void
1010 trap_print(const u_char *np, u_int length)
1011 {
1012 struct be elem;
1013 int count = 0, generic;
1014
1015 putchar(' ');
1016
1017 /* enterprise (oid) */
1018 if ((count = asn1_parse(np, length, &elem)) < 0)
1019 return;
1020 if (elem.type != BE_OID) {
1021 fputs("[enterprise!=OID]", stdout);
1022 asn1_print(&elem);
1023 return;
1024 }
1025 asn1_print(&elem);
1026 length -= count;
1027 np += count;
1028
1029 putchar(' ');
1030
1031 /* agent-addr (inetaddr) */
1032 if ((count = asn1_parse(np, length, &elem)) < 0)
1033 return;
1034 if (elem.type != BE_INETADDR) {
1035 fputs("[agent-addr!=INETADDR]", stdout);
1036 asn1_print(&elem);
1037 return;
1038 }
1039 asn1_print(&elem);
1040 length -= count;
1041 np += count;
1042
1043 /* generic-trap (Integer) */
1044 if ((count = asn1_parse(np, length, &elem)) < 0)
1045 return;
1046 if (elem.type != BE_INT) {
1047 fputs("[generic-trap!=INT]", stdout);
1048 asn1_print(&elem);
1049 return;
1050 }
1051 generic = elem.data.integer;
1052 {
1053 char buf[10];
1054 printf(" %s", DECODE_GenericTrap(generic));
1055 }
1056 length -= count;
1057 np += count;
1058
1059 /* specific-trap (Integer) */
1060 if ((count = asn1_parse(np, length, &elem)) < 0)
1061 return;
1062 if (elem.type != BE_INT) {
1063 fputs("[specific-trap!=INT]", stdout);
1064 asn1_print(&elem);
1065 return;
1066 }
1067 if (generic != GT_ENTERPRISE) {
1068 if (elem.data.integer != 0)
1069 printf("[specific-trap(%d)!=0]", elem.data.integer);
1070 } else
1071 printf(" s=%d", elem.data.integer);
1072 length -= count;
1073 np += count;
1074
1075 putchar(' ');
1076
1077 /* time-stamp (TimeTicks) */
1078 if ((count = asn1_parse(np, length, &elem)) < 0)
1079 return;
1080 if (elem.type != BE_UNS) { /* XXX */
1081 fputs("[time-stamp!=TIMETICKS]", stdout);
1082 asn1_print(&elem);
1083 return;
1084 }
1085 asn1_print(&elem);
1086 length -= count;
1087 np += count;
1088
1089 varbind_print (TRAP, np, length, 0);
1090 return;
1091 }
1092
1093 /*
1094 * Decode SNMP header and pass on to PDU printing routines
1095 */
1096 void
1097 snmp_print(const u_char *np, u_int length)
1098 {
1099 struct be elem, pdu;
1100 int count = 0;
1101 int version = 0;
1102
1103 truncated = 0;
1104
1105 /* truncated packet? */
1106 if (np + length > snapend) {
1107 truncated = 1;
1108 length = snapend - np;
1109 }
1110
1111 putchar(' ');
1112
1113 /* initial Sequence */
1114 if ((count = asn1_parse(np, length, &elem)) < 0)
1115 return;
1116 if (elem.type != BE_SEQ) {
1117 fputs("[!init SEQ]", stdout);
1118 asn1_print(&elem);
1119 return;
1120 }
1121 if (count < length)
1122 printf("[%d extra after iSEQ]", length - count);
1123 /* descend */
1124 length = elem.asnlen;
1125 np = (u_char *)elem.data.raw;
1126 /* Version (Integer) */
1127 if ((count = asn1_parse(np, length, &elem)) < 0)
1128 return;
1129 if (elem.type != BE_INT) {
1130 fputs("[version!=INT]", stdout);
1131 asn1_print(&elem);
1132 return;
1133 }
1134 /* only handle version==0 || version==1 */
1135 switch (elem.data.integer) {
1136 case SNMP_VERSION_1:
1137 case SNMP_VERSION_2: {
1138 char versionbuf[10];
1139 if (vflag)
1140 printf("%s ", DECODE_SnmpVersion(elem.data.integer));
1141 break;
1142 }
1143 default: {
1144 char versionbuf[10];
1145 printf("[%s]", DECODE_SnmpVersion(elem.data.integer));
1146 return;
1147 }
1148 }
1149 version = elem.data.integer;
1150 length -= count;
1151 np += count;
1152
1153 /* Community (String) */
1154 if ((count = asn1_parse(np, length, &elem)) < 0)
1155 return;
1156 if (elem.type != BE_STR) {
1157 fputs("[comm!=STR]", stdout);
1158 asn1_print(&elem);
1159 return;
1160 }
1161 /* default community */
1162 if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1163 sizeof(DEF_COMMUNITY) - 1))
1164 /* ! "public" */
1165 printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1166 length -= count;
1167 np += count;
1168
1169 /* PDU (Context) */
1170 if ((count = asn1_parse(np, length, &pdu)) < 0)
1171 return;
1172 if (pdu.type != BE_PDU) {
1173 fputs("[no PDU]", stdout);
1174 return;
1175 }
1176 if (count < length)
1177 printf("[%d extra after PDU]", length - count);
1178 asn1_print(&pdu);
1179 /* descend into PDU */
1180 length = pdu.asnlen;
1181 np = (u_char *)pdu.data.raw;
1182
1183 if (version == SNMP_VERSION_1 &&
1184 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1185 pdu.id == V2TRAP || pdu.id == REPORT)) {
1186 printf("[v2 PDU in v1 message]");
1187 return;
1188 }
1189
1190 if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1191 printf("[v1 PDU in v2 message]");
1192 return;
1193 }
1194
1195 switch (pdu.id) {
1196 case TRAP:
1197 trap_print(np, length);
1198 break;
1199 case GETREQ:
1200 case GETNEXTREQ:
1201 case GETRESP:
1202 case SETREQ:
1203 case GETBULKREQ:
1204 case INFORMREQ:
1205 case V2TRAP:
1206 case REPORT:
1207 snmppdu_print(pdu.id, np, length);
1208 break;
1209 }
1210 return;
1211 }