tcp: add support for reassembling out-of-order segments

Currently out-of-order segments will result in cutting a stream into
two pieces while the out-of-order segment itself is ignored. For
example, a stream of segments "ABDCE" is interpreted as "AB", "DE" with
"C" ignored. This behavior breaks TLS decryption or prevent application
layer PDUs (such as HTTP requests/responses) from being reconstructed.
To fix this, buffer segments when a gap is detected.

The proposed approach extends the "multi-segment PDU" (MSP) mechanism
which is normally used for linking multiple, sequential TCP segments
into a single PDU. When a gap is detected between segments, it is
assumed that the segments within this gap are out-of-order and will be
received (or retransmitted) later.

The current implementation has a limitation though, if multiple gaps
exist, then the subdissector will only be called when all gaps are
filled (the subdissector will receive segments later than necessary).
For example with "ACEBD", "ABC" can already be processed after "B" is
received (with "E" still buffered), but due to how MSP are extended, it
must receive "D" too before it reassembles "ABCDE". In practice this
could mean that the request/response times between HTTP requests and
responses are slightly off, but at least the stream is correct now.
(These limitations are documented in the User's Guide.)

As the feature fails at least the 802.11 decryption test where packets
are missing (instead of OoO), hide this feature behind a preference.

Tested with captures containing out-of-order TCP segments from the
linked bug reports, comparing the effect of toggling the preference on
the summary output of tshark, the verbose output (-V) and the two-pass
output (-2 or -2V). Captures marked with "ok" just needed "simple"
out-of-order handling. Captures marked with "ok2" additionally required
the reassembly API change to set the correct reassembled length.

This change does "regress" on bug 10289 though when the preference is
enabled as retransmitted single-segment PDUs are now passed to
subdissectors. I added a TODO comment for this unrelated cosmetic issue.

Bug: 3389   # capture 2907 (HTTP) ok
Bug: 4727   # capture 4590 (HTTP) ok
Bug: 9461   # capture 12130 (TLS/HTTP/RPC-over-HTTP +key 12131) ok
Bug: 12006  # capture 14236 (HTTP) ok2; capture 15261 (HTTP) ok
Bug: 13517  # capture 15370 (HTTP) ok; capture 16059 (MQ) ok
Bug: 13754  # capture 15593 (MySQL) ok2
Bug: 14649  # capture 16305 (WebSocket) ok
Change-Id: If3938c5c1c96db8f7f50e39ea779f623ce657d56
Reviewed-on: https://code.wireshark.org/review/27943
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2018-06-01 15:11:47 +02:00 committed by Anders Broman
parent e6935f9635
commit ca42331437
8 changed files with 359 additions and 20 deletions

View File

@ -39,12 +39,12 @@ Dumpcap might not quit if Wireshark or TShark crashes.
The following features are new (or have been significantly updated)
since version 2.6.0:
* The membership operator now supports ranges, allowing display filters such as
`tcp.port in {4430..4434}` to be expressed. See the Users Guide, chapter
_Building display filter expressions_ for details.
* TShark now supports the `-G elastic-mapping` option which generates an ElasticSearch mapping file.
* The “Capture Information” dialog has been added back (wsbuglink:12004[]).
* The Ethernet dissector no longer validates the frame check sequence (checksum) by default.
* The TCP dissector gained a new “Reassemble out-of-order segments” preference
to fix dissection and decryption issues in case TCP segments are received
out-of-order. See the Users Guide, chapter _TCP Reassembly_ for details.
=== Removed Features and Support

View File

@ -357,6 +357,9 @@ capture file is first opened. Packets are processed in the order in
which they appear in the packet list. You can enable or disable this
feature via the “Analyze TCP sequence numbers” TCP dissector preference.
For analysis of data or protocols layered on top of TCP (such as HTTP), see
<<ChAdvReassemblyTcp>>.
.“TCP Analysis” packet detail items
image::wsug_graphics/ws-tcp-analysis.png[{screenshot-attrs}]
@ -838,6 +841,66 @@ settings for a protocol typically requires two things:
The tooltip of the higher level protocol setting will notify you if and which
lower level protocol setting also has to be considered.
[[ChAdvReassemblyTcp]]
==== TCP Reassembly
Protocols such as HTTP or TLS are likely to span multiple TCP segments. The
TCP protocol preference “Allow subdissector to reassemble TCP streams” (enabled
by default) makes it possible for Wireshark to collect a contiguous sequence of
TCP segments and hand them over to the higher level protocol (for example, to
reconstruct a full HTTP message). All but the final segment will be marked with
“[TCP segment of a reassembled PDU]” in the packet list.
Disable this preference to reduce memory and processing overhead if you are only
interested in TCP sequence number analysis (<<ChAdvTCPAnalysis>>). Keep in mind,
though, that higher level protocols might be wrongly dissected. For example,
HTTP messages could be shown as “Continuation” and TLS records could be shown as
“Ignored Unknown Record”. Such results can also be observed if you start
capturing while a TCP connection was already started or when TCP segments
are lost or delivered out-of-order.
To reassemble of out-of-order TCP segments, the TCP protocol preference
“Reassemble out-of-order segments” (currently disabled by default) must be
enabled in addition to the previous preference.
If all packets are received in-order, this preference will not have any effect.
Otherwise (if missing segments are encountered while sequentially processing a
packet capture), it is assumes that the new and missing segments belong to the
same PDU. Caveats:
* Lost packets are assumed to be received out-of-order or retransmitted later.
Applications usually retransmit segments until these are acknowledged, but if
the packet capture drops packets, then Wireshark will not be able to
reconstruct the TCP stream. In such cases, you can try to disable this
preference and hopefully have a partial dissection instead of seeing just
“[TCP segment of a reassembled PDU]” for every TCP segment.
// See test/suite_decryption.py (suite_decryption.case_decrypt_80211)
// which would break when enabling the preference.
* When doing a capture in monitor mode (IEEE 802.11), packets are more likely to
get lost due to signal reception issues. In that case it is recommended to
disable the option.
// See test/suite_dissection.py (case_dissect_tcp.check_tcp_out_of_order)
* If the new and missing segments are in fact part of different PDUs,
then processing is currently delayed until no more segments are missing, even
if the begin of the missing segments completed a PDU. For example, assume six
segments forming two PDUs `ABC` and `DEF`. When received as `ABECDF`, an
application can start processing the first PDU after receiving `ABEC`.
Wireshark however requires the missing segment `D` to be received as well.
This issue will be addressed in the future.
// See test/suite_dissection.py (case_dissect_tcp.test_tcp_out_of_order_twopass)
* In the GUI and during a two-pass dissection (`tshark -2`), the previous
scenario will display both PDUs in the packet with last segment (`F`) rather
than displaying it in the first packet that has the final missing segment of a
PDU. This issue will be addressed in the future.
* When enabled, fields such as the SMB “Time from request” (`smb.time`) might be
smaller if the request follows other out-of-order segments (this reflects
application behavior). If the previous scenario however occurs, then the time
of the request is based on the frame where all missing segments are received.
Regardless of the setting of these two reassembly-related preferences, you can
always use the “Follow TCP Stream” option (<<ChAdvFollowStreamSection>>) which
displays segments in the expected order.
[[ChAdvNameResolutionSection]]
=== Name Resolution

View File

@ -2981,6 +2981,37 @@ static reassembly_table tcp_reassembly_table;
/* Enable desegmenting of TCP streams */
static gboolean tcp_desegment = TRUE;
/* Enable buffering of out-of-order TCP segments before passing it to a
* subdissector (depends on "tcp_desegment"). */
static gboolean tcp_reassemble_out_of_order = FALSE;
/* Returns true iff any gap exists in the segments associated with msp up to the
* given sequence number (it ignores any gaps after the sequence number). */
static gboolean
missing_segments(packet_info *pinfo, struct tcp_multisegment_pdu *msp, guint32 seq)
{
fragment_head *fd_head;
guint32 frag_offset = seq - msp->seq;
if ((gint32)frag_offset <= 0) {
return FALSE;
}
fd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, NULL);
/* msp implies existence of fragments, this should never be NULL. */
DISSECTOR_ASSERT(fd_head);
/* Find length of contiguous fragments. */
guint32 max = 0;
for (fragment_item *frag = fd_head; frag; frag = frag->next) {
guint32 frag_end = frag->offset + frag->len;
if (frag->offset <= max && max < frag_end) {
max = frag_end;
}
}
return max < frag_offset;
}
static void
desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
guint32 seq, guint32 nxtseq,
@ -2999,6 +3030,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_item *item;
struct tcp_multisegment_pdu *msp;
gboolean cleared_writable = col_get_writable(pinfo->cinfo, COL_PROTOCOL);
const gboolean reassemble_ooo = tcp_desegment && tcp_reassemble_out_of_order;
again:
ipfd_head = NULL;
@ -3027,8 +3059,11 @@ again:
if (tcpd) {
/* Have we seen this PDU before (and is it the start of a multi-
* segment PDU)?
* Only shortcircuit here when the first segment of the MSP is known,
* and when this this first segment is not one to complete the MSP.
*/
if ((msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, seq))) {
if ((msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, seq)) &&
!(msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) && msp->last_frame != pinfo->num) {
const char* str;
/* Yes. This could be because we've dissected this frame before
@ -3071,6 +3106,18 @@ again:
/* The above code only finds retransmission if the PDU boundaries and the seq coincide I think
* If we have sequence analysis active use the TCP_A_RETRANSMISSION flag.
* XXXX Could the above code be improved?
* XXX the following check works great for filtering duplicate
* retransmissions, but could there be a case where it prevents
* "tcp_reassemble_out_of_order" from functioning due to skipping
* retransmission of a lost segment?
* If the latter is enabled, it could use use "maxnextseq" for ignoring
* retransmitted single-segment PDUs (that would require storing
* per-packet state (tcp_per_packet_data_t) to make it work for two-pass
* and random access dissection). Retransmitted segments that are part
* of a MSP should already be passed only once to subdissectors due to
* the "reassembled_in" check below.
* The following should also check for TCP_A_SPURIOUS_RETRANSMISSION to
* address bug 10289.
*/
if((tcpd->ta) && ((tcpd->ta->flags&TCP_A_RETRANSMISSION) == TCP_A_RETRANSMISSION)){
const char* str = "Retransmitted ";
@ -3081,7 +3128,62 @@ again:
return;
}
/* Else, find the most previous PDU starting before this sequence number */
msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
if (!msp) {
msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
}
}
if (reassemble_ooo && tcpd && !PINFO_FD_VISITED(pinfo)) {
/* If there is a gap between this segment and any previous ones (that
* is, seqno is larger than the maximum expected seqno), then it is
* possibly an out-of-order segment. The very first segment is expected
* to be in-order though (otherwise captures starting in midst of a
* connection would never be reassembled).
*/
if (tcpd->fwd->maxnextseq) {
/* Segments may be missing due to packet loss (assume later
* retransmission) or out-of-order (assume it will appear later).
*
* Extend an unfinished MSP when (1) missing segments exist between
* the start of the previous, (2) unfinished MSP and new segment.
*
* Create a new MSP when no (1) previous MSP exists and (2) a gap is
* detected between the previous largest nxtseq and the new segment.
*/
/* Whether a previous MSP exists with missing segments. */
gboolean has_unfinished_msp = msp && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS);
/* Whether the new segment creates a new gap. */
gboolean has_gap = LT_SEQ(tcpd->fwd->maxnextseq, seq);
if (has_unfinished_msp && missing_segments(pinfo, msp, seq)) {
/* The last PDU is part of a MSP which still needed more data,
* extend it (if necessary) to cover the entire new segment.
*/
if (LT_SEQ(msp->nxtpdu, nxtseq)) {
msp->nxtpdu = nxtseq;
}
} else if (!has_unfinished_msp && has_gap) {
/* Either the previous segment was a single PDU that did not
* belong to a MSP, or the previous MSP was completed and cannot
* be extended.
* Create a new one starting at the expected next position and
* extend it to the end of the new segment.
*/
msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
tcpd->fwd->maxnextseq, nxtseq,
tcpd->fwd->multisegment_pdus);
msp->flags |= MSP_FLAGS_MISSING_FIRST_SEGMENT;
}
/* Now that the MSP is updated or created, continue adding the
* segments to the MSP below. The subdissector will not be called as
* the MSP is not complete yet. */
}
if (tcpd->fwd->maxnextseq == 0 || LT_SEQ(tcpd->fwd->maxnextseq, nxtseq)) {
/* Update the maximum expected seqno if unknown or if the new
* segment succeeds previous segments. */
tcpd->fwd->maxnextseq = nxtseq;
}
}
if (msp && msp->seq <= seq && msp->nxtpdu > seq) {
@ -3103,6 +3205,21 @@ again:
}
last_fragment_len = len;
if (reassemble_ooo) {
/*
* If the previous segment requested more data (setting
* FD_PARTIAL_REASSEMBLY as the next segment length is unknown), but
* subsequently an OoO segment was received (for an earlier hole),
* then "fragment_add" would truncate the reassembled PDU to the end
* of this OoO segment. To prevent that, explicitly specify the MSP
* length before calling "fragment_add".
*/
fragment_reset_tot_len(&tcp_reassembly_table, pinfo,
msp->first_frame, NULL,
MAX(seq + len, msp->nxtpdu) - msp->seq);
}
ipfd_head = fragment_add(&tcp_reassembly_table, tvb, offset,
pinfo, msp->first_frame, NULL,
seq - msp->seq, len,
@ -3123,6 +3240,19 @@ again:
msp->nxtpdu = nxtseq;
}
if (reassemble_ooo && !PINFO_FD_VISITED(pinfo)) {
/* If the first segment of the MSP was seen, remember it. */
if (msp->seq == seq) {
msp->flags &= ~MSP_FLAGS_MISSING_FIRST_SEGMENT;
}
/* Remember when all segments are ready to avoid subsequent
* out-of-order packets from extending this MSP. If a subsdissector
* needs more segments, the flag will be cleared below. */
if (ipfd_head) {
msp->flags |= MSP_FLAGS_GOT_ALL_SEGMENTS;
}
}
if( (msp->nxtpdu < nxtseq)
&& (msp->nxtpdu >= seq)
&& (len > 0)) {
@ -3214,6 +3344,10 @@ again:
* desegmented, or does it think we need even more
* data?
*/
if (reassemble_ooo && !PINFO_FD_VISITED(pinfo) && pinfo->desegment_len) {
/* "desegment_len" isn't 0, so it needs more data to extend the MSP. */
msp->flags &= ~MSP_FLAGS_GOT_ALL_SEGMENTS;
}
old_len = (int)(tvb_reported_length(next_tvb) - last_fragment_len);
if (pinfo->desegment_len &&
pinfo->desegment_offset<=old_len) {
@ -3247,13 +3381,24 @@ again:
* This means that the next segment
* will complete reassembly even if it
* is only one single byte in length.
* If this is an OoO segment, then increment the MSP end.
*/
msp->nxtpdu = seq + tvb_reported_length_remaining(tvb, offset) + 1;
msp->nxtpdu = MAX(seq + tvb_reported_length_remaining(tvb, offset), msp->nxtpdu) + 1;
msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
} else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
} else {
msp->nxtpdu=seq + last_fragment_len + pinfo->desegment_len;
if (seq + last_fragment_len >= msp->nxtpdu) {
/* This is the segment (overlapping) the end of the MSP. */
msp->nxtpdu = seq + last_fragment_len + pinfo->desegment_len;
} else {
/* This is a segment before the end of the MSP, so it
* must be an out-of-order segmented that completed the
* MSP. The requested additional data is relative to
* that end.
*/
msp->nxtpdu += pinfo->desegment_len;
}
}
/* Since we need at least some more data
@ -5507,10 +5652,15 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
}
}
if (tcp_no_subdissector_on_error && tcpd && tcpd->ta && tcpd->ta->flags & (TCP_A_RETRANSMISSION | TCP_A_OUT_OF_ORDER)) {
if (tcp_no_subdissector_on_error && !(tcp_desegment && tcp_reassemble_out_of_order) &&
tcpd && tcpd->ta && tcpd->ta->flags & (TCP_A_RETRANSMISSION | TCP_A_OUT_OF_ORDER)) {
/* Don't try to dissect a retransmission high chance that it will mess
* subdissectors for protocols that require in-order delivery of the
* PDUs. (i.e. DCE/RPCoverHTTP and encryption)
* If OoO reassembly is enabled and if this segment was previously lost,
* then this retransmission could have finished reassembly, so continue.
* XXX should this option be removed? "tcp_reassemble_out_of_order"
* should have addressed the above in-order requirement.
*/
return FALSE;
}
@ -7520,6 +7670,11 @@ proto_register_tcp(void)
"Allow subdissector to reassemble TCP streams",
"Whether subdissector can request TCP streams to be reassembled",
&tcp_desegment);
prefs_register_bool_preference(tcp_module, "reassemble_out_of_order",
"Reassemble out-of-order segments",
"Whether out-of-order segments should be buffered and reordered before passing it to a subdissector. "
"To use this option you must also enable \"Allow subdissector to reassemble TCP streams\".",
&tcp_reassemble_out_of_order);
prefs_register_bool_preference(tcp_module, "analyze_sequence_numbers",
"Analyze TCP sequence numbers",
"Make the TCP dissector analyze TCP sequence numbers to find and flag segment retransmissions, missing segments and RTT",

View File

@ -175,6 +175,10 @@ struct tcp_multisegment_pdu {
nstime_t last_frame_time;
guint32 flags;
#define MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT 0x00000001
/* Whether this MSP is finished and no more segments can be added. */
#define MSP_FLAGS_GOT_ALL_SEGMENTS 0x00000002
/* Whether the first segment of this MSP was not yet seen. */
#define MSP_FLAGS_MISSING_FIRST_SEGMENT 0x00000004
};
@ -335,6 +339,11 @@ typedef struct _tcp_flow_t {
/* see TCP_A_* in packet-tcp.c */
guint32 lastsegmentflags;
/* The next (largest) sequence number after all segments seen so far.
* Valid only on the first pass and used to handle out-of-order segments
* during reassembly. */
guint32 maxnextseq;
/* This tree is indexed by sequence number and keeps track of all
* all pdus spanning multiple segments for this flow.
*/

View File

@ -680,6 +680,33 @@ fragment_add_seq_offset(reassembly_table *table, const packet_info *pinfo, const
fd_head->fragment_nr_offset = fragment_offset;
}
/*
* For use with fragment_add (and not the fragment_add_seq functions).
* When the reassembled result is wrong (perhaps it needs to be extended), this
* function clears any previous reassembly result, allowing the new reassembled
* length to be set again.
*/
static void
fragment_reset_defragmentation(fragment_head *fd_head)
{
/* Caller must ensure that this function is only called when
* defragmentation is safe to undo. */
DISSECTOR_ASSERT(fd_head->flags & FD_DEFRAGMENTED);
for (fragment_item *fd_i = fd_head->next; fd_i; fd_i = fd_i->next) {
if (!fd_i->tvb_data) {
fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, fd_i->offset);
fd_i->flags |= FD_SUBSET_TVB;
}
fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
}
fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET);
fd_head->flags &= ~(FD_TOOLONGFRAGMENT|FD_MULTIPLETAILS);
fd_head->datalen = 0;
fd_head->reassembled_in = 0;
fd_head->reas_in_layer_num = 0;
}
/* This function can be used to explicitly set the total length (if known)
* for reassembly of a PDU.
* This is useful for reassembly of PDUs where one may have the total length specified
@ -736,6 +763,39 @@ fragment_set_tot_len(reassembly_table *table, const packet_info *pinfo,
fd_head->flags |= FD_DATALEN_SET;
}
void
fragment_reset_tot_len(reassembly_table *table, const packet_info *pinfo,
const guint32 id, const void *data, const guint32 tot_len)
{
fragment_head *fd_head;
fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
if (!fd_head)
return;
/*
* If FD_PARTIAL_REASSEMBLY is set, it would make the next fragment_add
* call set the reassembled length based on the fragment offset and
* length. As the length is known now, be sure to disable that magic.
*/
fd_head->flags &= ~FD_PARTIAL_REASSEMBLY;
/* If the length is already as expected, there is nothing else to do. */
if (tot_len == fd_head->datalen)
return;
if (fd_head->flags & FD_DEFRAGMENTED) {
/*
* Fragments were reassembled before, clear it to allow
* increasing the reassembled length.
*/
fragment_reset_defragmentation(fd_head);
}
fd_head->datalen = tot_len;
fd_head->flags |= FD_DATALEN_SET;
}
guint32
fragment_get_tot_len(reassembly_table *table, const packet_info *pinfo,
const guint32 id, const void *data)
@ -907,6 +967,7 @@ MERGE_FRAG(fragment_head *fd_head, fragment_item *fd)
}
fd_i->next = fd;
}
/*
* This function adds a new fragment to the fragment hash table.
* If this is the first fragment seen for this datagram, a new entry
@ -970,18 +1031,7 @@ fragment_add_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
* Yes. Set flag in already empty fds &
* point old fds to malloc'ed data.
*/
for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){
if( !fd_i->tvb_data ) {
fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, fd_i->offset);
fd_i->flags |= FD_SUBSET_TVB;
}
fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
}
fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET);
fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
fd_head->datalen=0;
fd_head->reassembled_in=0;
fd_head->reas_in_layer_num = 0;
fragment_reset_defragmentation(fd_head);
} else {
/*
* No. Bail out since we have no idea what to

View File

@ -364,6 +364,18 @@ WS_DLL_PUBLIC void
fragment_set_tot_len(reassembly_table *table, const packet_info *pinfo,
const guint32 id, const void *data, const guint32 tot_len);
/*
* Similar to fragment_set_tot_len, it sets the expected number of bytes (for
* fragment_add functions) for a previously started reassembly. If the specified
* length already matches the reassembled length, then nothing will be done.
*
* If the fragments were previously reassembled, then this state will be
* cleared, allowing new fragments to extend the reassembled result again.
*/
void
fragment_reset_tot_len(reassembly_table *table, const packet_info *pinfo,
const guint32 id, const void *data, const guint32 tot_len);
/*
* Return the expected index for the last block (for fragment_add_seq functions)
* or the expected number of bytes (for fragment_add functions).

BIN
test/captures/http-ooo.pcap Normal file

Binary file not shown.

View File

@ -29,3 +29,53 @@ class case_dissect_http2(subprocesstest.SubprocessTestCase):
),
env=config.test_env)
self.assertTrue(self.grepOutput('DATA'))
class case_dissect_tcp(subprocesstest.SubprocessTestCase):
def check_tcp_out_of_order(self, extraArgs=[]):
capture_file = os.path.join(config.capture_dir, 'http-ooo.pcap')
self.runProcess([config.cmd_tshark,
'-r', capture_file,
'-otcp.reassemble_out_of_order:TRUE',
'-Y', 'http',
] + extraArgs,
env=config.test_env)
self.assertEqual(self.countOutput('HTTP'), 5)
# TODO PDU /1 (segments in frames 1, 2, 4) should be reassembled in
# frame 4, but it is currently done in frame 6 because the current
# implementation reassembles only contiguous segments and PDU /2 has
# segments in frames 6, 3, 7.
self.assertTrue(self.grepOutput(r'^\s*6\s.*PUT /1 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*7\s.*GET /2 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*10\s.*PUT /3 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*11\s.*PUT /4 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*15\s.*PUT /5 HTTP/1.1'))
def test_tcp_out_of_order_onepass(self):
self.check_tcp_out_of_order()
@unittest.skip("MSP splitting is not implemented yet")
def test_tcp_out_of_order_twopass(self):
self.check_tcp_out_of_order(extraArgs=['-2'])
def test_tcp_out_of_order_twopass_with_bug(self):
# TODO fix the issue below, remove this and enable
# "test_tcp_out_of_order_twopass"
capture_file = os.path.join(config.capture_dir, 'http-ooo.pcap')
self.runProcess((config.cmd_tshark,
'-r', capture_file,
'-otcp.reassemble_out_of_order:TRUE',
'-Y', 'http',
'-2',
),
env=config.test_env)
self.assertEqual(self.countOutput('HTTP'), 3)
self.assertTrue(self.grepOutput(r'^\s*7\s.*PUT /1 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*7\s.*GET /2 HTTP/1.1'))
# TODO ideally this should not be concatenated.
# Normally a multi-segment PDU (MSP) covers only a single PDU, but OoO
# segments can extend MSP such that it covers two (or even more) PDUs.
# Until MSP splitting is implemented, two PDUs are shown in a single
# packet (and in case of -2, they are only shown in the last packet).
self.assertTrue(self.grepOutput(r'^\s*11\s.*PUT /3 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*11\s.*PUT /4 HTTP/1.1'))
self.assertTrue(self.grepOutput(r'^\s*15\s.*PUT /5 HTTP/1.1'))