BT-uTP: Track conversations
Add endpoint type for uTP connection IDs. Manage uTP conversations, creating generated stream ID to filter on both sides of a conversation. Display more information in INFO column, similar to TCP. This is some progress towards #8792.
This commit is contained in:
parent
abcadce44f
commit
c6e44fb00c
|
@ -80,7 +80,9 @@ typedef enum {
|
|||
ENDPOINT_GSMTAP,
|
||||
ENDPOINT_IUUP,
|
||||
ENDPOINT_DVBBBF, /* DVB Base Band Frame ISI/PLP_ID */
|
||||
ENDPOINT_IWARP_MPA /* iWarp MPA */
|
||||
ENDPOINT_IWARP_MPA, /* iWarp MPA */
|
||||
ENDPOINT_BT_UTP, /* BitTorrent uTP Connection ID */
|
||||
|
||||
} endpoint_type;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* packet-bt-utp.c
|
||||
* Routines for BT-UTP dissection
|
||||
* Copyright 2011, Xiao Xiangquan <xiaoxiangquan@gmail.com>
|
||||
* Copyright 2021, John Thacker <johnthacker@gmail.com>
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
|
@ -14,6 +15,10 @@
|
|||
#include <epan/packet.h>
|
||||
#include <epan/conversation.h>
|
||||
#include <epan/prefs.h>
|
||||
#include <epan/proto_data.h>
|
||||
|
||||
#include "packet-udp.h"
|
||||
#include "packet-bt-utp.h"
|
||||
|
||||
void proto_register_bt_utp(void);
|
||||
void proto_reg_handoff_bt_utp(void);
|
||||
|
@ -135,6 +140,7 @@ static int hf_bt_utp_extension_bitmask = -1;
|
|||
static int hf_bt_utp_extension_unknown = -1;
|
||||
static int hf_bt_utp_connection_id_v0 = -1;
|
||||
static int hf_bt_utp_connection_id_v1 = -1;
|
||||
static int hf_bt_utp_stream = -1;
|
||||
static int hf_bt_utp_timestamp_sec = -1;
|
||||
static int hf_bt_utp_timestamp_us = -1;
|
||||
static int hf_bt_utp_timestamp_diff_us = -1;
|
||||
|
@ -150,6 +156,106 @@ static gint ett_bt_utp_extension = -1;
|
|||
static gboolean enable_version0 = FALSE;
|
||||
static guint max_window_size = V1_MAX_WINDOW_SIZE;
|
||||
|
||||
static guint32 bt_utp_stream_count = 0;
|
||||
|
||||
typedef struct {
|
||||
guint32 stream;
|
||||
|
||||
#if 0
|
||||
/* XXX: Some other things to add in later. The flow will contain
|
||||
* multisegment PDU handling, base sequence numbers, etc. */
|
||||
utp_flow_t flow[2];
|
||||
nstime_t ts_first;
|
||||
nstime_t ts_prev;
|
||||
guint8 conversation_completeness;
|
||||
#endif
|
||||
} utp_stream_info_t;
|
||||
|
||||
/* Per-packet header information. */
|
||||
typedef struct {
|
||||
guint8 type;
|
||||
gboolean v0;
|
||||
guint32 connection; /* The prelease "V0" version is 32 bit */
|
||||
guint32 stream;
|
||||
guint16 seq;
|
||||
guint16 ack;
|
||||
guint32 seglen; /* reported length remaining */
|
||||
} utp_info_t;
|
||||
|
||||
static utp_stream_info_t*
|
||||
get_utp_stream_info(packet_info *pinfo, utp_info_t *utp_info)
|
||||
{
|
||||
conversation_t* conv;
|
||||
utp_stream_info_t *stream_info;
|
||||
guint32 id_up, id_down;
|
||||
|
||||
/* Handle connection ID wrapping correctly. (Mainline libutp source
|
||||
* does not appear to do this, probably fails to connect if the random
|
||||
* connection ID is GMAX_UINT16 and tries again.)
|
||||
*/
|
||||
if (utp_info->v0) {
|
||||
id_up = utp_info->connection+1;
|
||||
id_down = utp_info->connection-1;
|
||||
} else {
|
||||
id_up = (guint16)(utp_info->connection+1);
|
||||
id_down = (guint16)(utp_info->connection-1);
|
||||
}
|
||||
|
||||
if (utp_info->type == ST_SYN) {
|
||||
/* SYN packets are special, they have the connection ID for the other
|
||||
* side, and allow us to know both.
|
||||
*/
|
||||
conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP,
|
||||
id_up, utp_info->connection, 0);
|
||||
if (!conv) {
|
||||
/* XXX: A SYN for between the same pair of hosts with a duplicate
|
||||
* connection ID in the same direction is almost surely a retransmission
|
||||
* (unless there's a client that doesn't actually generate random IDs.)
|
||||
* We could check to see if we've gotten a FIN or RST on that same
|
||||
* connection, and also could do like TCP and see if the initial sequence
|
||||
* number matches. (The latter still doesn't help if the client also
|
||||
* doesn't start with random sequence numbers.)
|
||||
*/
|
||||
conv = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP, id_up, utp_info->connection, 0);
|
||||
}
|
||||
} else {
|
||||
/* For non-SYN packets, we know our connection ID, but we don't know if
|
||||
* the other side has our ID+1 (src initiated the connection) or our ID-1
|
||||
* (dst initiated). We also don't want find_conversation() to accidentally
|
||||
* call conversation_set_port2() with the wrong ID. So first we see if
|
||||
* we have a wildcarded conversation around (if we've seen previous
|
||||
* non-SYN packets from our current direction but none in the other.)
|
||||
*/
|
||||
conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP, utp_info->connection, 0, NO_PORT_B);
|
||||
if (!conv) {
|
||||
/* Do we have a complete conversation originated by our src, or
|
||||
* possibly a wildcarded conversation originated in this direction
|
||||
* (but we saw a non-SYN for the non-initiating side first)? */
|
||||
conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP, utp_info->connection, id_up, 0);
|
||||
if (!conv) {
|
||||
/* As above, but dst initiated? */
|
||||
conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP, utp_info->connection, id_down, 0);
|
||||
if (!conv) {
|
||||
/* Didn't find it, so create a new wildcarded conversation. When we
|
||||
* get a packet for the other direction, find_conversation() above
|
||||
* will set port2 with the other connection ID.
|
||||
*/
|
||||
conv = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_BT_UTP, utp_info->connection, 0, NO_PORT2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream_info = (utp_stream_info_t *)conversation_get_proto_data(conv, proto_bt_utp);
|
||||
if (!stream_info) {
|
||||
stream_info = wmem_new0(wmem_file_scope(), utp_stream_info_t);
|
||||
stream_info->stream = bt_utp_stream_count++;
|
||||
conversation_add_proto_data(conv, proto_bt_utp, stream_info);
|
||||
}
|
||||
|
||||
return stream_info;
|
||||
}
|
||||
|
||||
static gint
|
||||
get_utp_version(tvbuff_t *tvb) {
|
||||
guint8 v0_flags;
|
||||
|
@ -220,8 +326,18 @@ get_utp_version(tvbuff_t *tvb) {
|
|||
static int
|
||||
dissect_utp_header_v0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 *extension_type)
|
||||
{
|
||||
/* "Original" (V0) */
|
||||
proto_tree_add_item(tree, hf_bt_utp_connection_id_v0, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
/* "Original" (V0) */
|
||||
utp_info_t *p_utp_info = NULL;
|
||||
utp_stream_info_t *stream_info = NULL;
|
||||
|
||||
proto_item *ti;
|
||||
guint32 type, connection, win, seq, ack;
|
||||
|
||||
p_utp_info = wmem_new(pinfo->pool, utp_info_t);
|
||||
p_utp_info->v0 = TRUE;
|
||||
p_add_proto_data(pinfo->pool, pinfo, proto_bt_utp, 0, p_utp_info);
|
||||
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_connection_id_v0, tvb, offset, 4, ENC_BIG_ENDIAN, &connection);
|
||||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_timestamp_sec, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
offset += 4;
|
||||
|
@ -229,20 +345,38 @@ dissect_utp_header_v0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o
|
|||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_timestamp_diff_us, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_wnd_size_v0, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_wnd_size_v0, tvb, offset, 1, ENC_BIG_ENDIAN, &win);
|
||||
offset += 1;
|
||||
proto_tree_add_item(tree, hf_bt_utp_next_extension_type, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
|
||||
*extension_type = tvb_get_guint8(tvb, offset);
|
||||
offset += 1;
|
||||
proto_tree_add_item(tree, hf_bt_utp_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
col_append_fstr(pinfo->cinfo, COL_INFO, " Type: %s", val_to_str(tvb_get_guint8(tvb, offset), bt_utp_type_vals, "Unknown %d"));
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_flags, tvb, offset, 1, ENC_BIG_ENDIAN, &type);
|
||||
offset += 1;
|
||||
|
||||
col_append_fstr(pinfo->cinfo, COL_INFO, "Connection ID:%d [%s]", connection, val_to_str(type, bt_utp_type_vals, "Unknown %d"));
|
||||
p_utp_info->type = type;
|
||||
p_utp_info->connection = connection;
|
||||
|
||||
proto_tree_add_item(tree, hf_bt_utp_seq_nr, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(tree, hf_bt_utp_ack_nr, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||||
offset += 2;
|
||||
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_seq_nr, tvb, offset, 2, ENC_BIG_ENDIAN, &seq);
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Seq", seq, " ");
|
||||
p_utp_info->seq = seq;
|
||||
offset += 2;
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_ack_nr, tvb, offset, 2, ENC_BIG_ENDIAN, &ack);
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Ack", ack, " ");
|
||||
p_utp_info->ack = ack;
|
||||
offset += 2;
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Win", win, " ");
|
||||
|
||||
stream_info = get_utp_stream_info(pinfo, p_utp_info);
|
||||
ti = proto_tree_add_uint(tree, hf_bt_utp_stream, tvb, offset, 0, stream_info->stream);
|
||||
p_utp_info->stream = stream_info->stream;
|
||||
proto_item_set_generated(ti);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -250,26 +384,55 @@ static int
|
|||
dissect_utp_header_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint8 *extension_type)
|
||||
{
|
||||
/* V1 */
|
||||
utp_info_t *p_utp_info = NULL;
|
||||
utp_stream_info_t *stream_info = NULL;
|
||||
|
||||
proto_item *ti;
|
||||
|
||||
guint32 type, connection, win, seq, ack;
|
||||
|
||||
p_utp_info = wmem_new(pinfo->pool, utp_info_t);
|
||||
p_utp_info->v0 = FALSE;
|
||||
p_add_proto_data(pinfo->pool, pinfo, proto_bt_utp, 0, p_utp_info);
|
||||
|
||||
proto_tree_add_item(tree, hf_bt_utp_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item(tree, hf_bt_utp_type, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
col_append_fstr(pinfo->cinfo, COL_INFO, " Type: %s", val_to_str((tvb_get_guint8(tvb, offset) >> 4), bt_utp_type_vals, "Unknown %d"));
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_type, tvb, offset, 1, ENC_BIG_ENDIAN, &type);
|
||||
offset += 1;
|
||||
proto_tree_add_item(tree, hf_bt_utp_next_extension_type, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
*extension_type = tvb_get_guint8(tvb, offset);
|
||||
offset += 1;
|
||||
proto_tree_add_item(tree, hf_bt_utp_connection_id_v1, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_connection_id_v1, tvb, offset, 2, ENC_BIG_ENDIAN, &connection);
|
||||
offset += 2;
|
||||
|
||||
col_append_fstr(pinfo->cinfo, COL_INFO, "Connection ID:%d [%s]", connection, val_to_str(type, bt_utp_type_vals, "Unknown %d"));
|
||||
p_utp_info->type = type;
|
||||
p_utp_info->connection = connection;
|
||||
|
||||
proto_tree_add_item(tree, hf_bt_utp_timestamp_us, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_timestamp_diff_us, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_wnd_size_v1, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_wnd_size_v1, tvb, offset, 4, ENC_BIG_ENDIAN, &win);
|
||||
offset += 4;
|
||||
proto_tree_add_item(tree, hf_bt_utp_seq_nr, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_seq_nr, tvb, offset, 2, ENC_BIG_ENDIAN, &seq);
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Seq", seq, " ");
|
||||
p_utp_info->seq = seq;
|
||||
offset += 2;
|
||||
proto_tree_add_item(tree, hf_bt_utp_ack_nr, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||||
proto_tree_add_item_ret_uint(tree, hf_bt_utp_ack_nr, tvb, offset, 2, ENC_BIG_ENDIAN, &ack);
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Ack", ack, " ");
|
||||
p_utp_info->ack = ack;
|
||||
offset += 2;
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Win", win, " ");
|
||||
|
||||
stream_info = get_utp_stream_info(pinfo, p_utp_info);
|
||||
ti = proto_tree_add_uint(tree, hf_bt_utp_stream, tvb, offset, 0, stream_info->stream);
|
||||
p_utp_info->stream = stream_info->stream;
|
||||
proto_item_set_generated(ti);
|
||||
|
||||
/* XXX: Multisegment PDUs are the top priority to add, but a number of
|
||||
* other features in the TCP dissector would be useful- relative sequence
|
||||
* numbers, conversation completeness, maybe even tracking SACKs.
|
||||
*/
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -362,10 +525,7 @@ dissect_bt_utp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _
|
|||
|
||||
/* set the protocol column */
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT-uTP");
|
||||
/* set the info column */
|
||||
col_set_str( pinfo->cinfo, COL_INFO, "uTorrent Transport Protocol" );
|
||||
|
||||
len_tvb = tvb_reported_length(tvb);
|
||||
col_clear(pinfo->cinfo, COL_INFO);
|
||||
|
||||
/* Determine header version */
|
||||
|
||||
|
@ -382,9 +542,13 @@ dissect_bt_utp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _
|
|||
|
||||
offset = dissect_utp_extension(tvb, pinfo, sub_tree, offset, &extension_type);
|
||||
|
||||
len_tvb = tvb_captured_length_remaining(tvb, offset);
|
||||
if(len_tvb > 0)
|
||||
len_tvb = tvb_reported_length_remaining(tvb, offset);
|
||||
if(len_tvb > 0) {
|
||||
col_append_str_uint(pinfo->cinfo, COL_INFO, "Len", len_tvb, " ");
|
||||
proto_tree_add_item(sub_tree, hf_bt_utp_data, tvb, offset, len_tvb, ENC_NA);
|
||||
utp_info_t *p_utp_info = (utp_info_t *)p_get_proto_data(pinfo->pool, pinfo, proto_bt_utp, 0);
|
||||
p_utp_info->seglen = len_tvb;
|
||||
}
|
||||
|
||||
return offset+len_tvb;
|
||||
}
|
||||
|
@ -411,6 +575,12 @@ dissect_bt_utp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *d
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
utp_init(void)
|
||||
{
|
||||
bt_utp_stream_count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
proto_register_bt_utp(void)
|
||||
{
|
||||
|
@ -465,6 +635,11 @@ proto_register_bt_utp(void)
|
|||
FT_UINT16, BASE_DEC, NULL, 0x0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_bt_utp_stream,
|
||||
{ "Stream index", "bt-utp.stream",
|
||||
FT_UINT32, BASE_DEC, NULL, 0x0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_bt_utp_timestamp_sec,
|
||||
{ "Timestamp seconds", "bt-utp.timestamp_sec",
|
||||
FT_UINT32, BASE_DEC, NULL, 0x0,
|
||||
|
@ -535,6 +710,8 @@ proto_register_bt_utp(void)
|
|||
|
||||
proto_register_field_array(proto_bt_utp, hf, array_length(hf));
|
||||
proto_register_subtree_array(ett, array_length(ett));
|
||||
|
||||
register_init_routine(utp_init);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* packet-bt-utp.h
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __PACKET_BT_UTP_H__
|
||||
#define __PACKET_BT_UTP_H__
|
||||
|
||||
#include "ws_symbol_export.h"
|
||||
|
||||
#include <epan/conversation.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if 0
|
||||
WS_DLL_PUBLIC void
|
||||
utp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
||||
gboolean proto_desegment, guint fixed_len,
|
||||
guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*),
|
||||
dissector_t dissect_pdu, void* dissector_data);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue