aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2001-09-13 07:56:53 +0000
committerGuy Harris <guy@alum.mit.edu>2001-09-13 07:56:53 +0000
commit2a148564d67964bef7eb5a2e8511fa431060850f (patch)
treefcf01dc61619d9bf3313007110cdf70e83dd7b6f
parenta37ddb63b14cc5b314fa476d79c6afc260416e2c (diff)
TCP desegmentation support, and changes to the ONC RPC and NBSS
dissectors to use it, from Ronnie Sahlberg, with additional changes to handle the case where a frame contains messages that don't run past the end followed by one that does and where a reassembled chunk has, at the end, a message that runs past the end of that chunk (because the reassembly was for an earlier message). svn path=/trunk/; revision=3923
-rw-r--r--AUTHORS2
-rw-r--r--epan/packet_info.h5
-rw-r--r--packet-nbns.c33
-rw-r--r--packet-rpc.c170
-rw-r--r--packet-tcp.c507
-rw-r--r--reassemble.c19
6 files changed, 673 insertions, 63 deletions
diff --git a/AUTHORS b/AUTHORS
index 2bb26db326..3641760615 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -549,6 +549,8 @@ Ronnie Sahlberg <rsahlber[AT]bigpond.net.au> {
Tvbuffified ISIS dissector
Tvbuffified SMB NETLOGON dissector
Tvbuffified SMB BROWSER dissector
+ TCP segment reassembly and support for it in ONC RPC and NBSS
+ dissectors
}
Borosa Tomislav <tomislav.borosa[AT]SIEMENS.HR> {
diff --git a/epan/packet_info.h b/epan/packet_info.h
index f236b7559a..a80bb6b4bb 100644
--- a/epan/packet_info.h
+++ b/epan/packet_info.h
@@ -1,7 +1,7 @@
/* packet_info.h
* Definitions for packet info structures and routines
*
- * $Id: packet_info.h,v 1.5 2001/08/04 04:04:35 guy Exp $
+ * $Id: packet_info.h,v 1.6 2001/09/13 07:53:53 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -108,6 +108,9 @@ typedef struct _packet_info {
guint32 srcport; /* source port */
guint32 destport; /* destination port */
guint32 match_port;
+ gboolean can_desegment; /* TRUE if this segment could be desegmented */
+ int desegment_offset; /* offset of stuff needing desegmentation */
+ guint32 desegment_len; /* requested desegmentation additional length */
int iplen;
int iphdrlen;
int p2p_dir;
diff --git a/packet-nbns.c b/packet-nbns.c
index 42e268d08d..48cf6d54f5 100644
--- a/packet-nbns.c
+++ b/packet-nbns.c
@@ -4,7 +4,7 @@
* Gilbert Ramirez <gram@xiexie.org>
* Much stuff added by Guy Harris <guy@alum.mit.edu>
*
- * $Id: packet-nbns.c,v 1.54 2001/08/05 10:00:35 guy Exp $
+ * $Id: packet-nbns.c,v 1.55 2001/09/13 07:53:51 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -41,6 +41,7 @@
#include "packet-dns.h"
#include "packet-netbios.h"
#include "packet-smb.h"
+#include "prefs.h"
static int proto_nbns = -1;
static int hf_nbns_response = -1;
@@ -78,6 +79,10 @@ static int hf_nbss_flags = -1;
static gint ett_nbss = -1;
static gint ett_nbss_flags = -1;
+/* desegmentation of NBSS over TCP */
+static gboolean nbss_desegment = FALSE;
+
+
/* See RFC 1001 and 1002 for information on the first three, and see
http://www.cifs.com/specs/draft-leach-cifs-v1-spec-01.txt
@@ -1393,6 +1398,25 @@ dissect_nbss_packet(tvbuff_t *tvb, int offset, packet_info *pinfo,
length += 65536;
}
+ /*Desegmentation */
+ if (nbss_desegment) {
+ if (pinfo->can_desegment
+ && length > tvb_length_remaining(tvb, offset+4)) {
+ /*
+ * This frame doesn't have all of the data for
+ * this message, but we can do reassembly on it.
+ *
+ * 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 =
+ length - tvb_length_remaining(tvb, offset+4);
+ return max_data;
+ }
+ }
+
if (tree) {
ti = proto_tree_add_item(tree, proto_nbss, tvb, offset, length + 4, FALSE);
nbss_tree = proto_item_add_subtree(ti, ett_nbss);
@@ -1661,6 +1685,7 @@ proto_register_nbt(void)
&ett_nbss,
&ett_nbss_flags,
};
+ module_t *nbss_module;
proto_nbns = proto_register_protocol("NetBIOS Name Service", "NBNS", "nbns");
proto_register_field_array(proto_nbns, hf_nbns, array_length(hf_nbns));
@@ -1674,6 +1699,12 @@ proto_register_nbt(void)
proto_register_field_array(proto_nbss, hf_nbss, array_length(hf_nbss));
proto_register_subtree_array(ett, array_length(ett));
+
+ nbss_module = prefs_register_protocol(proto_nbss, NULL);
+ prefs_register_bool_preference(nbss_module, "desegment_nbss_commands",
+ "Desegment all NBSS commands spanning multiple TCP segments",
+ "Whether NBSS dissector should desegment all commands spanning multiple TCP segments",
+ &nbss_desegment);
}
void
diff --git a/packet-rpc.c b/packet-rpc.c
index 1637de7ad2..dcd5412669 100644
--- a/packet-rpc.c
+++ b/packet-rpc.c
@@ -2,7 +2,7 @@
* Routines for rpc dissection
* Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
*
- * $Id: packet-rpc.c,v 1.70 2001/09/12 08:46:39 guy Exp $
+ * $Id: packet-rpc.c,v 1.71 2001/09/13 07:53:51 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -40,6 +40,7 @@
#include "packet.h"
#include "conversation.h"
#include "packet-rpc.h"
+#include "prefs.h"
/*
* See:
@@ -60,6 +61,9 @@
#define RPC_RM_FRAGLEN 0x7fffffffL
+/* desegmentation of RPC over TCP */
+static gboolean rpc_desegment = FALSE;
+
static struct true_false_string yesno = { "Yes", "No" };
@@ -218,6 +222,7 @@ typedef struct _rpc_prog_info_value {
} rpc_prog_info_value;
static void dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static void dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
/***********************************/
/* Hash array with procedure names */
@@ -1175,7 +1180,8 @@ dissect_rpc_indir_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
/* Make the dissector for this conversation the non-heuristic
RPC dissector. */
- conversation_set_dissector(conversation, dissect_rpc);
+ conversation_set_dissector(conversation,
+ (pinfo->ptype == PT_TCP) ? dissect_rpc_tcp : dissect_rpc);
/* Prepare the key data.
@@ -1384,9 +1390,9 @@ dissect_rpc_continuation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
static gboolean
-dissect_rpc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_rpc_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ proto_tree *tree, gboolean use_rm, guint32 rpc_rm)
{
- int offset = 0;
guint32 msg_type;
rpc_call_info_key rpc_call_key;
rpc_call_info_value *rpc_call = NULL;
@@ -1425,9 +1431,6 @@ dissect_rpc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree *ptree = NULL;
int offset_old = offset;
- int use_rm = 0;
- guint32 rpc_rm = 0;
-
rpc_call_info_key *new_rpc_call_key;
rpc_proc_info_key key;
rpc_proc_info_value *value = NULL;
@@ -1436,17 +1439,6 @@ dissect_rpc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
dissect_function_t *dissect_function = NULL;
- /* TCP uses record marking */
- use_rm = (pinfo->ptype == PT_TCP);
-
- /* the first 4 bytes are special in "record marking mode" */
- if (use_rm) {
- if (!tvb_bytes_exist(tvb, offset, 4))
- return FALSE;
- rpc_rm = tvb_get_ntohl(tvb, offset);
- offset += 4;
- }
-
/*
* Check to see whether this looks like an RPC call or reply.
*/
@@ -1723,7 +1715,8 @@ dissect_rpc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* Make the dissector for this conversation the non-heuristic
RPC dissector. */
- conversation_set_dissector(conversation, dissect_rpc);
+ conversation_set_dissector(conversation,
+ (pinfo->ptype == PT_TCP) ? dissect_rpc_tcp : dissect_rpc);
/* prepare the key data */
rpc_call_key.xid = xid;
@@ -2068,13 +2061,140 @@ dissect_rpc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
static gboolean
dissect_rpc_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- return dissect_rpc_common(tvb, pinfo, tree);
+ return dissect_rpc_message(tvb, 0, pinfo, tree, FALSE, 0);
}
static void
dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- if (!dissect_rpc_common(tvb, pinfo, tree))
+ if (!dissect_rpc_message(tvb, 0, pinfo, tree, FALSE, 0))
+ dissect_rpc_continuation(tvb, pinfo, tree);
+}
+
+/*
+ * Can return:
+ *
+ * NEED_MORE_DATA, if we don't have enough data to dissect anything;
+ *
+ * IS_RPC, if we dissected at least one message in its entirety
+ * as RPC;
+ *
+ * IS_NOT_RPC, if we found no RPC message.
+ */
+typedef enum {
+ NEED_MORE_DATA,
+ IS_RPC,
+ IS_NOT_RPC
+} rpc_tcp_return_t;
+
+static rpc_tcp_return_t
+dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gboolean is_heur)
+{
+ int offset = 0;
+ guint32 rpc_rm;
+ gboolean saw_rpc = FALSE;
+ gint32 len, seglen;
+ gint tvb_len, tvb_reported_len;
+ tvbuff_t *msg_tvb;
+
+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
+ /*
+ * XXX - we need to handle records that don't have the "last
+ * fragment" bit set, and reassemble fragments.
+ */
+
+ /* the first 4 bytes are special in "record marking mode" */
+ if (!tvb_bytes_exist(tvb, offset, 4)) {
+ /*
+ * XXX - we should somehow arrange to handle
+ * a record mark split across TCP segments.
+ */
+ return saw_rpc ? IS_RPC : IS_NOT_RPC;
+ }
+ rpc_rm = tvb_get_ntohl(tvb, offset);
+
+ len = rpc_rm&RPC_RM_FRAGLEN;
+
+ /*
+ * XXX - reject fragments bigger than 2 megabytes.
+ * This is arbitrary, but should at least prevent
+ * some crashes from either packets with really
+ * large RPC-over-TCP fragments or from stuff that's
+ * not really RPC.
+ */
+ if (len > 2*1024*1024)
+ return saw_rpc ? IS_RPC : IS_NOT_RPC;
+ if (rpc_desegment) {
+ seglen = tvb_length_remaining(tvb, offset + 4);
+
+ if (len > seglen && pinfo->can_desegment) {
+ /*
+ * This frame doesn't have all of the
+ * data for this message, but we can do
+ * reassembly on it.
+ *
+ * If this is a heuristic dissector, just
+ * return IS_NOT_RPC - we don't want to try
+ * to get more data, as that's too likely
+ * to cause us to misidentify this as
+ * RPC.
+ *
+ * If this isn't a heuristic dissector,
+ * we've already identified this conversation
+ * as containing RPC data, as we saw RPC
+ * data in previous frames. Try to get
+ * more data.
+ */
+ if (is_heur)
+ return IS_NOT_RPC;
+ else {
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = len - seglen;
+ return NEED_MORE_DATA;
+ }
+ }
+ }
+ len += 4; /* include record mark */
+ tvb_len = tvb_length_remaining(tvb, offset);
+ tvb_reported_len = tvb_reported_length_remaining(tvb, offset);
+ if (tvb_len > len)
+ tvb_len = len;
+ if (tvb_reported_len > len)
+ tvb_reported_len = len;
+ msg_tvb = tvb_new_subset(tvb, offset, tvb_len,
+ tvb_reported_len);
+ if (!dissect_rpc_message(msg_tvb, 4, pinfo, tree,
+ TRUE, rpc_rm))
+ break;
+ offset += len;
+ saw_rpc = TRUE;
+ }
+ return saw_rpc ? IS_RPC : IS_NOT_RPC;
+}
+
+static gboolean
+dissect_rpc_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ switch (dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE)) {
+
+ case IS_RPC:
+ return TRUE;
+
+ case IS_NOT_RPC:
+ return FALSE;
+
+ default:
+ /* "Can't happen" */
+ g_assert_not_reached();
+ return FALSE;
+ }
+}
+
+static void
+dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ if (dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE) == IS_NOT_RPC)
dissect_rpc_continuation(tvb, pinfo, tree);
}
@@ -2259,12 +2379,18 @@ proto_register_rpc(void)
&ett_rpc_gss_data,
&ett_rpc_array,
};
+ module_t *rpc_module;
proto_rpc = proto_register_protocol("Remote Procedure Call",
"RPC", "rpc");
proto_register_field_array(proto_rpc, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
register_init_routine(&rpc_init_protocol);
+ rpc_module = prefs_register_protocol(proto_rpc, NULL);
+ prefs_register_bool_preference(rpc_module, "desegment_rpc_over_tcp",
+ "Desegment all RPC over TCP commands",
+ "Whether the RPC dissector should desegment all RPC over TCP commands",
+ &rpc_desegment);
/*
* Init the hash tables. Dissectors for RPC protocols must
@@ -2284,6 +2410,6 @@ proto_register_rpc(void)
void
proto_reg_handoff_rpc(void)
{
- heur_dissector_add("tcp", dissect_rpc_heur, proto_rpc);
+ heur_dissector_add("tcp", dissect_rpc_tcp_heur, proto_rpc);
heur_dissector_add("udp", dissect_rpc_heur, proto_rpc);
}
diff --git a/packet-tcp.c b/packet-tcp.c
index 547876df75..35cb968a89 100644
--- a/packet-tcp.c
+++ b/packet-tcp.c
@@ -1,7 +1,7 @@
/* packet-tcp.c
* Routines for TCP packet disassembly
*
- * $Id: packet-tcp.c,v 1.106 2001/09/03 17:57:17 guy Exp $
+ * $Id: packet-tcp.c,v 1.107 2001/09/13 07:53:51 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -51,6 +51,7 @@
#include "packet-ip.h"
#include "conversation.h"
#include "strutil.h"
+#include "reassemble.h"
/* Place TCP summary in proto tree */
static gboolean tcp_summary_in_tree = TRUE;
@@ -85,6 +86,7 @@ static gint ett_tcp = -1;
static gint ett_tcp_flags = -1;
static gint ett_tcp_options = -1;
static gint ett_tcp_option_sack = -1;
+static gint ett_tcp_segments = -1;
static dissector_table_t subdissector_table;
static heur_dissector_list_t heur_subdissector_list;
@@ -149,6 +151,410 @@ typedef struct _e_tcphdr {
#define TCPOLEN_CCECHO 6
#define TCPOLEN_MD5 18
+
+
+/* Desegmentation of TCP streams */
+/* table to hold defragmented TCP streams */
+static GHashTable *tcp_fragment_table = NULL;
+static void
+tcp_fragment_init(void)
+{
+ fragment_table_init(&tcp_fragment_table);
+}
+
+/* functions to trace tcp segments */
+/* Enable desegmenting of TCP streams */
+static gboolean tcp_desegment = FALSE;
+
+static GHashTable *tcp_segment_table = NULL;
+static GMemChunk *tcp_segment_key_chunk = NULL;
+static int tcp_segment_init_count = 200;
+
+typedef struct _tcp_segment_key {
+ /* for ouwn bookkeeping inside packet-tcp.c */
+ address *src;
+ address *dst;
+ guint32 seq;
+ /* xxx */
+ guint32 start_seq;
+ guint32 tot_len;
+ guint32 first_frame;
+} tcp_segment_key;
+
+static gboolean
+free_all_segments(gpointer key_arg, gpointer value, gpointer user_data)
+{
+ tcp_segment_key *key = key_arg;
+
+ if((key->src)&&(key->src->data)){
+ g_free((gpointer)key->src->data);
+ key->src->data=NULL;
+ g_free((gpointer)key->src);
+ key->src=NULL;
+ }
+ if((key->dst)&&(key->dst->data)){
+ g_free((gpointer)key->dst->data);
+ key->dst->data=NULL;
+ g_free((gpointer)key->dst);
+ key->dst=NULL;
+ }
+
+ return TRUE;
+}
+
+static guint
+tcp_segment_hash(gconstpointer k)
+{
+ tcp_segment_key *key = (tcp_segment_key *)k;
+
+ return key->seq;
+}
+
+static gint
+tcp_segment_equal(gconstpointer k1, gconstpointer k2)
+{
+ tcp_segment_key *key1 = (tcp_segment_key *)k1;
+ tcp_segment_key *key2 = (tcp_segment_key *)k2;
+
+ return ( ( (key1->seq==key2->seq)
+ &&(ADDRESSES_EQUAL(key1->src, key2->src))
+ &&(ADDRESSES_EQUAL(key1->dst, key2->dst))
+ ) ? TRUE:FALSE);
+}
+
+static void
+tcp_desegment_init(void)
+{
+
+ /* dont allocate any memory chunks unless the user really
+ uses this option
+ */
+ if(!tcp_desegment){
+ return;
+ }
+
+ if(tcp_segment_table){
+ g_hash_table_foreach_remove(tcp_segment_table,
+ free_all_segments, NULL);
+ } else {
+ tcp_segment_table = g_hash_table_new(tcp_segment_hash,
+ tcp_segment_equal);
+ }
+
+ if(tcp_segment_key_chunk){
+ g_mem_chunk_destroy(tcp_segment_key_chunk);
+ }
+ tcp_segment_key_chunk = g_mem_chunk_new("tcp_segment_key_chunk",
+ sizeof(tcp_segment_key),
+ tcp_segment_init_count*sizeof(tcp_segment_key),
+ G_ALLOC_ONLY);
+}
+
+static void
+desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ guint32 seq, guint32 nxtseq,
+ guint32 sport, guint32 dport,
+ proto_tree *tree, proto_tree *tcp_tree)
+{
+ fragment_data *ipfd_head;
+ tcp_segment_key old_tsk, *tsk;
+ gboolean must_desegment = FALSE;
+ gboolean called_dissector = FALSE;
+ int deseg_offset;
+
+ /*
+ * Initialize these to assume no desegmentation.
+ * If that's not the case, these will be set appropriately
+ * by the subdissector.
+ */
+ pinfo->desegment_offset = 0;
+ pinfo->desegment_len = 0;
+
+ /*
+ * Initialize this to assume that this segment will just be
+ * added to the middle of a desegmented chunk of data, so
+ * that we should show it all as data.
+ * If that's not the case, it will be set appropriately.
+ */
+ deseg_offset = offset;
+
+ /* First we must check if this TCP segment should be desegmented.
+ This is only to check if we should desegment this packet,
+ so we dont spend time doing COPY_ADDRESS/g_free.
+ We just "borrow" some address structures from pinfo instead. Cheaper.
+ */
+ old_tsk.src = &pinfo->src;
+ old_tsk.dst = &pinfo->dst;
+ old_tsk.seq = seq;
+ tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
+
+ if(tsk){
+ /* OK, this segment was found, which means it continues
+ a higher-level PDU. This means we must desegment it.
+ Add it to the defragmentation lists.
+ */
+ ipfd_head = fragment_add(tvb, offset, pinfo, tsk->start_seq,
+ tcp_fragment_table,
+ seq - tsk->start_seq,
+ nxtseq - seq,
+ (nxtseq < (tsk->start_seq + tsk->tot_len)) );
+
+ if(!ipfd_head){
+ /* fragment_add() returned NULL, This means that
+ desegmentation is not completed yet.
+ (its like defragmentation but we know we will
+ always add the segments in order).
+ XXX - no, we don't; there is no guarantee that
+ TCP segments are in order on the wire.
+
+ we must add next segment to our table so we will
+ find it later.
+ */
+ tcp_segment_key *new_tsk;
+
+ new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
+ memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
+ new_tsk->seq=nxtseq;
+ g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+ }
+ } else {
+ /* This segment was not found in our table, so it doesn't
+ contain a continuation of a higher-level PDU.
+ Call the normal subdissector.
+ */
+ decode_tcp_ports(tvb, offset, pinfo, tree,
+ sport, dport);
+ called_dissector = TRUE;
+
+ /*
+ * Advance the offset to the first byte that the
+ * subdissector didn't process.
+ */
+ offset += pinfo->desegment_offset;
+
+ /* Did the subdissector ask us to desegment some more data
+ before it could handle the packet?
+ If so we have to create some structures in our table but
+ this is something we only do the first time we see this
+ packet.
+ */
+ if(pinfo->desegment_len) {
+ if (!pinfo->fd->flags.visited)
+ must_desegment = TRUE;
+
+ /*
+ * Set "deseg_offset" to the offset in "tvb"
+ * of the first byte of data that the
+ * subdissector didn't process.
+ */
+ deseg_offset = offset;
+ }
+
+ /* Either no desegmentation is necessary, or this is
+ segment contains the beginning but not the end of
+ a higher-level PDU and thus isn't completely
+ desegmented.
+ */
+ ipfd_head = NULL;
+ }
+
+ /* is it completely desegmented? */
+ if(ipfd_head){
+ fragment_data *ipfd;
+ proto_tree *st = NULL;
+ proto_item *si = NULL;
+
+ /* first we show a tree with all segments */
+ si = proto_tree_add_text(tcp_tree, tvb, 0, 0,
+ "Segments");
+ st = proto_item_add_subtree(si, ett_tcp_segments);
+ for(ipfd=ipfd_head->next; ipfd; ipfd=ipfd->next){
+ proto_tree_add_text(st, tvb, 0, 0,
+ "Frame:%d seq#:%d-%d [%d-%d]",
+ ipfd->frame,
+ tsk->start_seq + ipfd->offset,
+ tsk->start_seq + ipfd->offset + ipfd->len - 1,
+ ipfd->offset,
+ ipfd->offset + ipfd->len - 1);
+ }
+
+ /*
+ * We only call subdissector for the last segment.
+ * Note that the last segment may include more than what
+ * we needed.
+ */
+ if(nxtseq >= (tsk->start_seq + tsk->tot_len)){
+ /* ok, lest call subdissector with desegmented data */
+ packet_info save_pi;
+ tvbuff_t *next_tvb;
+
+ /* create a new TVB structure for desegmented data */
+ next_tvb = tvb_new_real_data(ipfd_head->data,
+ ipfd_head->datalen, ipfd_head->datalen,
+ "Desegmented");
+
+ /* add this tvb as a child to the original one */
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+ /* add desegmented data to the data source list */
+ pinfo->fd->data_src = g_slist_append(pinfo->fd->data_src, next_tvb);
+
+ /* save current value of *pinfo across call to
+ dissector */
+ save_pi = *pinfo;
+ pinfo->compat_top_tvb = next_tvb;
+ pinfo->len = tvb_reported_length(next_tvb);
+ pinfo->captured_len = tvb_length(next_tvb);
+
+ /* call subdissector */
+ decode_tcp_ports(next_tvb, 0, pinfo, tree,
+ sport, dport);
+ called_dissector = TRUE;
+
+ /*
+ * Don't trash the new values of "desegment_offset"
+ * and "desegment_len".
+ */
+ save_pi.desegment_offset = pinfo->desegment_offset;
+ save_pi.desegment_len = pinfo->desegment_len;
+ *pinfo = save_pi;
+
+ /* Did the subdissector ask us to desegment some more
+ data? This means that the data at the beginning
+ of this segment completed a higher-level PDU,
+ but the data at the end of this segment started
+ a higher-level PDU but didn't complete it.
+
+ If so we have to create some structures in our
+ table but this is something we only do the first
+ time we see this packet.
+ */
+ if(pinfo->desegment_len) {
+ if (!pinfo->fd->flags.visited)
+ must_desegment = TRUE;
+
+ /*
+ * The stuff we couldn't dissect must have
+ * come from this segment, so it's all in
+ * "tvb".
+ *
+ * "pinfo->desegment_offset" is relative
+ * to the beginning of "next_tvb";
+ * we want an offset relative to the
+ * beginning of "tvb".
+ *
+ * First, compute the offset relative to
+ * the *end* of "next_tvb" - i.e., the number
+ * of bytes before the end of "next_tvb"
+ * at which the subdissector stopped.
+ * That's the length of "next_tvb" minus
+ * the offset, relative to the beginning
+ * of "next_tvb, at which the subdissector
+ * stopped.
+ */
+ deseg_offset =
+ ipfd_head->datalen - pinfo->desegment_offset;
+
+ /*
+ * "tvb" and "next_tvb" end at the same byte
+ * of data, so the offset relative to the
+ * end of "next_tvb" of the byte at which
+ * we stopped is also the offset relative
+ * to the end of "tvb" of the byte at which
+ * we stopped.
+ *
+ * Convert that back into an offset relative
+ * to the beginninng of "tvb", by taking
+ * the length of "tvb" and subtracting the
+ * offset relative to the end.
+ */
+ deseg_offset = tvb_length(tvb) - deseg_offset;
+ }
+ }
+ }
+
+ if (must_desegment) {
+ tcp_segment_key *tsk, *new_tsk;
+
+ /*
+ * XXX - how do we detect out-of-order transmissions?
+ * We can't just check for "nxtseq" being greater than
+ * "tsk->start_seq"; for now, we check for the difference
+ * being less than a megabyte, but this is a really
+ * gross hack - we really need to handle out-of-order
+ * transmissions correctly.
+ */
+ if ((nxtseq - (seq + pinfo->desegment_offset)) <= 1024*1024) {
+ /* OK, subdissector wants us to desegment
+ some data before it can process it. Add
+ what remains of this packet and set
+ up next packet/sequence number as well.
+
+ We must remember this segment
+ */
+ tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
+ tsk->src = g_malloc(sizeof(address));
+ COPY_ADDRESS(tsk->src, &pinfo->src);
+ tsk->dst = g_malloc(sizeof(address));
+ COPY_ADDRESS(tsk->dst, &pinfo->dst);
+ tsk->seq = seq + pinfo->desegment_offset;
+ tsk->start_seq = tsk->seq;
+ tsk->tot_len = nxtseq - tsk->start_seq + pinfo->desegment_len;
+ tsk->first_frame = pinfo->fd->num;
+ g_hash_table_insert(tcp_segment_table, tsk, tsk);
+
+ /* Add portion of segment unprocessed by the subdissector
+ to defragmentation lists */
+ fragment_add(tvb, deseg_offset, pinfo, tsk->start_seq,
+ tcp_fragment_table,
+ tsk->seq - tsk->start_seq,
+ nxtseq - tsk->start_seq,
+ (nxtseq < tsk->start_seq + tsk->tot_len));
+
+ /* this is the next segment in the sequence we want */
+ new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
+ memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
+ new_tsk->seq = nxtseq;
+ g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+ }
+ }
+
+ if (!called_dissector || pinfo->desegment_len != 0) {
+ /*
+ * Either we didn't call the subdissector at all (i.e.,
+ * this is a segment that contains the middle of a
+ * higher-level PDU, but contains neither the beginning
+ * nor the end), or the subdissector couldn't dissect it
+ * all, as some data was missing (i.e., it set
+ * "pinfo->desegment_len" to the amount of additional
+ * data it needs).
+ */
+ if (pinfo->desegment_offset == 0) {
+ /*
+ * It couldn't, in fact, dissect any of it (the
+ * first byte it couldn't dissect is at an offset
+ * of "pinfo->desegment_offset" from the beginning
+ * of the payload, and that's 0).
+ * Just mark this as TCP.
+ */
+ if (check_col(pinfo->fd, COL_PROTOCOL)){
+ col_set_str(pinfo->fd, COL_PROTOCOL, "TCP");
+ }
+ if (check_col(pinfo->fd, COL_INFO)){
+ col_set_str(pinfo->fd, COL_INFO, "[Desegmented TCP]");
+ }
+ }
+
+ /*
+ * Show what's left in the packet as data.
+ */
+ dissect_data(tvb, deseg_offset, pinfo, tree);
+ }
+}
+
+
+
+
static void
tcp_info_append_uint(frame_data *fd, const char *abbrev, guint32 val)
{
@@ -537,57 +943,72 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, th.th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, th.th_flags);
proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, th.th_win);
- if (!pinfo->fragmented && len >= reported_len) {
- /* The packet isn't part of a fragmented datagram and isn't
- truncated, so we can checksum it.
- XXX - make a bigger scatter-gather list once we do fragment
- reassembly? */
-
- /* Set up the fields of the pseudo-header. */
- cksum_vec[0].ptr = pinfo->src.data;
- cksum_vec[0].len = pinfo->src.len;
- cksum_vec[1].ptr = pinfo->dst.data;
- cksum_vec[1].len = pinfo->dst.len;
- cksum_vec[2].ptr = (const guint8 *)&phdr;
- switch (pinfo->src.type) {
-
- case AT_IPv4:
+ }
+
+ /*
+ * Assume, initially, that we can't desegment.
+ */
+ pinfo->can_desegment = FALSE;
+
+ if (!pinfo->fragmented && len >= reported_len) {
+ /* The packet isn't part of a fragmented datagram and isn't
+ truncated, so we can checksum it.
+ XXX - make a bigger scatter-gather list once we do fragment
+ reassembly? */
+
+ /* Set up the fields of the pseudo-header. */
+ cksum_vec[0].ptr = pinfo->src.data;
+ cksum_vec[0].len = pinfo->src.len;
+ cksum_vec[1].ptr = pinfo->dst.data;
+ cksum_vec[1].len = pinfo->dst.len;
+ cksum_vec[2].ptr = (const guint8 *)&phdr;
+ switch (pinfo->src.type) {
+
+ case AT_IPv4:
phdr[0] = htonl((IP_PROTO_TCP<<16) + reported_len);
cksum_vec[2].len = 4;
break;
- case AT_IPv6:
+ case AT_IPv6:
phdr[0] = htonl(reported_len);
phdr[1] = htonl(IP_PROTO_TCP);
cksum_vec[2].len = 8;
break;
- default:
+ default:
/* TCP runs only atop IPv4 and IPv6.... */
g_assert_not_reached();
break;
+ }
+ cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, len);
+ cksum_vec[3].len = reported_len;
+ computed_cksum = in_cksum(&cksum_vec[0], 4);
+ if (computed_cksum == 0) {
+ /*
+ * We have all the data for this TCP segment, and the checksum of
+ * the header and the data is good, so we can desegment it.
+ * Is desegmentation enabled?
+ */
+ if (tcp_desegment) {
+ /* Yes - indicate that we will desegment. */
+ pinfo->can_desegment = TRUE;
}
- cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, len);
- cksum_vec[3].len = reported_len;
- computed_cksum = in_cksum(&cksum_vec[0], 4);
- if (computed_cksum == 0) {
- proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
- offset + 16, 2, th.th_sum, "Checksum: 0x%04x (correct)", th.th_sum);
- } else {
- proto_tree_add_boolean_hidden(tcp_tree, hf_tcp_checksum_bad, tvb,
+ proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
+ offset + 16, 2, th.th_sum, "Checksum: 0x%04x (correct)", th.th_sum);
+ } else {
+ proto_tree_add_boolean_hidden(tcp_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, TRUE);
- proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
+ proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th.th_sum,
"Checksum: 0x%04x (incorrect, should be 0x%04x)", th.th_sum,
in_cksum_shouldbe(th.th_sum, computed_cksum));
- }
- } else {
- proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
- offset + 16, 2, th.th_sum, "Checksum: 0x%04x", th.th_sum);
}
- if (th.th_flags & TH_URG)
- proto_tree_add_uint(tcp_tree, hf_tcp_urgent_pointer, tvb, offset + 18, 2, th.th_urp);
+ } else {
+ proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
+ offset + 16, 2, th.th_sum, "Checksum: 0x%04x", th.th_sum);
}
+ if (th.th_flags & TH_URG)
+ proto_tree_add_uint(tcp_tree, hf_tcp_urgent_pointer, tvb, offset + 18, 2, th.th_urp);
/* Decode TCP options, if any. */
if (tree && hlen > sizeof (e_tcphdr)) {
@@ -631,8 +1052,16 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_text(tcp_tree, tvb, offset, length_remaining,
"Reset cause: %s",
tvb_format_text(tvb, offset, length_remaining));
- } else
- decode_tcp_ports( tvb, offset, pinfo, tree, th.th_sport, th.th_dport);
+ } else {
+ /* Can we desegment this segment? */
+ if (pinfo->can_desegment) {
+ /* Yes. */
+ desegment_tcp(tvb, pinfo, offset, th.th_seq, nxtseq, th.th_sport, th.th_dport, tree, tcp_tree);
+ } else {
+ /* No - just call the subdissector. */
+ decode_tcp_ports(tvb, offset, pinfo, tree, th.th_sport, th.th_dport);
+ }
+ }
}
if( data_out_file ) {
@@ -738,6 +1167,7 @@ proto_register_tcp(void)
&ett_tcp_flags,
&ett_tcp_options,
&ett_tcp_option_sack,
+ &ett_tcp_segments,
};
module_t *tcp_module;
@@ -757,6 +1187,13 @@ proto_register_tcp(void)
"Show TCP summary in protocol tree",
"Whether the TCP summary line should be shown in the protocol tree",
&tcp_summary_in_tree);
+ prefs_register_bool_preference(tcp_module, "desegment_tcp_streams",
+ "Allow subdissector to desegment TCP streams",
+"Whether subdissector can request TCP streams to be desegmented",
+ &tcp_desegment);
+
+ register_init_routine(tcp_desegment_init);
+ register_init_routine(tcp_fragment_init);
}
void
diff --git a/reassemble.c b/reassemble.c
index 82ff5fa98b..42301572e7 100644
--- a/reassemble.c
+++ b/reassemble.c
@@ -1,7 +1,7 @@
/* reassemble.c
* Routines for {fragment,segment} reassembly
*
- * $Id: reassemble.c,v 1.2 2001/06/28 19:15:11 guy Exp $
+ * $Id: reassemble.c,v 1.3 2001/09/13 07:53:52 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -60,9 +60,13 @@ fragment_equal(gconstpointer k1, gconstpointer k2)
fragment_key* key1 = (fragment_key*) k1;
fragment_key* key2 = (fragment_key*) k2;
- return ( ( (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
- (ADDRESSES_EQUAL(&key1->dst, &key2->dst)) &&
- (key1->id == key2->id)
+ /*key.id is the first item to compare since item is most
+ likely to differ between sessions, thus shortcircuiting
+ the comparasion of addresses.
+ */
+ return ( ( (key1->id == key2->id) &&
+ (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
+ (ADDRESSES_EQUAL(&key1->dst, &key2->dst))
) ?
TRUE : FALSE);
}
@@ -75,10 +79,17 @@ fragment_hash(gconstpointer k)
int i;
hash_val = 0;
+
+/* More than likely: in most captures src and dst addresses are the
+ same, and would hash the same.
+ We only use id as the hash as an optimization.
+
for (i = 0; i < key->src.len; i++)
hash_val += key->src.data[i];
for (i = 0; i < key->dst.len; i++)
hash_val += key->dst.data[i];
+*/
+
hash_val += key->id;
return hash_val;