X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/2373cb681bf166e79cdb159553bc3fa1b4eb9ec6..7f349b47608726a788c67747369423e64455bf5e:/print-snmp.c diff --git a/print-snmp.c b/print-snmp.c index d67a6110..aa8d78f0 100644 --- a/print-snmp.c +++ b/print-snmp.c @@ -1,16 +1,29 @@ /* * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 - * The Regents of the University of California. All rights reserved. + * John Robert LoVerso. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * This implementation has been influenced by the CMU SNMP release, * by Steve Waldbusser. However, this shares no code with that system. @@ -21,6 +34,9 @@ * that work is preserved below, even though it may not rightly apply * to this file. * + * Support for SNMPv2c/SNMPv3 and the ability to link the module against + * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999. + * * This started out as a very simple program, but the incremental decoding * (into the BE structure) complicated things. * @@ -42,19 +58,24 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.34 1999-10-17 21:12:42 mcr Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.50 2001-09-17 22:16:53 fenner Exp $ (LBL)"; +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" #endif #include #include #include -#ifdef HAVE_MEMORY_H -#include -#endif #include #include +#ifdef HAVE_SMI_H +#include +#endif + #include "interface.h" #include "addrtoname.h" @@ -125,6 +146,12 @@ char *Context[] = { #define REPORT 8 }; +#define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ) +#define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ) +#define WRITE_CLASS(x) (x == SETREQ) +#define RESPONSE_CLASS(x) (x == GETRESP) +#define INTERNAL_CLASS(x) (x == REPORT) + /* * Context-specific ASN.1 types for the SNMP Exceptions and their tags */ @@ -171,7 +198,8 @@ char *ErrorStatus[] = { }; #define DECODE_ErrorStatus(e) \ ( e >= 0 && e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \ - ? ErrorStatus[e] : (sprintf(errbuf, "err=%u", e), errbuf)) + ? ErrorStatus[e] \ + : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf)) /* * generic-trap values in the SNMP Trap-PDU @@ -188,7 +216,8 @@ char *GenericTrap[] = { }; #define DECODE_GenericTrap(t) \ ( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \ - ? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf)) + ? GenericTrap[t] \ + : (snprintf(buf, sizeof(buf), "gt=%d", t), buf)) /* * ASN.1 type class table @@ -265,6 +294,10 @@ struct obj_abrev { #ifndef NO_ABREV_EXPERI /* .iso.org.dod.internet.experimental */ { "X:", &_experimental_obj, "\53\6\1\3" }, +#endif +#ifndef NO_ABBREV_SNMPMODS + /* .iso.org.dod.internet.snmpV2.snmpModules */ + { "S:", &_snmpModules_obj, "\53\6\1\6\3" }, #endif { 0,0,0 } }; @@ -330,12 +363,13 @@ struct be { char *SnmpVersion[] = { "SNMPv1", #define SNMP_VERSION_1 0 - "SNMPv2" + "SNMPv2c", #define SNMP_VERSION_2 1 + "SNMPv2u", +#define SNMP_VERSION_2U 2 + "SNMPv3" +#define SNMP_VERSION_3 3 }; -#define DECODE_SnmpVersion(v) \ - ( v >= 0 && v < sizeof(SnmpVersion)/sizeof(SnmpVersion[0]) \ - ? SnmpVersion[v] : (sprintf(versionbuf, "version=%u", v), versionbuf)) /* * Defaults for SNMP PDU components @@ -384,7 +418,7 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) elem->asnlen = 0; elem->type = BE_ANY; if (len < 1) { - ifNotTruncated puts("[nothing to parse], stdout"); + ifNotTruncated fputs("[nothing to parse]", stdout); return -1; } @@ -407,16 +441,11 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) elem->form = form; elem->class = class; elem->id = id; - if (vflag) - printf("|%.2x", *p); p++; len--; hdr = 1; /* extended tag field */ if (id == ASN_ID_EXT) { - for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) { - if (vflag) - printf("|%.2x", *p); + for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) id = (id << 7) | (*p & ~ASN_BIT8); - } if (len == 0 && *p & ASN_BIT8) { ifNotTruncated fputs("[Xtagfield?]", stdout); return -1; @@ -431,8 +460,6 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) return -1; } elem->asnlen = *p; - if (vflag) - printf("|%.2x", *p); p++; len--; hdr++; if (elem->asnlen & ASN_BIT8) { int noct = elem->asnlen % ASN_BIT8; @@ -441,11 +468,8 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) ifNotTruncated printf("[asnlen? %d<%d]", len, noct); return -1; } - for (; noct-- > 0; len--, hdr++) { - if (vflag) - printf("|%.2x", *p); + for (; noct-- > 0; len--, hdr++) elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++; - } } if (len < elem->asnlen) { if (!truncated) { @@ -632,7 +656,7 @@ asn1_print(struct be *elem) switch (elem->type) { case BE_OCTET: - for (i = asnlen; i-- > 0; p++); + for (i = asnlen; i-- > 0; p++) printf("_%.2x", *p); break; @@ -642,7 +666,7 @@ asn1_print(struct be *elem) case BE_OID: { int o = 0, first = -1, i = asnlen; - if (!nflag && asnlen > 2) { + if (!sflag && !nflag && asnlen > 2) { struct obj_abrev *a = &obj_abrev_list[0]; for (; a->node; a++) { if (!memcmp(a->oid, (char *)p, @@ -656,11 +680,12 @@ asn1_print(struct be *elem) } } } - for (; i-- > 0; p++) { + + for (; !sflag && i-- > 0; p++) { o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8); if (*p & ASN_LONGLEN) - continue; - + continue; + /* * first subitem encodes two items with 1st*OIDMUX+2nd */ @@ -684,7 +709,7 @@ asn1_print(struct be *elem) break; case BE_UNS: - printf("%d", elem->data.uns); + printf("%u", elem->data.uns); break; case BE_UNS64: { /* idea borrowed from by Marshall Rose */ @@ -698,12 +723,21 @@ asn1_print(struct be *elem) d = elem->data.uns64.high * 4294967296.0; /* 2^32 */ if (elem->data.uns64.high <= 0x1fffff) { d += elem->data.uns64.low; +#if 0 /*is looks illegal, but what is the intention?*/ printf("%.f", d); +#else + printf("%f", d); +#endif break; } d += (elem->data.uns64.low & 0xfffff000); - sprintf(first, "%.f", d); - sprintf(last, "%5.5d", elem->data.uns64.low & 0xfff); +#if 0 /*is looks illegal, but what is the intention?*/ + snprintf(first, sizeof(first), "%.f", d); +#else + snprintf(first, sizeof(first), "%f", d); +#endif + snprintf(last, sizeof(last), "%5.5d", + elem->data.uns64.low & 0xfff); for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4; cpl >= last; cpf--, cpl--) { @@ -742,18 +776,13 @@ asn1_print(struct be *elem) printf("Seq(%u)", elem->asnlen); break; - case BE_INETADDR: { - char sep; + case BE_INETADDR: if (asnlen != ASNLEN_INETADDR) printf("[inetaddr len!=%d]", ASNLEN_INETADDR); - sep='['; for (i = asnlen; i-- > 0; p++) { - printf("%c%u", sep, *p); - sep='.'; + printf((i == asnlen-1) ? "%u" : ".%u", *p); } - putchar(']'); break; - } case BE_NOSUCHOBJECT: case BE_NOSUCHINST: @@ -809,6 +838,277 @@ asn1_decode(u_char *p, u_int length) } #endif +#ifdef LIBSMI + +struct smi2be { + SmiBasetype basetype; + int be; +}; + +static struct smi2be smi2betab[] = { + { SMI_BASETYPE_INTEGER32, BE_INT }, + { SMI_BASETYPE_OCTETSTRING, BE_STR }, + { SMI_BASETYPE_OCTETSTRING, BE_INETADDR }, + { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID }, + { SMI_BASETYPE_UNSIGNED32, BE_UNS }, + { SMI_BASETYPE_INTEGER64, BE_NONE }, + { SMI_BASETYPE_UNSIGNED64, BE_UNS64 }, + { SMI_BASETYPE_FLOAT32, BE_NONE }, + { SMI_BASETYPE_FLOAT64, BE_NONE }, + { SMI_BASETYPE_FLOAT128, BE_NONE }, + { SMI_BASETYPE_ENUM, BE_INT }, + { SMI_BASETYPE_BITS, BE_STR }, + { SMI_BASETYPE_UNKNOWN, BE_NONE } +}; + +static void smi_decode_oid(struct be *elem, unsigned int *oid, + unsigned int oidsize, unsigned int *oidlen) +{ + u_char *p = (u_char *)elem->data.raw; + u_int32_t asnlen = elem->asnlen; + int o = 0, first = -1, i = asnlen; + + for (*oidlen = 0; sflag && i-- > 0; p++) { + o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8); + if (*p & ASN_LONGLEN) + continue; + + /* + * first subitem encodes two items with 1st*OIDMUX+2nd + */ + if (first < 0) { + first = 0; + if (*oidlen < oidsize) { + oid[(*oidlen)++] = o/OIDMUX; + } + o %= OIDMUX; + } + if (*oidlen < oidsize) { + oid[(*oidlen)++] = o; + } + o = 0; + } +} + +static int smi_check_type(SmiBasetype basetype, int be) +{ + int i; + + for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) { + if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) { + return 1; + } + } + + return 0; +} + +static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange, + struct be *elem) +{ + int ok = 1; + + switch (smiType->basetype) { + case SMI_BASETYPE_OBJECTIDENTIFIER: + case SMI_BASETYPE_OCTETSTRING: + if (smiRange->minValue.value.unsigned32 + == smiRange->maxValue.value.unsigned32) { + ok = (elem->asnlen == smiRange->minValue.value.unsigned32); + } else { + ok = (elem->asnlen >= smiRange->minValue.value.unsigned32 + && elem->asnlen <= smiRange->maxValue.value.unsigned32); + } + break; + + case SMI_BASETYPE_INTEGER32: + ok = (elem->data.integer >= smiRange->minValue.value.integer32 + && elem->data.integer <= smiRange->maxValue.value.integer32); + break; + + case SMI_BASETYPE_UNSIGNED32: + ok = (elem->data.uns >= smiRange->minValue.value.unsigned32 + && elem->data.uns <= smiRange->maxValue.value.unsigned32); + break; + + case SMI_BASETYPE_UNSIGNED64: + /* XXX */ + break; + + /* case SMI_BASETYPE_INTEGER64: SMIng */ + /* case SMI_BASETYPE_FLOAT32: SMIng */ + /* case SMI_BASETYPE_FLOAT64: SMIng */ + /* case SMI_BASETYPE_FLOAT128: SMIng */ + + case SMI_BASETYPE_ENUM: + case SMI_BASETYPE_BITS: + case SMI_BASETYPE_UNKNOWN: + ok = 1; + break; + } + + return ok; +} + +static int smi_check_range(SmiType *smiType, struct be *elem) +{ + SmiRange *smiRange; + int ok = 1; + + for (smiRange = smiGetFirstRange(smiType); + smiRange; + smiRange = smiGetNextRange(smiRange)) { + + ok = smi_check_a_range(smiType, smiRange, elem); + + if (ok) { + break; + } + } + + if (ok) { + SmiType *parentType; + parentType = smiGetParentType(smiType); + if (parentType) { + ok = smi_check_range(parentType, elem); + } + } + + return ok; +} + +static SmiNode *smi_print_variable(struct be *elem) +{ + unsigned int oid[128], oidlen; + SmiNode *smiNode = NULL; + int i; + + smi_decode_oid(elem, oid, sizeof(oid)/sizeof(unsigned int), &oidlen); + smiNode = smiGetNodeByOID(oidlen, oid); + if (! smiNode) { + asn1_print(elem); + return NULL; + } + if (vflag) { + fputs(smiGetNodeModule(smiNode)->name, stdout); + fputs("::", stdout); + } + fputs(smiNode->name, stdout); + if (smiNode->oidlen < oidlen) { + for (i = smiNode->oidlen; i < oidlen; i++) { + printf(".%u", oid[i]); + } + } + return smiNode; +} + +static void smi_print_value(SmiNode *smiNode, u_char pduid, struct be *elem) +{ + unsigned int oid[128], oidlen; + SmiType *smiType; + SmiNamedNumber *nn; + int i, done = 0; + + if (! smiNode || ! (smiNode->nodekind + & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) { + asn1_print(elem); + return; + } + + if (elem->type == BE_NOSUCHOBJECT + || elem->type == BE_NOSUCHINST + || elem->type == BE_ENDOFMIBVIEW) { + asn1_print(elem); + return; + } + + if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) { + fputs("[notNotifyable]", stdout); + } + + if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) { + fputs("[notReadable]", stdout); + } + + if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) { + fputs("[notWritable]", stdout); + } + + if (RESPONSE_CLASS(pduid) + && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) { + fputs("[noAccess]", stdout); + } + + smiType = smiGetNodeType(smiNode); + if (! smiType) { + asn1_print(elem); + return; + } + + if (! smi_check_type(smiType->basetype, elem->type)) { + fputs("[wrongType]", stdout); + } + + if (! smi_check_range(smiType, elem)) { + fputs("[outOfRange]", stdout); + } + + /* resolve bits to named bits */ + + /* check whether instance identifier is valid */ + + /* apply display hints (integer, octetstring) */ + + /* convert instance identifier to index type values */ + + switch (elem->type) { + case BE_OID: + if (smiType->basetype == SMI_BASETYPE_BITS) { + /* print bit labels */ + } else { + smi_decode_oid(elem, oid, + sizeof(oid)/sizeof(unsigned int), + &oidlen); + smiNode = smiGetNodeByOID(oidlen, oid); + if (smiNode) { + if (vflag) { + fputs(smiGetNodeModule(smiNode)->name, stdout); + fputs("::", stdout); + } + fputs(smiNode->name, stdout); + if (smiNode->oidlen < oidlen) { + for (i = smiNode->oidlen; + i < oidlen; i++) { + printf(".%u", oid[i]); + } + } + done++; + } + } + break; + + case BE_INT: + if (smiType->basetype == SMI_BASETYPE_ENUM) { + for (nn = smiGetFirstNamedNumber(smiType); + nn; + nn = smiGetNextNamedNumber(nn)) { + if (nn->value.value.integer32 + == elem->data.integer) { + fputs(nn->name, stdout); + printf("(%d)", elem->data.integer); + done++; + break; + } + } + } + break; + } + + if (! done) { + asn1_print(elem); + } +} +#endif + /* * General SNMP header * SEQUENCE { @@ -846,10 +1146,13 @@ asn1_decode(u_char *p, u_int length) * Decode SNMP varBind */ static void -varbind_print(u_char pduid, const u_char *np, u_int length, int error) +varbind_print(u_char pduid, const u_char *np, u_int length) { struct be elem; int count = 0, ind; +#ifdef LIBSMI + SmiNode *smiNode = NULL; +#endif /* Sequence of varBind */ if ((count = asn1_parse(np, length, &elem)) < 0) @@ -869,8 +1172,7 @@ varbind_print(u_char pduid, const u_char *np, u_int length, int error) const u_char *vbend; u_int vblength; - if (!error || ind == error) - fputs(" ", stdout); + fputs(" ", stdout); /* Sequence */ if ((count = asn1_parse(np, length, &elem)) < 0) @@ -894,13 +1196,16 @@ varbind_print(u_char pduid, const u_char *np, u_int length, int error) asn1_print(&elem); return; } - if (!error || ind == error) - asn1_print(&elem); +#ifdef LIBSMI + smiNode = smi_print_variable(&elem); +#else + asn1_print(&elem); +#endif length -= count; np += count; if (pduid != GETREQ && pduid != GETNEXTREQ - && pduid != GETBULKREQ && !error) + && pduid != GETBULKREQ) fputs("=", stdout); /* objVal (ANY) */ @@ -912,12 +1217,15 @@ varbind_print(u_char pduid, const u_char *np, u_int length, int error) fputs("[objVal!=NULL]", stdout); asn1_print(&elem); } - } else - if (error && ind == error && elem.type != BE_NULL) - fputs("[err objVal!=NULL]", stdout); - if (!error || ind == error) + } else { + if (elem.type != BE_NULL) { +#ifdef LIBSMI + smi_print_value(smiNode, pduid, &elem); +#else asn1_print(&elem); - +#endif + } + } length = vblength; np = vbend; } @@ -941,7 +1249,8 @@ snmppdu_print(u_char pduid, const u_char *np, u_int length) asn1_print(&elem); return; } - /* ignore the reqId */ + if (vflag) + printf("R=%d ", elem.data.integer); length -= count; np += count; @@ -999,7 +1308,7 @@ snmppdu_print(u_char pduid, const u_char *np, u_int length) length -= count; np += count; - varbind_print(pduid, np, length, error); + varbind_print(pduid, np, length); return; } @@ -1007,7 +1316,7 @@ snmppdu_print(u_char pduid, const u_char *np, u_int length) * Decode SNMP Trap PDU */ static void -trap_print(const u_char *np, u_int length) +trappdu_print(const u_char *np, u_int length) { struct be elem; int count = 0, generic; @@ -1086,70 +1395,132 @@ trap_print(const u_char *np, u_int length) length -= count; np += count; - varbind_print (TRAP, np, length, 0); + varbind_print (TRAP, np, length); return; } /* - * Decode SNMP header and pass on to PDU printing routines + * Decode arbitrary SNMP PDUs. */ -void -snmp_print(const u_char *np, u_int length) +static void +pdu_print(const u_char *np, u_int length, int version) { - struct be elem, pdu; + struct be pdu; int count = 0; - int version = 0; - truncated = 0; + /* PDU (Context) */ + if ((count = asn1_parse(np, length, &pdu)) < 0) + return; + if (pdu.type != BE_PDU) { + fputs("[no PDU]", stdout); + return; + } + if (count < length) + printf("[%d extra after PDU]", length - count); + if (vflag) { + fputs("{ ", stdout); + } + asn1_print(&pdu); + fputs(" ", stdout); + /* descend into PDU */ + length = pdu.asnlen; + np = (u_char *)pdu.data.raw; - /* truncated packet? */ - if (np + length > snapend) { - truncated = 1; - length = snapend - np; + if (version == SNMP_VERSION_1 && + (pdu.id == GETBULKREQ || pdu.id == INFORMREQ || + pdu.id == V2TRAP || pdu.id == REPORT)) { + printf("[v2 PDU in v1 message]"); + return; } - putchar(' '); + if (version == SNMP_VERSION_2 && pdu.id == TRAP) { + printf("[v1 PDU in v2 message]"); + return; + } - /* initial Sequence */ + switch (pdu.id) { + case TRAP: + trappdu_print(np, length); + break; + case GETREQ: + case GETNEXTREQ: + case GETRESP: + case SETREQ: + case GETBULKREQ: + case INFORMREQ: + case V2TRAP: + case REPORT: + snmppdu_print(pdu.id, np, length); + break; + } + + if (vflag) { + fputs("} ", stdout); + } +} + +/* + * Decode a scoped SNMP PDU. + */ +static void +scopedpdu_print(const u_char *np, u_int length, int version) +{ + struct be elem; + int i, count = 0; + + /* Sequence */ if ((count = asn1_parse(np, length, &elem)) < 0) return; if (elem.type != BE_SEQ) { - fputs("[!init SEQ]", stdout); + fputs("[!scoped PDU]", stdout); asn1_print(&elem); return; } - if (count < length) - printf("[%d extra after iSEQ]", length - count); - /* descend */ length = elem.asnlen; np = (u_char *)elem.data.raw; - /* Version (Integer) */ + + /* contextEngineID (OCTET STRING) */ if ((count = asn1_parse(np, length, &elem)) < 0) return; - if (elem.type != BE_INT) { - fputs("[version!=INT]", stdout); + if (elem.type != BE_STR) { + fputs("[contextEngineID!=STR]", stdout); asn1_print(&elem); return; } - /* only handle version==0 || version==1 */ - switch (elem.data.integer) { - case SNMP_VERSION_1: - case SNMP_VERSION_2: { - char versionbuf[10]; - if (vflag) - printf("%s ", DECODE_SnmpVersion(elem.data.integer)); - break; - } - default: { - char versionbuf[10]; - printf("[%s]", DECODE_SnmpVersion(elem.data.integer)); + length -= count; + np += count; + + fputs("E= ", stdout); + for (i = 0; i < (int)elem.asnlen; i++) { + printf("0x%02X", elem.data.str[i]); + } + fputs(" ", stdout); + + /* contextName (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[contextName!=STR]", stdout); + asn1_print(&elem); return; } - } - version = elem.data.integer; length -= count; np += count; + printf("C=%.*s ", (int)elem.asnlen, elem.data.str); + + pdu_print(np, length, version); +} + +/* + * Decode SNMP Community Header (SNMPv1 and SNMPv2c) + */ +static void +community_print(const u_char *np, u_int length, int version) +{ + struct be elem; + int count = 0; + /* Community (String) */ if ((count = asn1_parse(np, length, &elem)) < 0) return; @@ -1166,46 +1537,314 @@ snmp_print(const u_char *np, u_int length) length -= count; np += count; - /* PDU (Context) */ - if ((count = asn1_parse(np, length, &pdu)) < 0) + pdu_print(np, length, version); +} + +/* + * Decode SNMPv3 User-based Security Message Header (SNMPv3) + */ +static void +usm_print(const u_char *np, u_int length) +{ + struct be elem; + int count = 0; + + /* Sequence */ + if ((count = asn1_parse(np, length, &elem)) < 0) return; - if (pdu.type != BE_PDU) { - fputs("[no PDU]", stdout); + if (elem.type != BE_SEQ) { + fputs("[!usm]", stdout); + asn1_print(&elem); + return; + } + length = elem.asnlen; + np = (u_char *)elem.data.raw; + + /* msgAuthoritativeEngineID (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgAuthoritativeEngineID!=STR]", stdout); + asn1_print(&elem); + return; + } + length -= count; + np += count; + + /* msgAuthoritativeEngineBoots (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[msgAuthoritativeEngineBoots!=INT]", stdout); + asn1_print(&elem); + return; + } + if (vflag) + printf("B=%d ", elem.data.integer); + length -= count; + np += count; + + /* msgAuthoritativeEngineTime (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[msgAuthoritativeEngineTime!=INT]", stdout); + asn1_print(&elem); + return; + } + if (vflag) + printf("T=%d ", elem.data.integer); + length -= count; + np += count; + + /* msgUserName (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgUserName!=STR]", stdout); + asn1_print(&elem); + return; + } + length -= count; + np += count; + + printf("U=%.*s ", (int)elem.asnlen, elem.data.str); + + /* msgAuthenticationParameters (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgAuthenticationParameters!=STR]", stdout); + asn1_print(&elem); return; } + length -= count; + np += count; + + /* msgPrivacyParameters (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgPrivacyParameters!=STR]", stdout); + asn1_print(&elem); + return; + } + length -= count; + np += count; + if (count < length) - printf("[%d extra after PDU]", length - count); - asn1_print(&pdu); - /* descend into PDU */ - length = pdu.asnlen; - np = (u_char *)pdu.data.raw; + printf("[%d extra after usm SEQ]", length - count); +} - if (version == SNMP_VERSION_1 && - (pdu.id == GETBULKREQ || pdu.id == INFORMREQ || - pdu.id == V2TRAP || pdu.id == REPORT)) { - printf("[v2 PDU in v1 message]"); +/* + * Decode SNMPv3 Message Header (SNMPv3) + */ +static void +v3msg_print(const u_char *np, u_int length) +{ + struct be elem; + int count = 0; + u_char flags; + int model; + const u_char *xnp = np; + int xlength = length; + + /* Sequence */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_SEQ) { + fputs("[!message]", stdout); + asn1_print(&elem); return; } + length = elem.asnlen; + np = (u_char *)elem.data.raw; - if (version == SNMP_VERSION_2 && pdu.id == TRAP) { - printf("[v1 PDU in v2 message]"); + if (vflag) { + fputs("{ ", stdout); + } + + /* msgID (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[msgID!=INT]", stdout); + asn1_print(&elem); return; } + length -= count; + np += count; - switch (pdu.id) { - case TRAP: - trap_print(np, length); + /* msgMaxSize (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[msgMaxSize!=INT]", stdout); + asn1_print(&elem); + return; + } + length -= count; + np += count; + + /* msgFlags (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgFlags!=STR]", stdout); + asn1_print(&elem); + return; + } + if (elem.asnlen != 1) { + printf("[msgFlags size %d]", elem.asnlen); + return; + } + flags = elem.data.str[0]; + if (flags != 0x00 && flags != 0x01 && flags != 0x03 + && flags != 0x04 && flags != 0x05 && flags != 0x07) { + printf("[msgFlags=0x%02X]", flags); + return; + } + length -= count; + np += count; + + fputs("F=", stdout); + if (flags & 0x01) fputs("a", stdout); + if (flags & 0x02) fputs("p", stdout); + if (flags & 0x04) fputs("r", stdout); + fputs(" ", stdout); + + /* msgSecurityModel (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[msgSecurityModel!=INT]", stdout); + asn1_print(&elem); + return; + } + model = elem.data.integer; + length -= count; + np += count; + + if (count < length) + printf("[%d extra after message SEQ]", length - count); + + if (vflag) { + fputs("} ", stdout); + } + + if (model == 3) { + if (vflag) { + fputs("{ USM ", stdout); + } + } else { + printf("[security model %d]", model); + return; + } + + np = xnp + (np - xnp); + length = xlength - (np - xnp); + + /* msgSecurityParameters (OCTET STRING) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + fputs("[msgSecurityParameters!=STR]", stdout); + asn1_print(&elem); + return; + } + length -= count; + np += count; + + if (model == 3) { + usm_print(elem.data.str, elem.asnlen); + if (vflag) { + fputs("} ", stdout); + } + } + + if (vflag) { + fputs("{ ScopedPDU ", stdout); + } + + scopedpdu_print(np, length, 3); + + if (vflag) { + fputs("} ", stdout); + } +} + +/* + * Decode SNMP header and pass on to PDU printing routines + */ +void +snmp_print(const u_char *np, u_int length) +{ + struct be elem; + int count = 0; + int version = 0; + + truncated = 0; + + /* truncated packet? */ + if (np + length > snapend) { + truncated = 1; + length = snapend - np; + } + + putchar(' '); + + /* initial Sequence */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_SEQ) { + fputs("[!init SEQ]", stdout); + asn1_print(&elem); + return; + } + if (count < length) + printf("[%d extra after iSEQ]", length - count); + /* descend */ + length = elem.asnlen; + np = (u_char *)elem.data.raw; + + /* Version (INTEGER) */ + if ((count = asn1_parse(np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + fputs("[version!=INT]", stdout); + asn1_print(&elem); + return; + } + + switch (elem.data.integer) { + case SNMP_VERSION_1: + case SNMP_VERSION_2: + case SNMP_VERSION_3: + if (vflag) + printf("{ %s ", SnmpVersion[elem.data.integer]); break; - case GETREQ: - case GETNEXTREQ: - case GETRESP: - case SETREQ: - case GETBULKREQ: - case INFORMREQ: - case V2TRAP: - case REPORT: - snmppdu_print(pdu.id, np, length); + default: + printf("[version = %d]", elem.data.integer); + return; + } + version = elem.data.integer; + length -= count; + np += count; + + switch (version) { + case SNMP_VERSION_1: + case SNMP_VERSION_2: + community_print(np, length, version); + break; + case SNMP_VERSION_3: + v3msg_print(np, length); + break; + default: + printf("[version = %d]", elem.data.integer); break; } - return; + + if (vflag) { + fputs("} ", stdout); + } }