Documente Academic
Documente Profesional
Documente Cultură
Appendix C:
Wireshark Code
Wireshark University™
/* packet-tcp.c
* Routines for TCP packet disassembly
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <epan/in_cksum.h>
#include <epan/packet.h>
#include <epan/addr_resolv.h>
#include <epan/ipproto.h>
#include <epan/ip_opts.h>
#include <epan/follow.h>
#include <epan/prefs.h>
#include <epan/emem.h>
#include "packet-tcp.h"
#include "packet-ip.h"
#include "packet-frame.h"
#include <epan/conversation.h>
#include <epan/strutil.h>
#include <epan/reassemble.h>
#include <epan/tap.h>
#include <epan/slab.h>
#include <epan/expert.h>
/*
* Flag to control whether to check the TCP checksum.
*
* In at least some Solaris network traces, there are packets with bad
* TCP checksums, but the traffic appears to indicate that the packets
* *were* received; the packets were probably sent by the host on which
* the capture was being done, on a network interface to which
* checksumming was offloaded, so that DLPI supplied an un-checksummed
* packet to the capture program but a checksummed packet got put onto
* the wire.
*/
static gboolean tcp_check_checksum = TRUE;
/* not all of the hf_fields below make sense for TCP but we have to provide
them anyways to comply with the api (which was aimed for ip fragment
reassembly) */
static const fragment_items tcp_segment_items = {
&ett_tcp_segment,
&ett_tcp_segments,
&hf_tcp_segments,
&hf_tcp_segment,
&hf_tcp_segment_overlap,
&hf_tcp_segment_overlap_conflict,
&hf_tcp_segment_multiple_tails,
&hf_tcp_segment_too_long_fragment,
&hf_tcp_segment_error,
&hf_tcp_reassembled_in,
"Segments"
};
/* **************************************************************************
static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
struct tcp_analysis *tcpd);
struct tcp_analysis *
get_tcp_conversation_data(packet_info *pinfo)
{
int direction;
conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
tcpd-
>flow2.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK,
"tcp_multisegment_pdus");
tcpd-
>acked_table=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK,
"tcp_analyze_acked_table");
tcpd->ta=NULL;
return tcpd;
}
static void
print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct
tcp_multisegment_pdu *msp)
{
proto_item *item;
if (check_col(pinfo->cinfo, COL_INFO)){
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u]
", msp->first_frame);
}
item=proto_tree_add_uint(tcp_tree, hf_tcp_continuation_to,
tvb, 0, 0, msp->first_frame);
PROTO_ITEM_SET_GENERATED(item);
}
/* 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(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset,
guint32 seq, guint32 nxtseq, struct tcp_analysis *tcpd)
{
struct tcp_multisegment_pdu *msp=NULL;
if(!pinfo->fd->flags.visited){
msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
if(msp){
/* If this is a continuation of a PDU started in a
* previous segment we need to update the last_frame
* variables.
*/
if(seq>msp->seq && seq<msp->nxtpdu){
msp->last_frame=pinfo->fd->num;
msp->last_frame_time=pinfo->fd->abs_ts;
print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
}
}
} else {
/* First we try to find the start and transfer time for a PDU.
* We only print this for the very first segment of a PDU
* and only for PDUs spanning multiple segments.
* Se we look for if there was any multisegment PDU started
* just BEFORE the end of this segment. I.e. either inside this
* segment or in a previous segment.
* Since this might also match PDUs that are completely within
* this segment we also verify that the found PDU does span
* beyond the end of this segment.
*/
msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, nxtseq-1);
if(msp){
if( (pinfo->fd->num==msp->first_frame)
){
proto_item *item;
nstime_t ns;
item=proto_tree_add_uint(tcp_tree,
hf_tcp_pdu_last_frame, tvb, 0, 0, msp->last_frame);
PROTO_ITEM_SET_GENERATED(item);
}
return offset;
}
msp=se_alloc(sizeof(struct tcp_multisegment_pdu));
msp->nxtpdu=nxtpdu;
msp->seq=seq;
msp->first_frame=pinfo->fd->num;
msp->last_frame=pinfo->fd->num;
msp->last_frame_time=pinfo->fd->abs_ts;
msp->flags=0;
se_tree_insert32(tcpd->fwd->multisegment_pdus, seq, (void *)msp);
return msp;
}
/* This is called for SYN+ACK packets and the purpose is to verify that we
* have seen window scaling in both directions.
* If we cant find window scaling being set in both directions
* that means it was present in the SYN but not in the SYN+ACK
* (or the SYN was missing) and then we disable the window scaling
* for this tcp session.
*/
static void
verify_tcp_window_scaling(struct tcp_analysis *tcpd)
{
if( tcpd && ((tcpd->flow1.win_scale==-1) || (tcpd->flow2.win_scale==-1)) ){
tcpd->flow1.win_scale=-1;
tcpd->flow2.win_scale=-1;
}
}
static void
tcp_get_relative_seq_ack(guint32 *seq, guint32 *ack, guint32 *win, struct tcp_analysis
*tcpd)
{
if(tcp_relative_seq){
(*seq) -= tcpd->fwd->base_seq;
(*ack) -= tcpd->rev->base_seq;
if(tcpd->fwd->win_scale!=-1){
(*win)<<=tcpd->fwd->win_scale;
}
}
}
/* when this function returns, it will (if createflag) populate the ta pointer.
*/
static void
tcp_analyze_get_acked_struct(guint32 frame, gboolean createflag, struct tcp_analysis
*tcpd)
{
tcpd->ta=se_tree_lookup32(tcpd->acked_table, frame);
if((!tcpd->ta) && createflag){
tcpd->ta=se_alloc(sizeof(struct tcp_acked));
tcpd->ta->frame_acked=0;
tcpd->ta->ts.secs=0;
tcpd->ta->ts.nsecs=0;
tcpd->ta->flags=0;
tcpd->ta->dupack_num=0;
tcpd->ta->dupack_frame=0;
se_tree_insert32(tcpd->acked_table, frame, (void *)tcpd->ta);
}
}
/* fwd contains a list of all segments processed but not yet ACKed in the
#ifdef REMOVED
printf("analyze_sequence numbers frame:%d direction:%s\n",pinfo->fd-
>num,direction>=0?"FWD":"REW");
printf("FWD list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->fwd->lastsegmentflags,tcpd-
>fwd->base_seq);for(ual=tcpd->fwd->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d
Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
printf("REV list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->rev->lastsegmentflags,tcpd-
>rev->base_seq);for(ual=tcpd->rev->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d
Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
#endif
/* if this is the first segment for this list we need to store the
* base_seq
*/
if(tcpd->fwd->base_seq==0){
tcpd->fwd->base_seq=seq;
}
/* if we have spotted a new base_Seq in the reverse direction
* store it.
*/
if(tcpd->rev->base_seq==0){
tcpd->rev->base_seq=ack;
}
/* ZERO WINDOW
* a zero window packet has window == 0 but none of the SYN/FIN/RST set
*/
/*QQQ tested*/
if( window==0
&& (flags&(TH_RST|TH_FIN|TH_SYN))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW;
}
/* LOST PACKET
* If this segment is beyond the last seen nextseq we must
* have missed some previous segment
*
* We only check for this if we have actually seen segments prior to this
* one.
* RST packets are not checked for this.
*/
if( tcpd->fwd->nextseq
&& GT_SEQ(seq, tcpd->fwd->nextseq)
&& (flags&(TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_LOST_PACKET;
}
/* KEEP ALIVE
* a keepalive contains 0 or 1 bytes of data and starts one byte prior
* to what should be the next sequence number.
* SYN/FIN/RST segments are never keepalives
*/
/*QQQ tested */
if( (seglen==0||seglen==1)
&& seq==(tcpd->fwd->nextseq-1)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_KEEP_ALIVE;
}
/* WINDOW UPDATE
* A window update is a 0 byte segment with the same SEQ/ACK numbers as
* the previous seen segment and with a new window value
*/
if( seglen==0
&& window
&& window!=tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
}
/* WINDOW FULL
* If we know the window scaling
* and if this segment contains data ang goes all the way to the
* edge of the advertized window
* then we mark it as WINDOW FULL
* SYN/RST/FIN packets are never WINDOW FULL
*/
/*QQQ tested*/
if( seglen>0
&& tcpd->fwd->win_scale!=-1
&& tcpd->rev->win_scale!=-1
&& (seq+seglen)==(tcpd->rev->lastack+(tcpd->rev->window<<tcpd->rev->win_scale))
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_WINDOW_FULL;
}
/* DUPLICATE ACK
* It is a duplicate ack if window/seq/ack is the same as the previous
* segment and if the segment length is 0
*/
if( seglen==0
&& window
&& window==tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
tcpd->fwd->dupacknum++;
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_DUPLICATE_ACK;
tcpd->ta->dupack_num=tcpd->fwd->dupacknum;
tcpd->ta->dupack_frame=tcpd->fwd->lastnondupack;
}
finished_fwd:
/* If this was NOT a dupack we must reset the dupack counters */
if( (!tcpd->ta) || !(tcpd->ta->flags&TCP_A_DUPLICATE_ACK) ){
tcpd->fwd->lastnondupack=pinfo->fd->num;
tcpd->fwd->dupacknum=0;
}
/* RETRANSMISSION/FAST RETRANSMISSION/OUT-OF-ORDER
* If the segments contains data and if it does not advance
* sequence number it must be either of these three.
* Only test for this if we know what the seq number should be
* (tcpd->fwd->nextseq)
*
* Note that a simple KeepAlive is not a retransmission
*/
if( seglen>0
&& tcpd->fwd->nextseq
&& (LT_SEQ(seq, tcpd->fwd->nextseq)) ){
guint32 t;
/* If the segment came <3ms since the segment with the highest
* seen sequence number, then it is an OUT-OF-ORDER segment.
* (3ms is an arbitrary number)
*/
t=(pinfo->fd->abs_ts.secs-tcpd->fwd->nextseqtime.secs)*1000000000;
t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->fwd->nextseqtime.nsecs;
if( t<3000000 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE,
tcpd);
}
tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
goto finished_checking_retransmission_type;
}
/* next sequence number is seglen bytes away, plus SYN/FIN which counts as one
byte */
ual->nextseq=seq+seglen;
if( flags&(TH_SYN|TH_FIN) ){
ual->nextseq+=1;
}
/* Store the highest number seen so far for nextseq so we can detect
* when we receive segments that arrive with a "hole"
* If we dont have anything since before, just store what we got.
* ZeroWindowProbes are special and dont really advance the nextseq
*/
if(GT_SEQ(ual->nextseq, tcpd->fwd->nextseq) || !tcpd->fwd->nextseq) {
if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ){
tcpd->fwd->nextseq=ual->nextseq;
tcpd->fwd->nextseqframe=pinfo->fd->num;
tcpd->fwd->nextseqtime.secs=pinfo->fd->abs_ts.secs;
tcpd->fwd->nextseqtime.nsecs=pinfo->fd->abs_ts.nsecs;
}
}
/* if there were any flags set for this segment we need to remember them
* we only remember the flags for the very last segment though.
*/
if(tcpd->ta){
tcpd->fwd->lastsegmentflags=tcpd->ta->flags;
} else {
tcpd->fwd->lastsegmentflags=0;
}
/* remove all segments this ACKs and we dont need to keep around any more
*/
ackcount=0;
/* first we remove all such segments at the head of the list */
while((ual=tcpd->rev->segments)){
tcp_unacked_t *tmpual;
if(ack==ual->nextseq){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
tcpd->ta->frame_acked=ual->frame;
nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &ual->ts);
}
if(GT_SEQ(ual->nextseq,ack)){
break;
}
if(!ackcount){
/*qqq do the ACKs segment x delta y */
}
ackcount++;
tmpual=tcpd->rev->segments->next;
TCP_UNACKED_FREE(ual);
tcpd->rev->segments=tmpual;
}
/* now we remove all such segments that are NOT at the head of the list */
ual=tcpd->rev->segments;
while(ual && ual->next){
tcp_unacked_t *tmpual;
if(GT_SEQ(ual->next->nextseq,ack)){
ual=ual->next;
continue;
}
if(!ackcount){
/*qqq do the ACKs segment x delta y */
}
ackcount++;
tmpual=ual->next->next;
TCP_UNACKED_FREE(ual->next);
ual->next=tmpual;
ual=ual->next;
}