diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols index 5190a60409..16c0cb1ba9 100644 --- a/debian/libwireshark0.symbols +++ b/debian/libwireshark0.symbols @@ -65,6 +65,24 @@ libwireshark.so.0 libwireshark0 #MINVER# bluetooth_uuid_vals@Base 1.99.2 bluetooth_uuid_vals_ext@Base 1.99.2 bluetooth_uuids@Base 2.1.0 + bp_block_canonical_new@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_block_primary_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_block_primary_new@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_ident_equal@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_ident_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_ident_hash@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_ident_new@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_bundle_new@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_creation_ts_compare@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_creation_ts_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_eid_equal@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_eid_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bp_eid_new@Base 3.5.1rc0-3265-gb0c69d7db316 + bpsec_id_equal@Base 3.5.1rc0-3265-gb0c69d7db316 + bpsec_id_free@Base 3.5.1rc0-3265-gb0c69d7db316 + bpsec_id_hash@Base 3.5.1rc0-3265-gb0c69d7db316 + bpsec_id_new@Base 3.5.1rc0-3265-gb0c69d7db316 bssgp_cause_vals_ext@Base 1.9.1 bthci_cmd_authentication_enable_values@Base 2.1.2 bthci_cmd_encrypt_mode_vals@Base 2.1.2 @@ -1226,6 +1244,7 @@ libwireshark.so.0 libwireshark0 #MINVER# proto_tree_add_cbor_bstr@Base 3.5.1 proto_tree_add_cbor_container@Base 3.5.1 proto_tree_add_cbor_ctrl@Base 3.5.1 + proto_tree_add_cbor_eid@Base 3.5.1rc0-3265-gb0c69d7db316 proto_tree_add_cbor_int64@Base 3.5.1 proto_tree_add_cbor_tstr@Base 3.5.1 proto_tree_add_cbor_uint64@Base 3.5.1 diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index a603b15956..9eae8cfc2e 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -120,6 +120,8 @@ Vector Informatik Binary Log File (BLF) [commaize] -- Bluetooth Link Manager Protocol (BT LMP) +Bundle Protocol version 7 (BPv7) +Bundle Protocol version 7 Security (BPSec) CBOR Object Signing and Encryption (COSE) E2 Application Protocol (E2AP) Event Tracing for Windows (ETW) diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index 2c51cf9ddf..0374df2315 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -252,6 +252,7 @@ set(DISSECTOR_PUBLIC_HEADERS packet-adb_service.h packet-afp.h packet-alcap.h + packet-amp.h packet-ansi_a.h packet-ansi_map.h packet-ansi_tcap.h @@ -267,6 +268,9 @@ set(DISSECTOR_PUBLIC_HEADERS packet-bgp.h packet-bicc_mst.h packet-bluetooth.h + packet-bpv6.h + packet-bpv7.h + packet-bpsec.h packet-bssap.h packet-bssgp.h packet-btatt.h @@ -330,7 +334,6 @@ set(DISSECTOR_PUBLIC_HEADERS packet-dop.h packet-dsp.h packet-dtls.h - packet-dtn.h packet-dvbci.h packet-e1ap.h packet-enip.h @@ -581,6 +584,7 @@ set(DISSECTOR_PUBLIC_HEADERS packet-tacacs.h packet-tcap.h packet-tcp.h + packet-tcpclv3.h packet-tetra.h packet-thrift.h packet-tls-utils.h @@ -694,6 +698,7 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-ansi_637.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-ansi_683.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-ansi_801.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-amp.h ${CMAKE_CURRENT_SOURCE_DIR}/packet-ansi_a.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-aodv.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-aoe.c @@ -764,6 +769,9 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-brcm-tag.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-brdwlk.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-brp.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-bpv6.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-bpv7.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-bpsec.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-bssap.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-bssgp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-bt-dht.c @@ -983,7 +991,6 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-dsr.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-dtcp-ip.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-dtls.c - ${CMAKE_CURRENT_SOURCE_DIR}/packet-dtn.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-dtp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-dtpt.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-dua.c @@ -1831,6 +1838,7 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-tapa.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcg-cp-oids.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcp.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcpclv3.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcpros.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-tdmoe.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-tdmop.c diff --git a/epan/dissectors/packet-amp.c b/epan/dissectors/packet-amp.c index 9fb07c714c..a8a22ec5db 100644 --- a/epan/dissectors/packet-amp.c +++ b/epan/dissectors/packet-amp.c @@ -14,6 +14,7 @@ #include #include +#include "packet-amp.h" /* The AMP standard can be found here: * https://tools.ietf.org/html/draft-birrane-dtn-amp-04 @@ -25,8 +26,6 @@ data-payload as AMP. Later in the future, when a dedicated field is given to this, this should be filled. */ -void dissect_amp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset); - /* */ static void diff --git a/epan/dissectors/packet-amp.h b/epan/dissectors/packet-amp.h new file mode 100644 index 0000000000..eef9d5eda7 --- /dev/null +++ b/epan/dissectors/packet-amp.h @@ -0,0 +1,42 @@ +/* packet-amp.h + * Routines for Asynchronous management Protocol dissection + * Copyright 2018, Krishnamurthy Mayya (krishnamurthymayya@gmail.com) + * Updated to CBOR encoding: Keith Scott, 2019 (kscott@mitre.org) + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef PACKET_AMP_H +#define PACKET_AMP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void dissect_amp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset); + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_AMP_H */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-bpsec.c b/epan/dissectors/packet-bpsec.c new file mode 100644 index 0000000000..049592a7a7 --- /dev/null +++ b/epan/dissectors/packet-bpsec.c @@ -0,0 +1,576 @@ +/* packet-sec.c + * Routines for Bundle Protocol Version 7 Security (BPSec) dissection + * References: + * BPSec: https://datatracker.ietf.org/doc/html/draft-ietf-dtn-bpsec-27 + * + * Copyright 2019-2021, Brian Sipos + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#include "packet-bpsec.h" +#include "packet-bpv7.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "epan/wscbor.h" + +/// Glib logging "domain" name +//static const char *LOG_DOMAIN = "bpsec"; + +/// Protocol handles +static int proto_bpsec = -1; + +/// Dissect opaque CBOR data +static dissector_handle_t handle_cbor = NULL; +/// Extension sub-dissectors +static dissector_table_t param_dissectors = NULL; +static dissector_table_t result_dissectors = NULL; + +static const val64_string shavar_vals[] = { + {5, "HMAC 256/256"}, + {6, "HMAC 384/384"}, + {7, "HMAC 512/512"}, + {0, NULL}, +}; + +static const val64_string aesvar_vals[] = { + {1, "A128GCM"}, + {3, "A256GCM"}, + {0, NULL}, +}; + +static int hf_bib = -1; +static int hf_bcb = -1; +static int hf_asb_target_list = -1; +static int hf_asb_target = -1; +static int hf_asb_ctxid = -1; +static int hf_asb_flags = -1; +static int hf_asb_flags_has_params = -1; +static int hf_asb_secsrc_nodeid = -1; +static int hf_asb_secsrc_uri = -1; +static int hf_asb_param_list = -1; +static int hf_asb_param_pair = -1; +static int hf_asb_param_id = -1; +static int hf_asb_result_all_list = -1; +static int hf_asb_result_tgt_list = -1; +static int hf_asb_result_tgt_ref = -1; +static int hf_asb_result_pair = -1; +static int hf_asb_result_id = -1; + +static int hf_defaultsc_shavar = -1; +static int hf_defaultsc_wrapedkey = -1; +static int hf_defaultsc_scope = -1; +static int hf_defaultsc_scope_pri_block = -1; +static int hf_defaultsc_scope_tgt_head = -1; +static int hf_defaultsc_scope_sec_head = -1; +static int hf_defaultsc_hmac = -1; +static int hf_defaultsc_iv = -1; +static int hf_defaultsc_aesvar = -1; +static int hf_defaultsc_authtag = -1; + +/// Field definitions +static hf_register_info fields[] = { + {&hf_bib, {"BPSec Block Integrity Block", "bpsec.bib", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_bcb, {"BPSec Block Confidentiality Block", "bpsec.bcb", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_target_list, {"Security Targets, Count", "bpsec.asb.target_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_target, {"Target Block Number", "bpsec.asb.target", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_ctxid, {"Context ID", "bpsec.asb.ctxid", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_flags, {"Flags", "bpsec.asb.flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_flags_has_params, {"Parameters Present", "bpsec.asb.flags.has_params", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BPSEC_ASB_HAS_PARAMS, NULL, HFILL}}, + {&hf_asb_secsrc_nodeid, {"Security Source", "bpsec.asb.secsrc.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_secsrc_uri, {"Security Source URI", "bpsec.asb.secsrc.uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_param_list, {"Security Parameters, Count", "bpsec.asb.param_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_param_pair, {"Parameter", "bpsec.asb.param", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_param_id, {"Type ID", "bpsec.asb.param.id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_result_all_list, {"Security Result Targets, Count", "bpsec.asb.result_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_result_tgt_ref, {"Associated Target Block Number", "bpsec.asb.result_tgt_ref", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_result_tgt_list, {"Security Results, Count", "bpsec.asb.result_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_result_pair, {"Result", "bpsec.asb.result", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_asb_result_id, {"Type ID", "bpsec.asb.result.id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + + {&hf_defaultsc_shavar, {"SHA Variant", "bpsec.defaultsc.shavar", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(shavar_vals), 0x0, NULL, HFILL}}, + {&hf_defaultsc_wrapedkey, {"Wrapped Key", "bpsec.defaultsc.wrappedkey", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_defaultsc_scope, {"BIB Scope", "bpsec.defaultsc.scope", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_defaultsc_scope_pri_block, {"Primary Block", "bpsec.defaultsc.scope.pri_block", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL}}, + {&hf_defaultsc_scope_tgt_head, {"Target Header", "bpsec.defaultsc.scope.tgt_head", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL}}, + {&hf_defaultsc_scope_sec_head, {"Security Header", "bpsec.defaultsc.scope.sec_head", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL}}, + {&hf_defaultsc_hmac, {"Expected HMAC", "bpsec.defaultsc.hmac", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_defaultsc_iv, {"IV", "bpsec.defaultsc.iv", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_defaultsc_aesvar, {"AES Variant", "bpsec.defaultsc.aesvar", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(aesvar_vals), 0x0, NULL, HFILL}}, + {&hf_defaultsc_authtag, {"Authentication Tag", "bpsec.defaultsc.authtag", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, +}; + +static int *const asb_flags[] = { + &hf_asb_flags_has_params, + NULL +}; + +static int *const defaultsc_scope[] = { + &hf_defaultsc_scope_pri_block, + &hf_defaultsc_scope_tgt_head, + &hf_defaultsc_scope_sec_head, + NULL +}; + +static int ett_asb = -1; +static int ett_asb_flags = -1; +static int ett_tgt_list = -1; +static int ett_param_list = -1; +static int ett_param_pair = -1; +static int ett_result_all_list = -1; +static int ett_result_tgt_list = -1; +static int ett_result_pair = -1; +static int ett_defaultsc_scope = -1; +/// Tree structures +static int *ett[] = { + &ett_asb, + &ett_asb_flags, + &ett_tgt_list, + &ett_param_list, + &ett_param_pair, + &ett_result_all_list, + &ett_result_tgt_list, + &ett_result_pair, + &ett_defaultsc_scope, +}; + +static expert_field ei_secsrc_diff = EI_INIT; +static expert_field ei_ctxid_zero = EI_INIT; +static expert_field ei_ctxid_priv = EI_INIT; +static expert_field ei_target_invalid = EI_INIT; +static expert_field ei_value_partial_decode = EI_INIT; +static ei_register_info expertitems[] = { + {&ei_secsrc_diff, {"bpsec.secsrc_diff", PI_SECURITY, PI_CHAT, "BPSec Security Source different from bundle Source", EXPFILL}}, + {&ei_ctxid_zero, {"bpsec.ctxid_zero", PI_SECURITY, PI_WARN, "BPSec Security Context ID zero is reserved", EXPFILL}}, + {&ei_ctxid_priv, {"bpsec.ctxid_priv", PI_SECURITY, PI_NOTE, "BPSec Security Context ID from private/experimental block", EXPFILL}}, + {&ei_target_invalid, {"bpsec.target_invalid", PI_PROTOCOL, PI_WARN, "Target block number not present", EXPFILL}}, + {&ei_value_partial_decode, {"bpsec.value_partial_decode", PI_UNDECODED, PI_WARN, "Value data not fully dissected", EXPFILL}}, +}; + +bpsec_id_t * bpsec_id_new(wmem_allocator_t *alloc, gint64 context_id, gint64 type_id) { + bpsec_id_t *obj; + if (alloc) { + obj = wmem_new(alloc, bpsec_id_t); + } + else { + obj = g_new(bpsec_id_t, 1); + } + obj->context_id = context_id; + obj->type_id = type_id; + return obj; +} + +void bpsec_id_free(wmem_allocator_t *alloc, gpointer ptr) { + //bpsec_id_t *obj = (bpsec_id_t *)ptr; + wmem_free(alloc, ptr); +} + +gboolean bpsec_id_equal(gconstpointer a, gconstpointer b) { + const bpsec_id_t *aobj = a; + const bpsec_id_t *bobj = b; + return ( + aobj && bobj + && (aobj->context_id == bobj->context_id) + && (aobj->type_id == bobj->type_id) + ); +} + +guint bpsec_id_hash(gconstpointer key) { + const bpsec_id_t *obj = key; + return ( + g_int64_hash(&(obj->context_id)) + ^ g_int64_hash(&(obj->type_id)) + ); +} + +/** Dissect an ID-value pair within a context. + * + * @param dissector + * @param typeid + * @param tvb + * @param pinfo + * @param tree + */ +static gint dissect_value(dissector_handle_t dissector, gint64 *typeid, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + gint sublen = 0; + if (dissector) { + sublen = call_dissector_with_data(dissector, tvb, pinfo, tree, typeid); + if ((sublen < 0) || ((guint)sublen < tvb_captured_length(tvb))) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_value_partial_decode); + } + } + if (sublen == 0) { + sublen = call_dissector(handle_cbor, tvb, pinfo, tree); + } + return sublen; +} + +/** Dissector for abstract security block structure. + */ +static int dissect_block_asb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const bp_dissector_data_t *const data, int root_hfindex) { + proto_item *item_asb = proto_tree_add_item(tree, root_hfindex, tvb, 0, -1, ENC_NA); + proto_tree *tree_asb = proto_item_add_subtree(item_asb, ett_asb); + gint offset = 0; + + wmem_array_t *targets; + targets = wmem_array_new(wmem_packet_scope(), sizeof(guint64)); + + wscbor_chunk_t *chunk_tgt_list = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array(chunk_tgt_list); + proto_item *item_tgt_list = proto_tree_add_cbor_container(tree_asb, hf_asb_target_list, pinfo, tvb, chunk_tgt_list); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_tgt_list)) { + proto_tree *tree_tgt_list = proto_item_add_subtree(item_tgt_list, ett_tgt_list); + + wscbor_chunk_t *chunk_tgt = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *tgt_blknum = wscbor_require_uint64(wmem_packet_scope(), chunk_tgt); + proto_item *item_tgt = proto_tree_add_cbor_uint64(tree_tgt_list, hf_asb_target, pinfo, tvb, chunk_tgt, tgt_blknum); + if (tgt_blknum) { + wmem_array_append(targets, tgt_blknum, 1); + + wmem_map_t *map = NULL; + if (*tgt_blknum == 0) { + map = (root_hfindex == hf_bib) + ? data->bundle->primary->sec.data_i + : data->bundle->primary->sec.data_c; + } + else { + bp_block_canonical_t *found = wmem_map_lookup(data->bundle->block_nums, tgt_blknum); + if (found) { + map = (root_hfindex == hf_bib) + ? found->sec.data_i + : found->sec.data_c; + } + else { + expert_add_info(pinfo, item_tgt, &ei_target_invalid); + } + } + if (map) { + wmem_map_insert( + map, + data->block->block_number, + NULL + ); + } + } + + proto_item_set_len(item_tgt_list, offset - chunk_tgt_list->start); + } + + wscbor_chunk_t *chunk_ctxid = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + gint64 *ctxid = wscbor_require_int64(wmem_packet_scope(), chunk_ctxid); + proto_item *item_ctxid = proto_tree_add_cbor_int64(tree_asb, hf_asb_ctxid, pinfo, tvb, chunk_ctxid, ctxid); + if (ctxid) { + if (*ctxid == 0) { + expert_add_info(pinfo, item_ctxid, &ei_ctxid_zero); + } + else if (*ctxid < 0) { + expert_add_info(pinfo, item_ctxid, &ei_ctxid_priv); + } + } + + 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_asb, hf_asb_flags, ett_asb_flags, asb_flags, pinfo, tvb, chunk_flags, flags); + + { + bp_eid_t *secsrc = bp_eid_new(wmem_packet_scope()); + proto_item *item_secsrc = proto_tree_add_cbor_eid(tree_asb, hf_asb_secsrc_nodeid, hf_asb_secsrc_uri, pinfo, tvb, &offset, secsrc); + if (!bp_eid_equal(data->bundle->primary->src_nodeid, secsrc)) { + expert_add_info(pinfo, item_secsrc, &ei_secsrc_diff); + } + } + + if (flags && (*flags & BPSEC_ASB_HAS_PARAMS)) { + wscbor_chunk_t *chunk_param_list = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array(chunk_param_list); + proto_item *item_param_list = proto_tree_add_cbor_container(tree_asb, hf_asb_param_list, pinfo, tvb, chunk_param_list); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_param_list)) { + proto_tree *tree_param_list = proto_item_add_subtree(item_param_list, ett_param_list); + + // iterate all parameters + for (guint64 param_ix = 0; param_ix < chunk_param_list->head_value; ++param_ix) { + wscbor_chunk_t *chunk_param_pair = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_param_pair, 2, 2); + proto_item *item_param_pair = proto_tree_add_cbor_container(tree_param_list, hf_asb_param_pair, pinfo, tvb, chunk_param_pair); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_param_pair)) { + proto_tree *tree_param_pair = proto_item_add_subtree(item_param_pair, ett_param_pair); + + wscbor_chunk_t *chunk_paramid = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + gint64 *paramid = wscbor_require_int64(wmem_packet_scope(), chunk_paramid); + proto_tree_add_cbor_int64(tree_param_pair, hf_asb_param_id, pinfo, tvb, chunk_paramid, paramid); + if (paramid) { + proto_item_append_text(item_param_pair, ", ID: %" PRIi64, *paramid); + } + + const gint offset_value = offset; + wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + tvbuff_t *tvb_value = tvb_new_subset_length(tvb, offset_value, offset - offset_value); + + dissector_handle_t value_dissect = NULL; + if (ctxid && paramid) { + bpsec_id_t *key = bpsec_id_new(wmem_packet_scope(), *ctxid, *paramid); + value_dissect = dissector_get_custom_table_handle(param_dissectors, key); + bpsec_id_free(wmem_packet_scope(), key); + } + dissect_value(value_dissect, paramid, tvb_value, pinfo, tree_param_pair); + + proto_item_set_len(item_param_pair, offset - chunk_param_pair->start); + } + } + + proto_item_set_len(item_param_list, offset - chunk_param_list->start); + } + } + + // array sizes should agree + const guint tgt_size = wmem_array_get_count(targets); + + wscbor_chunk_t *chunk_result_all_list = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_result_all_list, tgt_size, tgt_size); + proto_item *item_result_all_list = proto_tree_add_cbor_container(tree_asb, hf_asb_result_all_list, pinfo, tvb, chunk_result_all_list); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_result_all_list)) { + proto_tree *tree_result_all_list = proto_item_add_subtree(item_result_all_list, ett_result_all_list); + + // iterate each target's results + for (guint tgt_ix = 0; tgt_ix < tgt_size; ++tgt_ix) { + wscbor_chunk_t *chunk_result_tgt_list = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array(chunk_result_tgt_list); + proto_item *item_result_tgt_list = proto_tree_add_cbor_container(tree_result_all_list, hf_asb_result_tgt_list, pinfo, tvb, chunk_result_tgt_list); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_result_tgt_list)) { + proto_tree *tree_result_tgt_list = proto_item_add_subtree(item_result_tgt_list, ett_result_tgt_list); + + // Hint at the associated target number + if (tgt_ix < tgt_size) { + const guint64 *tgt_blknum = wmem_array_index(targets, tgt_ix); + proto_item *item_tgt_blknum = proto_tree_add_uint64(tree_result_tgt_list, hf_asb_result_tgt_ref, tvb, 0, 0, *tgt_blknum); + PROTO_ITEM_SET_GENERATED(item_tgt_blknum); + } + + // iterate all results for this target + for (guint64 result_ix = 0; result_ix < chunk_result_tgt_list->head_value; ++result_ix) { + wscbor_chunk_t *chunk_result_pair = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_result_pair, 2, 2); + proto_item *item_result_pair = proto_tree_add_cbor_container(tree_result_tgt_list, hf_asb_result_pair, pinfo, tvb, chunk_result_pair); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_result_pair)) { + proto_tree *tree_result_pair = proto_item_add_subtree(item_result_pair, ett_result_pair); + + wscbor_chunk_t *chunk_resultid = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + gint64 *resultid = wscbor_require_int64(wmem_packet_scope(), chunk_resultid); + proto_tree_add_cbor_int64(tree_result_pair, hf_asb_result_id, pinfo, tvb, chunk_resultid, resultid); + if (resultid) { + proto_item_append_text(item_result_pair, ", ID: %" PRIi64, *resultid); + } + + const gint offset_value = offset; + wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + tvbuff_t *tvb_value = tvb_new_subset_length(tvb, offset_value, offset - offset_value); + + dissector_handle_t value_dissect = NULL; + if (ctxid && resultid) { + bpsec_id_t *key = bpsec_id_new(wmem_packet_scope(), *ctxid, *resultid); + value_dissect = dissector_get_custom_table_handle(result_dissectors, key); + bpsec_id_free(wmem_packet_scope(), key); + } + dissect_value(value_dissect, resultid, tvb_value, pinfo, tree_result_pair); + + proto_item_set_len(item_result_pair, offset - chunk_result_pair->start); + } + } + + proto_item_set_len(item_result_tgt_list, offset - chunk_result_tgt_list->start); + } + } + + proto_item_set_len(item_result_all_list, offset - chunk_result_all_list->start); + } + + proto_item_set_len(item_asb, offset); + return offset; +} + +/** Dissector for Bundle Integrity block. + */ +static int dissect_block_bib(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + return dissect_block_asb(tvb, pinfo, tree, (bp_dissector_data_t *)data, hf_bib); +} + +/** Dissector for Bundle Confidentiality block. + */ +static int dissect_block_bcb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + return dissect_block_asb(tvb, pinfo, tree, (bp_dissector_data_t *)data, hf_bcb); +} + +static int dissect_defaultsc_param_shavar(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *val = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree, hf_defaultsc_shavar, pinfo, tvb, chunk, val); + return offset; +} + +static int dissect_defaultsc_param_wrappedkey(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_bstr(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bstr(tree, hf_defaultsc_wrapedkey, pinfo, tvb, chunk); + return offset; +} + +static int dissect_defaultsc_param_scope(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *flags = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bitmask(tree, hf_defaultsc_scope, ett_defaultsc_scope, defaultsc_scope, pinfo, tvb, chunk, flags); + return offset; +} + +static int dissect_defaultsc_result_hmac(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_bstr(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bstr(tree, hf_defaultsc_hmac, pinfo, tvb, chunk); + return offset; +} + +static int dissect_defaultsc_param_iv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_bstr(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bstr(tree, hf_defaultsc_iv, pinfo, tvb, chunk); + return offset; +} + +static int dissect_defaultsc_param_aesvar(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *val = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree, hf_defaultsc_aesvar, pinfo, tvb, chunk, val); + return offset; +} + +static int dissect_defaultsc_result_authtag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_bstr(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bstr(tree, hf_defaultsc_authtag, pinfo, tvb, chunk); + return offset; +} + +/// Re-initialize after a configuration change +static void reinit_bpsec(void) { +} + +/// Overall registration of the protocol +void proto_register_bpsec(void) { + proto_bpsec = proto_register_protocol( + "DTN Bundle Protocol Security", /* name */ + "BPSec", /* short name */ + "bpsec" /* abbrev */ + ); + + proto_register_field_array(proto_bpsec, fields, array_length(fields)); + proto_register_subtree_array(ett, array_length(ett)); + expert_module_t *expert = expert_register_protocol(proto_bpsec); + expert_register_field_array(expert, expertitems, array_length(expertitems)); + + param_dissectors = register_custom_dissector_table("bpsec.param", "BPSec Parameter", proto_bpsec, bpsec_id_hash, bpsec_id_equal); + result_dissectors = register_custom_dissector_table("bpsec.result", "BPSec Result", proto_bpsec, bpsec_id_hash, bpsec_id_equal); + + prefs_register_protocol(proto_bpsec, reinit_bpsec); +} + +void proto_reg_handoff_bpsec(void) { + handle_cbor = find_dissector("cbor"); + + /* Packaged extensions */ + { + guint64 *key = g_new(guint64, 1); + *key = 11; + dissector_handle_t hdl = create_dissector_handle(dissect_block_bib, proto_bpsec); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + { + guint64 *key = g_new(guint64, 1); + *key = 12; + dissector_handle_t hdl = create_dissector_handle(dissect_block_bcb, proto_bpsec); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + + // Context 1: BIB-HMAC-SHA2 + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 1; + key->type_id = 1; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_shavar, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 1; + key->type_id = 2; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_wrappedkey, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 1; + key->type_id = 3; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_scope, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 1; + key->type_id = 1; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_result_hmac, proto_bpsec); + dissector_add_custom_table_handle("bpsec.result", key, hdl); + } + // Context 2: BCB-AES-GCM + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 2; + key->type_id = 1; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_iv, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 2; + key->type_id = 2; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_aesvar, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 2; + key->type_id = 3; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_wrappedkey, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 2; + key->type_id = 4; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_param_scope, proto_bpsec); + dissector_add_custom_table_handle("bpsec.param", key, hdl); + } + { + bpsec_id_t *key = g_new(bpsec_id_t, 1); + key->context_id = 2; + key->type_id = 1; + dissector_handle_t hdl = create_dissector_handle(dissect_defaultsc_result_authtag, proto_bpsec); + dissector_add_custom_table_handle("bpsec.result", key, hdl); + } + + reinit_bpsec(); +} diff --git a/epan/dissectors/packet-bpsec.h b/epan/dissectors/packet-bpsec.h new file mode 100644 index 0000000000..dce2e5236a --- /dev/null +++ b/epan/dissectors/packet-bpsec.h @@ -0,0 +1,74 @@ +/* packet-bpv7.h + * Definitions for Bundle Protocol Version 7 Security (BPSec) dissection + * References: + * BPSec: https://datatracker.ietf.org/doc/html/draft-ietf-dtn-bpsec-27 + * + * Copyright 2019-2021, Brian Sipos + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef PACKET_BPSEC_H +#define PACKET_BPSEC_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * BPSec per-context parameter types and result types are registered with the + * dissector table "bpsec.param" and "bpsec.result" respectively. + * Both use bpsec_id_t* table keys, to identify both the context and the type + * code points. + */ + +/** Abstract Security Block Security Context Flags. + * Section 3.6. + */ +typedef enum { + /// Security Parameters present + BPSEC_ASB_HAS_PARAMS = 0x01, +} BpsecAsbFlag; + +/// Parameter/Result dissector lookup +typedef struct { + /// Security context ID + gint64 context_id; + /// Parameter/Result ID + gint64 type_id; +} bpsec_id_t; + +/** Construct a new ID. + */ +WS_DLL_PUBLIC +bpsec_id_t * bpsec_id_new(wmem_allocator_t *alloc, gint64 context_id, gint64 type_id); + +/** Function to match the GDestroyNotify signature. + */ +WS_DLL_PUBLIC +void bpsec_id_free(wmem_allocator_t *alloc, gpointer ptr); + +/** Function to match the GCompareFunc signature. + */ +WS_DLL_PUBLIC +gboolean bpsec_id_equal(gconstpointer a, gconstpointer b); + +/** Function to match the GHashFunc signature. + */ +WS_DLL_PUBLIC +guint bpsec_id_hash(gconstpointer key); + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_BPSEC_H */ diff --git a/epan/dissectors/packet-dtn.c b/epan/dissectors/packet-bpv6.c similarity index 80% rename from epan/dissectors/packet-dtn.c rename to epan/dissectors/packet-bpv6.c index 992cfc02ad..c3c1f76f06 100644 --- a/epan/dissectors/packet-dtn.c +++ b/epan/dissectors/packet-bpv6.c @@ -1,4 +1,7 @@ -/* packet-dtn.c +/* packet-bpv6.c + * References: + * RFC 5050: https://tools.ietf.org/html/rfc5050 + * * Copyright 2006-2007 The MITRE Corporation. * All Rights Reserved. * Approved for Public Release; Distribution Unlimited. @@ -41,8 +44,9 @@ #include #include #include -#include "packet-dtn.h" -#include "packet-tcp.h" +#include +#include "packet-bpv6.h" +#include "packet-cfdp.h" static int dissect_admin_record(proto_tree *primary_tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int payload_length, gboolean* success); @@ -53,59 +57,13 @@ dissect_amp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int static int add_sdnv_time_to_tree(proto_tree *tree, tvbuff_t *tvb, int offset, int hf_sdnv_time); -/* For Reassembling TCP Convergence Layer segments */ -static reassembly_table msg_reassembly_table; - -static const char magic[] = {'d', 't', 'n', '!'}; - static int proto_bundle = -1; -static int proto_tcp_conv = -1; +static dissector_handle_t bundle_handle = NULL; +static dissector_handle_t bpv6_handle = NULL; +static dissector_handle_t bpv7_handle = NULL; + static int hf_bundle_pdu_version = -1; -/* TCP Convergence Header Variables */ -static int hf_tcp_convergence_pkt_type = -1; - -/* Refuse-Bundle reason code */ -static int hf_dtn_refuse_bundle_reason_code = -1; - -static int hf_contact_hdr_version = -1; -static int hf_contact_hdr_flags = -1; -static int hf_contact_hdr_keep_alive = -1; -static int hf_contact_hdr_flags_ack_req = -1; -static int hf_contact_hdr_flags_frag_enable = -1; -static int hf_contact_hdr_flags_nak = -1; -static int hf_contact_hdr_magic = -1; -static int hf_contact_hdr_local_eid_length = -1; -static int hf_contact_hdr_local_eid = -1; - -/* TCP Convergence Data Header Variables */ -static int hf_tcp_convergence_data_procflags = -1; -static int hf_tcp_convergence_data_procflags_start = -1; -static int hf_tcp_convergence_data_procflags_end = -1; -static int hf_tcp_convergence_data_segment_length = -1; - -/* TCP Convergence Ack Variables */ -static int hf_tcp_convergence_ack_length = -1; - -/* TCP Convergence Shutdown Header Variables */ -static int hf_tcp_convergence_shutdown_flags = -1; -static int hf_tcp_convergence_shutdown_flags_reason = -1; -static int hf_tcp_convergence_shutdown_flags_delay = -1; -static int hf_tcp_convergence_shutdown_reason = -1; -static int hf_tcp_convergence_shutdown_delay = -1; - -/*TCP Convergence Layer Reassembly boilerplate*/ -static int hf_msg_fragments = -1; -static int hf_msg_fragment = -1; -static int hf_msg_fragment_overlap = -1; -static int hf_msg_fragment_overlap_conflicts = -1; -static int hf_msg_fragment_multiple_tails = -1; -static int hf_msg_fragment_too_long_fragment = -1; -static int hf_msg_fragment_error = -1; -static int hf_msg_fragment_count = -1; -static int hf_msg_reassembled_in = -1; -static int hf_msg_reassembled_length = -1; - /* Primary Header Processing Flag Variables */ static int hf_bundle_procflags = -1; static int hf_bundle_procflags_fragment = -1; @@ -267,8 +225,6 @@ static int hf_block_ciphersuite_range_length = -1; /* Tree Node Variables */ static gint ett_bundle = -1; -static gint ett_conv_flags = -1; -static gint ett_shutdown_flags = -1; static gint ett_bundle_hdr = -1; static gint ett_primary_hdr = -1; static gint ett_proc_flags = -1; @@ -279,17 +235,11 @@ static gint ett_dictionary = -1; static gint ett_payload_hdr = -1; static gint ett_payload_flags = -1; static gint ett_block_flags = -1; -static gint ett_contact_hdr_flags = -1; static gint ett_admin_record = -1; static gint ett_admin_rec_status = -1; static gint ett_metadata_hdr = -1; static gint ett_sec_block_param_data = -1; -static gint ett_tcp_conv = -1; -static gint ett_tcp_conv_hdr = -1; -static gint ett_msg_fragment = -1; -static gint ett_msg_fragments = -1; - static expert_field ei_bundle_payload_length = EI_INIT; static expert_field ei_bundle_control_flags_length = EI_INIT; static expert_field ei_bundle_block_control_flags = EI_INIT; @@ -299,14 +249,6 @@ static expert_field ei_bundle_offset_error = EI_INIT; static expert_field ei_block_control_block_cteb_invalid = EI_INIT; static expert_field ei_block_control_block_cteb_valid = EI_INIT; -static expert_field ei_tcp_convergence_data_flags = EI_INIT; -static expert_field ei_tcp_convergence_segment_length = EI_INIT; -static expert_field ei_tcp_convergence_ack_length = EI_INIT; - - -static dissector_handle_t bundle_handle; - -#define BUNDLE_PORT 4556 typedef struct dictionary_data { int bundle_header_dict_length; @@ -335,25 +277,6 @@ typedef struct dictionary_data { } dictionary_data_t; -static const value_string packet_type_vals[] = { - {((TCP_CONVERGENCE_DATA_SEGMENT>>4) & 0x0F), "Data"}, - {((TCP_CONVERGENCE_ACK_SEGMENT>>4) & 0x0F), "Ack"}, - {((TCP_CONVERGENCE_REFUSE_BUNDLE>>4) & 0x0F), "Refuse Bundle"}, - {((TCP_CONVERGENCE_KEEP_ALIVE>>4) & 0x0F), "Keep Alive"}, - {((TCP_CONVERGENCE_SHUTDOWN>>4) & 0x0F), "Shutdown"}, - {((TCP_CONVERGENCE_LENGTH>>4) & 0x0F), "Length"}, - {0, NULL} -}; - -/* Refuse-Bundle Reason-Code Flags as per RFC-7242: Section-5.4 */ -static const value_string refuse_bundle_reason_code[] = { - {TCP_REFUSE_BUNDLE_REASON_UNKNOWN, "Reason for refusal is unknown"}, - {TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE, "Complete Bundle Received"}, - {TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED, "Receiver's resources exhausted"}, - {TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT, "Receiver expects re-transmission of bundle"}, - {0, NULL} -}; - static const value_string admin_record_type_vals[] = { {ADMIN_REC_TYPE_STATUS_REPORT, "Bundle Status Report"}, {ADMIN_REC_TYPE_CUSTODY_SIGNAL, "Custody Signal"}, @@ -430,39 +353,6 @@ static const value_string res_params_types[] = { {0, NULL} }; -/* - * SDNV has a zero in high-order bit position of last byte. The high-order - * bit of all preceding bytes is set to one. This returns the numeric value - * in an integer and sets the value of the second argument to the number of - * bytes used to code the SDNV. A -1 is returned if the evaluation fails - * (value exceeds maximum for signed integer). 0 is an acceptable value. - */ - -#define SDNV_MASK 0x7f - -static const fragment_items msg_frag_items = { - /*Fragment subtrees*/ - &ett_msg_fragment, - &ett_msg_fragments, - /*Fragment Fields*/ - &hf_msg_fragments, - &hf_msg_fragment, - &hf_msg_fragment_overlap, - &hf_msg_fragment_overlap_conflicts, - &hf_msg_fragment_multiple_tails, - &hf_msg_fragment_too_long_fragment, - &hf_msg_fragment_error, - &hf_msg_fragment_count, - /*Reassembled in field*/ - &hf_msg_reassembled_in, - /*Reassembled length field*/ - &hf_msg_reassembled_length, - /* Reassembled data field */ - NULL, - /*Tag*/ - "Message fragments" -}; - /* * Adds the result of 2 SDNVs to tree: First SDNV is seconds, next is nanoseconds. * Returns bytes in both SDNVs or 0 if something goes wrong. @@ -1958,6 +1848,12 @@ evaluate_sdnv(tvbuff_t *tvb, int offset, int *bytecount) return value; } +int evaluate_sdnv_ei(tvbuff_t *tvb, int offset, int *bytecount, expert_field **error) { + int value = evaluate_sdnv(tvb, offset, bytecount); + *error = (value < 0) ? &ei_bundle_sdnv_length : NULL; + return value; +} + /* Special Function to evaluate 64 bit SDNVs */ /*3rd arg is number of bytes in field (returned)*/ gint64 @@ -2153,404 +2049,8 @@ evaluate_sdnv64(tvbuff_t *tvb, int offset, int *bytecount, guint64 *value) } -static guint -get_dtn_contact_header_len(packet_info *pinfo _U_, tvbuff_t *tvb, - int offset, void *data _U_) -{ - int len, bytecount; - - /* get length from sdnv */ - len = evaluate_sdnv(tvb, offset+8, &bytecount); - if (len < 0) - return 0; - - return len+bytecount+8; -} - static int -dissect_dtn_contact_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) -{ - proto_item *ti; - proto_tree *conv_proto_tree, *conv_tree, *conv_flag_tree; - int eid_length, sdnv_length; - int offset = 0; - - col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCL"); - col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */ - col_add_str(pinfo->cinfo, COL_INFO, "Contact Header"); - - ti = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA); - conv_proto_tree = proto_item_add_subtree(ti, ett_tcp_conv); - - conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, offset, -1, ett_tcp_conv, NULL, "Contact Header"); - - proto_tree_add_item(conv_tree, hf_contact_hdr_magic, tvb, offset, 4, ENC_NA|ENC_ASCII); - offset += 4; - proto_tree_add_item(conv_tree, hf_contact_hdr_version, tvb, offset, 1, ENC_BIG_ENDIAN); - offset++; - - /* Subtree to expand the bits in the Contact Header Flags */ - ti = proto_tree_add_item(conv_tree, hf_contact_hdr_flags, tvb, offset, 1, ENC_BIG_ENDIAN); - conv_flag_tree = proto_item_add_subtree(ti, ett_contact_hdr_flags); - proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_ack_req, tvb, offset, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_frag_enable, tvb, offset, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_nak, tvb, offset, 1, ENC_BIG_ENDIAN); - offset++; - - proto_tree_add_item(conv_tree, hf_contact_hdr_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN); - offset += 2; - - /* - * New format Contact header has length field followed by Bundle Header. - */ - eid_length = evaluate_sdnv(tvb, offset, &sdnv_length); - ti = proto_tree_add_int(tree, hf_contact_hdr_local_eid_length, tvb, offset, sdnv_length, eid_length); - if (eid_length < 0) { - expert_add_info(pinfo, ti, &ei_bundle_sdnv_length); - return offset; - } - - proto_tree_add_item(conv_tree, hf_contact_hdr_local_eid, tvb, sdnv_length + offset, eid_length, ENC_NA|ENC_ASCII); - return tvb_captured_length(tvb); -} - -static guint -get_tcpcl_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) -{ - int len, bytecount; - guint8 conv_hdr = tvb_get_guint8(tvb, offset); - - switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) - { - case TCP_CONVERGENCE_DATA_SEGMENT: - /* get length from sdnv */ - len = evaluate_sdnv(tvb, offset+1, &bytecount); - if (len < 0) - return 0; - - return len+bytecount+1; - - case TCP_CONVERGENCE_ACK_SEGMENT: - /* get length from sdnv */ - len = evaluate_sdnv(tvb, offset+1, &bytecount); - if (len < 0) - return 0; - - return bytecount+1; - - case TCP_CONVERGENCE_KEEP_ALIVE: - case TCP_CONVERGENCE_REFUSE_BUNDLE: - /* always 1 byte */ - return 1; - case TCP_CONVERGENCE_SHUTDOWN: - len = 1; - - if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) { - len += 1; - } - if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) { - len += 2; - } - - return len; - - case TCP_CONVERGENCE_LENGTH: - /* get length from sdnv */ - len = evaluate_sdnv(tvb, offset+1, &bytecount); - if (len < 0) - return 0; - return bytecount+1; - - } - - /* This probably isn't a TCPCL/Bundle packet, so just stop dissection */ - return -1; -} - -static int -dissect_tcpcl_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) -{ - guint8 conv_hdr; - guint8 refuse_bundle_hdr; - int offset = 0; - int sdnv_length, segment_length, convergence_hdr_size; - proto_item *ci, *sub_item; - proto_tree *conv_proto_tree, *conv_tree, *sub_tree; - fragment_head *frag_msg; - tvbuff_t *new_tvb; - gboolean more_frags; - int processed_length = 0; - const gchar* col_text; - gboolean bundle_in_col_info; - - static guint32 frag_id = 0; - static guint32 last_frame = 0; - static int last_raw_offset = 0; - - if (last_frame != pinfo->fd->num || tvb_raw_offset(tvb) < last_raw_offset) - frag_id = 0; - last_frame = pinfo->fd->num; - last_raw_offset = tvb_raw_offset(tvb); - - col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCL"); - col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */ - - col_text = col_get_text(pinfo->cinfo, COL_INFO); - bundle_in_col_info = (col_text && strstr(col_text, " > ")); - - ci = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA); - conv_proto_tree = proto_item_add_subtree(ci, ett_tcp_conv); - - conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, 0, -1, ett_tcp_conv_hdr, NULL, "TCP Convergence Header"); - - conv_hdr = tvb_get_guint8(tvb, offset); - proto_tree_add_item(conv_tree, hf_tcp_convergence_pkt_type, tvb, offset, 1, ENC_BIG_ENDIAN); - col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((conv_hdr>>4)&0xF, packet_type_vals, "Unknown")); - - switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) - { - case TCP_CONVERGENCE_DATA_SEGMENT: - sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_data_procflags, tvb, - offset, 1, ENC_BIG_ENDIAN); - sub_tree = proto_item_add_subtree(sub_item, ett_conv_flags); - proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_start, - tvb, offset, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_end, - tvb, offset, 1, ENC_BIG_ENDIAN); - - /* Only Start and End flags (bits 0 & 1) are valid in Data Segment */ - if ((conv_hdr & ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_DATA_FLAGS)) != 0) { - expert_add_info(pinfo, sub_item, &ei_tcp_convergence_data_flags); - } - - segment_length = evaluate_sdnv(tvb, 1, &sdnv_length); - sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_data_segment_length, tvb, 1, sdnv_length, segment_length); - if (segment_length < 0) { - expert_add_info(pinfo, sub_item, &ei_tcp_convergence_segment_length); - return 1; - } - - convergence_hdr_size = sdnv_length + 1; - - /* - * 1/11/2006 - If I got here, I should have a complete convergence layer - * "segment" beginning at frame_offset. However that might not be a - * complete bundle. Or there might be a complete bundle plus one or more - * additional convergence layer headers. - */ - - new_tvb = NULL; - sub_tree = NULL; - if ((conv_hdr & TCP_CONVERGENCE_DATA_END_FLAG) == TCP_CONVERGENCE_DATA_END_FLAG) { - more_frags = FALSE; - } - else { - more_frags = TRUE; - } - - frag_msg = fragment_add_seq_next(&msg_reassembly_table, - tvb, offset + convergence_hdr_size, - pinfo, frag_id, data, - segment_length, more_frags); - - if (!more_frags) ++frag_id; - - processed_length = convergence_hdr_size + segment_length; - - if (frag_msg && !more_frags) { - - int save_fd_head_layer = frag_msg->reas_in_layer_num; - frag_msg->reas_in_layer_num = pinfo->curr_layer_num; - - sub_item = proto_tree_add_item(tree, proto_bundle, tvb, offset, -1, ENC_NA); - sub_tree = proto_item_add_subtree(sub_item, ett_bundle); - - new_tvb = process_reassembled_data(tvb, offset + convergence_hdr_size, - pinfo, "Reassembled DTN", frag_msg, - &msg_frag_items, NULL, sub_tree); - - frag_msg->reas_in_layer_num = save_fd_head_layer; - } - - if (new_tvb) { - if (0 == call_dissector_with_data(bundle_handle, new_tvb, pinfo, sub_tree, data)) { - /*Couldn't parse bundle, treat as raw data */ - call_data_dissector(new_tvb, pinfo, sub_tree); - return tvb_captured_length(tvb); - } - } - else { - - /* - * If there are 2 segments, the second of which is very short, this - * gets displayed instead of the usual Source EID/Destination EID in - * the Bundle Dissection frame. If these statements are left out entirely, - * nothing is displayed, i.e., there seems to be no way to get the - * Source/Destination in the 2-segment case. I'll leave it in because I - * think it is informative in the multi-segment case although confusing in the - * 2-segment case. - */ - col_add_str(pinfo->cinfo, COL_INFO, "[Bundle TCPCL Segment]"); - } - break; - case TCP_CONVERGENCE_ACK_SEGMENT: - if (bundle_in_col_info) { - if (!strstr(col_text, ", TCPL ACK")) { - col_add_str(pinfo->cinfo, COL_INFO, ", TCPL ACK Segment(s)"); - } - } else { - col_set_str(pinfo->cinfo, COL_INFO, "TCPL ACK Segment(s)"); - } - segment_length = evaluate_sdnv(tvb, offset+1, &sdnv_length); - sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_ack_length, tvb, offset+1, sdnv_length, segment_length); - if (segment_length < 0) { - expert_add_info(pinfo, sub_item, &ei_tcp_convergence_ack_length); - processed_length = tvb_captured_length(tvb); - } else { - processed_length = sdnv_length + 1; - } - break; - case TCP_CONVERGENCE_KEEP_ALIVE: - if (bundle_in_col_info) { - if (!strstr(col_text, ", TCPL KEEPALIVE")) { - col_add_str(pinfo->cinfo, COL_INFO, ", TCPL KEEPALIVE Segment"); - } - } else { - col_set_str(pinfo->cinfo, COL_INFO, "TCPL KEEPALIVE Segment"); - } - /*No valid flags in Keep Alive*/ - processed_length = 1; - break; - - case TCP_CONVERGENCE_SHUTDOWN: - if (bundle_in_col_info) { - if (!strstr(col_text, ", TCPL SHUTDOWN")) { - col_add_str(pinfo->cinfo, COL_INFO, ", TCPL SHUTDOWN Segment"); - } - } else { - col_set_str(pinfo->cinfo, COL_INFO, "TCPL SHUTDOWN Segment"); - } - /* Add tree for Shutdown Flags */ - sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_shutdown_flags, tvb, - offset, 1, ENC_BIG_ENDIAN); - sub_tree = proto_item_add_subtree(sub_item, ett_shutdown_flags); - - proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_reason, - tvb, offset, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_delay, - tvb, offset, 1, ENC_BIG_ENDIAN); - - offset += 1; - if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) { - proto_tree_add_item(conv_tree, - hf_tcp_convergence_shutdown_reason, tvb, - offset, 1, ENC_BIG_ENDIAN); - offset += 1; - } - if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) { - proto_tree_add_item(conv_tree, - hf_tcp_convergence_shutdown_delay, tvb, - offset, 2, ENC_BIG_ENDIAN); - } - break; - case TCP_CONVERGENCE_REFUSE_BUNDLE: - if (bundle_in_col_info) { - if (!strstr(col_text, ", TCPL REFUSE")) { - col_add_str(pinfo->cinfo, COL_INFO, ", TCPL REFUSE_BUNDLE Segment"); - } - } else { - col_set_str(pinfo->cinfo, COL_INFO, "TCPL REFUSE_BUNDLE Segment"); - } - - refuse_bundle_hdr = tvb_get_guint8(tvb, offset); - proto_tree_add_item(conv_tree, hf_dtn_refuse_bundle_reason_code, tvb, offset, 1, ENC_BIG_ENDIAN); - col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((refuse_bundle_hdr>>4)&0xF, refuse_bundle_reason_code, "Unknown")); - - /*No valid flags*/ - processed_length = tvb_captured_length(tvb); - break; - } - - return processed_length; -} - -static int -dissect_tcpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) -{ - guint8 conv_hdr; - int offset, bytecount; - int processed_length; - - /* Make sure we have a convergence header byte */ - if (!tvb_bytes_exist(tvb, 0, 1)) - return 0; - - conv_hdr = tvb_get_guint8(tvb, 0); - switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) - { - case TCP_CONVERGENCE_DATA_SEGMENT: - case TCP_CONVERGENCE_ACK_SEGMENT: - /* ensure sdnv */ - offset = 1; - bytecount = 1; - - if (!tvb_bytes_exist(tvb, offset, 1)) { - pinfo->desegment_offset = 0; - pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; - return 0; - } - - while (tvb_get_guint8(tvb, offset) & ~SDNV_MASK) { - if (bytecount > (int)sizeof(int)) { - /* invalid length field */ - return 0; - } - - bytecount++; - offset++; - - if (!tvb_bytes_exist(tvb, offset, 1)) { - pinfo->desegment_offset = 0; - pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; - return 0; - } - } - break; - case TCP_CONVERGENCE_KEEP_ALIVE: - case TCP_CONVERGENCE_REFUSE_BUNDLE: - /* always 1 byte */ - break; - case TCP_CONVERGENCE_SHUTDOWN: - if ((conv_hdr & - ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_SHUTDOWN_FLAGS)) != 0) { - /* Not for us */ - return 0; - } - break; - default: - if (conv_hdr == (guint8)magic[0]) { - if (!tvb_bytes_exist(tvb, 0, 4) || tvb_memeql(tvb, 0, magic, 4)) { - /* Not for us */ - return 0; - } - - tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 8, get_dtn_contact_header_len, dissect_dtn_contact_header, data); - return tvb_captured_length(tvb); - } - - /* Not for us */ - return 0; - }; - - processed_length = get_tcpcl_pdu_len(pinfo, tvb, 0, data); - - tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 1, get_tcpcl_pdu_len, dissect_tcpcl_pdu, data); - - return processed_length; -} - -static int -dissect_bundle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +dissect_bpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { proto_item *ti, *ti_bundle_protocol; proto_tree *bundle_tree, *primary_tree; @@ -2620,12 +2120,40 @@ dissect_bundle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _ return(offset); } +/// Introspect the data to choose a dissector version +static int +dissect_bundle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + gint offset = 0; + + { + // Primary Header Version octet + guint8 version = tvb_get_guint8(tvb, offset); + if ((version == 4) || (version == 5) || (version == 6)) { + return call_dissector(bpv6_handle, tvb, pinfo, tree); + } + } + + wscbor_chunk_t *frame = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + if (frame->type_major == CBOR_TYPE_ARRAY) { + wscbor_chunk_t *primary = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + if (primary->type_major == CBOR_TYPE_ARRAY) { + wscbor_chunk_t *version = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + if (version->type_major == CBOR_TYPE_UINT) { + guint64 vers_val = version->head_value; + if (vers_val == 7) { + return call_dissector(bpv7_handle, tvb, pinfo, tree); + } + } + } + } + + return 0; +} -void proto_reg_handoff_bundle(void); -void proto_register_bundle(void); void -proto_register_bundle(void) +proto_register_bpv6(void) { static hf_register_info hf[] = { @@ -2633,47 +2161,6 @@ proto_register_bundle(void) {"Bundle Version", "bundle.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL} }, - {&hf_msg_fragments, - {"Message Fragments", "bundle.msg.fragments", - FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment, - {"Message Fragment", "bundle.msg.fragment", - FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_overlap, - {"Message fragment overlap", "bundle.msg.fragment.overlap", - FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_overlap_conflicts, - {"Message fragment overlapping with conflicting data", - "bundle.msg.fragment.overlap.conflicts", - FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_multiple_tails, - {"Message has multiple tails", "bundle.msg.fragment.multiple_tails", - FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_too_long_fragment, - {"Message fragment too long", "bundle.msg.fragment.too_long_fragment", - FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_error, - {"Message defragmentation error", "bundle.msg.fragment.error", - FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_fragment_count, - {"Message fragment count", "bundle.msg.fragment.count", - FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_reassembled_in, - {"Reassembled in", "bundle.msg.reassembled.in", - FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_msg_reassembled_length, - {"Reassembled DTN length", "bundle.msg.reassembled.length", - FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, {&hf_bundle_procflags, {"Primary Header Processing Flags", "bundle.primary.proc.flag", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} @@ -3192,93 +2679,6 @@ proto_register_bundle(void) }, }; - static hf_register_info hf_tcpcl[] = { - {&hf_tcp_convergence_pkt_type, - {"Pkt Type", "tcpcl.pkt_type", - FT_UINT8, BASE_DEC, VALS(packet_type_vals), 0xF0, NULL, HFILL} - }, - {&hf_dtn_refuse_bundle_reason_code, - {"Reason-Code", "tcpcl.refuse.reason_code", - FT_UINT8, BASE_DEC, VALS(refuse_bundle_reason_code), 0x0F, NULL, HFILL} - }, - {&hf_tcp_convergence_data_procflags, - {"TCP Convergence Data Flags", "tcpcl.data.proc.flag", - FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_DATA_FLAGS, NULL, HFILL} - }, - {&hf_tcp_convergence_data_procflags_start, - {"Segment contains start of bundle", "tcpcl.data.proc.start", - FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_START_FLAG, NULL, HFILL} - }, - {&hf_tcp_convergence_data_procflags_end, - {"Segment contains end of Bundle", "tcpcl.data.proc.end", - FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_END_FLAG, NULL, HFILL} - }, - {&hf_tcp_convergence_data_segment_length, - {"Segment Length", "tcpcl.data.length", - FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_tcp_convergence_shutdown_flags, - {"TCP Convergence Shutdown Flags", "tcpcl.shutdown.flags", - FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_SHUTDOWN_FLAGS, NULL, HFILL} - }, - {&hf_tcp_convergence_shutdown_flags_reason, - {"Shutdown includes Reason Code", "tcpcl.shutdown.reason.flag", - FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_REASON, NULL, HFILL} - }, - {&hf_tcp_convergence_shutdown_flags_delay, - {"Shutdown includes Reconnection Delay", "tcpcl.shutdown.delay.flag", - FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_DELAY, NULL, HFILL} - }, - {&hf_tcp_convergence_shutdown_reason, - {"Shutdown Reason Code", "tcpcl.shutdown.reason", - FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_tcp_convergence_shutdown_delay, - {"Shutdown Reconnection Delay", "tcpcl.shutdown.delay", - FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_tcp_convergence_ack_length, - {"Ack Length", "tcpcl.ack.length", - FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_version, - {"Version", "tcpcl.contact_hdr.version", - FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_flags, - {"Flags", "tcpcl.contact_hdr.flags", - FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_flags_ack_req, - {"Bundle Acks Requested", "tcpcl.contact_hdr.flags.ackreq", - FT_BOOLEAN, 8, NULL, TCP_CONV_BUNDLE_ACK_FLAG, NULL, HFILL} - }, - {&hf_contact_hdr_flags_frag_enable, - {"Reactive Fragmentation Enabled", "tcpcl.contact_hdr.flags.fragen", - FT_BOOLEAN, 8, NULL, TCP_CONV_REACTIVE_FRAG_FLAG, NULL, HFILL} - }, - {&hf_contact_hdr_flags_nak, - {"Support Negative Acknowledgements", "tcpcl.contact_hdr.flags.nak", - FT_BOOLEAN, 8, NULL, TCP_CONV_CONNECTOR_RCVR_FLAG, NULL, HFILL} - }, - {&hf_contact_hdr_keep_alive, - {"Keep Alive", "tcpcl.contact_hdr.keep_alive", - FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_magic, - {"Magic", "tcpcl.contact_hdr.magic", - FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_local_eid, - {"Local EID", "tcpcl.contact_hdr.local_eid", - FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} - }, - {&hf_contact_hdr_local_eid_length, - {"Local EID Length", "tcpcl.contact_hdr.local_eid_length", - FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} - }, - }; - static gint *ett[] = { &ett_bundle, &ett_bundle_hdr, @@ -3291,22 +2691,12 @@ proto_register_bundle(void) &ett_payload_hdr, &ett_payload_flags, &ett_block_flags, - &ett_contact_hdr_flags, - &ett_conv_flags, - &ett_shutdown_flags, &ett_admin_record, &ett_admin_rec_status, &ett_metadata_hdr, &ett_sec_block_param_data }; - static gint *ett_tcpcl[] = { - &ett_tcp_conv, - &ett_tcp_conv_hdr, - &ett_msg_fragment, - &ett_msg_fragments, - }; - static ei_register_info ei[] = { { &ei_bundle_control_flags_length, { "bundle.block.control.flags.length", PI_UNDECODED, PI_WARN, "Wrong bundle control flag length", EXPFILL } @@ -3334,47 +2724,23 @@ proto_register_bundle(void) }, }; - static ei_register_info ei_tcpcl[] = { - { &ei_tcp_convergence_data_flags, - { "tcpcl.data.flags.invalid", PI_PROTOCOL, PI_WARN, "Invalid TCP CL Data Segment Flags", EXPFILL } - }, - { &ei_tcp_convergence_segment_length, - { "tcpcl.data.length.invalid", PI_PROTOCOL, PI_ERROR, "Invalid Data Length", EXPFILL } - }, - { &ei_tcp_convergence_ack_length, - { "tcpcl.ack.length.error", PI_PROTOCOL, PI_WARN, "Ack Length: Error", EXPFILL } - }, - }; + expert_module_t *expert_bundle; - expert_module_t *expert_bundle, *expert_tcpcl; - - proto_bundle = proto_register_protocol("Bundle Protocol", "Bundle", "bundle"); + proto_bundle = proto_register_protocol("Bundle Protocol", "BP", "bundle"); + bpv6_handle = register_dissector("bpv6", dissect_bpv6, proto_bundle); bundle_handle = register_dissector("bundle", dissect_bundle, proto_bundle); - proto_tcp_conv = proto_register_protocol ("DTN TCP Convergence Layer Protocol", "TCPCL", "tcpcl"); - proto_register_field_array(proto_bundle, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); expert_bundle = expert_register_protocol(proto_bundle); expert_register_field_array(expert_bundle, ei, array_length(ei)); - - proto_register_field_array(proto_tcp_conv, hf_tcpcl, array_length(hf_tcpcl)); - proto_register_subtree_array(ett_tcpcl, array_length(ett_tcpcl)); - expert_tcpcl = expert_register_protocol(proto_tcp_conv); - expert_register_field_array(expert_tcpcl, ei_tcpcl, array_length(ei_tcpcl)); - - reassembly_table_register(&msg_reassembly_table, - &addresses_reassembly_table_functions); - } void -proto_reg_handoff_bundle(void) +proto_reg_handoff_bpv6(void) { - dissector_handle_t tcpcl_handle; + bpv7_handle = find_dissector("bpv7"); - tcpcl_handle = create_dissector_handle(dissect_tcpcl, proto_bundle); - dissector_add_uint_with_preference("tcp.port", BUNDLE_PORT, tcpcl_handle); dissector_add_uint_with_preference("udp.port", BUNDLE_PORT, bundle_handle); } diff --git a/epan/dissectors/packet-dtn.h b/epan/dissectors/packet-bpv6.h similarity index 71% rename from epan/dissectors/packet-dtn.h rename to epan/dissectors/packet-bpv6.h index 0eebd63679..03d2fbfa00 100644 --- a/epan/dissectors/packet-dtn.h +++ b/epan/dissectors/packet-bpv6.h @@ -1,4 +1,7 @@ -/* +/* packet-bpv6.h + * References: + * RFC 5050: https://tools.ietf.org/html/rfc5050 + * * Copyright 2006-2007 The MITRE Corporation. * All Rights Reserved. * Approved for Public Release; Distribution Unlimited. @@ -15,53 +18,18 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef PACKET_BPV6_H +#define PACKET_BPV6_H -/* TCP Convergence Layer - Message Types */ -#define TCP_CONV_MSG_TYPE_DATA 0x01 -#define TCP_CONV_MSG_TYPE_ACK 0x02 -#define TCP_CONV_MSG_TYPE_KEEP_ALIVE 0x03 -#define TCP_CONV_MSG_TYPE_SHUTDOWN 0x04 +#include +#include +#include -/* TCP Convergence Layer (3) - Message Types */ -#define TCP_CONVERGENCE_TYPE_MASK 0xf0 -#define TCP_CONVERGENCE_DATA_SEGMENT 0x10 -#define TCP_CONVERGENCE_ACK_SEGMENT 0x20 -#define TCP_CONVERGENCE_REFUSE_BUNDLE 0x30 -#define TCP_CONVERGENCE_KEEP_ALIVE 0x40 -#define TCP_CONVERGENCE_SHUTDOWN 0x50 -#define TCP_CONVERGENCE_LENGTH 0x60 - -/* TCP Convergence Layer - Contact Header Flags */ -#define TCP_CONV_BUNDLE_ACK_FLAG 0x01 -#define TCP_CONV_REACTIVE_FRAG_FLAG 0x02 -#define TCP_CONV_CONNECTOR_RCVR_FLAG 0x04 - -/* TCP Convergence Layer - Data Segment Flags */ -#define TCP_CONVERGENCE_DATA_FLAGS 0x03 -#define TCP_CONVERGENCE_DATA_END_FLAG 0x01 -#define TCP_CONVERGENCE_DATA_START_FLAG 0x02 - -/* TCP Convergence Layer - Shutdown Segment Flags */ -#define TCP_CONVERGENCE_SHUTDOWN_FLAGS 0x03 -#define TCP_CONVERGENCE_SHUTDOWN_REASON 0x02 -#define TCP_CONVERGENCE_SHUTDOWN_DELAY 0x01 - -/* REFUSE-BUNDLE Reason-Codes */ -#define TCP_REFUSE_BUNDLE_REASON_UNKNOWN 0x00 -#define TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE 0x01 -#define TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED 0x02 -#define TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT 0x03 -/* 0x4-0x7 - Unassigned - * 0x8-0xf - Reserved for future Use */ - -/* - * TCP Convergence Layer - Minimum buffer sizes - * For Data Packet require 5 bytes fixed plus - * up to 4 additional for length SDV - */ - -#define TCP_CONV_MIN_DATA_BUFFER 9 +#ifdef __cplusplus +extern "C" { +#endif +#define BUNDLE_PORT 4556 #define BUNDLE_PROCFLAGS_FRAG_MASK 0x01 #define BUNDLE_PROCFLAGS_ADMIN_MASK 0x02 @@ -106,12 +74,6 @@ #define PAYLOAD_PROCFLAGS_DISCARD_FAILURE 0x04 #define PAYLOAD_PROCFLAGS_LAST_HEADER 0x08 -/* Header Fixed Sizes */ -#define TCP_CONV_HDR_DATA_FIXED_LENGTH 5 -#define TCP_CONV_HDR_ACK_LENGTH 9 -#define TCP_CONV_HDR_KEEP_ALIVE_LENGTH 1 -#define TCP_CONV_HDR_SHUTDOWN_LENGTH 1 - /* Administrative Record Definitions */ #define ADMIN_REC_TYPE_STATUS_REPORT 0x01 #define ADMIN_REC_TYPE_CUSTODY_SIGNAL 0x02 @@ -150,7 +112,20 @@ #define DTN_SCHEME_STR "dtn" #define IPN_SCHEME_STR "ipn" +/* + * SDNV has a zero in high-order bit position of last byte. The high-order + * bit of all preceding bytes is set to one. This returns the numeric value + * in an integer and sets the value of the second argument to the number of + * bytes used to code the SDNV. A -1 is returned if the evaluation fails + * (value exceeds maximum for signed integer). 0 is an acceptable value. + */ +#define SDNV_MASK 0x7f + int evaluate_sdnv(tvbuff_t *tvb, int offset, int *bytecount); + +/// Return an error_info index if not valid +int evaluate_sdnv_ei(tvbuff_t *tvb, int offset, int *bytecount, expert_field **error); + gint64 evaluate_sdnv_64(tvbuff_t *tvb, int offset, int *bytecount); @@ -161,10 +136,14 @@ gint64 evaluate_sdnv_64(tvbuff_t *tvb, int offset, int *bytecount); * result is TRUE (1) on success else FALSE (0) */ int evaluate_sdnv32(tvbuff_t *tvb, int offset, int *bytecount, guint32 *value); + int evaluate_sdnv64(tvbuff_t *tvb, int offset, int *bytecount, guint64 *value); -void -dissect_cfdp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset); +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_AMP_H */ /* * Editor modelines - https://www.wireshark.org/tools/modelines.html diff --git a/epan/dissectors/packet-bpv7.c b/epan/dissectors/packet-bpv7.c new file mode 100644 index 0000000000..e50a8aee03 --- /dev/null +++ b/epan/dissectors/packet-bpv7.c @@ -0,0 +1,1993 @@ +/* packet-bpv7.c + * Routines for Bundle Protocol Version 7 dissection + * References: + * BPv7: https://datatracker.ietf.org/doc/html/draft-ietf-dtn-bpbis-31 + * + * Copyright 2019-2021, Brian Sipos + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#include "packet-bpv7.h" +#include "epan/wscbor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Glib logging "domain" name +static const char *LOG_DOMAIN = "bpv7"; +/// Protocol column name +static const char *const proto_name_bp = "BPv7"; +static const char *const proto_name_bp_admin = "BPv7 Admin"; + +/// Protocol preferences and defaults +static gboolean bp_compute_crc = TRUE; +static gboolean bp_reassemble_payload = TRUE; +static gboolean bp_payload_try_heur = FALSE; + +/// Protocol handles +static int proto_bp = -1; +static int proto_bp_admin = -1; +/// Protocol-level data +static bp_history_t *bp_history = NULL; + +/// Dissect opaque CBOR data +static dissector_handle_t handle_cbor = NULL; +/// Extension sub-dissectors +static dissector_table_t block_dissectors = NULL; +static dissector_table_t payload_dissectors_dtn_wkssp = NULL; +static dissector_table_t payload_dissectors_dtn_serv = NULL; +static dissector_table_t admin_dissectors = NULL; + +/// Fragment reassembly +static reassembly_table bp_reassembly_table; + +static const val64_string eid_schemes[] = { + {EID_SCHEME_DTN, "dtn"}, + {EID_SCHEME_IPN, "ipn"}, + {0, NULL}, +}; + +static const val64_string crc_vals[] = { + {BP_CRC_NONE, "None"}, + {BP_CRC_16, "CRC-16"}, + {BP_CRC_32, "CRC-32C"}, + {0, NULL}, +}; + +static const val64_string blocktype_vals[] = { + {BP_BLOCKTYPE_PAYLOAD, "Payload"}, + {BP_BLOCKTYPE_PREV_NODE, "Previous Node"}, + {BP_BLOCKTYPE_BUNDLE_AGE, "Bundle Age"}, + {BP_BLOCKTYPE_HOP_COUNT, "Hop Count"}, + {BP_BLOCKTYPE_BIB, "Block Integrity Block"}, + {BP_BLOCKTYPE_BCB, "Block Confidentiality Block"}, + {0, NULL}, +}; + +typedef struct { + /// Type of block + guint64 type_code; + /// Limit on total count + guint64 limit; +} blocktype_limit; +/// Block type count limits +static const blocktype_limit blocktype_limits[] = { + {BP_BLOCKTYPE_PAYLOAD, 1}, + {BP_BLOCKTYPE_PREV_NODE, 1}, + {BP_BLOCKTYPE_BUNDLE_AGE, 1}, + {BP_BLOCKTYPE_HOP_COUNT, 1}, + // Mandatory last row + {BP_BLOCKTYPE_INVALID, 0}, +}; + +static const val64_string admin_type_vals[] = { + {BP_ADMINTYPE_BUNDLE_STATUS, "Bundle Status Report"}, + {0, NULL}, +}; + +static const val64_string status_report_reason_vals[] = { + {0, "No additional information"}, + {1, "Lifetime expired"}, + {2, "Forwarded over unidirectional link"}, + {3, "Transmission canceled"}, + {4, "Depleted storage"}, + {5, "Destination endpoint ID unintelligible"}, + {6, "No known route to destination from here"}, + {7, "No timely contact with next node on route"}, + {8, "Block unintelligible"}, + {9, "Hop limit exceeded"}, + {10, "Traffic pared"}, + {11, "Block unsupported"}, + {12, "Missing Security Operation"}, + {13, "Unknown Security Operation"}, + {14, "Unexpected Security Operation"}, + {15, "Failed Security Operation"}, + {16, "Conflicting Security Operation"}, + {0, NULL}, +}; + +static int hf_bundle_head = -1; +static int hf_bundle_break = -1; +static int hf_block = -1; + +static int hf_crc_type = -1; +static int hf_crc_field_uint16 = -1; +static int hf_crc_field_uint32 = -1; +static int hf_crc_status = -1; + +static int hf_time_dtntime = -1; +static int hf_time_utctime = -1; + +static int hf_create_ts_time = -1; +static int hf_create_ts_seqno = -1; + +static int hf_eid_scheme = -1; +static int hf_eid_dtn_ssp_code = -1; +static int hf_eid_dtn_ssp_text = -1; +static int hf_eid_ipn_node = -1; +static int hf_eid_ipn_service = -1; +static int hf_eid_dtn_wkssp = -1; +static int hf_eid_dtn_serv = -1; + +static int hf_primary_version = -1; +static int hf_primary_bundle_flags = -1; +static int hf_primary_bundle_flags_deletion_report = -1; +static int hf_primary_bundle_flags_delivery_report = -1; +static int hf_primary_bundle_flags_forwarding_report = -1; +static int hf_primary_bundle_flags_reception_report = -1; +static int hf_primary_bundle_flags_req_status_time = -1; +static int hf_primary_bundle_flags_user_app_ack = -1; +static int hf_primary_bundle_flags_no_fragment = -1; +static int hf_primary_bundle_flags_payload_admin = -1; +static int hf_primary_bundle_flags_is_fragment = -1; +static int hf_primary_dst_eid = -1; +static int hf_primary_dst_uri = -1; +static int hf_primary_src_nodeid = -1; +static int hf_primary_src_uri = -1; +static int hf_primary_report_nodeid = -1; +static int hf_primary_report_uri = -1; +static int hf_primary_create_ts = -1; +static int hf_primary_lifetime = -1; +static int hf_primary_lifetime_exp = -1; +static int hf_primary_expire_ts = -1; +static int hf_primary_frag_offset = -1; +static int hf_primary_total_length = -1; + +static int hf_bundle_ident = -1; +static int hf_bundle_seen = -1; +static int hf_bundle_seen_time_diff = -1; +static int hf_bundle_status_ref = -1; + +static int hf_canonical_type_code = -1; +static int hf_canonical_block_num = -1; +static int hf_canonical_block_flags = -1; +static int hf_canonical_block_flags_delete_no_process = -1; +static int hf_canonical_block_flags_status_no_process = -1; +static int hf_canonical_block_flags_remove_no_process = -1; +static int hf_canonical_block_flags_replicate_in_fragment = -1; +static int hf_canonical_data = -1; + +static int hf_previous_node_nodeid = -1; +static int hf_previous_node_uri = -1; +static int hf_bundle_age_time = -1; +static int hf_hop_count_limit = -1; +static int hf_hop_count_current = -1; + +static int hf_admin_record_type = -1; +static int hf_status_rep = -1; +static int hf_status_rep_status_info = -1; +static int hf_status_assert_val = -1; +static int hf_status_assert_time = -1; +static int hf_status_rep_received = -1; +static int hf_status_rep_forwarded = -1; +static int hf_status_rep_delivered = -1; +static int hf_status_rep_deleted = -1; +static int hf_status_rep_reason_code = -1; +static int hf_status_rep_subj_src_nodeid = -1; +static int hf_status_rep_subj_src_uri = -1; +static int hf_status_rep_subj_ts = -1; +static int hf_status_rep_subj_frag_offset = -1; +static int hf_status_rep_subj_payload_len = -1; +static int hf_status_rep_subj_ident = -1; +static int hf_status_rep_subj_ref = -1; +static int hf_status_time_diff = -1; + +static int hf_payload_fragments = -1; +static int hf_payload_fragment = -1; +static int hf_payload_fragment_overlap = -1; +static int hf_payload_fragment_overlap_conflicts = -1; +static int hf_payload_fragment_multiple_tails = -1; +static int hf_payload_fragment_too_long_fragment = -1; +static int hf_payload_fragment_error = -1; +static int hf_payload_fragment_count = -1; +static int hf_payload_reassembled_in = -1; +static int hf_payload_reassembled_length = -1; +static int hf_payload_reassembled_data = -1; +static gint ett_payload_fragment = -1; +static gint ett_payload_fragments = -1; + +/// Field definitions +static hf_register_info fields[] = { + {&hf_bundle_head, {"Indefinite Array", "bpv7.bundle_head", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_bundle_break, {"Indefinite Break", "bpv7.bundle_break", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + + {&hf_block, {"Block", "bpv7.block", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + + {&hf_crc_type, {"CRC Type", "bpv7.crc_type", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(crc_vals), 0x0, NULL, HFILL}}, + {&hf_crc_field_uint16, {"CRC Field Integer", "bpv7.crc_field", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_crc_field_uint32, {"CRC field Integer", "bpv7.crc_field", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_crc_status, {"CRC Status", "bpv7.crc_status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL}}, + + {&hf_time_dtntime, {"DTN Time", "bpv7.time.dtntime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, + {&hf_time_utctime, {"UTC Time", "bpv7.time.utctime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL}}, + + {&hf_create_ts_time, {"Time", "bpv7.create_ts.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_create_ts_seqno, {"Sequence Number", "bpv7.create_ts.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_scheme, {"Scheme Code", "bpv7.eid.scheme", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(eid_schemes), 0x0, NULL, HFILL}}, + {&hf_eid_dtn_ssp_code, {"DTN SSP", "bpv7.eid.dtn_ssp_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_dtn_ssp_text, {"DTN SSP", "bpv7.eid.dtn_ssp_text", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_ipn_node, {"IPN Node Number", "bpv7.eid.ipn_node", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_ipn_service, {"IPN Service Number", "bpv7.eid.ipn_service", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_dtn_wkssp, {"Well-known SSP", "bpv7.eid.wkssp", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_eid_dtn_serv, {"Service Name", "bpv7.eid.serv", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + + {&hf_primary_version, {"Version", "bpv7.primary.version", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_bundle_flags, {"Bundle Flags", "bpv7.primary.bundle_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_bundle_flags_is_fragment, {"Bundle is a fragment", "bpv7.primary.bundle_flags.is_fragment", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_IS_FRAGMENT, NULL, HFILL}}, + {&hf_primary_bundle_flags_payload_admin, {"Payload is an administrative record", "bpv7.primary.bundle_flags.payload_admin", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_PAYLOAD_ADMIN, NULL, HFILL}}, + {&hf_primary_bundle_flags_no_fragment, {"Bundle must not be fragmented", "bpv7.primary.bundle_flags.no_fragment", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_NO_FRAGMENT, NULL, HFILL}}, + {&hf_primary_bundle_flags_user_app_ack, {"Acknowledgement by application is requested", "bpv7.primary.bundle_flags.user_app_ack", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_USER_APP_ACK, NULL, HFILL}}, + {&hf_primary_bundle_flags_req_status_time, {"Status time requested in reports", "bpv7.primary.bundle_flags.req_status_time", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_REQ_STATUS_TIME, NULL, HFILL}}, + {&hf_primary_bundle_flags_reception_report, {"Request reporting of bundle reception", "bpv7.primary.bundle_flags.reception_report", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_REQ_RECEPTION_REPORT, NULL, HFILL}}, + {&hf_primary_bundle_flags_forwarding_report, {"Request reporting of bundle forwarding", "bpv7.primary.bundle_flags.forwarding_report", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_REQ_FORWARDING_REPORT, NULL, HFILL}}, + {&hf_primary_bundle_flags_delivery_report, {"Request reporting of bundle delivery", "bpv7.primary.bundle_flags.delivery_report", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_REQ_DELIVERY_REPORT, NULL, HFILL}}, + {&hf_primary_bundle_flags_deletion_report, {"Request reporting of bundle deletion", "bpv7.primary.bundle_flags.deletion_report", FT_BOOLEAN, 24, TFS(&tfs_set_notset), BP_BUNDLE_REQ_DELETION_REPORT, NULL, HFILL}}, + {&hf_primary_dst_eid, {"Destination Endpoint ID", "bpv7.primary.dst_eid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_dst_uri, {"Destination URI", "bpv7.primary.dst_uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_src_nodeid, {"Source Node ID", "bpv7.primary.src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_src_uri, {"Source URI", "bpv7.primary.src_uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_report_nodeid, {"Report-to Node ID", "bpv7.primary.report_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_report_uri, {"Report-to URI", "bpv7.primary.report_uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_create_ts, {"Creation Timestamp", "bpv7.primary.create_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_lifetime, {"Lifetime", "bpv7.primary.lifetime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, + {&hf_primary_lifetime_exp, {"Lifetime Expanded", "bpv7.primary.lifetime_exp", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_expire_ts, {"Expire Time", "bpv7.primary.expire_time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL}}, + {&hf_primary_frag_offset, {"Fragment Offset", "bpv7.primary.frag_offset", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, + {&hf_primary_total_length, {"Total Application Data Unit Length", "bpv7.primary.total_len", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, + + {&hf_bundle_ident, {"Bundle Identity", "bpv7.bundle.identity", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_bundle_seen, {"First Seen", "bpv7.bundle.first_seen", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_PREV), 0x0, NULL, HFILL}}, + {&hf_bundle_seen_time_diff, {"Seen Time", "bpv7.bundle.seen_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_bundle_status_ref, {"Status Bundle", "bpv7.bundle.status_ref", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + + {&hf_canonical_type_code, {"Type Code", "bpv7.canonical.type_code", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(blocktype_vals), 0x0, NULL, HFILL}}, + {&hf_canonical_block_num, {"Block Number", "bpv7.canonical.block_num", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_canonical_block_flags, {"Block Flags", "bpv7.canonical.block_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}}, + {&hf_canonical_block_flags_replicate_in_fragment, {"Replicate block in fragment", "bpv7.canonical.block_flags.replicate_in_fragment", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BP_BLOCK_REPLICATE_IN_FRAGMENT, NULL, HFILL}}, + {&hf_canonical_block_flags_status_no_process, {"Status bundle if not processed", "bpv7.canonical.block_flags.status_if_no_process", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BP_BLOCK_STATUS_IF_NO_PROCESS, NULL, HFILL}}, + {&hf_canonical_block_flags_delete_no_process, {"Delete bundle if not processed", "bpv7.canonical.block_flags.delete_if_no_process", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BP_BLOCK_DELETE_IF_NO_PROCESS, NULL, HFILL}}, + {&hf_canonical_block_flags_remove_no_process, {"Discard block if not processed", "bpv7.canonical.block_flags.discard_if_no_process", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BP_BLOCK_REMOVE_IF_NO_PROCESS, NULL, HFILL}}, + {&hf_canonical_data, {"Block Type-Specific Data", "bpv7.canonical.data", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}}, + + {&hf_payload_fragments, + {"Payload fragments", "bpv7.payload.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment, + {"Payload fragment", "bpv7.payload.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_overlap, + {"Payload fragment overlap", "bpv7.payload.fragment.overlap", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_overlap_conflicts, + {"Payload fragment overlapping with conflicting data", + "bpv7.payload.fragment.overlap.conflicts", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_multiple_tails, + {"Message has multiple tail fragments", + "bpv7.payload.fragment.multiple_tails", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_too_long_fragment, + {"Payload fragment too long", "bpv7.payload.fragment.too_long_fragment", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_error, + {"Payload defragmentation error", "bpv7.payload.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_fragment_count, + {"Payload fragment count", "bpv7.payload.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_reassembled_in, + {"Reassembled in", "bpv7.payload.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_reassembled_length, + {"Reassembled length", "bpv7.payload.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_payload_reassembled_data, + {"Reassembled data", "bpv7.payload.reassembled.data", + FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + + {&hf_previous_node_nodeid, {"Previous Node ID", "bpv7.previous_node.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_previous_node_uri, {"Previous URI", "bpv7.previous_node.uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + + {&hf_bundle_age_time, {"Bundle Age", "bpv7.bundle_age.time", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL}}, + + {&hf_hop_count_limit, {"Hop Limit", "bpv7.hop_count.limit", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_hop_count_current, {"Hop Count", "bpv7.hop_count.current", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + + {&hf_admin_record_type, {"Record Type Code", "bpv7.admin_rec.type_code", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(admin_type_vals), 0x0, NULL, HFILL}}, + + {&hf_status_rep, {"Status Report", "bpv7.status_rep", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_status_info, {"Status Information", "bpv7.status_rep.status_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_assert_val, {"Status Value", "bpv7.status_assert.val", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_assert_time, {"Status at", "bpv7.status_assert.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_received, {"Reporting node received bundle", "bpv7.status_rep.received", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_forwarded, {"Reporting node forwarded bundle", "bpv7.status_rep.forwarded", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_delivered, {"Reporting node delivered bundle", "bpv7.status_rep.delivered", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_deleted, {"Reporting node deleted bundle", "bpv7.status_rep.deleted", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_reason_code, {"Reason Code", "bpv7.status_rep.reason_code", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(status_report_reason_vals), 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_src_nodeid, {"Subject Source Node ID", "bpv7.status_rep.subj_src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_src_uri, {"Subject Source URI", "bpv7.status_rep.subj_src_uri", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_ts, {"Subject Creation Timestamp", "bpv7.status_rep.subj_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_frag_offset, {"Subject Fragment Offset", "bpv7.status_rep.subj_frag_offset", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_payload_len, {"Subject Payload Length", "bpv7.status_rep.subj_payload_len", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_ident, {"Subject Identity", "bpv7.status_rep.identity", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}}, + {&hf_status_rep_subj_ref, {"Subject Bundle", "bpv7.status_rep.subj_ref", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL}}, + {&hf_status_time_diff, {"Status Time", "bpv7.status_rep.subj_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}}, +}; + +static int *const bundle_flags[] = { + &hf_primary_bundle_flags_deletion_report, + &hf_primary_bundle_flags_delivery_report, + &hf_primary_bundle_flags_forwarding_report, + &hf_primary_bundle_flags_reception_report, + &hf_primary_bundle_flags_req_status_time, + &hf_primary_bundle_flags_user_app_ack, + &hf_primary_bundle_flags_no_fragment, + &hf_primary_bundle_flags_payload_admin, + &hf_primary_bundle_flags_is_fragment, + NULL +}; + +static int *const block_flags[] = { + &hf_canonical_block_flags_remove_no_process, + &hf_canonical_block_flags_delete_no_process, + &hf_canonical_block_flags_status_no_process, + &hf_canonical_block_flags_replicate_in_fragment, + NULL +}; + +static int ett_bundle = -1; +static int ett_bundle_flags = -1; +static int ett_block = -1; +static int ett_eid = -1; +static int ett_time = -1; +static int ett_create_ts = -1; +static int ett_block_flags = -1; +static int ett_canonical_data = -1; +static int ett_payload = -1; +static int ett_admin = -1; +static int ett_status_rep = -1; +static int ett_status_info = -1; +static int ett_status_assert = -1; +/// Tree structures +static int *ett[] = { + &ett_bundle, + &ett_bundle_flags, + &ett_block, + &ett_eid, + &ett_time, + &ett_create_ts, + &ett_block_flags, + &ett_canonical_data, + &ett_payload, + &ett_admin, + &ett_status_rep, + &ett_status_info, + &ett_status_assert, + &ett_payload_fragment, + &ett_payload_fragments, +}; + +static const fragment_items payload_frag_items = { + /* Fragment subtrees */ + &ett_payload_fragment, + &ett_payload_fragments, + /* Fragment fields */ + &hf_payload_fragments, + &hf_payload_fragment, + &hf_payload_fragment_overlap, + &hf_payload_fragment_overlap_conflicts, + &hf_payload_fragment_multiple_tails, + &hf_payload_fragment_too_long_fragment, + &hf_payload_fragment_error, + &hf_payload_fragment_count, + /* Reassembled in field */ + &hf_payload_reassembled_in, + &hf_payload_reassembled_length, + &hf_payload_reassembled_data, + /* Tag */ + "Payload fragments" +}; + +static expert_field ei_invalid_framing = EI_INIT; +static expert_field ei_invalid_bp_version = EI_INIT; +static expert_field ei_eid_scheme_unknown = EI_INIT; +static expert_field ei_eid_ssp_type_invalid = EI_INIT; +static expert_field ei_eid_wkssp_unknown = EI_INIT; +static expert_field ei_block_type_dupe = EI_INIT; +static expert_field ei_sub_type_unknown = EI_INIT; +static expert_field ei_sub_partial_decode = EI_INIT; +static expert_field ei_crc_type_unknown = EI_INIT; +static expert_field ei_block_failed_crc = EI_INIT; +static expert_field ei_block_num_dupe = EI_INIT; +static expert_field ei_block_payload_index = EI_INIT; +static expert_field ei_block_payload_num = EI_INIT; +static expert_field ei_fragment_reassemble_size = EI_INIT; +static expert_field ei_fragment_tot_mismatch = EI_INIT; +static expert_field ei_block_sec_bib_tgt = EI_INIT; +static expert_field ei_block_sec_bcb_tgt = EI_INIT; +static ei_register_info expertitems[] = { + {&ei_invalid_framing, {"bpv7.invalid_framing", PI_MALFORMED, PI_WARN, "Invalid framing", EXPFILL}}, + {&ei_invalid_bp_version, {"bpv7.invalid_bp_version", PI_MALFORMED, PI_ERROR, "Invalid BP version", EXPFILL}}, + {&ei_eid_scheme_unknown, {"bpv7.eid_scheme_unknown", PI_UNDECODED, PI_WARN, "Unknown Node ID scheme code", EXPFILL}}, + {&ei_eid_ssp_type_invalid, {"bpv7.eid_ssp_type_invalid", PI_UNDECODED, PI_WARN, "Invalid scheme-specific part major type", EXPFILL}}, + {&ei_eid_wkssp_unknown, {"bpv7.eid_wkssp_unknown", PI_UNDECODED, PI_WARN, "Unknown well-known scheme-specific code point", EXPFILL}}, + {&ei_block_type_dupe, {"bpv7.block_type_dupe", PI_PROTOCOL, PI_WARN, "Too many blocks of this type", EXPFILL}}, + {&ei_sub_type_unknown, {"bpv7.sub_type_unknown", PI_UNDECODED, PI_WARN, "Unknown type code", EXPFILL}}, + {&ei_sub_partial_decode, {"bpv7.sub_partial_decode", PI_UNDECODED, PI_WARN, "Data not fully dissected", EXPFILL}}, + {&ei_crc_type_unknown, {"bpv7.crc_type_unknown", PI_UNDECODED, PI_WARN, "Unknown CRC Type code", EXPFILL}}, + {&ei_block_failed_crc, {"bpv7.block_failed_crc", PI_CHECKSUM, PI_WARN, "Block failed CRC", EXPFILL}}, + {&ei_block_num_dupe, {"bpv7.block_num_dupe", PI_PROTOCOL, PI_WARN, "Duplicate block number", EXPFILL}}, + {&ei_block_payload_index, {"bpv7.block_payload_index", PI_PROTOCOL, PI_WARN, "Payload must be the last block", EXPFILL}}, + {&ei_block_payload_num, {"bpv7.block_payload_num", PI_PROTOCOL, PI_WARN, "Invalid payload block number", EXPFILL}}, + {&ei_fragment_reassemble_size, {"bpv7.fragment_reassemble_size", PI_REASSEMBLE, PI_ERROR, "Cannot defragment this size (wireshark limitation)", EXPFILL}}, + {&ei_fragment_tot_mismatch, {"bpv7.fragment_tot_mismatch", PI_REASSEMBLE, PI_ERROR, "Inconsistent total length between fragments", EXPFILL}}, + {&ei_block_sec_bib_tgt, {"bpv7.bpsec.bib_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is an integrity target", EXPFILL}}, + {&ei_block_sec_bcb_tgt, {"bpv7.bpsec.bcb_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is a confidentiality target", EXPFILL}}, +}; + +/** Delete an arbitrary object allocated under this file scope. + * + * @param obj The object to delete. + */ +static void file_scope_delete(gpointer ptr) { + wmem_free(wmem_file_scope(), ptr); +} + +bp_creation_ts_t * bp_creation_ts_new(wmem_allocator_t *alloc) { + bp_creation_ts_t *obj = wmem_new0(alloc, bp_creation_ts_t); + return obj; +} + +void bp_creation_ts_free(wmem_allocator_t *alloc, bp_creation_ts_t *obj) { + // no sub-deletions + wmem_free(alloc, obj); +} + +gint bp_creation_ts_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_) { + const bp_creation_ts_t *ats = a; + const bp_creation_ts_t *bts = b; + if (ats->abstime.dtntime < bts->abstime.dtntime) { + return -1; + } + else if (ats->abstime.dtntime > bts->abstime.dtntime) { + return 1; + } + + if (ats->seqno < bts->seqno) { + return -1; + } + else if (ats->seqno > bts->seqno) { + return 1; + } + + return 0; +} + +bp_eid_t * bp_eid_new(wmem_allocator_t *alloc) { + bp_eid_t *obj = wmem_new0(alloc, bp_eid_t); + return obj; +} + +void bp_eid_free(wmem_allocator_t *alloc, bp_eid_t *obj) { + wmem_free(alloc, (char *)(obj->dtn_wkssp)); + wmem_free(alloc, (char *)(obj->dtn_serv)); + wmem_free(alloc, obj); +} + +gboolean bp_eid_equal(gconstpointer a, gconstpointer b) { + const bp_eid_t *aobj = a; + const bp_eid_t *bobj = b; + return aobj->uri && bobj->uri && g_str_equal(aobj->uri, bobj->uri); +} + +bp_block_primary_t * bp_block_primary_new(wmem_allocator_t *alloc) { + bp_block_primary_t *obj = wmem_new0(alloc, bp_block_primary_t); + obj->dst_eid = bp_eid_new(alloc); + obj->src_nodeid = bp_eid_new(alloc); + obj->rep_nodeid = bp_eid_new(alloc); + obj->frag_offset = NULL; + obj->total_len = NULL; + obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + return obj; +} + +void bp_block_primary_free(wmem_allocator_t *alloc, bp_block_primary_t *obj) { + if (!obj) { + return; + } + bp_eid_free(alloc, obj->dst_eid); + bp_eid_free(alloc, obj->src_nodeid); + bp_eid_free(alloc, obj->rep_nodeid); + wmem_free(alloc, obj->frag_offset); + wmem_free(alloc, obj->total_len); + wmem_free(alloc, obj->sec.data_i); + wmem_free(alloc, obj->sec.data_c); + wmem_free(alloc, obj); +} + +bp_block_canonical_t * bp_block_canonical_new(wmem_allocator_t *alloc, guint64 blk_ix) { + bp_block_canonical_t *obj = wmem_new0(alloc, bp_block_canonical_t); + obj->blk_ix = blk_ix; + obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + return obj; +} + +void bp_block_canonical_free(wmem_allocator_t *alloc, bp_block_canonical_t *obj) { + wmem_free(alloc, obj->sec.data_i); + wmem_free(alloc, obj->sec.data_c); + wmem_free(alloc, obj); +} + +static guint64 * guint64_new(wmem_allocator_t *alloc, const guint64 val) { + guint64 *obj = wmem_new(alloc, guint64); + *obj = val; + return obj; +} + +bp_bundle_t * bp_bundle_new(wmem_allocator_t *alloc) { + bp_bundle_t *obj = wmem_new0(alloc, bp_bundle_t); + obj->primary = bp_block_primary_new(alloc); + obj->blocks = wmem_list_new(alloc); + obj->block_nums = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + obj->block_types = wmem_map_new(alloc, g_int64_hash, g_int64_equal); + return obj; +} + +void bp_bundle_free(wmem_allocator_t *alloc, bp_bundle_t *obj) { + bp_bundle_ident_free(alloc, obj->ident); + bp_block_primary_free(alloc, obj->primary); + wmem_destroy_list(obj->blocks); + wmem_free(alloc, obj); +} + +bp_bundle_ident_t * bp_bundle_ident_new(wmem_allocator_t *alloc, bp_eid_t *src, bp_creation_ts_t *ts, guint64 *off, guint64 *len) { + bp_bundle_ident_t *ident = wmem_new(alloc, bp_bundle_ident_t); + ident->src = src ? wmem_strdup(alloc, src->uri) : NULL; + ident->ts = ts; + ident->frag_offset = off; + ident->total_len = len; + return ident; +} + +void bp_bundle_ident_free(wmem_allocator_t *alloc, bp_bundle_ident_t *obj) { + wmem_free(alloc, (char *)(obj->src)); + wmem_free(alloc, obj); +} + +/** Either both values are defined and equal or both are null. + */ +static gboolean optional_uint64_equal(const guint64 *a, const guint64 *b) { + if (a && b) { + return (*a == *b); + } + else { + return (a == NULL) && (b == NULL); + } +} + +gboolean bp_bundle_ident_equal(gconstpointer a, gconstpointer b) { + const bp_bundle_ident_t *aobj = a; + const bp_bundle_ident_t *bobj = b; + return ( + aobj->src && bobj->src && g_str_equal(aobj->src, bobj->src) + && (aobj->ts->abstime.dtntime == bobj->ts->abstime.dtntime) + && (aobj->ts->seqno == bobj->ts->seqno) + && optional_uint64_equal(aobj->frag_offset, bobj->frag_offset) + && optional_uint64_equal(aobj->total_len, bobj->total_len) + ); +} + +guint bp_bundle_ident_hash(gconstpointer key) { + const bp_bundle_ident_t *obj = key; + return ( + g_str_hash(obj->src ? obj->src : "") + ^ g_int64_hash(&(obj->ts->abstime.dtntime)) + ^ g_int64_hash(&(obj->ts->seqno)) + ); +} + +/** Convert DTN time to time delta. + * DTN Time is defined in Section 4.1.6. + * + * @param dtntime Number of milliseconds from an epoch. + * @return The associated absolute time. + */ +static nstime_t dtn_to_delta(const gint64 dtntime) { + nstime_t utctime; + utctime.secs = dtntime / 1000; + utctime.nsecs = 1000000 * (dtntime % 1000); + return utctime; +} + +/** Convert DTN time to absolute time. + * DTN Time is defined in Section 4.1.6. + * + * @param dtntime Number of milliseconds from an epoch. + * @return The associated absolute time. + */ +static nstime_t dtn_to_utctime(const gint64 dtntime) { + nstime_t utctime; + utctime.secs = 946684800 + dtntime / 1000; + utctime.nsecs = 1000000 * (dtntime % 1000); + return utctime; +} + +proto_item * proto_tree_add_cbor_eid(proto_tree *tree, int hfindex, int hfindex_uri, packet_info *pinfo, tvbuff_t *tvb, gint *offset, bp_eid_t *eid) { + wmem_allocator_t *alloc_eid = wmem_file_scope(); + proto_item *item_eid = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA); + proto_tree *tree_eid = proto_item_add_subtree(item_eid, ett_eid); + const gint eid_start = *offset; + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + wscbor_require_array_size(chunk, 2, 2); + if (!chunk) { + proto_item_set_len(item_eid, *offset - eid_start); + return item_eid; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + const guint64 *scheme = wscbor_require_uint64(alloc_eid, chunk); + proto_item *item_scheme = proto_tree_add_cbor_uint64(tree_eid, hf_eid_scheme, pinfo, tvb, chunk, scheme); + if (!scheme) { + wscbor_skip_next_item(wmem_packet_scope(), tvb, offset); + return item_eid; + } + + wmem_strbuf_t *uribuf = wmem_strbuf_new(alloc_eid, NULL); + const char *dtn_wkssp = NULL; + const char *dtn_serv = NULL; + switch (*scheme) { + case EID_SCHEME_DTN: { + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + switch (chunk->type_major) { + case CBOR_TYPE_UINT: { + const guint64 *ssp_code = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_item *item = proto_tree_add_cbor_uint64(tree_eid, hf_eid_dtn_ssp_code, pinfo, tvb, chunk, ssp_code); + + switch (*ssp_code) { + case 0: + dtn_wkssp = wmem_strdup(alloc_eid, "none"); + break; + default: + expert_add_info(pinfo, item, &ei_eid_wkssp_unknown); + break; + } + if (dtn_wkssp) { + wmem_strbuf_append_printf(uribuf, "dtn:%s", dtn_wkssp); + } + break; + } + case CBOR_TYPE_STRING: { + char *ssp = wscbor_require_tstr(wmem_packet_scope(), chunk); + proto_tree_add_cbor_tstr(tree_eid, hf_eid_dtn_ssp_text, pinfo, tvb, chunk); + wmem_strbuf_append_printf(uribuf, "dtn:%s", ssp); + + char *path_sep; + if ((path_sep = strrchr(ssp, '/')) != NULL) { + dtn_serv = wmem_strdup(alloc_eid, path_sep + 1); + } + else { + // no separator also means no authority part, so it's well-known + dtn_wkssp = wmem_strdup(alloc_eid, ssp); + } + + wmem_free(wmem_packet_scope(), ssp); + break; + } + default: { + *offset = chunk->start; + wscbor_skip_next_item(wmem_packet_scope(), tvb, offset); + tvbuff_t *sub_tvb = tvb_new_subset_length(tvb, chunk->start, *offset); + call_dissector(handle_cbor, sub_tvb, pinfo, tree_eid); + expert_add_info(pinfo, item_eid, &ei_eid_ssp_type_invalid); + break; + } + } + + break; + } + case EID_SCHEME_IPN: { + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + wscbor_require_array_size(chunk, 2, 2); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, offset, chunk)) { + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + const guint64 *node = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_eid, hf_eid_ipn_node, pinfo, tvb, chunk, node); + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + const guint64 *service = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_eid, hf_eid_ipn_service, pinfo, tvb, chunk, service); + + wmem_strbuf_append_printf(uribuf, "ipn:%"PRIu64".%"PRIu64, node ? *node : 0, service ? *service : 0); + } + break; + } + default: + wscbor_skip_next_item(wmem_packet_scope(), tvb, offset); + expert_add_info(pinfo, item_scheme, &ei_eid_scheme_unknown); + break; + } + + if (dtn_wkssp) { + proto_item *item = proto_tree_add_string(tree_eid, hf_eid_dtn_wkssp, tvb, eid_start, *offset - eid_start, dtn_wkssp); + PROTO_ITEM_SET_GENERATED(item); + } + if (dtn_serv) { + proto_item *item = proto_tree_add_string(tree_eid, hf_eid_dtn_serv, tvb, eid_start, *offset - eid_start, dtn_serv); + PROTO_ITEM_SET_GENERATED(item); + } + + char *uri = NULL; + if (wmem_strbuf_get_len(uribuf) > 0) { + uri = wmem_strbuf_finalize(uribuf); + + proto_item *item_uri = proto_tree_add_string(tree_eid, hfindex_uri, tvb, eid_start, *offset - eid_start, uri); + PROTO_ITEM_SET_GENERATED(item_uri); + + proto_item_append_text(item_eid, ": %s", uri); + } + + if (eid) { + eid->scheme = (scheme ? *scheme : 0); + eid->uri = uri; + eid->dtn_wkssp = dtn_wkssp; + eid->dtn_serv = dtn_serv; + } + else { + file_scope_delete(uri); + file_scope_delete((char *)dtn_wkssp); + file_scope_delete((char *)dtn_serv); + } + + proto_item_set_len(item_eid, *offset - eid_start); + return item_eid; +} + +static void proto_tree_add_dtn_time(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, gint *offset, bp_dtn_time_t *out) { + proto_item *item_time = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, 0); + proto_tree *tree_time = proto_item_add_subtree(item_time, ett_time); + const gint offset_start = *offset; + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + if (chunk) { + const guint64 *dtntime = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_time, hf_time_dtntime, pinfo, tvb, chunk, dtntime); + + if (dtntime) { + if (out) { + out->dtntime = *dtntime; + } + + if (*dtntime > 0) { + const nstime_t utctime = dtn_to_utctime(*dtntime); + proto_item *item_utctime = proto_tree_add_time(tree_time, hf_time_utctime, tvb, chunk->start, chunk->data_length, &utctime); + PROTO_ITEM_SET_GENERATED(item_utctime); + + gchar *time_text = abs_time_to_str(wmem_packet_scope(), &utctime, ABSOLUTE_TIME_UTC, TRUE); + proto_item_append_text(item_time, ": %s", time_text); + + if (out) { + out->utctime = utctime; + } + } + else { + proto_item_append_text(item_time, ": undefined"); + } + } + else if (out) { + out->dtntime = 0; + nstime_set_zero(&(out->utctime)); + } + } + proto_item_set_len(item_time, *offset - offset_start); +} + +/** Extract a timestamp. + * + * @param tree The tree to write items under. + * @param hfindex The root item field. + * @param pinfo Packet info to update. + * @param tvb Buffer to read from. + * @param[in,out] offset Starting offset within @c tvb. + * @param[out] ts If non-null, the timestamp to write to. + */ +static void proto_tree_add_cbor_timestamp(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, gint *offset, bp_creation_ts_t *ts) { + proto_item *item_ts = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, 0); + proto_tree *tree_ts = proto_item_add_subtree(item_ts, ett_create_ts); + + wscbor_chunk_t *chunk_ts = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + wscbor_require_array_size(chunk_ts, 2, 2); + wscbor_chunk_mark_errors(pinfo, item_ts, chunk_ts); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, offset, chunk_ts)) { + bp_dtn_time_t abstime; + proto_tree_add_dtn_time(tree_ts, hf_create_ts_time, pinfo, tvb, offset, &abstime); + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + const guint64 *seqno = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_uint64(tree_ts, hf_create_ts_seqno, pinfo, tvb, chunk, seqno); + + if (ts) { + ts->abstime = abstime; + ts->seqno = (seqno ? *seqno : 0); + } + } + proto_item_set_len(item_ts, *offset - chunk_ts->start); +} + +/** Show read-in and actual CRC information. + * + * @param tvb The single-block data. + * @param crc_type Type of CRC to compute. + * @param crc_field The read-in field value. + */ +static void show_crc_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block, const guint64 *crc_type, tvbuff_t *crc_field) { + if (!crc_type || !crc_field) { + return; + } + + // Display the data field information + int hf_crc_field; + switch (*crc_type) { + case BP_CRC_16: + hf_crc_field = hf_crc_field_uint16; + break; + case BP_CRC_32: + hf_crc_field = hf_crc_field_uint32; + break; + default: + hf_crc_field = -1; + break; + } + + // Compare against expected result + guint32 crc_actual = 0; + guint chksum_flags = PROTO_CHECKSUM_NO_FLAGS; + if (bp_compute_crc) { + if (*crc_type == BP_CRC_NONE) { + chksum_flags |= PROTO_CHECKSUM_NOT_PRESENT; + } + else { + const guint block_len = tvb_captured_length(tvb); + guint8 *crcbuf = tvb_memdup(pinfo->pool, tvb, 0, block_len); + switch (*crc_type) { + case BP_CRC_16: + memset(crcbuf + block_len - 2, 0, 2); + crc_actual = crc16_ccitt(crcbuf, block_len); + break; + case BP_CRC_32: + memset(crcbuf + block_len - 4, 0, 4); + crc_actual = ~crc32c_calculate_no_swap(crcbuf, block_len, CRC32C_PRELOAD); + break; + default: + break; + } + wmem_free(pinfo->pool, crcbuf); + + chksum_flags |= PROTO_CHECKSUM_VERIFY; + } + } + proto_tree_add_checksum(tree_block, crc_field, 0, hf_crc_field, hf_crc_status, &ei_block_failed_crc, pinfo, crc_actual, ENC_BIG_ENDIAN, chksum_flags); +} + +static void proto_tree_add_ident(proto_tree *tree, int hfindex, tvbuff_t *tvb, const bp_bundle_ident_t *ident) { + wmem_strbuf_t *ident_text = wmem_strbuf_new(wmem_packet_scope(), NULL); + wmem_strbuf_append_printf( + ident_text, + "Source: %s, DTN Time: %" PRIu64 ", Seq: %" PRIu64, + ident->src, + ident->ts->abstime.dtntime, + ident->ts->seqno + ); + if (ident->frag_offset) { + wmem_strbuf_append_printf(ident_text, ", Frag Offset: %" PRIu64, *(ident->frag_offset)); + } + if (ident->total_len) { + wmem_strbuf_append_printf(ident_text, ", Total Length: %" PRIu64, *(ident->total_len)); + } + + proto_item *item_subj_ident = proto_tree_add_string(tree, hfindex, tvb, 0, 0, wmem_strbuf_get_str(ident_text)); + PROTO_ITEM_SET_GENERATED(item_subj_ident); + wmem_strbuf_finalize(ident_text); +} + + +static gint dissect_block_primary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block, + gint start, bp_block_primary_t *block, + bp_bundle_t *bundle _U_) { + proto_item *item_block = proto_tree_get_parent(tree_block); + gint field_ix = 0; + gint offset = start; + block->item_block = item_block; + + wscbor_chunk_t *chunk_block = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_block, 8, 11); + wscbor_chunk_mark_errors(pinfo, item_block, chunk_block); + if (wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_block)) { + return offset - start; + } +#if 0 + proto_item_append_text(item_block, ", Items: %" PRIu64, chunk_block->head_value); +#endif + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *version = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_item *item_version = proto_tree_add_cbor_uint64(tree_block, hf_primary_version, pinfo, tvb, chunk, version); + field_ix++; + if (version && (*version != 7)) { + expert_add_info(pinfo, item_version, &ei_invalid_bp_version); + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *flags = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_bitmask(tree_block, hf_primary_bundle_flags, ett_bundle_flags, bundle_flags, pinfo, tvb, chunk, flags); + field_ix++; + block->flags = (flags ? *flags : 0); + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *crc_type = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type); + field_ix++; + block->crc_type = (crc_type ? (BundleCrcType)(*crc_type) : BP_CRC_NONE); + if (crc_type) { + proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str(*crc_type, crc_vals, "%" PRIu64)); + } + + proto_tree_add_cbor_eid(tree_block, hf_primary_dst_eid, hf_primary_dst_uri, pinfo, tvb, &offset, block->dst_eid); + field_ix++; + + proto_tree_add_cbor_eid(tree_block, hf_primary_src_nodeid, hf_primary_src_uri, pinfo, tvb, &offset, block->src_nodeid); + field_ix++; + + proto_tree_add_cbor_eid(tree_block, hf_primary_report_nodeid, hf_primary_report_uri, pinfo, tvb, &offset, block->rep_nodeid); + field_ix++; + + // Complex type + proto_tree_add_cbor_timestamp(tree_block, hf_primary_create_ts, pinfo, tvb, &offset, &(block->ts)); + field_ix++; + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *lifetime = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_block, hf_primary_lifetime, pinfo, tvb, chunk, lifetime); + if (lifetime) { + nstime_t lifetime_exp = dtn_to_delta(*lifetime); + proto_item *item_lifetime_exp = proto_tree_add_time(tree_block, hf_primary_lifetime_exp, tvb, chunk->start, chunk->head_length, &lifetime_exp); + PROTO_ITEM_SET_GENERATED(item_lifetime_exp); + + if (block->ts.abstime.dtntime > 0) { + nstime_t expiretime; + nstime_sum(&expiretime, &(block->ts.abstime.utctime), &lifetime_exp); + proto_item *item_expiretime = proto_tree_add_time(tree_block, hf_primary_expire_ts, tvb, 0, 0, &expiretime); + PROTO_ITEM_SET_GENERATED(item_expiretime); + } + } + field_ix++; + + // optional items + if (flags && (*flags & BP_BUNDLE_IS_FRAGMENT)) { + if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 3)) { + // Skip whole array + offset = start; + wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + + return offset - start; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + block->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_uint64(tree_block, hf_primary_frag_offset, pinfo, tvb, chunk, block->frag_offset); + field_ix++; + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + block->total_len = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_uint64(tree_block, hf_primary_total_length, pinfo, tvb, chunk, block->total_len); + field_ix++; + } + + switch (block->crc_type) { + case BP_CRC_NONE: + break; + case BP_CRC_16: + case BP_CRC_32: { + if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) { + // Skip whole array + offset = start; + wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + + return offset - start; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk); + field_ix++; + block->crc_field = crc_field; + + tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start); + show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field); + break; + } + default: + expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown); + break; + } + + return offset - start; +} + +static gint dissect_block_canonical(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block, + gint start, bp_block_canonical_t *block, + bp_bundle_t *bundle _U_) { + proto_item *item_block = proto_tree_get_parent(tree_block); + gint field_ix = 0; + gint offset = start; + block->item_block = item_block; + + wscbor_chunk_t *chunk_block = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_block, 5, 6); + wscbor_chunk_mark_errors(pinfo, item_block, chunk_block); + if (wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_block)) { + return offset - start; + } +#if 0 + proto_item_append_text(item_block, ", Items: %" PRIu64, chunk_block->head_value); +#endif + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *type_code = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_item *item_type = proto_tree_add_cbor_uint64(tree_block, hf_canonical_type_code, pinfo, tvb, chunk, type_code); + field_ix++; + block->type_code = type_code; + + if (type_code) { + proto_item_append_text(item_block, ": %s", val64_to_str(*type_code, blocktype_vals, "Type %" PRIu64)); + + // Check duplicate of this type + guint64 limit = UINT64_MAX; + for (int ix = 0; ; ++ix) { + const blocktype_limit *row = blocktype_limits + ix; + if (row->type_code == BP_BLOCKTYPE_INVALID) { + break; + } + if (row->type_code == *type_code) { + limit = row->limit; + break; + } + } + + guint64 count = 1; // this block counts regardless of presence in the map + wmem_list_t *list_found = wmem_map_lookup(bundle->block_types, type_code); + if (list_found) { + for (wmem_list_frame_t *it = wmem_list_head(list_found); it; + it = wmem_list_frame_next(it)) { + bp_block_canonical_t *block_found = wmem_list_frame_data(it); + if (block == block_found) { + continue; + } + ++count; + } + } + if (count > limit) { + // First non-identical block triggers the error + expert_add_info(pinfo, item_type, &ei_block_type_dupe); + } + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *block_num = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_item *item_block_num = proto_tree_add_cbor_uint64(tree_block, hf_canonical_block_num, pinfo, tvb, chunk, block_num); + field_ix++; + block->block_number = block_num; + if (block_num) { + proto_item_append_text(item_block, ", Block Num: %" PRIu64, *block_num); + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *flags = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_bitmask(tree_block, hf_canonical_block_flags, ett_block_flags, block_flags, pinfo, tvb, chunk, flags); + field_ix++; + block->flags = (flags ? *flags : 0); + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *crc_type = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type); + field_ix++; + block->crc_type = (crc_type ? (BundleCrcType)(*crc_type) : BP_CRC_NONE); + if (crc_type) { + proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str(*crc_type, crc_vals, "%" PRIu64)); + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + tvbuff_t *tvb_data = wscbor_require_bstr(wmem_file_scope(), chunk); + field_ix++; + block->data = tvb_data; + + const guint tvb_data_len = (tvb_data ? tvb_captured_length(tvb_data) : 0); + proto_item *item_data = proto_tree_add_uint64(tree_block, hf_canonical_data, tvb_data, 0, tvb_data_len, tvb_data_len); + proto_tree *tree_data = proto_item_add_subtree(item_data, ett_canonical_data); + block->tree_data = tree_data; + + switch (block->crc_type) { + case BP_CRC_NONE: + break; + case BP_CRC_16: + case BP_CRC_32: { + if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) { + // Skip whole array + offset = start; + wscbor_skip_next_item(wmem_packet_scope(), tvb, &offset); + + return offset - start; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk); + field_ix++; + block->crc_field = crc_field; + + tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start); + show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field); + break; + } + default: + expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown); + break; + } + + wmem_list_append(bundle->blocks, block); + + if (block->type_code) { + wmem_list_t *type_list = wmem_map_lookup(bundle->block_types, block->type_code); + if (!type_list) { + guint64 *key = guint64_new(wmem_file_scope(), *(block->type_code)); + type_list = wmem_list_new(wmem_file_scope()); + wmem_map_insert(bundle->block_types, key, type_list); + } + wmem_list_append(type_list, block); + } + if (block->block_number) { + bp_block_canonical_t *found = wmem_map_lookup(bundle->block_nums, block->block_number); + if (found) { + expert_add_info(pinfo, item_block_num, &ei_block_num_dupe); + } + else { + guint64 *key = guint64_new(wmem_file_scope(), *(block->block_number)); + wmem_map_insert(bundle->block_nums, key, block); + } + } + // Payload block requirements + if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) { + // must have index zero + if (block->block_number && (*(block->block_number) != 1)) { + expert_add_info(pinfo, item_block_num, &ei_block_payload_num); + } + } + + return offset - start; +} + +typedef struct { + packet_info *pinfo; + proto_item *pi; + expert_field *eiindex; + const char *sectype; +} bpsec_block_mark_t; +/// Mark blocks with BPSec expert info +static void mark_target_block(gpointer key, gpointer value _U_, gpointer user_data) { + const guint64 *blk_num = (guint64 *)key; + const bpsec_block_mark_t *mark = (bpsec_block_mark_t *)user_data; + expert_add_info_format( + mark->pinfo, mark->pi, mark->eiindex, + "Block is targed by %s block number %" PRIu64, mark->sectype, *blk_num + ); +} +static void apply_bpsec_mark(const security_mark_t *sec, packet_info *pinfo, proto_item *pi) { + { + bpsec_block_mark_t mark; + mark.pinfo = pinfo; + mark.pi = pi; + mark.eiindex = &ei_block_sec_bib_tgt; + mark.sectype = "BIB"; + wmem_map_foreach(sec->data_i, mark_target_block, &mark); + } + { + bpsec_block_mark_t mark; + mark.pinfo = pinfo; + mark.pi = pi; + mark.eiindex = &ei_block_sec_bcb_tgt; + mark.sectype = "BCB"; + wmem_map_foreach(sec->data_c, mark_target_block, &mark); + } +} + +/** Extract data from a block (including payload and admin). + * + * @param dissector The optional dissector to call. + * @param context Context for the @c dissector. + * @param tvb Buffer to read from. + * @param pinfo Packet info to update. + * @param tree The tree to write items under. + * @param payload True if this is bundle payload. + * @return The number of dissected octets. + */ +static gint dissect_carried_data(dissector_handle_t dissector, void *context, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean payload) { + int sublen = 0; + if (dissector) { + sublen = call_dissector_with_data(dissector, tvb, pinfo, tree, context); + if ((sublen < 0) || ((guint)sublen < tvb_captured_length(tvb))) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_partial_decode); + } + } + else { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_type_unknown); + } + + if ((sublen == 0) && bp_payload_try_heur) { + if (payload) { + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Assumed CBOR"); + } + TRY { + sublen = call_dissector(handle_cbor, tvb, pinfo, tree); + } + CATCH_ALL {} + ENDTRY; + } + if (sublen == 0) { + sublen = call_data_dissector(tvb, pinfo, tree); + } + return sublen; +} + +/** Handle iteration over status subject set. + * + */ +static void show_status_subj_ref(gpointer key, gpointer val _U_, gpointer data) { + bp_bundle_ident_t *status_ident = key; + proto_tree *tree_bundle = data; + const bp_bundle_t *status_found = wmem_map_lookup(bp_history->bundles, status_ident); + if (status_found) { + proto_item *item_subj_ref = proto_tree_add_uint(tree_bundle, hf_bundle_status_ref, NULL, 0, 0, status_found->frame_num); + PROTO_ITEM_SET_GENERATED(item_subj_ref); + } +} + +/// Top-level protocol dissector +static int dissect_bp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + { + const gchar *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL); + if (g_strcmp0(proto_name, proto_name_bp) != 0) { + col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp); + col_clear(pinfo->cinfo, COL_INFO); + } + } + gint offset = 0; + + proto_item *item_bundle = proto_tree_add_item(tree, proto_bp, tvb, 0, -1, ENC_NA); + proto_tree *tree_bundle = proto_item_add_subtree(item_bundle, ett_bundle); + + bp_bundle_t *bundle = bp_bundle_new(wmem_file_scope()); + bundle->frame_num = pinfo->num; + bundle->frame_time = pinfo->abs_ts; + + // Read blocks directly from buffer with same addresses as #tvb + const guint buflen = tvb_captured_length(tvb); + + // Require indefinite-length array type + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + proto_item *item_head = proto_tree_add_item(tree_bundle, hf_bundle_head, tvb, chunk->start, chunk->data_length, ENC_NA); + wscbor_require_array(chunk); + if (wscbor_chunk_mark_errors(pinfo, item_head, chunk)) { + return buflen; + } + else if (chunk->type_minor != 31) { + expert_add_info_format(pinfo, item_head, &ei_invalid_framing, "Expected indefinite length array"); + // continue on even for definite-length array + } + + guint64 block_ix = 0; + while (TRUE) { + if (offset >= (gint)buflen) { + proto_item *item_break = proto_tree_add_item(tree_bundle, hf_bundle_break, tvb, offset, -1, ENC_NA); + expert_add_info_format(pinfo, item_break, &ei_invalid_framing, "Array break missing"); + break; + } + + // Either detect BREAK or decode block + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + if (wscbor_is_indefinite_break(chunk)) { + proto_tree_add_cbor_ctrl(tree_bundle, hf_bundle_break, pinfo, tvb, chunk); + break; + } + offset = chunk->start; + + // Load just the array start + const gint block_start = offset; + proto_item *item_block = proto_tree_add_item(tree_bundle, hf_block, tvb, block_start, -1, ENC_NA); + proto_tree *tree_block = proto_item_add_subtree(item_block, ett_block); + + if (block_ix == 0) { + // Primary block + proto_item_prepend_text(item_block, "Primary "); + bp_block_primary_t *block = bp_block_primary_new(wmem_file_scope()); + offset += dissect_block_primary(tvb, pinfo, tree_block, offset, block, bundle); + bundle->primary = block; + + if (!(bundle->ident)) { + bundle->ident = bp_bundle_ident_new( + wmem_file_scope(), + bundle->primary->src_nodeid, + &(bundle->primary->ts), + bundle->primary->frag_offset, + bundle->primary->total_len + ); + proto_tree_add_ident(tree_bundle, hf_bundle_ident, tvb, bundle->ident); + + const bp_bundle_t *seen_found = wmem_map_lookup(bp_history->bundles, bundle->ident); + if (seen_found && (seen_found->frame_num != pinfo->num)) { + proto_item *item_seen = proto_tree_add_uint(tree_bundle, hf_bundle_seen, tvb, 0, 0, seen_found->frame_num); + PROTO_ITEM_SET_GENERATED(item_seen); + + nstime_t td; + nstime_delta(&td, &(bundle->frame_time), &(seen_found->frame_time)); + proto_item *item_td = proto_tree_add_time(tree_bundle, hf_bundle_seen_time_diff, tvb, 0, 0, &td); + PROTO_ITEM_SET_GENERATED(item_td); + } + + // Indicate related status (may be multiple) + wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, bundle->ident); + if (status_set) { + wmem_map_foreach(status_set, show_status_subj_ref, tree_bundle); + } + } + } + else { + // Non-primary block + proto_item_prepend_text(item_block, "Canonical "); + bp_block_canonical_t *block = bp_block_canonical_new(wmem_file_scope(), block_ix); + offset += dissect_block_canonical(tvb, pinfo, tree_block, offset, block, bundle); + } + + proto_item_set_len(item_block, offset - block_start); + block_ix++; + } + + // Handle block-type-specific data after all blocks are present + for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it; + it = wmem_list_frame_next(it)) { + bp_block_canonical_t *block = wmem_list_frame_data(it); + + // Payload block requirements + if (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD) { + // must be last block (i.e. next is NULL) + if (wmem_list_frame_next(it)) { + expert_add_info(pinfo, block->item_block, &ei_block_payload_index); + } + } + + if (block->data) { + // sub-dissect after all is read + dissector_handle_t data_dissect = NULL; + if (block->type_code) { + data_dissect = dissector_get_custom_table_handle(block_dissectors, block->type_code); + } + + bp_dissector_data_t dissect_data; + dissect_data.bundle = bundle; + dissect_data.block = block; + dissect_carried_data(data_dissect, &dissect_data, block->data, pinfo, block->tree_data, FALSE); + } + } + + // Block-data-derived markings + apply_bpsec_mark(&(bundle->primary->sec), pinfo, bundle->primary->item_block); + for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it; + it = wmem_list_frame_next(it)) { + bp_block_canonical_t *block = wmem_list_frame_data(it); + apply_bpsec_mark(&(block->sec), pinfo, block->item_block); + } + + proto_item_append_text(item_bundle, ", Blocks: %"PRIu64, block_ix); + if (bundle->primary) { + const bp_block_primary_t *block = bundle->primary; + proto_item_append_text(item_bundle, ", Dst: %s", block->dst_eid ? block->dst_eid->uri : NULL); + proto_item_append_text(item_bundle, ", Src: %s", block->src_nodeid ? block->src_nodeid->uri : NULL); + if (bundle->ident && (bundle->ident->ts)) { + proto_item_append_text(item_bundle, ", Time: %" PRIu64, bundle->ident->ts->abstime.dtntime); + proto_item_append_text(item_bundle, ", Seq: %" PRIu64, bundle->ident->ts->seqno); + } + } + { + // Keep bundle metadata around for the whole file + bp_bundle_t *found = wmem_map_lookup(bp_history->bundles, bundle->ident); + if (!found) { + wmem_map_insert(bp_history->bundles, bundle->ident, bundle); + } + else { + bp_bundle_free(wmem_file_scope(), bundle); + } + } + + proto_item_set_len(item_bundle, offset); + return buflen; +} + +static gboolean proto_tree_add_status_assertion(proto_tree *tree, int hfassert, packet_info *pinfo, tvbuff_t *tvb, gint *offset) { + proto_item *item_assert = proto_tree_add_item(tree, hfassert, tvb, *offset, -1, 0); + + gboolean result = FALSE; + + wscbor_chunk_t *chunk_assert = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + wscbor_require_array_size(chunk_assert, 1, 2); + wscbor_chunk_mark_errors(pinfo, item_assert, chunk_assert); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, offset, chunk_assert)) { + proto_tree *tree_assert = proto_item_add_subtree(item_assert, ett_status_assert); + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, offset); + gboolean *status_val = wscbor_require_boolean(wmem_packet_scope(), chunk); + proto_tree_add_cbor_boolean(tree_assert, hf_status_assert_val, pinfo, tvb, chunk, status_val); + if (status_val) { + result = *status_val; + } + + if (chunk_assert->head_value > 1) { + bp_dtn_time_t abstime; + proto_tree_add_dtn_time(tree_assert, hf_status_assert_time, pinfo, tvb, offset, &abstime); + } + } + + proto_item_set_len(item_assert, *offset - chunk_assert->start); + return result; +} + +static int dissect_payload_admin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bp_dissector_data_t *context) { + { + const gchar *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL); + if (g_strcmp0(proto_name, proto_name_bp_admin) != 0) { + col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp_admin); + col_clear(pinfo->cinfo, COL_INFO); + } + } + proto_item *item_rec = proto_tree_add_item(tree, proto_bp_admin, tvb, 0, -1, ENC_NA); + gint offset = 0; + + wscbor_chunk_t *chunk_rec = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_rec, 1, 2); + wscbor_chunk_mark_errors(pinfo, item_rec, chunk_rec); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_rec)) { + proto_tree *tree_rec = proto_item_add_subtree(item_rec, ett_admin); + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *type_code = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_rec, hf_admin_record_type, pinfo, tvb, chunk, type_code); + + dissector_handle_t admin_dissect = NULL; + if (type_code) { + proto_item_append_text(item_rec, ": %s", val64_to_str(*type_code, admin_type_vals, "Type %" PRIu64)); + admin_dissect = dissector_get_custom_table_handle(admin_dissectors, type_code); + } + tvbuff_t *tvb_record = tvb_new_subset_remaining(tvb, offset); + gint sublen = dissect_carried_data(admin_dissect, context, tvb_record, pinfo, tree_rec, TRUE); + offset += sublen; + } + + proto_item_set_len(item_rec, offset); + return offset; +} + +static int dissect_status_report(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + bp_dissector_data_t *context = (bp_dissector_data_t *)data; + if (!context) { + return -1; + } + gint offset = 0; + + // Status Information array head + proto_item *item_status = proto_tree_add_item(tree, hf_status_rep, tvb, offset, -1, ENC_NA); + proto_tree *tree_status = proto_item_add_subtree(item_status, ett_status_rep); + guint status_field_ix = 0; + + wscbor_chunk_t *chunk_status = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_status, 4, 6); + wscbor_chunk_mark_errors(pinfo, item_status, chunk_status); + if (wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_status)) { + proto_item_set_len(item_status, offset - chunk_status->start); + return offset; + } + + wscbor_chunk_t *chunk; + gboolean status_received = FALSE; + gboolean status_forwarded = FALSE; + gboolean status_delivered = FALSE; + gboolean status_deleted = FALSE; + + wscbor_chunk_t *chunk_info = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk_info, 4, 4); + { + proto_item *item_info = proto_tree_add_item(tree_status, hf_status_rep_status_info, tvb, offset, -1, ENC_NA); + wscbor_chunk_mark_errors(pinfo, item_info, chunk_info); + if (!wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk_info)) { + proto_tree *tree_info = proto_item_add_subtree(item_info, ett_status_info); + + status_received = proto_tree_add_status_assertion(tree_info, hf_status_rep_received, pinfo, tvb, &offset); + status_forwarded = proto_tree_add_status_assertion(tree_info, hf_status_rep_forwarded, pinfo, tvb, &offset); + status_delivered = proto_tree_add_status_assertion(tree_info, hf_status_rep_delivered, pinfo, tvb, &offset); + status_deleted = proto_tree_add_status_assertion(tree_info, hf_status_rep_deleted, pinfo, tvb, &offset); + } + + proto_item_set_len(item_info, offset - chunk_info->start); + status_field_ix++; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + guint64 *reason_code = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree_status, hf_status_rep_reason_code, pinfo, tvb, chunk, reason_code); + status_field_ix++; + + bp_eid_t *subj_eid = bp_eid_new(wmem_file_scope()); + proto_tree_add_cbor_eid(tree_status, hf_status_rep_subj_src_nodeid, hf_status_rep_subj_src_uri, pinfo, tvb, &offset, subj_eid); + status_field_ix++; + + bp_creation_ts_t *subj_ts = bp_creation_ts_new(wmem_file_scope()); + proto_tree_add_cbor_timestamp(tree_status, hf_status_rep_subj_ts, pinfo, tvb, &offset, subj_ts); + status_field_ix++; + + bp_bundle_ident_t *subj = bp_bundle_ident_new(wmem_file_scope(), subj_eid, subj_ts, NULL, NULL); + + if (chunk_info->head_value > status_field_ix) { + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + subj->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_frag_offset, pinfo, tvb, chunk, subj->frag_offset); + status_field_ix++; + } + + if (chunk_info->head_value > status_field_ix) { + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + subj->total_len = wscbor_require_uint64(wmem_file_scope(), chunk); + proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_payload_len, pinfo, tvb, chunk, subj->total_len); + status_field_ix++; + } + + proto_tree_add_ident(tree_status, hf_status_rep_subj_ident, tvb, subj); + + { + // Pointer back to subject + const bp_bundle_t *subj_found = wmem_map_lookup(bp_history->bundles, subj); + if (subj_found) { + proto_item *item_subj_ref = proto_tree_add_uint(tree_status, hf_status_rep_subj_ref, tvb, 0, 0, subj_found->frame_num); + PROTO_ITEM_SET_GENERATED(item_subj_ref); + + nstime_t td; + nstime_delta(&td, &(context->bundle->frame_time), &(subj_found->frame_time)); + proto_item *item_td = proto_tree_add_time(tree_status, hf_status_time_diff, tvb, 0, 0, &td); + PROTO_ITEM_SET_GENERATED(item_td); + } + } + { + // Pointers from subject to this status + wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, subj); + if (!status_set) { + status_set = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal); + wmem_map_insert(bp_history->admin_status, subj, status_set); + } + else { + bp_bundle_ident_free(wmem_file_scope(), subj); + } + + // Back-references to this status + if (!wmem_map_contains(status_set, context->bundle->ident)) { + g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "status for %p in frame %d", (void*)context->bundle, context->bundle->frame_num); + wmem_map_insert(status_set, context->bundle->ident, NULL); + } + } + + proto_item *item_admin = proto_tree_get_parent(tree); + { + wmem_strbuf_t *status_text = wmem_strbuf_new(wmem_packet_scope(), NULL); + gboolean sep = FALSE; + if (status_received) { + if (sep) { + wmem_strbuf_append(status_text, "|"); + } + wmem_strbuf_append(status_text, "RECEIVED"); + sep = TRUE; + } + if (status_forwarded) { + if (sep) { + wmem_strbuf_append(status_text, "|"); + } + wmem_strbuf_append(status_text, "FORWARDED"); + sep = TRUE; + } + if (status_delivered) { + if (sep) { + wmem_strbuf_append(status_text, "|"); + } + wmem_strbuf_append(status_text, "DELIVERED"); + sep = TRUE; + } + if (status_deleted) { + if (sep) { + wmem_strbuf_append(status_text, "|"); + } + wmem_strbuf_append(status_text, "DELETED"); + sep = TRUE; + } + const char *status_buf = wmem_strbuf_finalize(status_text); + proto_item_append_text(item_admin, ", Status: %s", status_buf); + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Status: %s", status_buf); + } + if (reason_code) { + proto_item_append_text(item_admin, ", Reason: %s", val64_to_str(*reason_code, status_report_reason_vals, "%" PRIu64)); + } + + proto_item_set_len(item_status, offset - chunk_status->start); + return offset; +} + +/** Dissector for Bundle Payload block. + */ +static int dissect_block_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + bp_dissector_data_t *context = (bp_dissector_data_t *)data; + if (!context) { + return -1; + } + const bp_bundle_t *bundle = context->bundle; + + // Parent bundle tree + proto_tree *tree_block = proto_tree_get_parent_tree(tree); + proto_tree *tree_bundle = proto_tree_get_parent_tree(tree_block); + proto_tree *item_bundle = proto_tree_get_parent(tree_bundle); + // Back up to top-level + proto_item *tree_top = proto_tree_get_parent_tree(tree_bundle); + + const gboolean is_fragment = bundle->primary->flags & BP_BUNDLE_IS_FRAGMENT; + const gboolean is_admin = bundle->primary->flags & BP_BUNDLE_PAYLOAD_ADMIN; + if (is_admin) { + proto_item_append_text(item_bundle, ", ADMIN"); + } + if (is_fragment) { + proto_item_append_text(item_bundle, ", FRAGMENT"); + } + const guint payload_len = tvb_captured_length(tvb); + proto_item_append_text(item_bundle, ", Payload-Size: %d", payload_len); + + // identify bundle regardless of payload decoding + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Bundle"); + + // Set if the payload is fully defragmented + tvbuff_t *tvb_payload = NULL; + const char *col_suffix = NULL; + if (is_fragment) { + col_suffix = " (fragment)"; + + if (bp_reassemble_payload) { + if (!(bundle->primary->frag_offset + && bundle->primary->total_len)) { + return -1; + } + // correlate by non-fragment bundle identity hash + bp_bundle_ident_t *corr_ident = bp_bundle_ident_new( + wmem_packet_scope(), + bundle->primary->src_nodeid, + &(bundle->primary->ts), + NULL, + NULL + ); + + if ( + (G_MAXUINT32 < *(bundle->primary->frag_offset)) + || (G_MAXUINT32 < *(bundle->primary->total_len))) { + expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_reassemble_size); + } + else { + const guint32 frag_offset = (guint32)*(bundle->primary->frag_offset); + const guint32 total_len = (guint32)*(bundle->primary->total_len); + fragment_head *payload_frag_msg = fragment_add_check( + &bp_reassembly_table, + tvb, 0, + pinfo, 0, corr_ident, + frag_offset, + payload_len, + TRUE + ); + const guint32 old_total_len = fragment_get_tot_len( + &bp_reassembly_table, + pinfo, 0, corr_ident + ); + if (old_total_len > 0) { + if (total_len != old_total_len) { + expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_tot_mismatch); + } + } + else { + fragment_set_tot_len( + &bp_reassembly_table, + pinfo, 0, corr_ident, + total_len + ); + } + tvb_payload = process_reassembled_data( + tvb, 0, pinfo, + "Reassembled Payload", + payload_frag_msg, + &payload_frag_items, + NULL, + tree_bundle + ); + if (tvb_payload) { + col_suffix = " (reassembled)"; + } + } + bp_bundle_ident_free(wmem_packet_scope(), corr_ident); + } + } + else { + tvb_payload = tvb; + } + if (col_suffix) { + col_append_str(pinfo->cinfo, COL_INFO, col_suffix); + } + if (!tvb_payload) { + return payload_len; + } + + // confidentiality target (i.e. ciphertext) + if (wmem_map_size(context->block->sec.data_c) > 0) { + return payload_len; + } + + // Payload is known to be administrative, independent of destination EID + if (is_admin) { + col_append_str(pinfo->cinfo, COL_INFO, " [Admin]"); + return dissect_payload_admin(tvb_payload, pinfo, tree_top, context); + } + + // an EID shouldn't have both of these set + dissector_handle_t payload_dissect = NULL; + if (bundle->primary->dst_eid->dtn_wkssp) { + payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_wkssp, bundle->primary->dst_eid->dtn_wkssp); + } + else if (bundle->primary->dst_eid->dtn_serv) { + payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_serv, bundle->primary->dst_eid->dtn_serv); + } + + return dissect_carried_data(payload_dissect, &data, tvb_payload, pinfo, tree_top, TRUE); +} + +/** Dissector for Previous Node block. + */ +static int dissect_block_prev_node(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + + proto_tree_add_cbor_eid(tree, hf_previous_node_nodeid, hf_previous_node_uri, pinfo, tvb, &offset, NULL); + + return offset; +} + +/** Dissector for Bundle Age block. + */ +static int dissect_block_bundle_age(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *age = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree, hf_bundle_age_time, pinfo, tvb, chunk, age); + + return offset; +} + +/** Dissector for Hop Count block. + */ +static int dissect_block_hop_count(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + gint offset = 0; + + wscbor_chunk_t *chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + wscbor_require_array_size(chunk, 2, 2); + if (wscbor_skip_if_errors(wmem_packet_scope(), tvb, &offset, chunk)) { + return offset; + } + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *limit = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree, hf_hop_count_limit, pinfo, tvb, chunk, limit); + + chunk = wscbor_chunk_read(wmem_packet_scope(), tvb, &offset); + const guint64 *current = wscbor_require_uint64(wmem_packet_scope(), chunk); + proto_tree_add_cbor_uint64(tree, hf_hop_count_current, pinfo, tvb, chunk, current); + + return offset; +} + +/// Clear state when new file scope is entered +static void bp_init(void) { + bp_history = wmem_new0(wmem_file_scope(), bp_history_t); + // ident keys are owned by the respective bundles + bp_history->bundles = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal); + // subject ident key is not kept + bp_history->admin_status = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal); +} + +static void bp_cleanup(void) {} + +/// Re-initialize after a configuration change +static void bp_reinit_config(void) {} + + +static gpointer fragment_bundle_ident_temporary_key( + const packet_info *pinfo _U_, const guint32 id _U_, const void *data) { + return (bp_bundle_ident_t *)data; +} +static gpointer fragment_bundle_ident_persistent_key( + const packet_info *pinfo _U_, const guint32 id _U_, const void *data) { + const bp_bundle_ident_t *ident = (const bp_bundle_ident_t *)data; + + bp_bundle_ident_t *key = g_slice_new0(bp_bundle_ident_t); + + if (ident->src) { + key->src = g_strdup(ident->src); + } + if (ident->ts) { + key->ts = g_slice_new(bp_creation_ts_t); + key->ts->abstime = ident->ts->abstime; + key->ts->seqno = ident->ts->seqno; + } + if (ident->frag_offset) { + key->frag_offset = g_slice_new(guint64); + key->frag_offset = ident->frag_offset; + } + if (ident->total_len) { + key->total_len = g_slice_new(guint64); + key->total_len = ident->total_len; + } + return key; +} +static void fragment_bundle_ident_free_temporary_key(gpointer ptr _U_) {} +static void fragment_bundle_ident_free_persistent_key(gpointer ptr) { + bp_bundle_ident_t *key = (bp_bundle_ident_t *)ptr; + + if (key->src) { + g_free((char *)key->src); + } + g_slice_free(bp_creation_ts_t, key->ts); + g_slice_free(guint64, key->frag_offset); + g_slice_free(guint64, key->total_len); + + g_slice_free(bp_bundle_ident_t, key); +} +static const reassembly_table_functions bundle_reassembly_table_functions = { + bp_bundle_ident_hash, + bp_bundle_ident_equal, + fragment_bundle_ident_temporary_key, + fragment_bundle_ident_persistent_key, + fragment_bundle_ident_free_temporary_key, + fragment_bundle_ident_free_persistent_key +}; + +/// Overall registration of the protocol +void proto_register_bpv7(void) { + proto_bp = proto_register_protocol( + "DTN Bundle Protocol Version 7", /* name */ + "BPv7", /* short name */ + "bpv7" /* abbrev */ + ); + register_init_routine(&bp_init); + register_cleanup_routine(&bp_cleanup); + + proto_register_field_array(proto_bp, fields, array_length(fields)); + proto_register_subtree_array(ett, array_length(ett)); + expert_module_t *expert = expert_register_protocol(proto_bp); + expert_register_field_array(expert, expertitems, array_length(expertitems)); + + register_dissector("bpv7", dissect_bp, proto_bp); + block_dissectors = register_custom_dissector_table("bpv7.block_type", "BPv7 Block", proto_bp, g_int64_hash, g_int64_equal); + // case-sensitive string matching + payload_dissectors_dtn_wkssp = register_dissector_table("bpv7.payload.dtn_wkssp", "BPv7 Payload (by well-known SSP)", proto_bp, FT_STRING, FALSE); + payload_dissectors_dtn_serv = register_dissector_table("bpv7.payload.serv", "BPv7 Payload (by service demux)", proto_bp, FT_STRING, FALSE); + + module_t *module_bp = prefs_register_protocol(proto_bp, bp_reinit_config); + prefs_register_bool_preference( + module_bp, + "bp_compute_crc", + "Compute and compare CRCs", + "If enabled, the blocks will have CRC checks performed.", + &bp_compute_crc + ); + prefs_register_bool_preference( + module_bp, + "bp_reassemble_payload", + "Reassemble fragmented payloads", + "Whether the dissector should reassemble fragmented bundle payloads.", + &bp_reassemble_payload + ); + prefs_register_bool_preference( + module_bp, + "bp_payload_try_heur", + "Attempt heuristic dissection of BTSD/payload", + "When dissecting block type-specific data and payload and no destination matches, attempt heuristic dissection.", + &bp_payload_try_heur + ); + + reassembly_table_register( + &bp_reassembly_table, + &bundle_reassembly_table_functions + ); + + + proto_bp_admin = proto_register_protocol( + "BPv7 Administrative Record", /* name */ + "BPv7 Admin", /* short name */ + "bpv7.admin_rec" /* abbrev */ + ); + admin_dissectors = register_custom_dissector_table("bpv7.admin_record_type", "BPv7 Administrative Record Type", proto_bp_admin, g_int64_hash, g_int64_equal); +} + +void proto_reg_handoff_bpv7(void) { + handle_cbor = find_dissector("cbor"); + + /* Packaged extensions */ + { + guint64 *key = g_new(guint64, 1); + *key = BP_BLOCKTYPE_PAYLOAD; + dissector_handle_t hdl = create_dissector_handle(dissect_block_payload, proto_bp); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + { + guint64 *key = g_new(guint64, 1); + *key = BP_BLOCKTYPE_PREV_NODE; + dissector_handle_t hdl = create_dissector_handle(dissect_block_prev_node, proto_bp); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + { + guint64 *key = g_new(guint64, 1); + *key = BP_BLOCKTYPE_BUNDLE_AGE; + dissector_handle_t hdl = create_dissector_handle(dissect_block_bundle_age, proto_bp); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + { + guint64 *key = g_new(guint64, 1); + *key = BP_BLOCKTYPE_HOP_COUNT; + dissector_handle_t hdl = create_dissector_handle(dissect_block_hop_count, proto_bp); + dissector_add_custom_table_handle("bpv7.block_type", key, hdl); + } + { + guint64 *key = g_new(guint64, 1); + *key = BP_ADMINTYPE_BUNDLE_STATUS; + dissector_handle_t hdl = create_dissector_handle(dissect_status_report, proto_bp); + dissector_add_custom_table_handle("bpv7.admin_record_type", key, hdl); + } + + bp_reinit_config(); +} diff --git a/epan/dissectors/packet-bpv7.h b/epan/dissectors/packet-bpv7.h new file mode 100644 index 0000000000..eb40fd34fc --- /dev/null +++ b/epan/dissectors/packet-bpv7.h @@ -0,0 +1,357 @@ +/* packet-bpv7.h + * Definitions for Bundle Protocol Version 7 dissection. + * References: + * BPv7: https://datatracker.ietf.org/doc/html/draft-ietf-dtn-bpbis-31 + * + * Copyright 2019-2021, Brian Sipos + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef PACKET_BPV7_H +#define PACKET_BPV7_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * BPv7 block-type-specific data dissectors are registered with the + * dissector table "bpv7.block_type" and Administrative Record dissectors + * with the table "bpv7.admin_record_type". Both use guint64* table keys. + */ + +/** Bundle CRC types. + * Section 4.1.1. + */ +typedef enum { + /// no CRC is present. + BP_CRC_NONE = 0, + /// a standard X-25 CRC-16 is present. + BP_CRC_16 = 1, + /// a standard CRC32C (Castagnoli) CRC-32 is present. + BP_CRC_32 = 2, +} BundleCrcType; + +/** Bundle processing control flags. + * Section 4.1.3. + */ +typedef enum { + /// bundle deletion status reports are requested. + BP_BUNDLE_REQ_DELETION_REPORT = 0x040000, + /// bundle delivery status reports are requested. + BP_BUNDLE_REQ_DELIVERY_REPORT = 0x020000, + /// bundle forwarding status reports are requested. + BP_BUNDLE_REQ_FORWARDING_REPORT = 0x010000, + /// bundle reception status reports are requested. + BP_BUNDLE_REQ_RECEPTION_REPORT = 0x004000, + /// status time is requested in all status reports. + BP_BUNDLE_REQ_STATUS_TIME = 0x000040, + /// user application acknowledgement is requested. + BP_BUNDLE_USER_APP_ACK = 0x000020, + /// bundle must not be fragmented. + BP_BUNDLE_NO_FRAGMENT = 0x000004, + /// payload is an administrative record. + BP_BUNDLE_PAYLOAD_ADMIN = 0x000002, + /// bundle is a fragment. + BP_BUNDLE_IS_FRAGMENT = 0x000001, +} BundleProcessingFlag; + +/** Block processing control flags. + * Section 4.1.4. + */ +typedef enum { + /// block must be removed from bundle if it can't be processed. + BP_BLOCK_REMOVE_IF_NO_PROCESS = 0x10, + /// bundle must be deleted if block can't be processed. + BP_BLOCK_DELETE_IF_NO_PROCESS = 0x04, + /// transmission of a status report is requested if block can't be processed. + BP_BLOCK_STATUS_IF_NO_PROCESS = 0x02, + /// block must be replicated in every fragment. + BP_BLOCK_REPLICATE_IN_FRAGMENT = 0x01, +} BlockProcessingFlag; + +/** Standard block type codes. + * Section 4.2.3 and Section 4.3. + */ +typedef enum { + BP_BLOCKTYPE_INVALID = 0, + /// Payload (data) + BP_BLOCKTYPE_PAYLOAD = 1, + /// Previous Node + BP_BLOCKTYPE_PREV_NODE = 6, + /// Bundle Age + BP_BLOCKTYPE_BUNDLE_AGE = 7, + /// Hop Count + BP_BLOCKTYPE_HOP_COUNT = 10, + /// Block Integrity Block + BP_BLOCKTYPE_BIB = 11, + /// Block Confidentiality Block + BP_BLOCKTYPE_BCB = 12, +} BlockTypeCode; + +/** Administrative record type codes. + * Section 6.1. + */ +typedef enum { + /// Bundle status report + BP_ADMINTYPE_BUNDLE_STATUS = 1, +} AdminRecordTypeCode; + +/// DTN time with derived UTC time +typedef struct { + /// DTN time + guint64 dtntime; + /// Converted to UTC + nstime_t utctime; +} bp_dtn_time_t; + +/// Creation Timestamp used to correlate bundles +typedef struct { + /// Absolute time + bp_dtn_time_t abstime; + /// Sequence number + guint64 seqno; +} bp_creation_ts_t; + +/** Construct a new timestamp. + */ +WS_DLL_PUBLIC +bp_creation_ts_t * bp_creation_ts_alloc(wmem_allocator_t *alloc); + +/** Function to match the GDestroyNotify signature. + */ +WS_DLL_PUBLIC +void bp_creation_ts_free(wmem_allocator_t *alloc, bp_creation_ts_t *obj); + +/** Function to match the GCompareDataFunc signature. + */ +WS_DLL_PUBLIC +gint bp_creation_ts_compare(gconstpointer a, gconstpointer b, gpointer user_data); + +/** Endpoint ID scheme encodings. + */ +typedef enum { + EID_SCHEME_DTN = 1, + EID_SCHEME_IPN = 2, +} EidScheme; + +/// Metadata from a Endpoint ID +typedef struct { + /// Scheme ID number + gint64 scheme; + /// Derived URI text + const char *uri; + + /// Optional DTN well-known SSP + const char *dtn_wkssp; + /// Optional URI authority part +// const char *node_name; + /// Optional DTN service name + const char *dtn_serv; +} bp_eid_t; + +/** Construct a new timestamp. + */ +WS_DLL_PUBLIC +bp_eid_t * bp_eid_new(wmem_allocator_t *alloc); + +/** Function to match the GDestroyNotify signature. + */ +WS_DLL_PUBLIC +void bp_eid_free(wmem_allocator_t *alloc, bp_eid_t *obj); + +/** Function to match the GCompareFunc signature. + */ +WS_DLL_PUBLIC +gboolean bp_eid_equal(gconstpointer a, gconstpointer b); + +/// Security marking metadata +typedef struct { + /// Block numbers marking the data as security integrity protected + wmem_map_t *data_i; + /// Block numbers marking the data as security-modified and not decodable + wmem_map_t *data_c; +} security_mark_t; + +/// Metadata extracted from the primary block +typedef struct { + /// Display item for the whole block + proto_item *item_block; + + /// Bundle flags (assumed zero). + /// Values are BundleProcessingFlag. + guint64 flags; + /// Destination EID + bp_eid_t *dst_eid; + /// Source NID + bp_eid_t *src_nodeid; + /// Report-to NID + bp_eid_t *rep_nodeid; + /// Creation Timestamp + bp_creation_ts_t ts; + /// Optional fragment start offset + guint64 *frag_offset; + /// Optional bundle total length + guint64 *total_len; + /// CRC type code (assumed zero) + BundleCrcType crc_type; + /// Raw bytes of CRC field + tvbuff_t *crc_field; + + security_mark_t sec; +} bp_block_primary_t; + +/** Construct a new object on the file allocator. + */ +WS_DLL_PUBLIC +bp_block_primary_t * bp_block_primary_new(wmem_allocator_t *alloc); + +/** Function to match the GDestroyNotify signature. + */ +WS_DLL_PUBLIC +void bp_block_primary_free(wmem_allocator_t *alloc, bp_block_primary_t *obj); + +typedef struct { + /// The index of the block within the bundle. + /// This is for internal bookkeeping, *not* the block number. + guint64 blk_ix; + /// Display item for the whole block + proto_item *item_block; + + /// Type of this block + guint64 *type_code; + /// Unique identifier for this block + guint64 *block_number; + /// All flags on this block + guint64 flags; + /// CRC type code (assumed zero) + BundleCrcType crc_type; + /// Raw bytes of CRC field + tvbuff_t *crc_field; + + /// Type-specific data, unencoded + tvbuff_t *data; + /// Type-specific data tree + proto_tree *tree_data; + + security_mark_t sec; +} bp_block_canonical_t; + +/** Construct a new object on the file allocator. + * @param blk_ix The index of the block within the bundle. + * The canonical index is always greater than zero. + */ +WS_DLL_PUBLIC +bp_block_canonical_t * bp_block_canonical_new(wmem_allocator_t *alloc, guint64 blk_ix); + +WS_DLL_PUBLIC +void bp_block_canonical_delete(wmem_allocator_t *alloc, bp_block_canonical_t *obj); + +/// Identification of an individual bundle +typedef struct { + /// Normalized EID URI for the Source Node ID + const char *src; + /// Pointer to an external Creation Timestamp + bp_creation_ts_t *ts; + /// Pointer to external optional fragment start offset + guint64 *frag_offset; + /// Pointer to external optional bundle total length + guint64 *total_len; +} bp_bundle_ident_t; + +/** Construct a new object on the file allocator. + */ +WS_DLL_PUBLIC +bp_bundle_ident_t * bp_bundle_ident_new(wmem_allocator_t *alloc, bp_eid_t *src, bp_creation_ts_t *ts, guint64 *off, guint64 *len); + +WS_DLL_PUBLIC +void bp_bundle_ident_free(wmem_allocator_t *alloc, bp_bundle_ident_t *obj); + +/** Function to match the GCompareFunc signature. + */ +WS_DLL_PUBLIC +gboolean bp_bundle_ident_equal(gconstpointer a, gconstpointer b); + +/** Function to match the GHashFunc signature. + */ +WS_DLL_PUBLIC +guint bp_bundle_ident_hash(gconstpointer key); + +/// Metadata extracted per-bundle +typedef struct { + /// Index of the frame + guint32 frame_num; + /// Timestamp on the frame (end time if reassembled) + nstime_t frame_time; + /// Bundle identity derived from #primary data + bp_bundle_ident_t *ident; + /// Required primary block + bp_block_primary_t *primary; + /// Additional blocks in order (type bp_block_canonical_t) + wmem_list_t *blocks; + /// Map from block number (guint64) to pointer to block of that number + /// (bp_block_canonical_t owned by #blocks) + wmem_map_t *block_nums; + /// Map from block type code (guint64) to sequence (wmem_list_t) of + /// pointers to block of that type (bp_block_canonical_t owned by #blocks) + wmem_map_t *block_types; +} bp_bundle_t; + +/** Construct a new object on the file allocator. + */ +WS_DLL_PUBLIC +bp_bundle_t * bp_bundle_new(wmem_allocator_t *alloc); + +/** Function to match the GDestroyNotify signature. + */ +WS_DLL_PUBLIC +void bp_bundle_free(wmem_allocator_t *alloc, bp_bundle_t *obj); + +/** Extract an Endpoint ID. + * All EID fields are allocated with wmem_file_scope(). + * + * @param tree The tree to write items under. + * @param hfindex The root item field. + * @param hfindex_uri The reassembled URI item field. + * @param pinfo Packet info to update. + * @param tvb Buffer to read from. + * @param[in,out] offset Starting offset within @c tvb. + * @param[out] eid If non-null, the EID to write to. + * @return The new tree item. + */ +WS_DLL_PUBLIC +proto_item * proto_tree_add_cbor_eid(proto_tree *tree, int hfindex, int hfindex_uri, packet_info *pinfo, tvbuff_t *tvb, gint *offset, bp_eid_t *eid); + +/// Metadata for an entire file +typedef struct { + /// Map from a bundle ID (bp_bundle_ident_t) to bundle (bp_bundle_t) + wmem_map_t *bundles; + /// Map from subject bundle ID (bp_bundle_ident_t) to + /// map from references (bp_bundle_ident_t) of status bundles to NULL + /// i.e. a set + wmem_map_t *admin_status; +} bp_history_t; + +/** Data supplied to each block sub-dissector. + */ +typedef struct { + /// The overall bundle being decoded (so far) + bp_bundle_t *bundle; + /// This block being decoded + bp_block_canonical_t *block; +} bp_dissector_data_t; + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_BPV7_H */ diff --git a/epan/dissectors/packet-cfdp.c b/epan/dissectors/packet-cfdp.c index 7d6334d3c0..80db3b45f0 100644 --- a/epan/dissectors/packet-cfdp.c +++ b/epan/dissectors/packet-cfdp.c @@ -19,7 +19,7 @@ #include #include -#include "packet-dtn.h" +#include "packet-cfdp.h" /* The CFDP standard can be found here: * http://public.ccsds.org/publications/archive/727x0b4.pdf diff --git a/epan/dissectors/packet-cfdp.h b/epan/dissectors/packet-cfdp.h new file mode 100644 index 0000000000..ec802abbbf --- /dev/null +++ b/epan/dissectors/packet-cfdp.h @@ -0,0 +1,46 @@ +/* packet-cfdp.c + * Routines for CCSDS File Delivery Protocol (CFDP) dissection + * Copyright 2013, Juan Antonio Montesinos juan.mondl@gmail.com + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Slightly updated to allow more in-depth decoding when called + * with the 'dissect_as_subtree' method and to leverage some + * of the bitfield display operations: Keith Scott + * . + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef PACKET_CFDP_H +#define PACKET_CFDP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void dissect_cfdp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset); + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_CFDP_H */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-ltp.c b/epan/dissectors/packet-ltp.c index e4a11834fe..2cb32a9b9b 100644 --- a/epan/dissectors/packet-ltp.c +++ b/epan/dissectors/packet-ltp.c @@ -36,7 +36,7 @@ #include #include -#include "packet-dtn.h" +#include "packet-bpv6.h" void proto_register_ltp(void); void proto_reg_handoff_ltp(void); diff --git a/epan/dissectors/packet-tcpclv3.c b/epan/dissectors/packet-tcpclv3.c new file mode 100644 index 0000000000..2f7734be00 --- /dev/null +++ b/epan/dissectors/packet-tcpclv3.c @@ -0,0 +1,775 @@ +/* packet-tcpclv3.c + * References: + * RFC 7242: https://tools.ietf.org/html/rfc7242 + * + * Copyright 2006-2007 The MITRE Corporation. + * All Rights Reserved. + * Approved for Public Release; Distribution Unlimited. + * Tracking Number 07-0090. + * + * The US Government will not be charged any license fee and/or royalties + * related to this software. Neither name of The MITRE Corporation; nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Specification reference: + * RFC 5050 + * https://tools.ietf.org/html/rfc5050 + */ + +/* + * Modifications were made to this file under designation MFS-33289-1 and + * are Copyright 2015 United States Government as represented by NASA + * Marshall Space Flight Center. All Rights Reserved. + * + * Released under the GNU GPL with NASA legal approval granted 2016-06-10. + * + * The subject software is provided "AS IS" WITHOUT ANY WARRANTY of any kind, + * either expressed, implied or statutory and this agreement does not, + * in any manner, constitute an endorsement by government agency of any + * results, designs or products resulting from use of the subject software. + * See the Agreement for the specific language governing permissions and + * limitations. + */ + +#include "config.h" + +#include +#include +#include +#include +#include "packet-tcpclv3.h" +#include "packet-bpv6.h" +#include "packet-tcp.h" + +/* For Reassembling TCP Convergence Layer segments */ +static reassembly_table msg_reassembly_table; + +static const char magic[] = {'d', 't', 'n', '!'}; + +static int proto_tcp_conv = -1; + +/* TCP Convergence Header Variables */ +static int hf_tcp_convergence_pkt_type = -1; + +/* Refuse-Bundle reason code */ +static int hf_dtn_refuse_bundle_reason_code = -1; + +static int hf_contact_hdr_version = -1; +static int hf_contact_hdr_flags = -1; +static int hf_contact_hdr_keep_alive = -1; +static int hf_contact_hdr_flags_ack_req = -1; +static int hf_contact_hdr_flags_frag_enable = -1; +static int hf_contact_hdr_flags_nak = -1; +static int hf_contact_hdr_magic = -1; +static int hf_contact_hdr_local_eid_length = -1; +static int hf_contact_hdr_local_eid = -1; + +/* TCP Convergence Data Header Variables */ +static int hf_tcp_convergence_data_procflags = -1; +static int hf_tcp_convergence_data_procflags_start = -1; +static int hf_tcp_convergence_data_procflags_end = -1; +static int hf_tcp_convergence_data_segment_length = -1; + +/* TCP Convergence Ack Variables */ +static int hf_tcp_convergence_ack_length = -1; + +/* TCP Convergence Shutdown Header Variables */ +static int hf_tcp_convergence_shutdown_flags = -1; +static int hf_tcp_convergence_shutdown_flags_reason = -1; +static int hf_tcp_convergence_shutdown_flags_delay = -1; +static int hf_tcp_convergence_shutdown_reason = -1; +static int hf_tcp_convergence_shutdown_delay = -1; + +/*TCP Convergence Layer Reassembly boilerplate*/ +static int hf_msg_fragments = -1; +static int hf_msg_fragment = -1; +static int hf_msg_fragment_overlap = -1; +static int hf_msg_fragment_overlap_conflicts = -1; +static int hf_msg_fragment_multiple_tails = -1; +static int hf_msg_fragment_too_long_fragment = -1; +static int hf_msg_fragment_error = -1; +static int hf_msg_fragment_count = -1; +static int hf_msg_reassembled_in = -1; +static int hf_msg_reassembled_length = -1; + +/* Tree Node Variables */ +static gint ett_conv_flags = -1; +static gint ett_shutdown_flags = -1; +static gint ett_contact_hdr_flags = -1; +static gint ett_tcp_conv = -1; +static gint ett_tcp_conv_hdr = -1; +static gint ett_msg_fragment = -1; +static gint ett_msg_fragments = -1; + +static expert_field ei_tcp_convergence_data_flags = EI_INIT; +static expert_field ei_tcp_convergence_segment_length = EI_INIT; +static expert_field ei_tcp_convergence_ack_length = EI_INIT; + + +static dissector_handle_t bundle_handle; + +typedef struct dictionary_data { + int bundle_header_dict_length; + + int dest_scheme_offset; + int dst_scheme_pos; + int dst_scheme_len; + int source_scheme_offset; + int src_scheme_pos; + int src_scheme_len; + int report_scheme_offset; + int rpt_scheme_pos; + int rpt_scheme_len; + int cust_scheme_offset; + int cust_scheme_pos; + int cust_scheme_len; + int dest_ssp_offset; + int dst_ssp_len; + int source_ssp_offset; + int src_ssp_len; + int report_ssp_offset; + int rpt_ssp_len; + int cust_ssp_offset; + int cust_ssp_len; + +} dictionary_data_t; + + +static const value_string packet_type_vals[] = { + {((TCP_CONVERGENCE_DATA_SEGMENT>>4) & 0x0F), "Data"}, + {((TCP_CONVERGENCE_ACK_SEGMENT>>4) & 0x0F), "Ack"}, + {((TCP_CONVERGENCE_REFUSE_BUNDLE>>4) & 0x0F), "Refuse Bundle"}, + {((TCP_CONVERGENCE_KEEP_ALIVE>>4) & 0x0F), "Keep Alive"}, + {((TCP_CONVERGENCE_SHUTDOWN>>4) & 0x0F), "Shutdown"}, + {((TCP_CONVERGENCE_LENGTH>>4) & 0x0F), "Length"}, + {0, NULL} +}; + +/* Refuse-Bundle Reason-Code Flags as per RFC-7242: Section-5.4 */ +static const value_string refuse_bundle_reason_code[] = { + {TCP_REFUSE_BUNDLE_REASON_UNKNOWN, "Reason for refusal is unknown"}, + {TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE, "Complete Bundle Received"}, + {TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED, "Receiver's resources exhausted"}, + {TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT, "Receiver expects re-transmission of bundle"}, + {0, NULL} +}; + +static const fragment_items msg_frag_items = { + /*Fragment subtrees*/ + &ett_msg_fragment, + &ett_msg_fragments, + /*Fragment Fields*/ + &hf_msg_fragments, + &hf_msg_fragment, + &hf_msg_fragment_overlap, + &hf_msg_fragment_overlap_conflicts, + &hf_msg_fragment_multiple_tails, + &hf_msg_fragment_too_long_fragment, + &hf_msg_fragment_error, + &hf_msg_fragment_count, + /*Reassembled in field*/ + &hf_msg_reassembled_in, + /*Reassembled length field*/ + &hf_msg_reassembled_length, + /* Reassembled data field */ + NULL, + /*Tag*/ + "Message fragments" +}; + +static guint +get_dtn_contact_header_len(packet_info *pinfo _U_, tvbuff_t *tvb, + int offset, void *data _U_) +{ + int len, bytecount; + + /* get length from sdnv */ + len = evaluate_sdnv(tvb, offset+8, &bytecount); + if (len < 0) + return 0; + + return len+bytecount+8; +} + +static int +dissect_dtn_contact_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_item *ti; + proto_tree *conv_proto_tree, *conv_tree, *conv_flag_tree; + int eid_length, sdnv_length; + int offset = 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCLv3"); + col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */ + col_add_str(pinfo->cinfo, COL_INFO, "Contact Header"); + + ti = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA); + conv_proto_tree = proto_item_add_subtree(ti, ett_tcp_conv); + + conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, offset, -1, ett_tcp_conv, NULL, "Contact Header"); + + proto_tree_add_item(conv_tree, hf_contact_hdr_magic, tvb, offset, 4, ENC_NA|ENC_ASCII); + offset += 4; + proto_tree_add_item(conv_tree, hf_contact_hdr_version, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Subtree to expand the bits in the Contact Header Flags */ + ti = proto_tree_add_item(conv_tree, hf_contact_hdr_flags, tvb, offset, 1, ENC_BIG_ENDIAN); + conv_flag_tree = proto_item_add_subtree(ti, ett_contact_hdr_flags); + proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_ack_req, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_frag_enable, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_nak, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + proto_tree_add_item(conv_tree, hf_contact_hdr_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* + * New format Contact header has length field followed by Bundle Header. + */ + expert_field *ei_bundle_sdnv_length; + eid_length = evaluate_sdnv_ei(tvb, offset, &sdnv_length, &ei_bundle_sdnv_length); + ti = proto_tree_add_int(tree, hf_contact_hdr_local_eid_length, tvb, offset, sdnv_length, eid_length); + if (ei_bundle_sdnv_length) { + expert_add_info(pinfo, ti, ei_bundle_sdnv_length); + return offset; + } + + proto_tree_add_item(conv_tree, hf_contact_hdr_local_eid, tvb, sdnv_length + offset, eid_length, ENC_NA|ENC_ASCII); + return tvb_captured_length(tvb); +} + +static guint +get_tcpcl_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) +{ + int len, bytecount; + guint8 conv_hdr = tvb_get_guint8(tvb, offset); + + switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) + { + case TCP_CONVERGENCE_DATA_SEGMENT: + /* get length from sdnv */ + len = evaluate_sdnv(tvb, offset+1, &bytecount); + if (len < 0) + return 0; + + return len+bytecount+1; + + case TCP_CONVERGENCE_ACK_SEGMENT: + /* get length from sdnv */ + len = evaluate_sdnv(tvb, offset+1, &bytecount); + if (len < 0) + return 0; + + return bytecount+1; + + case TCP_CONVERGENCE_KEEP_ALIVE: + case TCP_CONVERGENCE_REFUSE_BUNDLE: + /* always 1 byte */ + return 1; + case TCP_CONVERGENCE_SHUTDOWN: + len = 1; + + if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) { + len += 1; + } + if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) { + len += 2; + } + + return len; + + case TCP_CONVERGENCE_LENGTH: + /* get length from sdnv */ + len = evaluate_sdnv(tvb, offset+1, &bytecount); + if (len < 0) + return 0; + return bytecount+1; + + } + + /* This probably isn't a TCPCL/Bundle packet, so just stop dissection */ + return -1; +} + +static int +dissect_tcpcl_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + guint8 conv_hdr; + guint8 refuse_bundle_hdr; + int offset = 0; + int sdnv_length, segment_length, convergence_hdr_size; + proto_item *ci, *sub_item; + proto_tree *conv_proto_tree, *conv_tree, *sub_tree; + fragment_head *frag_msg; + tvbuff_t *new_tvb; + gboolean more_frags; + int processed_length = 0; + const gchar* col_text; + gboolean bundle_in_col_info; + + static guint32 frag_id = 0; + static guint32 last_frame = 0; + static int last_raw_offset = 0; + + if (last_frame != pinfo->fd->num || tvb_raw_offset(tvb) < last_raw_offset) + frag_id = 0; + last_frame = pinfo->fd->num; + last_raw_offset = tvb_raw_offset(tvb); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCL"); + col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */ + + col_text = col_get_text(pinfo->cinfo, COL_INFO); + bundle_in_col_info = (col_text && strstr(col_text, " > ")); + + ci = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA); + conv_proto_tree = proto_item_add_subtree(ci, ett_tcp_conv); + + conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, 0, -1, ett_tcp_conv_hdr, NULL, "TCP Convergence Header"); + + conv_hdr = tvb_get_guint8(tvb, offset); + proto_tree_add_item(conv_tree, hf_tcp_convergence_pkt_type, tvb, offset, 1, ENC_BIG_ENDIAN); + col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((conv_hdr>>4)&0xF, packet_type_vals, "Unknown")); + + switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) + { + case TCP_CONVERGENCE_DATA_SEGMENT: + sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_data_procflags, tvb, + offset, 1, ENC_BIG_ENDIAN); + sub_tree = proto_item_add_subtree(sub_item, ett_conv_flags); + proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_start, + tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_end, + tvb, offset, 1, ENC_BIG_ENDIAN); + + /* Only Start and End flags (bits 0 & 1) are valid in Data Segment */ + if ((conv_hdr & ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_DATA_FLAGS)) != 0) { + expert_add_info(pinfo, sub_item, &ei_tcp_convergence_data_flags); + } + + segment_length = evaluate_sdnv(tvb, 1, &sdnv_length); + sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_data_segment_length, tvb, 1, sdnv_length, segment_length); + if (segment_length < 0) { + expert_add_info(pinfo, sub_item, &ei_tcp_convergence_segment_length); + return 1; + } + + convergence_hdr_size = sdnv_length + 1; + + /* + * 1/11/2006 - If I got here, I should have a complete convergence layer + * "segment" beginning at frame_offset. However that might not be a + * complete bundle. Or there might be a complete bundle plus one or more + * additional convergence layer headers. + */ + + new_tvb = NULL; + sub_tree = NULL; + if ((conv_hdr & TCP_CONVERGENCE_DATA_END_FLAG) == TCP_CONVERGENCE_DATA_END_FLAG) { + more_frags = FALSE; + } + else { + more_frags = TRUE; + } + + frag_msg = fragment_add_seq_next(&msg_reassembly_table, + tvb, offset + convergence_hdr_size, + pinfo, frag_id, data, + segment_length, more_frags); + + if (!more_frags) ++frag_id; + + processed_length = convergence_hdr_size + segment_length; + + if (frag_msg && !more_frags) { + + int save_fd_head_layer = frag_msg->reas_in_layer_num; + frag_msg->reas_in_layer_num = pinfo->curr_layer_num; + + new_tvb = process_reassembled_data(tvb, offset + convergence_hdr_size, + pinfo, "Reassembled DTN", frag_msg, + &msg_frag_items, NULL, + proto_tree_get_parent_tree(tree) + ); + + frag_msg->reas_in_layer_num = save_fd_head_layer; + } + + if (new_tvb) { + if (0 == call_dissector_with_data(bundle_handle, new_tvb, pinfo, sub_tree, data)) { + /*Couldn't parse bundle, treat as raw data */ + call_data_dissector(new_tvb, pinfo, sub_tree); + return tvb_captured_length(tvb); + } + } + else { + + /* + * If there are 2 segments, the second of which is very short, this + * gets displayed instead of the usual Source EID/Destination EID in + * the Bundle Dissection frame. If these statements are left out entirely, + * nothing is displayed, i.e., there seems to be no way to get the + * Source/Destination in the 2-segment case. I'll leave it in because I + * think it is informative in the multi-segment case although confusing in the + * 2-segment case. + */ + col_add_str(pinfo->cinfo, COL_INFO, "[Bundle TCPCL Segment]"); + } + break; + case TCP_CONVERGENCE_ACK_SEGMENT: + if (bundle_in_col_info) { + if (!strstr(col_text, ", TCPL ACK")) { + col_add_str(pinfo->cinfo, COL_INFO, ", TCPL ACK Segment(s)"); + } + } else { + col_set_str(pinfo->cinfo, COL_INFO, "TCPL ACK Segment(s)"); + } + segment_length = evaluate_sdnv(tvb, offset+1, &sdnv_length); + sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_ack_length, tvb, offset+1, sdnv_length, segment_length); + if (segment_length < 0) { + expert_add_info(pinfo, sub_item, &ei_tcp_convergence_ack_length); + processed_length = tvb_captured_length(tvb); + } else { + processed_length = sdnv_length + 1; + } + break; + case TCP_CONVERGENCE_KEEP_ALIVE: + if (bundle_in_col_info) { + if (!strstr(col_text, ", TCPL KEEPALIVE")) { + col_add_str(pinfo->cinfo, COL_INFO, ", TCPL KEEPALIVE Segment"); + } + } else { + col_set_str(pinfo->cinfo, COL_INFO, "TCPL KEEPALIVE Segment"); + } + /*No valid flags in Keep Alive*/ + processed_length = 1; + break; + + case TCP_CONVERGENCE_SHUTDOWN: + if (bundle_in_col_info) { + if (!strstr(col_text, ", TCPL SHUTDOWN")) { + col_add_str(pinfo->cinfo, COL_INFO, ", TCPL SHUTDOWN Segment"); + } + } else { + col_set_str(pinfo->cinfo, COL_INFO, "TCPL SHUTDOWN Segment"); + } + /* Add tree for Shutdown Flags */ + sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_shutdown_flags, tvb, + offset, 1, ENC_BIG_ENDIAN); + sub_tree = proto_item_add_subtree(sub_item, ett_shutdown_flags); + + proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_reason, + tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_delay, + tvb, offset, 1, ENC_BIG_ENDIAN); + + offset += 1; + if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) { + proto_tree_add_item(conv_tree, + hf_tcp_convergence_shutdown_reason, tvb, + offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) { + proto_tree_add_item(conv_tree, + hf_tcp_convergence_shutdown_delay, tvb, + offset, 2, ENC_BIG_ENDIAN); + } + break; + case TCP_CONVERGENCE_REFUSE_BUNDLE: + if (bundle_in_col_info) { + if (!strstr(col_text, ", TCPL REFUSE")) { + col_add_str(pinfo->cinfo, COL_INFO, ", TCPL REFUSE_BUNDLE Segment"); + } + } else { + col_set_str(pinfo->cinfo, COL_INFO, "TCPL REFUSE_BUNDLE Segment"); + } + + refuse_bundle_hdr = tvb_get_guint8(tvb, offset); + proto_tree_add_item(conv_tree, hf_dtn_refuse_bundle_reason_code, tvb, offset, 1, ENC_BIG_ENDIAN); + col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((refuse_bundle_hdr>>4)&0xF, refuse_bundle_reason_code, "Unknown")); + + /*No valid flags*/ + processed_length = tvb_captured_length(tvb); + break; + } + + return processed_length; +} + +static int +dissect_tcpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + guint8 conv_hdr; + int offset, bytecount; + int processed_length; + + /* Make sure we have a convergence header byte */ + if (!tvb_bytes_exist(tvb, 0, 1)) + return 0; + + conv_hdr = tvb_get_guint8(tvb, 0); + switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK) + { + case TCP_CONVERGENCE_DATA_SEGMENT: + case TCP_CONVERGENCE_ACK_SEGMENT: + /* ensure sdnv */ + offset = 1; + bytecount = 1; + + if (!tvb_bytes_exist(tvb, offset, 1)) { + pinfo->desegment_offset = 0; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + return 0; + } + + while (tvb_get_guint8(tvb, offset) & ~SDNV_MASK) { + if (bytecount > (int)sizeof(int)) { + /* invalid length field */ + return 0; + } + + bytecount++; + offset++; + + if (!tvb_bytes_exist(tvb, offset, 1)) { + pinfo->desegment_offset = 0; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + return 0; + } + } + break; + case TCP_CONVERGENCE_KEEP_ALIVE: + case TCP_CONVERGENCE_REFUSE_BUNDLE: + /* always 1 byte */ + break; + case TCP_CONVERGENCE_SHUTDOWN: + if ((conv_hdr & + ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_SHUTDOWN_FLAGS)) != 0) { + /* Not for us */ + return 0; + } + break; + default: + if (conv_hdr == (guint8)magic[0]) { + if (!tvb_bytes_exist(tvb, 0, 4) || tvb_memeql(tvb, 0, magic, 4)) { + /* Not for us */ + return 0; + } + + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 8, get_dtn_contact_header_len, dissect_dtn_contact_header, data); + return tvb_captured_length(tvb); + } + + /* Not for us */ + return 0; + }; + + processed_length = get_tcpcl_pdu_len(pinfo, tvb, 0, data); + + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 1, get_tcpcl_pdu_len, dissect_tcpcl_pdu, data); + + return processed_length; +} + + +void +proto_register_tcpclv3(void) +{ + + static hf_register_info hf_tcpcl[] = { + {&hf_tcp_convergence_pkt_type, + {"Pkt Type", "tcpcl.pkt_type", + FT_UINT8, BASE_DEC, VALS(packet_type_vals), 0xF0, NULL, HFILL} + }, + {&hf_dtn_refuse_bundle_reason_code, + {"Reason-Code", "tcpcl.refuse.reason_code", + FT_UINT8, BASE_DEC, VALS(refuse_bundle_reason_code), 0x0F, NULL, HFILL} + }, + {&hf_tcp_convergence_data_procflags, + {"TCP Convergence Data Flags", "tcpcl.data.proc.flag", + FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_DATA_FLAGS, NULL, HFILL} + }, + {&hf_tcp_convergence_data_procflags_start, + {"Segment contains start of bundle", "tcpcl.data.proc.start", + FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_START_FLAG, NULL, HFILL} + }, + {&hf_tcp_convergence_data_procflags_end, + {"Segment contains end of Bundle", "tcpcl.data.proc.end", + FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_END_FLAG, NULL, HFILL} + }, + {&hf_tcp_convergence_data_segment_length, + {"Segment Length", "tcpcl.data.length", + FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_tcp_convergence_shutdown_flags, + {"TCP Convergence Shutdown Flags", "tcpcl.shutdown.flags", + FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_SHUTDOWN_FLAGS, NULL, HFILL} + }, + {&hf_tcp_convergence_shutdown_flags_reason, + {"Shutdown includes Reason Code", "tcpcl.shutdown.reason.flag", + FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_REASON, NULL, HFILL} + }, + {&hf_tcp_convergence_shutdown_flags_delay, + {"Shutdown includes Reconnection Delay", "tcpcl.shutdown.delay.flag", + FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_DELAY, NULL, HFILL} + }, + {&hf_tcp_convergence_shutdown_reason, + {"Shutdown Reason Code", "tcpcl.shutdown.reason", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_tcp_convergence_shutdown_delay, + {"Shutdown Reconnection Delay", "tcpcl.shutdown.delay", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_tcp_convergence_ack_length, + {"Ack Length", "tcpcl.ack.length", + FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_version, + {"Version", "tcpcl.contact_hdr.version", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_flags, + {"Flags", "tcpcl.contact_hdr.flags", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_flags_ack_req, + {"Bundle Acks Requested", "tcpcl.contact_hdr.flags.ackreq", + FT_BOOLEAN, 8, NULL, TCP_CONV_BUNDLE_ACK_FLAG, NULL, HFILL} + }, + {&hf_contact_hdr_flags_frag_enable, + {"Reactive Fragmentation Enabled", "tcpcl.contact_hdr.flags.fragen", + FT_BOOLEAN, 8, NULL, TCP_CONV_REACTIVE_FRAG_FLAG, NULL, HFILL} + }, + {&hf_contact_hdr_flags_nak, + {"Support Negative Acknowledgements", "tcpcl.contact_hdr.flags.nak", + FT_BOOLEAN, 8, NULL, TCP_CONV_CONNECTOR_RCVR_FLAG, NULL, HFILL} + }, + {&hf_contact_hdr_keep_alive, + {"Keep Alive", "tcpcl.contact_hdr.keep_alive", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_magic, + {"Magic", "tcpcl.contact_hdr.magic", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_local_eid, + {"Local EID", "tcpcl.contact_hdr.local_eid", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_contact_hdr_local_eid_length, + {"Local EID Length", "tcpcl.contact_hdr.local_eid_length", + FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + + {&hf_msg_fragments, + {"Message Fragments", "tcpcl.msg.fragments", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment, + {"Message Fragment", "tcpcl.msg.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_overlap, + {"Message fragment overlap", "tcpcl.msg.fragment.overlap", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_overlap_conflicts, + {"Message fragment overlapping with conflicting data", + "tcpcl.msg.fragment.overlap.conflicts", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_multiple_tails, + {"Message has multiple tails", "tcpcl.msg.fragment.multiple_tails", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_too_long_fragment, + {"Message fragment too long", "tcpcl.msg.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_error, + {"Message defragmentation error", "tcpcl.msg.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_fragment_count, + {"Message fragment count", "tcpcl.msg.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_reassembled_in, + {"Reassembled in", "tcpcl.msg.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_msg_reassembled_length, + {"Reassembled DTN length", "tcpcl.msg.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + }; + + static gint *ett_tcpcl[] = { + &ett_tcp_conv, + &ett_tcp_conv_hdr, + &ett_conv_flags, + &ett_contact_hdr_flags, + &ett_shutdown_flags, + &ett_msg_fragment, + &ett_msg_fragments, + }; + + static ei_register_info ei_tcpcl[] = { + { &ei_tcp_convergence_data_flags, + { "tcpcl.data.flags.invalid", PI_PROTOCOL, PI_WARN, "Invalid TCP CL Data Segment Flags", EXPFILL } + }, + { &ei_tcp_convergence_segment_length, + { "tcpcl.data.length.invalid", PI_PROTOCOL, PI_ERROR, "Invalid Data Length", EXPFILL } + }, + { &ei_tcp_convergence_ack_length, + { "tcpcl.ack.length.error", PI_PROTOCOL, PI_WARN, "Ack Length: Error", EXPFILL } + }, + }; + + expert_module_t *expert_tcpcl; + + proto_tcp_conv = proto_register_protocol ("DTN TCP Convergence Layer Protocol", "TCPCL", "tcpcl"); + + proto_register_field_array(proto_tcp_conv, hf_tcpcl, array_length(hf_tcpcl)); + proto_register_subtree_array(ett_tcpcl, array_length(ett_tcpcl)); + expert_tcpcl = expert_register_protocol(proto_tcp_conv); + expert_register_field_array(expert_tcpcl, ei_tcpcl, array_length(ei_tcpcl)); + + reassembly_table_register(&msg_reassembly_table, + &addresses_reassembly_table_functions); + +} + +void +proto_reg_handoff_tcpclv3(void) +{ + dissector_handle_t tcpcl_handle; + + bundle_handle = find_dissector("bundle"); + + tcpcl_handle = create_dissector_handle(dissect_tcpcl, proto_tcp_conv); + dissector_add_uint_with_preference("tcp.port", BUNDLE_PORT, tcpcl_handle); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-tcpclv3.h b/epan/dissectors/packet-tcpclv3.h new file mode 100644 index 0000000000..17db040a2a --- /dev/null +++ b/epan/dissectors/packet-tcpclv3.h @@ -0,0 +1,101 @@ +/* packet-tcpclv3.h + * References: + * RFC 7242: https://tools.ietf.org/html/rfc7242 + * + * Copyright 2006-2007 The MITRE Corporation. + * All Rights Reserved. + * Approved for Public Release; Distribution Unlimited. + * Tracking Number 07-0090. + * + * The US Government will not be charged any license fee and/or royalties + * related to this software. Neither name of The MITRE Corporation; nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef PACKET_TCPCLV3_H +#define PACKET_TCPCLV3_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* TCP Convergence Layer - Message Types */ +#define TCP_CONV_MSG_TYPE_DATA 0x01 +#define TCP_CONV_MSG_TYPE_ACK 0x02 +#define TCP_CONV_MSG_TYPE_KEEP_ALIVE 0x03 +#define TCP_CONV_MSG_TYPE_SHUTDOWN 0x04 + +/* TCP Convergence Layer (3) - Message Types */ +#define TCP_CONVERGENCE_TYPE_MASK 0xf0 +#define TCP_CONVERGENCE_DATA_SEGMENT 0x10 +#define TCP_CONVERGENCE_ACK_SEGMENT 0x20 +#define TCP_CONVERGENCE_REFUSE_BUNDLE 0x30 +#define TCP_CONVERGENCE_KEEP_ALIVE 0x40 +#define TCP_CONVERGENCE_SHUTDOWN 0x50 +#define TCP_CONVERGENCE_LENGTH 0x60 + +/* TCP Convergence Layer - Contact Header Flags */ +#define TCP_CONV_BUNDLE_ACK_FLAG 0x01 +#define TCP_CONV_REACTIVE_FRAG_FLAG 0x02 +#define TCP_CONV_CONNECTOR_RCVR_FLAG 0x04 + +/* TCP Convergence Layer - Data Segment Flags */ +#define TCP_CONVERGENCE_DATA_FLAGS 0x03 +#define TCP_CONVERGENCE_DATA_END_FLAG 0x01 +#define TCP_CONVERGENCE_DATA_START_FLAG 0x02 + +/* TCP Convergence Layer - Shutdown Segment Flags */ +#define TCP_CONVERGENCE_SHUTDOWN_FLAGS 0x03 +#define TCP_CONVERGENCE_SHUTDOWN_REASON 0x02 +#define TCP_CONVERGENCE_SHUTDOWN_DELAY 0x01 + +/* REFUSE-BUNDLE Reason-Codes */ +#define TCP_REFUSE_BUNDLE_REASON_UNKNOWN 0x00 +#define TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE 0x01 +#define TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED 0x02 +#define TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT 0x03 +/* 0x4-0x7 - Unassigned + * 0x8-0xf - Reserved for future Use */ + +/* + * TCP Convergence Layer - Minimum buffer sizes + * For Data Packet require 5 bytes fixed plus + * up to 4 additional for length SDV + */ + +#define TCP_CONV_MIN_DATA_BUFFER 9 + +/* Header Fixed Sizes */ +#define TCP_CONV_HDR_DATA_FIXED_LENGTH 5 +#define TCP_CONV_HDR_ACK_LENGTH 9 +#define TCP_CONV_HDR_KEEP_ALIVE_LENGTH 1 +#define TCP_CONV_HDR_SHUTDOWN_LENGTH 1 + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_TCPCLV3_H */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag new file mode 100644 index 0000000000..4b55f65209 --- /dev/null +++ b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag @@ -0,0 +1,7 @@ +[_ + [7, 11094, 1, [2, [26622, 12070]], [2, [5279, 7390]], [2, [4785, 1111]], [81089243, 993], 52532350140, 1646, 2047, h'55E4'], + [7, 7, 175, 0, 24(h'1B000000013075CD37')], + [10, 5, 89, 0, 24(h'820007')], + [12, 25, 162, 0, 63(h'8101 02 01 82028219149f191cde 84 8201426869 820205 8203426869 820407 8181 8201457468657265')], + [1, 1, 3, 0, 24(h'8201848482F41B000000018BA3F02382F41A3027AC8782F41B000000018DFAF97381F503820282185D18B9821A533D733D190119')] +] diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng new file mode 100644 index 0000000000..1625c19c3f Binary files /dev/null and b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng differ diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag new file mode 100644 index 0000000000..fdb4ade2ae --- /dev/null +++ b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag @@ -0,0 +1,7 @@ +[_ + [7, 11094, 1, [2, [26622, 12070]], [2, [5279, 7390]], [2, [4785, 1111]], [81089243, 993], 52532350140, 1646, 2047, h'55E4'], + [7, 7, 175, 0, 24(h'1B000000013075CD37')], + [10, 5, 89, 0, 24(h'820007')], + [11, 25, 162, 0, 63(h'8101 01 01 82028219149f191cde 83 820105 8202426869 820307 8181 8201457468657265')], + [1, 1, 3, 0, 24(h'8201848482F41B000000018BA3F02382F41A3027AC8782F41B000000018DFAF97381F503820282185D18B9821A533D733D190119')] +] diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng new file mode 100644 index 0000000000..245fecafdb Binary files /dev/null and b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng differ diff --git a/test/suite_dissection.py b/test/suite_dissection.py index 686a23bf59..40fbe53f00 100644 --- a/test/suite_dissection.py +++ b/test/suite_dissection.py @@ -14,6 +14,49 @@ import unittest import fixtures import sys + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_dissect_bpv7(subprocesstest.SubprocessTestCase): + + def test_bpv7_admin_status(self, cmd_tshark, features, dirs, capture_file): + self.assertRun((cmd_tshark, + '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'), + '-Tfields', '-ebpv7.status_rep.identity', + )) + self.assertTrue(self.grepOutput(r'Source: ipn:93.185, DTN Time: 1396536125, Seq: 281')) + + def test_bpv7_bpsec_bib(self, cmd_tshark, features, dirs, capture_file): + self.assertRun((cmd_tshark, + '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'), + '-Tfields', '-ebpsec.asb.ctxid', + )) + self.assertEqual(self.countOutput(r'1'), 1) + + def test_bpv7_bpsec_bib_admin_type(self, cmd_tshark, features, dirs, capture_file): + # BIB doesn't alter payload + self.assertRun((cmd_tshark, + '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'), + '-Tfields', '-ebpv7.admin_rec.type_code', + )) + self.assertEqual(self.countOutput(r'1'), 1) + + def test_bpv7_bpsec_bcb(self, cmd_tshark, features, dirs, capture_file): + self.assertRun((cmd_tshark, + '-r', capture_file('dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng'), + '-Tfields', '-ebpsec.asb.ctxid', + )) + self.assertEqual(self.countOutput(r'2'), 1) + + def test_bpv7_bpsec_bcb_admin_type(self, cmd_tshark, features, dirs, capture_file): + # BCB inhibits payload dissection + self.assertRun((cmd_tshark, + '-r', capture_file('dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng'), + '-Tfields', '-ebpv7.admin_rec.type_code', + )) + self.assertEqual(self.countOutput(r'1'), 0) + + @fixtures.mark_usefixtures('test_env') @fixtures.uses_fixtures class case_dissect_cose(subprocesstest.SubprocessTestCase):