forked from osmocom/wireshark
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:
parent
d153113cdf
commit
2fcc819366
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue