* Copyright (c) 1990, by John Robert LoVerso.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by John Robert LoVerso.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* This implementaion has been influenced by the CMU SNMP release,
* by Steve Waldbusser. However, this shares no code with that system.
* Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
* Earlier forms of this implemention were derived and/or inspired by an
* awk script originally written by C. Philip Wood of LANL (but later
* heavily modified by John Robert LoVerso). The copyright notice for
* that work is preserved below, even though it may not rightly apply
* This started out as a very simple program, but the incremental decoding
* (into the BE structure) complicated things.
# Los Alamos National Laboratory
# Copyright, 1990. The Regents of the University of California.
# This software was produced under a U.S. Government contract
# (W-7405-ENG-36) by Los Alamos National Laboratory, which is
# operated by the University of California for the U.S. Department
# of Energy. The U.S. Government is licensed to use, reproduce,
# and distribute this software. Permission is granted to the
# public to copy and use this software without charge, provided
# that this Notice and any statement of authorship are reproduced
# on all copies. Neither the Government nor the University makes
# any warranty, express or implied, or assumes any liability or
# responsibility for the use of this software.
# @(#)snmp.awk.x 1.1 (LANL) 1/15/90
"@(#) $Id: print-snmp.c,v 3.10 91/01/17 01:18:13 loverso Exp Locker: loverso $ (jlv)";
* (we only care about the tag values for those allowed in the Internet SMI)
"U-8","U-9","U-10","U-11", /* 8-11 */
"U-12","U-13","U-14","U-15", /* 12-15 */
* Application-wide ASN.1 types from the Internet SMI and their tags
* Context-specific ASN.1 types for the SNMP PDUs and their tags
* The Internet SMI does not specify any
* error-status values for any SNMP PDU
#define DECODE_ErrorStatus(e) \
( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
? ErrorStatus[e] : (sprintf(errbuf, "err=%d", e), errbuf))
* generic-trap values in the SNMP Trap-PDU
#define DECODE_GenericTrap(t) \
( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf))
* Ties together the preceding Universal, Application, Context, and Private
#define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
defineCLASS(Application
),
* defined forms for ASN.1 types
* A structure for the OID tree for the compiled-in MIB.
* This is stored as a general-order tree.
char *desc
; /* name of object */
u_char oid
; /* sub-id following parent */
u_char type
; /* object type (unused) */
struct obj
*child
, *next
; /* child and next sibling pointers */
* Include the compiled in SNMP MIB. "mib.h" is produced by feeding
* RFC-1156 format files into "makemib". "mib.h" MUST define at least
* In particluar, this is gross, as this is including initialized structures,
* and by right shouldn't be an "include" file.
* This defines a list of OIDs which will be abreviated on output.
* Currently, this includes the prefixes for the Internet MIB, the
* private enterprises tree, and the experimental tree.
char *prefix
; /* prefix for this abrev */
struct obj
*node
; /* pointer into object table */
char *oid
; /* ASN.1 encoded OID */
/* .iso.org.dod.internet.mgmt.mib */
{ "", &_mib_obj
, "\53\6\1\2\1" },
/* .iso.org.dod.internet.private.enterprises */
{ "E:", &_enterprises_obj
, "\53\6\1\4\1" },
/* .iso.org.dod.internet.experimental */
{ "X:", &_experimental_obj
, "\53\6\1\3" },
* This is used in the OID print routine to walk down the object tree
#define OBJ_PRINT(o, suppressdot) \
} while (objp = objp->next); \
printf(suppressdot?"%s":".%s", objp->desc); \
printf(suppressdot?"%u":".%u", (o)); \
* This is the definition for the Any-Data-Type storage used purely for
* temporary internal representation while decoding an ASN.1 data stream.
unsigned char form
, class, id
; /* tag info */
* Defaults for SNMP PDU components
#define DEF_COMMUNITY "public"
* constants for ASN.1 decoding
#define ASNLEN_INETADDR 4
#define ASN_FORM_BITS 0x20
#define ASN_CLASS_BITS 0xc0
#define ASN_CLASS_SHIFT 6
#define ASN_ID_EXT 0x1f /* extension ID in tag field */
* truncated==1 means the packet was complete, but we don't have all of
#define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
* This decodes the next ASN.1 object in the stream pointed to by "p"
* (and of real-length "len") and stores the intermediate data in the
* This returns -l if it fails (i.e., the ASN.1 stream is not valid).
* O/w, this returns the number of bytes parsed from "p".
unsigned char form
, class, id
;
ifNotTruncated
puts("[nothing to parse], stdout");
* it would be nice to use a bit field, but you can't depend on them.
* +---+---+---+---+---+---+---+---+
* +---+---+---+---+---+---+---+---+
id
= *p
& ASN_ID_BITS
; /* lower 5 bits, range 00-1f */
form
= (*p
& 0xe0) >> 5; /* move upper 3 bits to lower 3 */
class = form
>> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
form
&= 0x1; /* bit 5 -> bit 0, range 0-1 */
form
= (*p
& ASN_FORM_BITS
) >> ASN_FORM_SHIFT
;
class = (*p
& ASN_CLASS_BITS
) >> ASN_CLASS_SHIFT
;
for (id
= 0; *p
& ASN_BIT8
&& len
> 0; len
--, hdr
++, p
++) {
if (len
== 0 && *p
& ASN_BIT8
) {
ifNotTruncated
fputs("[Xtagfield?]", stdout
);
ifNotTruncated
fputs("[no asnlen]", stdout
);
if (elem
->asnlen
& ASN_BIT8
) {
int noct
= elem
->asnlen
% ASN_BIT8
;
ifNotTruncated
printf("[asnlen? %d<%d]", len
, noct
);
for (; noct
-- > 0; len
--, hdr
++) {
elem
->asnlen
= (elem
->asnlen
<< ASN_SHIFT8
) | *p
++;
if (len
< elem
->asnlen
) {
printf("[len%d<asnlen%u]", len
, elem
->asnlen
);
/* maybe should check at least 4? */
if (form
>= sizeof(Form
)/sizeof(Form
[0])) {
ifNotTruncated
printf("[form?%d]", form
);
if (class >= sizeof(Class
)/sizeof(Class
[0])) {
ifNotTruncated
printf("[class?%c/%d]", *Form
[form
], class);
if (id
>= Class
[class].numIDs
) {
ifNotTruncated
printf("[id?%c/%s/%d]", *Form
[form
],
if (*p
& ASN_BIT8
) /* negative */
for (i
= elem
->asnlen
; i
-- > 0; p
++)
data
= (data
<< ASN_SHIFT8
) | *p
;
elem
->data
.integer
= data
;
elem
->data
.raw
= (caddr_t
)p
;
elem
->data
.raw
= (caddr_t
)p
;
elem
->type
= BE_INETADDR
;
elem
->data
.raw
= (caddr_t
)p
;
register unsigned long data
;
for (i
= elem
->asnlen
; i
-- > 0; p
++)
elem
->data
.raw
= (caddr_t
)p
;
elem
->data
.raw
= (caddr_t
)p
;
Class
[class].name
, Class
[class].Id
[id
]);
elem
->data
.raw
= (caddr_t
)p
;
elem
->data
.raw
= (caddr_t
)p
;
printf("C/U/%s", Class
[class].Id
[id
]);
elem
->data
.raw
= (caddr_t
)p
;
elem
->data
.raw
= (caddr_t
)p
;
Class
[class].name
, Class
[class].Id
[id
]);
return elem
->asnlen
+ hdr
;
* Display the ASN.1 object represented by the BE object.
* This used to be an integral part of asn1_parse() before the intermediate
u_char
*p
= (u_char
*)elem
->data
.raw
;
u_long asnlen
= elem
->asnlen
;
for (i
= asnlen
; i
-- > 0; p
++);
int o
= 0, first
= -1, i
= asnlen
;
if (!nflag
&& asnlen
> 2) {
struct obj_abrev
*a
= &obj_abrev_list
[0];
if (!memcmp(a
->oid
, p
, strlen(a
->oid
))) {
fputs(a
->prefix
, stdout
);
o
= (o
<< ASN_SHIFT7
) + (*p
& ~ASN_BIT8
);
* first subitem encodes two items with 1st*OIDMUX+2nd
OBJ_PRINT(o
/OIDMUX
, first
);
printf("%ld", elem
->data
.integer
);
printf("%ld", elem
->data
.uns
);
register int printable
= 1, first
= 1;
u_char
*p
= elem
->data
.str
;
for (i
= asnlen
; printable
&& i
-- > 0; p
++)
printable
= isprint(*p
) || isspace(*p
);
(void)printfn(p
, p
+asnlen
);
for (i
= asnlen
; i
-- > 0; p
++) {
printf(first
? "%.2x" : "_%.2x", *p
);
printf("Seq(%d)", elem
->asnlen
);
if (asnlen
!= ASNLEN_INETADDR
)
printf("[inetaddr len!=%d]", ASNLEN_INETADDR
);
for (i
= asnlen
; i
-- > 0; p
++) {
Class
[CONTEXT
].Id
[elem
->id
], elem
->asnlen
);
fputs("[BE_ANY!?]", stdout
);
* This is a brute force ASN.1 printer: recurses to dump an entire structure.
* This will work for any ASN.1 stream, not just an SNMP PDU.
* By adding newlines and spaces at the correct places, this would print in
* This is not currently used.
while (i
>= 0 && length
> 0) {
i
= asn1_parse(p
, length
, &elem
);
if (elem
.type
== BE_SEQ
|| elem
.type
== BE_PDU
) {
asn1_decode(elem
.data
.raw
, elem
.asnlen
);
* version INTEGER {version-1(0)},
* community OCTET STRING,
* PDUs for all but Trap: (see rfc1157 from page 15 on)
* varbindlist SEQUENCE OF
* enterprise OBJECT IDENTIFIER,
* agent-addr NetworkAddress,
* varbindlist SEQUENCE OF
varbind_print (pduid
, np
, length
, error
)
/* Sequence of varBind */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_SEQ
) {
fputs("[!SEQ of varbind]", stdout
);
printf("[%d extra after SEQ of varbind]", length
- count
);
np
= (u_char
*)elem
.data
.raw
;
for (index
= 1; length
> 0; index
++) {
if (!error
|| index
== error
)
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_SEQ
) {
fputs("[!varbind]", stdout
);
vblength
= length
- count
;
np
= (u_char
*)elem
.data
.raw
;
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_OID
) {
fputs("[objName!=OID]", stdout
);
if (!error
|| index
== error
)
if (pduid
!= GETREQ
&& pduid
!= GETNEXTREQ
&& !error
)
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (pduid
== GETREQ
|| pduid
== GETNEXTREQ
) {
if (elem
.type
!= BE_NULL
) {
fputs("[objVal!=NULL]", stdout
);
if (error
&& index
== error
&& elem
.type
!= BE_NULL
)
fputs("[err objVal!=NULL]", stdout
);
if (!error
|| index
== error
)
* Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
snmppdu_print (pduid
, np
, length
)
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[reqId!=INT]", stdout
);
/* errorStatus (Integer) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[errorStatus!=INT]", stdout
);
if ((pduid
== GETREQ
|| pduid
== GETNEXTREQ
)
&& elem
.data
.integer
!= 0) {
printf("[errorStatus(%s)!=0]",
DECODE_ErrorStatus(elem
.data
.integer
));
} else if (elem
.data
.integer
!= 0) {
printf(" %s", DECODE_ErrorStatus(elem
.data
.integer
));
error
= elem
.data
.integer
;
/* errorIndex (Integer) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[errorIndex!=INT]", stdout
);
if ((pduid
== GETREQ
|| pduid
== GETNEXTREQ
)
&& elem
.data
.integer
!= 0)
printf("[errorIndex(%d)!=0]", elem
.data
.integer
);
else if (elem
.data
.integer
!= 0) {
printf("[errorIndex(%d) w/o errorStatus]",
printf("@%d", elem
.data
.integer
);
error
= elem
.data
.integer
;
fputs("[errorIndex==0]", stdout
);
varbind_print(pduid
, np
, length
, error
);
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_OID
) {
fputs("[enterprise!=OID]", stdout
);
/* agent-addr (inetaddr) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INETADDR
) {
fputs("[agent-addr!=INETADDR]", stdout
);
/* generic-trap (Integer) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[generic-trap!=INT]", stdout
);
generic
= elem
.data
.integer
;
printf(" %s", DECODE_GenericTrap(generic
));
/* specific-trap (Integer) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[specific-trap!=INT]", stdout
);
if (generic
!= GT_ENTERPRISE
) {
if (elem
.data
.integer
!= 0)
printf("[specific-trap(%d)!=0]", elem
.data
.integer
);
printf(" s=%d", elem
.data
.integer
);
/* time-stamp (TimeTicks) */
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_UNS
) { /* XXX */
fputs("[time-stamp!=TIMETICKS]", stdout
);
varbind_print (TRAP
, np
, length
, 0);
* Decode SNMP header and pass on to PDU printing routines
if (np
+ length
> snapend
) {
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_SEQ
) {
fputs("[!init SEQ]", stdout
);
printf("[%d extra after iSEQ]", length
- count
);
np
= (u_char
*)elem
.data
.raw
;
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_INT
) {
fputs("[version!=INT]", stdout
);
/* only handle version==0 */
if (elem
.data
.integer
!= DEF_VERSION
) {
printf("[version(%d)!=0]", elem
.data
.integer
);
if ((count
= asn1_parse(np
, length
, &elem
)) < 0)
if (elem
.type
!= BE_STR
) {
fputs("[comm!=STR]", stdout
);
if (strncmp(elem
.data
.str
, DEF_COMMUNITY
, sizeof(DEF_COMMUNITY
)-1))
printf("C=%.*s ", elem
.asnlen
, elem
.data
.str
);
if ((count
= asn1_parse(np
, length
, &pdu
)) < 0)
if (pdu
.type
!= BE_PDU
) {
fputs("[no PDU]", stdout
);
printf("[%d extra after PDU]", length
- count
);
np
= (u_char
*)pdu
.data
.raw
;
snmppdu_print(pdu
.id
, np
, length
);