Update to TCP to handle hints from dissectors where the next PDU may start.

ONCRPC dissector updated to provide hint to TCP where the next RPCoverTCP
PDU starts as example.
Trivial updates to the other TCP based protocols required to amke them handle
this as well.  See the updates to packet-rpc.c as an example.

This is enabled by activating tcp analysis and provides hints to TCP to know where PDUs starts when not aligned to the start of the segment.

svn path=/trunk/; revision=7543
This commit is contained in:
Ronnie Sahlberg 2003-04-23 10:20:29 +00:00
parent 1b872b3648
commit cb5e97d49a
6 changed files with 299 additions and 48 deletions

View File

@ -1,7 +1,7 @@
/* packet.c
* Routines for packet disassembly
*
* $Id: packet.c,v 1.90 2003/04/16 05:55:39 guy Exp $
* $Id: packet.c,v 1.91 2003/04/23 10:20:27 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -287,6 +287,7 @@ dissect_packet(epan_dissect_t *edt, union wtap_pseudo_header *pseudo_header,
edt->pi.destport = 0;
edt->pi.match_port = 0;
edt->pi.can_desegment = 0;
edt->pi.want_pdu_tracking = 0;
edt->pi.p2p_dir = P2P_DIR_UNKNOWN;
edt->pi.private_data = NULL;
edt->pi.oxid = 0;
@ -487,6 +488,7 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb,
pinfo->net_dst = save_net_dst;
pinfo->src = save_src;
pinfo->dst = save_dst;
pinfo->want_pdu_tracking = 0;
} else {
/*
* Just call the subdissector.
@ -495,6 +497,7 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb,
}
pinfo->current_proto = saved_proto;
pinfo->can_desegment = saved_can_desegment;
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return ret;
}
@ -1073,6 +1076,7 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors,
}
pinfo->current_proto = saved_proto;
pinfo->can_desegment=saved_can_desegment;
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return status;
}

View File

@ -1,7 +1,7 @@
/* packet_info.h
* Definitions for packet info structures and routines
*
* $Id: packet_info.h,v 1.31 2003/02/28 20:30:06 guy Exp $
* $Id: packet_info.h,v 1.32 2003/04/23 10:20:27 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -154,8 +154,34 @@ typedef struct _packet_info {
decremented by 1 each time we pass to the next
subdissector. Thus only the dissector immediately
above the protocol which sets the flag can use it*/
int desegment_offset; /* offset of stuff needing desegmentation */
int desegment_offset; /* offset to stuff needing desegmentation */
guint32 desegment_len; /* requested desegmentation additional length */
guint16 want_pdu_tracking; /* >0 if the subdissector has specified
a value in 'bytes_until_next_pdu'.
When a dissector detects that the next PDU
will start beyond the start of the next
segment, it can set this value to 2
and 'bytes_until_next_pdu' to the number of
bytes beyond the next segment where the
next PDU starts.
If the protocol dissector below this
one is capable of PDU tracking it can
use this hint to detect PDUs that starts
unaligned to the segment boundaries.
The TCP dissector is using this hint from
(some) protocols to detect when a new PDU
starts in the middle of a tcp segment.
There is intelligence in the glue between
dissector layers to make sure that this
request is only passed down to the protocol
immediately below the current one and not
any further.
*/
guint32 bytes_until_next_pdu;
int iplen;
int iphdrlen;
int p2p_dir;

View File

@ -2,7 +2,7 @@
* Routines for Microsoft Proxy packet dissection
* Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
*
* $Id: packet-msproxy.c,v 1.32 2002/08/28 21:00:22 jmayer Exp $
* $Id: packet-msproxy.c,v 1.33 2003/04/23 10:20:29 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -245,7 +245,7 @@ static void msproxy_sub_dissector( tvbuff_t *tvb, packet_info *pinfo,
if ( redirect_info->proto == PT_TCP)
decode_tcp_ports( tvb, 0, pinfo, tree, pinfo->srcport,
pinfo->destport);
pinfo->destport, 0);
else
decode_udp_ports( tvb, 0, pinfo, tree, pinfo->srcport,
pinfo->destport);

View File

@ -2,7 +2,7 @@
* Routines for socks versions 4 &5 packet dissection
* Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
*
* $Id: packet-socks.c,v 1.44 2003/02/26 01:35:07 gerald Exp $
* $Id: packet-socks.c,v 1.45 2003/04/23 10:20:29 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -945,7 +945,7 @@ static void call_next_dissector(tvbuff_t *tvb, int offset, packet_info *pinfo,
ptr = &pinfo->srcport;
*ptr = hash_info->port;
decode_tcp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport);
decode_tcp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport, 0);
*ptr = TCP_PORT_SOCKS;
}
}

View File

@ -1,7 +1,7 @@
/* packet-tcp.c
* Routines for TCP packet disassembly
*
* $Id: packet-tcp.c,v 1.190 2003/04/20 11:36:16 guy Exp $
* $Id: packet-tcp.c,v 1.191 2003/04/23 10:20:29 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -234,8 +234,199 @@ struct tcp_analysis {
guint32 base_seq1;
struct tcp_unacked *ual2; /* UnAcked List 2*/
guint32 base_seq2;
/* these two lists are used to track when PDUs may start
inside a segment.
*/
struct tcp_next_pdu *pdu_seq1;
struct tcp_next_pdu *pdu_seq2;
};
static GMemChunk *tcp_next_pdu_chunk = NULL;
static int tcp_next_pdu_count = 20;
struct tcp_next_pdu {
struct tcp_next_pdu *next;
guint32 seq;
};
static GHashTable *tcp_pdu_tracking_table = NULL;
static struct tcp_analysis *
get_tcp_conversation_data(packet_info *pinfo)
{
conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
/* Have we seen this conversation before? */
if( (conv=find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0)) == NULL){
/* No this is a new conversation. */
conv=conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
}
/* check if we have any data for this conversation */
tcpd=conversation_get_proto_data(conv, proto_tcp);
if(!tcpd){
/* No no such data yet. Allocate and init it */
tcpd=g_mem_chunk_alloc(tcp_analysis_chunk);
tcpd->ual1=NULL;
tcpd->base_seq1=0;
tcpd->ual2=NULL;
tcpd->base_seq2=0;
tcpd->pdu_seq1=NULL;
tcpd->pdu_seq2=NULL;
conversation_add_proto_data(conv, proto_tcp, tcpd);
}
return tcpd;
}
/* This function is called from the tcp analysis code to provide
clues on how the seq and ack numbers are changed.
To prevent the next_pdu lists from growing uncontrollable in size we
use this function to do the following :
IF we see an ACK then we assume that the left edge of the window has changed
at least to this point and assuming it is rare with reordering and
trailing duplicate/retransmitted segments, we just assume that after
we have seen the ACK we will not see any more segments prior to the
ACK value.
If we will not see any segments prior to the ACK value then we can just
delete all next_pdu entries that describe pdu's starting prior to the
ACK.
If this heuristics is prooved to be too simplistic we can just enhance it
later.
*/
/* XXX this function should be ehnanced to handle sequence number wrapping */
/* XXX to handle retransmissions and reordered packets maybe we should only
discard entries that are more than (guesstimate) 50kb older than the
specified sequence number ?
*/
static void
prune_next_pdu_list(struct tcp_next_pdu **tnp, guint32 seq)
{
struct tcp_next_pdu *tmptnp;
if(*tnp == NULL){
return;
}
for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
if(tmptnp->seq<=seq){
struct tcp_next_pdu *oldtnp;
oldtnp=tmptnp;
if(tmptnp==*tnp){
tmptnp=tmptnp->next;
*tnp=tmptnp;
g_mem_chunk_free(tcp_next_pdu_chunk, oldtnp);
if(!tmptnp){
return;
}
continue;
} else {
for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
if(tmptnp->next==oldtnp){
tmptnp->next=oldtnp->next;
g_mem_chunk_free(tcp_next_pdu_chunk, oldtnp);
break;
}
}
if(!tmptnp){
return;
}
}
}
}
}
/* if we know that a PDU starts inside this segment, return the adjusted
offset to where that PDU starts or just return offset back
and let TCP try to find out what it can about this segment
*/
static int
scan_for_next_pdu(packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq)
{
struct tcp_analysis *tcpd=NULL;
struct tcp_next_pdu *tnp=NULL;
int direction;
if(!pinfo->fd->flags.visited){
/* find(or create if needed) the conversation for this tcp session */
tcpd=get_tcp_conversation_data(pinfo);
/* check direction and get pdu start lists */
direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
/* if the addresses are equal, match the ports instead */
if(direction==0) {
direction= (pinfo->srcport > pinfo->destport)*2-1;
}
if(direction>=0){
tnp=tcpd->pdu_seq1;
} else {
tnp=tcpd->pdu_seq2;
}
/* scan and see if we find any pdus starting inside this tvb */
for(;tnp;tnp=tnp->next){
/* XXX here we should also try to handle sequence number
wrapping
*/
if(seq<tnp->seq && nxtseq>tnp->seq){
g_hash_table_insert(tcp_pdu_tracking_table,
(void *)pinfo->fd->num, (void *)tnp->seq);
offset+=tnp->seq-seq;
break;
}
}
} else {
guint32 pduseq;
pduseq=(guint32)g_hash_table_lookup(tcp_pdu_tracking_table, (void *)pinfo->fd->num);
if(pduseq){
offset+=pduseq-seq;
}
}
return offset;
}
/* if we saw a PDU that extended beyond the end of the segment,
use this function to remember where the next pdu starts
*/
static void
pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 nxtpdu)
{
struct tcp_analysis *tcpd=NULL;
struct tcp_next_pdu *tnp=NULL;
int direction;
/* find(or create if needed) the conversation for this tcp session */
tcpd=get_tcp_conversation_data(pinfo);
tnp=g_mem_chunk_alloc(tcp_next_pdu_chunk);
tnp->seq=nxtpdu;
/* check direction and get pdu start list */
direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
/* if the addresses are equal, match the ports instead */
if(direction==0) {
direction= (pinfo->srcport > pinfo->destport)*2-1;
}
if(direction>=0){
tnp->next=tcpd->pdu_seq1;
tcpd->pdu_seq1=tnp;
} else {
tnp->next=tcpd->pdu_seq2;
tcpd->pdu_seq2=tnp;
}
/*QQQ
Add check for ACKs and purge list of sequence numbers
already acked.
*/
}
static void
tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
{
@ -272,32 +463,17 @@ tcp_analyze_get_acked_struct(guint32 frame, gboolean createflag)
static void
tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint8 flags, guint16 window)
{
conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
int direction;
struct tcp_unacked *ual1=NULL;
struct tcp_unacked *ual2=NULL;
struct tcp_unacked *ual=NULL;
guint32 base_seq;
guint32 base_ack;
guint32 base_seq=0;
guint32 base_ack=0;
struct tcp_next_pdu **tnp=NULL;
/* Have we seen this conversation before? */
if( (conv=find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0)) == NULL){
/* No this is a new conversation. */
conv=conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
}
/* check if we have any data for this conversation */
tcpd=conversation_get_proto_data(conv, proto_tcp);
if(!tcpd){
/* No no such data yet. Allocate and init it */
tcpd=g_mem_chunk_alloc(tcp_analysis_chunk);
tcpd->ual1=NULL;
tcpd->base_seq1=0;
tcpd->ual2=NULL;
tcpd->base_seq2=0;
conversation_add_proto_data(conv, proto_tcp, tcpd);
}
/* find(or create if needed) the conversation for this tcp session */
tcpd=get_tcp_conversation_data(pinfo);
/* check direction and get ua lists */
direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
@ -308,22 +484,25 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
if(direction>=0){
ual1=tcpd->ual1;
ual2=tcpd->ual2;
tnp=&tcpd->pdu_seq2;
base_seq=tcpd->base_seq1;
base_ack=tcpd->base_seq2;
} else {
ual1=tcpd->ual2;
ual2=tcpd->ual1;
tnp=&tcpd->pdu_seq1;
base_seq=tcpd->base_seq2;
base_ack=tcpd->base_seq1;
}
if(base_seq==0){
base_seq=seq;
if(tcp_relative_seq){
if(base_seq==0){
base_seq=seq;
}
if(base_ack==0){
base_ack=ack;
}
}
if(base_ack==0){
base_ack=ack;
}
/* To handle FIN, just add 1 to the length.
else the ACK following the FIN-ACK will look like it was
@ -351,8 +530,10 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
ual1->ts.secs=pinfo->fd->abs_secs;
ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
ual1->window=window;
base_seq=seq;
base_ack=ack;
if(tcp_relative_seq){
base_seq=seq;
base_ack=ack;
}
goto seq_finished;
}
@ -369,7 +550,10 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
ual1->ts.secs=pinfo->fd->abs_secs;
ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
ual1->window=window;
base_seq=seq;
if(tcp_relative_seq){
base_seq=seq;
base_ack=ack;
}
goto seq_finished;
}
@ -488,6 +672,7 @@ seq_finished:
ual=ual2->next;
g_mem_chunk_free(tcp_unacked_chunk, ual2);
}
prune_next_pdu_list(tnp, ack-base_ack);
goto ack_finished;
}
@ -510,6 +695,7 @@ seq_finished:
ual=ual2->next;
g_mem_chunk_free(tcp_unacked_chunk, ual2);
}
prune_next_pdu_list(tnp, ack-base_ack);
goto ack_finished;
}
@ -545,7 +731,7 @@ seq_finished:
tmpual=ual->next;
g_mem_chunk_free(tcp_unacked_chunk, ual);
}
prune_next_pdu_list(tnp, ack-base_ack);
}
ack_finished:
@ -809,11 +995,21 @@ tcp_analyze_seq_init(void)
g_hash_table_destroy(tcp_rel_seq_table);
tcp_rel_seq_table = NULL;
}
if( tcp_pdu_tracking_table ){
g_hash_table_foreach_remove(tcp_rel_seq_table,
free_all_acked, NULL);
g_hash_table_destroy(tcp_pdu_tracking_table);
tcp_pdu_tracking_table = NULL;
}
/*
* Now destroy the chunk from which the conversation table
* structures were allocated.
*/
if (tcp_next_pdu_chunk) {
g_mem_chunk_destroy(tcp_next_pdu_chunk);
tcp_next_pdu_chunk = NULL;
}
if (tcp_analysis_chunk) {
g_mem_chunk_destroy(tcp_analysis_chunk);
tcp_analysis_chunk = NULL;
@ -836,6 +1032,12 @@ tcp_analyze_seq_init(void)
tcp_acked_equal);
tcp_rel_seq_table = g_hash_table_new(tcp_acked_hash,
tcp_acked_equal);
tcp_pdu_tracking_table = g_hash_table_new(tcp_acked_hash,
tcp_acked_equal);
tcp_next_pdu_chunk = g_mem_chunk_new("tcp_next_pdu_chunk",
sizeof(struct tcp_next_pdu),
tcp_next_pdu_count * sizeof(struct tcp_next_pdu),
G_ALLOC_ONLY);
tcp_analysis_chunk = g_mem_chunk_new("tcp_analysis_chunk",
sizeof(struct tcp_analysis),
tcp_analysis_count * sizeof(struct tcp_analysis),
@ -1098,7 +1300,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
Call the normal subdissector.
*/
decode_tcp_ports(tvb, offset, pinfo, tree,
sport, dport);
sport, dport, 0);
called_dissector = TRUE;
/* Did the subdissector ask us to desegment some more data
@ -1167,7 +1369,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
/* call subdissector */
decode_tcp_ports(next_tvb, 0, pinfo, tree,
sport, dport);
sport, dport, 0);
called_dissector = TRUE;
/*
@ -1779,11 +1981,19 @@ static const ip_tcp_opt tcpopts[] = {
void
decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, int src_port, int dst_port)
proto_tree *tree, int src_port, int dst_port, guint32 nxtseq)
{
tvbuff_t *next_tvb;
int low_port, high_port;
/*qqq see if it is an unaligned PDU */
if(nxtseq && tcp_analyze_seq && (!tcp_desegment)){
guint32 seq;
seq=nxtseq-tvb_reported_length_remaining(tvb, offset);
offset=scan_for_next_pdu(pinfo, offset, seq, nxtseq);
}
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
/* determine if this packet is part of a conversation and call dissector */
@ -1791,7 +2001,7 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
if (try_conversation_dissector(&pinfo->src, &pinfo->dst, PT_TCP,
src_port, dst_port, next_tvb, pinfo, tree))
return;
goto end_decode_tcp_ports;
/* Do lookups with the subdissector table.
We try the port number with the lower value first, followed by the
@ -1817,17 +2027,28 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
}
if (low_port != 0 &&
dissector_try_port(subdissector_table, low_port, next_tvb, pinfo, tree))
return;
goto end_decode_tcp_ports;
if (high_port != 0 &&
dissector_try_port(subdissector_table, high_port, next_tvb, pinfo, tree))
return;
goto end_decode_tcp_ports;
/* do lookup with the heuristic subdissector table */
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
return;
goto end_decode_tcp_ports;
/* Oh, well, we don't know this; dissect it as data. */
call_dissector(data_handle,next_tvb, pinfo, tree);
return;
end_decode_tcp_ports:
/* if !visited, check want_pdu_tracking and store it in table */
/* XXX fix nxtseq so that it always has valid content and skip the ==0 check */
if((!pinfo->fd->flags.visited) && nxtseq && tcp_analyze_seq && pinfo->want_pdu_tracking){
pdu_store_sequencenumber_of_next_pdu(pinfo, nxtseq+pinfo->bytes_until_next_pdu);
}
}
@ -2178,7 +2399,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
we don't report it as a malformed frame. */
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
decode_tcp_ports(tvb, offset, pinfo, tree, tcph->th_sport, tcph->th_dport);
decode_tcp_ports(tvb, offset, pinfo, tree, tcph->th_sport, tcph->th_dport, nxtseq);
pinfo->fragmented = save_fragmented;
}
}

View File

@ -1,6 +1,6 @@
/* packet-tcp.h
*
* $Id: packet-tcp.h,v 1.14 2003/03/03 23:20:57 sahlberg Exp $
* $Id: packet-tcp.h,v 1.15 2003/04/23 10:20:29 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -85,6 +85,6 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void (*dissect_pdu)(tvbuff_t *, packet_info *, proto_tree *));
extern void decode_tcp_ports(tvbuff_t *, int, packet_info *,
proto_tree *, int, int);
proto_tree *, int, int, guint32);
#endif