forked from osmocom/wireshark
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:
parent
a37ddb63b1
commit
2a148564d6
2
AUTHORS
2
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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
170
packet-rpc.c
170
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);
|
||||
}
|
||||
|
|
503
packet-tcp.c
503
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) {
|
||||
/*
|
||||
* 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
|
||||
|
|
19
reassemble.c
19
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;
|
||||
|
|
Loading…
Reference in New Issue