USBLL: Correctly handle last fragment retransmissions

Add fragment_add_check_with_fallback() and use it in USBLL dissector
instead of fragment_add_check() to avoid last fragment retransmissions
from being treated as separate transfers. With this change, the last
fragment retransmissions are correctly grouped together with the rest
of the transfer.

Only skip single fragment reassembly if retransmission is not possible
at the protocol level, i.e. for SETUP DATA0 (when it is not merged with
OUT data) and for isochronous transfers. The reassembly must not be
skipped for other transfers (especially for full-speed bulk) because
otherwise it wouldn't be possible to group retransmissions together with
the first data packet.

Do not use DATA0/DATA1 tracking for isochronous transfers. Isochronous
data cannot be retransmitted because there are no handshakes (there is
no ACK nor NAK after isochronous data packets).
This commit is contained in:
Tomasz Moń 2022-11-25 09:15:29 +01:00
parent d153113cdf
commit 2fcc819366
No known key found for this signature in database
GPG Key ID: 397DFEBE343AD96F
4 changed files with 72 additions and 13 deletions

View File

@ -1628,9 +1628,7 @@ dissect_usbll_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offs
*/
}
}
else if ((ep_info->type == USBLL_EP_BULK) ||
(ep_info->type == USBLL_EP_INTERRUPT) ||
(ep_info->type == USBLL_EP_ISOCHRONOUS))
else if ((ep_info->type == USBLL_EP_BULK) || (ep_info->type == USBLL_EP_INTERRUPT))
{
if (pid == ep_info->last_data_pid)
{
@ -1679,6 +1677,17 @@ dissect_usbll_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offs
ep_info->last_data_len = data_size;
}
}
else if (ep_info->type == USBLL_EP_ISOCHRONOUS)
{
/* TODO: Reassemble high-bandwidth endpoints data */
transfer = wmem_new0(wmem_file_scope(), usbll_transfer_info_t);
transfer->first_packet = pinfo->num;
transfer->offset = 0;
transfer->type = ep_info->type;
transfer->from_host = from_host;
transfer->more_frags = FALSE;
wmem_map_insert(transfer_info, GUINT_TO_POINTER(pinfo->num), transfer);
}
}
transfer = (usbll_transfer_info_t *)wmem_map_lookup(transfer_info, GUINT_TO_POINTER(pinfo->num));
@ -1686,7 +1695,9 @@ dissect_usbll_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offs
{
tvbuff_t *transfer_tvb;
if ((transfer->first_packet == pinfo->num) && (!transfer->more_frags))
if ((transfer->first_packet == pinfo->num) && (!transfer->more_frags) &&
(((transfer->type == USBLL_EP_CONTROL) && (transfer->from_host)) ||
(transfer->type == USBLL_EP_ISOCHRONOUS)))
{
/* No multi-packet reassembly needed, simply construct tvb */
transfer_tvb = tvb_new_subset_length(tvb, data_offset, data_size);
@ -1695,9 +1706,10 @@ dissect_usbll_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offs
else
{
fragment_head *head;
head = fragment_add_check(&usbll_reassembly_table, tvb, data_offset,
pinfo, transfer->first_packet, NULL,
transfer->offset, data_size, transfer->more_frags);
head = fragment_add_check_with_fallback(&usbll_reassembly_table,
tvb, data_offset,
pinfo, transfer->first_packet, NULL,
transfer->offset, data_size, transfer->more_frags, transfer->first_packet);
transfer_tvb = process_reassembled_data(tvb, data_offset, pinfo,
"USB transfer", head, &usbll_frag_items,
NULL, tree);

View File

@ -1200,7 +1200,7 @@ static gboolean
fragment_add_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags,
const guint32 frag_frame)
const guint32 frag_frame, const gboolean allow_overlaps)
{
fragment_item *fd;
fragment_item *fd_i;
@ -1220,7 +1220,7 @@ fragment_add_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
/*
* Are we adding to an already-completed reassembly?
*/
if (fd_head->flags & FD_DEFRAGMENTED) {
if ((fd_head->flags & FD_DEFRAGMENTED) && !allow_overlaps) {
/*
* Yes. Does this fragment go past the end of the results
* of that reassembly?
@ -1698,7 +1698,7 @@ fragment_add_common(reassembly_table *table, tvbuff_t *tvb, const int offset,
}
if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
frag_data_len, more_frags, frag_frame)) {
frag_data_len, more_frags, frag_frame, FALSE)) {
/*
* Reassembly is complete.
*/
@ -1759,14 +1759,16 @@ fragment_add_out_of_order(reassembly_table *table, tvbuff_t *tvb,
}
fragment_head *
fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
fragment_add_check_with_fallback(reassembly_table *table, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 id,
const void *data, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags)
const guint32 frag_data_len, const gboolean more_frags,
const guint32 fallback_frame)
{
reassembled_key reass_key;
fragment_head *fd_head;
gpointer orig_key;
gboolean late_retransmission = FALSE;
/*
* If this isn't the first pass, look for this frame in the table
@ -1783,6 +1785,20 @@ fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
* the memory allocated for the original key, for example before calling g_hash_table_remove()
*/
fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
if ((fd_head == NULL) && (fallback_frame != pinfo->num)) {
/* Check if there is completed reassembly reachable from fallback frame */
reass_key.frame = fallback_frame;
reass_key.id = id;
fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
if (fd_head != NULL) {
/* Found completely reassembled packet, hash it with current frame number */
reassembled_key *new_key = g_slice_new(reassembled_key);
new_key->frame = pinfo->num;
new_key->id = id;
g_hash_table_insert(table->reassembled_table, new_key, fd_head);
late_retransmission = TRUE;
}
}
if (fd_head == NULL) {
/* not found, this must be the first snooped fragment for this
* packet. Create list-head.
@ -1804,7 +1820,11 @@ fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
}
if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
frag_data_len, more_frags, pinfo->num)) {
frag_data_len, more_frags, pinfo->num, late_retransmission)) {
/* Nothing left to do if it was a late retransmission */
if (late_retransmission) {
return fd_head;
}
/*
* Reassembly is complete.
* Remove this from the table of in-progress
@ -1831,6 +1851,16 @@ fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
}
}
fragment_head *
fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 id,
const void *data, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags)
{
return fragment_add_check_with_fallback(table, tvb, offset, pinfo, id, data,
frag_offset, frag_data_len, more_frags, pinfo->num);
}
static void
fragment_defragment_and_free (fragment_head *fd_head, const packet_info *pinfo)
{

View File

@ -263,6 +263,22 @@ fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
const void *data, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags);
/*
* Like fragment_add_check, but handles retransmissions after reassembly.
*
* Start new reassembly only if there is no reassembly in progress and there
* is no completed reassembly reachable from fallback_frame. If there is
* completed reassembly (reachable from fallback_frame), simply links this
* packet into the list, updating the flags if necessary (however actual data
* and reassembled in frame won't be modified).
*/
WS_DLL_PUBLIC fragment_head *
fragment_add_check_with_fallback(reassembly_table *table, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 id,
const void *data, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags,
const guint32 fallback_frame);
/*
* Like fragment_add, but fragments have a block sequence number starting from
* zero (for the first fragment of each datagram). This differs from

View File

@ -701,6 +701,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
follow_tvb_tap_listener@Base 2.1.0
fragment_add@Base 1.9.1
fragment_add_check@Base 1.9.1
fragment_add_check_with_fallback@Base 4.2.0
fragment_add_multiple_ok@Base 1.9.1
fragment_add_out_of_order@Base 3.7.0
fragment_add_seq@Base 1.9.1