*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
static int hf_tcp_analysis_zero_window_probe_ack = -1;
static int hf_tcp_continuation_to = -1;
static int hf_tcp_pdu_time = -1;
+static int hf_tcp_pdu_size = -1;
static int hf_tcp_pdu_last_frame = -1;
static int hf_tcp_reassembled_in = -1;
static int hf_tcp_segments = -1;
static int hf_tcp_segment_multiple_tails = -1;
static int hf_tcp_segment_too_long_fragment = -1;
static int hf_tcp_segment_error = -1;
+static int hf_tcp_options = -1;
static int hf_tcp_option_mss = -1;
static int hf_tcp_option_mss_val = -1;
static int hf_tcp_option_wscale = -1;
tcpd->flow1.nextseqframe=0;
tcpd->flow1.window=0;
tcpd->flow1.win_scale=-1;
+ tcpd->flow1.flags=0;
tcpd->flow1.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
tcpd->flow2.segments=NULL;
tcpd->flow2.base_seq=0;
tcpd->flow2.nextseqframe=0;
tcpd->flow2.window=0;
tcpd->flow2.win_scale=-1;
+ tcpd->flow2.flags=0;
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");
struct tcp_multisegment_pdu *msp=NULL;
if(!pinfo->fd->flags.visited){
- msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
if(msp){
/* If this segment is completely within a previous PDU
* then we just skip this packet
}
} else {
- msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
if(msp){
if(pinfo->fd->num==msp->first_frame){
proto_item *item;
}
}
}
-
return offset;
}
finished_fwd:
- /* If this was NOT a dupack we must reset the dupack countres */
+ /* 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;
* in the other direction.
*/
if( tcpd->rev->nextseq
- && GT_SEQ(ack, tcpd->rev->nextseq )){
+ && GT_SEQ(ack, tcpd->rev->nextseq )
+ && (flags&(TH_ACK))!=0 ){
/*QQQ tested*/
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
/* 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;
}
ual=ual->next;
}
-#ifdef REMOVED
- tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
- tcpd->ta->frame_acked=tcpd->rev->segments->frame;
- nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &tcpd->rev->segments->ts);
-#endif
}
static void
flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
tvb, 0, 0, ta->dupack_frame);
PROTO_ITEM_SET_GENERATED(flags_item);
- expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u) to ACK in packet #%u",
- ta->dupack_num, ta->dupack_frame);
+ expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u)",
+ ta->dupack_num);
}
if( ta->flags&TCP_A_ZERO_WINDOW_PROBE ){
flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe, tvb, 0, 0, "This is a TCP zero-window-probe");
deseg_offset = offset;
/* find the most previous PDU starting before this sequence number */
- msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
if(msp && msp->seq<=seq && msp->nxtpdu>seq){
int len;
}
if (must_desegment) {
+ /* If the dissector requested "reassemble until FIN"
+ * just set this flag for the flow and let reassembly
+ * proceed at normal. We will check/pick up these
+ * reassembled PDUs later down in dissect_tcp() when checking
+ * for the FIN flag.
+ */
+ if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){
+ tcpd->fwd->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+ }
+
/*
* The sequence number at which the stuff to be desegmented
* starts is the sequence number of the byte at an offset
*/
deseg_seq = seq + (deseg_offset - offset);
- if ((nxtseq - deseg_seq) <= 1024*1024) {
- if(!pinfo->fd->flags.visited){
- msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
- nxtseq + pinfo->desegment_len, tcpd);
-
- /* add this segment as the first one for this new pdu */
- fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
- tcp_fragment_table,
- 0,
- nxtseq - deseg_seq,
- LT_SEQ(nxtseq, msp->nxtpdu));
+ if( ((nxtseq - deseg_seq) <= 1024*1024)
+ && (!pinfo->fd->flags.visited) ){
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
+ nxtseq + pinfo->desegment_len, tcpd);
+
+ /* add this segment as the first one for this new pdu */
+ fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
+ tcp_fragment_table,
+ 0,
+ nxtseq - deseg_seq,
+ LT_SEQ(nxtseq, msp->nxtpdu));
}
- }
}
if (!called_dissector || pinfo->desegment_len != 0) {
guint plen;
guint length;
tvbuff_t *next_tvb;
+ proto_item *item=NULL;
while (tvb_reported_length_remaining(tvb, offset) != 0) {
/*
show_reported_bounds_error(tvb, pinfo, tree);
return;
}
+ /*
+ * Display the PDU length as a field
+ */
+ item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size, tvb, 0, 0, plen);
+ PROTO_ITEM_SET_GENERATED(item);
+
+
/* give a hint to TCP where the next PDU starts
* so that it can attempt to find it in case it starts
RETHROW;
}
CATCH(ReportedBoundsError) {
- show_reported_bounds_error(tvb, pinfo, tree);
+ show_reported_bounds_error(tvb, pinfo, tree);
}
ENDTRY;
proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_time_stamp, tvb,
offset, optlen, TRUE);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
- "%s: tsval %u, tsecr %u", optp->name, tsv, tser);
+ "%s: TSval %u, TSecr %u", optp->name, tsv, tser);
tcp_info_append_uint(pinfo, "TSV", tsv);
tcp_info_append_uint(pinfo, "TSER", tser);
}
},
{
TCPOPT_TIMESTAMP,
- "Time stamp",
+ "Timestamps",
NULL,
FIXED_LENGTH,
TCPOLEN_TIMESTAMP,
guint length_remaining;
gboolean desegment_ok;
struct tcpinfo tcpinfo;
- static struct tcpheader tcphstruct[4], *tcph;
- static int tcph_count=0;
+ struct tcpheader *tcph;
proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL;
struct tcp_analysis *tcpd=NULL;
- tcph_count++;
- if(tcph_count>=4){
- tcph_count=0;
- }
- tcph=&tcphstruct[tcph_count];
+ tcph=ep_alloc(sizeof(struct tcpheader));
SET_ADDRESS(&tcph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
SET_ADDRESS(&tcph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
ti = proto_tree_add_item(tree, proto_tcp, tvb, 0, -1, FALSE);
}
tcp_tree = proto_item_add_subtree(ti, ett_tcp);
+ pinfo->tcp_tree=tcp_tree;
+
proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, tvb, offset, 2, tcph->th_sport,
"Source port: %s (%u)", get_tcp_port(tcph->th_sport), tcph->th_sport);
proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, tvb, offset + 2, 2, tcph->th_dport,
if (!pinfo->fragmented && !pinfo->in_error_pkt) {
if (reported_len < tcph->th_hlen) {
- proto_tree_add_text(tcp_tree, tvb, offset, 0,
+ proto_item *pi;
+ pi = proto_tree_add_text(tcp_tree, tvb, offset, 0,
"Short segment. Segment/fragment does not contain a full TCP header"
" (might be NMAP or someone else deliberately sending unusual packets)");
+ PROTO_ITEM_SET_GENERATED(pi);
+ expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN, "Short segment");
tcph->th_have_seglen = FALSE;
} else {
/* Compute the length of data in this segment. */
proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
"Header length: %u bytes", tcph->th_hlen);
tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 13, 1,
- tcph->th_flags, "Flags: 0x%04x (%s)", tcph->th_flags, flags);
+ tcph->th_flags, "Flags: 0x%02x (%s)", tcph->th_flags, flags);
field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
tf_rst = proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
tf_syn = proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
tf_fin = proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
- if(tcp_relative_seq && (tcph->th_win!=real_window)){
+ if(tcp_relative_seq
+ && (tcph->th_win!=real_window)
+ && !(tcph->th_flags&TH_SYN) ){ /* SYNs are never scaled */
proto_tree_add_uint_format(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win, "Window size: %u (scaled)", tcph->th_win);
} else {
- proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win);
+ proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, real_window);
}
}
if(tcph->th_flags & TH_SYN) {
if(tcph->th_flags & TH_ACK)
- expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): server port %s",
+ get_tcp_port(tcph->th_sport));
else
- expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): server port %s",
+ get_tcp_port(tcph->th_dport));
}
if(tcph->th_flags & TH_FIN)
- expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ /* XXX - find a way to know the server port and output only that one */
+ expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN)");
if(tcph->th_flags & TH_RST)
- expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ /* XXX - find a way to know the server port and output only that one */
+ expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST)");
/* Supply the sequence number of the first byte and of the first byte
after the segment. */
optlen = tcph->th_hlen - TCPH_MIN_LEN; /* length of options, in bytes */
tvb_ensure_bytes_exist(tvb, offset + 20, optlen);
if (tcp_tree != NULL) {
- tf = proto_tree_add_text(tcp_tree, tvb, offset + 20, optlen,
- "Options: (%u bytes)", optlen);
+ guint8 *p_options = tvb_get_ephemeral_string(tvb, offset + 20, optlen);
+ tf = proto_tree_add_bytes_format(tcp_tree, hf_tcp_options, tvb, offset + 20,
+ optlen, p_options, "Options: (%u bytes)", optlen);
field_tree = proto_item_add_subtree(tf, ett_tcp_options);
} else
field_tree = NULL;
}
tap_queue_packet(tcp_tap, pinfo, tcph);
+
+ /* A FIN packet might complete reassembly so we need to explicitely
+ * check for this here.
+ * If this segment completes reassembly we add the FIN as a final dummy
+ * byte to the reassembled PDU and check if reassembly completed successfully
+ */
+ if( (tcph->th_flags & TH_FIN)
+ && (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ){
+ struct tcp_multisegment_pdu *msp;
+
+ /* find the most previous PDU starting before this sequence number */
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1);
+ if(msp){
+ fragment_data *ipfd_head;
+
+ ipfd_head = fragment_add(tvb, offset-1, pinfo, msp->first_frame,
+ tcp_fragment_table,
+ tcph->th_seq - msp->seq,
+ 1,
+ FALSE );
+ if(ipfd_head){
+ tvbuff_t *next_tvb;
+
+ /* create a new TVB structure for desegmented data
+ * datalen-1 to strip the dummy FIN byte off
+ */
+ next_tvb = tvb_new_real_data(ipfd_head->data, ipfd_head->datalen-1, ipfd_head->datalen-1);
+
+ /* 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 */
+ add_new_data_source(pinfo, next_tvb, "Reassembled TCP");
+
+ /* call the payload dissector
+ * but make sure we dont offer desegmentation any more
+ */
+ pinfo->can_desegment = 0;
+
+ process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq, nxtseq, FALSE, tcpd);
+
+ return;
+ }
+ }
+ }
+
/*
* XXX - what, if any, of this should we do if this is included in an
* error packet? It might be nice to see the details of the packet
{ "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
+ { &hf_tcp_options,
+ { "TCP Options", "tcp.options", FT_BYTES,
+ BASE_HEX, NULL, 0x0, "TCP Options", HFILL }},
+
{ &hf_tcp_option_mss,
{ "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
{ &hf_tcp_pdu_time,
{ "Time until the last segment of this PDU", "tcp.pdu.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"How long time has passed until the last frame of this PDU", HFILL}},
+ { &hf_tcp_pdu_size,
+ { "PDU Size", "tcp.pdu.size", FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The size of this PDU", HFILL}},
+
{ &hf_tcp_pdu_last_frame,
{ "Last frame of this PDU", "tcp.pdu.last_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is the last frame of the PDU starting in this segment", HFILL }},