]>
The Tcpdump Group git mirrors - tcpdump/blob - print-snmp.c
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3 * The Regents of the University of California. All rights reserved.
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.
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
24 * This started out as a very simple program, but the incremental decoding
25 * (into the BE structure) complicated things.
27 # Los Alamos National Laboratory
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
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)";
48 #include <sys/param.h>
58 #include "interface.h"
59 #include "addrtoname.h"
62 * Universal ASN.1 types
63 * (we only care about the tag values for those allowed in the Internet SMI)
78 "U-8","U-9","U-10","U-11", /* 8-11 */
79 "U-12","U-13","U-14","U-15", /* 12-15 */
86 * Application-wide ASN.1 types from the Internet SMI and their tags
88 char *Application
[] = {
101 * Context-specific ASN.1 types for the SNMP PDUs and their tags
117 * Private ASN.1 types
118 * The Internet SMI does not specify any
125 * error-status values for any SNMP PDU
127 char *ErrorStatus
[] = {
135 #define DECODE_ErrorStatus(e) \
136 ( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
137 ? ErrorStatus[e] : (sprintf(errbuf, "err=%u", e), errbuf))
140 * generic-trap values in the SNMP Trap-PDU
142 char *GenericTrap
[] = {
147 "authenticationFailure",
150 #define GT_ENTERPRISE 7
152 #define DECODE_GenericTrap(t) \
153 ( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
154 ? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf))
157 * ASN.1 type class table
158 * Ties together the preceding Universal, Application, Context, and Private
161 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
167 defineCLASS(Universal
),
169 defineCLASS(Application
),
170 #define APPLICATION 1
171 defineCLASS(Context
),
173 defineCLASS(Private
),
178 * defined forms for ASN.1 types
184 #define CONSTRUCTED 1
188 * A structure for the OID tree for the compiled-in MIB.
189 * This is stored as a general-order tree.
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 */
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'.
203 * In particular, this is gross, as this is including initialized structures,
204 * and by right shouldn't be an "include" file.
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.
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
[] = {
219 /* .iso.org.dod.internet.mgmt.mib */
220 { "", &_mib_obj
, "\53\6\1\2\1" },
222 #ifndef NO_ABREV_ENTER
223 /* .iso.org.dod.internet.private.enterprises */
224 { "E:", &_enterprises_obj
, "\53\6\1\4\1" },
226 #ifndef NO_ABREV_EXPERI
227 /* .iso.org.dod.internet.experimental */
228 { "X:", &_experimental_obj
, "\53\6\1\3" },
234 * This is used in the OID print routine to walk down the object tree
235 * rooted at `mibroot'.
237 #define OBJ_PRINT(o, suppressdot) \
241 if ((o) == objp->oid) \
243 } while ((objp = objp->next) != NULL); \
246 printf(suppressdot?"%s":".%s", objp->desc); \
247 objp = objp->child; \
249 printf(suppressdot?"%u":".%u", (o)); \
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.
265 u_char form
, class; /* tag info */
276 #define BE_INETADDR 8
281 * Defaults for SNMP PDU components
283 #define DEF_COMMUNITY "public"
284 #define DEF_VERSION 0
287 * constants for ASN.1 decoding
290 #define ASNLEN_INETADDR 4
293 #define ASN_BIT8 0x80
294 #define ASN_LONGLEN 0x80
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
302 #define ASN_ID_EXT 0x1f /* extension ID in tag field */
305 * truncated==1 means the packet was complete, but we don't have all of
308 static int truncated
;
309 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
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.
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".
320 asn1_parse(register const u_char
*p
, u_int len
, struct be
*elem
)
322 u_char form
, class, id
;
328 ifNotTruncated
puts("[nothing to parse], stdout");
333 * it would be nice to use a bit field, but you can't depend on them.
334 * +---+---+---+---+---+---+---+---+
336 * +---+---+---+---+---+---+---+---+
339 id
= *p
& ASN_ID_BITS
; /* lower 5 bits, range 00-1f */
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 */
345 form
= (u_char
)(*p
& ASN_FORM_BITS
) >> ASN_FORM_SHIFT
;
346 class = (u_char
)(*p
& ASN_CLASS_BITS
) >> ASN_CLASS_SHIFT
;
354 /* extended tag field */
355 if (id
== ASN_ID_EXT
) {
356 for (id
= 0; *p
& ASN_BIT8
&& len
> 0; len
--, hdr
++, p
++) {
359 id
= (id
<< 7) | (*p
& ~ASN_BIT8
);
361 if (len
== 0 && *p
& ASN_BIT8
) {
362 ifNotTruncated
fputs("[Xtagfield?]", stdout
);
365 elem
->id
= id
= (id
<< 7) | *p
;
371 ifNotTruncated
fputs("[no asnlen]", stdout
);
378 if (elem
->asnlen
& ASN_BIT8
) {
379 int noct
= elem
->asnlen
% ASN_BIT8
;
382 ifNotTruncated
printf("[asnlen? %d<%d]", len
, noct
);
385 for (; noct
-- > 0; len
--, hdr
++) {
388 elem
->asnlen
= (elem
->asnlen
<< ASN_SHIFT8
) | *p
++;
391 if (len
< elem
->asnlen
) {
393 printf("[len%d<asnlen%u]", len
, elem
->asnlen
);
396 /* maybe should check at least 4? */
399 if (form
>= sizeof(Form
)/sizeof(Form
[0])) {
400 ifNotTruncated
printf("[form?%d]", form
);
403 if (class >= sizeof(Class
)/sizeof(Class
[0])) {
404 ifNotTruncated
printf("[class?%c/%d]", *Form
[form
], class);
407 if ((int)id
>= Class
[class].numIDs
) {
408 ifNotTruncated
printf("[id?%c/%s/%d]", *Form
[form
],
409 Class
[class].name
, id
);
424 register int32_t data
;
428 if (*p
& ASN_BIT8
) /* negative */
430 for (i
= elem
->asnlen
; i
-- > 0; p
++)
431 data
= (data
<< ASN_SHIFT8
) | *p
;
432 elem
->data
.integer
= data
;
438 elem
->data
.raw
= (caddr_t
)p
;
442 elem
->type
= BE_NULL
;
443 elem
->data
.raw
= NULL
;
447 elem
->type
= BE_OCTET
;
448 elem
->data
.raw
= (caddr_t
)p
;
450 Class
[class].Id
[id
]);
458 elem
->type
= BE_INETADDR
;
459 elem
->data
.raw
= (caddr_t
)p
;
465 register u_int32_t data
;
468 for (i
= elem
->asnlen
; i
-- > 0; p
++)
469 data
= (data
<< 8) + *p
;
470 elem
->data
.uns
= data
;
475 elem
->type
= BE_OCTET
;
476 elem
->data
.raw
= (caddr_t
)p
;
478 Class
[class].Id
[id
]);
484 elem
->type
= BE_OCTET
;
485 elem
->data
.raw
= (caddr_t
)p
;
487 Class
[class].name
, Class
[class].Id
[id
]);
498 elem
->data
.raw
= (caddr_t
)p
;
502 elem
->type
= BE_OCTET
;
503 elem
->data
.raw
= (caddr_t
)p
;
504 printf("C/U/%s", Class
[class].Id
[id
]);
511 elem
->data
.raw
= (caddr_t
)p
;
515 elem
->type
= BE_OCTET
;
516 elem
->data
.raw
= (caddr_t
)p
;
518 Class
[class].name
, Class
[class].Id
[id
]);
525 return elem
->asnlen
+ hdr
;
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
534 asn1_print(struct be
*elem
)
536 u_char
*p
= (u_char
*)elem
->data
.raw
;
537 u_int32_t asnlen
= elem
->asnlen
;
540 switch (elem
->type
) {
543 for (i
= asnlen
; i
-- > 0; p
++);
551 int o
= 0, first
= -1, i
= asnlen
;
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
,
558 objp
= a
->node
->child
;
561 fputs(a
->prefix
, stdout
);
567 for (; i
-- > 0; p
++) {
568 o
= (o
<< ASN_SHIFT7
) + (*p
& ~ASN_BIT8
);
569 if (*p
& ASN_LONGLEN
)
573 * first subitem encodes two items with 1st*OIDMUX+2nd
579 OBJ_PRINT(o
/OIDMUX
, first
);
591 printf("%d", elem
->data
.integer
);
595 printf("%d", elem
->data
.uns
);
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
);
606 (void)fn_print(p
, p
+ asnlen
);
609 for (i
= asnlen
; i
-- > 0; p
++) {
610 printf(first
? "%.2x" : "_%.2x", *p
);
617 printf("Seq(%u)", elem
->asnlen
);
622 if (asnlen
!= ASNLEN_INETADDR
)
623 printf("[inetaddr len!=%d]", ASNLEN_INETADDR
);
625 for (i
= asnlen
; i
-- > 0; p
++) {
626 printf("%c%u", sep
, *p
);
635 Class
[CONTEXT
].Id
[elem
->id
], elem
->asnlen
);
639 fputs("[BE_ANY!?]", stdout
);
643 fputs("[be!?]", stdout
);
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.
653 * By adding newlines and spaces at the correct places, this would print in
656 * This is not currently used.
659 asn1_decode(u_char
*p
, u_int length
)
664 while (i
>= 0 && length
> 0) {
665 i
= asn1_parse(p
, length
, &elem
);
669 if (elem
.type
== BE_SEQ
|| elem
.type
== BE_PDU
) {
671 asn1_decode(elem
.data
.raw
, elem
.asnlen
);
682 * General SNMP header
684 * version INTEGER {version-1(0)},
685 * community OCTET STRING,
688 * PDUs for all but Trap: (see rfc1157 from page 15 on)
690 * request-id INTEGER,
691 * error-status INTEGER,
692 * error-index INTEGER,
693 * varbindlist SEQUENCE OF
701 * enterprise OBJECT IDENTIFIER,
702 * agent-addr NetworkAddress,
703 * generic-trap INTEGER,
704 * specific-trap INTEGER,
705 * time-stamp TimeTicks,
706 * varbindlist SEQUENCE OF
715 * Decode SNMP varBind
718 varbind_print(u_char pduid
, const u_char
*np
, u_int length
, int error
)
723 /* Sequence of varBind */
724 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
726 if (elem
.type
!= BE_SEQ
) {
727 fputs("[!SEQ of varbind]", stdout
);
732 printf("[%d extra after SEQ of varbind]", length
- count
);
734 length
= elem
.asnlen
;
735 np
= (u_char
*)elem
.data
.raw
;
737 for (ind
= 1; length
> 0; ind
++) {
741 if (!error
|| ind
== error
)
745 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
747 if (elem
.type
!= BE_SEQ
) {
748 fputs("[!varbind]", stdout
);
753 vblength
= length
- count
;
755 length
= elem
.asnlen
;
756 np
= (u_char
*)elem
.data
.raw
;
759 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
761 if (elem
.type
!= BE_OID
) {
762 fputs("[objName!=OID]", stdout
);
766 if (!error
|| ind
== error
)
771 if (pduid
!= GETREQ
&& pduid
!= GETNEXTREQ
&& !error
)
775 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
777 if (pduid
== GETREQ
|| pduid
== GETNEXTREQ
) {
778 if (elem
.type
!= BE_NULL
) {
779 fputs("[objVal!=NULL]", stdout
);
783 if (error
&& ind
== error
&& elem
.type
!= BE_NULL
)
784 fputs("[err objVal!=NULL]", stdout
);
785 if (!error
|| ind
== error
)
794 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
797 snmppdu_print(u_char pduid
, const u_char
*np
, u_int length
)
800 int count
= 0, error
;
802 /* reqId (Integer) */
803 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
805 if (elem
.type
!= BE_INT
) {
806 fputs("[reqId!=INT]", stdout
);
810 /* ignore the reqId */
814 /* errorStatus (Integer) */
815 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
817 if (elem
.type
!= BE_INT
) {
818 fputs("[errorStatus!=INT]", stdout
);
823 if ((pduid
== GETREQ
|| pduid
== GETNEXTREQ
)
824 && elem
.data
.integer
!= 0) {
826 printf("[errorStatus(%s)!=0]",
827 DECODE_ErrorStatus(elem
.data
.integer
));
828 } else if (elem
.data
.integer
!= 0) {
830 printf(" %s", DECODE_ErrorStatus(elem
.data
.integer
));
831 error
= elem
.data
.integer
;
836 /* errorIndex (Integer) */
837 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
839 if (elem
.type
!= BE_INT
) {
840 fputs("[errorIndex!=INT]", stdout
);
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) {
849 printf("[errorIndex(%d) w/o errorStatus]",
852 printf("@%d", elem
.data
.integer
);
853 error
= elem
.data
.integer
;
856 fputs("[errorIndex==0]", stdout
);
862 varbind_print(pduid
, np
, length
, error
);
867 * Decode SNMP Trap PDU
870 trap_print(const u_char
*np
, u_int length
)
873 int count
= 0, generic
;
877 /* enterprise (oid) */
878 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
880 if (elem
.type
!= BE_OID
) {
881 fputs("[enterprise!=OID]", stdout
);
891 /* agent-addr (inetaddr) */
892 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
894 if (elem
.type
!= BE_INETADDR
) {
895 fputs("[agent-addr!=INETADDR]", stdout
);
903 /* generic-trap (Integer) */
904 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
906 if (elem
.type
!= BE_INT
) {
907 fputs("[generic-trap!=INT]", stdout
);
911 generic
= elem
.data
.integer
;
914 printf(" %s", DECODE_GenericTrap(generic
));
919 /* specific-trap (Integer) */
920 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
922 if (elem
.type
!= BE_INT
) {
923 fputs("[specific-trap!=INT]", stdout
);
927 if (generic
!= GT_ENTERPRISE
) {
928 if (elem
.data
.integer
!= 0)
929 printf("[specific-trap(%d)!=0]", elem
.data
.integer
);
931 printf(" s=%d", elem
.data
.integer
);
937 /* time-stamp (TimeTicks) */
938 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
940 if (elem
.type
!= BE_UNS
) { /* XXX */
941 fputs("[time-stamp!=TIMETICKS]", stdout
);
949 varbind_print (TRAP
, np
, length
, 0);
954 * Decode SNMP header and pass on to PDU printing routines
957 snmp_print(const u_char
*np
, u_int length
)
964 /* truncated packet? */
965 if (np
+ length
> snapend
) {
967 length
= snapend
- np
;
972 /* initial Sequence */
973 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
975 if (elem
.type
!= BE_SEQ
) {
976 fputs("[!init SEQ]", stdout
);
981 printf("[%d extra after iSEQ]", length
- count
);
983 length
= elem
.asnlen
;
984 np
= (u_char
*)elem
.data
.raw
;
985 /* Version (Integer) */
986 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
988 if (elem
.type
!= BE_INT
) {
989 fputs("[version!=INT]", stdout
);
993 /* only handle version==0 */
994 if (elem
.data
.integer
!= DEF_VERSION
) {
995 printf("[version(%d)!=0]", elem
.data
.integer
);
1001 /* Community (String) */
1002 if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
1004 if (elem
.type
!= BE_STR
) {
1005 fputs("[comm!=STR]", stdout
);
1009 /* default community */
1010 if (strncmp((char *)elem
.data
.str
, DEF_COMMUNITY
,
1011 sizeof(DEF_COMMUNITY
) - 1))
1013 printf("C=%.*s ", (int)elem
.asnlen
, elem
.data
.str
);
1018 if ((count
= asn1_parse(np
, length
, &pdu
)) < 0)
1020 if (pdu
.type
!= BE_PDU
) {
1021 fputs("[no PDU]", stdout
);
1025 printf("[%d extra after PDU]", length
- count
);
1027 /* descend into PDU */
1028 length
= pdu
.asnlen
;
1029 np
= (u_char
*)pdu
.data
.raw
;
1033 trap_print(np
, length
);
1039 snmppdu_print(pdu
.id
, np
, length
);