aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-http2.c
diff options
context:
space:
mode:
authorAlexander Gryanko <xpahos@gmail.com>2019-02-27 07:55:52 +0300
committerAlexis La Goutte <alexis.lagoutte@gmail.com>2019-09-22 05:29:30 +0000
commit9fff62e2a8a19b94f665b2937242b65fe0ef49fc (patch)
tree0089c0553d2e6686fb9797dc17f928298caf7abc /epan/dissectors/packet-http2.c
parent893a2d9c62e8dcc4fe0f43c47e01743136f55386 (diff)
Qt, http2: Add Follow HTTP/2 Stream functionality
The HTTP/2 protocol multiplexes a single TCP connection into multiple independent streams. The Follow TCP output can interleave multiple HTTP/2 streams, making it harder to analyze a single HTTP/2 stream. Add the ability to select HTTP/2 Streams within a TCP stream. Internally, the HTTP/2 dissector now stores the known Stream IDs in a set for every TCP session which allows an amortized O(n) lookup time for the previous/next/max Stream ID. [Peter: make the dissector responsible for clamping the HTTP/2 Stream ID instead of the Qt code, that should permit future optimizations.] Change-Id: I5d78f29904ae8f227ae36e1a883155c0ed719200 Reviewed-on: https://code.wireshark.org/review/32221 Reviewed-by: Peter Wu <peter@lekensteyn.nl> Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Alexander Gryanko <xpahos@gmail.com> Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-http2.c')
-rw-r--r--epan/dissectors/packet-http2.c151
1 files changed, 142 insertions, 9 deletions
diff --git a/epan/dissectors/packet-http2.c b/epan/dissectors/packet-http2.c
index f59cec3fab..0bd6698d2b 100644
--- a/epan/dissectors/packet-http2.c
+++ b/epan/dissectors/packet-http2.c
@@ -36,16 +36,16 @@
#ifdef HAVE_NGHTTP2
#include <epan/uat.h>
-
#include <nghttp2/nghttp2.h>
-
#endif
-#include "packet-tcp.h"
#include <epan/tap.h>
#include <epan/stats_tree.h>
#include <epan/reassemble.h>
+#include <epan/follow.h>
+#include <epan/addr_resolv.h>
+#include "packet-tcp.h"
#include "wsutil/pint.h"
#include "wsutil/strtoi.h"
@@ -199,11 +199,13 @@ typedef struct {
nghttp2_hd_inflater *hd_inflater[2];
http2_header_repr_info_t header_repr_info[2];
wmem_map_t *per_stream_info;
- guint32 current_stream_id;
#endif
+ guint32 current_stream_id;
tcp_flow_t *fwd_flow;
} http2_session_t;
+static GHashTable* streamid_hash = NULL;
+
void proto_register_http2(void);
void proto_reg_handoff_http2(void);
@@ -212,6 +214,7 @@ struct HTTP2Tap {
};
static int http2_tap = -1;
+static int http2_follow_tap = -1;
static const guint8* st_str_http2 = "HTTP2";
static const guint8* st_str_http2_type = "Type";
@@ -884,6 +887,8 @@ http2_init_protocol(void)
proto_register_field_array(proto_http2, hf_uat, num_header_fields);
}
#endif
+ /* Init hash table with mapping of stream id -> frames count for Follow HTTP2 */
+ streamid_hash = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_hash_table_destroy);
}
static void
@@ -895,6 +900,7 @@ http2_cleanup_protocol(void) {
proto_add_deregistered_data(hf_uat);
proto_free_deregistered_fields();
#endif
+ g_hash_table_destroy(streamid_hash);
}
static dissector_handle_t http2_handle;
@@ -1881,6 +1887,109 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
}
#endif
+static gchar*
+http2_follow_conv_filter(packet_info *pinfo, guint *stream, guint *sub_stream)
+{
+ http2_session_t *h2session;
+ struct tcp_analysis *tcpd;
+
+ 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)))
+ {
+ h2session = get_http2_session(pinfo);
+ tcpd = get_tcp_conversation_data(NULL, pinfo);
+
+ if (tcpd == NULL)
+ return NULL;
+ if (h2session == NULL)
+ return NULL;
+
+ *stream = tcpd->stream;
+ *sub_stream = h2session->current_stream_id;
+ return g_strdup_printf("tcp.stream eq %u and http2.streamid eq %u", tcpd->stream, h2session->current_stream_id);
+ }
+
+ return NULL;
+}
+
+static guint32
+get_http2_stream_count(guint streamid)
+{
+ guint32 result = 0;
+ guint32 key;
+ GHashTable *entry;
+ GList *entry_set, *it;
+
+ entry = (GHashTable*)g_hash_table_lookup(streamid_hash, GUINT_TO_POINTER(streamid));
+ if (entry != NULL) {
+ entry_set = g_hash_table_get_keys(entry);
+
+ /* this is a doubly-linked list, g_list_sort has the same time complexity */
+ for (it = entry_set; it != NULL; it = it->next) {
+ key = GPOINTER_TO_UINT(it->data);
+ result = key > result ? key : result;
+ }
+ g_list_free(entry_set);
+ }
+
+ return result;
+}
+
+static gboolean
+is_http2_stream_contains(guint streamid, gint sub_stream_id)
+{
+ GHashTable *entry;
+
+ entry = (GHashTable*)g_hash_table_lookup(streamid_hash, GUINT_TO_POINTER(streamid));
+ if (entry == NULL) {
+ return FALSE;
+ }
+
+ if (!g_hash_table_contains(entry, GINT_TO_POINTER(sub_stream_id))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+http2_get_stream_id_le(guint streamid, guint sub_stream_id, guint *sub_stream_id_out)
+{
+ // HTTP/2 Stream IDs are always 31 bit.
+ gint max_id = (gint)get_http2_stream_count(streamid);
+ gint id = (gint)(sub_stream_id & MASK_HTTP2_STREAMID);
+ if (id > max_id) {
+ id = max_id;
+ }
+ for (; id >= 0; id--) {
+ if (is_http2_stream_contains(streamid, id)) {
+ *sub_stream_id_out = (guint)id;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+gboolean
+http2_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id_out)
+{
+ // HTTP/2 Stream IDs are always 31 bit.
+ gint max_id = (gint)get_http2_stream_count(streamid);
+ for (gint id = (gint)(sub_stream_id & MASK_HTTP2_STREAMID); id <= max_id; id++) {
+ if (is_http2_stream_contains(streamid, id)) {
+ *sub_stream_id_out = (guint)id;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gchar*
+http2_follow_index_filter(guint stream, guint sub_stream)
+{
+ return g_strdup_printf("tcp.stream eq %u and http2.streamid eq %u", stream, sub_stream);
+}
+
static guint8
dissect_http2_header_flags(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 type)
{
@@ -2645,6 +2754,8 @@ dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
guint16 length;
guint32 streamid;
struct HTTP2Tap *http2_stats;
+ GHashTable* entry;
+ struct tcp_analysis* tcpd;
if(!p_get_proto_data(wmem_file_scope(), pinfo, proto_http2, 0)) {
http2_header_data_t *header_data;
@@ -2702,6 +2813,12 @@ dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
proto_tree_add_item(http2_tree, hf_http2_type, tvb, offset, 1, ENC_BIG_ENDIAN);
type = tvb_get_guint8(tvb, offset);
+ gint type_idx;
+ const gchar *type_str = try_val_to_str_idx(type, http2_type_vals, &type_idx);
+ if (type_str == NULL) {
+ type_str = wmem_strdup_printf(wmem_packet_scope(), "Unknown type (%d)", type);
+ }
+
offset += 1;
flags = dissect_http2_header_flags(tvb, pinfo, http2_tree, offset, type);
@@ -2710,17 +2827,27 @@ dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
proto_tree_add_item(http2_tree, hf_http2_r, tvb, offset, 4, ENC_BIG_ENDIAN);
proto_tree_add_item(http2_tree, hf_http2_streamid, tvb, offset, 4, ENC_BIG_ENDIAN);
streamid = tvb_get_ntohl(tvb, offset) & MASK_HTTP2_STREAMID;
- proto_item_append_text(ti, ": %s, Stream ID: %u, Length %u", val_to_str(type, http2_type_vals, "Unknown type (%d)"), streamid, length);
+ proto_item_append_text(ti, ": %s, Stream ID: %u, Length %u", type_str, streamid, length);
offset += 4;
/* append stream id after frame type on info column, like: HEADERS[1], DATA[1], HEADERS[3], DATA[3] */
- col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s[%u]", val_to_str(type, http2_type_vals, "Unknown type (%d)"), streamid);
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s[%u]", type_str, streamid);
+
+ /* fill hash table with stream ids and skip all unknown frames */
+ tcpd = get_tcp_conversation_data(NULL, pinfo);
+ if (tcpd != NULL && type_idx != -1) {
+ entry = (GHashTable*)g_hash_table_lookup(streamid_hash, GUINT_TO_POINTER(tcpd->stream));
+ if (entry == NULL) {
+ entry = g_hash_table_new(NULL, NULL);
+ g_hash_table_insert(streamid_hash, GUINT_TO_POINTER(tcpd->stream), entry);
+ }
+
+ g_hash_table_add(entry, GUINT_TO_POINTER(streamid));
+ }
-#ifdef HAVE_NGHTTP2
/* Mark the current stream, used for per-stream processing later in the dissection */
http2_session_t *http2_session = get_http2_session(pinfo);
http2_session->current_stream_id = streamid;
-#endif
/* Collect stats */
http2_stats = wmem_new0(wmem_packet_scope(), struct HTTP2Tap);
@@ -2779,9 +2906,11 @@ dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
proto_tree_add_item(http2_tree, hf_http2_unknown, tvb, offset, -1, ENC_NA);
break;
}
-
tap_queue_packet(http2_tap, pinfo, http2_stats);
+ if (have_tap_listener(http2_follow_tap)) {
+ tap_queue_packet(http2_follow_tap, pinfo, tvb);
+ }
return tvb_captured_length(tvb);
}
@@ -3386,6 +3515,10 @@ proto_register_http2(void)
&addresses_ports_reassembly_table_functions);
http2_tap = register_tap("http2");
+ 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_tvb_tap_listener);
}
static void http2_stats_tree_init(stats_tree* st)