follow: Add function for sub stream id to registration

When dissectors register for Follow Stream, have them register a
function for finding the next valid sub stream id for a given
stream and substream id pair. This function is NULL if the dissector
does not use sub stream IDs.

Use this function in follow_stream_dialog to update the sub stream
id widget (and use the absence of the function to disable and hide
the widget.) Use this function in the CLI tap-follow to determine
whether to parse a sub stream id from the command line options.

This removes the dependencies on epan/dissectors from the Qt
follow_stream_dialog, and gets us closer to having dissectors
being able to register for Follow Stream without having to update
anything in the common source code.
This commit is contained in:
John Thacker 2022-09-13 18:51:01 -04:00
parent 3120e64570
commit 0e93070745
12 changed files with 101 additions and 78 deletions

View File

@ -1990,7 +1990,7 @@ proto_register_dccp(void)
register_conversation_table(proto_dccp, FALSE, dccpip_conversation_packet, dccpip_endpoint_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, get_dccp_stream_count);
dccp_port_to_display, follow_tvb_tap_listener, get_dccp_stream_count, NULL);
register_init_routine(dccp_init);
}

View File

@ -4513,7 +4513,7 @@ proto_register_http(void)
register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
tcp_port_to_display, follow_tvb_tap_listener,
get_tcp_stream_count);
get_tcp_stream_count, NULL);
http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL);
/* compile patterns, exluding "/" */

View File

@ -2489,6 +2489,16 @@ http2_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id
return FALSE;
}
gboolean
http2_get_sub_stream_id(guint streamid, guint sub_stream_id, gboolean le, guint *sub_stream_id_out)
{
if (le) {
return http2_get_stream_id_le(streamid, sub_stream_id, sub_stream_id_out);
} else {
return http2_get_stream_id_ge(streamid, sub_stream_id, sub_stream_id_out);
}
}
static gchar*
http2_follow_index_filter(guint stream, guint sub_stream)
{
@ -4635,7 +4645,8 @@ proto_register_http2(void)
http2_follow_tap = register_tap("http2_follow");
register_follow_stream(proto_http2, "http2_follow", http2_follow_conv_filter, http2_follow_index_filter, tcp_follow_address_filter,
tcp_port_to_display, follow_http2_tap_listener, get_tcp_stream_count);
tcp_port_to_display, follow_http2_tap_listener, get_tcp_stream_count,
http2_get_sub_stream_id);
}
static void http2_stats_tree_init(stats_tree* st)

View File

@ -4551,6 +4551,16 @@ quic_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id_
return FALSE;
}
gboolean
quic_get_sub_stream_id(guint streamid, guint sub_stream_id, gboolean le, guint *sub_stream_id_out)
{
if (le) {
return quic_get_stream_id_le(streamid, sub_stream_id, sub_stream_id_out);
} else {
return quic_get_stream_id_ge(streamid, sub_stream_id, sub_stream_id_out);
}
}
static gchar *
quic_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint *stream, guint *sub_stream)
{
@ -5409,7 +5419,8 @@ proto_register_quic(void)
register_cleanup_routine(quic_cleanup);
register_follow_stream(proto_quic, "quic_follow", quic_follow_conv_filter, quic_follow_index_filter, quic_follow_address_filter,
udp_port_to_display, follow_quic_tap_listener, get_quic_connections_count);
udp_port_to_display, follow_quic_tap_listener, get_quic_connections_count,
quic_get_sub_stream_id);
// TODO implement custom reassembly functions that uses the QUIC Connection
// ID instead of address and port numbers.

View File

@ -7759,7 +7759,7 @@ void proto_register_sip(void)
ws_mempbrk_compile(&pbrk_via_param_end, "\t;, ");
register_follow_stream(proto_sip, "sip_follow", sip_follow_conv_filter, sip_follow_index_filter, sip_follow_address_filter,
udp_port_to_display, follow_tvb_tap_listener, NULL);
udp_port_to_display, follow_tvb_tap_listener, NULL, NULL);
}
void

View File

@ -9742,7 +9742,7 @@ proto_register_tcp(void)
register_conversation_table(proto_mptcp, FALSE, mptcpip_conversation_packet, tcpip_endpoint_packet);
register_follow_stream(proto_tcp, "tcp_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
tcp_port_to_display, follow_tcp_tap_listener, get_tcp_stream_count);
tcp_port_to_display, follow_tcp_tap_listener, get_tcp_stream_count, NULL);
}
void

View File

@ -4766,7 +4766,7 @@ proto_register_tls(void)
"tls", tls_tap);
register_follow_stream(proto_tls, "tls", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
tcp_port_to_display, ssl_follow_tap_listener, get_tcp_stream_count);
tcp_port_to_display, ssl_follow_tap_listener, get_tcp_stream_count, NULL);
secrets_register_type(SECRETS_TYPE_TLS, tls_secrets_block_callback);
}

View File

@ -1476,7 +1476,7 @@ proto_register_udp(void)
register_conversation_table(proto_udp, FALSE, udpip_conversation_packet, udpip_endpoint_packet);
register_conversation_filter("udp", "UDP", udp_filter_valid, udp_build_filter);
register_follow_stream(proto_udp, "udp_follow", udp_follow_conv_filter, udp_follow_index_filter, udp_follow_address_filter,
udp_port_to_display, follow_tvb_tap_listener, get_udp_stream_count);
udp_port_to_display, follow_tvb_tap_listener, get_udp_stream_count, NULL);
register_init_routine(udp_init);
}

View File

@ -29,6 +29,7 @@ struct register_follow {
follow_port_to_display_func port_to_display; /* port to name resolution for follow type */
tap_packet_cb tap_handler; /* tap listener handler */
follow_stream_count_func stream_count; /* maximum stream count, used for UI */
follow_sub_stream_id_func sub_stream_id; /* sub-stream id, used for UI */
};
static wmem_tree_t *registered_followers = NULL;
@ -36,7 +37,7 @@ static wmem_tree_t *registered_followers = NULL;
void register_follow_stream(const int proto_id, const char* tap_listener,
follow_conv_filter_func conv_filter, follow_index_filter_func index_filter, follow_address_filter_func address_filter,
follow_port_to_display_func port_to_display, tap_packet_cb tap_handler,
follow_stream_count_func stream_count)
follow_stream_count_func stream_count, follow_sub_stream_id_func sub_stream_id)
{
register_follow_t *follower;
DISSECTOR_ASSERT(tap_listener);
@ -56,6 +57,7 @@ void register_follow_stream(const int proto_id, const char* tap_listener,
follower->port_to_display = port_to_display;
follower->tap_handler = tap_handler;
follower->stream_count = stream_count;
follower->sub_stream_id = sub_stream_id;
if (registered_followers == NULL)
registered_followers = wmem_tree_new(wmem_epan_scope());
@ -109,6 +111,11 @@ follow_stream_count_func get_follow_stream_count_func(register_follow_t* followe
return follower->stream_count;
}
follow_sub_stream_id_func get_follow_sub_stream_id_func(register_follow_t* follower)
{
return follower->sub_stream_id;
}
register_follow_t* get_follow_by_name(const char* proto_short_name)
{
return (register_follow_t*)wmem_tree_lookup_string(registered_followers, proto_short_name, 0);

View File

@ -111,12 +111,13 @@ typedef gchar* (*follow_index_filter_func)(guint stream, guint sub_stream);
typedef gchar* (*follow_address_filter_func)(address* src_addr, address* dst_addr, int src_port, int dst_port);
typedef gchar* (*follow_port_to_display_func)(wmem_allocator_t *allocator, guint port);
typedef guint32 (*follow_stream_count_func)(void);
typedef gboolean (*follow_sub_stream_id_func)(guint stream, guint sub_stream, gboolean le, guint *sub_stream_out);
WS_DLL_PUBLIC
void register_follow_stream(const int proto_id, const char* tap_listener,
follow_conv_filter_func conv_filter, follow_index_filter_func index_filter, follow_address_filter_func address_filter,
follow_port_to_display_func port_to_display, tap_packet_cb tap_handler,
follow_stream_count_func stream_count);
follow_stream_count_func stream_count, follow_sub_stream_id_func sub_stream_id);
/** Get protocol ID from registered follower
*
@ -182,6 +183,19 @@ WS_DLL_PUBLIC tap_packet_cb get_follow_tap_handler(register_follow_t* follower);
*/
WS_DLL_PUBLIC follow_stream_count_func get_follow_stream_count_func(register_follow_t* follower);
/** Provide function that, for given stream and sub stream ids, searches for
* the first sub stream id less than or equal (or greater than or equal) the
* given sub stream id present on the given stream id. Returns TRUE and the
* sub stream id found, or FALSE.
* This is used by the GUI to select valid sub stream numbers, e.g. when
* incrementing or decrementing the sub stream ID widget.
* This function should be NULL if the follower does not have sub streams.
*
* @param follower [in] Registered follower
* @return A sub stream id function handler
*/
WS_DLL_PUBLIC follow_sub_stream_id_func get_follow_sub_stream_id_func(register_follow_t* follower);
/** Tap function handler when dissector's tap provides follow data as a tvb.
* Used by TCP, UDP and HTTP followers
*/

View File

@ -513,9 +513,9 @@ static void follow_stream(const char *opt_argp, void *userdata)
cli_follow_info = g_new0(cli_follow_info_t, 1);
cli_follow_info->stream_index = -1;
/* use second parameter only for HTTP2 or QUIC substream */
if (g_str_equal(proto_filter_name, "http2") ||
g_str_equal(proto_filter_name, "quic")) {
/* use second parameter only for followers that have sub streams
* (currently HTTP2 or QUIC) */
if (get_follow_sub_stream_id_func(follower)) {
cli_follow_info->sub_stream_index = -1;
} else {
cli_follow_info->sub_stream_index = 0;

View File

@ -15,8 +15,6 @@
#include "frame_tvbuff.h"
#include "epan/follow.h"
#include "epan/dissectors/packet-http2.h"
#include "epan/dissectors/packet-quic.h"
#include "epan/prefs.h"
#include "epan/addr_resolv.h"
#include "epan/charsets.h"
@ -256,7 +254,7 @@ void FollowStreamDialog::updateWidgets(bool follow_in_progress)
ui->cbDirections->setEnabled(enable);
ui->cbCharset->setEnabled(enable);
ui->streamNumberSpinBox->setEnabled(enable);
if (follow_type_ == FOLLOW_HTTP2 || follow_type_ == FOLLOW_QUIC) {
if (get_follow_sub_stream_id_func(follower_) != NULL) {
ui->subStreamNumberSpinBox->setEnabled(enable);
}
ui->leFind->setEnabled(enable);
@ -413,24 +411,32 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num)
gboolean ok;
if (ui->subStreamNumberSpinBox->isVisible()) {
/* We need to find a suitable sub stream for the new stream */
follow_sub_stream_id_func sub_stream_func;
sub_stream_func = get_follow_sub_stream_id_func(follower_);
if (sub_stream_func == NULL) {
// Should not happen, this field is only visible for suitable protocols.
return;
}
guint sub_stream_num_new = static_cast<guint>(sub_stream_num);
if (sub_stream_num < 0) {
// Stream ID 0 should always exist as it is used for control messages.
// XXX: That is only guaranteed for HTTP2. For example, in QUIC,
// stream ID 0 is a normal stream used by the first standard client-
// initiated bidirectional stream (if it exists, and it might not)
// and we might have a stream (connection) but only the CRYPTO
// stream, which does not have a (sub) stream ID.
// What should we do if there is a stream with no substream to
// follow? Right now the substream spinbox is left active and
// the user can change the value to no effect.
sub_stream_num_new = 0;
ok = TRUE;
} else if (follow_type_ == FOLLOW_HTTP2) {
ok = http2_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
if (!ok) {
ok = http2_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
}
} else if (follow_type_ == FOLLOW_QUIC) {
ok = quic_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
if (!ok) {
ok = quic_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
}
} else {
// Should not happen, this field is only visible for suitable protocols.
return;
ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new);
if (!ok) {
ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new);
}
}
sub_stream_num = static_cast<gint>(sub_stream_num_new);
} else {
@ -457,28 +463,26 @@ void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_n
stream_num = ui->streamNumberSpinBox->value();
ui->streamNumberSpinBox->blockSignals(false);
follow_sub_stream_id_func sub_stream_func;
sub_stream_func = get_follow_sub_stream_id_func(follower_);
if (sub_stream_func == NULL) {
// Should not happen, this field is only visible for suitable protocols.
return;
}
guint sub_stream_num_new = static_cast<guint>(sub_stream_num);
gboolean ok;
/* previous_sub_stream_num_ is a hack to track which buttons was pressed without event handling */
if (sub_stream_num < 0) {
// Stream ID 0 should always exist as it is used for control messages.
// XXX: That is only guaranteed for HTTP2, see above.
sub_stream_num_new = 0;
ok = TRUE;
} else if (follow_type_ == FOLLOW_HTTP2) {
if (previous_sub_stream_num_ < sub_stream_num) {
ok = http2_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
} else {
ok = http2_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
}
} else if (follow_type_ == FOLLOW_QUIC) {
if (previous_sub_stream_num_ < sub_stream_num) {
ok = quic_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
} else {
ok = quic_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
}
} else if (previous_sub_stream_num_ < sub_stream_num) {
ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new);
} else {
// Should not happen, this field is only visible for suitable protocols.
return;
ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new);
}
sub_stream_num = static_cast<gint>(sub_stream_num_new);
@ -1023,15 +1027,6 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
return false;
}
/* disable substream spin box for all protocols except HTTP2 and QUIC */
ui->subStreamNumberSpinBox->blockSignals(true);
ui->subStreamNumberSpinBox->setEnabled(false);
ui->subStreamNumberSpinBox->setValue(0);
ui->subStreamNumberSpinBox->setKeyboardTracking(false);
ui->subStreamNumberSpinBox->blockSignals(false);
ui->subStreamNumberSpinBox->setVisible(false);
ui->subStreamNumberLabel->setVisible(false);
stream_count_func = get_follow_stream_count_func(follower_);
if (stream_count_func == NULL) {
@ -1046,45 +1041,30 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip());
}
switch (follow_type_)
{
case FOLLOW_HTTP2:
{
follow_sub_stream_id_func sub_stream_func;
sub_stream_func = get_follow_sub_stream_id_func(follower_);
if (sub_stream_func != NULL) {
guint substream_max_id = 0;
http2_get_stream_id_le(static_cast<guint>(stream_num), G_MAXINT32, &substream_max_id);
sub_stream_func(static_cast<guint>(stream_num), G_MAXINT32, TRUE, &substream_max_id);
stream_count = static_cast<gint>(substream_max_id);
ui->subStreamNumberSpinBox->blockSignals(true);
ui->subStreamNumberSpinBox->setEnabled(true);
ui->subStreamNumberSpinBox->setMaximum(stream_count);
ui->subStreamNumberSpinBox->setValue(sub_stream_num);
ui->subStreamNumberSpinBox->blockSignals(false);
ui->subStreamNumberSpinBox->setToolTip(tr("%Ln total sub stream(s).", "", stream_count));
ui->subStreamNumberSpinBox->setToolTip(tr("Max sub stream ID for the selected stream: %Ln", "", stream_count));
ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip());
ui->subStreamNumberSpinBox->setVisible(true);
ui->subStreamNumberLabel->setVisible(true);
break;
}
case FOLLOW_QUIC:
{
guint substream_max_id = 0;
quic_get_stream_id_le(static_cast<guint>(stream_num), G_MAXINT32, &substream_max_id);
stream_count = static_cast<gint>(substream_max_id);
} else {
/* disable substream spin box for protocols without substreams */
ui->subStreamNumberSpinBox->blockSignals(true);
ui->subStreamNumberSpinBox->setEnabled(true);
ui->subStreamNumberSpinBox->setMaximum(stream_count);
ui->subStreamNumberSpinBox->setValue(sub_stream_num);
ui->subStreamNumberSpinBox->setEnabled(false);
ui->subStreamNumberSpinBox->setValue(0);
ui->subStreamNumberSpinBox->setKeyboardTracking(false);
ui->subStreamNumberSpinBox->blockSignals(false);
ui->subStreamNumberSpinBox->setToolTip(tr("Max QUIC Stream ID for the selected connection: %Ln", "", stream_count));
ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip());
ui->subStreamNumberSpinBox->setVisible(true);
ui->subStreamNumberLabel->setVisible(true);
break;
}
default:
/* No extra handling */
break;
ui->subStreamNumberSpinBox->setVisible(false);
ui->subStreamNumberLabel->setVisible(false);
}
beginRetapPackets();