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
This commit is contained in:
Guy Harris 2001-09-13 07:56:53 +00:00
parent a37ddb63b1
commit 2a148564d6
6 changed files with 671 additions and 61 deletions

View File

@ -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> {

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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) {
/*
* Assume, initially, that we can't desegment.
*/
pinfo->can_desegment = FALSE;
case AT_IPv4:
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

View File

@ -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;