X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/f555c163f90c9de17ebcef8313f86404c5174ca9..d6aacc676d8540f31d1df72b12b43fc9cde93df6:/print-snmp.c diff --git a/print-snmp.c b/print-snmp.c index 49b03f48..a2aa0742 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. * @@ -40,29 +56,30 @@ # @(#)snmp.awk.x 1.1 (LANL) 1/15/90 */ -#ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.33 1999-10-07 23:47:12 mcr Exp $ (LBL)"; +#ifdef HAVE_CONFIG_H +#include "config.h" #endif -#include -#include +#include -#include -#ifdef HAVE_MEMORY_H -#include -#endif #include #include -#include "interface.h" -#include "addrtoname.h" +#ifdef USE_LIBSMI +#include +#endif + +#include "netdissect.h" + +#undef OPAQUE /* defined in */ + +static const char tstr[] = "[|snmp]"; /* * Universal ASN.1 types * (we only care about the tag values for those allowed in the Internet SMI) */ -char *Universal[] = { +static const char *Universal[] = { "U-0", "Boolean", "Integer", @@ -85,7 +102,7 @@ char *Universal[] = { /* * Application-wide ASN.1 types from the Internet SMI and their tags */ -char *Application[] = { +static const char *Application[] = { "IpAddress", #define IPADDR 0 "Counter", @@ -94,13 +111,17 @@ char *Application[] = { #define GAUGE 2 "TimeTicks", #define TIMETICKS 3 - "Opaque" + "Opaque", +#define OPAQUE 4 + "C-5", + "Counter64" +#define COUNTER64 6 }; /* * Context-specific ASN.1 types for the SNMP PDUs and their tags */ -char *Context[] = { +static const char *Context[] = { "GetRequest", #define GETREQ 0 "GetNextRequest", @@ -109,37 +130,77 @@ char *Context[] = { #define GETRESP 2 "SetRequest", #define SETREQ 3 - "Trap" + "Trap", #define TRAP 4 + "GetBulk", +#define GETBULKREQ 5 + "Inform", +#define INFORMREQ 6 + "V2Trap", +#define V2TRAP 7 + "Report" +#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 + */ +static const char *Exceptions[] = { + "noSuchObject", +#define NOSUCHOBJECT 0 + "noSuchInstance", +#define NOSUCHINSTANCE 1 + "endOfMibView", +#define ENDOFMIBVIEW 2 }; /* * Private ASN.1 types * The Internet SMI does not specify any */ -char *Private[] = { +static const char *Private[] = { "P-0" }; /* * error-status values for any SNMP PDU */ -char *ErrorStatus[] = { +static const char *ErrorStatus[] = { "noError", "tooBig", "noSuchName", "badValue", "readOnly", - "genErr" + "genErr", + "noAccess", + "wrongType", + "wrongLength", + "wrongEncoding", + "wrongValue", + "noCreation", + "inconsistentValue", + "resourceUnavailable", + "commitFailed", + "undoFailed", + "authorizationError", + "notWritable", + "inconsistentName" }; #define DECODE_ErrorStatus(e) \ - ( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \ - ? ErrorStatus[e] : (sprintf(errbuf, "err=%u", e), errbuf)) + ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \ + ? ErrorStatus[e] \ + : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf)) /* * generic-trap values in the SNMP Trap-PDU */ -char *GenericTrap[] = { +static const char *GenericTrap[] = { "coldStart", "warmStart", "linkDown", @@ -147,11 +208,12 @@ char *GenericTrap[] = { "authenticationFailure", "egpNeighborLoss", "enterpriseSpecific" -#define GT_ENTERPRISE 7 +#define GT_ENTERPRISE 6 }; #define DECODE_GenericTrap(t) \ - ( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \ - ? GenericTrap[t] : (sprintf(buf, "gt=%d", t), buf)) + ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \ + ? GenericTrap[t] \ + : (snprintf(buf, sizeof(buf), "gt=%d", t), buf)) /* * ASN.1 type class table @@ -159,9 +221,9 @@ char *GenericTrap[] = { * type definitions. */ #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */ -struct { - char *name; - char **Id; +static const struct { + const char *name; + const char **Id; int numIDs; } Class[] = { defineCLASS(Universal), @@ -172,12 +234,14 @@ struct { #define CONTEXT 2 defineCLASS(Private), #define PRIVATE 3 + defineCLASS(Exceptions), +#define EXCEPTIONS 4 }; /* * defined forms for ASN.1 types */ -char *Form[] = { +static const char *Form[] = { "Primitive", #define PRIMITIVE 0 "Constructed", @@ -189,7 +253,7 @@ char *Form[] = { * This is stored as a general-order tree. */ struct obj { - char *desc; /* name of object */ + const 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 */ @@ -210,10 +274,10 @@ struct obj { * Currently, this includes the prefixes for the Internet MIB, the * private enterprises tree, and the experimental tree. */ -struct obj_abrev { - char *prefix; /* prefix for this abrev */ +static const struct obj_abrev { + const char *prefix; /* prefix for this abrev */ struct obj *node; /* pointer into object table */ - char *oid; /* ASN.1 encoded OID */ + const char *oid; /* ASN.1 encoded OID */ } obj_abrev_list[] = { #ifndef NO_ABREV_MIB /* .iso.org.dod.internet.mgmt.mib */ @@ -226,6 +290,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 } }; @@ -243,10 +311,10 @@ struct obj_abrev { } while ((objp = objp->next) != NULL); \ } \ if (objp) { \ - printf(suppressdot?"%s":".%s", objp->desc); \ + ND_PRINT((ndo, suppressdot?"%s":".%s", objp->desc)); \ objp = objp->child; \ } else \ - printf(suppressdot?"%u":".%u", (o)); \ + ND_PRINT((ndo, suppressdot?"%u":".%u", (o))); \ } /* @@ -254,12 +322,16 @@ struct obj_abrev { * temporary internal representation while decoding an ASN.1 data stream. */ struct be { - u_int32_t asnlen; + uint32_t asnlen; union { - caddr_t raw; + const uint8_t *raw; int32_t integer; - u_int32_t uns; + uint32_t uns; const u_char *str; + struct { + uint32_t high; + uint32_t low; + } uns64; } data; u_short id; u_char form, class; /* tag info */ @@ -275,13 +347,30 @@ struct be { #define BE_SEQ 7 #define BE_INETADDR 8 #define BE_PDU 9 +#define BE_UNS64 10 +#define BE_NOSUCHOBJECT 128 +#define BE_NOSUCHINST 129 +#define BE_ENDOFMIBVIEW 130 +}; + +/* + * SNMP versions recognized by this module + */ +static const char *SnmpVersion[] = { + "SNMPv1", +#define SNMP_VERSION_1 0 + "SNMPv2c", +#define SNMP_VERSION_2 1 + "SNMPv2u", +#define SNMP_VERSION_2U 2 + "SNMPv3" +#define SNMP_VERSION_3 3 }; /* * Defaults for SNMP PDU components */ #define DEF_COMMUNITY "public" -#define DEF_VERSION 0 /* * constants for ASN.1 decoding @@ -301,13 +390,6 @@ struct be { #define ASN_ID_EXT 0x1f /* extension ID in tag field */ -/* - * truncated==1 means the packet was complete, but we don't have all of - * it to decode. - */ -static int truncated; -#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 @@ -317,7 +399,8 @@ static int truncated; * O/w, this returns the number of bytes parsed from "p". */ static int -asn1_parse(register const u_char *p, u_int len, struct be *elem) +asn1_parse(netdissect_options *ndo, + register const u_char *p, u_int len, struct be *elem) { u_char form, class, id; int i, hdr; @@ -325,9 +408,10 @@ 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"); + ND_PRINT((ndo, "[nothing to parse]")); return -1; } + ND_TCHECK(*p); /* * it would be nice to use a bit field, but you can't depend on them. @@ -348,65 +432,69 @@ 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); + /* + * The ID follows, as a sequence of octets with the + * 8th bit set and the remaining 7 bits being + * the next 7 bits of the value, terminated with + * an octet with the 8th bit not set. + * + * First, assemble all the octets with the 8th + * bit set. XXX - this doesn't handle a value + * that won't fit in 32 bits. + */ + for (id = 0; *p & ASN_BIT8; len--, hdr++, p++) { + if (len < 1) { + ND_PRINT((ndo, "[Xtagfield?]")); + return -1; + } + ND_TCHECK(*p); id = (id << 7) | (*p & ~ASN_BIT8); } - if (len == 0 && *p & ASN_BIT8) { - ifNotTruncated fputs("[Xtagfield?]", stdout); + if (len < 1) { + ND_PRINT((ndo, "[Xtagfield?]")); return -1; } + ND_TCHECK(*p); elem->id = id = (id << 7) | *p; --len; ++hdr; ++p; } if (len < 1) { - ifNotTruncated fputs("[no asnlen]", stdout); + ND_PRINT((ndo, "[no asnlen]")); return -1; } + ND_TCHECK(*p); elem->asnlen = *p; - if (vflag) - printf("|%.2x", *p); p++; len--; hdr++; if (elem->asnlen & ASN_BIT8) { - int noct = elem->asnlen % ASN_BIT8; + uint32_t noct = elem->asnlen % ASN_BIT8; elem->asnlen = 0; if (len < noct) { - ifNotTruncated printf("[asnlen? %d<%d]", len, noct); + ND_PRINT((ndo, "[asnlen? %d<%d]", len, noct)); return -1; } - for (; noct-- > 0; len--, hdr++) { - if (vflag) - printf("|%.2x", *p); + ND_TCHECK2(*p, noct); + for (; noct-- > 0; len--, hdr++) elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++; - } } if (len < elem->asnlen) { - if (!truncated) { - printf("[len%dasnlen); - return -1; - } - /* maybe should check at least 4? */ - elem->asnlen = len; + ND_PRINT((ndo, "[len%dasnlen)); + return -1; } if (form >= sizeof(Form)/sizeof(Form[0])) { - ifNotTruncated printf("[form?%d]", form); + ND_PRINT((ndo, "[form?%d]", form)); return -1; } if (class >= sizeof(Class)/sizeof(Class[0])) { - ifNotTruncated printf("[class?%c/%d]", *Form[form], class); + ND_PRINT((ndo, "[class?%c/%d]", *Form[form], class)); return -1; } if ((int)id >= Class[class].numIDs) { - ifNotTruncated printf("[id?%c/%s/%d]", *Form[form], - Class[class].name, id); + ND_PRINT((ndo, "[id?%c/%s/%d]", *Form[form], Class[class].name, id)); return -1; } @@ -425,6 +513,7 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) elem->type = BE_INT; data = 0; + ND_TCHECK2(*p, elem->asnlen); if (*p & ASN_BIT8) /* negative */ data = -1; for (i = elem->asnlen; i-- > 0; p++) @@ -435,7 +524,7 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) case OBJECTID: elem->type = BE_OID; - elem->data.raw = (caddr_t)p; + elem->data.raw = (const uint8_t *)p; break; case ASN_NULL: @@ -445,9 +534,8 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) default: elem->type = BE_OCTET; - elem->data.raw = (caddr_t)p; - printf("[P/U/%s]", - Class[class].Id[id]); + elem->data.raw = (const uint8_t *)p; + ND_PRINT((ndo, "[P/U/%s]", Class[class].Id[id])); break; } break; @@ -456,13 +544,14 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) switch (id) { case IPADDR: elem->type = BE_INETADDR; - elem->data.raw = (caddr_t)p; + elem->data.raw = (const uint8_t *)p; break; case COUNTER: case GAUGE: case TIMETICKS: { - register u_int32_t data; + register uint32_t data; + ND_TCHECK2(*p, elem->asnlen); elem->type = BE_UNS; data = 0; for (i = elem->asnlen; i-- > 0; p++) @@ -471,20 +560,54 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) break; } + case COUNTER64: { + register uint32_t high, low; + ND_TCHECK2(*p, elem->asnlen); + elem->type = BE_UNS64; + high = 0, low = 0; + for (i = elem->asnlen; i-- > 0; p++) { + high = (high << 8) | + ((low & 0xFF000000) >> 24); + low = (low << 8) | *p; + } + elem->data.uns64.high = high; + elem->data.uns64.low = low; + break; + } + default: elem->type = BE_OCTET; - elem->data.raw = (caddr_t)p; - printf("[P/A/%s]", - Class[class].Id[id]); + elem->data.raw = (const uint8_t *)p; + ND_PRINT((ndo, "[P/A/%s]", + Class[class].Id[id])); + break; + } + break; + + case CONTEXT: + switch (id) { + case NOSUCHOBJECT: + elem->type = BE_NOSUCHOBJECT; + elem->data.raw = NULL; + break; + + case NOSUCHINSTANCE: + elem->type = BE_NOSUCHINST; + elem->data.raw = NULL; + break; + + case ENDOFMIBVIEW: + elem->type = BE_ENDOFMIBVIEW; + elem->data.raw = NULL; break; } break; default: + ND_PRINT((ndo, "[P/%s/%s]", Class[class].name, Class[class].Id[id])); + ND_TCHECK2(*p, elem->asnlen); elem->type = BE_OCTET; - elem->data.raw = (caddr_t)p; - printf("[P/%s/%s]", - Class[class].name, Class[class].Id[id]); + elem->data.raw = (const uint8_t *)p; break; } break; @@ -495,27 +618,26 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) switch (id) { case SEQUENCE: elem->type = BE_SEQ; - elem->data.raw = (caddr_t)p; + elem->data.raw = (const uint8_t *)p; break; default: elem->type = BE_OCTET; - elem->data.raw = (caddr_t)p; - printf("C/U/%s", Class[class].Id[id]); + elem->data.raw = (const uint8_t *)p; + ND_PRINT((ndo, "C/U/%s", Class[class].Id[id])); break; } break; case CONTEXT: elem->type = BE_PDU; - elem->data.raw = (caddr_t)p; + elem->data.raw = (const uint8_t *)p; break; default: elem->type = BE_OCTET; - elem->data.raw = (caddr_t)p; - printf("C/%s/%s", - Class[class].name, Class[class].Id[id]); + elem->data.raw = (const uint8_t *)p; + ND_PRINT((ndo, "C/%s/%s", Class[class].name, Class[class].Id[id])); break; } break; @@ -523,6 +645,10 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) p += elem->asnlen; len -= elem->asnlen; return elem->asnlen + hdr; + +trunc: + ND_PRINT((ndo, "%s", tstr)); + return -1; } /* @@ -530,54 +656,64 @@ asn1_parse(register const u_char *p, u_int len, struct be *elem) * This used to be an integral part of asn1_parse() before the intermediate * BE form was added. */ -static void -asn1_print(struct be *elem) +static int +asn1_print(netdissect_options *ndo, + struct be *elem) { - u_char *p = (u_char *)elem->data.raw; - u_int32_t asnlen = elem->asnlen; - int i; + const u_char *p = (const u_char *)elem->data.raw; + uint32_t asnlen = elem->asnlen; + uint32_t i; switch (elem->type) { case BE_OCTET: - for (i = asnlen; i-- > 0; p++); - printf("_%.2x", *p); + ND_TCHECK2(*p, asnlen); + for (i = asnlen; i-- > 0; p++) + ND_PRINT((ndo, "_%.2x", *p)); break; case BE_NULL: break; case BE_OID: { - int o = 0, first = -1, i = asnlen; + int o = 0, first = -1; - if (!nflag && asnlen > 2) { - struct obj_abrev *a = &obj_abrev_list[0]; + i = asnlen; + if (!ndo->ndo_mflag && !ndo->ndo_nflag && asnlen > 2) { + const struct obj_abrev *a = &obj_abrev_list[0]; + size_t a_len = strlen(a->oid); for (; a->node; a++) { - if (!memcmp(a->oid, (char *)p, - strlen(a->oid))) { + ND_TCHECK2(*p, a_len); + if (memcmp(a->oid, p, a_len) == 0) { objp = a->node->child; i -= strlen(a->oid); p += strlen(a->oid); - fputs(a->prefix, stdout); + ND_PRINT((ndo, "%s", a->prefix)); first = 1; break; } } } - for (; i-- > 0; p++) { + + for (; !ndo->ndo_mflag && i-- > 0; p++) { + ND_TCHECK(*p); o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8); if (*p & ASN_LONGLEN) - continue; + continue; /* * first subitem encodes two items with 1st*OIDMUX+2nd + * (see X.690:1997 clause 8.19 for the details) */ if (first < 0) { - if (!nflag) + int s; + if (!ndo->ndo_nflag) objp = mibroot; first = 0; - OBJ_PRINT(o/OIDMUX, first); - o %= OIDMUX; + s = o / OIDMUX; + if (s > 2) s = 2; + OBJ_PRINT(s, first); + o -= s * OIDMUX; } OBJ_PRINT(o, first); if (--first < 0) @@ -588,61 +724,114 @@ asn1_print(struct be *elem) } case BE_INT: - printf("%d", elem->data.integer); + ND_PRINT((ndo, "%d", elem->data.integer)); break; case BE_UNS: - printf("%d", elem->data.uns); + ND_PRINT((ndo, "%u", elem->data.uns)); break; + case BE_UNS64: { /* idea borrowed from by Marshall Rose */ + double d; + int j, carry; + char *cpf, *cpl, last[6], first[30]; + if (elem->data.uns64.high == 0) { + ND_PRINT((ndo, "%u", elem->data.uns64.low)); + break; + } + 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?*/ + ND_PRINT((ndo, "%.f", d)); +#else + ND_PRINT((ndo, "%f", d)); +#endif + break; + } + d += (elem->data.uns64.low & 0xfffff000); +#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--) { + j = carry + (*cpf - '0') + (*cpl - '0'); + if (j > 9) { + j -= 10; + carry = 1; + } else { + carry = 0; + } + *cpf = j + '0'; + } + ND_PRINT((ndo, "%s", first)); + break; + } + case BE_STR: { register int printable = 1, first = 1; - const u_char *p = elem->data.str; + + p = elem->data.str; + ND_TCHECK2(*p, asnlen); for (i = asnlen; printable && i-- > 0; p++) - printable = isprint(*p) || isspace(*p); + printable = ND_ISPRINT(*p); p = elem->data.str; if (printable) { - putchar('"'); - (void)fn_print(p, p + asnlen); - putchar('"'); + ND_PRINT((ndo, "\"")); + if (fn_printn(ndo, p, asnlen, ndo->ndo_snapend)) { + ND_PRINT((ndo, "\"")); + goto trunc; + } + ND_PRINT((ndo, "\"")); } else for (i = asnlen; i-- > 0; p++) { - printf(first ? "%.2x" : "_%.2x", *p); + ND_PRINT((ndo, first ? "%.2x" : "_%.2x", *p)); first = 0; } break; } case BE_SEQ: - printf("Seq(%u)", elem->asnlen); + ND_PRINT((ndo, "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='.'; + ND_PRINT((ndo, "[inetaddr len!=%d]", ASNLEN_INETADDR)); + ND_TCHECK2(*p, asnlen); + for (i = asnlen; i-- != 0; p++) { + ND_PRINT((ndo, (i == asnlen-1) ? "%u" : ".%u", *p)); } - putchar(']'); break; - } + + case BE_NOSUCHOBJECT: + case BE_NOSUCHINST: + case BE_ENDOFMIBVIEW: + ND_PRINT((ndo, "[%s]", Class[EXCEPTIONS].Id[elem->id])); + break; case BE_PDU: - printf("%s(%u)", - Class[CONTEXT].Id[elem->id], elem->asnlen); + ND_PRINT((ndo, "%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen)); break; case BE_ANY: - fputs("[BE_ANY!?]", stdout); + ND_PRINT((ndo, "[BE_ANY!?]")); break; default: - fputs("[be!?]", stdout); + ND_PRINT((ndo, "[be!?]")); break; } + return 0; + +trunc: + ND_PRINT((ndo, "%s", tstr)); + return -1; } #ifdef notdef @@ -662,14 +851,15 @@ asn1_decode(u_char *p, u_int length) int i = 0; while (i >= 0 && length > 0) { - i = asn1_parse(p, length, &elem); + i = asn1_parse(ndo, p, length, &elem); if (i >= 0) { - fputs(" ", stdout); - asn1_print(&elem); + ND_PRINT((ndo, " ")); + if (asn1_print(ndo, &elem) < 0) + return; if (elem.type == BE_SEQ || elem.type == BE_PDU) { - fputs(" {", stdout); + ND_PRINT((ndo, " {")); asn1_decode(elem.data.raw, elem.asnlen); - fputs(" }", stdout); + ND_PRINT((ndo, " }")); } length -= i; p += i; @@ -678,6 +868,297 @@ asn1_decode(u_char *p, u_int length) } #endif +#ifdef USE_LIBSMI + +struct smi2be { + SmiBasetype basetype; + int be; +}; + +static const 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 int +smi_decode_oid(netdissect_options *ndo, + struct be *elem, unsigned int *oid, + unsigned int oidsize, unsigned int *oidlen) +{ + const u_char *p = (const u_char *)elem->data.raw; + uint32_t asnlen = elem->asnlen; + int o = 0, first = -1, i = asnlen; + unsigned int firstval; + + for (*oidlen = 0; ndo->ndo_mflag && i-- > 0; p++) { + ND_TCHECK(*p); + o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8); + if (*p & ASN_LONGLEN) + continue; + + /* + * first subitem encodes two items with 1st*OIDMUX+2nd + * (see X.690:1997 clause 8.19 for the details) + */ + if (first < 0) { + first = 0; + firstval = o / OIDMUX; + if (firstval > 2) firstval = 2; + o -= firstval * OIDMUX; + if (*oidlen < oidsize) { + oid[(*oidlen)++] = firstval; + } + } + if (*oidlen < oidsize) { + oid[(*oidlen)++] = o; + } + o = 0; + } + return 0; + +trunc: + ND_PRINT((ndo, "%s", tstr)); + return -1; +} + +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; + + default: + ok = 0; + 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(netdissect_options *ndo, + struct be *elem, int *status) +{ + unsigned int oid[128], oidlen; + SmiNode *smiNode = NULL; + unsigned int i; + + *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int), + &oidlen); + if (*status < 0) + return NULL; + smiNode = smiGetNodeByOID(oidlen, oid); + if (! smiNode) { + *status = asn1_print(ndo, elem); + return NULL; + } + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name)); + } + ND_PRINT((ndo, "%s", smiNode->name)); + if (smiNode->oidlen < oidlen) { + for (i = smiNode->oidlen; i < oidlen; i++) { + ND_PRINT((ndo, ".%u", oid[i])); + } + } + *status = 0; + return smiNode; +} + +static int +smi_print_value(netdissect_options *ndo, + SmiNode *smiNode, u_short pduid, struct be *elem) +{ + unsigned int i, oid[128], oidlen; + SmiType *smiType; + SmiNamedNumber *nn; + int done = 0; + + if (! smiNode || ! (smiNode->nodekind + & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) { + return asn1_print(ndo, elem); + } + + if (elem->type == BE_NOSUCHOBJECT + || elem->type == BE_NOSUCHINST + || elem->type == BE_ENDOFMIBVIEW) { + return asn1_print(ndo, elem); + } + + if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) { + ND_PRINT((ndo, "[notNotifyable]")); + } + + if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) { + ND_PRINT((ndo, "[notReadable]")); + } + + if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) { + ND_PRINT((ndo, "[notWritable]")); + } + + if (RESPONSE_CLASS(pduid) + && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) { + ND_PRINT((ndo, "[noAccess]")); + } + + smiType = smiGetNodeType(smiNode); + if (! smiType) { + return asn1_print(ndo, elem); + } + + if (! smi_check_type(smiType->basetype, elem->type)) { + ND_PRINT((ndo, "[wrongType]")); + } + + if (! smi_check_range(smiType, elem)) { + ND_PRINT((ndo, "[outOfRange]")); + } + + /* 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(ndo, elem, oid, + sizeof(oid)/sizeof(unsigned int), + &oidlen); + smiNode = smiGetNodeByOID(oidlen, oid); + if (smiNode) { + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "%s::", smiGetNodeModule(smiNode)->name)); + } + ND_PRINT((ndo, "%s", smiNode->name)); + if (smiNode->oidlen < oidlen) { + for (i = smiNode->oidlen; + i < oidlen; i++) { + ND_PRINT((ndo, ".%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) { + ND_PRINT((ndo, "%s", nn->name)); + ND_PRINT((ndo, "(%d)", elem->data.integer)); + done++; + break; + } + } + } + break; + } + + if (! done) { + return asn1_print(ndo, elem); + } + return 0; +} +#endif + /* * General SNMP header * SEQUENCE { @@ -715,151 +1196,173 @@ 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(netdissect_options *ndo, + u_short pduid, const u_char *np, u_int length) { struct be elem; int count = 0, ind; +#ifdef USE_LIBSMI + SmiNode *smiNode = NULL; +#endif + int status; /* Sequence of varBind */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_SEQ) { - fputs("[!SEQ of varbind]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[!SEQ of varbind]")); + asn1_print(ndo, &elem); return; } - if (count < length) - printf("[%d extra after SEQ of varbind]", length - count); + if ((u_int)count < length) + ND_PRINT((ndo, "[%d extra after SEQ of varbind]", length - count)); /* descend */ length = elem.asnlen; - np = (u_char *)elem.data.raw; + np = (const u_char *)elem.data.raw; for (ind = 1; length > 0; ind++) { const u_char *vbend; u_int vblength; - if (!error || ind == error) - fputs(" ", stdout); + ND_PRINT((ndo, " ")); /* Sequence */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_SEQ) { - fputs("[!varbind]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[!varbind]")); + asn1_print(ndo, &elem); return; } vbend = np + count; vblength = length - count; /* descend */ length = elem.asnlen; - np = (u_char *)elem.data.raw; + np = (const u_char *)elem.data.raw; /* objName (OID) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_OID) { - fputs("[objName!=OID]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[objName!=OID]")); + asn1_print(ndo, &elem); return; } - if (!error || ind == error) - asn1_print(&elem); +#ifdef USE_LIBSMI + smiNode = smi_print_variable(ndo, &elem, &status); +#else + status = asn1_print(ndo, &elem); +#endif + if (status < 0) + return; length -= count; np += count; - if (pduid != GETREQ && pduid != GETNEXTREQ && !error) - fputs("=", stdout); + if (pduid != GETREQ && pduid != GETNEXTREQ + && pduid != GETBULKREQ) + ND_PRINT((ndo, "=")); /* objVal (ANY) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; - if (pduid == GETREQ || pduid == GETNEXTREQ) { + if (pduid == GETREQ || pduid == GETNEXTREQ + || pduid == GETBULKREQ) { if (elem.type != BE_NULL) { - fputs("[objVal!=NULL]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[objVal!=NULL]")); + if (asn1_print(ndo, &elem) < 0) + return; } - } else - if (error && ind == error && elem.type != BE_NULL) - fputs("[err objVal!=NULL]", stdout); - if (!error || ind == error) - asn1_print(&elem); - + } else { + if (elem.type != BE_NULL) { +#ifdef USE_LIBSMI + status = smi_print_value(ndo, smiNode, pduid, &elem); +#else + status = asn1_print(ndo, &elem); +#endif + } + if (status < 0) + return; + } length = vblength; np = vbend; } } /* - * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest + * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest, + * GetBulk, Inform, V2Trap, and Report */ static void -snmppdu_print(u_char pduid, const u_char *np, u_int length) +snmppdu_print(netdissect_options *ndo, + u_short pduid, const u_char *np, u_int length) { struct be elem; - int count = 0, error; + int count = 0, error_status; /* reqId (Integer) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INT) { - fputs("[reqId!=INT]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[reqId!=INT]")); + asn1_print(ndo, &elem); return; } - /* ignore the reqId */ + if (ndo->ndo_vflag) + ND_PRINT((ndo, "R=%d ", elem.data.integer)); length -= count; np += count; /* errorStatus (Integer) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INT) { - fputs("[errorStatus!=INT]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[errorStatus!=INT]")); + asn1_print(ndo, &elem); return; } - error = 0; - if ((pduid == GETREQ || pduid == GETNEXTREQ) + error_status = 0; + if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ + || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT) && elem.data.integer != 0) { - char errbuf[10]; - printf("[errorStatus(%s)!=0]", - DECODE_ErrorStatus(elem.data.integer)); + char errbuf[20]; + ND_PRINT((ndo, "[errorStatus(%s)!=0]", + DECODE_ErrorStatus(elem.data.integer))); + } else if (pduid == GETBULKREQ) { + ND_PRINT((ndo, " N=%d", elem.data.integer)); } else if (elem.data.integer != 0) { - char errbuf[10]; - printf(" %s", DECODE_ErrorStatus(elem.data.integer)); - error = elem.data.integer; + char errbuf[20]; + ND_PRINT((ndo, " %s", DECODE_ErrorStatus(elem.data.integer))); + error_status = elem.data.integer; } length -= count; np += count; /* errorIndex (Integer) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INT) { - fputs("[errorIndex!=INT]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[errorIndex!=INT]")); + asn1_print(ndo, &elem); return; } - if ((pduid == GETREQ || pduid == GETNEXTREQ) + if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ + || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT) && elem.data.integer != 0) - printf("[errorIndex(%d)!=0]", elem.data.integer); + ND_PRINT((ndo, "[errorIndex(%d)!=0]", elem.data.integer)); + else if (pduid == GETBULKREQ) + ND_PRINT((ndo, " M=%d", elem.data.integer)); else if (elem.data.integer != 0) { - if (!error) - printf("[errorIndex(%d) w/o errorStatus]", - elem.data.integer); - else { - printf("@%d", elem.data.integer); - error = elem.data.integer; - } - } else if (error) { - fputs("[errorIndex==0]", stdout); - error = 0; + if (!error_status) + ND_PRINT((ndo, "[errorIndex(%d) w/o errorStatus]", elem.data.integer)); + else + ND_PRINT((ndo, "@%d", elem.data.integer)); + } else if (error_status) { + ND_PRINT((ndo, "[errorIndex==0]")); } length -= count; np += count; - varbind_print(pduid, np, length, error); + varbind_print(ndo, pduid, np, length); return; } @@ -867,177 +1370,538 @@ 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(netdissect_options *ndo, + const u_char *np, u_int length) { struct be elem; int count = 0, generic; - putchar(' '); + ND_PRINT((ndo, " ")); /* enterprise (oid) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_OID) { - fputs("[enterprise!=OID]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[enterprise!=OID]")); + asn1_print(ndo, &elem); return; } - asn1_print(&elem); + if (asn1_print(ndo, &elem) < 0) + return; length -= count; np += count; - putchar(' '); + ND_PRINT((ndo, " ")); /* agent-addr (inetaddr) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INETADDR) { - fputs("[agent-addr!=INETADDR]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[agent-addr!=INETADDR]")); + asn1_print(ndo, &elem); return; } - asn1_print(&elem); + if (asn1_print(ndo, &elem) < 0) + return; length -= count; np += count; /* generic-trap (Integer) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INT) { - fputs("[generic-trap!=INT]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[generic-trap!=INT]")); + asn1_print(ndo, &elem); return; } generic = elem.data.integer; { - char buf[10]; - printf(" %s", DECODE_GenericTrap(generic)); + char buf[20]; + ND_PRINT((ndo, " %s", DECODE_GenericTrap(generic))); } length -= count; np += count; /* specific-trap (Integer) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_INT) { - fputs("[specific-trap!=INT]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[specific-trap!=INT]")); + asn1_print(ndo, &elem); return; } if (generic != GT_ENTERPRISE) { if (elem.data.integer != 0) - printf("[specific-trap(%d)!=0]", elem.data.integer); + ND_PRINT((ndo, "[specific-trap(%d)!=0]", elem.data.integer)); } else - printf(" s=%d", elem.data.integer); + ND_PRINT((ndo, " s=%d", elem.data.integer)); length -= count; np += count; - putchar(' '); + ND_PRINT((ndo, " ")); /* time-stamp (TimeTicks) */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_UNS) { /* XXX */ - fputs("[time-stamp!=TIMETICKS]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[time-stamp!=TIMETICKS]")); + asn1_print(ndo, &elem); return; } - asn1_print(&elem); + if (asn1_print(ndo, &elem) < 0) + return; length -= count; np += count; - varbind_print (TRAP, np, length, 0); + varbind_print(ndo, 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(netdissect_options *ndo, + const u_char *np, u_int length, int version) { - struct be elem, pdu; + struct be pdu; int count = 0; - truncated = 0; + /* PDU (Context) */ + if ((count = asn1_parse(ndo, np, length, &pdu)) < 0) + return; + if (pdu.type != BE_PDU) { + ND_PRINT((ndo, "[no PDU]")); + return; + } + if ((u_int)count < length) + ND_PRINT((ndo, "[%d extra after PDU]", length - count)); + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "{ ")); + } + if (asn1_print(ndo, &pdu) < 0) + return; + ND_PRINT((ndo, " ")); + /* descend into PDU */ + length = pdu.asnlen; + np = (const u_char *)pdu.data.raw; + + if (version == SNMP_VERSION_1 && + (pdu.id == GETBULKREQ || pdu.id == INFORMREQ || + pdu.id == V2TRAP || pdu.id == REPORT)) { + ND_PRINT((ndo, "[v2 PDU in v1 message]")); + return; + } - /* truncated packet? */ - if (np + length > snapend) { - truncated = 1; - length = snapend - np; + if (version == SNMP_VERSION_2 && pdu.id == TRAP) { + ND_PRINT((ndo, "[v1 PDU in v2 message]")); + return; } - putchar(' '); + switch (pdu.id) { + case TRAP: + trappdu_print(ndo, np, length); + break; + case GETREQ: + case GETNEXTREQ: + case GETRESP: + case SETREQ: + case GETBULKREQ: + case INFORMREQ: + case V2TRAP: + case REPORT: + snmppdu_print(ndo, pdu.id, np, length); + break; + } - /* initial Sequence */ - if ((count = asn1_parse(np, length, &elem)) < 0) + if (ndo->ndo_vflag) { + ND_PRINT((ndo, " } ")); + } +} + +/* + * Decode a scoped SNMP PDU. + */ +static void +scopedpdu_print(netdissect_options *ndo, + const u_char *np, u_int length, int version) +{ + struct be elem; + int i, count = 0; + + /* Sequence */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_SEQ) { - fputs("[!init SEQ]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[!scoped PDU]")); + asn1_print(ndo, &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) + np = (const u_char *)elem.data.raw; + + /* contextEngineID (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; - if (elem.type != BE_INT) { - fputs("[version!=INT]", stdout); - asn1_print(&elem); + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[contextEngineID!=STR]")); + asn1_print(ndo, &elem); return; } - /* only handle version==0 */ - if (elem.data.integer != DEF_VERSION) { - printf("[version(%d)!=0]", elem.data.integer); + length -= count; + np += count; + + ND_PRINT((ndo, "E= ")); + for (i = 0; i < (int)elem.asnlen; i++) { + ND_PRINT((ndo, "0x%02X", elem.data.str[i])); + } + ND_PRINT((ndo, " ")); + + /* contextName (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[contextName!=STR]")); + asn1_print(ndo, &elem); return; } length -= count; np += count; + ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str)); + + pdu_print(ndo, np, length, version); +} + +/* + * Decode SNMP Community Header (SNMPv1 and SNMPv2c) + */ +static void +community_print(netdissect_options *ndo, + 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) + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; if (elem.type != BE_STR) { - fputs("[comm!=STR]", stdout); - asn1_print(&elem); + ND_PRINT((ndo, "[comm!=STR]")); + asn1_print(ndo, &elem); return; } /* default community */ - if (strncmp((char *)elem.data.str, DEF_COMMUNITY, - sizeof(DEF_COMMUNITY) - 1)) + if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 && + strncmp((const char *)elem.data.str, DEF_COMMUNITY, + sizeof(DEF_COMMUNITY) - 1) == 0)) /* ! "public" */ - printf("C=%.*s ", (int)elem.asnlen, elem.data.str); + ND_PRINT((ndo, "C=%.*s ", (int)elem.asnlen, elem.data.str)); length -= count; np += count; - /* PDU (Context) */ - if ((count = asn1_parse(np, length, &pdu)) < 0) + pdu_print(ndo, np, length, version); +} + +/* + * Decode SNMPv3 User-based Security Message Header (SNMPv3) + */ +static void +usm_print(netdissect_options *ndo, + const u_char *np, u_int length) +{ + struct be elem; + int count = 0; + + /* Sequence */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) return; - if (pdu.type != BE_PDU) { - fputs("[no PDU]", stdout); + if (elem.type != BE_SEQ) { + ND_PRINT((ndo, "[!usm]")); + asn1_print(ndo, &elem); return; } - 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; + length = elem.asnlen; + np = (const u_char *)elem.data.raw; - switch (pdu.id) { - case TRAP: - trap_print(np, length); + /* msgAuthoritativeEngineID (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgAuthoritativeEngineID!=STR]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + /* msgAuthoritativeEngineBoots (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[msgAuthoritativeEngineBoots!=INT]")); + asn1_print(ndo, &elem); + return; + } + if (ndo->ndo_vflag) + ND_PRINT((ndo, "B=%d ", elem.data.integer)); + length -= count; + np += count; + + /* msgAuthoritativeEngineTime (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[msgAuthoritativeEngineTime!=INT]")); + asn1_print(ndo, &elem); + return; + } + if (ndo->ndo_vflag) + ND_PRINT((ndo, "T=%d ", elem.data.integer)); + length -= count; + np += count; + + /* msgUserName (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgUserName!=STR]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + ND_PRINT((ndo, "U=%.*s ", (int)elem.asnlen, elem.data.str)); + + /* msgAuthenticationParameters (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgAuthenticationParameters!=STR]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + /* msgPrivacyParameters (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgPrivacyParameters!=STR]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + if ((u_int)count < length) + ND_PRINT((ndo, "[%d extra after usm SEQ]", length - count)); +} + +/* + * Decode SNMPv3 Message Header (SNMPv3) + */ +static void +v3msg_print(netdissect_options *ndo, + 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(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_SEQ) { + ND_PRINT((ndo, "[!message]")); + asn1_print(ndo, &elem); + return; + } + length = elem.asnlen; + np = (const u_char *)elem.data.raw; + + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "{ ")); + } + + /* msgID (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[msgID!=INT]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + /* msgMaxSize (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[msgMaxSize!=INT]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + /* msgFlags (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgFlags!=STR]")); + asn1_print(ndo, &elem); + return; + } + if (elem.asnlen != 1) { + ND_PRINT((ndo, "[msgFlags size %d]", elem.asnlen)); + return; + } + flags = elem.data.str[0]; + if (flags != 0x00 && flags != 0x01 && flags != 0x03 + && flags != 0x04 && flags != 0x05 && flags != 0x07) { + ND_PRINT((ndo, "[msgFlags=0x%02X]", flags)); + return; + } + length -= count; + np += count; + + ND_PRINT((ndo, "F=%s%s%s ", + flags & 0x01 ? "a" : "", + flags & 0x02 ? "p" : "", + flags & 0x04 ? "r" : "")); + + /* msgSecurityModel (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[msgSecurityModel!=INT]")); + asn1_print(ndo, &elem); + return; + } + model = elem.data.integer; + length -= count; + np += count; + + if ((u_int)count < length) + ND_PRINT((ndo, "[%d extra after message SEQ]", length - count)); + + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "} ")); + } + + if (model == 3) { + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "{ USM ")); + } + } else { + ND_PRINT((ndo, "[security model %d]", model)); + return; + } + + np = xnp + (np - xnp); + length = xlength - (np - xnp); + + /* msgSecurityParameters (OCTET STRING) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_STR) { + ND_PRINT((ndo, "[msgSecurityParameters!=STR]")); + asn1_print(ndo, &elem); + return; + } + length -= count; + np += count; + + if (model == 3) { + usm_print(ndo, elem.data.str, elem.asnlen); + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "} ")); + } + } + + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "{ ScopedPDU ")); + } + + scopedpdu_print(ndo, np, length, 3); + + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "} ")); + } +} + +/* + * Decode SNMP header and pass on to PDU printing routines + */ +void +snmp_print(netdissect_options *ndo, + const u_char *np, u_int length) +{ + struct be elem; + int count = 0; + int version = 0; + + ND_PRINT((ndo, " ")); + + /* initial Sequence */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_SEQ) { + ND_PRINT((ndo, "[!init SEQ]")); + asn1_print(ndo, &elem); + return; + } + if ((u_int)count < length) + ND_PRINT((ndo, "[%d extra after iSEQ]", length - count)); + /* descend */ + length = elem.asnlen; + np = (const u_char *)elem.data.raw; + + /* Version (INTEGER) */ + if ((count = asn1_parse(ndo, np, length, &elem)) < 0) + return; + if (elem.type != BE_INT) { + ND_PRINT((ndo, "[version!=INT]")); + asn1_print(ndo, &elem); + return; + } + + switch (elem.data.integer) { + case SNMP_VERSION_1: + case SNMP_VERSION_2: + case SNMP_VERSION_3: + if (ndo->ndo_vflag) + ND_PRINT((ndo, "{ %s ", SnmpVersion[elem.data.integer])); break; - case GETREQ: - case GETNEXTREQ: - case GETRESP: - case SETREQ: - snmppdu_print(pdu.id, np, length); + default: + ND_PRINT((ndo, "[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(ndo, np, length, version); + break; + case SNMP_VERSION_3: + v3msg_print(ndo, np, length); + break; + default: + ND_PRINT((ndo, "[version = %d]", elem.data.integer)); break; } - return; + + if (ndo->ndo_vflag) { + ND_PRINT((ndo, "} ")); + } }