From 1e5f2e533c707eb6cd3b0a035dd1d9c6c09e80ca Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 23 Oct 2024 13:35:59 -0400 Subject: [PATCH] Internet Draft updates (#4) * Updates for latest drafts of UDPCLv2 and BPSec COSE * Updating job conditions * Use wireshark upstream as baseline --- .github/workflows/wireshark-plugin.yml | 13 +- README.md | 4 +- src/CMakeLists.txt | 2 +- src/packet-bpsec-cose.c | 161 ++++--- src/packet-udpcl.c | 606 ++++++++++++++++++++----- src/packet-udpcl.h | 18 - src/plugin_bpv7.c | 2 + 7 files changed, 608 insertions(+), 198 deletions(-) delete mode 100644 src/packet-udpcl.h diff --git a/.github/workflows/wireshark-plugin.yml b/.github/workflows/wireshark-plugin.yml index ffa526d..545c361 100644 --- a/.github/workflows/wireshark-plugin.yml +++ b/.github/workflows/wireshark-plugin.yml @@ -1,6 +1,13 @@ name: Wireshark Plugin -on: [push] +on: + schedule: + - cron: '0 0 * * 0' + push: + branches: + - main + pull_request: {} # any target + jobs: build: @@ -24,7 +31,7 @@ jobs: - uses: actions/checkout@v3 - name: wireshark run: | - git clone https://gitlab.com/BrianSipos/wireshark.git + git clone https://gitlab.com/wireshark/wireshark.git cd wireshark git checkout master sudo ./tools/debian-setup.sh ${COMMON_APT_GET_ARGS} --install-optional --install-deb-deps --install-test-deps \ @@ -67,4 +74,4 @@ jobs: ctest -V tshark -G protocols | grep bpsec-cose tshark -G protocols | grep bpv7.acme - \ No newline at end of file + diff --git a/README.md b/README.md index e5b6bcb..68516fa 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ The reference commands below use the Ninja build tool, but that is not required. Building the wireshark modules can be done with a command sequence similar to: ``` PLUGIN_PATH=$(pkg-config --define-variable=libdir=${HOME}/.local/lib --variable=plugindir wireshark) - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DINSTALL_MODULE_PATH=${PLUGIN_PATH}/epan/ -G Ninja - cmake --build build --target install + cmake -S . -B build/default -DCMAKE_BUILD_TYPE=Debug -DINSTALL_MODULE_PATH=${PLUGIN_PATH}/epan/ -G Ninja + cmake --build build/default --target install ``` At this point the two modules "libudpcl" and "libbpv7" will be installed in the wireshark plugin path and will be loaded at next wireshark application startup. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81c6d10..a99fb92 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ add_library( udpcl MODULE - packet-udpcl.h packet-udpcl.c + packet-udpcl.c ) target_link_libraries(udpcl PUBLIC PkgConfig::WIRESHARK) diff --git a/src/packet-bpsec-cose.c b/src/packet-bpsec-cose.c index 7ecb82d..2af1f1b 100644 --- a/src/packet-bpsec-cose.c +++ b/src/packet-bpsec-cose.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,13 +14,15 @@ * Section 3.2.2. */ typedef enum { - HAS_PRIMARY_CTX = 0x01, - HAS_TARGET_CTX = 0x02, - HAS_SECURITY_CTX = 0x04, + AAD_METADATA = 0x01, + AAD_BTSD = 0x02, } AadScopeFlag; +/// IANA registered security context ID +static const int64_t bpsec_cose_ctxid = 99; + /// Protocol handles -static int proto_bpsec_cose = -1; +static int proto_bpsec_cose; /// Dissect opaque CBOR parameters/results static dissector_table_t table_cose_msg = NULL; @@ -27,52 +30,70 @@ static dissector_table_t table_cose_msg = NULL; static dissector_handle_t handle_cose_msg_hdr = NULL; static int hf_aad_scope = -1; -static int hf_aad_scope_primary = -1; -static int hf_aad_scope_target = -1; -static int hf_aad_scope_security = -1; +static int hf_aad_blknum = -1; +static int hf_aad_flags = -1; +static int hf_aad_flags_metadata = -1; +static int hf_aad_flags_btsd = -1; static int hf_addl_prot_bstr = -1; -static int hf_addl_unprot = -1; +static int hf_addl_unprot_bstr = -1; static int hf_cose_msg = -1; /// Field definitions static hf_register_info fields[] = { - {&hf_aad_scope, {"AAD Scope", "bpsec.cose.aad_scope", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, - {&hf_aad_scope_primary, {"Primary Block", "bpsec.cose.aad_scope.primary", FT_BOOLEAN, 8, TFS(&tfs_set_notset), HAS_PRIMARY_CTX, NULL, HFILL}}, - {&hf_aad_scope_target, {"Target Block", "bpsec.cose.aad_scope.target", FT_BOOLEAN, 8, TFS(&tfs_set_notset), HAS_TARGET_CTX, NULL, HFILL}}, - {&hf_aad_scope_security, {"BPSec Block", "bpsec.cose.aad_scope.security", FT_BOOLEAN, 8, TFS(&tfs_set_notset), HAS_SECURITY_CTX, NULL, HFILL}}, + {&hf_aad_scope, {"AAD Scope, Block count", "bpsec.cose.aad_scope", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_aad_blknum, {"Block Number", "bpsec.cose.aad_scope.blknum", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_aad_flags, {"Flags", "bpsec.cose.aad_scope.flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_aad_flags_metadata, {"Metadata", "bpsec.cose.aad_scope.flags.metadata", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AAD_METADATA, NULL, HFILL}}, + {&hf_aad_flags_btsd, {"BTSD", "bpsec.cose.aad_scope.flags.btsd", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AAD_BTSD, NULL, HFILL}}, {&hf_addl_prot_bstr, {"Additional Protected Headers (bstr)", "bpsec.cose.addl_proected_bstr", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, - {&hf_addl_unprot, {"Additional Unprotected Headers", "bpsec.cose.addl_unprotected", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_addl_unprot_bstr, {"Additional Unprotected Headers (bstr)", "bpsec.cose.addl_unprotected", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, {&hf_cose_msg, {"COSE Message (bstr)", "bpsec.cose.msg", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, }; -static int *const aad_scope[] = { - &hf_aad_scope_primary, - &hf_aad_scope_target, - &hf_aad_scope_security, +static int *const aad_flags[] = { + &hf_aad_flags_metadata, + &hf_aad_flags_btsd, NULL }; static int ett_aad_scope = -1; -static int ett_addl_prot_bstr = -1; -static int ett_addl_prot = -1; -static int ett_addl_unprot = -1; +static int ett_aad_blknum = -1; +static int ett_aad_flags = -1; +static int ett_addl_hdr_bstr = -1; +static int ett_addl_hdr = -1; static int ett_cose_msg = -1; /// Tree structures static int *ett[] = { &ett_aad_scope, - &ett_addl_prot_bstr, - &ett_addl_prot, - &ett_addl_unprot, + &ett_aad_blknum, + &ett_aad_flags, + &ett_addl_hdr_bstr, + &ett_addl_hdr, &ett_cose_msg, }; /** Dissector for AAD Scope parameter. */ static int dissect_param_scope(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { - gint offset = 0; - - wscbor_chunk_t *chunk_flags = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - guint64 *flags = wscbor_require_uint64(wmem_packet_scope(), chunk_flags); - proto_tree_add_cbor_bitmask(tree, hf_aad_scope, ett_aad_scope, aad_scope, pinfo, tvb, chunk_flags, flags); + int offset = 0; + + wscbor_chunk_t *chunk_aad_map = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_map(chunk_aad_map); + proto_item *item_aad_map = proto_tree_add_cbor_container(tree, hf_aad_scope, pinfo, tvb, chunk_aad_map); + wscbor_chunk_mark_errors(pinfo, item_aad_map, chunk_aad_map); + if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_aad_map)) { + proto_tree *tree_aad_map = proto_item_add_subtree(item_aad_map, ett_aad_scope); + + for (guint64 ix = 0; ix < chunk_aad_map->head_value; ++ix) { + wscbor_chunk_t *chunk_blknum = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + int64_t *blknum = wscbor_require_int64(wmem_packet_scope(), chunk_blknum); + proto_item *item_blknum = proto_tree_add_cbor_int64(tree_aad_map, hf_aad_blknum, pinfo, tvb, chunk_blknum, blknum); + proto_tree *tree_blknum = proto_item_add_subtree(item_blknum, ett_aad_blknum); + + wscbor_chunk_t *chunk_flags = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *flags = wscbor_require_uint64(wmem_packet_scope(), chunk_flags); + proto_tree_add_cbor_bitmask(tree_blknum, hf_aad_flags, ett_aad_flags, aad_flags, pinfo, tvb, chunk_flags, flags); + } + } return offset; } @@ -80,15 +101,15 @@ static int dissect_param_scope(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr /** Dissector for COSE protected header. */ static int dissect_addl_protected(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { - gint offset = 0; + int offset = 0; - wscbor_chunk_t *chunk_prot_bstr = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - tvbuff_t *prot_bstr = wscbor_require_bstr(wmem_packet_scope(), chunk_prot_bstr); - proto_item *item_prot_bstr = proto_tree_add_cbor_bstr(tree, hf_addl_prot_bstr, pinfo, tvb, chunk_prot_bstr); - if (prot_bstr) { - proto_tree *tree_prot_bstr = proto_item_add_subtree(item_prot_bstr, ett_addl_prot_bstr); + wscbor_chunk_t *chunk_hdr_bstr = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + tvbuff_t *hdr_bstr = wscbor_require_bstr(wmem_packet_scope(), chunk_hdr_bstr); + proto_item *item_hdr_bstr = proto_tree_add_cbor_bstr(tree, hf_addl_prot_bstr, pinfo, tvb, chunk_hdr_bstr); + if (hdr_bstr) { + proto_tree *tree_hdr_bstr = proto_item_add_subtree(item_hdr_bstr, ett_addl_hdr_bstr); - int sublen = call_dissector(handle_cose_msg_hdr, prot_bstr, pinfo, tree_prot_bstr); + int sublen = call_dissector(handle_cose_msg_hdr, hdr_bstr, pinfo, tree_hdr_bstr); if (sublen < 0) { return sublen; } @@ -101,18 +122,30 @@ static int dissect_addl_protected(tvbuff_t *tvb, packet_info *pinfo, proto_tree /** Dissector for COSE unprotected header. */ static int dissect_addl_unprotected(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { - proto_item *item_hdr = proto_tree_add_item(tree, hf_addl_unprot, tvb, 0, -1, ENC_NA); - proto_tree *tree_hdr = proto_item_add_subtree(item_hdr, ett_addl_prot); - int sublen = call_dissector(handle_cose_msg_hdr, tvb, pinfo, tree_hdr); - return sublen; + int offset = 0; + + wscbor_chunk_t *chunk_hdr_bstr = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + tvbuff_t *hdr_bstr = wscbor_require_bstr(wmem_packet_scope(), chunk_hdr_bstr); + proto_item *item_hdr_bstr = proto_tree_add_cbor_bstr(tree, hf_addl_unprot_bstr, pinfo, tvb, chunk_hdr_bstr); + if (hdr_bstr) { + proto_tree *tree_hdr_bstr = proto_item_add_subtree(item_hdr_bstr, ett_addl_hdr_bstr); + + int sublen = call_dissector(handle_cose_msg_hdr, hdr_bstr, pinfo, tree_hdr_bstr); + if (sublen < 0) { + return sublen; + } + offset += sublen; + } + + return offset; } /** Dissector for bstr-wrapped CBOR. */ static int dissect_cose_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { - gint64 *typeid = data; - DISSECTOR_ASSERT(typeid != NULL); - gint offset = 0; + bpsec_id_t *secid = data; + DISSECTOR_ASSERT(secid != NULL); + int offset = 0; wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); tvbuff_t *tvb_data = wscbor_require_bstr(wmem_packet_scope(), chunk); @@ -121,7 +154,7 @@ static int dissect_cose_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *tree_msg = proto_item_add_subtree(item_msg, ett_cose_msg); if (tvb_data) { - dissector_handle_t dissector = dissector_get_custom_table_handle(table_cose_msg, typeid); + dissector_handle_t dissector = dissector_get_custom_table_handle(table_cose_msg, &(secid->type_id)); int sublen = call_dissector(dissector, tvb_data, pinfo, tree_msg); if (sublen < 0) { return sublen; @@ -149,46 +182,52 @@ void proto_register_bpsec_cose(void) { prefs_register_protocol(proto_bpsec_cose, reinit_bpsec_cose); } +static void bpsec_cose_result_register(int64_t result_id, const char *dis_name) { + bpsec_id_t *rkey = bpsec_id_new(NULL, bpsec_cose_ctxid, result_id); + const char *description = dissector_handle_get_description(find_dissector_add_dependency(dis_name, proto_bpsec_cose)); + dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_cose_msg, proto_bpsec_cose, NULL, description); + dissector_add_custom_table_handle("bpsec.result", rkey, hdl); + +} + void proto_reg_handoff_bpsec_cose(void) { table_cose_msg = find_dissector_table("cose.msgtag"); handle_cose_msg_hdr = find_dissector_add_dependency("cose.msg.headers", proto_bpsec_cose); /* Packaged extensions */ - const gint64 ctxid = 99; { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, 1); + bpsec_id_t *key = bpsec_id_new(NULL, bpsec_cose_ctxid, 1); dissector_handle_t hdl = find_dissector_add_dependency("cose_key", proto_bpsec_cose); dissector_add_custom_table_handle("bpsec.param", key, hdl); } { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, 2); + bpsec_id_t *key = bpsec_id_new(NULL, bpsec_cose_ctxid, 2); dissector_handle_t hdl = find_dissector_add_dependency("cose_key_set", proto_bpsec_cose); dissector_add_custom_table_handle("bpsec.param", key, hdl); } { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, 3); - dissector_handle_t hdl = create_dissector_handle_with_name(dissect_addl_protected, proto_bpsec_cose, "Additional Protected Headers"); + bpsec_id_t *key = bpsec_id_new(NULL, bpsec_cose_ctxid, 3); + dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_addl_protected, proto_bpsec_cose, NULL, "Additional Protected Headers"); dissector_add_custom_table_handle("bpsec.param", key, hdl); } { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, 4); - dissector_handle_t hdl = create_dissector_handle_with_name(dissect_addl_unprotected, proto_bpsec_cose, "Additional Unprotected Headers"); + bpsec_id_t *key = bpsec_id_new(NULL, bpsec_cose_ctxid, 4); + dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_addl_unprotected, proto_bpsec_cose, NULL, "Additional Unprotected Headers"); dissector_add_custom_table_handle("bpsec.param", key, hdl); } { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, 5); - dissector_handle_t hdl = create_dissector_handle_with_name(dissect_param_scope, proto_bpsec_cose, "Scope"); + bpsec_id_t *key = bpsec_id_new(NULL, bpsec_cose_ctxid, 5); + dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_param_scope, proto_bpsec_cose, NULL, "AAD Scope"); dissector_add_custom_table_handle("bpsec.param", key, hdl); } - { - const gint64 cose_msg_ids[] = {16, 17, 18, 96, 97, 98}; - for (const gint64 *it = cose_msg_ids; it != cose_msg_ids + 6; ++it) { - bpsec_id_t *key = bpsec_id_new(NULL, ctxid, *it); - char *name = wmem_strdup_printf(wmem_epan_scope(), "COSE message type %" PRId64, *it); - dissector_handle_t hdl = create_dissector_handle_with_name(dissect_cose_msg, proto_bpsec_cose, name); - dissector_add_custom_table_handle("bpsec.result", key, hdl); - } - } + + // Propagate COSE tags as result IDs + bpsec_cose_result_register(98, "cose_sign"); + bpsec_cose_result_register(18, "cose_sign1"); + bpsec_cose_result_register(96, "cose_encrypt"); + bpsec_cose_result_register(16, "cose_encrypt0"); + bpsec_cose_result_register(97, "cose_mac"); + bpsec_cose_result_register(17, "cose_mac0"); reinit_bpsec_cose(); } diff --git a/src/packet-udpcl.c b/src/packet-udpcl.c index 55c1e98..c066e88 100644 --- a/src/packet-udpcl.c +++ b/src/packet-udpcl.c @@ -7,10 +7,12 @@ #include #include #include +#include +#include +#include #include #include #include "epan/wscbor.h" -#include "packet-udpcl.h" #if defined(WIRESHARK_HAS_VERSION_H) #include @@ -42,9 +44,10 @@ static const char *LOG_DOMAIN = "udpcl"; static const char *const proto_name_udpcl = "UDPCL"; /// Protocol preferences and defaults -static const guint UDPCL_PORT_NUM = 4556; -static gboolean udpcl_desegment_transfer = TRUE; -static gboolean udpcl_decode_bundle = TRUE; +static const unsigned UDPCL_PORT_NUM = 4556; +static bool udpcl_desegment_transfer = TRUE; +static bool udpcl_analyze_sequence = TRUE; +static bool udpcl_decode_bundle = TRUE; /// Protocol handles static int proto_udpcl = -1; @@ -63,8 +66,6 @@ static dissector_table_t table_ext = NULL; /// Fragment reassembly static reassembly_table udpcl_reassembly_table; -const unit_name_string units_item_items = { " item", " items" }; - static int hf_padding = -1; static int hf_ext_map = -1; static int hf_ext_id = -1; @@ -89,33 +90,71 @@ static int hf_xferload_reassembled_data = -1; static gint ett_xferload_fragment = -1; static gint ett_xferload_fragments = -1; -static int hf_ext_rtn = -1; -static int hf_rtn_interval = -1; +static int hf_ext_sender_listen = -1; +static int hf_sender_listen_interval = -1; static int hf_ext_nodeid = -1; static int hf_nodeid_str = -1; static int hf_ext_starttls = -1; +static int hf_ext_peer_probe = -1; +static int hf_probe_nonce = -1; +static int hf_probe_seqno = -1; +static int hf_probe_delay = -1; +static int hf_probe_confirm_ref = -1; + +static int hf_ext_peer_confirm = -1; +static int hf_confirm_nonce = -1; +static int hf_confirm_offset = -1; +static int hf_confirm_length = -1; +static int hf_confirm_gap = -1; +static int hf_confirm_probe_ref = -1; +static int hf_confirm_probe_tlp = -1; + +static int hf_ext_ecn_counts = -1; +static int hf_ecn_ect0 = -1; +static int hf_ecn_ect1 = -1; +static int hf_ecn_ce = -1; + /// Field definitions static hf_register_info fields[] = { - {&hf_padding, {"Padding", "udpcl.padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, - {&hf_ext_map, {"Extension Map", "udpcl.ext", FT_INT64, BASE_DEC | BASE_UNIT_STRING, &units_item_items, 0x0, NULL, HFILL}}, + {&hf_padding, {"Padding, Length", "udpcl.padding", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, + {&hf_ext_map, {"Extension Map, Count", "udpcl.ext", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_ext_id, {"Extension ID", "udpcl.ext.id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, - {&hf_ext_xfer, {"Transfer", "udpcl.ext.xfer", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_ext_xfer, {"Transfer", "udpcl.ext.xfer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, {&hf_xfer_id, {"Transfer ID", "udpcl.ext.xfer.id", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_xfer_total_length, {"Transfer Length", "udpcl.ext.xfer.total_len", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, {&hf_xfer_frag_offset, {"Fragment Offset", "udpcl.ext.xfer.frag_offset", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, {&hf_xfer_data, {"Fragment Data", "udpcl.ext.xfer.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, - {&hf_ext_rtn, {"Sender Listen", "udpcl.ext.rtn", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, - {&hf_rtn_interval, {"Interval", "udpcl.ext.rtn.interval", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, + {&hf_ext_sender_listen, {"Sender Listen", "udpcl.ext.rtn", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_sender_listen_interval, {"Interval", "udpcl.ext.rtn.interval", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, - {&hf_ext_nodeid, {"Sender Node ID", "udpcl.ext.nodeid", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_ext_nodeid, {"Sender Node ID", "udpcl.ext.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, {&hf_nodeid_str, {"Node ID", "udpcl.ext.nodeid.str", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, - {&hf_ext_starttls, {"Initiate DTLS", "udpcl.ext.starttls", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_ext_starttls, {"DTLS Initiation", "udpcl.ext.starttls", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + + {&hf_ext_peer_probe, {"Peer Probe", "udpcl.ext.peer_probe", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_probe_nonce, {"Nonce", "udpcl.ext.peer_probe.nonce", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_probe_seqno, {"Sequence Number", "udpcl.ext.peer_probe.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_probe_delay, {"Confirmation Delay", "udpcl.ext.peer_probe.delay", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, + {&hf_probe_confirm_ref, {"Confirmation in frame", "udpcl.ext.peer_probe.confirm_rev", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, NULL, HFILL}}, + + {&hf_ext_peer_confirm, {"Peer Confirmation", "udpcl.ext.peer_confirm", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_confirm_nonce, {"Nonce", "udpcl.ext.peer_confirm.nonce", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_confirm_offset, {"Offset", "udpcl.ext.peer_confirm.offset", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_confirm_length, {"Length", "udpcl.ext.peer_confirm.length", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_confirm_gap, {"Gap Length", "udpcl.ext.peer_confirm.gap_length", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_confirm_probe_ref, {"Probe in frame", "udpcl.ext.peer_confirm.probe_rev", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, NULL, HFILL}}, + {&hf_confirm_probe_tlp, {"Time since latest Probe", "udpcl.ext.peer_confirm.time_last_probe", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + + {&hf_ext_ecn_counts, {"ECN Counts", "udpcl.ext.ecn_counts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_ecn_ect0, {"ECT(0) Count", "udpcl.ext.ecn_counts.ect0", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_ecn_ect1, {"ECT(1) Count", "udpcl.ext.ecn_counts.ect1", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_ecn_ce, {"CE Count", "udpcl.ext.ecn_counts.ce", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_xferload_fragments, {"Transfer fragments", "udpcl.xferload.fragments", @@ -179,35 +218,61 @@ static int ett_udpcl = -1; static int ett_ext_map = -1; static int ett_ext_item = -1; static int ett_ext_xfer = -1; -static int ett_ext_rtn = -1; +static int ett_ext_sender_listen = -1; static int ett_ext_nodeid = -1; +static int ett_ext_ecn_counts = -1; +static int ett_ext_peer_probe = -1; +static int ett_ext_peer_confirm = -1; +static int ett_confirm_intvl = -1; /// Tree structures static int *ett[] = { &ett_udpcl, &ett_ext_map, &ett_ext_item, &ett_ext_xfer, - &ett_ext_rtn, + &ett_ext_sender_listen, &ett_ext_nodeid, + &ett_ext_ecn_counts, + &ett_ext_peer_probe, + &ett_ext_peer_confirm, + &ett_confirm_intvl, &ett_xferload_fragment, &ett_xferload_fragments, }; static expert_field ei_pad_nonzero = EI_INIT; +static expert_field ei_ext_key_invalid = EI_INIT; static expert_field ei_ext_key_unknown = EI_INIT; static expert_field ei_transfer_id_size = EI_INIT; static expert_field ei_fragment_reassemble_size = EI_INIT; static expert_field ei_fragment_tot_mismatch = EI_INIT; static expert_field ei_non_bundle_data = EI_INIT; +static expert_field ei_probe_no_confirm = EI_INIT; static ei_register_info expertitems[] = { - {&ei_pad_nonzero, { "udpcl.padding.nonzero", PI_MALFORMED, PI_WARN, "Padding has non-zero octet", EXPFILL}}, - {&ei_ext_key_unknown, {"bpv7.ext_key_unknown", PI_UNDECODED, PI_WARN, "Unknown extension ID", EXPFILL}}, + {&ei_pad_nonzero, { "udpcl.padding.nonzero", PI_MALFORMED, PI_WARN, "Padding has non-zero-value octet", EXPFILL}}, + {&ei_ext_key_invalid, {"udpcl.ext_key_invalid", PI_PROTOCOL, PI_WARN, "Extension ID is not in the valid range", EXPFILL}}, + {&ei_ext_key_unknown, {"udpcl.ext_key_unknown", PI_UNDECODED, PI_WARN, "Extension ID is unknown", EXPFILL}}, {&ei_transfer_id_size, {"udpcl.transfer_id_size", PI_REASSEMBLE, PI_ERROR, "Cannot defragment this Transfer ID (wireshark limitation)", EXPFILL}}, {&ei_fragment_reassemble_size, {"udpcl.fragment_reassemble_size", PI_REASSEMBLE, PI_ERROR, "Cannot defragment this size (wireshark limitation)", EXPFILL}}, {&ei_fragment_tot_mismatch, {"udpcl.fragment_tot_mismatch", PI_REASSEMBLE, PI_ERROR, "Inconsistent total length between fragments", EXPFILL}}, {&ei_non_bundle_data, { "udpcl.non_bundle_data", PI_UNDECODED, PI_WARN, "Non-bundle data present", EXPFILL}}, + {&ei_probe_no_confirm, { "udpcl.peer_probe_no_confirm", PI_PROTOCOL, PI_CHAT, "Peer Probe has no associated Peer Confirmation", EXPFILL}}, }; +typedef struct { + /// Original frame number + uint32_t frame_num; + /// Timestamp on the frame + nstime_t frame_time; +} udpcl_frameinfo_t; + +typedef struct { + /// Map from uint64* nonce to wmem_tree_t* mapping sequence-number to udpcl_frameinfo_t* + wmem_map_t *peer_probe; + /// Map from uint64* nonce to wmem_itree_t* mapping sequence-number to udpcl_frameinfo_t* + wmem_map_t *peer_confirm; +} udpcl_convo_t; + /** Dissect pure bundle data. * This may contain either BPv6 or BPv7. */ @@ -217,8 +282,8 @@ static int dissect_bundle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ud proto_item *tree = proto_tree_get_root(tree_udpcl); // Peek at first octet - const guint8 first_octet = tvb_get_guint8(tvb, 0); - wscbor_chunk_t *first_head = wscbor_chunk_read(wmem_packet_scope(), tvb, &sublen); + const guint8 first_octet = tvb_get_uint8(tvb, 0); + wscbor_chunk_t *first_head = wscbor_chunk_read(pinfo->pool, tvb, &sublen); sublen = 0; if (first_octet == 0x06) { @@ -267,58 +332,65 @@ static int dissect_bundle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ud /// Dissect transfer extension item static int dissect_transfer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { gint offset = 0; - col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Transfer"); - proto_item *item_xfer = proto_tree_add_item(tree_ext_item, hf_ext_xfer, tvb, offset, -1, ENC_NA); - proto_tree *tree_xfer = proto_item_add_subtree(item_xfer, ett_ext_xfer); + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_xfer, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_xfer); - wscbor_chunk_t *chunk_xfer = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - wscbor_require_array_size(chunk_xfer, 4, 4); - if (wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_xfer)) { - proto_item_set_len(item_xfer, offset); - return offset; + wscbor_chunk_t *chunk_xfer = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_array_size(chunk_xfer, 2, 4); + if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_xfer)) { + proto_item_set_len(item_ext, offset); + return 0; } - wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - guint64 *xfer_id = wscbor_require_uint64(wmem_packet_scope(), chunk); - proto_tree_add_cbor_uint64(tree_xfer, hf_xfer_id, pinfo, tvb, chunk, xfer_id); + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *xfer_id = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_xfer_id, pinfo, tvb, chunk, xfer_id); - chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - guint64 *xfer_tot_len = wscbor_require_uint64(wmem_packet_scope(), chunk); - proto_tree_add_cbor_uint64(tree_xfer, hf_xfer_total_length, pinfo, tvb, chunk, xfer_tot_len); + uint64_t *xfer_tot_len = NULL; + uint64_t *xfer_frag_offset = NULL; + if (chunk->head_value == 4) { + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + xfer_tot_len = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_xfer_total_length, pinfo, tvb, chunk, xfer_tot_len); - chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - guint64 *xfer_frag_offset = wscbor_require_uint64(wmem_packet_scope(), chunk); - proto_tree_add_cbor_uint64(tree_xfer, hf_xfer_frag_offset, pinfo, tvb, chunk, xfer_frag_offset); + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + xfer_frag_offset = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_xfer_frag_offset, pinfo, tvb, chunk, xfer_frag_offset); + } - chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - tvbuff_t *xfer_fragment = wscbor_require_bstr(wmem_packet_scope(), chunk); - proto_tree_add_cbor_bstr(tree_xfer, hf_xfer_data, pinfo, tvb, chunk); + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + tvbuff_t *xfer_fragment = wscbor_require_bstr(pinfo->pool, chunk); + proto_tree_add_cbor_bstr(tree_ext, hf_xfer_data, pinfo, tvb, chunk); if (udpcl_desegment_transfer - && xfer_id && xfer_tot_len && xfer_frag_offset && xfer_fragment) { + && xfer_id && xfer_fragment) { proto_tree *tree_ext_map = proto_tree_get_parent_tree(tree_ext_item); proto_tree *tree_udpcl = proto_tree_get_parent_tree(tree_ext_map); proto_item *item_udpcl = proto_tree_get_parent(tree_udpcl); proto_item_append_text(item_udpcl, ", Transfer (ID: %" PRId64 ", Fragment offset: %" PRId64 ")", *xfer_id, *xfer_frag_offset); - const guint32 corr_id = *xfer_id; - const gboolean overflow_corr_id = ( + const uint32_t corr_id = *xfer_id; + const bool overflow_corr_id = ( (corr_id != *xfer_id) ); if (overflow_corr_id) { expert_add_info(pinfo, item_udpcl, &ei_transfer_id_size); } - const guint32 frag_offset = *(xfer_frag_offset); - const guint32 total_len = *(xfer_tot_len); - const gboolean overflow_frag_size = ( - (frag_offset != *(xfer_frag_offset)) - || (total_len != *(xfer_tot_len)) - ); - if (overflow_frag_size) { - expert_add_info(pinfo, item_udpcl, &ei_fragment_reassemble_size); + const uint32_t frag_offset = xfer_frag_offset ? *xfer_frag_offset : 0; + const uint32_t total_len = xfer_tot_len ? *xfer_tot_len : tvb_reported_length(xfer_fragment); + + bool overflow_frag_size = FALSE; + if (xfer_frag_offset && xfer_tot_len) { + overflow_frag_size = ( + (frag_offset != *(xfer_frag_offset)) + || (total_len != *(xfer_tot_len)) + ); + if (overflow_frag_size) { + expert_add_info(pinfo, item_udpcl, &ei_fragment_reassemble_size); + } } if (!overflow_corr_id && !overflow_frag_size) { @@ -330,13 +402,13 @@ static int dissect_transfer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ tvb_captured_length(xfer_fragment), TRUE ); - const guint32 old_total_len = fragment_get_tot_len( + const uint32_t old_total_len = fragment_get_tot_len( &udpcl_reassembly_table, pinfo, corr_id, NULL ); if (old_total_len > 0) { if (total_len != old_total_len) { - expert_add_info(pinfo, item_xfer, &ei_fragment_tot_mismatch); + expert_add_info(pinfo, item_ext, &ei_fragment_tot_mismatch); } } else { @@ -364,53 +436,52 @@ static int dissect_transfer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ } } - proto_item_set_len(item_xfer, offset); + proto_item_set_len(item_ext, offset); return offset; } -static int dissect_return_accept(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { +static int dissect_sender_listen(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { gint offset = 0; - proto_item *item_rtn = proto_tree_add_item(tree_ext_item, hf_ext_rtn, tvb, offset, -1, ENC_NA); - proto_tree *tree_rtn = proto_item_add_subtree(item_rtn, ett_ext_rtn); + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_sender_listen, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_sender_listen); - wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - guint64 *interval = wscbor_require_uint64(wmem_packet_scope(), chunk); - proto_tree_add_cbor_uint64(tree_rtn, hf_rtn_interval, pinfo, tvb, chunk, interval); + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *interval = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_sender_listen_interval, pinfo, tvb, chunk, interval); proto_tree *tree_ext_map = proto_tree_get_parent_tree(tree_ext_item); proto_tree *tree_udpcl = proto_tree_get_parent_tree(tree_ext_map); proto_item *item_udpcl = proto_tree_get_parent(tree_udpcl); proto_item_append_text(item_udpcl, ", Sender Listen"); - proto_item_set_len(item_rtn, offset); + proto_item_set_len(item_ext, offset); return offset; } static int dissect_nodeid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { gint offset = 0; - proto_item *item_nodeid = proto_tree_add_item(tree_ext_item, hf_ext_nodeid, tvb, offset, -1, ENC_NA); - proto_tree *tree_nodeid = proto_item_add_subtree(item_nodeid, ett_ext_nodeid); + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_nodeid, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_nodeid); - wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); wscbor_require_major_type(chunk, CBOR_TYPE_STRING); - proto_tree_add_cbor_tstr(tree_nodeid, hf_nodeid_str, pinfo, tvb, chunk); + proto_tree_add_cbor_tstr(tree_ext, hf_nodeid_str, pinfo, tvb, chunk); proto_tree *tree_ext_map = proto_tree_get_parent_tree(tree_ext_item); proto_tree *tree_udpcl = proto_tree_get_parent_tree(tree_ext_map); proto_item *item_udpcl = proto_tree_get_parent(tree_udpcl); proto_item_append_text(item_udpcl, ", Source Node ID"); - proto_item_set_len(item_nodeid, offset); + proto_item_set_len(item_ext, offset); return offset; } static int dissect_starttls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { gint offset = 0; - col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Initiate DTLS"); - wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + wscbor_skip_next_item(pinfo->pool, tvb, &offset); // no real value proto_tree_add_item(tree_ext_item, hf_ext_starttls, tvb, 0, offset, ENC_NA); @@ -418,6 +489,308 @@ static int dissect_starttls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ return offset; } +static int dissect_peer_probe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data) { + udpcl_convo_t *clconvo = data; + gint offset = 0; + + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_peer_probe, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_peer_probe); + + wscbor_chunk_t *chunk_ext = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_array_size(chunk_ext, 3, 3); + if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_ext)) { + proto_item_set_len(item_ext, offset); + return 0; + } + + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *nonce = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_probe_nonce, pinfo, tvb, chunk, nonce); + + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *seqno = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_probe_seqno, pinfo, tvb, chunk, seqno); + + if (udpcl_analyze_sequence && nonce && seqno) { + wmem_tree_t *probe_nums = wmem_map_lookup(clconvo->peer_probe, nonce); + if (!probe_nums) { + uint64_t *key = wmem_new0(wmem_file_scope(), uint64_t); + *key = *nonce; + probe_nums = wmem_tree_new(wmem_file_scope()); + wmem_map_insert(clconvo->peer_probe, key, probe_nums); + } + if (!wmem_tree_contains32(probe_nums, *seqno)) { + udpcl_frameinfo_t *uinfo = wmem_new0(wmem_file_scope(), udpcl_frameinfo_t); + uinfo->frame_num = pinfo->num; + uinfo->frame_time = pinfo->abs_ts; + wmem_tree_insert32(probe_nums, *seqno, uinfo); + } + + wmem_itree_t *confirm_nums = wmem_map_lookup(clconvo->peer_confirm, nonce); + bool confirm_found = FALSE; + if (confirm_nums) { + wmem_list_t *intvls = wmem_itree_find_intervals(confirm_nums, pinfo->pool, *seqno, *seqno); + for (wmem_list_frame_t *it = wmem_list_head(intvls); it; + it = wmem_list_frame_next(it)) { + udpcl_frameinfo_t *uinfo = wmem_list_frame_data(it); + confirm_found = TRUE; + + proto_item_set_generated( + proto_tree_add_uint(tree_ext, hf_probe_confirm_ref, NULL, 0, 0, uinfo->frame_num) + ); + } + wmem_destroy_list(intvls); + } + if (!confirm_found) { + expert_add_info(pinfo, item_ext, &ei_probe_no_confirm); + } + } + + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *delay = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_probe_delay, pinfo, tvb, chunk, delay); + + proto_item_set_len(item_ext, offset); + return offset; +} + +static int dissect_peer_confirm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data) { + udpcl_convo_t *clconvo = data; + gint offset = 0; + + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_peer_confirm, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_peer_confirm); + + wscbor_chunk_t *chunk_ext = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_array_size(chunk_ext, 2, 2); + if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_ext)) { + proto_item_set_len(item_ext, offset); + return 0; + } + + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *nonce = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_confirm_nonce, pinfo, tvb, chunk, nonce); + + wmem_itree_t *confirm_nums = NULL; + wmem_tree_t *probe_nums = NULL; + if (udpcl_analyze_sequence && nonce) { + confirm_nums = wmem_map_lookup(clconvo->peer_confirm, nonce); + if (!confirm_nums) { + uint64_t *key = wmem_new0(wmem_file_scope(), uint64_t); + *key = *nonce; + confirm_nums = wmem_itree_new(wmem_file_scope()); + wmem_map_insert(clconvo->peer_confirm, key, confirm_nums); + } + + probe_nums = wmem_map_lookup(clconvo->peer_probe, nonce); + } + + wscbor_chunk_t *chunk_seen = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_array(chunk_seen); + if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_seen)) { + proto_item_set_len(item_ext, offset); + return 0; + } + + uint64_t last_intvl = 0; + /// Time of latest probe acknowledged by this confirmation + nstime_t last_probe = NSTIME_INIT_UNSET; + for (uint64_t ix = 0; ix < chunk_seen->head_value; ix += 2) { + const unsigned init_offset = offset; + + // decode data first to spot a gap + wscbor_chunk_t *chunk_offset = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *intvl_offset = wscbor_require_uint64(pinfo->pool, chunk_offset); + wscbor_chunk_t *chunk_length = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *intvl_length = wscbor_require_uint64(pinfo->pool, chunk_length); + + if (intvl_offset && (*intvl_offset > 0)) { + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(tree_ext, hf_confirm_gap, tvb, 0, 0, *intvl_offset) + ); + } + + proto_item *item_intvl; + proto_tree *tree_intvl = proto_tree_add_subtree(tree_ext, tvb, init_offset, offset - init_offset, ett_confirm_intvl, &item_intvl, "Seen Interval"); + proto_tree_add_cbor_uint64(tree_intvl, hf_confirm_offset, pinfo, tvb, chunk_offset, intvl_offset); + proto_tree_add_cbor_uint64(tree_intvl, hf_confirm_length, pinfo, tvb, chunk_length, intvl_length); + + if (intvl_offset && intvl_length) { + const uint64_t intvl_fst = last_intvl + *intvl_offset; + const uint64_t intvl_lst = intvl_fst + *intvl_length - 1; + last_intvl = intvl_lst + 1; + proto_item_append_text(item_intvl, + ": %" PRIu64 "-%" PRIu64 " (%" PRIu64 " items)", + intvl_fst, intvl_lst, *intvl_length + ); + + if (confirm_nums) { + wmem_list_t *intvls = wmem_itree_find_intervals(confirm_nums, pinfo->pool, intvl_fst, intvl_lst); + if (wmem_list_count(intvls) == 0) { + udpcl_frameinfo_t *uinfo = wmem_new0(wmem_file_scope(), udpcl_frameinfo_t); + uinfo->frame_num = pinfo->num; + uinfo->frame_time = pinfo->abs_ts; + wmem_itree_insert(confirm_nums, intvl_fst, intvl_lst, uinfo); + } + wmem_destroy_list(intvls); + } + if (probe_nums) { + for (uint64_t seqno = intvl_fst; seqno <= intvl_lst; ++seqno) { + udpcl_frameinfo_t *uinfo = wmem_tree_lookup32(probe_nums, seqno); + if (uinfo) { + if (nstime_is_unset(&last_probe) + || (nstime_cmp(&last_probe, &(uinfo->frame_time)) > 0)) { + nstime_copy(&last_probe, &(uinfo->frame_time)); + } + proto_item_set_generated( + proto_tree_add_uint(tree_intvl, hf_confirm_probe_ref, NULL, 0, 0, uinfo->frame_num) + ); + } + } + } + } + } + + if (!nstime_is_unset(&last_probe)) { + nstime_t delta; + nstime_delta(&delta, &(pinfo->abs_ts), &last_probe); + proto_item_set_generated( + proto_tree_add_time(tree_ext, hf_confirm_probe_tlp, NULL, 0, 0, &delta) + ); + } + + proto_item_set_len(item_ext, offset); + return offset; +} + +static int dissect_ecn_counts(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_ext_item, void *data _U_) { + gint offset = 0; + + proto_item *item_ext = proto_tree_add_item(tree_ext_item, hf_ext_ecn_counts, tvb, offset, -1, ENC_NA); + proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_ext_ecn_counts); + + wscbor_chunk_t *chunk_ext = wscbor_chunk_read(pinfo->pool, tvb, &offset); + wscbor_require_array_size(chunk_ext, 3, 3); + if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_ext)) { + proto_item_set_len(item_ext, offset); + return 0; + } + + wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *ect0 = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_ecn_ect0, pinfo, tvb, chunk, ect0); + + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *ect1 = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_ecn_ect1, pinfo, tvb, chunk, ect1); + + chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + uint64_t *ce = wscbor_require_uint64(pinfo->pool, chunk); + proto_tree_add_cbor_uint64(tree_ext, hf_ecn_ce, pinfo, tvb, chunk, ce); + + proto_item_set_len(item_ext, offset); + return offset; +} + +static int dissect_extmap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_udpcl, wscbor_chunk_t *first_head) { + gint offset = first_head->start; + const gint init_offset = offset; + + // Ignore the destination address for multicast compatibility + gint copts = 0; + if (pinfo->dst.type == AT_IPv4) { + const ws_in4_addr *dst = pinfo->dst.data; + if (in4_addr_is_multicast(*dst)) { + copts |= NO_ADDR_B; + } + } + else if (pinfo->dst.type == AT_IPv6) { + const ws_in6_addr *dst = pinfo->dst.data; + if (in6_addr_is_multicast(dst)) { + copts |= NO_ADDR_B; + } + } + // the UDP conversation should already exist + conversation_t *convo = find_conversation_pinfo(pinfo, copts); + + udpcl_convo_t *clconvo; + clconvo = conversation_get_proto_data(convo, proto_udpcl); + if (!clconvo) + { + wmem_allocator_t *alloc = wmem_file_scope(); + clconvo = wmem_new0(alloc, udpcl_convo_t); + clconvo->peer_probe = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + clconvo->peer_confirm = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + + conversation_add_proto_data(convo, proto_udpcl, clconvo); + } + + col_append_str(pinfo->cinfo, COL_INFO, "["); + offset += 1; + + const gint64 count = first_head->head_value; + proto_item *item_ext_map = proto_tree_add_cbor_int64(tree_udpcl, hf_ext_map, pinfo, tvb, first_head, &count); + proto_tree *tree_ext_map = proto_item_add_subtree(item_ext_map, ett_ext_map); + + for (gint64 ix = 0; ix < count; ++ix) { + wscbor_chunk_t *key_chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset); + gint64 *key = wscbor_require_int64(pinfo->pool, key_chunk); + proto_item *item_ext_item = proto_tree_add_cbor_int64(tree_ext_map, hf_ext_id, pinfo, tvb, key_chunk, key); + proto_tree *tree_ext_item = proto_item_add_subtree(item_ext_item, ett_ext_item); + + // Restrict to valid range + if (key && ((*key == 0) || (*key < -32768) || (*key >= 32768))) { + expert_add_info(pinfo, item_ext_item, &ei_ext_key_invalid); + key = NULL; + } + + // Skip the item to detect its length for subset TVB + const unsigned init_offset = offset; + wscbor_skip_next_item(pinfo->pool, tvb, &offset); + if (!key) { + continue; + } + + dissector_handle_t dissector = NULL; + if (*key >= 0) { + dissector = dissector_get_uint_handle(table_ext, *key); + } + + tvbuff_t *tvb_item = tvb_new_subset_length(tvb, init_offset, offset - init_offset); + int sublen = 0; + const char *dis_name = NULL; + if (dissector) { + sublen = call_dissector_only(dissector, tvb_item, pinfo, tree_ext_item, clconvo); + dis_name = dissector_handle_get_dissector_name(dissector); + } + else if (*key >= 0) { + // negative keys are private use + expert_add_info(pinfo, item_ext_item, &ei_ext_key_unknown); + } + + if (ix > 0) { + col_append_str(pinfo->cinfo, COL_INFO, ","); + } + if (dis_name) { + proto_item_set_text(item_ext_item, "Extension ID: %s (%" PRId64 ")", dis_name, *key); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s (%" PRId64 ")", dis_name, *key); + } + else { + col_append_fstr(pinfo->cinfo, COL_INFO, "%" PRIu64, *key); + } + + // show something even if known dissector failed + if (sublen == 0) { + sublen = call_dissector(handle_cbor, tvb_item, pinfo, tree_ext_item); + } + } + proto_item_set_len(item_ext_map, offset - init_offset); + + col_append_str(pinfo->cinfo, COL_INFO, "]"); + return offset - init_offset; +} + /// Top-level protocol dissector static int dissect_udpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { { @@ -428,24 +801,25 @@ static int dissect_udpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo } } - const guint buflen = tvb_captured_length(tvb); + const unsigned buflen = tvb_captured_length(tvb); gint offset = 0; proto_item *item_udpcl = proto_tree_add_item(tree, proto_udpcl, tvb, 0, -1, ENC_NA); proto_tree *tree_udpcl = proto_item_add_subtree(item_udpcl, ett_udpcl); - while ((guint)offset < buflen) { + while ((unsigned)offset < buflen) { // Peek at first octet - const guint8 first_octet = tvb_get_guint8(tvb, offset); - wscbor_chunk_t *first_head = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - offset -= first_head->data_length; + const guint8 first_octet = tvb_get_uint8(tvb, offset); + gint peek_offset = offset; + wscbor_chunk_t *first_head = wscbor_chunk_read(pinfo->pool, tvb, &peek_offset); if (first_octet == 0x00) { - col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Padding"); + const unsigned padlen = buflen - offset; + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Padding[len=%" PRIu32 "]", padlen); proto_item_append_text(item_udpcl, ", Padding"); - proto_item *item_ka = proto_tree_add_item(tree_udpcl, hf_padding, tvb, offset, buflen - offset, ENC_NA); - for (guint ix = offset + 1; ix < buflen; ++ix) { - if (tvb_get_guint8(tvb, ix) != 0x0) { + proto_item *item_ka = proto_tree_add_uint64(tree_udpcl, hf_padding, tvb, offset, padlen, padlen); + for (unsigned ix = offset + 1; ix < buflen; ++ix) { + if (tvb_get_uint8(tvb, ix) != 0x0) { expert_add_info(pinfo, item_ka, &ei_pad_nonzero); break; } @@ -461,38 +835,22 @@ static int dissect_udpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo else if (first_head->type_major == CBOR_TYPE_MAP) { col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Extension Map"); proto_item_append_text(item_udpcl, ", Extension Map"); - offset += 1; - - const gint64 count = first_head->head_value; - proto_item *item_ext_map = proto_tree_add_cbor_int64(tree_udpcl, hf_ext_map, pinfo, tvb, first_head, &count); - proto_tree *tree_ext_map = proto_item_add_subtree(item_ext_map, ett_ext_map); - - for (gint64 ix = 0; ix < count; ++ix) { - wscbor_chunk_t *key_chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); - gint64 *key = wscbor_require_int64(wmem_packet_scope(), key_chunk); - proto_item *item_ext_item = proto_tree_add_cbor_int64(tree_ext_map, hf_ext_id, pinfo, tvb, key_chunk, key); - proto_tree *tree_ext_item = proto_item_add_subtree(item_ext_item, ett_ext_item); - - // Skip the item to detect its length for subset TVB - const guint init_offset = offset; - wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); - if (!key) { - continue; - } - tvbuff_t *tvb_item = tvb_new_subset_length(tvb, init_offset, offset - init_offset); - int sublen = dissector_try_uint(table_ext, *key, tvb_item, pinfo, tree_ext_item); - if (sublen == 0) { - expert_add_info(pinfo, item_ext_item, &ei_ext_key_unknown); - sublen = call_dissector(handle_cbor, tvb_item, pinfo, tree_ext_item); - } - } - proto_item_set_len(item_ext_map, offset); + const int sublen = dissect_extmap(tvb, pinfo, tree_udpcl, first_head); + if (sublen > 0) { + offset += sublen; + } proto_item_set_len(item_udpcl, offset); } else { // Captured data but not part of item_udpcl - offset += dissect_bundle(tvb, pinfo, tree_udpcl); + const int sublen = dissect_bundle(tvb, pinfo, tree_udpcl); + if (sublen > 0) { + offset += sublen; + } + else { + break; + } proto_item_set_len(item_udpcl, 0); } } @@ -531,6 +889,14 @@ static void proto_register_udpcl(void) { table_ext = register_dissector_table("udpcl.ext", "UDPCL Extension", proto_udpcl, FT_UINT16, BASE_DEC); module_t *module_udpcl = prefs_register_protocol(proto_udpcl, udpcl_reinit); + prefs_register_bool_preference( + module_udpcl, + "analyze_sequence", + "Analyze message sequences", + "Whether the UDPCL dissector should analyze the sequencing of " + "the messages within each conversation.", + &udpcl_analyze_sequence + ); prefs_register_bool_preference( module_udpcl, "desegment_transfer", @@ -564,20 +930,32 @@ static void proto_reg_handoff_udpcl(void) { /* Packaged extensions */ { - dissector_handle_t dis_h = create_dissector_handle(dissect_transfer, proto_udpcl); - dissector_add_uint("udpcl.ext", UDPCL_EXT_TRANSFER, dis_h); + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_transfer, proto_udpcl, NULL, "Transfer"); + dissector_add_uint("udpcl.ext", 2, dis_h); } { - dissector_handle_t dis_h = create_dissector_handle(dissect_return_accept, proto_udpcl); - dissector_add_uint("udpcl.ext", UDPCL_EXT_RETURN_ACCEPT, dis_h); + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_sender_listen, proto_udpcl, NULL, "Sender Listen"); + dissector_add_uint("udpcl.ext", 3, dis_h); } { - dissector_handle_t dis_h = create_dissector_handle(dissect_nodeid, proto_udpcl); - dissector_add_uint("udpcl.ext", UDPCL_EXT_NODEID, dis_h); + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_nodeid, proto_udpcl, NULL, "Node ID"); + dissector_add_uint("udpcl.ext", 4, dis_h); } { - dissector_handle_t dis_h = create_dissector_handle(dissect_starttls, proto_udpcl); - dissector_add_uint("udpcl.ext", UDPCL_EXT_STARTTLS, dis_h); + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_starttls, proto_udpcl, NULL, "DTLS Initiation"); + dissector_add_uint("udpcl.ext", 5, dis_h); + } + { + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_peer_probe, proto_udpcl, NULL, "Peer Probe"); + dissector_add_uint("udpcl.ext", 6, dis_h); + } + { + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_peer_confirm, proto_udpcl, NULL, "Peer Confirmation"); + dissector_add_uint("udpcl.ext", 7, dis_h); + } + { + dissector_handle_t dis_h = create_dissector_handle_with_name_and_description(dissect_ecn_counts, proto_udpcl, NULL, "ECN Counts"); + dissector_add_uint("udpcl.ext", 8, dis_h); } udpcl_reinit(); @@ -585,6 +963,8 @@ static void proto_reg_handoff_udpcl(void) { #define PP_STRINGIZE_I(text) #text +/// Interface for wireshark plugin +WS_DLL_PUBLIC_DEF const char plugin_type[] = "epan_plugin"; /// Interface for wireshark plugin WS_DLL_PUBLIC_DEF const char plugin_version[] = "0.0"; /// Interface for wireshark plugin diff --git a/src/packet-udpcl.h b/src/packet-udpcl.h deleted file mode 100644 index 0009ad0..0000000 --- a/src/packet-udpcl.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WIRESHARK_PLUGIN_SRC_PACKET_UDPCL_H_ -#define WIRESHARK_PLUGIN_SRC_PACKET_UDPCL_H_ - -/// Keys in the extension map. -/// All are unsigned integers. -typedef enum { - UDPCL_EXT_RESERVED = 0, - /// Fragmented transfer - UDPCL_EXT_TRANSFER = 2, - /// Return-Path Accept - UDPCL_EXT_RETURN_ACCEPT = 3, - /// Sender Node ID - UDPCL_EXT_NODEID = 4, - /// Client-initiated DTLS - UDPCL_EXT_STARTTLS = 5, -} UdpclExtensionType; - -#endif /* WIRESHARK_PLUGIN_SRC_PACKET_UDPCL_H_ */ diff --git a/src/plugin_bpv7.c b/src/plugin_bpv7.c index 6895f1d..38e5a95 100644 --- a/src/plugin_bpv7.c +++ b/src/plugin_bpv7.c @@ -16,6 +16,8 @@ void proto_reg_handoff_bpsec_cose(void); #define PP_STRINGIZE_I(text) #text +/// Interface for wireshark plugin +WS_DLL_PUBLIC_DEF const char plugin_type[] = "epan_plugin"; /// Interface for wireshark plugin WS_DLL_PUBLIC_DEF const char plugin_version[] = "0.0"; /// Interface for wireshark plugin