Handle SNMP-over-TCP.
[obnox/wireshark/wip.git] / packet-snmp.c
index 1b55fddd1451d81a8fab6f050523e83147585474..5761c770c739b1ce72195c3d362c6a41e8642776 100644 (file)
@@ -10,7 +10,7 @@
  *
  * See RFCs 2570-2576 for SNMPv3
  *
- * $Id: packet-snmp.c,v 1.102 2002/11/11 17:34:22 guy Exp $
+ * $Id: packet-snmp.c,v 1.113 2003/09/06 01:21:00 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #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
 
 # include <ucd-snmp/default_store.h>
 # include <ucd-snmp/read_config.h>
 # include <ucd-snmp/tools.h>
-# define netsnmp_ds_set_boolean ds_set_boolean
-# define netsnmp_ds_set_int ds_set_int
+#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
-#endif /* HAVE_NET_SNMP */
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define netsnmp_ds_set_int ds_set_int
+#endif
 
 #ifdef WIN32
 # include <epan/filesystem.h>
 #include "packet-snmp.h"
 #include "format-oid.h"
 
-/* Null string of type "guchar[]". */
-static const guchar nullstring[] = "";
-
 /* 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. */
-#define        SAFE_STRING(s)  (((s) != NULL) ? (s) : nullstring)
+   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;
-static int proto_smux = -1;
+
+static gboolean display_oid = TRUE;
 
 static gint ett_snmp = -1;
-static gint ett_smux = -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;
 
@@ -134,6 +160,8 @@ static dissector_handle_t data_handle;
 
 #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 */
@@ -237,26 +265,63 @@ static const value_string smux_sout[] = {
 };
 
 /* 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
-#define SNMP_ERR_GENERROR              5
-
+#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" },
@@ -264,7 +329,7 @@ static const value_string error_statuses[] = {
        { SNMP_ERR_NOSUCHNAME,          "NO SUCH NAME" },
        { SNMP_ERR_BADVALUE,            "BAD VALUE" },
        { SNMP_ERR_READONLY,            "READ ONLY" },
-       { SNMP_ERR_GENERROR,            "GENERIC ERROR" },
+       { SNMP_ERR_GENERR,              "GENERIC ERROR" },
        { SNMP_ERR_NOACCESS,            "NO ACCESS" },
        { SNMP_ERR_WRONGTYPE,           "WRONG TYPE" },
        { SNMP_ERR_WRONGLENGTH,         "WRONG LENGTH" },
@@ -283,13 +348,27 @@ static const value_string error_statuses[] = {
 
 /* 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" },
@@ -473,10 +552,10 @@ format_oid(subid_t *oid, guint oid_length)
         * Get the decoded form of the OID, and add its length to the
         * length of the result string.
         *
-        * XXX - check for "malloc" and "sprint_realloc_objid()" failure.
+        * XXX - check for "sprint_realloc_objid()" failure.
         */
        oid_string_len = 256;
-       oid_string = malloc(oid_string_len);
+       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,
@@ -498,12 +577,53 @@ format_oid(subid_t *oid, guint oid_length)
         * Append the decoded form of the OID.
         */
        sprintf(buf, " (%s)", oid_string);
-       free(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)
@@ -515,7 +635,7 @@ check_var_length(guint vb_length, guint required_length)
                /* Enough room for the largest "Length is XXX,
                   should be XXX" message - 10 digits for each
                   XXX. */
-               buf = malloc(sizeof badlen_fmt + 10 + 10);
+               buf = g_malloc(sizeof badlen_fmt + 10 + 10);
                sprintf(buf, badlen_fmt, vb_length, required_length);
                return buf;
        }
@@ -602,10 +722,10 @@ format_var(struct variable_list *variable, subid_t *variable_oid,
        variable->val_len = val_len;
 
        /*
-        * XXX - check for "malloc" and "sprint_realloc_objid()" failure.
+        * XXX - check for "sprint_realloc_objid()" failure.
         */
        buf_len = 256;
-       buf = malloc(buf_len);
+       buf = g_malloc(buf_len);
        *buf = '\0';
        out_len = 0;
        sprint_realloc_value(&buf, &buf_len, &out_len, 1,  variable_oid,
@@ -694,7 +814,7 @@ snmp_variable_decode(proto_tree *snmp_tree,
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
                            "Value: %s", vb_display_string);
-                       free(vb_display_string);
+                       g_free(vb_display_string);
 #else /* HAVE_SOME_SNMP */
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
@@ -722,7 +842,7 @@ snmp_variable_decode(proto_tree *snmp_tree,
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
                            "Value: %s", vb_display_string);
-                       free(vb_display_string);
+                       g_free(vb_display_string);
 #else /* HAVE_SOME_SNMP */
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
@@ -752,7 +872,7 @@ snmp_variable_decode(proto_tree *snmp_tree,
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
                            "Value: %s", vb_display_string);
-                       free(vb_display_string);
+                       g_free(vb_display_string);
 #else /* HAVE_SOME_SNMP */
                        /*
                         * If some characters are not printable, display
@@ -786,9 +906,8 @@ snmp_variable_decode(proto_tree *snmp_tree,
                        } else {
                                proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                                    length,
-                                   "Value: %s: %.*s", vb_type_name,
-                                   (int)vb_length,
-                                   SAFE_STRING(vb_octet_string));
+                                   "Value: %s: %s", vb_type_name,
+                                   SAFE_STRING(vb_octet_string, vb_length));
                        }
 #endif /* HAVE_SOME_SNMP */
                }
@@ -821,7 +940,7 @@ snmp_variable_decode(proto_tree *snmp_tree,
                        proto_tree_add_text(snmp_tree, asn1->tvb, offset,
                            length,
                            "Value: %s", vb_display_string);
-                       free(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,
@@ -884,6 +1003,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        subid_t *enterprise;
        guint enterprise_length;
 
+       guint32 agent_ipaddr;
+
        guint8 *agent_address;
        guint agent_address_length;
 
@@ -912,8 +1033,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
        length = asn1.offset - start;
        if (tree) {
-               proto_tree_add_text(tree, tvb, offset, length,
-                   "PDU type: %s", pdu_type_string);
+               proto_tree_add_uint(tree, hf_snmp_pdutype, tvb, offset, length,
+                   pdu_type);
        }
        offset += length;
 
@@ -936,8 +1057,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        return;
                }
                if (tree) {
-                       proto_tree_add_text(tree, tvb, offset, length,
-                           "Request Id: %#x", request_id);
+                       proto_tree_add_uint(tree, hf_snmp_request_id,
+                               tvb, offset, length, request_id);
                }
                offset += length;
 
@@ -955,10 +1076,10 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                proto_tree_add_text(tree, tvb, offset,
                                    length, "Non-repeaters: %u", error_status);
                        } else {
-                               proto_tree_add_text(tree, tvb, offset,
-                                   length, "Error Status: %s",
-                                   val_to_str(error_status, error_statuses,
-                                     "Unknown (%d)"));
+                               proto_tree_add_uint(tree, 
+                                                   hf_snmp_error_status,
+                                                   tvb, offset,
+                                                   length, error_status);
                        }
                }
                offset += length;
@@ -995,8 +1116,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                }
                if (tree) {
                        oid_string = format_oid(enterprise, enterprise_length);
-                       proto_tree_add_text(tree, tvb, offset, length,
-                           "Enterprise: %s", oid_string);
+                       proto_tree_add_string(tree, hf_snmp_enterprise, tvb,
+                           offset, length, oid_string);
                        g_free(oid_string);
                }
                g_free(enterprise);
@@ -1039,10 +1160,10 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                    "Agent address: <length is %u, not 4>",
                                    agent_address_length);
                        } else {
-                               proto_tree_add_text(tree, tvb, offset,
-                                   length,
-                                   "Agent address: %s",
-                                   ip_to_str(agent_address));
+                               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);
@@ -1056,9 +1177,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        return;
                }
                if (tree) {
-                       proto_tree_add_text(tree, tvb, offset, length,
-                           "Trap type: %s",
-                           val_to_str(trap_type, trap_types, "Unknown (%u)"));
+                       proto_tree_add_uint(tree, hf_snmp_traptype, tvb,
+                           offset, length, trap_type);
                }
                offset += length;
 
@@ -1070,9 +1190,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        return;
                }
                if (tree) {
-                       proto_tree_add_text(tree, tvb, offset, length,
-                           "Specific trap type: %u (%#x)",
-                           specific_type, specific_type);
+                       proto_tree_add_uint(tree, hf_snmp_spectraptype, tvb,
+                           offset, length, specific_type);
                }
                offset += length;
 
@@ -1100,8 +1219,8 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                }
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(tree, tvb, offset, length,
-                           "Timestamp: %u", timestamp);
+                       proto_tree_add_uint(tree, hf_snmp_timestamp, tvb,
+                           offset, length, timestamp);
                }
                offset += length;
                break;
@@ -1142,13 +1261,52 @@ dissect_common_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                }
                sequence_length += length;
 
-               if (tree) {
-                       oid_string = format_oid(variable_oid,
-                           variable_oid_length);
-                       proto_tree_add_text(tree, tvb, offset, sequence_length,
-                           "Object identifier %d: %s", vb_index, oid_string);
-                       g_free(oid_string);
+               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;
 
@@ -1199,8 +1357,7 @@ dissect_snmp2u_parameters(proto_tree *tree, tvbuff_t *tvb, int offset, int lengt
        parameters_length -= 1;
        if (model != 1) {
                /* Unknown model. */
-               proto_tree_add_text(parameters_tree, tvb, offset,
-                   parameters_length, "parameters: %s",
+               proto_tree_add_text(parameters_tree, tvb, offset,                   parameters_length, "parameters: %s",
                    bytes_to_str(parameters, parameters_length));
                return;
        }
@@ -1294,10 +1451,11 @@ dissect_snmp2u_parameters(proto_tree *tree, tvbuff_t *tvb, int offset, int lengt
            "contextSelector: %s", bytes_to_str(parameters, parameters_length));
 }
 
-void
+guint
 dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
-    proto_tree *tree, char *proto_name, int proto, gint ett)
+    proto_tree *tree, int proto, gint ett, gboolean is_tcp)
 {
+       guint length_remaining;
        ASN1_SCK asn1;
        int start;
        gboolean def;
@@ -1314,6 +1472,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        guint32 enginetime;
 
        guchar *msgflags;
+       guchar *commustr;
        guchar *community;
        guchar *secparm;
        guchar *cengineid;
@@ -1345,25 +1504,130 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        int ret;
        guint cls, con, tag;
 
-       if (check_col(pinfo->cinfo, COL_PROTOCOL))
-               col_add_str(pinfo->cinfo, COL_PROTOCOL, proto_name);
-
-       if (tree) {
-               item = proto_tree_add_item(tree, proto, tvb, offset, -1, FALSE);
-               snmp_tree = proto_item_add_subtree(item, ett);
-       }
+       /*
+        * 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.
         */
-       /* parse the SNMP header */
+
+       /*
+        * 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;
+
+               /*
+                * 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(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;
 
@@ -1371,12 +1635,11 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (ret != ASN1_ERR_NOERROR) {
                dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                    "version number", ret);
-               return;
+               return message_length;
        }
        if (snmp_tree) {
-               proto_tree_add_text(snmp_tree, tvb, offset, length,
-                   "Version: %s",
-                   val_to_str(version, versions, "Unknown version %#x"));
+               proto_tree_add_uint(snmp_tree, hf_snmp_version, tvb, offset,
+                   length, version);
        }
        offset += length;
 
@@ -1389,12 +1652,16 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "community", ret);
-                       return;
+                       return message_length;
                }
                if (tree) {
-                       proto_tree_add_text(snmp_tree, tvb, offset, length,
-                           "Community: %.*s", community_length,
-                           SAFE_STRING(community));
+                       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;
@@ -1414,7 +1681,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                                "message global header", ret);
-                       return;
+                       return message_length;
                }
                if (snmp_tree) {
                        item = proto_tree_add_text(snmp_tree, tvb, offset,
@@ -1429,7 +1696,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "message id", ret);
-                       return;
+                       return message_length;
                }
                if (global_tree) {
                        proto_tree_add_text(global_tree, tvb, offset,
@@ -1440,7 +1707,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "message max size", ret);
-                       return;
+                       return message_length;
                }
                if (global_tree) {
                        proto_tree_add_text(global_tree, tvb, offset,
@@ -1452,13 +1719,13 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "message flags", ret);
-                       return;
+                       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;
+                       return message_length;
                }
                if (global_tree) {
                        item = proto_tree_add_uint_format(global_tree,
@@ -1479,7 +1746,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "message security model", ret);
-                       return;
+                       return message_length;
                }
                if (global_tree) {
                        proto_tree_add_text(global_tree, tvb, offset,
@@ -1499,7 +1766,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "Message Security Parameters",
                                    ASN1_ERR_WRONG_TYPE);
-                               return;
+                               return message_length;
                        }
                        if (snmp_tree) {
                                item = proto_tree_add_text(snmp_tree, tvb,
@@ -1518,7 +1785,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "USM sequence header", ret);
-                               return;
+                               return message_length;
                        }
                        offset += length;
                        ret = asn1_octet_string_decode (&asn1, &aengineid,
@@ -1526,7 +1793,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "authoritative engine id", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb, offset,
@@ -1539,7 +1806,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "engine boots", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb,
@@ -1551,7 +1818,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree,  "engine time", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb,
@@ -1564,13 +1831,12 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "user name", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb, offset,
-                                   length, "User Name: %.*s",
-                                   username_length,
-                                   SAFE_STRING(username));
+                                   length, "User Name: %s",
+                                   SAFE_STRING(username, username_length));
                        }
                        g_free(username);
                        offset += length;
@@ -1579,7 +1845,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "authentication parameter", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb, offset,
@@ -1593,7 +1859,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "privacy parameter", ret);
-                               return;
+                               return message_length;
                        }
                        if (secur_tree) {
                                proto_tree_add_text(secur_tree, tvb, offset,
@@ -1610,7 +1876,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "Message Security Parameters",
                                    ret);
-                               return;
+                               return message_length;
                        }
                        if (snmp_tree) {
                                proto_tree_add_text(snmp_tree, tvb, offset,
@@ -1629,20 +1895,20 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        if (ret != ASN1_ERR_NOERROR) {
                                dissect_snmp_parse_error(tvb, offset, pinfo,
                                    snmp_tree, "encrypted PDU header", ret);
-                               return;
+                               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;
+                       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;
+                       return message_length;
                }
                offset += length;
                ret = asn1_octet_string_decode (&asn1, &cengineid,
@@ -1650,7 +1916,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "context engine id", ret);
-                       return;
+                       return message_length;
                }
                if (snmp_tree) {
                        proto_tree_add_text(snmp_tree, tvb, offset, length,
@@ -1664,12 +1930,12 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                if (ret != ASN1_ERR_NOERROR) {
                        dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                            "context name", ret);
-                       return;
+                       return message_length;
                }
                if (snmp_tree) {
                        proto_tree_add_text(snmp_tree, tvb, offset, length,
-                           "Context Name: %.*s", cname_length,
-                           SAFE_STRING(cname));
+                           "Context Name: %s",
+                           SAFE_STRING(cname, cname_length));
                }
                g_free(cname);
                offset += length;
@@ -1677,7 +1943,7 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        default:
                dissect_snmp_error(tvb, offset, pinfo, snmp_tree,
                    "PDU for unknown version of SNMP");
-               return;
+               return message_length;
        }
 
        start = asn1.offset;
@@ -1686,14 +1952,15 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (ret != ASN1_ERR_NOERROR) {
                dissect_snmp_parse_error(tvb, offset, pinfo, snmp_tree,
                    "PDU type", ret);
-               return;
+               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;
+               return message_length;
        }
        dissect_common_pdu(tvb, offset, pinfo, snmp_tree, asn1, pdu_type, start);
+       return message_length;
 }
 
 static void
@@ -1762,8 +2029,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "PDU type: %s", pdu_type_string);
+                       proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb,
+                           offset, length, pdu_type);
                }
                offset += length;
                ret = asn1_uint32_decode (&asn1, &version, &length);
@@ -1773,8 +2040,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        return;
                }
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "Version: %d", version);
+                       proto_tree_add_uint(smux_tree, hf_smux_version, tvb,
+                           offset, length, version);
                }
                offset += length;
 
@@ -1802,8 +2069,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                }
                if (tree) {
                        proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "Application: %.*s", application_length,
-                            SAFE_STRING(application));
+                           "Application: %s",
+                            SAFE_STRING(application, application_length));
                }
                g_free(application);
                offset += length;
@@ -1817,8 +2084,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                }
                if (tree) {
                        proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "Password: %.*s", password_length,
-                           SAFE_STRING(password));
+                           "Password: %s",
+                           SAFE_STRING(password, password_length));
                }
                g_free(password);
                offset += length;
@@ -1831,8 +2098,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "PDU type: %s", pdu_type_string);
+                       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);
@@ -1857,8 +2124,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "PDU type: %s", pdu_type_string);
+                       proto_tree_add_uint(smux_tree, hf_smux_pdutype, tvb,
+                           offset, length, pdu_type);
                }
                offset += length;
                ret = asn1_oid_decode (&asn1, &regid, &regid_length, &length);
@@ -1910,8 +2177,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "PDU type: %s", pdu_type_string);
+                       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);
@@ -1936,8 +2203,8 @@ dissect_smux_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
                        col_add_str(pinfo->cinfo, COL_INFO, pdu_type_string);
                length = asn1.offset - start;
                if (tree) {
-                       proto_tree_add_text(smux_tree, tvb, offset, length,
-                           "PDU type: %s", pdu_type_string);
+                       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);
@@ -1996,7 +2263,28 @@ dissect_snmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
          }
        }
 
-       dissect_snmp_pdu(tvb, 0, pinfo, tree, "SNMP", proto_snmp, ett_snmp);
+       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
@@ -2014,6 +2302,39 @@ proto_register_snmp(void)
 #endif
 
        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 }},
@@ -2026,16 +2347,16 @@ proto_register_snmp(void)
                { &hf_snmpv3_flags_report,
                { "Reportable", "snmpv3.flags.report", FT_BOOLEAN, 8,
                    TFS(&flags_set_truth), TH_REPORT, "", HFILL }},
-        };
+       };
        static gint *ett[] = {
                &ett_snmp,
-               &ett_smux,
                &ett_parameters,
                &ett_parameters_qos,
                &ett_global,
                &ett_flags,
                &ett_secur,
        };
+       module_t *snmp_module;
 
 #ifdef HAVE_SOME_SNMP
 
@@ -2069,26 +2390,70 @@ proto_register_snmp(void)
        init_mib();
        read_configs();
 #endif /* HAVE_SOME_SNMP */
-        proto_snmp = proto_register_protocol("Simple Network Management Protocol",
+       proto_snmp = proto_register_protocol("Simple Network Management Protocol",
            "SNMP", "snmp");
-        proto_smux = proto_register_protocol("SNMP Multiplex Protocol",
-           "SMUX", "smux");
-        proto_register_field_array(proto_snmp, hf, array_length(hf));
+       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, NULL);
+       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);
+       prefs_register_bool_preference(snmp_module, "desegment",
+           "Desegment all SNMP-over-TCP messages spanning 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 smux_handle;
+       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);
-       smux_handle = create_dissector_handle(dissect_smux, proto_smux);
-       dissector_add("tcp.port", TCP_PORT_SMUX, smux_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");
 }
+
+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);
+}