diff options
Diffstat (limited to 'epan/dissectors/packet-snmp.c')
-rw-r--r-- | epan/dissectors/packet-snmp.c | 2569 |
1 files changed, 2569 insertions, 0 deletions
diff --git a/epan/dissectors/packet-snmp.c b/epan/dissectors/packet-snmp.c new file mode 100644 index 0000000000..fc6d191df8 --- /dev/null +++ b/epan/dissectors/packet-snmp.c @@ -0,0 +1,2569 @@ +/* packet-snmp.c + * Routines for SNMP (simple network management protocol) + * Copyright (C) 1998 Didier Jorand + * + * See RFC 1157 for SNMPv1. + * + * See RFCs 1901, 1905, and 1906 for SNMPv2c. + * + * See RFCs 1905, 1906, 1909, and 1910 for SNMPv2u [historic]. + * + * See RFCs 2570-2576 for SNMPv3 + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Some stuff from: + * + * GXSNMP -- An snmp mangament application + * Copyright (C) 1998 Gregory McLean & Jochen Friedrich + * Beholder RMON ethernet network monitor,Copyright (C) 1993 DNPAP group + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include <glib.h> + +#include "isprint.h" + +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/conversation.h> +#include "etypes.h" +#include "prefs.h" +#include "packet-ipx.h" +#include "packet-hpext.h" +#include "packet-frame.h" + +#ifdef HAVE_SOME_SNMP + +#ifdef HAVE_NET_SNMP +# include <net-snmp/net-snmp-config.h> +# include <net-snmp/mib_api.h> +# include <net-snmp/library/default_store.h> +# include <net-snmp/config_api.h> +#else /* HAVE_NET_SNMP */ +# include <ucd-snmp/ucd-snmp-config.h> +# include <ucd-snmp/asn1.h> +# include <ucd-snmp/snmp_api.h> +# include <ucd-snmp/snmp_impl.h> +# include <ucd-snmp/mib.h> +# include <ucd-snmp/default_store.h> +# include <ucd-snmp/read_config.h> +# include <ucd-snmp/tools.h> +#endif /* HAVE_NET_SNMP */ + +#ifndef NETSNMP_DS_LIBRARY_ID +# define NETSNMP_DS_LIBRARY_ID DS_LIBRARY_ID +# define NETSNMP_DS_LIB_NO_TOKEN_WARNINGS DS_LIB_NO_TOKEN_WARNINGS +# define NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY DS_LIB_PRINT_SUFFIX_ONLY +# define netsnmp_ds_set_boolean ds_set_boolean +# define netsnmp_ds_set_int ds_set_int +#endif + +#ifdef WIN32 +# include <epan/filesystem.h> +#endif /* WIN32 */ + + /* + * Define values "sprint_realloc_value()" expects. + */ +# define VALTYPE_INTEGER ASN_INTEGER +# define VALTYPE_COUNTER ASN_COUNTER +# define VALTYPE_GAUGE ASN_GAUGE +# define VALTYPE_TIMETICKS ASN_TIMETICKS +# define VALTYPE_STRING ASN_OCTET_STR +# define VALTYPE_IPADDR ASN_IPADDRESS +# define VALTYPE_OPAQUE ASN_OPAQUE +# define VALTYPE_NSAP ASN_NSAP +# define VALTYPE_OBJECTID ASN_OBJECT_ID +# define VALTYPE_BITSTR ASN_BIT_STR +# define VALTYPE_COUNTER64 ASN_COUNTER64 + +#endif /* HAVE_SOME_SNMP */ + +#include "asn1.h" + +#include "packet-snmp.h" +#include "format-oid.h" + +/* Take a pointer that may be null and return a pointer that's not null + by turning null pointers into pointers to the above null string, + and, if the argument pointer wasn't null, make sure we handle + non-printable characters in the string by escaping them. */ +#define SAFE_STRING(s, l) (((s) != NULL) ? format_text((s), (l)) : "") + +static int proto_snmp = -1; + +/* Default MIB modules to load */ +#define DEF_MIB_MODULES "IP-MIB:IF-MIB:TCP-MIB:UDP-MIB:SNMPv2-MIB:RFC1213-MIB:UCD-SNMP-MIB" + +static gchar *mib_modules = DEF_MIB_MODULES; +static gboolean display_oid = TRUE; + +static gint ett_snmp = -1; +static gint ett_parameters = -1; +static gint ett_parameters_qos = -1; +static gint ett_global = -1; +static gint ett_flags = -1; +static gint ett_secur = -1; + +static int hf_snmp_version = -1; +static int hf_snmp_community = -1; +static int hf_snmp_request_id = -1; +static int hf_snmp_pdutype = -1; +static int hf_snmp_agent = -1; +static int hf_snmp_enterprise = -1; +static int hf_snmp_error_status = -1; +static int hf_snmp_oid = -1; +static int hf_snmp_traptype = -1; +static int hf_snmp_spectraptype = -1; +static int hf_snmp_timestamp = -1; +static int hf_snmpv3_flags = -1; +static int hf_snmpv3_flags_auth = -1; +static int hf_snmpv3_flags_crypt = -1; +static int hf_snmpv3_flags_report = -1; + +static int proto_smux = -1; + +static gint ett_smux = -1; + +static int hf_smux_version = -1; +static int hf_smux_pdutype = -1; + +/* desegmentation of SNMP-over-TCP */ +static gboolean snmp_desegment = TRUE; + +static dissector_handle_t snmp_handle; +static dissector_handle_t data_handle; + +#define TH_AUTH 0x01 +#define TH_CRYPT 0x02 +#define TH_REPORT 0x04 + +#define UDP_PORT_SNMP 161 +#define UDP_PORT_SNMP_TRAP 162 +#define TCP_PORT_SNMP 161 +#define TCP_PORT_SNMP_TRAP 162 +#define TCP_PORT_SMUX 199 + +/* Protocol version numbers */ +#define SNMP_VERSION_1 0 +#define SNMP_VERSION_2c 1 +#define SNMP_VERSION_2u 2 +#define SNMP_VERSION_3 3 + +static const value_string versions[] = { + { SNMP_VERSION_1, "1" }, + { SNMP_VERSION_2c, "2C" }, + { SNMP_VERSION_2u, "2U" }, + { SNMP_VERSION_3, "3" }, + { 0, NULL }, +}; + +/* defined in net-SNMP; include/net-snmp/library/snmp.h */ +#undef SNMP_MSG_GET +#undef SNMP_MSG_SET +#undef SNMP_MSG_GETNEXT +#undef SNMP_MSG_RESPONSE +#undef SNMP_MSG_TRAP +#undef SNMP_MSG_GETBULK +#undef SNMP_MSG_INFORM +#undef SNMP_MSG_TRAP2 +#undef SNMP_MSG_REPORT +#undef SNMP_NOSUCHOBJECT +#undef SNMP_NOSUCHINSTANCE +#undef SNMP_ENDOFMIBVIEW + +/* PDU types */ +#define SNMP_MSG_GET 0 +#define SNMP_MSG_GETNEXT 1 +#define SNMP_MSG_RESPONSE 2 +#define SNMP_MSG_SET 3 +#define SNMP_MSG_TRAP 4 + +#define SNMP_MSG_GETBULK 5 +#define SNMP_MSG_INFORM 6 +#define SNMP_MSG_TRAP2 7 +#define SNMP_MSG_REPORT 8 + +static const value_string pdu_types[] = { + { SNMP_MSG_GET, "GET" }, + { SNMP_MSG_GETNEXT, "GET-NEXT" }, + { SNMP_MSG_SET, "SET" }, + { SNMP_MSG_RESPONSE, "RESPONSE" }, + { SNMP_MSG_TRAP, "TRAP-V1" }, + { SNMP_MSG_GETBULK, "GETBULK" }, + { SNMP_MSG_INFORM, "INFORM" }, + { SNMP_MSG_TRAP2, "TRAP-V2" }, + { SNMP_MSG_REPORT, "REPORT" }, + { 0, NULL } +}; + +/* SMUX PDU types */ +#define SMUX_MSG_OPEN 0 +#define SMUX_MSG_CLOSE 1 +#define SMUX_MSG_RREQ 2 +#define SMUX_MSG_RRSP 3 +#define SMUX_MSG_SOUT 4 + +static const value_string smux_types[] = { + { SMUX_MSG_OPEN, "Open" }, + { SMUX_MSG_CLOSE, "Close" }, + { SMUX_MSG_RREQ, "Registration Request" }, + { SMUX_MSG_RRSP, "Registration Response" }, + { SMUX_MSG_SOUT, "Commit Or Rollback" }, + { 0, NULL } +}; + +/* SMUX Closing causes */ +#define SMUX_CLOSE_DOWN 0 +#define SMUX_CLOSE_VERSION 1 +#define SMUX_CLOSE_PACKET 2 +#define SMUX_CLOSE_PROTOCOL 3 +#define SMUX_CLOSE_INTERNAL 4 +#define SMUX_CLOSE_NOAUTH 5 + +static const value_string smux_close[] = { + { SMUX_CLOSE_DOWN, "Going down" }, + { SMUX_CLOSE_VERSION, "Unsupported Version" }, + { SMUX_CLOSE_PACKET, "Packet Format Error" }, + { SMUX_CLOSE_PROTOCOL, "Protocol Error" }, + { SMUX_CLOSE_INTERNAL, "Internal Error" }, + { SMUX_CLOSE_NOAUTH, "Unauthorized" }, + { 0, NULL } +}; + +/* SMUX Request codes */ +#define SMUX_RREQ_DELETE 0 +#define SMUX_RREQ_READONLY 1 +#define SMUX_RREQ_READWRITE 2 + +static const value_string smux_rreq[] = { + { SMUX_RREQ_DELETE, "Delete" }, + { SMUX_RREQ_READONLY, "Read Only" }, + { SMUX_RREQ_READWRITE, "Read Write" }, + { 0, NULL } +}; + +static const value_string smux_prio[] = { + { -1, "Failure" }, + { 0, NULL } +}; + +/* SMUX SOut codes */ +#define SMUX_SOUT_COMMIT 0 +#define SMUX_SOUT_ROLLBACK 1 + +static const value_string smux_sout[] = { + { SMUX_SOUT_COMMIT, "Commit" }, + { SMUX_SOUT_ROLLBACK, "Rollback" }, + { 0, NULL } +}; + +/* Error status values */ +#ifndef SNMP_ERR_NOERROR +#define SNMP_ERR_NOERROR 0 +#endif +#ifndef SNMP_ERR_TOOBIG +#define SNMP_ERR_TOOBIG 1 +#endif +#ifndef SNMP_ERR_NOSUCHNAME +#define SNMP_ERR_NOSUCHNAME 2 +#endif +#ifndef SNMP_ERR_BADVALUE +#define SNMP_ERR_BADVALUE 3 +#endif +#ifndef SNMP_ERR_READONLY +#define SNMP_ERR_READONLY 4 +#endif +#ifndef SNMP_ERR_GENERR +#define SNMP_ERR_GENERR 5 +#endif +#ifndef SNMP_ERR_NOACCESS +#define SNMP_ERR_NOACCESS 6 +#endif +#ifndef SNMP_ERR_WRONGTYPE +#define SNMP_ERR_WRONGTYPE 7 +#endif +#ifndef SNMP_ERR_WRONGLENGTH +#define SNMP_ERR_WRONGLENGTH 8 +#endif +#ifndef SNMP_ERR_WRONGENCODING +#define SNMP_ERR_WRONGENCODING 9 +#endif +#ifndef SNMP_ERR_WRONGVALUE +#define SNMP_ERR_WRONGVALUE 10 +#endif +#ifndef SNMP_ERR_NOCREATION +#define SNMP_ERR_NOCREATION 11 +#endif +#ifndef SNMP_ERR_INCONSISTENTVALUE +#define SNMP_ERR_INCONSISTENTVALUE 12 +#endif +#ifndef SNMP_ERR_RESOURCEUNAVAILABLE +#define SNMP_ERR_RESOURCEUNAVAILABLE 13 +#endif +#ifndef SNMP_ERR_COMMITFAILED +#define SNMP_ERR_COMMITFAILED 14 +#endif +#ifndef SNMP_ERR_UNDOFAILED +#define SNMP_ERR_UNDOFAILED 15 +#endif +#ifndef SNMP_ERR_AUTHORIZATIONERROR +#define SNMP_ERR_AUTHORIZATIONERROR 16 +#endif +#ifndef SNMP_ERR_NOTWRITABLE +#define SNMP_ERR_NOTWRITABLE 17 +#endif +#ifndef SNMP_ERR_INCONSISTENTNAME +#define SNMP_ERR_INCONSISTENTNAME 18 +#endif + +static const value_string error_statuses[] = { + { SNMP_ERR_NOERROR, "NO ERROR" }, + { SNMP_ERR_TOOBIG, "TOOBIG" }, + { SNMP_ERR_NOSUCHNAME, "NO SUCH NAME" }, + { SNMP_ERR_BADVALUE, "BAD VALUE" }, + { SNMP_ERR_READONLY, "READ ONLY" }, + { SNMP_ERR_GENERR, "GENERIC ERROR" }, + { SNMP_ERR_NOACCESS, "NO ACCESS" }, + { SNMP_ERR_WRONGTYPE, "WRONG TYPE" }, + { SNMP_ERR_WRONGLENGTH, "WRONG LENGTH" }, + { SNMP_ERR_WRONGENCODING, "WRONG ENCODING" }, + { SNMP_ERR_WRONGVALUE, "WRONG VALUE" }, + { SNMP_ERR_NOCREATION, "NO CREATION" }, + { SNMP_ERR_INCONSISTENTVALUE, "INCONSISTENT VALUE" }, + { SNMP_ERR_RESOURCEUNAVAILABLE, "RESOURCE UNAVAILABLE" }, + { SNMP_ERR_COMMITFAILED, "COMMIT FAILED" }, + { SNMP_ERR_UNDOFAILED, "UNDO FAILED" }, + { SNMP_ERR_AUTHORIZATIONERROR, "AUTHORIZATION ERROR" }, + { SNMP_ERR_NOTWRITABLE, "NOT WRITABLE" }, + { SNMP_ERR_INCONSISTENTNAME, "INCONSISTENT NAME" }, + { 0, NULL } +}; + +/* General SNMP V1 Traps */ + +#ifndef SNMP_TRAP_COLDSTART +#define SNMP_TRAP_COLDSTART 0 +#endif +#ifndef SNMP_TRAP_WARMSTART +#define SNMP_TRAP_WARMSTART 1 +#endif +#ifndef SNMP_TRAP_LINKDOWN +#define SNMP_TRAP_LINKDOWN 2 +#endif +#ifndef SNMP_TRAP_LINKUP +#define SNMP_TRAP_LINKUP 3 +#endif +#ifndef SNMP_TRAP_AUTHFAIL +#define SNMP_TRAP_AUTHFAIL 4 +#endif +#ifndef SNMP_TRAP_EGPNEIGHBORLOSS +#define SNMP_TRAP_EGPNEIGHBORLOSS 5 +#endif +#ifndef SNMP_TRAP_ENTERPRISESPECIFIC +#define SNMP_TRAP_ENTERPRISESPECIFIC 6 +#endif + +static const value_string trap_types[] = { + { SNMP_TRAP_COLDSTART, "COLD START" }, + { SNMP_TRAP_WARMSTART, "WARM START" }, + { SNMP_TRAP_LINKDOWN, "LINK DOWN" }, + { SNMP_TRAP_LINKUP, "LINK UP" }, + { SNMP_TRAP_AUTHFAIL, "AUTHENTICATION FAILED" }, + { SNMP_TRAP_EGPNEIGHBORLOSS, "EGP NEIGHBORLOSS" }, + { SNMP_TRAP_ENTERPRISESPECIFIC, "ENTERPRISE SPECIFIC" }, + { 0, NULL } +}; + +/* Security Models */ + +#define SNMP_SEC_ANY 0 +#define SNMP_SEC_V1 1 +#define SNMP_SEC_V2C 2 +#define SNMP_SEC_USM 3 + +static const value_string sec_models[] = { + { SNMP_SEC_ANY, "Any" }, + { SNMP_SEC_V1, "V1" }, + { SNMP_SEC_V2C, "V2C" }, + { SNMP_SEC_USM, "USM" }, + { 0, NULL } +}; + +/* SNMP Tags */ + +#define SNMP_IPA 0 /* IP Address */ +#define SNMP_CNT 1 /* Counter (Counter32) */ +#define SNMP_GGE 2 /* Gauge (Gauge32) */ +#define SNMP_TIT 3 /* TimeTicks */ +#define SNMP_OPQ 4 /* Opaque */ +#define SNMP_NSP 5 /* NsapAddress */ +#define SNMP_C64 6 /* Counter64 */ +#define SNMP_U32 7 /* Uinteger32 */ + +#define SERR_NSO 0 +#define SERR_NSI 1 +#define SERR_EOM 2 + +/* SNMPv1 Types */ + +#define SNMP_NULL 0 +#define SNMP_INTEGER 1 /* l */ +#define SNMP_OCTETSTR 2 /* c */ +#define SNMP_DISPLAYSTR 2 /* c */ +#define SNMP_OBJECTID 3 /* ul */ +#define SNMP_IPADDR 4 /* uc */ +#define SNMP_COUNTER 5 /* ul */ +#define SNMP_GAUGE 6 /* ul */ +#define SNMP_TIMETICKS 7 /* ul */ +#define SNMP_OPAQUE 8 /* c */ + +/* additional SNMPv2 Types */ + +#define SNMP_UINTEGER 5 /* ul */ +#define SNMP_BITSTR 9 /* uc */ +#define SNMP_NSAP 10 /* uc */ +#define SNMP_COUNTER64 11 /* ul */ +#define SNMP_NOSUCHOBJECT 12 +#define SNMP_NOSUCHINSTANCE 13 +#define SNMP_ENDOFMIBVIEW 14 + +typedef struct _SNMP_CNV SNMP_CNV; + +struct _SNMP_CNV +{ + guint class; + guint tag; + gint syntax; + gchar *name; +}; + +static SNMP_CNV SnmpCnv [] = +{ + {ASN1_UNI, ASN1_NUL, SNMP_NULL, "NULL"}, + {ASN1_UNI, ASN1_INT, SNMP_INTEGER, "INTEGER"}, + {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR, "OCTET STRING"}, + {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID, "OBJECTID"}, + {ASN1_APL, SNMP_IPA, SNMP_IPADDR, "IPADDR"}, + {ASN1_APL, SNMP_CNT, SNMP_COUNTER, "COUNTER"}, /* Counter32 */ + {ASN1_APL, SNMP_GGE, SNMP_GAUGE, "GAUGE"}, /* Gauge32 == Unsigned32 */ + {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS, "TIMETICKS"}, + {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE, "OPAQUE"}, + +/* SNMPv2 data types and errors */ + + {ASN1_UNI, ASN1_BTS, SNMP_BITSTR, "BITSTR"}, + {ASN1_APL, SNMP_C64, SNMP_COUNTER64, "COUNTER64"}, + {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT, "NOSUCHOBJECT"}, + {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE, "NOSUCHINSTANCE"}, + {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW, "ENDOFMIBVIEW"}, + {0, 0, -1, NULL} +}; + +/* + * NAME: g_snmp_tag_cls2syntax + * SYNOPSIS: gboolean g_snmp_tag_cls2syntax + * ( + * guint tag, + * guint cls, + * gushort *syntax + * ) + * DESCRIPTION: Converts ASN1 tag and class to Syntax tag and name. + * See SnmpCnv for conversion. + * RETURNS: name on success, NULL on failure + */ + +static gchar * +snmp_tag_cls2syntax ( guint tag, guint cls, gushort *syntax) +{ + SNMP_CNV *cnv; + + cnv = SnmpCnv; + while (cnv->syntax != -1) + { + if (cnv->tag == tag && cnv->class == cls) + { + *syntax = cnv->syntax; + return cnv->name; + } + cnv++; + } + return NULL; +} + +static void +dissect_snmp_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, const char *field_name, int ret) +{ + char *errstr; + + errstr = asn1_err_to_str(ret); + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, + "ERROR: Couldn't parse %s: %s", field_name, errstr); + } + if (tree != NULL) { + proto_tree_add_text(tree, tvb, offset, 0, + "ERROR: Couldn't parse %s: %s", field_name, errstr); + call_dissector(data_handle, + tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); + } +} + +static void +dissect_snmp_error(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, const char *message) +{ + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, message); + + if (tree != NULL) { + proto_tree_add_text(tree, tvb, offset, 0, "%s", message); + call_dissector(data_handle, + tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); + } +} + +gchar * +format_oid(subid_t *oid, guint oid_length) +{ + char *result; + int result_len; + int len; + unsigned int i; + char *buf; +#ifdef HAVE_SOME_SNMP + guchar *oid_string; + size_t oid_string_len; + size_t oid_out_len; +#endif + + result_len = oid_length * 22; + +#ifdef HAVE_SOME_SNMP + /* + * Get the decoded form of the OID, and add its length to the + * length of the result string. + * + * XXX - check for "sprint_realloc_objid()" failure. + */ + oid_string_len = 256; + oid_string = g_malloc(oid_string_len); + *oid_string = '\0'; + oid_out_len = 0; + sprint_realloc_objid(&oid_string, &oid_string_len, &oid_out_len, 1, + oid, oid_length); + result_len += strlen(oid_string) + 3; +#endif + + result = g_malloc(result_len + 1); + buf = result; + len = sprintf(buf, "%lu", (unsigned long)oid[0]); + buf += len; + for (i = 1; i < oid_length;i++) { + len = sprintf(buf, ".%lu", (unsigned long)oid[i]); + buf += len; + } + +#ifdef HAVE_SOME_SNMP + /* + * Append the decoded form of the OID. + */ + sprintf(buf, " (%s)", oid_string); + g_free(oid_string); +#endif + + return result; +} + +/* returns the decoded (can be NULL) and non_decoded OID strings, + returned pointers shall be freed by the caller */ +void +new_format_oid(subid_t *oid, guint oid_length, + gchar **non_decoded, gchar **decoded) +{ + int len; + unsigned int i; + char *buf; + +#ifdef HAVE_SOME_SNMP + guchar *oid_string; + size_t oid_string_len; + size_t oid_out_len; + + /* + * Get the decoded form of the OID, and add its length to the + * length of the result string. + */ + + oid_string_len = 256; + oid_string = g_malloc(oid_string_len); + *oid_string = '\0'; + oid_out_len = 0; + sprint_realloc_objid(&oid_string, &oid_string_len, &oid_out_len, 1, + oid, oid_length); + *decoded = oid_string; +#else + *decoded = NULL; +#endif + + *non_decoded = g_malloc(oid_length * 22 + 1); + buf = *non_decoded; + len = sprintf(buf, "%lu", (unsigned long)oid[0]); + buf += len; + for (i = 1; i < oid_length; i++) { + len = sprintf(buf, ".%lu", (unsigned long)oid[i]); + buf += len; + } +} + +#ifdef HAVE_SOME_SNMP +static guchar * +check_var_length(guint vb_length, guint required_length) +{ + gchar *buf; + static const char badlen_fmt[] = "Length is %u, should be %u"; + + if (vb_length != required_length) { + /* Enough room for the largest "Length is XXX, + should be XXX" message - 10 digits for each + XXX. */ + buf = g_malloc(sizeof badlen_fmt + 10 + 10); + sprintf(buf, badlen_fmt, vb_length, required_length); + return buf; + } + return NULL; /* length is OK */ +} + +static gchar * +format_var(struct variable_list *variable, subid_t *variable_oid, + guint variable_oid_length, gushort vb_type, guint val_len) +{ + guchar *buf; + size_t buf_len; + size_t out_len; + + switch (vb_type) { + + case SNMP_IPADDR: + /* Length has to be 4 bytes. */ + buf = check_var_length(val_len, 4); + if (buf != NULL) + return buf; /* it's not 4 bytes */ + break; + +#ifdef REMOVED + /* not all counters are encoded as a full 64bit integer */ + case SNMP_COUNTER64: + /* Length has to be 8 bytes. */ + buf = check_var_length(val_len, 8); + if (buf != NULL) + return buf; /* it's not 8 bytes */ + break; +#endif + default: + break; + } + + variable->next_variable = NULL; + variable->name = variable_oid; + variable->name_length = variable_oid_length; + switch (vb_type) { + + case SNMP_INTEGER: + variable->type = VALTYPE_INTEGER; + break; + + case SNMP_COUNTER: + variable->type = VALTYPE_COUNTER; + break; + + case SNMP_GAUGE: + variable->type = VALTYPE_GAUGE; + break; + + case SNMP_TIMETICKS: + variable->type = VALTYPE_TIMETICKS; + break; + + case SNMP_OCTETSTR: + variable->type = VALTYPE_STRING; + break; + + case SNMP_IPADDR: + variable->type = VALTYPE_IPADDR; + break; + + case SNMP_OPAQUE: + variable->type = VALTYPE_OPAQUE; + break; + + case SNMP_NSAP: + variable->type = VALTYPE_NSAP; + break; + + case SNMP_OBJECTID: + variable->type = VALTYPE_OBJECTID; + break; + + case SNMP_BITSTR: + variable->type = VALTYPE_BITSTR; + break; + + case SNMP_COUNTER64: + variable->type = VALTYPE_COUNTER64; + break; + } + variable->val_len = val_len; + + /* + * XXX - check for "sprint_realloc_objid()" failure. + */ + buf_len = 256; + buf = g_malloc(buf_len); + *buf = '\0'; + out_len = 0; + sprint_realloc_value(&buf, &buf_len, &out_len, 1, variable_oid, + variable_oid_length, variable); + return buf; +} +#endif + +static int +snmp_variable_decode(proto_tree *snmp_tree, + subid_t *variable_oid +#ifndef HAVE_SOME_SNMP + _U_ +#endif + , + guint variable_oid_length +#ifndef HAVE_SOME_SNMP + _U_ +#endif + , + ASN1_SCK *asn1, int offset, guint *lengthp) +{ + int start; + guint length; + gboolean def; + guint vb_length; + gushort vb_type; + gchar *vb_type_name; + int ret; + guint cls, con, tag; + + gint32 vb_integer_value; + guint32 vb_uinteger_value; + + guint8 *vb_octet_string; + + subid_t *vb_oid; + guint vb_oid_length; + + gchar *vb_display_string; + +#ifdef HAVE_SOME_SNMP + struct variable_list variable; + long value; +#else /* HAVE_SOME_SNMP */ + unsigned int i; + gchar *buf; + int len; +#endif /* HAVE_SOME_SNMP */ + + /* parse the type of the object */ + start = asn1->offset; + ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &vb_length); + if (ret != ASN1_ERR_NOERROR) + return ret; + if (!def) + return ASN1_ERR_LENGTH_NOT_DEFINITE; + + /* Convert the class, constructed flag, and tag to a type. */ + vb_type_name = snmp_tag_cls2syntax(tag, cls, &vb_type); + if (vb_type_name == NULL) { + /* + * Unsupported type. + * Dissect the value as an opaque string of octets. + */ + vb_type_name = "unsupported type"; + vb_type = SNMP_OPAQUE; + } + + /* parse the value */ + switch (vb_type) { + + case SNMP_INTEGER: + ret = asn1_int32_value_decode(asn1, vb_length, + &vb_integer_value); + if (ret != ASN1_ERR_NOERROR) + return ret; + length = asn1->offset - start; + if (snmp_tree) { +#ifdef HAVE_SOME_SNMP + value = vb_integer_value; + variable.val.integer = &value; + vb_display_string = format_var(&variable, + variable_oid, variable_oid_length, vb_type, + vb_length); + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s", vb_display_string); + g_free(vb_display_string); +#else /* HAVE_SOME_SNMP */ + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s: %d (%#x)", vb_type_name, + vb_integer_value, vb_integer_value); +#endif /* HAVE_SOME_SNMP */ + } + break; + + case SNMP_COUNTER: + case SNMP_GAUGE: + case SNMP_TIMETICKS: + ret = asn1_uint32_value_decode(asn1, vb_length, + &vb_uinteger_value); + if (ret != ASN1_ERR_NOERROR) + return ret; + length = asn1->offset - start; + if (snmp_tree) { +#ifdef HAVE_SOME_SNMP + value = vb_uinteger_value; + variable.val.integer = &value; + vb_display_string = format_var(&variable, + variable_oid, variable_oid_length, vb_type, + vb_length); + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s", vb_display_string); + g_free(vb_display_string); +#else /* HAVE_SOME_SNMP */ + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s: %u (%#x)", vb_type_name, + vb_uinteger_value, vb_uinteger_value); +#endif /* HAVE_SOME_SNMP */ + } + break; + + case SNMP_OCTETSTR: + case SNMP_IPADDR: + case SNMP_OPAQUE: + case SNMP_NSAP: + case SNMP_BITSTR: + case SNMP_COUNTER64: + ret = asn1_string_value_decode (asn1, vb_length, + &vb_octet_string); + if (ret != ASN1_ERR_NOERROR) + return ret; + length = asn1->offset - start; + if (snmp_tree) { +#ifdef HAVE_SOME_SNMP + variable.val.string = vb_octet_string; + vb_display_string = format_var(&variable, + variable_oid, variable_oid_length, vb_type, + vb_length); + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s", vb_display_string); + g_free(vb_display_string); +#else /* HAVE_SOME_SNMP */ + /* + * If some characters are not printable, display + * the string as bytes. + */ + for (i = 0; i < vb_length; i++) { + if (!(isprint(vb_octet_string[i]) + || isspace(vb_octet_string[i]))) + break; + } + if (i < vb_length) { + /* + * We stopped, due to a non-printable + * character, before we got to the end + * of the string. + */ + vb_display_string = g_malloc(4*vb_length); + buf = &vb_display_string[0]; + len = sprintf(buf, "%03u", vb_octet_string[0]); + buf += len; + for (i = 1; i < vb_length; i++) { + len = sprintf(buf, ".%03u", + vb_octet_string[i]); + buf += len; + } + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s: %s", vb_type_name, + vb_display_string); + g_free(vb_display_string); + } else { + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s: %s", vb_type_name, + SAFE_STRING(vb_octet_string, vb_length)); + } +#endif /* HAVE_SOME_SNMP */ + } + g_free(vb_octet_string); + break; + + case SNMP_NULL: + ret = asn1_null_decode (asn1, vb_length); + if (ret != ASN1_ERR_NOERROR) + return ret; + length = asn1->offset - start; + if (snmp_tree) { + proto_tree_add_text(snmp_tree, asn1->tvb, offset, length, + "Value: %s", vb_type_name); + } + break; + + case SNMP_OBJECTID: + ret = asn1_oid_value_decode (asn1, vb_length, &vb_oid, + &vb_oid_length); + if (ret != ASN1_ERR_NOERROR) + return ret; + length = asn1->offset - start; + if (snmp_tree) { +#ifdef HAVE_SOME_SNMP + variable.val.objid = vb_oid; + vb_display_string = format_var(&variable, + variable_oid, variable_oid_length, vb_type, + vb_oid_length * sizeof (subid_t)); + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s", vb_display_string); + g_free(vb_display_string); +#else /* HAVE_SOME_SNMP */ + vb_display_string = format_oid(vb_oid, vb_oid_length); + proto_tree_add_text(snmp_tree, asn1->tvb, offset, + length, + "Value: %s: %s", vb_type_name, vb_display_string); + g_free(vb_display_string); +#endif /* HAVE_SOME_SNMP */ + } + g_free(vb_oid); + break; + + case SNMP_NOSUCHOBJECT: + length = asn1->offset - start; + if (snmp_tree) { + proto_tree_add_text(snmp_tree, asn1->tvb, offset, length, + "Value: %s: no such object", vb_type_name); + } + break; + + case SNMP_NOSUCHINSTANCE: + length = asn1->offset - start; + if (snmp_tree) { + proto_tree_add_text(snmp_tree, asn1->tvb, offset, length, + "Value: %s: no such instance", vb_type_name); + } + break; + + case SNMP_ENDOFMIBVIEW: + length = asn1->offset - start; + if (snmp_tree) { + proto_tree_add_text(snmp_tree, asn1->tvb, offset, length, + "Value: %s: end of mib view", vb_type_name); + } + break; + + default: + g_assert_not_reached(); + return ASN1_ERR_WRONG_TYPE; + } + *lengthp = length; + return ASN1_ERR_NOERROR; +} + +static void +dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, ASN1_SCK asn1, guint pdu_type, int start) +{ + gboolean def; + guint length; + guint sequence_length; + + guint32 request_id; + + guint32 error_status; + + guint32 error_index; + + char *pdu_type_string; + + subid_t *enterprise; + guint enterprise_length; + + guint32 agent_ipaddr; + + guint8 *agent_address; + guint agent_address_length; + + guint32 trap_type; + + guint32 specific_type; + + guint timestamp; + guint timestamp_length; + + gchar *oid_string; + + guint variable_bindings_length; + + int vb_index; + guint variable_length; + subid_t *variable_oid; + guint variable_oid_length; + + int ret; + guint cls, con, tag; + + pdu_type_string = val_to_str(pdu_type, pdu_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(tree, hf_snmp_pdutype, tvb, offset, length, + pdu_type); + } + offset += length; + + /* get the fields in the PDU preceeding the variable-bindings sequence */ + switch (pdu_type) { + + case SNMP_MSG_GET: + case SNMP_MSG_GETNEXT: + case SNMP_MSG_RESPONSE: + case SNMP_MSG_SET: + case SNMP_MSG_GETBULK: + case SNMP_MSG_INFORM: + case SNMP_MSG_TRAP2: + case SNMP_MSG_REPORT: + /* request id */ + ret = asn1_uint32_decode (&asn1, &request_id, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "request ID", ret); + return; + } + if (tree) { + proto_tree_add_uint(tree, hf_snmp_request_id, + tvb, offset, length, request_id); + } + offset += length; + + /* error status, or getbulk non-repeaters */ + ret = asn1_uint32_decode (&asn1, &error_status, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + (pdu_type == SNMP_MSG_GETBULK) ? "non-repeaters" + : "error status", + ret); + return; + } + if (tree) { + if (pdu_type == SNMP_MSG_GETBULK) { + proto_tree_add_text(tree, tvb, offset, + length, "Non-repeaters: %u", error_status); + } else { + proto_tree_add_uint(tree, + hf_snmp_error_status, + tvb, offset, + length, error_status); + } + } + offset += length; + + /* error index, or getbulk max-repetitions */ + ret = asn1_uint32_decode (&asn1, &error_index, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + (pdu_type == SNMP_MSG_GETBULK) ? "max repetitions" + : "error index", + ret); + return; + } + if (tree) { + if (pdu_type == SNMP_MSG_GETBULK) { + proto_tree_add_text(tree, tvb, offset, + length, "Max repetitions: %u", error_index); + } else { + proto_tree_add_text(tree, tvb, offset, + length, "Error Index: %u", error_index); + } + } + offset += length; + break; + + case SNMP_MSG_TRAP: + /* enterprise */ + ret = asn1_oid_decode (&asn1, &enterprise, &enterprise_length, + &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "enterprise OID", ret); + return; + } + if (tree) { + oid_string = format_oid(enterprise, enterprise_length); + proto_tree_add_string(tree, hf_snmp_enterprise, tvb, + offset, length, oid_string); + g_free(oid_string); + } + g_free(enterprise); + offset += length; + + /* agent address */ + start = asn1.offset; + ret = asn1_header_decode (&asn1, &cls, &con, &tag, + &def, &agent_address_length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "agent address", ret); + return; + } + if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) || + (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) { + /* GXSNMP 0.0.15 says the latter is "needed for + Banyan" */ + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "agent_address", ASN1_ERR_WRONG_TYPE); + return; + } + if (agent_address_length != 4) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "agent_address", ASN1_ERR_WRONG_LENGTH_FOR_TYPE); + return; + } + ret = asn1_string_value_decode (&asn1, + agent_address_length, &agent_address); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "agent address", ret); + return; + } + length = asn1.offset - start; + if (tree) { + if (agent_address_length != 4) { + proto_tree_add_text(tree, tvb, offset, + length, + "Agent address: <length is %u, not 4>", + agent_address_length); + } else { + memcpy((guint8 *)&agent_ipaddr, agent_address, + agent_address_length); + proto_tree_add_ipv4(tree, hf_snmp_agent, tvb, + offset, length, agent_ipaddr); + } + } + g_free(agent_address); + offset += length; + + /* generic trap type */ + ret = asn1_uint32_decode (&asn1, &trap_type, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "generic trap type", ret); + return; + } + if (tree) { + proto_tree_add_uint(tree, hf_snmp_traptype, tvb, + offset, length, trap_type); + } + offset += length; + + /* specific trap type */ + ret = asn1_uint32_decode (&asn1, &specific_type, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "specific trap type", ret); + return; + } + if (tree) { + proto_tree_add_uint(tree, hf_snmp_spectraptype, tvb, + offset, length, specific_type); + } + offset += length; + + /* timestamp */ + start = asn1.offset; + ret = asn1_header_decode (&asn1, &cls, &con, &tag, + &def, ×tamp_length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "timestamp", ret); + return; + } + if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) || + (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "timestamp", ASN1_ERR_WRONG_TYPE); + return; + } + ret = asn1_uint32_value_decode(&asn1, timestamp_length, + ×tamp); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "timestamp", ret); + return; + } + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(tree, hf_snmp_timestamp, tvb, + offset, length, timestamp); + } + offset += length; + break; + } + + /* variable bindings */ + /* get header for variable-bindings sequence */ + ret = asn1_sequence_decode(&asn1, &variable_bindings_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "variable bindings header", ret); + return; + } + offset += length; + + /* loop on variable bindings */ + vb_index = 0; + while (variable_bindings_length > 0) { + vb_index++; + sequence_length = 0; + + /* parse type */ + ret = asn1_sequence_decode(&asn1, &variable_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "variable binding header", ret); + return; + } + sequence_length += length; + + /* parse object identifier */ + ret = asn1_oid_decode (&asn1, &variable_oid, + &variable_oid_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "variable binding OID", ret); + return; + } + sequence_length += length; + + if (display_oid || tree) { + + gchar *decoded_oid; + gchar *non_decoded_oid; + + new_format_oid(variable_oid, variable_oid_length, + &non_decoded_oid, &decoded_oid); + + if (display_oid && check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, + " %s", + (decoded_oid == NULL) ? non_decoded_oid : + decoded_oid); + } + + if (tree) { + if (decoded_oid) { + proto_tree_add_string_format(tree, hf_snmp_oid, + tvb, offset, + sequence_length, + decoded_oid, + "Object identifier %d: %s (%s)", + vb_index, + non_decoded_oid, + decoded_oid); + /* add also the non decoded oid string */ + proto_tree_add_string_hidden(tree, hf_snmp_oid, + tvb, offset, + sequence_length, + non_decoded_oid); + } else { + proto_tree_add_string_format(tree, hf_snmp_oid, + tvb, offset, + sequence_length, + non_decoded_oid, + "Object identifier %d: %s", + vb_index, + non_decoded_oid); + } + } + + if (decoded_oid) g_free(decoded_oid); + g_free(non_decoded_oid); + + } + + offset += sequence_length; + variable_bindings_length -= sequence_length; + + /* + * Register a cleanup function in case one of our + * tvbuff accesses throws an exception. We need + * to clean up variable_oid. + */ + CLEANUP_PUSH(g_free, variable_oid); + + /* Parse the variable's value */ + ret = snmp_variable_decode(tree, variable_oid, + variable_oid_length, &asn1, offset, &length); + + /* + * We're done with variable_oid, so we can call the cleanup + * handler to free* it, and then pop the cleanup handler. + */ + CLEANUP_CALL_AND_POP; + + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, tree, + "variable", ret); + return; + } + offset += length; + variable_bindings_length -= length; + } +} + +static const value_string qos_vals[] = { + { 0x0, "No authentication or privacy" }, + { 0x1, "Authentication, no privacy" }, + { 0x2, "Authentication and privacy" }, + { 0x3, "Authentication and privacy" }, + { 0, NULL }, +}; + +static void +dissect_snmp2u_parameters(proto_tree *tree, tvbuff_t *tvb, int offset, int length, + guchar *parameters, int parameters_length) +{ + proto_item *item; + proto_tree *parameters_tree; + proto_tree *qos_tree; + guint8 model; + guint8 qos; + guint8 len; + + item = proto_tree_add_text(tree, tvb, offset, length, + "Parameters"); + parameters_tree = proto_item_add_subtree(item, ett_parameters); + offset += length - parameters_length; + + if (parameters_length < 1) + return; + model = *parameters; + proto_tree_add_text(parameters_tree, tvb, offset, 1, + "model: %u", model); + offset += 1; + parameters += 1; + parameters_length -= 1; + if (model != 1) { + /* Unknown model. */ + proto_tree_add_text(parameters_tree, tvb, offset, parameters_length, "parameters: %s", + bytes_to_str(parameters, parameters_length)); + return; + } + + if (parameters_length < 1) + return; + qos = *parameters; + item = proto_tree_add_text(parameters_tree, tvb, offset, 1, + "qoS: 0x%x", qos); + qos_tree = proto_item_add_subtree(item, ett_parameters_qos); + proto_tree_add_text(qos_tree, tvb, offset, 1, "%s", + decode_boolean_bitfield(qos, 0x04, + 8, "Generation of report PDU allowed", + "Generation of report PDU not allowed")); + proto_tree_add_text(qos_tree, tvb, offset, 1, "%s", + decode_enumerated_bitfield(qos, 0x03, + 8, qos_vals, "%s")); + offset += 1; + parameters += 1; + parameters_length -= 1; + + if (parameters_length < 12) + return; + proto_tree_add_text(parameters_tree, tvb, offset, 12, + "agentID: %s", bytes_to_str(parameters, 12)); + offset += 12; + parameters += 12; + parameters_length -= 12; + + if (parameters_length < 4) + return; + proto_tree_add_text(parameters_tree, tvb, offset, 4, + "agentBoots: %u", pntohl(parameters)); + offset += 4; + parameters += 4; + parameters_length -= 4; + + if (parameters_length < 4) + return; + proto_tree_add_text(parameters_tree, tvb, offset, 4, + "agentTime: %u", pntohl(parameters)); + offset += 4; + parameters += 4; + parameters_length -= 4; + + if (parameters_length < 2) + return; + proto_tree_add_text(parameters_tree, tvb, offset, 2, + "maxSize: %u", pntohs(parameters)); + offset += 2; + parameters += 2; + parameters_length -= 2; + + if (parameters_length < 1) + return; + len = *parameters; + proto_tree_add_text(parameters_tree, tvb, offset, 1, + "userLen: %u", len); + offset += 1; + parameters += 1; + parameters_length -= 1; + + if (parameters_length < len) + return; + proto_tree_add_text(parameters_tree, tvb, offset, len, + "userName: %.*s", len, parameters); + offset += len; + parameters += len; + parameters_length -= len; + + if (parameters_length < 1) + return; + len = *parameters; + proto_tree_add_text(parameters_tree, tvb, offset, 1, + "authLen: %u", len); + offset += 1; + parameters += 1; + parameters_length -= 1; + + if (parameters_length < len) + return; + proto_tree_add_text(parameters_tree, tvb, offset, len, + "authDigest: %s", bytes_to_str(parameters, len)); + offset += len; + parameters += len; + parameters_length -= len; + + if (parameters_length < 1) + return; + proto_tree_add_text(parameters_tree, tvb, offset, parameters_length, + "contextSelector: %s", bytes_to_str(parameters, parameters_length)); +} + +guint +dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, int proto, gint ett, gboolean is_tcp) +{ + guint length_remaining; + ASN1_SCK asn1; + int start; + gboolean def; + gboolean encrypted; + guint length; + guint message_length; + guint global_length; + + guint32 version; + guint32 msgid; + guint32 msgmax; + guint32 msgsec; + guint32 engineboots; + guint32 enginetime; + + guchar *msgflags; + gchar *commustr; + guchar *community; + guchar *secparm; + guchar *cengineid; + guchar *cname; + guchar *cryptpdu; + guchar *aengineid; + guchar *username; + guchar *authpar; + guchar *privpar; + guint msgflags_length; + guint community_length; + guint secparm_length; + guint cengineid_length; + guint cname_length; + guint cryptpdu_length; + guint aengineid_length; + guint username_length; + guint authpar_length; + guint privpar_length; + + guint pdu_type; + guint pdu_length; + + proto_tree *snmp_tree = NULL; + proto_tree *global_tree = NULL; + proto_tree *flags_tree = NULL; + proto_tree *secur_tree = NULL; + proto_item *item = NULL; + int ret; + guint cls, con, tag; + + /* + * This will throw an exception if we don't have any data left. + * That's what we want. (See "tcp_dissect_pdus()", which is + * similar, but doesn't have to deal with ASN.1. + * XXX - can we make "tcp_dissect_pdus()" provide enough + * information to the "get_pdu_len" routine so that we could + * have that routine deal with ASN.1, and just use + * "tcp_dissect_pdus()"?) + */ + length_remaining = tvb_ensure_length_remaining(tvb, offset); + + /* NOTE: we have to parse the message piece by piece, since the + * capture length may be less than the message length: a 'global' + * parsing is likely to fail. + */ + + /* + * If this is SNMP-over-TCP, we might have to do reassembly + * in order to read the "Sequence Of" header. + */ + if (is_tcp && snmp_desegment && pinfo->can_desegment) { + /* + * This is TCP, and we should, and can, do reassembly. + * + * Is the "Sequence Of" header split across segment + * boundaries? We requre at least 6 bytes for the + * header, which allows for a 4-byte length (ASN.1 + * BER). + */ + if (length_remaining < 6) { + pinfo->desegment_offset = offset; + pinfo->desegment_len = 6 - length_remaining; + + /* + * Return 0, which means "I didn't dissect anything + * because I don't have enough data - we need + * to desegment". + */ + return 0; + } + } + + /* + * OK, try to read the "Sequence Of" header; this gets the total + * length of the SNMP message. + */ + asn1_open(&asn1, tvb, offset); + ret = asn1_sequence_decode(&asn1, &message_length, &length); + if (ret != ASN1_ERR_NOERROR) { + if (tree) { + item = proto_tree_add_item(tree, proto, tvb, offset, + -1, FALSE); + snmp_tree = proto_item_add_subtree(item, ett); + } + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message header", ret); + + /* + * Return the length remaining in the tvbuff, so + * if this is SNMP-over-TCP, our caller thinks there's + * nothing left to dissect. + */ + return length_remaining; + } + + /* + * Add the length of the "Sequence Of" header to the message + * length. + */ + message_length += length; + if (message_length < length) { + /* + * The message length was probably so large that the + * total length overflowed. + * + * Report this as an error. + */ + show_reported_bounds_error(tvb, pinfo, tree); + + /* + * Return the length remaining in the tvbuff, so + * if this is SNMP-over-TCP, our caller thinks there's + * nothing left to dissect. + */ + return length_remaining; + } + + /* + * If this is SNMP-over-TCP, we might have to do reassembly + * to get all of this message. + */ + if (is_tcp && snmp_desegment && pinfo->can_desegment) { + /* + * Yes - is the message split across segment boundaries? + */ + if (length_remaining < message_length) { + /* + * Yes. Tell the TCP dissector where the data + * for this message starts in the data it handed + * us, and how many more bytes we need, and + * return. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = + message_length - length_remaining; + + /* + * Return 0, which means "I didn't dissect anything + * because I don't have enough data - we need + * to desegment". + */ + return 0; + } + } + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) { + col_set_str(pinfo->cinfo, COL_PROTOCOL, + proto_get_protocol_short_name(find_protocol_by_id(proto))); + } + + if (tree) { + item = proto_tree_add_item(tree, proto, tvb, offset, + message_length, FALSE); + snmp_tree = proto_item_add_subtree(item, ett); + } + offset += length; + + ret = asn1_uint32_decode (&asn1, &version, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "version number", ret); + return message_length; + } + if (snmp_tree) { + proto_tree_add_uint(snmp_tree, hf_snmp_version, tvb, offset, + length, version); + } + offset += length; + + + switch (version) { + case SNMP_VERSION_1: + case SNMP_VERSION_2c: + ret = asn1_octet_string_decode (&asn1, &community, + &community_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "community", ret); + return message_length; + } + if (tree) { + commustr = g_malloc(community_length+1); + memcpy(commustr, community, community_length); + commustr[community_length] = '\0'; + + proto_tree_add_string(snmp_tree, hf_snmp_community, + tvb, offset, length, commustr); + g_free(commustr); + } + g_free(community); + offset += length; + break; + case SNMP_VERSION_2u: + ret = asn1_octet_string_decode (&asn1, &community, + &community_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "community (2u)", ret); + return message_length; + } + if (tree) { + dissect_snmp2u_parameters(snmp_tree, tvb, offset, length, + community, community_length); + } + g_free(community); + offset += length; + break; + case SNMP_VERSION_3: + ret = asn1_sequence_decode(&asn1, &global_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message global header", ret); + return message_length; + } + if (snmp_tree) { + item = proto_tree_add_text(snmp_tree, tvb, offset, + global_length + length, "Message Global Header"); + global_tree = proto_item_add_subtree(item, ett_global); + proto_tree_add_text(global_tree, tvb, offset, + length, + "Message Global Header Length: %d", global_length); + } + offset += length; + ret = asn1_uint32_decode (&asn1, &msgid, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message id", ret); + return message_length; + } + if (global_tree) { + proto_tree_add_text(global_tree, tvb, offset, + length, "Message ID: %d", msgid); + } + offset += length; + ret = asn1_uint32_decode (&asn1, &msgmax, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message max size", ret); + return message_length; + } + if (global_tree) { + proto_tree_add_text(global_tree, tvb, offset, + length, "Message Max Size: %d", msgmax); + } + offset += length; + ret = asn1_octet_string_decode (&asn1, &msgflags, + &msgflags_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message flags", ret); + return message_length; + } + if (msgflags_length != 1) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message flags wrong length", ret); + g_free(msgflags); + return message_length; + } + if (global_tree) { + item = proto_tree_add_uint_format(global_tree, + hf_snmpv3_flags, tvb, offset, length, + msgflags[0], "Flags: 0x%02x", msgflags[0]); + flags_tree = proto_item_add_subtree(item, ett_flags); + proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_report, + tvb, offset, length, msgflags[0]); + proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_crypt, + tvb, offset, length, msgflags[0]); + proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_auth, + tvb, offset, length, msgflags[0]); + } + encrypted = msgflags[0] & TH_CRYPT; + g_free(msgflags); + offset += length; + ret = asn1_uint32_decode (&asn1, &msgsec, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "message security model", ret); + return message_length; + } + if (global_tree) { + proto_tree_add_text(global_tree, tvb, offset, + length, "Message Security Model: %s", + val_to_str(msgsec, sec_models, + "Unknown model %#x")); + } + offset += length; + switch(msgsec) { + case SNMP_SEC_USM: + start = asn1.offset; + ret = asn1_header_decode (&asn1, &cls, &con, &tag, + &def, &secparm_length); + length = asn1.offset - start; + if (cls != ASN1_UNI && con != ASN1_PRI && + tag != ASN1_OTS) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "Message Security Parameters", + ASN1_ERR_WRONG_TYPE); + return message_length; + } + if (snmp_tree) { + item = proto_tree_add_text(snmp_tree, tvb, + offset, secparm_length + length, + "Message Security Parameters"); + secur_tree = proto_item_add_subtree(item, + ett_secur); + proto_tree_add_text(secur_tree, tvb, offset, + length, + "Message Security Parameters Length: %d", + secparm_length); + } + offset += length; + ret = asn1_sequence_decode(&asn1, &secparm_length, + &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "USM sequence header", ret); + return message_length; + } + offset += length; + ret = asn1_octet_string_decode (&asn1, &aengineid, + &aengineid_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "authoritative engine id", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, offset, + length, "Authoritative Engine ID: %s", + bytes_to_str(aengineid, aengineid_length)); + } + g_free(aengineid); + offset += length; + ret = asn1_uint32_decode (&asn1, &engineboots, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "engine boots", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, + offset, length, "Engine Boots: %d", + engineboots); + } + offset += length; + ret = asn1_uint32_decode (&asn1, &enginetime, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "engine time", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, + offset, length, "Engine Time: %d", + enginetime); + } + offset += length; + ret = asn1_octet_string_decode (&asn1, &username, + &username_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "user name", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, offset, + length, "User Name: %s", + SAFE_STRING(username, username_length)); + } + g_free(username); + offset += length; + ret = asn1_octet_string_decode (&asn1, &authpar, + &authpar_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "authentication parameter", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, offset, + length, "Authentication Parameter: %s", + bytes_to_str(authpar, authpar_length)); + } + g_free(authpar); + offset += length; + ret = asn1_octet_string_decode (&asn1, &privpar, + &privpar_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "privacy parameter", ret); + return message_length; + } + if (secur_tree) { + proto_tree_add_text(secur_tree, tvb, offset, + length, "Privacy Parameter: %s", + bytes_to_str(privpar, privpar_length)); + } + g_free(privpar); + offset += length; + break; + default: + ret = asn1_octet_string_decode (&asn1, + &secparm, &secparm_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "Message Security Parameters", + ret); + return message_length; + } + if (snmp_tree) { + proto_tree_add_text(snmp_tree, tvb, offset, + length, + "Message Security Parameters Data" + " (%d bytes)", secparm_length); + } + g_free(secparm); + offset += length; + break; + } + /* PDU starts here */ + if (encrypted) { + ret = asn1_octet_string_decode (&asn1, &cryptpdu, + &cryptpdu_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, + snmp_tree, "encrypted PDU header", ret); + return message_length; + } + proto_tree_add_text(snmp_tree, tvb, offset, length, + "Encrypted PDU (%d bytes)", length); + g_free(cryptpdu); + if (check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "Encrypted PDU"); + return message_length; + } + ret = asn1_sequence_decode(&asn1, &global_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "PDU header", ret); + return message_length; + } + offset += length; + ret = asn1_octet_string_decode (&asn1, &cengineid, + &cengineid_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "context engine id", ret); + return message_length; + } + if (snmp_tree) { + proto_tree_add_text(snmp_tree, tvb, offset, length, + "Context Engine ID: %s", + bytes_to_str(cengineid, cengineid_length)); + } + g_free(cengineid); + offset += length; + ret = asn1_octet_string_decode (&asn1, &cname, + &cname_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "context name", ret); + return message_length; + } + if (snmp_tree) { + proto_tree_add_text(snmp_tree, tvb, offset, length, + "Context Name: %s", + SAFE_STRING(cname, cname_length)); + } + g_free(cname); + offset += length; + break; + default: + dissect_snmp_error(tvb, offset, pinfo, snmp_tree, + "PDU for unknown version of SNMP"); + return message_length; + } + + start = asn1.offset; + ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def, + &pdu_length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "PDU type", ret); + return message_length; + } + if (cls != ASN1_CTX || con != ASN1_CON) { + dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree, + "PDU type", ASN1_ERR_WRONG_TYPE); + return message_length; + } + dissect_common_pdu(tvb, offset, pinfo, snmp_tree, asn1, pdu_type, start); + return message_length; +} + +static void +dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, int proto, gint ett) +{ + ASN1_SCK asn1; + int start; + gboolean def; + guint length; + + guint pdu_type; + char *pdu_type_string; + guint pdu_length; + + guint32 version; + guint32 cause; + guint32 priority; + guint32 operation; + guint32 commit; + + guchar *password; + guint password_length; + + guchar *application; + guint application_length; + + subid_t *regid; + guint regid_length; + + gchar *oid_string; + + proto_tree *smux_tree = NULL; + proto_item *item = NULL; + int ret; + guint cls, con; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMUX"); + + if (tree) { + item = proto_tree_add_item(tree, proto, tvb, offset, -1, FALSE); + smux_tree = proto_item_add_subtree(item, ett); + } + + /* NOTE: we have to parse the message piece by piece, since the + * capture length may be less than the message length: a 'global' + * parsing is likely to fail. + */ + /* parse the SNMP header */ + asn1_open(&asn1, tvb, offset); + start = asn1.offset; + ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def, + &pdu_length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "PDU type", ret); + return; + } + + /* Dissect SMUX here */ + if (cls == ASN1_APL && con == ASN1_CON && pdu_type == SMUX_MSG_OPEN) { + pdu_type_string = val_to_str(pdu_type, smux_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb, + offset, length, pdu_type); + } + offset += length; + ret = asn1_uint32_decode (&asn1, &version, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "version", ret); + return; + } + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_version, tvb, + offset, length, version); + } + offset += length; + + ret = asn1_oid_decode (&asn1, ®id, ®id_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "registration OID", ret); + return; + } + if (tree) { + oid_string = format_oid(regid, regid_length); + proto_tree_add_text(smux_tree, tvb, offset, length, + "Registration: %s", oid_string); + g_free(oid_string); + } + g_free(regid); + offset += length; + + ret = asn1_octet_string_decode (&asn1, &application, + &application_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "application", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, length, + "Application: %s", + SAFE_STRING(application, application_length)); + } + g_free(application); + offset += length; + + ret = asn1_octet_string_decode (&asn1, &password, + &password_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "password", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, length, + "Password: %s", + SAFE_STRING(password, password_length)); + } + g_free(password); + offset += length; + return; + } + if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_CLOSE) { + pdu_type_string = val_to_str(pdu_type, smux_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb, + offset, length, pdu_type); + } + offset += length; + ret = asn1_uint32_value_decode (&asn1, pdu_length, &cause); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "cause", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, + pdu_length, "Cause: %s", + val_to_str(cause, smux_close, + "Unknown cause %#x")); + } + offset += pdu_length; + return; + } + if (cls == ASN1_APL && con == ASN1_CON && pdu_type == SMUX_MSG_RREQ) { + pdu_type_string = val_to_str(pdu_type, smux_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb, + offset, length, pdu_type); + } + offset += length; + ret = asn1_oid_decode (&asn1, ®id, ®id_length, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "registration subtree", ret); + return; + } + if (tree) { + oid_string = format_oid(regid, regid_length); + proto_tree_add_text(smux_tree, tvb, offset, length, + "Registration: %s", oid_string); + g_free(oid_string); + } + g_free(regid); + offset += length; + + ret = asn1_uint32_decode (&asn1, &priority, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "priority", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, length, + "Priority: %d", priority); + } + offset += length; + + ret = asn1_uint32_decode (&asn1, &operation, &length); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "operation", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, length, + "Operation: %s", + val_to_str(operation, smux_rreq, + "Unknown operation %#x")); + } + offset += length; + return; + } + if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_RRSP) { + pdu_type_string = val_to_str(pdu_type, smux_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb, + offset, length, pdu_type); + } + offset += length; + ret = asn1_uint32_value_decode (&asn1, pdu_length, &priority); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "priority", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, + pdu_length, "%s", + val_to_str(priority, smux_prio, + "Priority: %#x")); + } + offset += pdu_length; + return; + } + if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_SOUT) { + pdu_type_string = val_to_str(pdu_type, smux_types, + "Unknown PDU type %#x"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string); + length = asn1.offset - start; + if (tree) { + proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb, + offset, length, pdu_type); + } + offset += length; + ret = asn1_uint32_value_decode (&asn1, pdu_length, &commit); + if (ret != ASN1_ERR_NOERROR) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "commit", ret); + return; + } + if (tree) { + proto_tree_add_text(smux_tree, tvb, offset, + pdu_length, "%s", + val_to_str(commit, smux_sout, + "Unknown SOUT Value: %#x")); + } + offset += pdu_length; + return; + } + if (cls != ASN1_CTX || con != ASN1_CON) { + dissect_snmp_parse_error(tvb, offset, pinfo, smux_tree, + "PDU type", ASN1_ERR_WRONG_TYPE); + return; + } + dissect_common_pdu(tvb, offset, pinfo, smux_tree, asn1, pdu_type, start); +} + +static void +dissect_snmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + conversation_t *conversation; + + /* + * The first SNMP packet goes to the SNMP port; the second one + * may come from some *other* port, but goes back to the same + * IP address and port as the ones from which the first packet + * came; all subsequent packets presumably go between those two + * IP addresses and ports. + * + * If this packet went to the SNMP port, we check to see if + * there's already a conversation with one address/port pair + * matching the source IP address and port of this packet, + * the other address matching the destination IP address of this + * packet, and any destination port. + * + * If not, we create one, with its address 1/port 1 pair being + * the source address/port of this packet, its address 2 being + * the destination address of this packet, and its port 2 being + * wildcarded, and give it the SNMP dissector as a dissector. + */ + if (pinfo->destport == UDP_PORT_SNMP) { + conversation = find_conversation(&pinfo->src, &pinfo->dst, PT_UDP, + pinfo->srcport, 0, NO_PORT_B); + if (conversation == NULL) { + conversation = conversation_new(&pinfo->src, &pinfo->dst, PT_UDP, + pinfo->srcport, 0, NO_PORT2); + conversation_set_dissector(conversation, snmp_handle); + } + } + + dissect_snmp_pdu(tvb, 0, pinfo, tree, proto_snmp, ett_snmp, FALSE); +} + +static void +dissect_snmp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + guint message_len; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + message_len = dissect_snmp_pdu(tvb, 0, pinfo, tree, + proto_snmp, ett_snmp, TRUE); + if (message_len == 0) { + /* + * We don't have all the data for that message, + * so we need to do desegmentation; + * "dissect_snmp_pdu()" has set that up. + */ + break; + } + offset += message_len; + } +} + +static void +dissect_smux(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + dissect_smux_pdu(tvb, 0, pinfo, tree, proto_smux, ett_smux); +} + +static void +process_prefs(void) +{ +#ifdef HAVE_SOME_SNMP + gchar *tmp_mib_modules; + static gboolean mibs_loaded = FALSE; + + if (mibs_loaded) { + /* + * Unload the MIBs, as we'll be reloading them based on + * the current preference setting. + */ + shutdown_mib(); /* unload MIBs */ + } + + /* + * Cannot check if MIBS is already set, as it could be set by Ethereal. + * + * If we have a list of modules to load, put that list in MIBS, + * otherwise clear MIBS. + */ + if (mib_modules != NULL) { + tmp_mib_modules = g_strconcat("MIBS=", mib_modules, NULL); + +#ifdef WIN32 + _putenv(tmp_mib_modules); +#else + putenv(tmp_mib_modules); +#endif /*WIN32*/ + } else { +#ifdef WIN32 + _putenv("MIBS"); +#else + putenv("MIBS"); +#endif /* WIN32 */ + } + + /* + * Load the MIBs. + */ + register_mib_handlers(); + read_premib_configs(); + init_mib(); + read_configs(); + mibs_loaded = TRUE; +#endif /* HAVE_SOME_SNMP */ +} + +void +proto_register_snmp(void) +{ +#if defined(WIN32) && defined(HAVE_SOME_SNMP) + char *mib_path; +#define MIB_PATH_APPEND "snmp\\mibs" +#endif + gchar *tmp_mib_modules; + + static hf_register_info hf[] = { + { &hf_snmp_version, + { "Version", "snmp.version", FT_UINT8, BASE_DEC, VALS(versions), + 0x0, "", HFILL }}, + { &hf_snmp_community, + { "Community", "snmp.community", FT_STRING, BASE_NONE, NULL, + 0x0, "", HFILL }}, + { &hf_snmp_request_id, + { "Request Id", "snmp.id", FT_UINT32, BASE_HEX, NULL, + 0x0, "Id for this transaction", HFILL }}, + { &hf_snmp_pdutype, + { "PDU type", "snmp.pdutype", FT_UINT8, BASE_DEC, VALS(pdu_types), + 0x0, "", HFILL }}, + { &hf_snmp_agent, + { "Agent address", "snmp.agent", FT_IPv4, BASE_NONE, NULL, + 0x0, "", HFILL }}, + { &hf_snmp_enterprise, + { "Enterprise", "snmp.enterprise", FT_STRING, BASE_NONE, NULL, + 0x0, "", HFILL }}, + { &hf_snmp_error_status, + { "Error Status", "snmp.error", FT_UINT8, BASE_DEC, VALS(error_statuses), + 0x0, "", HFILL }}, + { &hf_snmp_oid, + { "Object identifier", "snmp.oid", FT_STRING, BASE_NONE, NULL, + 0x0, "", HFILL }}, + { &hf_snmp_traptype, + { "Trap type", "snmp.traptype", FT_UINT8, BASE_DEC, VALS(trap_types), + 0x0, "", HFILL }}, + { &hf_snmp_spectraptype, + { "Specific trap type", "snmp.spectraptype", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL }}, + { &hf_snmp_timestamp, + { "Timestamp", "snmp.timestamp", FT_UINT8, BASE_DEC, NULL, + 0x0, "", HFILL }}, + { &hf_snmpv3_flags, + { "SNMPv3 Flags", "snmpv3.flags", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL }}, + { &hf_snmpv3_flags_auth, + { "Authenticated", "snmpv3.flags.auth", FT_BOOLEAN, 8, + TFS(&flags_set_truth), TH_AUTH, "", HFILL }}, + { &hf_snmpv3_flags_crypt, + { "Encrypted", "snmpv3.flags.crypt", FT_BOOLEAN, 8, + TFS(&flags_set_truth), TH_CRYPT, "", HFILL }}, + { &hf_snmpv3_flags_report, + { "Reportable", "snmpv3.flags.report", FT_BOOLEAN, 8, + TFS(&flags_set_truth), TH_REPORT, "", HFILL }}, + }; + static gint *ett[] = { + &ett_snmp, + &ett_parameters, + &ett_parameters_qos, + &ett_global, + &ett_flags, + &ett_secur, + }; + module_t *snmp_module; + +#ifdef HAVE_SOME_SNMP + +#ifdef WIN32 + /* Set MIBDIRS so that the SNMP library can find its mibs. */ + /* XXX - Should we set MIBS or MIBFILES as well? */ + + mib_path = g_malloc (strlen(get_datafile_dir()) + strlen(MIB_PATH_APPEND) + 20); + sprintf (mib_path, "MIBDIRS=%s\\%s", get_datafile_dir(), MIB_PATH_APPEND); + /* Amazingly enough, Windows does not provide setenv(). */ + if (getenv("MIBDIRS") == NULL) + _putenv(mib_path); + g_free(mib_path); + +#endif /* WIN32 */ + + /* + * Suppress warnings about unknown tokens - we aren't initializing + * UCD SNMP in its entirety, we're just initializing the + * MIB-handling part because that's all we're using, which + * means that entries in the configuration file for other + * pars of the library will not be handled, and we don't want + * the config file reading code to whine about that. + */ + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_NO_TOKEN_WARNINGS, TRUE); + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY, 2); +#endif /* HAVE_SOME_SNMP */ + proto_snmp = proto_register_protocol("Simple Network Management Protocol", + "SNMP", "snmp"); + proto_register_field_array(proto_snmp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + snmp_handle = create_dissector_handle(dissect_snmp, proto_snmp); + + /* Register configuration preferences */ + snmp_module = prefs_register_protocol(proto_snmp, process_prefs); + prefs_register_bool_preference(snmp_module, "display_oid", + "Show SNMP OID in info column", + "Whether the SNMP OID should be shown in the info column", + &display_oid); + + /* + * Set the default value of "mib_modules". + * + * If the MIBS environment variable is set, make its value + * the value of "mib_modules", otherwise, set "mib_modules" + * to DEF_MIB_MODULES. + */ + tmp_mib_modules = getenv("MIBS"); + if (tmp_mib_modules != NULL) + mib_modules = tmp_mib_modules; + prefs_register_string_preference(snmp_module, "mib_modules", + "MIB modules to load", + "List of MIB modules to load (the list is set to environment variable MIBS if the variable is not already set)", + &mib_modules); + prefs_register_bool_preference(snmp_module, "desegment", + "Desegment all SNMP-over-TCP messages\nspanning multiple TCP segments", + "Whether the SNMP dissector should desegment all messages " + "spanning multiple TCP segments", + &snmp_desegment); +} + +void +proto_reg_handoff_snmp(void) +{ + dissector_handle_t snmp_tcp_handle; + + dissector_add("udp.port", UDP_PORT_SNMP, snmp_handle); + dissector_add("udp.port", UDP_PORT_SNMP_TRAP, snmp_handle); + dissector_add("ethertype", ETHERTYPE_SNMP, snmp_handle); + dissector_add("ipx.socket", IPX_SOCKET_SNMP_AGENT, snmp_handle); + dissector_add("ipx.socket", IPX_SOCKET_SNMP_SINK, snmp_handle); + dissector_add("hpext.dxsap", HPEXT_SNMP, snmp_handle); + + snmp_tcp_handle = create_dissector_handle(dissect_snmp_tcp, proto_snmp); + dissector_add("tcp.port", TCP_PORT_SNMP, snmp_tcp_handle); + dissector_add("tcp.port", TCP_PORT_SNMP_TRAP, snmp_tcp_handle); + + data_handle = find_dissector("data"); + + /* + * Process preference settings. + * + * We can't do this in the register routine, as preferences aren't + * read until all dissector register routines have been called (so + * that all dissector preferences have been registered). + */ + process_prefs(); +} + +void +proto_register_smux(void) +{ + static hf_register_info hf[] = { + { &hf_smux_version, + { "Version", "smux.version", FT_UINT8, BASE_DEC, NULL, + 0x0, "", HFILL }}, + { &hf_smux_pdutype, + { "PDU type", "smux.pdutype", FT_UINT8, BASE_DEC, VALS(smux_types), + 0x0, "", HFILL }}, + }; + static gint *ett[] = { + &ett_smux, + }; + + proto_smux = proto_register_protocol("SNMP Multiplex Protocol", + "SMUX", "smux"); + proto_register_field_array(proto_smux, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_smux(void) +{ + dissector_handle_t smux_handle; + + smux_handle = create_dissector_handle(dissect_smux, proto_smux); + dissector_add("tcp.port", TCP_PORT_SMUX, smux_handle); +} |