diff --git a/CMakeLists.txt b/CMakeLists.txt index 253498cdbf..d7dd4ace82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3056,6 +3056,7 @@ if(BUILD_sharkd) ${APPLE_SYSTEM_CONFIGURATION_LIBRARY} ${WIN_WS2_32_LIBRARY} ${SPEEXDSP_LIBRARIES} + ${M_LIBRARIES} ) set(sharkd_FILES # @@ -3066,6 +3067,7 @@ if(BUILD_sharkd) sharkd.c sharkd_daemon.c sharkd_session.c + ${TSHARK_TAP_SRC} ) set_executable_resources(sharkd "SharkD") add_executable(sharkd ${sharkd_FILES}) diff --git a/sharkd_session.c b/sharkd_session.c index 4f98425fce..9fcaf713f4 100644 --- a/sharkd_session.c +++ b/sharkd_session.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -1031,6 +1032,11 @@ sharkd_session_process_info(void) sharkd_json_value_string("tap", "rtp-streams"); json_dumper_end_object(&dumper); + json_dumper_begin_object(&dumper); + sharkd_json_value_string("name", "Protocol Hierarchy Statistics"); + sharkd_json_value_string("tap", "phs"); + json_dumper_end_object(&dumper); + json_dumper_begin_object(&dumper); sharkd_json_value_string("name", "Expert Information"); sharkd_json_value_string("tap", "expert"); @@ -2629,6 +2635,80 @@ sharkd_session_free_tap_srt_cb(void *arg) g_free(srt_data); } +struct sharkd_phs_req +{ + const char *tap_name; + phs_t *rs; +}; + +static void +sharkd_session_process_tap_phs_cb_aux(phs_t *rs) +{ + for (; rs; rs = rs->sibling) { + if (rs->protocol == -1) { + return; + } + sharkd_json_object_open(NULL); + sharkd_json_value_string("proto", rs->proto_name); + sharkd_json_value_anyf("frames", "%u", rs->frames); + sharkd_json_value_anyf("bytes", "%lu", rs->bytes); + if (rs->child != NULL && rs->child->protocol != -1) { + sharkd_json_array_open("protos"); + sharkd_session_process_tap_phs_cb_aux(rs->child); + sharkd_json_array_close(); + } + sharkd_json_object_close(); + } +} + +static tap_packet_status +sharkd_session_packet_tap_phs_cb(void *pphs_req, packet_info *pinfo, epan_dissect_t *edt, const void *dummy, tap_flags_t flags) +{ + struct sharkd_phs_req *phs_req = (struct sharkd_phs_req *)pphs_req; + phs_t *rs = phs_req->rs; + return protohierstat_packet(rs, pinfo, edt, dummy, flags); +} + +/** + * sharkd_session_process_tap_phs_cb() + * + * Output phs tap: + * (m) tap - tap name + * (m) type - tap output type + * (m) filter - tap filter argument + * (m) protos - array of proto objects + * + * proto object: + * (m) proto - protocol name + * (m) frames - frame count + * (m) bytes - bytes count + * (o) protos - array of proto objects + */ +static void +sharkd_session_process_tap_phs_cb(void *arg) +{ + struct sharkd_phs_req *phs_req = (struct sharkd_phs_req *)arg; + phs_t *rs = phs_req->rs; + sharkd_json_object_open(NULL); + sharkd_json_value_string("tap", phs_req->tap_name); + sharkd_json_value_string("type", "phs"); + sharkd_json_value_string("filter", rs->filter ? rs->filter : ""); + sharkd_json_array_open("protos"); + sharkd_session_process_tap_phs_cb_aux(rs); + sharkd_json_array_close(); + sharkd_json_object_close(); +} + +static void +sharkd_session_free_tap_phs_cb(void *arg) +{ + struct sharkd_phs_req *phs_req = (struct sharkd_phs_req *)arg; + phs_t *rs = phs_req->rs; + free_phs(rs); + g_free(phs_req); + +} + struct sharkd_export_object_list { struct sharkd_export_object_list *next; @@ -3247,6 +3327,26 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) tap_data = mcaststream_tapinfo; tap_free = sharkd_session_process_free_tap_multicast_cb; } + else if (!strncmp(tok_tap, "phs", 3)) + { + phs_t *rs; + + pc_proto_id = proto_registrar_get_id_byname("pkt_comment"); + + rs = new_phs_t(NULL, tap_filter); + + struct sharkd_phs_req *phs_req; + phs_req = (struct sharkd_phs_req *)g_malloc0(sizeof(*phs_req)); + phs_req->tap_name = tok_tap; + phs_req->rs = rs; + + tap_error = register_tap_listener("frame", phs_req, tap_filter, TL_REQUIRES_PROTO_TREE, NULL, + sharkd_session_packet_tap_phs_cb, + sharkd_session_process_tap_phs_cb, NULL); + + tap_data = phs_req; + tap_free = sharkd_session_free_tap_phs_cb; + } else { sharkd_json_error( diff --git a/test/captures/protohier-with-comments.pcapng b/test/captures/protohier-with-comments.pcapng new file mode 100644 index 0000000000..4a4d78e4d3 Binary files /dev/null and b/test/captures/protohier-with-comments.pcapng differ diff --git a/test/captures/protohier-without-comments.pcapng b/test/captures/protohier-without-comments.pcapng new file mode 100644 index 0000000000..942e920275 Binary files /dev/null and b/test/captures/protohier-without-comments.pcapng differ diff --git a/test/suite_sharkd.py b/test/suite_sharkd.py index 42f63b182e..2701cd9661 100644 --- a/test/suite_sharkd.py +++ b/test/suite_sharkd.py @@ -309,6 +309,195 @@ class TestSharkd: }}, )) + def test_sharkd_req_tap_phs(self, check_sharkd_session, capture_file): + check_sharkd_session(( + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('protohier-with-comments.pcapng')} + }, + {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "phs"}}, + {"jsonrpc":"2.0", "id":3, "method":"load", + "params":{"file": capture_file('protohier-without-comments.pcapng')} + }, + {"jsonrpc":"2.0", "id":4, "method":"tap", "params":{"tap0": "phs"}}, + ), ( + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{ + "taps":[{ + "tap":"phs", + "type":"phs", + "filter":"", + "protos":[{ + "proto":"eth", + "frames":115, + "bytes":22186, + "protos":[{ + "proto":"ipv6", + "frames":39, + "bytes":7566, + "protos":[{ + "proto":"icmpv6", + "frames":36, + "bytes":3684 + },{ + "proto":"udp", + "frames":3, + "bytes":3882, + "protos":[{ + "proto":"data", + "frames":3, + "bytes":3882 + }] + }] + },{ + "proto":"ip", + "frames":70, + "bytes":14260, + "protos":[{ + "proto":"udp", + "frames":60, + "bytes":13658, + "protos":[{ + "proto":"mdns", + "frames":1, + "bytes":138 + },{ + "proto":"ssdp", + "frames":30, + "bytes":8828 + },{ + "proto":"nbns", + "frames":20, + "bytes":2200 + },{ + "proto":"nbdgm", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"smb", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"mailslot", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"browser", + "frames":1, + "bytes":248 + }] + }] + }] + },{"proto":"dhcp", + "frames":4, + "bytes":1864 + },{ + "proto":"dns", + "frames":4, + "bytes":380 + }] + },{ + "proto":"igmp", + "frames":10, + "bytes":602 + }] + },{ + "proto":"arp", + "frames":6, + "bytes":360 + }] + }] + }] + }}, + {"jsonrpc":"2.0","id":3,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":4,"result":{ + "taps":[{ + "tap":"phs", + "type":"phs", + "filter":"", + "protos":[{ + "proto":"eth", + "frames":115, + "bytes":22186, + "protos":[{ + "proto":"ipv6", + "frames":39, + "bytes":7566, + "protos":[{ + "proto":"icmpv6", + "frames":36, + "bytes":3684 + },{ + "proto":"udp", + "frames":3, + "bytes":3882, + "protos":[{ + "proto":"data", + "frames":3, + "bytes":3882 + }] + }] + },{ + "proto":"ip", + "frames":70, + "bytes":14260, + "protos":[{ + "proto":"udp", + "frames":60, + "bytes":13658, + "protos":[{ + "proto":"mdns", + "frames":1, + "bytes":138 + },{ + "proto":"ssdp", + "frames":30, + "bytes":8828 + },{ + "proto":"nbns", + "frames":20, + "bytes":2200 + },{ + "proto":"nbdgm", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"smb", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"mailslot", + "frames":1, + "bytes":248, + "protos":[{ + "proto":"browser", + "frames":1, + "bytes":248 + }] + }] + }] + },{"proto":"dhcp", + "frames":4, + "bytes":1864 + },{ + "proto":"dns", + "frames":4, + "bytes":380 + }] + },{ + "proto":"igmp", + "frames":10, + "bytes":602 + }] + },{ + "proto":"arp", + "frames":6, + "bytes":360 + }] + }] + }] + }}, + )) + def test_sharkd_req_follow_bad(self, check_sharkd_session, capture_file): # Unrecognized taps currently produce no output (not even err). check_sharkd_session(( diff --git a/ui/cli/tap-protohierstat.c b/ui/cli/tap-protohierstat.c index 31f748aaa8..2628fdcf3c 100644 --- a/ui/cli/tap-protohierstat.c +++ b/ui/cli/tap-protohierstat.c @@ -21,25 +21,14 @@ #include #include +#include "tap-protohierstat.h" -static int pc_proto_id = -1; +int pc_proto_id = -1; void register_tap_listener_protohierstat(void); -typedef struct _phs_t { - struct _phs_t *sibling; - struct _phs_t *child; - struct _phs_t *parent; - char *filter; - int protocol; - const char *proto_name; - guint32 frames; - guint64 bytes; -} phs_t; - - -static phs_t * -new_phs_t(phs_t *parent) +phs_t * +new_phs_t(phs_t *parent, const char *filter) { phs_t *rs; rs = g_new(phs_t, 1); @@ -47,6 +36,9 @@ new_phs_t(phs_t *parent) rs->child = NULL; rs->parent = parent; rs->filter = NULL; + if (filter != NULL) { + rs->filter = g_strdup(filter); + } rs->protocol = -1; rs->proto_name = NULL; rs->frames = 0; @@ -54,8 +46,30 @@ new_phs_t(phs_t *parent) return rs; } +void +free_phs(phs_t *rs) +{ + if (!rs) { + return; + } + if (rs->filter) { + g_free(rs->filter); + rs->filter = NULL; + } + if (rs->sibling) + { + free_phs(rs->sibling); + rs->sibling = NULL; + } + if (rs->child) + { + free_phs(rs->child); + rs->child = NULL; + } + g_free(rs); +} -static tap_packet_status +tap_packet_status protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_) { phs_t *rs = (phs_t *)prs; @@ -76,13 +90,23 @@ protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const v for (node=edt->tree->first_child; node; node=node->next) { fi = PNODE_FINFO(node); + /* + * If the first child is a tree of comments, skip over it. + * This keeps us from having a top-level "pkt_comment" + * entry that represents a nonexistent protocol, + * and matches how the GUI treats comments. + */ + if (G_UNLIKELY(fi->hfinfo->id == pc_proto_id)) { + continue; + } + /* first time we saw a protocol at this leaf */ if (rs->protocol == -1) { rs->protocol = fi->hfinfo->id; rs->proto_name = fi->hfinfo->abbrev; rs->frames = 1; rs->bytes = pinfo->fd->pkt_len; - rs->child = new_phs_t(rs); + rs->child = new_phs_t(rs, NULL); rs = rs->child; continue; } @@ -98,7 +122,7 @@ protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const v if (!tmprs) { for (tmprs=rs; tmprs->sibling; tmprs=tmprs->sibling) ; - tmprs->sibling = new_phs_t(rs->parent); + tmprs->sibling = new_phs_t(rs->parent, NULL); rs = tmprs->sibling; rs->protocol = fi->hfinfo->id; rs->proto_name = fi->hfinfo->abbrev; @@ -110,7 +134,7 @@ protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const v rs->bytes += pinfo->fd->pkt_len; if (!rs->child) { - rs->child = new_phs_t(rs); + rs->child = new_phs_t(rs, NULL); } rs = rs->child; } @@ -127,16 +151,6 @@ phs_draw(phs_t *rs, int indentation) if (rs->protocol == -1) { return; } - /* - * If the first child is a tree of comments, skip over it. - * This keeps us from having a top-level "pkt_comment" - * entry that represents a nonexistent protocol, - * and matches how the GUI treats comments. - */ - if (G_UNLIKELY(rs->protocol == pc_proto_id)) { - phs_draw(rs->child, indentation); - continue; - } str[0] = 0; stroff = 0; for (i=0; ifilter = g_strdup(filter); + rs = new_phs_t(NULL, filter); error_string = register_tap_listener("frame", rs, filter, TL_REQUIRES_PROTO_TREE, NULL, protohierstat_packet, protohierstat_draw, NULL); if (error_string) { /* error, we failed to attach to the tap. clean up */ - g_free(rs->filter); - g_free(rs); + free_phs(rs); cmdarg_err("Couldn't register io,phs tap: %s", error_string->str); diff --git a/ui/cli/tap-protohierstat.h b/ui/cli/tap-protohierstat.h new file mode 100644 index 0000000000..3a68ff18c7 --- /dev/null +++ b/ui/cli/tap-protohierstat.h @@ -0,0 +1,51 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __TAP_PROTO_HIER_STAT_H__ +#define __TAP_PROTO_HIER_STAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern int pc_proto_id; + +typedef struct _phs_t { + struct _phs_t *sibling; + struct _phs_t *child; + struct _phs_t *parent; + char *filter; + int protocol; + const char *proto_name; + guint32 frames; + guint64 bytes; +} phs_t; + +extern phs_t * new_phs_t(phs_t *parent, const char *filter); +extern void free_phs(phs_t *rs); +extern tap_packet_status protohierstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TAP_PROTO_HIER_STAT_H__ */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */