Added "Follow DCCP stream" feature.

This pull request includes:
* The "Follow DCCP stream" feature.
* Updated docbook documentation for the "Follow DCCP stream" feature.
* Test for the feature.
* Corresponding packet trace for the test.
This commit is contained in:
Thomas Dreibholz 2021-02-22 12:48:46 +01:00
parent a57a32c04e
commit 2e7f2ffb7a
No known key found for this signature in database
GPG Key ID: 5CD5D12AA0877B49
17 changed files with 494 additions and 4 deletions

View File

@ -2621,8 +2621,16 @@ Kees Cook <kees[AT]outflux.net> {
TiVoConnect Discovery Protocol
}
Thomas Dreibholz <dreibh[AT]iem.uni-due.de> {
Thomas Dreibholz <dreibh[AT]iem.uni-due.de>/<dreibh[AT]simula.no> {
Calculation Application Protocol support
Component Status Protocol support
Fractal Generator Protocol support
DCCP dissector improvements
Follow DCCP stream feature
NetPerfMeter Protocol support
Ping Pong Protocol support
RSerPol protocol stack
SCTP dissector improvements
Scripting Service Protocol support
}

View File

@ -3462,6 +3462,7 @@ set(_test_group_list
suite_extcaps
suite_fileformats
suite_follow
suite_follow_dccp
suite_io
suite_mergecap
suite_netperfmeter

View File

@ -784,6 +784,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
get_data_source_name@Base 1.9.1
get_data_source_tvb@Base 1.9.1
get_data_source_tvb_by_name@Base 2.3.0
get_dccp_stream_count@Base 3.5.0
get_dissector_names@Base 1.12.0~rc1
get_dissector_table_param@Base 1.99.2
get_dissector_table_selector_type@Base 1.9.1

View File

@ -46,6 +46,8 @@ They previously shipped with Npcap 1.00.
created to dissect DLT_ETW packets so Wireshark can display the DLT_ETW packet header, its message and packet_etw dissector
calls packet_mbim sub_dissector if its provider matches the MBIM provider GUID.
* "Follow DCCP stream" feature to filter for and extract the contents of DCCP streams.
// === Removed Features and Support
//=== Removed Dissectors

View File

@ -22,7 +22,7 @@ display filter to show only the packets in a TLS or SSL stream. If so,
Wiresharks ability to follow protocol streams will be useful to you.
To filter to a particular stream,
select a TCP, UDP, TLS, or HTTP packet in the packet list of the stream/connection you are
select a TCP, UDP, DCCP, TLS, HTTP, HTTP/2 or QUIC packet in the packet list of the stream/connection you are
interested in and then select the menu item menu:Analyze[Follow TCP Stream]
(or use the context menu in the packet list). Wireshark will set an
appropriate display filter and display a dialog box with the data from the
@ -628,7 +628,7 @@ For example, a conversation containing only a three-way handshake will be found
with the filter 'tcp.completeness==7' (1+2+4) while a complete conversation with
data transfer will be found with a longer filter as closing a connection can be
associated with FIN or RST packets, or even both :
'tcp.completeness==31 or tcp.completeness==47 or tcp.completeness==63'
'tcp.completeness==31 or tcp.completeness==47 or tcp.completeness==63'
[[ChAdvTimestamps]]

View File

@ -162,6 +162,9 @@ See <<ChAdvFollowStreamSection>>.
|menu:Follow[UDP Stream] |menu:Analyze[] |
Same functionality as “Follow TCP Stream” but for UDP “streams”.
|menu:Follow[DCCP Stream] |menu:Analyze[] |
Same functionality as “Follow TCP Stream” but for DCCP streams.
|menu:Follow[TLS Stream] |menu:Analyze[] |
Same functionality as “Follow TCP Stream” but for TLS or SSL streams.
See the wiki page on link:{wireshark-wiki-url}SSL[SSL] for instructions

View File

@ -6,6 +6,8 @@
*
* Francesco Fondelli <francesco dot fondelli, gmail dot com>
*
* Copyright 2020-2021 by Thomas Dreibholz <dreibh [AT] simula.no>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
@ -45,8 +47,12 @@
#include <epan/ipproto.h>
#include <epan/in_cksum.h>
#include <epan/prefs.h>
#include <epan/follow.h>
#include <epan/expert.h>
#include <epan/conversation.h>
#include <epan/conversation_table.h>
#include <epan/conversation_filter.h>
#include <epan/exported_pdu.h>
#include <epan/tap.h>
#include <wsutil/str_util.h>
#include <wsutil/utf8_entities.h>
@ -181,10 +187,12 @@ static const unit_name_string units_bytes_sec = { "bytes/sec", NULL };
static int proto_dccp = -1;
static int dccp_tap = -1;
static int dccp_follow_tap = -1;
static int hf_dccp_srcport = -1;
static int hf_dccp_dstport = -1;
static int hf_dccp_port = -1;
static int hf_dccp_stream = -1;
static int hf_dccp_data_offset = -1;
static int hf_dccp_ccval = -1;
static int hf_dccp_cscov = -1;
@ -246,6 +254,7 @@ static heur_dissector_list_t heur_subdissector_list;
static gboolean dccp_summary_in_tree = TRUE;
static gboolean try_heuristic_first = FALSE;
static gboolean dccp_check_checksum = TRUE;
static guint32 dccp_stream_count;
static void
decode_dccp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
@ -257,6 +266,11 @@ decode_dccp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
next_tvb = tvb_new_subset_remaining(tvb, offset);
/* If the user has a "Follow DCCP Stream" window loading, pass a pointer
to the payload tvb through the tap system. */
if (have_tap_listener(dccp_follow_tap))
tap_queue_packet(dccp_follow_tap, pinfo, next_tvb);
/*
* determine if this packet is part of a conversation and call dissector
* for the conversation if available
@ -322,6 +336,251 @@ decode_dccp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
call_data_dissector(next_tvb, pinfo, tree);
}
/* Conversation and process code originally copied from packet-udp.c */
static struct dccp_analysis *
init_dccp_conversation_data(packet_info *pinfo)
{
struct dccp_analysis *dccpd;
/* Initialize the dccp protocol data structure to add to the dccp conversation */
dccpd = wmem_new0(wmem_file_scope(), struct dccp_analysis);
/*
dccpd->flow1.username = NULL;
dccpd->flow1.command = NULL;
dccpd->flow2.username = NULL;
dccpd->flow2.command = NULL;
*/
dccpd->stream = dccp_stream_count++;
dccpd->ts_first = pinfo->abs_ts;
dccpd->ts_prev = pinfo->abs_ts;
return dccpd;
}
static struct dccp_analysis *
get_dccp_conversation_data(conversation_t *conv, packet_info *pinfo)
{
int direction;
struct dccp_analysis *dccpd;
/* Get the data for this conversation */
dccpd=(struct dccp_analysis *)conversation_get_proto_data(conv, proto_dccp);
/* If the conversation was just created or it matched a
* conversation with template options, dccpd will not
* have been initialized. So, initialize
* a new dccpd structure for the conversation.
*/
if (!dccpd) {
dccpd = init_dccp_conversation_data(pinfo);
conversation_add_proto_data(conv, proto_dccp, dccpd);
}
/* check direction and get ua lists */
direction=cmp_address(&pinfo->src, &pinfo->dst);
/* if the addresses are equal, match the ports instead */
if (direction == 0) {
direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
}
if (direction >= 0) {
dccpd->fwd=&(dccpd->flow1);
dccpd->rev=&(dccpd->flow2);
} else {
dccpd->fwd=&(dccpd->flow2);
dccpd->rev=&(dccpd->flow1);
}
return dccpd;
}
static const char* dccp_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter)
{
if (filter == CONV_FT_SRC_PORT)
return "dccp.srcport";
if (filter == CONV_FT_DST_PORT)
return "dccp.dstport";
if (filter == CONV_FT_ANY_PORT)
return "dccp.port";
if(!conv) {
return CONV_FILTER_INVALID;
}
if (filter == CONV_FT_SRC_ADDRESS) {
if (conv->src_address.type == AT_IPv4)
return "ip.src";
if (conv->src_address.type == AT_IPv6)
return "ipv6.src";
}
if (filter == CONV_FT_DST_ADDRESS) {
if (conv->dst_address.type == AT_IPv4)
return "ip.dst";
if (conv->dst_address.type == AT_IPv6)
return "ipv6.dst";
}
if (filter == CONV_FT_ANY_ADDRESS) {
if (conv->src_address.type == AT_IPv4)
return "ip.addr";
if (conv->src_address.type == AT_IPv6)
return "ipv6.addr";
}
return CONV_FILTER_INVALID;
}
static ct_dissector_info_t dccp_ct_dissector_info = {&dccp_conv_get_filter_type};
static tap_packet_status
dccpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
{
conv_hash_t *hash = (conv_hash_t*) pct;
const e_dccphdr *dccphdr=(const e_dccphdr *)vip;
add_conversation_table_data_with_conv_id(hash, &dccphdr->ip_src, &dccphdr->ip_dst, dccphdr->sport, dccphdr->dport, (conv_id_t) dccphdr->stream, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, &dccp_ct_dissector_info, ENDPOINT_DCCP);
return TAP_PACKET_REDRAW;
}
static const char* dccp_host_get_filter_type(hostlist_talker_t* host, conv_filter_type_e filter)
{
if (filter == CONV_FT_SRC_PORT)
return "dccp.srcport";
if (filter == CONV_FT_DST_PORT)
return "dccp.dstport";
if (filter == CONV_FT_ANY_PORT)
return "dccp.port";
if(!host) {
return CONV_FILTER_INVALID;
}
if (filter == CONV_FT_SRC_ADDRESS) {
if (host->myaddress.type == AT_IPv4)
return "ip.src";
if (host->myaddress.type == AT_IPv6)
return "ipv6.src";
}
if (filter == CONV_FT_DST_ADDRESS) {
if (host->myaddress.type == AT_IPv4)
return "ip.dst";
if (host->myaddress.type == AT_IPv6)
return "ipv6.dst";
}
if (filter == CONV_FT_ANY_ADDRESS) {
if (host->myaddress.type == AT_IPv4)
return "ip.addr";
if (host->myaddress.type == AT_IPv6)
return "ipv6.addr";
}
return CONV_FILTER_INVALID;
}
static hostlist_dissector_info_t dccp_host_dissector_info = {&dccp_host_get_filter_type};
static tap_packet_status
dccpip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
{
conv_hash_t *hash = (conv_hash_t*) pit;
const e_dccphdr *dccphdr=(const e_dccphdr *)vip;
/* Take two "add" passes per packet, adding for each direction, ensures that all
packets are counted properly (even if address is sending to itself)
XXX - this could probably be done more efficiently inside hostlist_table */
add_hostlist_table_data(hash, &dccphdr->ip_src, dccphdr->sport, TRUE, 1, pinfo->fd->pkt_len, &dccp_host_dissector_info, ENDPOINT_DCCP);
add_hostlist_table_data(hash, &dccphdr->ip_dst, dccphdr->dport, FALSE, 1, pinfo->fd->pkt_len, &dccp_host_dissector_info, ENDPOINT_DCCP);
return TAP_PACKET_REDRAW;
}
/* Return the current stream count */
guint32 get_dccp_stream_count(void)
{
return dccp_stream_count;
}
static gboolean
dccp_filter_valid(packet_info *pinfo)
{
return proto_is_frame_protocol(pinfo->layers, "dccp");
}
static gchar*
dccp_build_filter(packet_info *pinfo)
{
if( pinfo->net_src.type == AT_IPv4 && pinfo->net_dst.type == AT_IPv4 ) {
/* DCCP over IPv4 */
return g_strdup_printf("(ip.addr eq %s and ip.addr eq %s) and (dccp.port eq %d and dccp.port eq %d)",
address_to_str(pinfo->pool, &pinfo->net_src),
address_to_str(pinfo->pool, &pinfo->net_dst),
pinfo->srcport, pinfo->destport );
}
if( pinfo->net_src.type == AT_IPv6 && pinfo->net_dst.type == AT_IPv6 ) {
/* DCCP over IPv6 */
return g_strdup_printf("(ipv6.addr eq %s and ipv6.addr eq %s) and (dccp.port eq %d and dccp.port eq %d)",
address_to_str(pinfo->pool, &pinfo->net_src),
address_to_str(pinfo->pool, &pinfo->net_dst),
pinfo->srcport, pinfo->destport );
}
return NULL;
}
static gchar *dccp_follow_conv_filter(packet_info *pinfo, guint *stream, guint *sub_stream _U_)
{
conversation_t *conv;
struct dccp_analysis *dccpd;
if( ((pinfo->net_src.type == AT_IPv4 && pinfo->net_dst.type == AT_IPv4) ||
(pinfo->net_src.type == AT_IPv6 && pinfo->net_dst.type == AT_IPv6))
&& (conv=find_conversation_pinfo(pinfo, 0)) != NULL )
{
/* DCCP over IPv4/6 */
dccpd = get_dccp_conversation_data(conv, pinfo);
*stream = dccpd->stream;
return g_strdup_printf("dccp.stream eq %u", dccpd->stream);
}
return NULL;
}
static gchar *dccp_follow_index_filter(guint stream, guint sub_stream _U_)
{
return g_strdup_printf("dccp.stream eq %u", stream);
}
static gchar *dccp_follow_address_filter(address *src_addr, address *dst_addr, int src_port, int dst_port)
{
const gchar *ip_version = src_addr->type == AT_IPv6 ? "v6" : "";
gchar src_addr_str[WS_INET6_ADDRSTRLEN];
gchar dst_addr_str[WS_INET6_ADDRSTRLEN];
address_to_str_buf(src_addr, src_addr_str, sizeof(src_addr_str));
address_to_str_buf(dst_addr, dst_addr_str, sizeof(dst_addr_str));
return g_strdup_printf("((ip%s.src eq %s and dccp.srcport eq %d) and "
"(ip%s.dst eq %s and dccp.dstport eq %d))"
" or "
"((ip%s.src eq %s and dccp.srcport eq %d) and "
"(ip%s.dst eq %s and dccp.dstport eq %d))",
ip_version, src_addr_str, src_port,
ip_version, dst_addr_str, dst_port,
ip_version, dst_addr_str, dst_port,
ip_version, src_addr_str, src_port);
}
/*
* decode a variable-length number of nbytes starting at offset. Based on
* a concept by Arnaldo de Melo
@ -620,6 +879,7 @@ static int
dissect_dccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
proto_tree *dccp_tree;
proto_item *item;
proto_tree *dccp_options_tree = NULL;
proto_item *dccp_item = NULL;
proto_item *hidden_item, *offset_item;
@ -632,6 +892,8 @@ dissect_dccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_
guint advertised_dccp_header_len = 0;
guint options_len = 0;
e_dccphdr *dccph;
conversation_t *conv = NULL;
struct dccp_analysis *dccpd;
dccph = wmem_new0(wmem_packet_scope(), e_dccphdr);
dccph->sport = tvb_get_ntohs(tvb, offset);
@ -669,6 +931,17 @@ dissect_dccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_
pinfo->srcport = dccph->sport;
pinfo->destport = dccph->dport;
/* find (or create if needed) the conversation for this DCCP session */
conv = find_or_create_conversation(pinfo);
dccpd = get_dccp_conversation_data(conv, pinfo);
item = proto_tree_add_uint(dccp_tree, hf_dccp_stream, tvb, offset, 0, dccpd->stream);
proto_item_set_generated(item);
/* Copy the stream index into the header as well to make it available
* to tap listeners.
*/
dccph->stream = dccpd->stream;
dccph->data_offset = tvb_get_guint8(tvb, offset);
advertised_dccp_header_len = dccph->data_offset * 4;
offset_item = proto_tree_add_uint(dccp_tree, hf_dccp_data_offset, tvb, offset, 1,
@ -1028,6 +1301,12 @@ dissect_dccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_
return tvb_reported_length(tvb);
}
static void
dccp_init(void)
{
dccp_stream_count = 0;
}
void
proto_register_dccp(void)
{
@ -1058,6 +1337,14 @@ proto_register_dccp(void)
NULL, HFILL
}
},
{
&hf_dccp_stream,
{
"Stream index", "dccp.stream",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL
}
},
{
&hf_dccp_data_offset,
{
@ -1328,6 +1615,13 @@ proto_register_dccp(void)
"Check the validity of the DCCP checksum when possible",
"Whether to check the validity of the DCCP checksum",
&dccp_check_checksum);
register_conversation_table(proto_dccp, FALSE, dccpip_conversation_packet, dccpip_hostlist_packet);
register_conversation_filter("dccp", "DCCP", dccp_filter_valid, dccp_build_filter);
register_follow_stream(proto_dccp, "dccp_follow", dccp_follow_conv_filter, dccp_follow_index_filter, dccp_follow_address_filter,
dccp_port_to_display, follow_tvb_tap_listener);
register_init_routine(dccp_init);
}
void
@ -1338,6 +1632,7 @@ proto_reg_handoff_dccp(void)
dccp_handle = create_dissector_handle(dissect_dccp, proto_dccp);
dissector_add_uint("ip.proto", IP_PROTO_DCCP, dccp_handle);
dccp_tap = register_tap("dccp");
dccp_follow_tap = register_tap("dccp_follow");
}
/*

View File

@ -6,6 +6,8 @@
*
* Francesco Fondelli <francesco dot fondelli, gmail dot com>
*
* Copyright 2020-2021 by Thomas Dreibholz <dreibh [AT] simula.no>
*
* template taken from packet-udp.c
*
* Wireshark - Network traffic analyzer
@ -18,6 +20,10 @@
#ifndef __PACKET_DCCP_H__
#define __PACKET_DCCP_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* DCCP structs and definitions */
typedef struct _e_dccphdr {
guint16 sport;
@ -44,10 +50,74 @@ typedef struct _e_dccphdr {
guint8 data2;
guint8 data3;
guint32 stream; /* this stream index field is included to help differentiate when address/port pairs are reused */
address ip_src;
address ip_dst;
} e_dccphdr;
/* Conversation and process structures originally copied from packet-tcp.c */
typedef struct _dccp_flow_t {
/* Process info, currently discovered via IPFIX */
guint32 process_uid; /* UID of local process */
guint32 process_pid; /* PID of local process */
gchar *username; /* Username of the local process */
gchar *command; /* Local process name + path + args */
} dccp_flow_t;
struct dccp_analysis {
/* These two structs are managed based on comparing the source
* and destination addresses and, if they're equal, comparing
* the source and destination ports.
*
* If the source is greater than the destination, then stuff
* sent from src is in ual1.
*
* If the source is less than the destination, then stuff
* sent from src is in ual2.
*
* XXX - if the addresses and ports are equal, we don't guarantee
* the behavior.
*/
dccp_flow_t flow1;
dccp_flow_t flow2;
/* These pointers are set by get_dccp_conversation_data()
* fwd point in the same direction as the current packet
* and rev in the reverse direction
*/
dccp_flow_t *fwd;
dccp_flow_t *rev;
/* Keep track of dccp stream numbers instead of using the conversation
* index (as how it was done before). This prevents gaps in the
* stream index numbering
*/
guint32 stream;
/* Remember the timestamp of the first frame seen in this dccp
* conversation to be able to calculate a relative time compared
* to the start of this conversation
*/
nstime_t ts_first;
/* Remember the timestamp of the frame that was last seen in this
* dccp conversation to be able to calculate a delta time compared
* to previous frame in this conversation
*/
nstime_t ts_prev;
};
/** Get the current number of DCCP streams
*
* @return The number of DCCP streams
*/
WS_DLL_PUBLIC guint32 get_dccp_stream_count(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __PACKET_DCCP_H__ */
/*

View File

@ -42,6 +42,7 @@ typedef enum {
FOLLOW_TCP,
FOLLOW_TLS,
FOLLOW_UDP,
FOLLOW_DCCP,
FOLLOW_HTTP,
FOLLOW_HTTP2,
FOLLOW_QUIC,

Binary file not shown.

71
test/suite_follow_dccp.py Normal file
View File

@ -0,0 +1,71 @@
#
# Wireshark tests
#
# Copyright 2020-2021 by Thomas Dreibholz <dreibh [AT] simula.no>
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
'''Follow DCCP Stream tests'''
import subprocesstest
import fixtures
@fixtures.mark_usefixtures('test_env')
@fixtures.uses_fixtures
class case_follow_dccp(subprocesstest.SubprocessTestCase):
def test_follow_dccp_bad_conditions(self, cmd_tshark, capture_file):
'''Checks whether Follow DCCP correctly handles some tests.'''
# Test 1:
# 1. Identification of DCCP Flow #9
# 2. Selection and decoding of DCCP Flow #9
proc = self.assertRun((cmd_tshark,
'-r', capture_file('netperfmeter-dccp.pcapng.gz'),
'-qz', 'follow,dccp,hex,9',
))
self.assertIn("""\
===================================================================
Follow: dccp,hex
Filter: dccp.stream eq 9
Node 0: 127.0.0.1:43933
Node 1: 127.0.0.1:9000
00000000 04 00 00 1a 00 00 00 09 4b cd f3 aa 30 3c 67 74 ........ K...0<gt
00000010 f2 41 ee 5f c8 10 1f 41 00 00 .A._...A ..
0000001A 05 03 27 10 00 00 00 09 f2 41 ee 5f c8 10 1f 41 ..'..... .A._...A
0000002A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
0000003A 00 00 00 00 00 00 00 00 00 05 ba 0a bf 18 68 19 ........ ......h.
0000004A 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d .. !"#$% &'()*+,-
0000005A 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d ./012345 6789:;<=
0000006A 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d >?@ABCDE FGHIJKLM
0000007A 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d NOPQRSTU VWXYZ[\]
0000008A 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d ^_`abcde fghijklm
0000009A 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d nopqrstu vwxyz{|}
000000AA 7e 7f 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b ~... !"# $%&'()*+
000000BA 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b ,-./0123 456789:;
000000CA 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b <=>?@ABC DEFGHIJK
000000DA 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b LMNOPQRS TUVWXYZ[
000000EA 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b \]^_`abc defghijk
000000FA 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b lmnopqrs tuvwxyz{
0000010A 7c 7d 7e 7f 1e 1f 20 21 22 23 24 25 26 27 28 29 |}~... ! "#$%&'()
0000011A 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 *+,-./01 23456789
""".replace("\r\n", "\n"),
proc.stdout_str)
# Test 2:
# Trying identification of not-existing DCCP Flow #10
proc = self.assertRun((cmd_tshark,
'-r', capture_file('netperfmeter-dccp.pcapng.gz'),
'-qz', 'follow,dccp,hex,10',
))
self.assertIn("""\
===================================================================
Follow: dccp,hex
Filter: dccp.stream eq 10
Node 0: :0
Node 1: :0
===================================================================
""".replace("\r\n", "\n"),
proc.stdout_str)

View File

@ -16,6 +16,7 @@
#include "epan/follow.h"
#include "epan/dissectors/packet-tcp.h"
#include "epan/dissectors/packet-udp.h"
#include "epan/dissectors/packet-dccp.h"
#include "epan/dissectors/packet-http2.h"
#include "epan/dissectors/packet-quic.h"
#include "epan/prefs.h"
@ -93,6 +94,9 @@ FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, follow_
case FOLLOW_UDP:
follower_ = get_follow_by_name("UDP");
break;
case FOLLOW_DCCP:
follower_ = get_follow_by_name("DCCP");
break;
case FOLLOW_HTTP:
follower_ = get_follow_by_name("HTTP");
break;
@ -519,6 +523,7 @@ FollowStreamDialog::readStream()
case FOLLOW_TCP :
case FOLLOW_UDP :
case FOLLOW_DCCP :
case FOLLOW_HTTP :
case FOLLOW_HTTP2:
case FOLLOW_QUIC:
@ -941,6 +946,18 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
break;
}
case FOLLOW_DCCP:
{
int stream_count = get_dccp_stream_count();
ui->streamNumberSpinBox->blockSignals(true);
ui->streamNumberSpinBox->setMaximum(stream_count-1);
ui->streamNumberSpinBox->setValue(stream_num);
ui->streamNumberSpinBox->blockSignals(false);
ui->streamNumberSpinBox->setToolTip(tr("%Ln total stream(s).", "", stream_count));
ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip());
break;
}
case FOLLOW_HTTP2:
{
int stream_count = get_tcp_stream_count();

View File

@ -574,6 +574,7 @@ private slots:
void openFollowStreamDialogForType(follow_type_t type);
void on_actionAnalyzeFollowTCPStream_triggered();
void on_actionAnalyzeFollowUDPStream_triggered();
void on_actionAnalyzeFollowDCCPStream_triggered();
void on_actionAnalyzeFollowTLSStream_triggered();
void on_actionAnalyzeFollowHTTPStream_triggered();
void on_actionAnalyzeFollowHTTP2Stream_triggered();

View File

@ -1710,6 +1710,17 @@
<string notr="true">Ctrl+Alt+Shift+U</string>
</property>
</action>
<action name="actionAnalyzeFollowDCCPStream">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>DCCP Stream</string>
</property>
<property name="shortcut">
<string notr="true">Ctrl+Alt+Shift+E</string>
</property>
</action>
<action name="actionAnalyzeFollowTLSStream">
<property name="enabled">
<bool>false</bool>

View File

@ -1123,7 +1123,7 @@ void MainWindow::recentActionTriggered() {
void MainWindow::setMenusForSelectedPacket()
{
gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_tls = FALSE, is_rtp = FALSE, is_lte_rlc = FALSE,
gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_dccp = FALSE, is_sctp = FALSE, is_tls = FALSE, is_rtp = FALSE, is_lte_rlc = FALSE,
is_http = FALSE, is_http2 = FALSE, is_quic = FALSE;
/* Making the menu context-sensitive allows for easier selection of the
@ -1193,6 +1193,7 @@ void MainWindow::setMenusForSelectedPacket()
proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers,
&is_ip, &is_tcp, &is_udp, &is_sctp,
&is_tls, &is_rtp, &is_lte_rlc);
is_dccp = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "dccp");
is_http = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "http");
is_http2 = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "http2");
is_quic = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "quic");
@ -1237,6 +1238,7 @@ void MainWindow::setMenusForSelectedPacket()
main_ui_->actionAnalyzeFollowTCPStream->setEnabled(is_tcp);
main_ui_->actionAnalyzeFollowUDPStream->setEnabled(is_udp);
main_ui_->actionAnalyzeFollowDCCPStream->setEnabled(is_dccp);
main_ui_->actionAnalyzeFollowTLSStream->setEnabled(is_tls);
main_ui_->actionAnalyzeFollowHTTPStream->setEnabled(is_http);
main_ui_->actionAnalyzeFollowHTTP2Stream->setEnabled(is_http2);
@ -2880,6 +2882,11 @@ void MainWindow::on_actionAnalyzeFollowUDPStream_triggered()
openFollowStreamDialogForType(FOLLOW_UDP);
}
void MainWindow::on_actionAnalyzeFollowDCCPStream_triggered()
{
openFollowStreamDialogForType(FOLLOW_DCCP);
}
void MainWindow::on_actionAnalyzeFollowTLSStream_triggered()
{
openFollowStreamDialogForType(FOLLOW_TLS);

View File

@ -683,6 +683,7 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event)
ctx_menu->addMenu(submenu);
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowDCCPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTLSStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTP2Stream"));

View File

@ -304,6 +304,7 @@ void ProtoTree::contextMenuEvent(QContextMenuEvent *event)
ctx_menu.addMenu(submenu);
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowDCCPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTLSStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTPStream"));
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTP2Stream"));