Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-bpv7.c
Line
Count
Source
1
/* packet-bpv7.c
2
 * Routines for Bundle Protocol Version 7 dissection
3
 * References:
4
 *     RFC 9171: https://www.rfc-editor.org/rfc/rfc9171.html
5
 *     https://www.ietf.org/archive/id/draft-ietf-dtn-ipn-update-14.html
6
 *
7
 * Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
8
 *
9
 * Wireshark - Network traffic analyzer
10
 * By Gerald Combs <gerald@wireshark.org>
11
 * Copyright 1998 Gerald Combs
12
 *
13
 * SPDX-License-Identifier: LGPL-2.1-or-later
14
 */
15
#include "config.h"
16
0
#define WS_LOG_DOMAIN "packet-bpv7"
17
18
#include <inttypes.h>
19
20
#include "packet-bpv7.h"
21
#include "packet-cbor.h"
22
#include "epan/wscbor.h"
23
#include <epan/proto.h>
24
#include <epan/packet.h>
25
#include <epan/prefs.h>
26
#include <epan/expert.h>
27
#include <epan/to_str.h>
28
#include <epan/reassemble.h>
29
#include <epan/decode_as.h>
30
#include <epan/proto_data.h>
31
#include <epan/conversation_table.h>
32
#include <epan/conversation_filter.h>
33
#include <epan/stats_tree.h>
34
#include <epan/ftypes/ftypes.h>
35
#include <epan/unit_strings.h>
36
#include <epan/tfs.h>
37
#include <wsutil/crc16.h>
38
#include <wsutil/array.h>
39
#include <wsutil/crc32.h>
40
#include <wsutil/epochs.h>
41
42
void proto_register_bpv7(void);
43
void proto_reg_handoff_bpv7(void);
44
45
/// Protocol column name
46
static const char *const proto_name_bp = "BPv7";
47
static const char *const proto_name_bp_admin = "BPv7 Admin";
48
49
/// Protocol preferences and defaults
50
static bool bp_compute_crc = true;
51
static bool bp_reassemble_payload = true;
52
static bool bp_payload_try_heur = false;
53
54
/// Protocol handles
55
static int proto_bp;
56
static int bp_tap;
57
static int proto_blocktype;
58
static int proto_bp_admin;
59
static int proto_admintype;
60
/// Protocol-level data
61
static bp_history_t *bp_history;
62
63
static dissector_handle_t handle_admin;
64
/// Dissect opaque CBOR data
65
static dissector_handle_t handle_cbor;
66
static dissector_handle_t handle_cborseq;
67
/// Extension sub-dissectors
68
static dissector_table_t eid_dissectors;
69
static dissector_table_t block_dissectors;
70
static dissector_table_t payload_dissectors_dtn_wkssp;
71
static dissector_table_t payload_dissectors_dtn_serv;
72
static dissector_table_t payload_dissectors_ipn_serv;
73
static dissector_table_t admin_dissectors;
74
/// BTSD heuristic
75
static heur_dissector_list_t btsd_heur;
76
77
/// Fragment reassembly
78
static reassembly_table bp_reassembly_table;
79
80
static const range_string iana_eid_schemes[] = {
81
    {0, 0, "Reserved"},
82
    {1, 254, "Unassigned"},
83
    {255, 65535, "Reserved"},
84
    {0x10000, UINT64_MAX, "Private Use"},
85
    {0, 0, NULL},
86
};
87
88
/// Allocations from IANA
89
/// https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml#ipn-scheme-uri-allocator-identifiers
90
static const range_string iana_ipn_alloc_names[] = {
91
    {0, 0, "Default Allocator"},
92
    {974848, 978943, "Example"},
93
    {0x100000000, UINT64_MAX, "Reserved"},
94
    {0,0, NULL},
95
};
96
97
/// Allocations from IANA
98
/// https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml#ipn-scheme-uri-well-known-service-numbers-for-bpv7
99
static const range_string iana_ipn_svc_names[] = {
100
    {0, 0, "Administrative"},
101
    {1, 127, "Private Use"},
102
    {0x0100, 0x7FFF, "Private Use"},
103
    {0xEEE0, 0xEEEF, "Example"},
104
    {0x10000, 0xFFFFFFFF, "Private Use"},
105
    {0x100000000, UINT64_MAX, "Reserved"},
106
    {0,0, NULL},
107
};
108
109
static const val64_string crc_vals[] = {
110
    {BP_CRC_NONE, "None"},
111
    {BP_CRC_16, "CRC-16"},
112
    {BP_CRC_32, "CRC-32C"},
113
    {0, NULL},
114
};
115
116
typedef struct {
117
    /// Type of block
118
    uint64_t type_code;
119
    /// Limit on total count
120
    uint64_t limit;
121
} blocktype_limit;
122
/// Block type count limits
123
static const blocktype_limit blocktype_limits[] = {
124
    {BP_BLOCKTYPE_PAYLOAD, 1},
125
    {BP_BLOCKTYPE_PREV_NODE, 1},
126
    {BP_BLOCKTYPE_BUNDLE_AGE, 1},
127
    {BP_BLOCKTYPE_HOP_COUNT, 1},
128
    // Mandatory last row
129
    {BP_BLOCKTYPE_INVALID, 0},
130
};
131
132
/// Dissection order by block type
133
4.84k
static int blocktype_order(const bp_block_canonical_t *block) {
134
4.84k
    if (block->type_code) {
135
3.55k
        switch (*(block->type_code)) {
136
136
            case BP_BLOCKTYPE_BCB:
137
136
                return -2;
138
126
            case BP_BLOCKTYPE_BIB:
139
126
                return -1;
140
2.75k
            case BP_BLOCKTYPE_PAYLOAD:
141
2.75k
                return 1; // last block
142
534
            default:
143
534
                return 0;
144
3.55k
        }
145
3.55k
    }
146
1.29k
    return 0;
147
4.84k
}
148
149
static const val64_string status_report_reason_vals[] = {
150
    {0, "No additional information"},
151
    {1, "Lifetime expired"},
152
    {2, "Forwarded over unidirectional link"},
153
    {3, "Transmission canceled"},
154
    {4, "Depleted storage"},
155
    {5, "Destination endpoint ID unintelligible"},
156
    {6, "No known route to destination from here"},
157
    {7, "No timely contact with next node on route"},
158
    {8, "Block unintelligible"},
159
    {9, "Hop limit exceeded"},
160
    {10, "Traffic pared"},
161
    {11, "Block unsupported"},
162
    {12, "Missing Security Operation"},
163
    {13, "Unknown Security Operation"},
164
    {14, "Unexpected Security Operation"},
165
    {15, "Failed Security Operation"},
166
    {16, "Conflicting Security Operation"},
167
    {0, NULL},
168
};
169
170
enum {
171
    /// The last bundle in the frame (as a bp_bundle_t*)
172
    PROTO_DATA_BUNDLE = 1,
173
};
174
175
static int hf_bundle_head;
176
static int hf_bundle_break;
177
static int hf_block;
178
179
static int hf_crc_type;
180
static int hf_crc_field_uint16;
181
static int hf_crc_field_uint32;
182
static int hf_crc_status;
183
184
static int hf_time_dtntime;
185
static int hf_time_utctime;
186
187
static int hf_create_ts_time;
188
static int hf_create_ts_seqno;
189
190
static int hf_eid_scheme;
191
static int hf_eid_uri;
192
static int hf_eid_dtn_ssp_code;
193
static int hf_eid_dtn_ssp_text;
194
static int hf_eid_dtn_wkssp;
195
static int hf_eid_dtn_serv;
196
static int hf_eid_ipn_count;
197
static int hf_eid_ipn_alloc;
198
static int hf_eid_ipn_node;
199
static int hf_eid_ipn_fqnn;
200
static int hf_eid_ipn_service;
201
static int hf_eid_ipn_altform;
202
203
static int hf_primary_version;
204
static int hf_primary_bundle_flags;
205
static int hf_primary_bundle_flags_deletion_report;
206
static int hf_primary_bundle_flags_delivery_report;
207
static int hf_primary_bundle_flags_forwarding_report;
208
static int hf_primary_bundle_flags_reception_report;
209
static int hf_primary_bundle_flags_req_status_time;
210
static int hf_primary_bundle_flags_user_app_ack;
211
static int hf_primary_bundle_flags_no_fragment;
212
static int hf_primary_bundle_flags_payload_admin;
213
static int hf_primary_bundle_flags_is_fragment;
214
static int hf_primary_dst_eid;
215
static int hf_primary_dst_uri;
216
static int hf_primary_src_nodeid;
217
static int hf_primary_src_uri;
218
static int hf_primary_srcdst_uri;
219
static int hf_primary_report_nodeid;
220
static int hf_primary_report_uri;
221
static int hf_primary_create_ts;
222
static int hf_primary_lifetime;
223
static int hf_primary_lifetime_exp;
224
static int hf_primary_expire_ts;
225
static int hf_primary_frag_offset;
226
static int hf_primary_total_length;
227
228
static int hf_bundle_ident;
229
static int hf_bundle_first_seen;
230
static int hf_bundle_retrans_seen;
231
static int hf_bundle_seen_time_diff;
232
static int hf_bundle_dst_dtn_srv;
233
static int hf_bundle_dst_ipn_srv;
234
static int hf_bundle_status_ref;
235
236
static int hf_canonical_type_code;
237
static int hf_canonical_block_num;
238
static int hf_canonical_block_flags;
239
static int hf_canonical_block_flags_delete_no_process;
240
static int hf_canonical_block_flags_status_no_process;
241
static int hf_canonical_block_flags_remove_no_process;
242
static int hf_canonical_block_flags_replicate_in_fragment;
243
static int hf_canonical_data_size;
244
static int hf_canonical_data;
245
246
static int hf_previous_node_nodeid;
247
static int hf_previous_node_uri;
248
static int hf_bundle_age_time;
249
static int hf_bundle_age_exp;
250
static int hf_hop_count_limit;
251
static int hf_hop_count_current;
252
253
static int hf_admin_record_type;
254
static int hf_status_rep;
255
static int hf_status_rep_status_info;
256
static int hf_status_assert_val;
257
static int hf_status_assert_time;
258
static int hf_status_rep_received;
259
static int hf_status_rep_forwarded;
260
static int hf_status_rep_delivered;
261
static int hf_status_rep_deleted;
262
static int hf_status_rep_reason_code;
263
static int hf_status_rep_subj_src_nodeid;
264
static int hf_status_rep_subj_src_uri;
265
static int hf_status_rep_subj_ts;
266
static int hf_status_rep_subj_frag_offset;
267
static int hf_status_rep_subj_payload_len;
268
static int hf_status_rep_subj_ident;
269
static int hf_status_rep_subj_ref;
270
static int hf_status_time_diff;
271
272
static int hf_payload_fragments;
273
static int hf_payload_fragment;
274
static int hf_payload_fragment_overlap;
275
static int hf_payload_fragment_overlap_conflicts;
276
static int hf_payload_fragment_multiple_tails;
277
static int hf_payload_fragment_too_long_fragment;
278
static int hf_payload_fragment_error;
279
static int hf_payload_fragment_count;
280
static int hf_payload_reassembled_in;
281
static int hf_payload_reassembled_length;
282
static int hf_payload_reassembled_data;
283
static int ett_payload_fragment;
284
static int ett_payload_fragments;
285
286
/// Field definitions
287
static hf_register_info fields[] = {
288
    {&hf_bundle_head, {"Indefinite Array", "bpv7.bundle_head", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
289
    {&hf_bundle_break, {"Indefinite Break", "bpv7.bundle_break", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
290
291
    {&hf_block, {"Block", "bpv7.block", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
292
293
    {&hf_crc_type, {"CRC Type", "bpv7.crc_type", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(crc_vals), 0x0, NULL, HFILL}},
294
    {&hf_crc_field_uint16, {"CRC Field Integer", "bpv7.crc_field", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
295
    {&hf_crc_field_uint32, {"CRC field Integer", "bpv7.crc_field", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
296
    {&hf_crc_status, {"CRC Status", "bpv7.crc_status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL}},
297
298
    {&hf_time_dtntime, {"DTN Time", "bpv7.time.dtntime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
299
    {&hf_time_utctime, {"UTC Time", "bpv7.time.utctime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL}},
300
301
    {&hf_create_ts_time, {"Time", "bpv7.create_ts.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
302
    {&hf_create_ts_seqno, {"Sequence Number", "bpv7.create_ts.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
303
304
    {&hf_eid_scheme, {"Scheme Code", "bpv7.eid.scheme", FT_UINT64, BASE_DEC | BASE_RANGE_STRING, RVALS(iana_eid_schemes), 0x0, NULL, HFILL}},
305
    {&hf_eid_uri, {"EID as text URI", "bpv7.eid.uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
306
    {&hf_eid_dtn_ssp_code, {"DTN SSP", "bpv7.eid.dtn_ssp_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
307
    {&hf_eid_dtn_ssp_text, {"DTN SSP", "bpv7.eid.dtn_ssp_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
308
    {&hf_eid_dtn_wkssp, {"Well-known SSP", "bpv7.eid.wkssp", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
309
    {&hf_eid_dtn_serv, {"Service Name", "bpv7.eid.serv", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
310
    {&hf_eid_ipn_count, {"IPN Element Count", "bpv7.eid.ipn_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
311
    {&hf_eid_ipn_alloc, {"IPN Allocator", "bpv7.eid.ipn_alloc", FT_UINT64, BASE_DEC | BASE_RANGE_STRING, RVALS(iana_ipn_alloc_names), 0x0, NULL, HFILL}},
312
    {&hf_eid_ipn_node, {"IPN Node Number", "bpv7.eid.ipn_node", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
313
    {&hf_eid_ipn_fqnn, {"IPN Fully-Qualified Node Number", "bpv7.eid.ipn_fqnn", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
314
    {&hf_eid_ipn_service, {"IPN Service Number", "bpv7.eid.ipn_service", FT_UINT64, BASE_DEC | BASE_RANGE_STRING, RVALS(iana_ipn_svc_names), 0x0, NULL, HFILL}},
315
    {&hf_eid_ipn_altform, {"IPN Alternative Form URI", "bpv7.eid.ipn_altform", FT_STRING, BASE_NONE, NULL, 0x0, "The conversion to or from fully-qualified node number form", HFILL}},
316
317
    {&hf_primary_version, {"Version", "bpv7.primary.version", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
318
    {&hf_primary_bundle_flags, {"Bundle Flags", "bpv7.primary.bundle_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
319
    {&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}},
320
    {&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}},
321
    {&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}},
322
    {&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}},
323
    {&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}},
324
    {&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}},
325
    {&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}},
326
    {&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}},
327
    {&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}},
328
    {&hf_primary_dst_eid, {"Destination Endpoint ID", "bpv7.primary.dst_eid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
329
    {&hf_primary_dst_uri, {"Destination URI", "bpv7.primary.dst_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
330
    {&hf_primary_src_nodeid, {"Source Node ID", "bpv7.primary.src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
331
    {&hf_primary_src_uri, {"Source URI", "bpv7.primary.src_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
332
    {&hf_primary_srcdst_uri, {"Source or Destination URI", "bpv7.primary.srcdst_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
333
    {&hf_primary_report_nodeid, {"Report-to Node ID", "bpv7.primary.report_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
334
    {&hf_primary_report_uri, {"Report-to URI", "bpv7.primary.report_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
335
    {&hf_primary_create_ts, {"Creation Timestamp", "bpv7.primary.create_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
336
    {&hf_primary_lifetime, {"Lifetime", "bpv7.primary.lifetime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
337
    {&hf_primary_lifetime_exp, {"Lifetime Expanded", "bpv7.primary.lifetime_exp", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
338
    {&hf_primary_expire_ts, {"Expire Time", "bpv7.primary.expire_time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, "Lifetime duration after the Creation Time", HFILL}},
339
    {&hf_primary_frag_offset, {"Fragment Offset", "bpv7.primary.frag_offset", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_octet_octets), 0x0, NULL, HFILL}},
340
    {&hf_primary_total_length, {"Total Application Data Unit Length", "bpv7.primary.total_len", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_octet_octets), 0x0, NULL, HFILL}},
341
342
    {&hf_bundle_ident, {"Bundle Identity", "bpv7.bundle.identity", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
343
    {&hf_bundle_first_seen, {"First Seen", "bpv7.bundle.first_seen", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_PREV), 0x0, NULL, HFILL}},
344
    {&hf_bundle_retrans_seen, {"Retransmit Seen", "bpv7.bundle.retransmit_seen", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_NEXT), 0x0, NULL, HFILL}},
345
    {&hf_bundle_seen_time_diff, {"Seen Time", "bpv7.bundle.seen_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
346
    {&hf_bundle_dst_dtn_srv, {"Destination Service", "bpv7.bundle.dst_dtn_srv", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
347
    {&hf_bundle_dst_ipn_srv, {"Destination Service", "bpv7.bundle.dst_ipn_srv", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
348
349
    {&hf_bundle_status_ref, {"Status Bundle", "bpv7.bundle.status_ref", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
350
351
    {&hf_canonical_type_code, {"Type Code", "bpv7.canonical.type_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
352
    {&hf_canonical_block_num, {"Block Number", "bpv7.canonical.block_num", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
353
    {&hf_canonical_block_flags, {"Block Flags", "bpv7.canonical.block_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
354
    {&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}},
355
    {&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}},
356
    {&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}},
357
    {&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}},
358
    {&hf_canonical_data_size, {"Block Type-Specific Data Length", "bpv7.canonical.data_length", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_octet_octets), 0x0, NULL, HFILL}},
359
    {&hf_canonical_data, {"Block Type-Specific Data", "bpv7.canonical.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
360
361
    {&hf_payload_fragments,
362
        {"Payload fragments", "bpv7.payload.fragments",
363
        FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
364
    {&hf_payload_fragment,
365
        {"Payload fragment", "bpv7.payload.fragment",
366
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
367
    {&hf_payload_fragment_overlap,
368
        {"Payload fragment overlap", "bpv7.payload.fragment.overlap",
369
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
370
    {&hf_payload_fragment_overlap_conflicts,
371
        {"Payload fragment overlapping with conflicting data",
372
        "bpv7.payload.fragment.overlap.conflicts",
373
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
374
    {&hf_payload_fragment_multiple_tails,
375
        {"Message has multiple tail fragments",
376
        "bpv7.payload.fragment.multiple_tails",
377
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
378
    {&hf_payload_fragment_too_long_fragment,
379
        {"Payload fragment too long", "bpv7.payload.fragment.too_long_fragment",
380
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
381
    {&hf_payload_fragment_error,
382
        {"Payload defragmentation error", "bpv7.payload.fragment.error",
383
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
384
    {&hf_payload_fragment_count,
385
        {"Payload fragment count", "bpv7.payload.fragment.count",
386
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
387
    {&hf_payload_reassembled_in,
388
        {"Reassembled in", "bpv7.payload.reassembled.in",
389
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
390
    {&hf_payload_reassembled_length,
391
        {"Reassembled length", "bpv7.payload.reassembled.length",
392
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
393
    {&hf_payload_reassembled_data,
394
        {"Reassembled data", "bpv7.payload.reassembled.data",
395
        FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
396
397
    {&hf_previous_node_nodeid, {"Previous Node ID", "bpv7.previous_node.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
398
    {&hf_previous_node_uri, {"Previous URI", "bpv7.previous_node.uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
399
400
    {&hf_bundle_age_time, {"Bundle Age", "bpv7.bundle_age.time", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
401
    {&hf_bundle_age_exp, {"Bundle Age Expanded", "bpv7.bundle_age.exp", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
402
403
    {&hf_hop_count_limit, {"Hop Limit", "bpv7.hop_count.limit", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
404
    {&hf_hop_count_current, {"Hop Count", "bpv7.hop_count.current", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
405
406
    {&hf_admin_record_type, {"Record Type Code", "bpv7.admin_rec.type_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
407
408
    {&hf_status_rep, {"Status Report", "bpv7.status_rep", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
409
    {&hf_status_rep_status_info, {"Status Information", "bpv7.status_rep.status_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
410
    {&hf_status_assert_val, {"Status Value", "bpv7.status_assert.val", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}},
411
    {&hf_status_assert_time, {"Status at", "bpv7.status_assert.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
412
    {&hf_status_rep_received, {"Reporting node received bundle", "bpv7.status_rep.received", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
413
    {&hf_status_rep_forwarded, {"Reporting node forwarded bundle", "bpv7.status_rep.forwarded", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
414
    {&hf_status_rep_delivered, {"Reporting node delivered bundle", "bpv7.status_rep.delivered", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
415
    {&hf_status_rep_deleted, {"Reporting node deleted bundle", "bpv7.status_rep.deleted", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
416
    {&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}},
417
    {&hf_status_rep_subj_src_nodeid, {"Subject Source Node ID", "bpv7.status_rep.subj_src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
418
    {&hf_status_rep_subj_src_uri, {"Subject Source URI", "bpv7.status_rep.subj_src_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
419
    {&hf_status_rep_subj_ts, {"Subject Creation Timestamp", "bpv7.status_rep.subj_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
420
    {&hf_status_rep_subj_frag_offset, {"Subject Fragment Offset", "bpv7.status_rep.subj_frag_offset", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
421
    {&hf_status_rep_subj_payload_len, {"Subject Payload Length", "bpv7.status_rep.subj_payload_len", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
422
    {&hf_status_rep_subj_ident, {"Subject Identity", "bpv7.status_rep.identity", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
423
    {&hf_status_rep_subj_ref, {"Subject Bundle", "bpv7.status_rep.subj_ref", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL}},
424
    {&hf_status_time_diff, {"Status Time", "bpv7.status_rep.subj_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
425
};
426
427
static int *const bundle_flags[] = {
428
    &hf_primary_bundle_flags_deletion_report,
429
    &hf_primary_bundle_flags_delivery_report,
430
    &hf_primary_bundle_flags_forwarding_report,
431
    &hf_primary_bundle_flags_reception_report,
432
    &hf_primary_bundle_flags_req_status_time,
433
    &hf_primary_bundle_flags_user_app_ack,
434
    &hf_primary_bundle_flags_no_fragment,
435
    &hf_primary_bundle_flags_payload_admin,
436
    &hf_primary_bundle_flags_is_fragment,
437
    NULL
438
};
439
440
static int *const block_flags[] = {
441
    &hf_canonical_block_flags_remove_no_process,
442
    &hf_canonical_block_flags_delete_no_process,
443
    &hf_canonical_block_flags_status_no_process,
444
    &hf_canonical_block_flags_replicate_in_fragment,
445
    NULL
446
};
447
448
static int ett_bundle;
449
static int ett_bundle_flags;
450
static int ett_block;
451
static int ett_eid;
452
static int ett_eid_ssp;
453
static int ett_time;
454
static int ett_create_ts;
455
static int ett_ident;
456
static int ett_block_flags;
457
static int ett_canonical_data;
458
static int ett_payload;
459
static int ett_admin;
460
static int ett_status_rep;
461
static int ett_status_info;
462
static int ett_status_assert;
463
/// Tree structures
464
static int *ett[] = {
465
    &ett_bundle,
466
    &ett_bundle_flags,
467
    &ett_block,
468
    &ett_eid,
469
    &ett_eid_ssp,
470
    &ett_time,
471
    &ett_create_ts,
472
    &ett_ident,
473
    &ett_block_flags,
474
    &ett_canonical_data,
475
    &ett_payload,
476
    &ett_admin,
477
    &ett_status_rep,
478
    &ett_status_info,
479
    &ett_status_assert,
480
    &ett_payload_fragment,
481
    &ett_payload_fragments,
482
};
483
484
static const fragment_items payload_frag_items = {
485
    /* Fragment subtrees */
486
    &ett_payload_fragment,
487
    &ett_payload_fragments,
488
    /* Fragment fields */
489
    &hf_payload_fragments,
490
    &hf_payload_fragment,
491
    &hf_payload_fragment_overlap,
492
    &hf_payload_fragment_overlap_conflicts,
493
    &hf_payload_fragment_multiple_tails,
494
    &hf_payload_fragment_too_long_fragment,
495
    &hf_payload_fragment_error,
496
    &hf_payload_fragment_count,
497
    /* Reassembled in field */
498
    &hf_payload_reassembled_in,
499
    &hf_payload_reassembled_length,
500
    &hf_payload_reassembled_data,
501
    /* Tag */
502
    "Payload fragments"
503
};
504
505
static expert_field ei_invalid_framing;
506
static expert_field ei_invalid_bp_version;
507
static expert_field ei_eid_struct_invalid;
508
static expert_field ei_eid_scheme_unknown;
509
static expert_field ei_eid_ssp_type_invalid;
510
static expert_field ei_eid_wkssp_unknown;
511
static expert_field ei_eid_ipn_num_invalid;
512
static expert_field ei_block_type_dupe;
513
static expert_field ei_sub_type_unknown;
514
static expert_field ei_sub_partial_decode;
515
static expert_field ei_primary_crc_type;
516
static expert_field ei_crc_type_unknown;
517
static expert_field ei_frag_fields_missing;
518
static expert_field ei_crc_value_missing;
519
static expert_field ei_block_failed_crc;
520
static expert_field ei_block_num_dupe;
521
static expert_field ei_block_payload_index;
522
static expert_field ei_block_payload_num;
523
static expert_field ei_fragment_reassemble_size;
524
static expert_field ei_fragment_tot_mismatch;
525
static expert_field ei_block_sec_bib_tgt;
526
static expert_field ei_block_sec_bcb_tgt;
527
static ei_register_info expertitems[] = {
528
    {&ei_invalid_framing, {"bpv7.invalid_framing", PI_MALFORMED, PI_WARN, "Invalid framing", EXPFILL}},
529
    {&ei_invalid_bp_version, {"bpv7.invalid_bp_version", PI_MALFORMED, PI_ERROR, "Invalid BP version", EXPFILL}},
530
    {&ei_eid_struct_invalid, {"bpv7.eid_struct_invalid", PI_MALFORMED, PI_ERROR, "Invalid EID structure", EXPFILL}},
531
    {&ei_eid_scheme_unknown, {"bpv7.eid_scheme_unknown", PI_UNDECODED, PI_WARN, "Unknown EID scheme code", EXPFILL}},
532
    {&ei_eid_ssp_type_invalid, {"bpv7.eid_ssp_type_invalid", PI_UNDECODED, PI_WARN, "Invalid scheme-specific part major type", EXPFILL}},
533
    {&ei_eid_wkssp_unknown, {"bpv7.eid_wkssp_unknown", PI_UNDECODED, PI_WARN, "Unknown well-known scheme-specific code point", EXPFILL}},
534
    {&ei_eid_ipn_num_invalid, {"bpv7.eid_ipn_num_invalid", PI_PROTOCOL, PI_WARN, "IPN element too large (>=2^32)", EXPFILL}},
535
    {&ei_block_type_dupe, {"bpv7.block_type_dupe", PI_PROTOCOL, PI_WARN, "Too many blocks of this type", EXPFILL}},
536
    {&ei_sub_type_unknown, {"bpv7.sub_type_unknown", PI_UNDECODED, PI_WARN, "Unknown type code", EXPFILL}},
537
    {&ei_sub_partial_decode, {"bpv7.sub_partial_decode", PI_UNDECODED, PI_WARN, "Data not fully dissected", EXPFILL}},
538
    {&ei_primary_crc_type, {"bpv7.primary_crc_type", PI_PROTOCOL, PI_WARN, "Primary block does not have a CRC", EXPFILL}},
539
    {&ei_crc_type_unknown, {"bpv7.crc_type_unknown", PI_UNDECODED, PI_WARN, "Unknown CRC Type code", EXPFILL}},
540
    {&ei_frag_fields_missing, {"bpv7.frag_fields_missing", PI_MALFORMED, PI_ERROR, "Missing Fragmentation Fields", EXPFILL}},
541
    {&ei_crc_value_missing, {"bpv7.crc_value_missing", PI_MALFORMED, PI_ERROR, "Missing CRC Value", EXPFILL}},
542
    {&ei_block_failed_crc, {"bpv7.block_failed_crc", PI_CHECKSUM, PI_WARN, "Block failed CRC", EXPFILL}},
543
    {&ei_block_num_dupe, {"bpv7.block_num_dupe", PI_PROTOCOL, PI_WARN, "Duplicate block number", EXPFILL}},
544
    {&ei_block_payload_index, {"bpv7.block_payload_index", PI_PROTOCOL, PI_WARN, "Payload must be the last block", EXPFILL}},
545
    {&ei_block_payload_num, {"bpv7.block_payload_num", PI_PROTOCOL, PI_WARN, "Invalid payload block number", EXPFILL}},
546
    {&ei_fragment_reassemble_size, {"bpv7.fragment_reassemble_size", PI_REASSEMBLE, PI_ERROR, "Cannot defragment this size (wireshark limitation)", EXPFILL}},
547
    {&ei_fragment_tot_mismatch, {"bpv7.fragment_tot_mismatch", PI_REASSEMBLE, PI_ERROR, "Inconsistent total length between fragments", EXPFILL}},
548
    {&ei_block_sec_bib_tgt, {"bpv7.bpsec.bib_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is an integrity target", EXPFILL}},
549
    {&ei_block_sec_bcb_tgt, {"bpv7.bpsec.bcb_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is a confidentiality target", EXPFILL}},
550
};
551
552
0
int bp_creation_ts_compare(const void *a, const void *b, void *user_data _U_) {
553
0
    const bp_creation_ts_t *ats = a;
554
0
    const bp_creation_ts_t *bts = b;
555
0
    if (ats->abstime.dtntime < bts->abstime.dtntime) {
556
0
        return -1;
557
0
    }
558
0
    else if (ats->abstime.dtntime > bts->abstime.dtntime) {
559
0
        return 1;
560
0
    }
561
562
0
    if (ats->seqno < bts->seqno) {
563
0
        return -1;
564
0
    }
565
0
    else if (ats->seqno > bts->seqno) {
566
0
        return 1;
567
0
    }
568
569
0
    return 0;
570
0
}
571
572
1.35k
bp_eid_t * bp_eid_new(wmem_allocator_t *alloc) {
573
1.35k
    bp_eid_t *obj = wmem_new0(alloc, bp_eid_t);
574
1.35k
    obj->alloc = alloc;
575
1.35k
    clear_address(&(obj->uri));
576
1.35k
    return obj;
577
1.35k
}
578
579
0
void bp_eid_free(bp_eid_t *obj) {
580
0
    wmem_free(obj->alloc, (char *)(obj->uri.data));
581
0
    wmem_free(obj->alloc, (char *)(obj->dtn_wkssp));
582
0
    wmem_free(obj->alloc, (char *)(obj->dtn_serv));
583
0
    wmem_free(obj->alloc, obj->ipn_serv);
584
0
    wmem_free(obj->alloc, obj);
585
0
}
586
587
7
bool bp_eid_equal(const void *a, const void *b) {
588
7
    const bp_eid_t *aobj = a;
589
7
    const bp_eid_t *bobj = b;
590
7
    return addresses_equal(&(aobj->uri), &(bobj->uri));
591
7
}
592
593
450
bp_block_primary_t * bp_block_primary_new(wmem_allocator_t *alloc) {
594
450
    bp_block_primary_t *obj = wmem_new0(alloc, bp_block_primary_t);
595
450
    obj->dst_eid = bp_eid_new(alloc);
596
450
    obj->src_nodeid = bp_eid_new(alloc);
597
450
    obj->rep_nodeid = bp_eid_new(alloc);
598
450
    obj->frag_offset = NULL;
599
450
    obj->total_len = NULL;
600
450
    obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
601
450
    obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
602
450
    return obj;
603
450
}
604
605
0
void bp_block_primary_free(wmem_allocator_t *alloc, bp_block_primary_t *obj) {
606
0
    if (!obj) {
607
0
        return;
608
0
    }
609
0
    bp_eid_free(obj->dst_eid);
610
0
    bp_eid_free(obj->src_nodeid);
611
0
    bp_eid_free(obj->rep_nodeid);
612
0
    wmem_free(alloc, obj->frag_offset);
613
0
    wmem_free(alloc, obj->total_len);
614
0
    wmem_map_destroy(obj->sec.data_i, true, true);
615
0
    wmem_map_destroy(obj->sec.data_c, true, true);
616
0
    wmem_free(alloc, obj);
617
0
}
618
619
17.2k
bp_block_canonical_t * bp_block_canonical_new(wmem_allocator_t *alloc, uint64_t blk_ix) {
620
17.2k
    bp_block_canonical_t *obj = wmem_new0(alloc, bp_block_canonical_t);
621
17.2k
    obj->blk_ix = blk_ix;
622
17.2k
    obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
623
17.2k
    obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
624
17.2k
    return obj;
625
17.2k
}
626
627
438
static uint64_t * uint64_new(wmem_allocator_t *alloc, const uint64_t val) {
628
438
    uint64_t *obj = wmem_new(alloc, uint64_t);
629
438
    *obj = val;
630
438
    return obj;
631
438
}
632
633
450
bp_bundle_t * bp_bundle_new(wmem_allocator_t *alloc) {
634
450
    bp_bundle_t *obj = wmem_new0(alloc, bp_bundle_t);
635
450
    obj->primary = bp_block_primary_new(alloc);
636
450
    obj->blocks = wmem_list_new(alloc);
637
450
    obj->block_nums = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
638
450
    obj->block_types = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
639
450
    return obj;
640
450
}
641
642
0
void bp_bundle_free(wmem_allocator_t *alloc, bp_bundle_t *obj) {
643
0
    bp_bundle_ident_free(alloc, obj->ident);
644
0
    bp_block_primary_free(alloc, obj->primary);
645
0
    wmem_destroy_list(obj->blocks);
646
0
    wmem_free(alloc, obj);
647
0
}
648
649
/** Function to match the GCompareFunc signature.
650
 */
651
8.99k
static int bp_bundle_frameloc_compare(const void *a, const void *b) {
652
8.99k
  const bp_bundle_t *aobj = a;
653
8.99k
  const bp_bundle_t *bobj = b;
654
8.99k
  if (aobj->frame_num < bobj->frame_num) {
655
8.99k
      return -1;
656
8.99k
  }
657
0
  if (aobj->frame_num > bobj->frame_num) {
658
0
      return 1;
659
0
  }
660
0
  if (aobj->layer_num < bobj->layer_num) {
661
0
      return -1;
662
0
  }
663
0
  if (aobj->layer_num > bobj->layer_num) {
664
0
      return 1;
665
0
  }
666
0
  return 0;
667
0
}
668
669
427
bp_bundle_ident_t * bp_bundle_ident_new(wmem_allocator_t *alloc, const bp_eid_t *src, const bp_creation_ts_t *ts, const uint64_t *off, const uint64_t *len) {
670
427
    DISSECTOR_ASSERT(src != NULL);
671
427
    DISSECTOR_ASSERT(ts != NULL);
672
427
    bp_bundle_ident_t *ident = wmem_new(alloc, bp_bundle_ident_t);
673
427
    copy_address_wmem(alloc, &(ident->src), &(src->uri));
674
427
    ident->ts = *ts;
675
427
    ident->frag_offset = off;
676
427
    ident->total_len = len;
677
427
    return ident;
678
427
}
679
680
46
void bp_bundle_ident_free(wmem_allocator_t *alloc, bp_bundle_ident_t *obj) {
681
46
    wmem_free(alloc, obj);
682
46
}
683
684
/** Either both values are defined and equal or both are null.
685
 */
686
2.85k
static bool optional_uint64_equal(const uint64_t *a, const uint64_t *b) {
687
2.85k
    if (a && b) {
688
1.06k
        return (*a == *b);
689
1.06k
    }
690
1.78k
    else {
691
1.78k
        return (a == NULL) && (b == NULL);
692
1.78k
    }
693
2.85k
}
694
695
2.01k
gboolean bp_bundle_ident_equal(const void *a, const void *b) {
696
2.01k
    const bp_bundle_ident_t *aobj = a;
697
2.01k
    const bp_bundle_ident_t *bobj = b;
698
2.01k
    return (
699
2.01k
        addresses_equal(&(aobj->src), &(bobj->src))
700
2.01k
        && (aobj->ts.abstime.dtntime == bobj->ts.abstime.dtntime)
701
2.00k
        && (aobj->ts.seqno == bobj->ts.seqno)
702
2.00k
        && optional_uint64_equal(aobj->frag_offset, bobj->frag_offset)
703
848
        && optional_uint64_equal(aobj->total_len, bobj->total_len)
704
2.01k
    );
705
2.01k
}
706
707
715
unsigned bp_bundle_ident_hash(const void *key) {
708
715
    const bp_bundle_ident_t *obj = key;
709
715
    return (
710
715
        add_address_to_hash(0, &(obj->src))
711
715
        ^ g_int64_hash(&(obj->ts.abstime.dtntime))
712
715
        ^ g_int64_hash(&(obj->ts.seqno))
713
715
    );
714
715
}
715
716
/** Convert DTN time to time delta.
717
 * DTN Time is defined in Section 4.1.6.
718
 *
719
 * @param dtntime Number of milliseconds from an epoch.
720
 * @return The associated absolute time.
721
 */
722
209
static nstime_t dtn_to_delta(const int64_t dtntime) {
723
209
    nstime_t utctime;
724
209
    utctime.secs = dtntime / 1000;
725
209
    utctime.nsecs = 1000000 * (dtntime % 1000);
726
209
    return utctime;
727
209
}
728
729
/** Convert DTN time to absolute time.
730
 * DTN Time is defined in Section 4.1.6.
731
 *
732
 * @param dtntime Number of milliseconds from an epoch.
733
 * @return The associated absolute time.
734
 */
735
2
static nstime_t dtn_to_utctime(const int64_t dtntime) {
736
2
    nstime_t utctime;
737
2
    utctime.secs = EPOCH_DELTA_2000_01_01_00_00_00_UTC + dtntime / 1000;
738
2
    utctime.nsecs = 1000000 * (dtntime % 1000);
739
2
    return utctime;
740
2
}
741
742
/** Label type-field items with sub-dissector name.
743
 * This is similar to using val64_string labels but based on dissector name.
744
 *
745
 * @param type_code The dissected value, which must not be null.
746
 * @param type_dissect The associated sub-dissector, which may be null.
747
 * @param[in,out] item_type The item associated with the type field.
748
 * @param[in,out] item_parent Optional parent item to label.
749
 */
750
static void label_type_field(const uint64_t *type_code, dissector_handle_t type_dissect, proto_item *item_type, proto_item *item_parent)
751
1.38k
{
752
1.38k
    if (!item_type) {
753
0
        return;
754
0
    }
755
1.38k
    const char *type_name = dissector_handle_get_description(type_dissect);
756
1.38k
    if (!type_name) {
757
189
        type_name = "Unknown";
758
189
    }
759
760
1.38k
    proto_item_set_text(item_type, "%s: %s (%" PRIu64 ")", PITEM_HFINFO(item_type)->name, type_name, *type_code);
761
762
1.38k
    if (item_parent) {
763
1.11k
        if (type_name) {
764
1.11k
            proto_item_append_text(item_parent, ": %s", type_name);
765
1.11k
        }
766
0
        else {
767
0
            proto_item_append_text(item_parent, ": Type %" PRIu64, *type_code);
768
0
        }
769
1.11k
    }
770
1.38k
}
771
772
/** Sub-dissector for dtn scheme EID.
773
 */
774
157
static int dissect_eid_dtn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
775
157
    bp_eid_t *eid = (bp_eid_t *)data;
776
157
    DISSECTOR_ASSERT(eid);
777
157
    wmem_allocator_t *alloc_eid = eid->alloc;
778
157
    int offset = 0;
779
157
    char *uribuf = NULL;
780
781
157
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
782
157
    switch (chunk->type_major) {
783
110
        case CBOR_TYPE_UINT: {
784
110
            const uint64_t *ssp_code = wscbor_require_uint64(pinfo->pool, chunk);
785
110
            proto_item *item = proto_tree_add_cbor_uint64(tree, hf_eid_dtn_ssp_code, pinfo, tvb, chunk, ssp_code);
786
787
110
            switch (*ssp_code) {
788
88
                case 0:
789
88
                    eid->dtn_wkssp = wmem_strdup(alloc_eid, "none");
790
88
                    break;
791
22
                default:
792
22
                    expert_add_info(pinfo, item, &ei_eid_wkssp_unknown);
793
22
                    break;
794
110
            }
795
110
            if (eid->dtn_wkssp) {
796
88
                uribuf = wmem_strdup_printf(alloc_eid, "dtn:%s", eid->dtn_wkssp);
797
88
            }
798
110
            break;
799
110
        }
800
20
        case CBOR_TYPE_STRING: {
801
20
            char *ssp = wscbor_require_tstr(pinfo->pool, chunk);
802
20
            proto_tree_add_cbor_tstr(tree, hf_eid_dtn_ssp_text, pinfo, tvb, chunk);
803
20
            uribuf = wmem_strdup_printf(alloc_eid, "dtn:%s", ssp);
804
805
20
            char *path_sep;
806
20
            if ((path_sep = strrchr(ssp, '/')) != NULL) {
807
0
                eid->dtn_serv = wmem_strdup(alloc_eid, path_sep + 1);
808
0
            }
809
20
            else {
810
                // no separator also means no authority part, so it's well-known
811
20
                eid->dtn_wkssp = wmem_strdup(alloc_eid, ssp);
812
20
            }
813
814
20
            wmem_free(pinfo->pool, ssp);
815
20
            break;
816
110
        }
817
27
        default: {
818
27
            expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_eid_ssp_type_invalid);
819
820
27
            offset = chunk->start;
821
27
            wscbor_skip_next_item(pinfo->pool, tvb, &offset);
822
27
            tvbuff_t *sub_tvb = tvb_new_subset_length(tvb, chunk->start, offset);
823
27
            call_dissector(handle_cbor, sub_tvb, pinfo, tree);
824
27
            break;
825
110
        }
826
157
    }
827
154
    if (eid->dtn_wkssp) {
828
108
        proto_item *item = proto_tree_add_string(tree, hf_eid_dtn_wkssp, tvb, 0, offset, eid->dtn_wkssp);
829
108
        proto_item_set_generated(item);
830
108
    }
831
154
    if (eid->dtn_serv) {
832
0
        proto_item *item = proto_tree_add_string(tree, hf_eid_dtn_serv, tvb, 0, offset, eid->dtn_serv);
833
0
        proto_item_set_generated(item);
834
0
    }
835
154
    if (uribuf) {
836
108
        set_address(&(eid->uri), AT_STRINGZ, (int)strlen(uribuf) + 1, uribuf);
837
108
    }
838
839
154
    return offset;
840
157
}
841
842
/** Sub-dissector for ipn scheme EID.
843
 */
844
110
static int dissect_eid_ipn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
845
110
    bp_eid_t *eid = (bp_eid_t *)data;
846
110
    DISSECTOR_ASSERT(eid);
847
110
    wmem_allocator_t *alloc_eid = eid->alloc;
848
110
    int offset = 0;
849
110
    char *uribuf = NULL;
850
851
110
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
852
110
    wscbor_require_array_size(chunk, 2, 3);
853
110
    const uint64_t ssp_count = chunk->head_value;
854
110
    proto_tree_add_cbor_container(tree, hf_eid_ipn_count, pinfo, tvb, chunk);
855
856
110
    if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk)) {
857
        // no deeper processing
858
109
    }
859
1
    else if (ssp_count == 2) {
860
1
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
861
1
        const uint64_t *fqnn = wscbor_require_uint64(pinfo->pool, chunk);
862
1
        proto_tree_add_cbor_uint64(tree, hf_eid_ipn_fqnn, pinfo, tvb, chunk, fqnn);
863
864
1
        uint64_t alloc, node;
865
1
        if (fqnn) {
866
            // decompose qualified node number
867
0
            alloc = *fqnn >> 32;
868
0
            node = *fqnn & UINT32_MAX;
869
0
            proto_item *item_alloc = proto_tree_add_uint64(tree, hf_eid_ipn_alloc, tvb, 0, 0, alloc);
870
0
            proto_item_set_generated(item_alloc);
871
0
            proto_item *item_node = proto_tree_add_uint64(tree, hf_eid_ipn_node, tvb, 0, 0, node);
872
0
            proto_item_set_generated(item_node);
873
0
        }
874
875
1
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
876
1
        eid->ipn_serv = wscbor_require_uint64(alloc_eid, chunk);
877
1
        proto_tree_add_cbor_uint64(tree, hf_eid_ipn_service, pinfo, tvb, chunk, eid->ipn_serv);
878
879
1
        if (fqnn && eid->ipn_serv) {
880
0
            const char *altform = wmem_strdup_printf(pinfo->pool, "ipn:%" PRIu64 ".%" PRIu64 ".%" PRIu64, alloc, node, *(eid->ipn_serv));
881
0
            proto_item *item_alt = proto_tree_add_string(tree, hf_eid_ipn_altform, tvb, 0, offset, altform);
882
0
            proto_item_set_generated(item_alt);
883
884
0
            uribuf = wmem_strdup_printf(alloc_eid, "ipn:%" PRIu64 ".%" PRIu64, *fqnn, *(eid->ipn_serv));
885
0
        }
886
1
    }
887
0
    else if (ssp_count == 3) {
888
0
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
889
0
        const uint64_t *alloc = wscbor_require_uint64(pinfo->pool, chunk);
890
0
        proto_item *item_alloc = proto_tree_add_cbor_uint64(tree, hf_eid_ipn_alloc, pinfo, tvb, chunk, alloc);
891
0
        if (alloc && (*alloc > UINT32_MAX)) {
892
0
            expert_add_info(pinfo, item_alloc, &ei_eid_ipn_num_invalid);
893
0
            alloc = NULL;
894
0
        }
895
896
0
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
897
0
        const uint64_t *node = wscbor_require_uint64(pinfo->pool, chunk);
898
0
        proto_item *item_node = proto_tree_add_cbor_uint64(tree, hf_eid_ipn_node, pinfo, tvb, chunk, node);
899
0
        if (node && (*node > UINT32_MAX)) {
900
0
            expert_add_info(pinfo, item_node, &ei_eid_ipn_num_invalid);
901
0
            node = NULL;
902
0
        }
903
904
0
        uint64_t fqnn;
905
0
        if (alloc && node) {
906
            // compose qualified node number
907
0
            fqnn = (*alloc << 32) | *node;
908
0
            proto_item *item_qnode = proto_tree_add_uint64(tree, hf_eid_ipn_fqnn, tvb, 0, 0, fqnn);
909
0
            proto_item_set_generated(item_qnode);
910
0
        }
911
912
0
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
913
0
        eid->ipn_serv = wscbor_require_uint64(alloc_eid, chunk);
914
0
        proto_tree_add_cbor_uint64(tree, hf_eid_ipn_service, pinfo, tvb, chunk, eid->ipn_serv);
915
916
0
        if (alloc && node && eid->ipn_serv) {
917
0
            const char *altform = wmem_strdup_printf(pinfo->pool, "ipn:%" PRIu64 ".%" PRIu64, fqnn, *(eid->ipn_serv));
918
0
            proto_item *item_alt = proto_tree_add_string(tree, hf_eid_ipn_altform, tvb, 0, offset, altform);
919
0
            proto_item_set_generated(item_alt);
920
921
0
            uribuf = wmem_strdup_printf(alloc_eid, "ipn:%" PRIu64 ".%" PRIu64 ".%" PRIu64, *alloc, *node, *(eid->ipn_serv));
922
0
        }
923
0
    }
924
110
    if (uribuf) {
925
0
        set_address(&(eid->uri), AT_STRINGZ, (int)strlen(uribuf) + 1, uribuf);
926
0
    }
927
110
    else {
928
110
        clear_address(&(eid->uri));
929
110
    }
930
931
110
    return offset;
932
110
}
933
934
1.22k
proto_item * proto_tree_add_cbor_eid(proto_tree *tree, int hfindex, int hfindex_uri, packet_info *pinfo, tvbuff_t *tvb, int *offset, bp_eid_t *eid) {
935
1.22k
    wmem_allocator_t *alloc_eid = wmem_file_scope();
936
1.22k
    proto_item *item_eid = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
937
1.22k
    proto_tree *tree_eid = proto_item_add_subtree(item_eid, ett_eid);
938
1.22k
    const int eid_start = *offset;
939
940
1.22k
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
941
1.22k
    if (!chunk) {
942
0
        proto_item_set_len(item_eid, *offset - eid_start);
943
0
        expert_add_info(pinfo, item_eid, &ei_eid_struct_invalid);
944
0
        return item_eid;
945
0
    }
946
1.22k
    wscbor_require_array_size(chunk, 2, 2);
947
948
1.22k
    chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
949
1.22k
    const uint64_t *scheme = wscbor_require_uint64(alloc_eid, chunk);
950
1.22k
    proto_item *item_scheme = proto_tree_add_cbor_uint64(tree_eid, hf_eid_scheme, pinfo, tvb, chunk, scheme);
951
1.22k
    if (!scheme) {
952
458
        wscbor_skip_next_item(pinfo->pool, tvb, offset);
953
458
        expert_add_info(pinfo, item_eid, &ei_eid_struct_invalid);
954
458
        return item_eid;
955
458
    }
956
957
    // ensure some storage, even if local temporary
958
768
    bp_eid_t *buf = NULL;
959
768
    if (!eid)
960
0
    {
961
0
        eid = buf = bp_eid_new(alloc_eid);
962
0
    }
963
768
    eid->scheme = *scheme;
964
768
    clear_address(&(eid->uri));
965
966
    // Handle SSP in generic way
967
768
    const int ssp_start = *offset;
968
768
    proto_item *item_ssp;
969
768
    proto_tree *tree_ssp = proto_tree_add_subtree(tree_eid, tvb, ssp_start, 0, ett_eid, &item_ssp, "Scheme-specific Part");
970
971
768
    if (!wscbor_skip_next_item(pinfo->pool, tvb, offset)) {
972
24
        expert_add_info(pinfo, item_ssp, &ei_eid_struct_invalid);
973
24
        return item_eid;
974
24
    }
975
744
    const int ssp_len = *offset - ssp_start;
976
744
    tvbuff_t *ssp_tvb = tvb_new_subset_length(tvb, ssp_start, ssp_len);
977
978
744
    dissector_handle_t ssp_dissect = NULL;
979
744
    if (eid->scheme <= UINT32_MAX) {
980
705
        ssp_dissect = dissector_get_uint_handle(eid_dissectors, (uint32_t)(eid->scheme));
981
705
    }
982
983
744
    int sublen;
984
744
    if (ssp_dissect) {
985
267
        label_type_field(&(eid->scheme), ssp_dissect, item_scheme, NULL);
986
267
        sublen = call_dissector_only(ssp_dissect, ssp_tvb, pinfo, tree_ssp, eid);
987
267
    }
988
477
    else {
989
477
        expert_add_info(pinfo, item_scheme, &ei_eid_scheme_unknown);
990
477
        sublen = call_dissector(handle_cbor, ssp_tvb, pinfo, tree_ssp);
991
477
    }
992
744
    if (sublen == 0)
993
0
    {
994
0
        expert_add_info(pinfo, item_ssp, &ei_eid_struct_invalid);
995
0
    }
996
997
744
    if (eid->uri.type == AT_STRINGZ) {
998
108
        const int eid_len = *offset - eid_start;
999
108
        const char *uribuf = (const char *)(eid->uri.data);
1000
1001
        // Generic text URI field
1002
108
        proto_item *item_uri = proto_tree_add_string(tree_eid, hf_eid_uri, tvb, eid_start, eid_len, uribuf);
1003
108
        proto_item_set_generated(item_uri);
1004
1005
        // More specific URI field
1006
108
        item_uri = proto_tree_add_string(tree_eid, hfindex_uri, tvb, eid_start, eid_len, uribuf);
1007
108
        proto_item_set_generated(item_uri);
1008
1009
108
        proto_item_append_text(item_eid, ": %s", uribuf);
1010
108
    }
1011
1012
744
    if (buf) {
1013
0
        bp_eid_free(buf);
1014
0
    }
1015
1016
744
    proto_item_set_len(item_ssp, *offset - ssp_start);
1017
744
    proto_item_set_len(item_eid, *offset - eid_start);
1018
744
    return item_eid;
1019
768
}
1020
1021
static void dissect_dtn_time(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, int *offset, bp_dtn_time_t *out)
1022
8
{
1023
8
    proto_item *item_time = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
1024
8
    proto_tree *tree_time = proto_item_add_subtree(item_time, ett_time);
1025
8
    const int offset_start = *offset;
1026
1027
8
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
1028
8
    if (chunk) {
1029
8
        const uint64_t *dtntime = wscbor_require_uint64(pinfo->pool, chunk);
1030
8
        proto_tree_add_cbor_uint64(tree_time, hf_time_dtntime, pinfo, tvb, chunk, dtntime);
1031
1032
8
        if (dtntime) {
1033
5
            if (out) {
1034
5
                out->dtntime = *dtntime;
1035
5
            }
1036
1037
5
            if (*dtntime > 0) {
1038
2
                const nstime_t utctime = dtn_to_utctime(*dtntime);
1039
2
                proto_item *item_utctime = proto_tree_add_time(tree_time, hf_time_utctime, tvb, chunk->start, chunk->data_length, &utctime);
1040
2
                proto_item_set_generated(item_utctime);
1041
1042
2
                char *time_text = abs_time_to_str(pinfo->pool, &utctime, ABSOLUTE_TIME_UTC, true);
1043
2
                proto_item_append_text(item_time, ": %s", time_text);
1044
1045
2
                if (out) {
1046
2
                    out->utctime = utctime;
1047
2
                }
1048
2
            }
1049
3
            else {
1050
3
                proto_item_append_text(item_time, ": undefined");
1051
3
            }
1052
5
        }
1053
3
        else if (out) {
1054
3
            out->dtntime = 0;
1055
3
            nstime_set_zero(&(out->utctime));
1056
3
        }
1057
8
    }
1058
8
    proto_item_set_len(item_time, *offset - offset_start);
1059
8
}
1060
1061
/** Extract a timestamp.
1062
 *
1063
 * @param tree The tree to write items under.
1064
 * @param hfindex The root item field.
1065
 * @param pinfo Packet info to update.
1066
 * @param tvb Buffer to read from.
1067
 * @param[in,out] offset Starting offset within @c tvb.
1068
 * @param[out] ts If non-null, the timestamp to write to.
1069
 */
1070
static void dissect_cbor_timestamp(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, int *offset, bp_creation_ts_t *ts)
1071
381
{
1072
381
    proto_item *item_ts = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
1073
381
    proto_tree *tree_ts = proto_item_add_subtree(item_ts, ett_create_ts);
1074
1075
381
    wscbor_chunk_t *chunk_ts = wscbor_chunk_read(pinfo->pool, tvb, offset);
1076
381
    wscbor_require_array_size(chunk_ts, 2, 2);
1077
381
    wscbor_chunk_mark_errors(pinfo, item_ts, chunk_ts);
1078
381
    if (!wscbor_skip_if_errors(pinfo->pool, tvb, offset, chunk_ts)) {
1079
8
        bp_dtn_time_t abstime;
1080
8
        dissect_dtn_time(tree_ts, hf_create_ts_time, pinfo, tvb, offset, &abstime);
1081
1082
8
        wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
1083
8
        const uint64_t *seqno = wscbor_require_uint64(wmem_file_scope(), chunk);
1084
8
        proto_tree_add_cbor_uint64(tree_ts, hf_create_ts_seqno, pinfo, tvb, chunk, seqno);
1085
1086
8
        if (ts) {
1087
7
            ts->abstime = abstime;
1088
7
            ts->seqno = (seqno ? *seqno : 0);
1089
7
        }
1090
8
    }
1091
381
    proto_item_set_len(item_ts, *offset - chunk_ts->start);
1092
381
}
1093
1094
/** Show read-in and actual CRC information.
1095
 *
1096
 * @param tvb The single-block data.
1097
 * @param crc_type Type of CRC to compute.
1098
 * @param crc_field The read-in field value.
1099
 */
1100
64
static void show_crc_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block, const uint64_t *crc_type, tvbuff_t *crc_field) {
1101
64
    if (!crc_type || !crc_field) {
1102
53
        return;
1103
53
    }
1104
1105
    // Display the data field information
1106
11
    int hf_crc_field;
1107
11
    switch (*crc_type) {
1108
7
        case BP_CRC_16:
1109
7
            hf_crc_field = hf_crc_field_uint16;
1110
7
            break;
1111
4
        case BP_CRC_32:
1112
4
            hf_crc_field = hf_crc_field_uint32;
1113
4
            break;
1114
0
        default:
1115
0
            hf_crc_field = -1;
1116
0
            break;
1117
11
    }
1118
1119
    // Compare against expected result
1120
11
    uint32_t crc_actual = 0;
1121
11
    unsigned chksum_flags = PROTO_CHECKSUM_NO_FLAGS;
1122
11
    if (bp_compute_crc) {
1123
11
        if (*crc_type == BP_CRC_NONE) {
1124
0
            chksum_flags |= PROTO_CHECKSUM_NOT_PRESENT;
1125
0
        }
1126
11
        else {
1127
11
            const unsigned block_len = tvb_reported_length(tvb);
1128
11
            uint8_t *crcbuf = tvb_memdup(pinfo->pool, tvb, 0, block_len);
1129
11
            switch (*crc_type) {
1130
7
                case BP_CRC_16:
1131
7
                    memset(crcbuf + block_len - 2, 0, 2);
1132
7
                    crc_actual = crc16_ccitt(crcbuf, block_len);
1133
7
                    break;
1134
3
                case BP_CRC_32:
1135
3
                    memset(crcbuf + block_len - 4, 0, 4);
1136
3
                    crc_actual = ~crc32c_calculate_no_swap(crcbuf, block_len, CRC32C_PRELOAD);
1137
3
                    break;
1138
0
                default:
1139
0
                    break;
1140
11
            }
1141
10
            wmem_free(pinfo->pool, crcbuf);
1142
1143
10
            chksum_flags |= PROTO_CHECKSUM_VERIFY;
1144
10
        }
1145
11
    }
1146
10
    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);
1147
10
}
1148
1149
381
static proto_item * proto_tree_add_ident(packet_info *pinfo, proto_tree *tree, int hfindex, tvbuff_t *tvb, const bp_bundle_ident_t *ident) {
1150
381
    wmem_strbuf_t *ident_text = wmem_strbuf_new(pinfo->pool, NULL);
1151
381
    wmem_strbuf_append_printf(
1152
381
        ident_text,
1153
381
        "Source: %s, DTN Time: %" PRIu64 ", Seq: %" PRIu64,
1154
381
        address_to_name(&(ident->src)),
1155
381
        ident->ts.abstime.dtntime,
1156
381
        ident->ts.seqno
1157
381
    );
1158
381
    if (ident->frag_offset) {
1159
87
        wmem_strbuf_append_printf(ident_text, ", Frag Offset: %" PRIu64, *(ident->frag_offset));
1160
87
    }
1161
381
    if (ident->total_len) {
1162
90
        wmem_strbuf_append_printf(ident_text, ", Total Length: %" PRIu64, *(ident->total_len));
1163
90
    }
1164
1165
381
    proto_item *item_ident = proto_tree_add_string(tree, hfindex, tvb, 0, 0, wmem_strbuf_get_str(ident_text));
1166
381
    proto_item_set_generated(item_ident);
1167
381
    wmem_strbuf_finalize(ident_text);
1168
381
    return item_ident;
1169
381
}
1170
1171
1172
static int dissect_block_primary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block,
1173
                                  int start, bp_block_primary_t *block,
1174
446
                                  bp_bundle_t *bundle _U_) {
1175
446
    proto_item *item_block = proto_tree_get_parent(tree_block);
1176
446
    int field_ix = 0;
1177
446
    int offset = start;
1178
446
    block->item_block = item_block;
1179
1180
446
    wscbor_chunk_t *chunk_block = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1181
446
    wscbor_require_array_size(chunk_block, 8, 11);
1182
446
    wscbor_chunk_mark_errors(pinfo, item_block, chunk_block);
1183
446
    if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_block)) {
1184
20
        return offset - start;
1185
20
    }
1186
1187
426
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1188
426
    const uint64_t *version = wscbor_require_uint64(pinfo->pool, chunk);
1189
426
    proto_item *item_version = proto_tree_add_cbor_uint64(tree_block, hf_primary_version, pinfo, tvb, chunk, version);
1190
426
    field_ix++;
1191
426
    if (version && (*version != 7)) {
1192
0
        expert_add_info(pinfo, item_version, &ei_invalid_bp_version);
1193
0
    }
1194
1195
426
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1196
426
    const uint64_t *flags = wscbor_require_uint64(pinfo->pool, chunk);
1197
426
    proto_tree_add_cbor_bitmask(tree_block, hf_primary_bundle_flags, ett_bundle_flags, bundle_flags, pinfo, tvb, chunk, flags);
1198
426
    field_ix++;
1199
426
    block->flags = (flags ? *flags : 0);
1200
1201
426
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1202
426
    uint64_t *crc_type = wscbor_require_uint64(pinfo->pool, chunk);
1203
426
    proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type);
1204
426
    field_ix++;
1205
426
    block->crc_type = (crc_type ? *crc_type : BP_CRC_NONE);
1206
426
    if (crc_type) {
1207
170
        proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str_wmem(pinfo->pool, *crc_type, crc_vals, "%" PRIu64));
1208
170
    }
1209
1210
426
    proto_tree_add_cbor_eid(tree_block, hf_primary_dst_eid, hf_primary_dst_uri, pinfo, tvb, &offset, block->dst_eid);
1211
426
    field_ix++;
1212
426
    if (block->dst_eid->uri.type != AT_NONE) {
1213
69
        proto_item_set_hidden(
1214
69
            proto_tree_add_string(tree_block, hf_primary_srcdst_uri, tvb, 0, 0, address_to_name(&(block->dst_eid->uri)))
1215
69
        );
1216
69
    }
1217
1218
426
    proto_tree_add_cbor_eid(tree_block, hf_primary_src_nodeid, hf_primary_src_uri, pinfo, tvb, &offset, block->src_nodeid);
1219
426
    field_ix++;
1220
426
    if (block->src_nodeid->uri.type != AT_NONE) {
1221
25
        proto_item_set_hidden(
1222
25
            proto_tree_add_string(tree_block, hf_primary_srcdst_uri, tvb, 0, 0, address_to_name(&(block->src_nodeid->uri)))
1223
25
        );
1224
25
    }
1225
1226
426
    proto_tree_add_cbor_eid(tree_block, hf_primary_report_nodeid, hf_primary_report_uri, pinfo, tvb, &offset, block->rep_nodeid);
1227
426
    field_ix++;
1228
1229
    // Complex type
1230
426
    dissect_cbor_timestamp(tree_block, hf_primary_create_ts, pinfo, tvb, &offset, &(block->ts));
1231
426
    field_ix++;
1232
1233
426
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1234
426
    const uint64_t *lifetime = wscbor_require_uint64(pinfo->pool, chunk);
1235
426
    proto_tree_add_cbor_uint64(tree_block, hf_primary_lifetime, pinfo, tvb, chunk, lifetime);
1236
426
    if (lifetime) {
1237
209
        nstime_t lifetime_exp = dtn_to_delta(*lifetime);
1238
209
        proto_item *item_lifetime_exp = proto_tree_add_time(tree_block, hf_primary_lifetime_exp, tvb, chunk->start, chunk->head_length, &lifetime_exp);
1239
209
        proto_item_set_generated(item_lifetime_exp);
1240
1241
        // zero DTN Time is a sentinel value for "undefined"
1242
209
        if (block->ts.abstime.dtntime > 0) {
1243
1
            nstime_t expiretime;
1244
1
            nstime_sum(&expiretime, &(block->ts.abstime.utctime), &lifetime_exp);
1245
1
            proto_item *item_expiretime = proto_tree_add_time(tree_block, hf_primary_expire_ts, tvb, 0, 0, &expiretime);
1246
1
            proto_item_set_generated(item_expiretime);
1247
1
        }
1248
209
    }
1249
426
    field_ix++;
1250
1251
    // optional items
1252
426
    if (flags && (*flags & BP_BUNDLE_IS_FRAGMENT)) {
1253
137
        if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 3)) {
1254
2
            expert_add_info(pinfo, item_block, &ei_frag_fields_missing);
1255
1256
            // Skip whole array
1257
2
            offset = start;
1258
2
            wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1259
1260
2
            return offset - start;
1261
2
        }
1262
1263
135
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1264
135
        block->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk);
1265
135
        proto_tree_add_cbor_uint64(tree_block, hf_primary_frag_offset, pinfo, tvb, chunk, block->frag_offset);
1266
135
        field_ix++;
1267
1268
135
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1269
135
        block->total_len = wscbor_require_uint64(wmem_file_scope(), chunk);
1270
135
        proto_tree_add_cbor_uint64(tree_block, hf_primary_total_length, pinfo, tvb, chunk, block->total_len);
1271
135
        field_ix++;
1272
135
    }
1273
1274
424
    switch (block->crc_type) {
1275
294
        case BP_CRC_NONE:
1276
294
            expert_add_info(pinfo, item_crc_type, &ei_primary_crc_type);
1277
294
            break;
1278
28
        case BP_CRC_16:
1279
59
        case BP_CRC_32: {
1280
59
            if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) {
1281
25
                expert_add_info(pinfo, item_block, &ei_crc_value_missing);
1282
1283
                // Skip whole array
1284
25
                offset = start;
1285
25
                wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1286
1287
25
                return offset - start;
1288
25
            }
1289
1290
34
            chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1291
34
            tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk);
1292
34
            field_ix++;
1293
34
            block->crc_field = crc_field;
1294
1295
34
            tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start);
1296
34
            show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field);
1297
34
            break;
1298
59
        }
1299
9
        default:
1300
9
            expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown);
1301
9
            break;
1302
424
    }
1303
1304
334
    return offset - start;
1305
424
}
1306
1307
static int dissect_block_canonical(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block,
1308
                                    int start, bp_block_canonical_t *block,
1309
17.2k
                                    bp_bundle_t *bundle _U_) {
1310
17.2k
    proto_item *item_block = proto_tree_get_parent(tree_block);
1311
17.2k
    int field_ix = 0;
1312
17.2k
    int offset = start;
1313
17.2k
    block->item_block = item_block;
1314
1315
17.2k
    wscbor_chunk_t *chunk_block = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1316
17.2k
    wscbor_require_array_size(chunk_block, 5, 6);
1317
17.2k
    wscbor_chunk_mark_errors(pinfo, item_block, chunk_block);
1318
17.2k
    if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_block)) {
1319
15.6k
        return offset - start;
1320
15.6k
    }
1321
#if 0
1322
    proto_item_append_text(item_block, ", Items: %" PRIu64, chunk_block->head_value);
1323
#endif
1324
1325
1.58k
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1326
1.58k
    uint64_t *type_code = wscbor_require_uint64(wmem_file_scope(), chunk);
1327
1.58k
    proto_item *item_type = proto_tree_add_cbor_uint64(tree_block, hf_canonical_type_code, pinfo, tvb, chunk, type_code);
1328
1.58k
    field_ix++;
1329
1.58k
    block->type_code = type_code;
1330
1331
1.58k
    if (type_code) {
1332
1.11k
        dissector_handle_t type_dissect = dissector_get_custom_table_handle(block_dissectors, type_code);
1333
1.11k
        label_type_field(type_code, type_dissect, item_type, item_block);
1334
1335
        // Check duplicate of this type
1336
1.11k
        uint64_t limit = UINT64_MAX;
1337
2.45k
        for (int ix = 0; ; ++ix) {
1338
2.45k
            const blocktype_limit *row = blocktype_limits + ix;
1339
2.45k
            if (row->type_code == BP_BLOCKTYPE_INVALID) {
1340
324
                break;
1341
324
            }
1342
2.12k
            if (row->type_code == *type_code) {
1343
790
                limit = row->limit;
1344
790
                break;
1345
790
            }
1346
2.12k
        }
1347
1348
1.11k
        uint64_t count = 1; // this block counts regardless of presence in the map
1349
1.11k
        wmem_list_t *list_found = wmem_map_lookup(bundle->block_types, type_code);
1350
1.11k
        if (list_found) {
1351
9.26k
            for (wmem_list_frame_t *it = wmem_list_head(list_found); it;
1352
8.40k
                    it = wmem_list_frame_next(it)) {
1353
8.40k
                bp_block_canonical_t *block_found = wmem_list_frame_data(it);
1354
8.40k
                if (block == block_found) {
1355
0
                    continue;
1356
0
                }
1357
8.40k
                ++count;
1358
8.40k
            }
1359
853
        }
1360
1.11k
        if (count > limit) {
1361
            // First non-identical block triggers the error
1362
664
            expert_add_info(pinfo, item_type, &ei_block_type_dupe);
1363
664
        }
1364
1.11k
    }
1365
1366
1.58k
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1367
1.58k
    uint64_t *block_num = wscbor_require_uint64(wmem_file_scope(), chunk);
1368
1.58k
    proto_item *item_block_num = proto_tree_add_cbor_uint64(tree_block, hf_canonical_block_num, pinfo, tvb, chunk, block_num);
1369
1.58k
    field_ix++;
1370
1.58k
    block->block_number = block_num;
1371
1.58k
    if (block_num) {
1372
1.08k
        proto_item_append_text(item_block, ", Block Num: %" PRIu64, *block_num);
1373
1.08k
    }
1374
1375
1.58k
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1376
1.58k
    const uint64_t *flags = wscbor_require_uint64(wmem_file_scope(), chunk);
1377
1.58k
    proto_tree_add_cbor_bitmask(tree_block, hf_canonical_block_flags, ett_block_flags, block_flags, pinfo, tvb, chunk, flags);
1378
1.58k
    field_ix++;
1379
1.58k
    block->flags = (flags ? *flags : 0);
1380
1381
1.58k
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1382
1.58k
    uint64_t *crc_type = wscbor_require_uint64(wmem_file_scope(), chunk);
1383
1.58k
    proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type);
1384
1.58k
    field_ix++;
1385
1.58k
    block->crc_type = (crc_type ? *crc_type : BP_CRC_NONE);
1386
1.58k
    if (crc_type) {
1387
1.12k
        proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str_wmem(pinfo->pool, *crc_type, crc_vals, "%" PRIu64));
1388
1.12k
    }
1389
1390
1.58k
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1391
1.58k
    block->btsd_len = (chunk->head_value < INT_MAX) ? (int)(chunk->head_value) : INT_MAX;
1392
1.58k
    tvbuff_t *tvb_data = wscbor_require_bstr(wmem_file_scope(), chunk);
1393
1.58k
    field_ix++;
1394
1.58k
    block->data = tvb_data;
1395
1396
1.58k
    proto_item_set_generated(
1397
1.58k
        proto_tree_add_cbor_strlen(tree_block, hf_canonical_data_size, pinfo, tvb, chunk)
1398
1.58k
    );
1399
1.58k
    proto_item *item_data = proto_tree_add_cbor_bstr(tree_block, hf_canonical_data, pinfo, tvb, chunk);
1400
1.58k
    proto_tree *tree_data = proto_item_add_subtree(item_data, ett_canonical_data);
1401
1.58k
    block->tree_data = tree_data;
1402
1403
1.58k
    switch (block->crc_type) {
1404
1.27k
        case BP_CRC_NONE:
1405
1.27k
            break;
1406
67
        case BP_CRC_16:
1407
111
        case BP_CRC_32: {
1408
111
            if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) {
1409
                // Skip whole array
1410
80
                offset = start;
1411
80
                wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1412
1413
80
                return offset - start;
1414
80
            }
1415
1416
31
            chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1417
31
            tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk);
1418
31
            field_ix++;
1419
31
            block->crc_field = crc_field;
1420
1421
31
            tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start);
1422
31
            show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field);
1423
31
            break;
1424
111
        }
1425
143
        default:
1426
143
            expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown);
1427
143
            break;
1428
1.58k
    }
1429
1430
1.45k
    wmem_list_append(bundle->blocks, block);
1431
1432
1.45k
    if (block->type_code) {
1433
1.05k
        wmem_list_t *type_list = wmem_map_lookup(bundle->block_types, block->type_code);
1434
1.05k
        if (!type_list) {
1435
247
            uint64_t *key = uint64_new(wmem_file_scope(), *(block->type_code));
1436
247
            type_list = wmem_list_new(wmem_file_scope());
1437
247
            wmem_map_insert(bundle->block_types, key, type_list);
1438
247
        }
1439
1.05k
        wmem_list_append(type_list, block);
1440
1.05k
    }
1441
1.45k
    if (block->block_number) {
1442
1.00k
        bp_block_canonical_t *found = wmem_map_lookup(bundle->block_nums, block->block_number);
1443
1.00k
        if (found) {
1444
813
            expert_add_info(pinfo, item_block_num, &ei_block_num_dupe);
1445
813
        }
1446
191
        else {
1447
191
            uint64_t *key = uint64_new(wmem_file_scope(), *(block->block_number));
1448
191
            wmem_map_insert(bundle->block_nums, key, block);
1449
191
        }
1450
1.00k
    }
1451
    // Payload block requirements
1452
1.45k
    if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1453
        // must have index zero
1454
718
        if (block->block_number && (*(block->block_number) != 1)) {
1455
633
            expert_add_info(pinfo, item_block_num, &ei_block_payload_num);
1456
633
        }
1457
718
    }
1458
1459
1.45k
    return offset - start;
1460
1.58k
}
1461
1462
typedef struct {
1463
    packet_info *pinfo;
1464
    proto_item *pi;
1465
    expert_field *eiindex;
1466
    const char *sectype;
1467
} bpsec_block_mark_t;
1468
/// Mark blocks with BPSec expert info
1469
0
static void mark_target_block(void *key, void *value _U_, void *user_data) {
1470
0
    const uint64_t *blk_num = (uint64_t *)key;
1471
0
    const bpsec_block_mark_t *mark = (bpsec_block_mark_t *)user_data;
1472
0
    expert_add_info_format(
1473
0
        mark->pinfo, mark->pi, mark->eiindex,
1474
0
        "Block is targed by %s block number %" PRIu64, mark->sectype, *blk_num
1475
0
    );
1476
0
}
1477
1.30k
static void apply_bpsec_mark(const security_mark_t *sec, packet_info *pinfo, proto_item *pi) {
1478
1.30k
    {
1479
1.30k
        bpsec_block_mark_t mark;
1480
1.30k
        mark.pinfo = pinfo;
1481
1.30k
        mark.pi = pi;
1482
1.30k
        mark.eiindex = &ei_block_sec_bib_tgt;
1483
1.30k
        mark.sectype = "BIB";
1484
1.30k
        wmem_map_foreach(sec->data_i, mark_target_block, &mark);
1485
1.30k
    }
1486
1.30k
    {
1487
1.30k
        bpsec_block_mark_t mark;
1488
1.30k
        mark.pinfo = pinfo;
1489
1.30k
        mark.pi = pi;
1490
1.30k
        mark.eiindex = &ei_block_sec_bcb_tgt;
1491
1.30k
        mark.sectype = "BCB";
1492
1.30k
        wmem_map_foreach(sec->data_c, mark_target_block, &mark);
1493
1.30k
    }
1494
1.30k
}
1495
1496
/** Extract data from a block (including payload and admin).
1497
 *
1498
 * @param dissector The optional dissector to call.
1499
 * @param context Context for the @c dissector.
1500
 * @param tvb Buffer to read from.
1501
 * @param pinfo Packet info to update.
1502
 * @param tree The tree to write items under.
1503
 * @param type_exp True if the type code is in the private/experimental range.
1504
 * @return The number of dissected octets.
1505
 */
1506
245
static int dissect_carried_data(dissector_handle_t dissector, void *context, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool type_exp) {
1507
245
    int sublen = 0;
1508
245
    if (dissector) {
1509
148
        sublen = call_dissector_only(dissector, tvb, pinfo, tree, context);
1510
148
        if ((sublen < 0) ||
1511
126
            ((sublen > 0) && ((unsigned)sublen < tvb_reported_length(tvb)))) {
1512
25
            expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_partial_decode);
1513
25
        }
1514
148
    }
1515
97
    else if (!type_exp) {
1516
24
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_type_unknown);
1517
24
    }
1518
1519
245
    if ((sublen <= 0) && bp_payload_try_heur) {
1520
0
        heur_dtbl_entry_t *entry = NULL;
1521
0
        if (dissector_try_heuristic(btsd_heur, tvb, pinfo, tree, &entry, context)) {
1522
0
            sublen = tvb_reported_length(tvb);
1523
0
        }
1524
0
    }
1525
245
    if (sublen == 0) {
1526
206
        sublen = call_data_dissector(tvb, pinfo, tree);
1527
206
    }
1528
245
    return sublen;
1529
245
}
1530
1531
/** Handle iteration over status subject set.
1532
 *
1533
 */
1534
0
static void show_status_subj_ref(void *key, void *val _U_, void *data) {
1535
0
    bp_bundle_ident_t *status_ident = key;
1536
0
    proto_tree *tree_bundle = data;
1537
0
    const wmem_list_t *subj_list = wmem_map_lookup(bp_history->bundles, status_ident);
1538
0
    const wmem_list_frame_t *subj_it = subj_list ? wmem_list_head(subj_list) : NULL;
1539
0
    const bp_bundle_t *subj_found = subj_it ? wmem_list_frame_data(subj_it) : NULL;
1540
0
    if (subj_found) {
1541
0
        proto_item *item_subj_ref = proto_tree_add_uint(tree_bundle, hf_bundle_status_ref, NULL, 0, 0, subj_found->frame_num);
1542
0
        proto_item_set_generated(item_subj_ref);
1543
0
    }
1544
0
}
1545
1546
/// Stable sort, preserving relative order of same priority
1547
2.42k
static int block_dissect_sort(const void *a, const void *b) {
1548
2.42k
    DISSECTOR_ASSERT(a && b);
1549
2.42k
    const bp_block_canonical_t *aobj = *(bp_block_canonical_t **)a;
1550
2.42k
    const bp_block_canonical_t *bobj = *(bp_block_canonical_t **)b;
1551
2.42k
    const int aord = blocktype_order(aobj);
1552
2.42k
    const int bord = blocktype_order(bobj);
1553
2.42k
    if (aord < bord) {
1554
156
        return -1;
1555
156
    }
1556
2.26k
    else if (aord > bord) {
1557
367
        return 1;
1558
367
    }
1559
1560
1.90k
    return g_int_equal(&(aobj->blk_ix), &(bobj->blk_ix));
1561
2.42k
}
1562
1563
/// Top-level protocol dissector
1564
450
static int dissect_bp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1565
450
    {
1566
450
        const char *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL);
1567
450
        if (g_strcmp0(proto_name, proto_name_bp) != 0) {
1568
450
            col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp);
1569
450
            col_clear(pinfo->cinfo, COL_INFO);
1570
450
        }
1571
450
    }
1572
450
    int offset = 0;
1573
1574
450
    proto_item *item_bundle = proto_tree_add_item(tree, proto_bp, tvb, 0, -1, ENC_NA);
1575
450
    proto_tree *tree_bundle = proto_item_add_subtree(item_bundle, ett_bundle);
1576
1577
450
    bp_bundle_t *bundle = bp_bundle_new(wmem_file_scope());
1578
450
    bundle->frame_num = pinfo->num;
1579
450
    bundle->layer_num = pinfo->curr_layer_num;
1580
450
    bundle->frame_time = pinfo->abs_ts;
1581
1582
    // Read blocks directly from buffer with same addresses as #tvb
1583
450
    const unsigned buflen = tvb_reported_length(tvb);
1584
1585
    // Require indefinite-length array type
1586
450
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1587
450
    proto_item *item_head = proto_tree_add_item(tree_bundle, hf_bundle_head, tvb, chunk->start, chunk->data_length, ENC_NA);
1588
450
    wscbor_require_array(chunk);
1589
450
    if (wscbor_chunk_mark_errors(pinfo, item_head, chunk)) {
1590
3
        return 0;
1591
3
    }
1592
447
    else if (chunk->type_minor != 31) {
1593
445
        expert_add_info_format(pinfo, item_head, &ei_invalid_framing, "Expected indefinite length array");
1594
        // continue on even for definite-length array
1595
445
    }
1596
1597
447
    uint64_t block_ix = 0;
1598
17.9k
    while (true) {
1599
17.9k
        if (offset >= (int)buflen) {
1600
99
            proto_item *item_break = proto_tree_add_item(tree_bundle, hf_bundle_break, tvb, offset, -1, ENC_NA);
1601
99
            expert_add_info_format(pinfo, item_break, &ei_invalid_framing, "Array break missing");
1602
99
            break;
1603
99
        }
1604
1605
        // Either detect BREAK or decode block
1606
17.8k
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1607
17.8k
        if (wscbor_is_indefinite_break(chunk)) {
1608
209
            proto_tree_add_cbor_ctrl(tree_bundle, hf_bundle_break, pinfo, tvb, chunk);
1609
209
            break;
1610
209
        }
1611
17.6k
        offset = chunk->start;
1612
1613
        // Load just the array start
1614
17.6k
        const int block_start = offset;
1615
17.6k
        proto_item *item_block = proto_tree_add_item(tree_bundle, hf_block, tvb, block_start, -1, ENC_NA);
1616
17.6k
        proto_tree *tree_block = proto_item_add_subtree(item_block, ett_block);
1617
1618
17.6k
        if (block_ix == 0) {
1619
            // Primary block
1620
446
            proto_item_prepend_text(item_block, "Primary ");
1621
446
            const int sublen = dissect_block_primary(tvb, pinfo, tree_block, offset, bundle->primary, bundle);
1622
446
            if (sublen <= 0) {
1623
0
                break;
1624
0
            }
1625
446
            offset += sublen;
1626
1627
446
            if (!(bundle->ident)) {
1628
381
                bundle->ident = bp_bundle_ident_new(
1629
381
                    wmem_file_scope(),
1630
381
                    bundle->primary->src_nodeid,
1631
381
                    &(bundle->primary->ts),
1632
381
                    bundle->primary->frag_offset,
1633
381
                    bundle->primary->total_len
1634
381
                );
1635
381
                proto_item *item_ident = proto_tree_add_ident(pinfo, tree_bundle, hf_bundle_ident, tvb, bundle->ident);
1636
381
                proto_tree *tree_ident = proto_item_add_subtree(item_ident, ett_ident);
1637
1638
381
                const wmem_list_t *seen_list = wmem_map_lookup(bp_history->bundles, bundle->ident);
1639
381
                wmem_list_frame_t *seen_it = seen_list ? wmem_list_head(seen_list) : NULL;
1640
381
                const bp_bundle_t *seen_first = seen_it ? wmem_list_frame_data(seen_it) : NULL;
1641
                // show first occurrence if not this one
1642
381
                if (seen_first && (seen_first->frame_num != pinfo->num)) {
1643
325
                    proto_item *item_seen = proto_tree_add_uint(tree_ident, hf_bundle_first_seen, tvb, 0, 0, seen_first->frame_num);
1644
325
                    proto_item_set_generated(item_seen);
1645
1646
325
                    nstime_t td;
1647
325
                    nstime_delta(&td, &(bundle->frame_time), &(seen_first->frame_time));
1648
325
                    proto_item *item_td = proto_tree_add_time(tree_ident, hf_bundle_seen_time_diff, tvb, 0, 0, &td);
1649
325
                    proto_item_set_generated(item_td);
1650
325
                }
1651
                // show any retransmits
1652
56
                else if (seen_it) {
1653
0
                    for (seen_it = wmem_list_frame_next(seen_it); seen_it != NULL; seen_it = wmem_list_frame_next(seen_it)) {
1654
0
                        const bp_bundle_t *seen_re = wmem_list_frame_data(seen_it);
1655
0
                        if (seen_re && (seen_re->frame_num != pinfo->num)) {
1656
0
                            proto_item *item_seen = proto_tree_add_uint(tree_ident, hf_bundle_retrans_seen, tvb, 0, 0, seen_re->frame_num);
1657
0
                            proto_item_set_generated(item_seen);
1658
0
                        }
1659
0
                    }
1660
0
                }
1661
1662
                // Indicate related status (may be multiple)
1663
381
                wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, bundle->ident);
1664
381
                if (status_set) {
1665
0
                    wmem_map_foreach(status_set, show_status_subj_ref, tree_ident);
1666
0
                }
1667
381
            }
1668
446
            {
1669
446
                const bp_eid_t *dst = bundle->primary->dst_eid;
1670
446
                if (dst) {
1671
381
                    proto_item *item_dst = NULL;
1672
381
                    if (dst->dtn_serv) {
1673
0
                        item_dst = proto_tree_add_string(tree_bundle, hf_bundle_dst_dtn_srv, tvb, 0, 0, dst->dtn_serv);
1674
0
                    }
1675
381
                    else if (dst->ipn_serv) {
1676
0
                        item_dst = proto_tree_add_uint64(tree_bundle, hf_bundle_dst_ipn_srv, tvb, 0, 0, *(dst->ipn_serv));
1677
0
                    }
1678
381
                    if (item_dst) {
1679
0
                        proto_item_set_generated(item_dst);
1680
0
                    }
1681
381
                }
1682
446
            }
1683
446
        }
1684
17.2k
        else {
1685
            // Non-primary block
1686
17.2k
            proto_item_prepend_text(item_block, "Canonical ");
1687
17.2k
            bp_block_canonical_t *block = bp_block_canonical_new(wmem_file_scope(), block_ix);
1688
17.2k
            const int sublen = dissect_block_canonical(tvb, pinfo, tree_block, offset, block, bundle);
1689
17.2k
            if (sublen <= 0) {
1690
0
                break;
1691
0
            }
1692
17.2k
            offset += sublen;
1693
17.2k
        }
1694
1695
17.6k
        proto_item_set_len(item_block, offset - block_start);
1696
17.6k
        block_ix++;
1697
17.6k
    }
1698
    // all done with PDU
1699
447
    bundle->pdu_len = offset;
1700
1701
447
    { // Extract primary block info first
1702
447
        const bp_block_primary_t *primary = bundle->primary;
1703
1704
447
        proto_item_append_text(item_bundle, ", Src: %s", address_to_name(&(primary->src_nodeid->uri)));
1705
447
        proto_item_append_text(item_bundle, ", Dst: %s", address_to_name(&(primary->dst_eid->uri)));
1706
447
        if (bundle->ident) {
1707
209
            proto_item_append_text(item_bundle, ", Time: %" PRIu64, bundle->ident->ts.abstime.dtntime);
1708
209
            proto_item_append_text(item_bundle, ", Seq: %" PRIu64, bundle->ident->ts.seqno);
1709
209
        }
1710
447
        proto_item_append_text(item_bundle, ", Blocks: %" PRIu64, block_ix);
1711
1712
447
        copy_address_wmem(pinfo->pool, &(pinfo->src), &(primary->src_nodeid->uri));
1713
447
        copy_address_wmem(pinfo->pool, &(pinfo->dst), &(primary->dst_eid->uri));
1714
447
        pinfo->ptype = PT_NONE;
1715
1716
447
        if ((primary->src_nodeid->uri.type != AT_NONE)
1717
14
                && (primary->dst_eid->uri.type != AT_NONE)) {
1718
            // conversation starts in either order
1719
4
            address *addra, *addrb;
1720
4
            if (cmp_address(&(pinfo->src), &(pinfo->dst)) < 0) {
1721
0
                addra = &(pinfo->src);
1722
0
                addrb = &(pinfo->dst);
1723
0
            }
1724
4
            else {
1725
4
                addra = &(pinfo->dst);
1726
4
                addrb = &(pinfo->src);
1727
4
            }
1728
1729
4
            conversation_element_t *conv_el = wmem_alloc_array(pinfo->pool, conversation_element_t, 3);
1730
4
            conv_el[0].type=CE_ADDRESS;
1731
4
            copy_address_shallow(&(conv_el[0].addr_val), addra);
1732
4
            conv_el[1].type=CE_ADDRESS;
1733
4
            copy_address_shallow(&(conv_el[1].addr_val), addrb);
1734
4
            conv_el[2].type=CE_CONVERSATION_TYPE;
1735
4
            conv_el[2].conversation_type_val=CONVERSATION_BP;
1736
1737
4
            pinfo->use_conv_addr_port_endpoints = false;
1738
4
            pinfo->conv_addr_port_endpoints = NULL;
1739
4
            pinfo->conv_elements = conv_el;
1740
4
            find_or_create_conversation(pinfo);
1741
4
        }
1742
447
    }
1743
1744
    // Extract pre-dissection block info
1745
1.60k
    for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1746
1.15k
            it = wmem_list_frame_next(it)) {
1747
1.15k
        bp_block_canonical_t *block = wmem_list_frame_data(it);
1748
1.15k
        if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1749
            // must be last block (i.e. next is NULL)
1750
653
            if (wmem_list_frame_next(it)) {
1751
578
                expert_add_info(pinfo, block->item_block, &ei_block_payload_index);
1752
578
            }
1753
1754
653
            if (block->data) {
1755
141
                bundle->pyld_start = wmem_new(wmem_file_scope(), int);
1756
141
                *(bundle->pyld_start) = tvb_raw_offset(block->data) - tvb_raw_offset(tvb);
1757
141
                bundle->pyld_len = wmem_new(wmem_file_scope(), int);
1758
141
                *(bundle->pyld_len) = tvb_reported_length(block->data);
1759
141
            }
1760
653
        }
1761
1.15k
    }
1762
1763
    // Handle block-type-specific data after all blocks are present
1764
447
    wmem_array_t *sorted = wmem_array_sized_new(
1765
447
        pinfo->pool,  sizeof(bp_block_canonical_t*),
1766
447
        wmem_list_count(bundle->blocks)
1767
447
    );
1768
447
    unsigned ix = 0;
1769
1.60k
    for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1770
1.15k
            it = wmem_list_frame_next(it), ++ix) {
1771
1.15k
        bp_block_canonical_t *block = wmem_list_frame_data(it);
1772
1.15k
        wmem_array_append_one(sorted, block);
1773
1.15k
    }
1774
447
    wmem_array_sort(sorted, block_dissect_sort);
1775
1776
    // Dissect in sorted order (payload last)
1777
1.55k
    for (ix = 0; ix < wmem_array_get_count(sorted); ++ix) {
1778
1.10k
        bp_block_canonical_t *block = *(bp_block_canonical_t **)wmem_array_index(sorted, ix);
1779
1780
        // Show payload block info before BTSD dissection (if possible)
1781
1.10k
        if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1782
642
            if (block->data) {
1783
138
                proto_item_append_text(item_bundle, ", Payload-Size: %d", *(bundle->pyld_len));
1784
138
            }
1785
1786
642
            col_append_str(pinfo->cinfo, COL_INFO, "Payload Size: ");
1787
642
            if (block->data) {
1788
138
                col_append_fstr(pinfo->cinfo, COL_INFO, "%d", *(bundle->pyld_len));
1789
138
            }
1790
504
            else {
1791
504
                col_append_str(pinfo->cinfo, COL_INFO, "N/A");
1792
504
            }
1793
642
            if (wmem_map_size(block->sec.data_c) > 0) {
1794
0
                col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "BPSec BCB target");
1795
0
            }
1796
642
            if (wmem_map_size(block->sec.data_i) > 0) {
1797
0
                col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "BPSec BIB target");
1798
0
            }
1799
1800
642
            const bool is_admin = bundle->primary->flags & BP_BUNDLE_PAYLOAD_ADMIN;
1801
642
            if (is_admin) {
1802
308
                col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Admin. Record");
1803
308
            }
1804
642
        }
1805
1806
        // Ignore when data is absent or is a
1807
        // confidentiality target (i.e. ciphertext)
1808
1.10k
        if (!(block->data) || (wmem_map_size(block->sec.data_c) > 0)) {
1809
937
            continue;
1810
937
        }
1811
1812
        // sub-dissect after all is read
1813
172
        dissector_handle_t data_dissect = NULL;
1814
172
        bool type_exp = false;
1815
172
        if (block->type_code) {
1816
152
            data_dissect = dissector_get_custom_table_handle(block_dissectors, block->type_code);
1817
152
            type_exp = (*(block->type_code) >= 192) && (*(block->type_code) <= 255);
1818
152
        }
1819
1820
172
        bp_dissector_data_t dissect_data;
1821
172
        dissect_data.bundle = bundle;
1822
172
        dissect_data.block = block;
1823
172
        dissect_carried_data(data_dissect, &dissect_data, block->data, pinfo, block->tree_data, type_exp);
1824
172
    }
1825
1826
    // Block-data-derived markings
1827
447
    apply_bpsec_mark(&(bundle->primary->sec), pinfo, bundle->primary->item_block);
1828
1.54k
    for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1829
1.10k
            it = wmem_list_frame_next(it)) {
1830
1.10k
        bp_block_canonical_t *block = wmem_list_frame_data(it);
1831
1.10k
        apply_bpsec_mark(&(block->sec), pinfo, block->item_block);
1832
1.10k
    }
1833
1834
447
    if (bundle->pyld_start && bundle->pyld_len) {
1835
        // Treat payload as non-protocol data
1836
50
        const unsigned trailer_start = *(bundle->pyld_start) + *(bundle->pyld_len);
1837
50
        proto_item_set_len(item_bundle, *(bundle->pyld_start));
1838
50
        proto_tree_set_appendix(tree_bundle, tvb, trailer_start, offset - trailer_start);
1839
50
    }
1840
397
    else {
1841
397
        proto_item_set_len(item_bundle, offset);
1842
397
    }
1843
1844
    // Tap handles after all blocks are dissected
1845
447
    bp_bundle_t *unique_bundle = bundle;
1846
447
    if (bundle->ident) {
1847
        // Keep bundle metadata around for the whole file
1848
207
        wmem_list_t *found_list = wmem_map_lookup(bp_history->bundles, bundle->ident);
1849
207
        if (!found_list) {
1850
31
            found_list = wmem_list_new(wmem_file_scope());
1851
31
            wmem_map_insert(bp_history->bundles, bundle->ident, found_list);
1852
31
        }
1853
1854
207
        const wmem_list_frame_t *found_it = wmem_list_find_custom(found_list, bundle, bp_bundle_frameloc_compare);
1855
207
        bp_bundle_t *found_item = found_it ? wmem_list_frame_data(found_it) : NULL;
1856
207
        if (!found_item) {
1857
207
            wmem_list_append(found_list, bundle);
1858
207
        }
1859
0
        else {
1860
0
            bp_bundle_free(wmem_file_scope(), bundle);
1861
0
            unique_bundle = found_item;
1862
0
        }
1863
207
    }
1864
447
    p_add_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE, unique_bundle);
1865
447
    tap_queue_packet(bp_tap, pinfo, unique_bundle);
1866
1867
447
    return offset;
1868
450
}
1869
1870
static bool dissect_status_assertion(proto_tree *tree, int hfassert, packet_info *pinfo, tvbuff_t *tvb, int *offset)
1871
0
{
1872
0
    proto_item *item_assert = proto_tree_add_item(tree, hfassert, tvb, *offset, -1, ENC_NA);
1873
1874
0
    bool result = false;
1875
1876
0
    wscbor_chunk_t *chunk_assert = wscbor_chunk_read(pinfo->pool, tvb, offset);
1877
0
    wscbor_require_array_size(chunk_assert, 1, 2);
1878
0
    wscbor_chunk_mark_errors(pinfo, item_assert, chunk_assert);
1879
0
    if (!wscbor_skip_if_errors(pinfo->pool, tvb, offset, chunk_assert)) {
1880
0
        proto_tree *tree_assert = proto_item_add_subtree(item_assert, ett_status_assert);
1881
1882
0
        wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
1883
0
        bool *status_val = wscbor_require_boolean(pinfo->pool, chunk);
1884
0
        proto_tree_add_cbor_boolean(tree_assert, hf_status_assert_val, pinfo, tvb, chunk, status_val);
1885
0
        if (status_val) {
1886
0
            result = *status_val;
1887
0
        }
1888
1889
0
        if (chunk_assert->head_value > 1) {
1890
0
            bp_dtn_time_t abstime;
1891
0
            dissect_dtn_time(tree_assert, hf_status_assert_time, pinfo, tvb, offset, &abstime);
1892
0
        }
1893
0
    }
1894
1895
0
    proto_item_set_len(item_assert, *offset - chunk_assert->start);
1896
0
    return result;
1897
0
}
1898
1899
0
static int dissect_payload_admin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
1900
0
    bp_dissector_data_t *context = (bp_dissector_data_t *)data;
1901
0
    DISSECTOR_ASSERT(context);
1902
0
    {
1903
0
        const char *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL);
1904
0
        if (g_strcmp0(proto_name, proto_name_bp_admin) != 0) {
1905
0
            col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp_admin);
1906
0
            col_clear(pinfo->cinfo, COL_INFO);
1907
0
        }
1908
0
    }
1909
0
    proto_item *item_rec = proto_tree_add_item(tree, proto_bp_admin, tvb, 0, -1, ENC_NA);
1910
0
    int offset = 0;
1911
1912
0
    wscbor_chunk_t *chunk_rec = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1913
0
    wscbor_require_array_size(chunk_rec, 1, 2);
1914
0
    wscbor_chunk_mark_errors(pinfo, item_rec, chunk_rec);
1915
0
    if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_rec)) {
1916
0
        proto_tree *tree_rec = proto_item_add_subtree(item_rec, ett_admin);
1917
1918
0
        wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1919
0
        uint64_t *type_code = wscbor_require_uint64(pinfo->pool, chunk);
1920
0
        proto_item *item_type = proto_tree_add_cbor_uint64(tree_rec, hf_admin_record_type, pinfo, tvb, chunk, type_code);
1921
1922
0
        dissector_handle_t type_dissect = NULL;
1923
0
        bool type_exp = false;
1924
0
        if (type_code) {
1925
0
            type_dissect = dissector_get_custom_table_handle(admin_dissectors, type_code);
1926
0
            label_type_field(type_code, type_dissect, item_type, item_rec);
1927
0
            type_exp = (*type_code >= 65536);
1928
0
        }
1929
0
        tvbuff_t *tvb_record = tvb_new_subset_remaining(tvb, offset);
1930
0
        int sublen = dissect_carried_data(type_dissect, context, tvb_record, pinfo, tree_rec, type_exp);
1931
0
        offset += sublen;
1932
0
    }
1933
1934
0
    proto_item_set_len(item_rec, offset);
1935
0
    return offset;
1936
0
}
1937
1938
0
static int dissect_status_report(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
1939
0
    bp_dissector_data_t *context = (bp_dissector_data_t *)data;
1940
0
    if (!context) {
1941
0
        return -1;
1942
0
    }
1943
0
    int offset = 0;
1944
1945
    // Status Information array head
1946
0
    proto_item *item_status = proto_tree_add_item(tree, hf_status_rep, tvb, offset, -1, ENC_NA);
1947
0
    proto_tree *tree_status = proto_item_add_subtree(item_status, ett_status_rep);
1948
0
    unsigned status_field_ix = 0;
1949
1950
0
    wscbor_chunk_t *chunk_status = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1951
0
    wscbor_require_array_size(chunk_status, 4, 6);
1952
0
    wscbor_chunk_mark_errors(pinfo, item_status, chunk_status);
1953
0
    if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_status)) {
1954
0
        proto_item_set_len(item_status, offset - chunk_status->start);
1955
0
        return 0;
1956
0
    }
1957
1958
0
    wscbor_chunk_t *chunk;
1959
0
    bool status_received = false;
1960
0
    bool status_forwarded = false;
1961
0
    bool status_delivered = false;
1962
0
    bool status_deleted = false;
1963
1964
0
    wscbor_chunk_t *chunk_info = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1965
0
    wscbor_require_array_size(chunk_info, 4, 4);
1966
0
    {
1967
0
        proto_item *item_info = proto_tree_add_item(tree_status, hf_status_rep_status_info, tvb, offset, -1, ENC_NA);
1968
0
        wscbor_chunk_mark_errors(pinfo, item_info, chunk_info);
1969
0
        if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_info)) {
1970
0
            proto_tree *tree_info = proto_item_add_subtree(item_info, ett_status_info);
1971
1972
0
            status_received = dissect_status_assertion(tree_info, hf_status_rep_received, pinfo, tvb, &offset);
1973
0
            status_forwarded = dissect_status_assertion(tree_info, hf_status_rep_forwarded, pinfo, tvb, &offset);
1974
0
            status_delivered = dissect_status_assertion(tree_info, hf_status_rep_delivered, pinfo, tvb, &offset);
1975
0
            status_deleted = dissect_status_assertion(tree_info, hf_status_rep_deleted, pinfo, tvb, &offset);
1976
0
        }
1977
1978
0
        proto_item_set_len(item_info, offset - chunk_info->start);
1979
0
        status_field_ix++;
1980
0
    }
1981
1982
0
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1983
0
    uint64_t *reason_code = wscbor_require_uint64(pinfo->pool, chunk);
1984
0
    proto_tree_add_cbor_uint64(tree_status, hf_status_rep_reason_code, pinfo, tvb, chunk, reason_code);
1985
0
    status_field_ix++;
1986
1987
0
    bp_eid_t *subj_eid = bp_eid_new(pinfo->pool);
1988
0
    proto_tree_add_cbor_eid(tree_status, hf_status_rep_subj_src_nodeid, hf_status_rep_subj_src_uri, pinfo, tvb, &offset, subj_eid);
1989
0
    status_field_ix++;
1990
1991
0
    bp_creation_ts_t subj_ts = {0};
1992
0
    dissect_cbor_timestamp(tree_status, hf_status_rep_subj_ts, pinfo, tvb, &offset, &subj_ts);
1993
0
    status_field_ix++;
1994
1995
0
    bp_bundle_ident_t *subj = bp_bundle_ident_new(wmem_file_scope(), subj_eid, &subj_ts, NULL, NULL);
1996
1997
0
    if (chunk_info->head_value > status_field_ix) {
1998
0
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1999
0
        subj->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk);
2000
0
        proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_frag_offset, pinfo, tvb, chunk, subj->frag_offset);
2001
0
        status_field_ix++;
2002
0
    }
2003
2004
0
    if (chunk_info->head_value > status_field_ix) {
2005
0
        chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2006
0
        subj->total_len = wscbor_require_uint64(wmem_file_scope(), chunk);
2007
0
        proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_payload_len, pinfo, tvb, chunk, subj->total_len);
2008
0
        status_field_ix++;
2009
0
    }
2010
2011
0
    proto_tree_add_ident(pinfo, tree_status, hf_status_rep_subj_ident, tvb, subj);
2012
2013
0
    {
2014
        // Pointer back to subject
2015
0
        const wmem_list_t *subj_list = wmem_map_lookup(bp_history->bundles, subj);
2016
0
        const wmem_list_frame_t *subj_it = subj_list ? wmem_list_head(subj_list) : NULL;
2017
0
        const bp_bundle_t *subj_found = subj_it ? wmem_list_frame_data(subj_it) : NULL;
2018
0
        if (subj_found) {
2019
0
            proto_item *item_subj_ref = proto_tree_add_uint(tree_status, hf_status_rep_subj_ref, tvb, 0, 0, subj_found->frame_num);
2020
0
            proto_item_set_generated(item_subj_ref);
2021
2022
0
            nstime_t td;
2023
0
            nstime_delta(&td, &(context->bundle->frame_time), &(subj_found->frame_time));
2024
0
            proto_item *item_td = proto_tree_add_time(tree_status, hf_status_time_diff, tvb, 0, 0, &td);
2025
0
            proto_item_set_generated(item_td);
2026
0
        }
2027
0
    }
2028
0
    {
2029
        // Pointers from subject to this status
2030
0
        wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, subj);
2031
0
        if (!status_set) {
2032
0
            status_set = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
2033
0
            wmem_map_insert(bp_history->admin_status, subj, status_set);
2034
0
        }
2035
0
        else {
2036
0
            bp_bundle_ident_free(wmem_file_scope(), subj);
2037
0
        }
2038
2039
        // Back-references to this status
2040
0
        if (!wmem_map_contains(status_set, context->bundle->ident)) {
2041
0
            ws_debug("status for %p in frame %d", (void*)context->bundle, context->bundle->frame_num);
2042
0
            wmem_map_insert(status_set, context->bundle->ident, NULL);
2043
0
        }
2044
0
    }
2045
2046
0
    proto_item *item_admin = proto_tree_get_parent(tree);
2047
0
    {
2048
0
        wmem_strbuf_t *status_text = wmem_strbuf_new(pinfo->pool, NULL);
2049
0
        bool sep = false;
2050
0
        if (status_received) {
2051
0
            if (sep) {
2052
0
                wmem_strbuf_append(status_text, "|");
2053
0
            }
2054
0
            wmem_strbuf_append(status_text, "RECEIVED");
2055
0
            sep = true;
2056
0
        }
2057
0
        if (status_forwarded) {
2058
0
            if (sep) {
2059
0
                wmem_strbuf_append(status_text, "|");
2060
0
            }
2061
0
            wmem_strbuf_append(status_text, "FORWARDED");
2062
0
            sep = true;
2063
0
        }
2064
0
        if (status_delivered) {
2065
0
            if (sep) {
2066
0
                wmem_strbuf_append(status_text, "|");
2067
0
            }
2068
0
            wmem_strbuf_append(status_text, "DELIVERED");
2069
0
            sep = true;
2070
0
        }
2071
0
        if (status_deleted) {
2072
0
            if (sep) {
2073
0
                wmem_strbuf_append(status_text, "|");
2074
0
            }
2075
0
            wmem_strbuf_append(status_text, "DELETED");
2076
0
        }
2077
0
        const char *status_buf = wmem_strbuf_finalize(status_text);
2078
0
        proto_item_append_text(item_admin, ", Status: %s", status_buf);
2079
0
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Status: %s",
2080
0
                            status_buf);
2081
0
    }
2082
0
    if (reason_code) {
2083
0
        proto_item_append_text(item_admin, ", Reason: %s", val64_to_str_wmem(pinfo->pool, *reason_code, status_report_reason_vals, "%" PRIu64));
2084
0
    }
2085
2086
0
    proto_item_set_len(item_status, offset - chunk_status->start);
2087
0
    return offset;
2088
0
}
2089
2090
/** Dissector for Bundle Payload block.
2091
 */
2092
138
static int dissect_block_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
2093
138
    bp_dissector_data_t *context = (bp_dissector_data_t *)data;
2094
138
    if (!context) {
2095
0
        return -1;
2096
0
    }
2097
138
    const bp_bundle_t *bundle = context->bundle;
2098
2099
    // Parent bundle tree
2100
138
    proto_tree *tree_block = proto_tree_get_parent_tree(tree);
2101
138
    proto_tree *tree_bundle = proto_tree_get_parent_tree(tree_block);
2102
    // Back up to top-level
2103
138
    proto_item *tree_top = proto_tree_get_parent_tree(tree_bundle);
2104
2105
138
    const bool is_fragment = bundle->primary->flags & BP_BUNDLE_IS_FRAGMENT;
2106
138
    const bool is_admin = bundle->primary->flags & BP_BUNDLE_PAYLOAD_ADMIN;
2107
138
    const unsigned payload_len = tvb_reported_length(tvb);
2108
2109
    // Set if the payload is fully defragmented
2110
138
    tvbuff_t *tvb_payload = NULL;
2111
138
    const char *col_suffix = NULL;
2112
138
    if (is_fragment) {
2113
66
        col_suffix = " (fragment)";
2114
2115
66
        if (bp_reassemble_payload) {
2116
66
            if (!(bundle->primary->frag_offset
2117
55
                  && bundle->primary->total_len)) {
2118
20
                return -1;
2119
20
            }
2120
            // correlate by non-fragment bundle identity hash
2121
46
            bp_bundle_ident_t *corr_ident = bp_bundle_ident_new(
2122
46
                pinfo->pool,
2123
46
                bundle->primary->src_nodeid,
2124
46
                &(bundle->primary->ts),
2125
46
                NULL,
2126
46
                NULL
2127
46
            );
2128
2129
46
            if (
2130
46
                (UINT32_MAX < *(bundle->primary->frag_offset))
2131
46
                || (UINT32_MAX < *(bundle->primary->total_len))) {
2132
0
                expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_reassemble_size);
2133
0
            }
2134
46
            else {
2135
46
                const uint32_t frag_offset = (uint32_t)*(bundle->primary->frag_offset);
2136
46
                const uint32_t total_len = (uint32_t)*(bundle->primary->total_len);
2137
46
                fragment_head *payload_frag_msg = fragment_add_check(
2138
46
                    &bp_reassembly_table,
2139
46
                    tvb, 0,
2140
46
                    pinfo, 0, corr_ident,
2141
46
                    frag_offset,
2142
46
                    payload_len,
2143
46
                    true
2144
46
                );
2145
46
                const uint32_t old_total_len = fragment_get_tot_len(
2146
46
                    &bp_reassembly_table,
2147
46
                    pinfo, 0, corr_ident
2148
46
                );
2149
46
                if (old_total_len > 0) {
2150
42
                    if (total_len != old_total_len) {
2151
14
                        expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_tot_mismatch);
2152
14
                    }
2153
42
                }
2154
4
                else {
2155
4
                    fragment_set_tot_len(
2156
4
                        &bp_reassembly_table,
2157
4
                        pinfo, 0, corr_ident,
2158
4
                        total_len
2159
4
                    );
2160
4
                }
2161
46
                tvb_payload = process_reassembled_data(
2162
46
                    tvb, 0, pinfo,
2163
46
                    "Reassembled Payload",
2164
46
                    payload_frag_msg,
2165
46
                    &payload_frag_items,
2166
46
                    NULL,
2167
46
                    tree_bundle
2168
46
                );
2169
46
                if (tvb_payload) {
2170
1
                    col_suffix = " (reassembled)";
2171
1
                }
2172
46
            }
2173
46
            bp_bundle_ident_free(pinfo->pool, corr_ident);
2174
46
        }
2175
66
    }
2176
72
    else {
2177
72
        tvb_payload = tvb;
2178
72
    }
2179
2180
118
    if (col_suffix) {
2181
46
        col_append_str(pinfo->cinfo, COL_INFO, col_suffix);
2182
46
    }
2183
118
    if (!tvb_payload) {
2184
45
        return payload_len;
2185
45
    }
2186
2187
    // Payload is known to be administrative, independent of destination EID
2188
73
    if (is_admin) {
2189
0
        const int sublen = call_dissector_only(handle_admin, tvb_payload, pinfo, tree_top, context);
2190
0
        if (sublen > 0) {
2191
0
            return sublen;
2192
0
        }
2193
0
    }
2194
2195
    // an EID shouldn't have multiple of these set
2196
73
    dissector_handle_t payload_dissect = NULL;
2197
73
    if (bundle->primary->dst_eid->dtn_wkssp) {
2198
51
        payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_wkssp, bundle->primary->dst_eid->dtn_wkssp);
2199
51
    }
2200
22
    else if (bundle->primary->dst_eid->dtn_serv) {
2201
0
        payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_serv, bundle->primary->dst_eid->dtn_serv);
2202
0
    }
2203
22
    else if (bundle->primary->dst_eid->ipn_serv &&
2204
0
        (*(bundle->primary->dst_eid->ipn_serv) <= UINT32_MAX)) {
2205
0
        payload_dissect = dissector_get_uint_handle(payload_dissectors_ipn_serv, (uint32_t)(*(bundle->primary->dst_eid->ipn_serv)));
2206
0
    }
2207
2208
73
    return dissect_carried_data(payload_dissect, context, tvb_payload, pinfo, tree_top, true);
2209
73
}
2210
2211
/** Dissector for Previous Node block.
2212
 */
2213
1
static int dissect_block_prev_node(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2214
1
    int offset = 0;
2215
2216
1
    proto_tree_add_cbor_eid(tree, hf_previous_node_nodeid, hf_previous_node_uri, pinfo, tvb, &offset, NULL);
2217
2218
1
    return offset;
2219
1
}
2220
2221
/** Dissector for Bundle Age block.
2222
 */
2223
0
static int dissect_block_bundle_age(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2224
0
    int offset = 0;
2225
2226
0
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2227
0
    const uint64_t *age = wscbor_require_uint64(pinfo->pool, chunk);
2228
0
    proto_tree_add_cbor_uint64(tree, hf_bundle_age_time, pinfo, tvb, chunk, age);
2229
2230
0
    if (age) {
2231
0
        nstime_t age_exp = dtn_to_delta(*age);
2232
0
        proto_item *item_age_exp = proto_tree_add_time(tree, hf_bundle_age_exp, tvb, chunk->start, chunk->data_length, &age_exp);
2233
0
        proto_item_set_generated(item_age_exp);
2234
0
    }
2235
2236
0
    return offset;
2237
0
}
2238
2239
/** Dissector for Hop Count block.
2240
 */
2241
2
static int dissect_block_hop_count(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2242
2
    int offset = 0;
2243
2244
2
    wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2245
2
    wscbor_require_array_size(chunk, 2, 2);
2246
2
    if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk)) {
2247
1
        return 0;
2248
1
    }
2249
2250
1
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2251
1
    const uint64_t *limit = wscbor_require_uint64(pinfo->pool, chunk);
2252
1
    proto_tree_add_cbor_uint64(tree, hf_hop_count_limit, pinfo, tvb, chunk, limit);
2253
2254
1
    chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2255
1
    const uint64_t *current = wscbor_require_uint64(pinfo->pool, chunk);
2256
1
    proto_tree_add_cbor_uint64(tree, hf_hop_count_current, pinfo, tvb, chunk, current);
2257
2258
1
    return offset;
2259
2
}
2260
2261
/// Clear state when new file scope is entered
2262
16
static void bp_init(void) {
2263
16
    bp_history = wmem_new0(wmem_file_scope(), bp_history_t);
2264
    // ident keys are owned by the respective bundles
2265
16
    bp_history->bundles = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
2266
    // subject ident key is not kept
2267
16
    bp_history->admin_status = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
2268
16
}
2269
2270
0
static void bp_cleanup(void) {}
2271
2272
/// Re-initialize after a configuration change
2273
16
static void bp_reinit_config(void) {}
2274
2275
2276
static void *fragment_bundle_ident_temporary_key(
2277
96
        const packet_info *pinfo _U_, const uint32_t id _U_, const void *data) {
2278
96
    return (bp_bundle_ident_t *)data;
2279
96
}
2280
static void *fragment_bundle_ident_persistent_key(
2281
3
        const packet_info *pinfo _U_, const uint32_t id _U_, const void *data) {
2282
3
    const bp_bundle_ident_t *ident = (const bp_bundle_ident_t *)data;
2283
2284
3
    bp_bundle_ident_t *key = g_slice_new0(bp_bundle_ident_t);
2285
2286
3
    copy_address(&(key->src), &(ident->src));
2287
3
    key->ts = ident->ts;
2288
3
    if (ident->frag_offset) {
2289
0
        key->frag_offset = g_slice_new(uint64_t);
2290
0
        key->frag_offset = ident->frag_offset;
2291
0
    }
2292
3
    if (ident->total_len) {
2293
0
        key->total_len = g_slice_new(uint64_t);
2294
0
        key->total_len = ident->total_len;
2295
0
    }
2296
3
    return key;
2297
3
}
2298
96
static void fragment_bundle_ident_free_temporary_key(void *ptr _U_) {}
2299
1
static void fragment_bundle_ident_free_persistent_key(void *ptr) {
2300
1
    bp_bundle_ident_t *key = (bp_bundle_ident_t *)ptr;
2301
2302
1
    free_address(&(key->src));
2303
1
    g_slice_free(uint64_t, (void *)(key->frag_offset));
2304
1
    g_slice_free(uint64_t, (void *)(key->total_len));
2305
2306
1
    g_slice_free(bp_bundle_ident_t, key);
2307
1
}
2308
static const reassembly_table_functions bundle_reassembly_table_functions = {
2309
    bp_bundle_ident_hash,
2310
    bp_bundle_ident_equal,
2311
    fragment_bundle_ident_temporary_key,
2312
    fragment_bundle_ident_persistent_key,
2313
    fragment_bundle_ident_free_temporary_key,
2314
    fragment_bundle_ident_free_persistent_key
2315
};
2316
2317
0
static void dtn_serv_prompt(packet_info *pinfo, char *result) {
2318
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2319
2320
0
    const char *serv = NULL;
2321
0
    if (bundle && bundle->primary->dst_eid->dtn_serv) {
2322
0
        serv = bundle->primary->dst_eid->dtn_serv;
2323
0
    }
2324
2325
0
    snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "dst (%s)", serv);
2326
0
}
2327
2328
0
static void *dtn_serv_value(packet_info *pinfo) {
2329
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2330
0
    if (bundle && bundle->primary->dst_eid->dtn_serv) {
2331
0
        const char *serv = bundle->primary->dst_eid->dtn_serv;
2332
0
        return (char *)serv;
2333
0
    }
2334
0
    return 0;
2335
0
}
2336
2337
0
static void ipn_serv_prompt(packet_info *pinfo, char *result) {
2338
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2339
2340
0
    uint32_t serv = 0;
2341
0
    if (bundle && bundle->primary->dst_eid->ipn_serv
2342
0
        && (*(bundle->primary->dst_eid->ipn_serv) <= UINT32_MAX)) {
2343
0
        serv = (uint32_t) *(bundle->primary->dst_eid->ipn_serv);
2344
0
    }
2345
2346
0
    snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "dst (%u)", serv);
2347
0
}
2348
2349
0
static void *ipn_serv_value(packet_info *pinfo) {
2350
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2351
0
    if (bundle && bundle->primary->dst_eid->ipn_serv
2352
0
        && (*(bundle->primary->dst_eid->ipn_serv) <= UINT32_MAX)) {
2353
0
        uint32_t serv = (uint32_t) *(bundle->primary->dst_eid->ipn_serv);
2354
0
        return GUINT_TO_POINTER(serv);
2355
0
    }
2356
0
    return 0;
2357
0
}
2358
2359
0
static const char * bp_conv_get_filter_type(conv_item_t *item, conv_filter_type_e filter) {
2360
0
    if ((filter == CONV_FT_SRC_ADDRESS) && (item->src_address.type == AT_STRINGZ)) {
2361
0
        return "bpv7.primary.src_uri";
2362
0
    }
2363
0
    if ((filter == CONV_FT_DST_ADDRESS) && (item->dst_address.type == AT_STRINGZ)) {
2364
0
        return "bpv7.primary.dst_uri";
2365
0
    }
2366
0
    if ((filter == CONV_FT_ANY_ADDRESS) && (
2367
0
            (item->src_address.type == AT_STRINGZ)
2368
0
            || (item->dst_address.type == AT_STRINGZ))) {
2369
0
        return "bpv7.primary.srcdst_uri";
2370
0
    }
2371
0
    return CONV_FILTER_INVALID;
2372
0
}
2373
2374
static ct_dissector_info_t bp_ct_dissector_info = {&bp_conv_get_filter_type};
2375
2376
0
static tap_packet_status bp_conv_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) {
2377
0
    conv_hash_t *hash = (conv_hash_t*) pct;
2378
0
    const bp_bundle_t *bundle = (const bp_bundle_t *)vip;
2379
0
    hash->flags = flags;
2380
2381
0
    add_conversation_table_data(
2382
0
        hash,
2383
0
        &(bundle->primary->src_nodeid->uri),
2384
0
        &(bundle->primary->dst_eid->uri),
2385
0
        0, 0,
2386
0
        1, pinfo->fd->pkt_len,
2387
0
        &pinfo->rel_ts, &pinfo->abs_ts,
2388
0
        &bp_ct_dissector_info, CONVERSATION_NONE
2389
0
    );
2390
2391
0
    return TAP_PACKET_REDRAW;
2392
0
}
2393
2394
0
static const char * bp_endp_get_filter_type(endpoint_item_t *item, conv_filter_type_e filter) {
2395
0
    if ((filter == CONV_FT_SRC_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2396
0
        return "bpv7.primary.src_uri";
2397
0
    }
2398
0
    if ((filter == CONV_FT_DST_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2399
0
        return "bpv7.primary.dst_uri";
2400
0
    }
2401
0
    if ((filter == CONV_FT_ANY_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2402
0
        return "bpv7.primary.srcdst_uri";
2403
0
    }
2404
0
    return CONV_FILTER_INVALID;
2405
0
}
2406
2407
static et_dissector_info_t bp_endp_dissector_info = {&bp_endp_get_filter_type};
2408
2409
0
static tap_packet_status bp_endp_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) {
2410
0
    conv_hash_t *hash = (conv_hash_t*)pit;
2411
0
    const bp_bundle_t *bundle = (const bp_bundle_t *)vip;
2412
0
    hash->flags = flags;
2413
2414
0
    if (bundle->primary->src_nodeid->uri.type == AT_STRINGZ) {
2415
0
        add_endpoint_table_data(
2416
0
            hash, &(bundle->primary->src_nodeid->uri), 0, true,
2417
0
            1, pinfo->fd->pkt_len,
2418
0
            &bp_endp_dissector_info, ENDPOINT_NONE
2419
0
        );
2420
0
    }
2421
0
    if (bundle->primary->dst_eid->uri.type == AT_STRINGZ) {
2422
0
        add_endpoint_table_data(
2423
0
            hash, &(bundle->primary->dst_eid->uri), 0, false,
2424
0
            1, pinfo->fd->pkt_len,
2425
0
            &bp_endp_dissector_info, ENDPOINT_NONE
2426
0
        );
2427
0
    }
2428
0
    return TAP_PACKET_REDRAW;
2429
0
}
2430
2431
0
static bool bp_filter_valid(packet_info *pinfo, void *user_data _U_) {
2432
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2433
0
    return bundle != NULL;
2434
0
}
2435
2436
0
static char * bp_build_filter(packet_info *pinfo, void *user_data _U_) {
2437
0
    const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2438
0
    if (!bundle) {
2439
0
        return NULL;
2440
0
    }
2441
2442
0
    return ws_strdup_printf(
2443
0
        "bpv7.primary.srcdst_uri == \"%s\" and bpv7.primary.srcdst_uri == \"%s\"",
2444
0
        address_to_name(&(bundle->primary->src_nodeid->uri)),
2445
0
        address_to_name(&(bundle->primary->dst_eid->uri))
2446
0
    );
2447
0
}
2448
2449
static const char* st_str_bp = "Bundle";
2450
static const char* st_str_pdu = "PDU Length";
2451
static const char* st_str_blks = "Extension Block Count";
2452
static const char* st_str_btsd = "BTSD Length (by Block Type)";
2453
static int st_node_bp = -1;
2454
static int st_node_btsd = -1;
2455
2456
static void
2457
bpv7_stats_tree_init(stats_tree *st)
2458
0
{
2459
0
    st_node_bp = stats_tree_create_node(st, st_str_bp, 0, STAT_DT_INT, false);
2460
0
    stats_tree_create_node(st, st_str_pdu, st_node_bp, STAT_DT_INT, false);
2461
0
    stats_tree_create_node(st, st_str_blks, st_node_bp, STAT_DT_INT, false);
2462
2463
0
    st_node_btsd = stats_tree_create_pivot(st, st_str_btsd, 0);
2464
0
}
2465
2466
static tap_packet_status
2467
bpv7_stats_tree_packet(stats_tree *st, packet_info *pinfo, epan_dissect_t *edt _U_, const void *p, tap_flags_t flags _U_)
2468
0
{
2469
0
    const bp_bundle_t *bundle = (const bp_bundle_t *)p;
2470
2471
0
    tick_stat_node(st, st_str_bp, 0, false);
2472
0
    avg_stat_node_add_value_int(st, st_str_pdu, st_node_bp, false, bundle->pdu_len);
2473
0
    avg_stat_node_add_value_int(st, st_str_blks, st_node_bp, false, wmem_list_count(bundle->blocks));
2474
2475
    // parent count of all bundles, not all blocks
2476
0
    tick_stat_node(st, st_str_btsd, 0, true);
2477
2478
0
    for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
2479
0
            it = wmem_list_frame_next(it)) {
2480
0
        const bp_block_canonical_t *block = wmem_list_frame_data(it);
2481
2482
0
        const char *blk_type = "invalid";
2483
0
        if (block->type_code) {
2484
0
            blk_type = wmem_strdup_printf(pinfo->pool, "%" PRIu64, *(block->type_code));
2485
0
        }
2486
0
        avg_stat_node_add_value_int(st, blk_type, st_node_btsd, false, block->btsd_len);
2487
0
    }
2488
2489
0
    return TAP_PACKET_REDRAW;
2490
0
}
2491
2492
/// Overall registration of the protocol
2493
16
void proto_register_bpv7(void) {
2494
16
    proto_bp = proto_register_protocol("DTN Bundle Protocol Version 7", "BPv7", "bpv7");
2495
16
    register_init_routine(&bp_init);
2496
16
    register_cleanup_routine(&bp_cleanup);
2497
2498
16
    proto_register_field_array(proto_bp, fields, array_length(fields));
2499
16
    proto_register_subtree_array(ett, array_length(ett));
2500
16
    expert_module_t *expert = expert_register_protocol(proto_bp);
2501
16
    expert_register_field_array(expert, expertitems, array_length(expertitems));
2502
2503
16
    register_dissector_with_description("bpv7", "Bundle Protocol Version 7", dissect_bp, proto_bp);
2504
2505
16
    eid_dissectors = register_dissector_table("bpv7.eid", "BPv7 EID Scheme-Specific Part", proto_bp, FT_UINT32, 0);
2506
16
    block_dissectors = register_custom_dissector_table("bpv7.block_type", "BPv7 Block", proto_bp, g_int64_hash, g_int64_equal, g_free);
2507
    // Blocks don't count as protocol layers
2508
16
    proto_blocktype = proto_register_protocol_in_name_only("BPv7 Block Type", "Block Type", "bpv7.block_type", proto_bp, FT_PROTOCOL);
2509
2510
    // case-sensitive string matching
2511
16
    payload_dissectors_dtn_wkssp = register_dissector_table("bpv7.payload.dtn_wkssp", "BPv7 DTN-scheme well-known SSP", proto_bp, FT_STRING, STRING_CASE_SENSITIVE);
2512
2513
16
    payload_dissectors_dtn_serv = register_dissector_table("bpv7.payload.dtn_serv", "BPv7 DTN-scheme service", proto_bp, FT_STRING, STRING_CASE_SENSITIVE);
2514
2515
16
    static build_valid_func dtn_serv_da_build_value[1] = {dtn_serv_value};
2516
16
    static decode_as_value_t dtn_serv_da_values[1] = {
2517
16
        {dtn_serv_prompt, 1, dtn_serv_da_build_value}
2518
16
    };
2519
16
    static decode_as_t dtn_serv_da = {
2520
16
        "bpv7", "bpv7.payload.dtn_serv",
2521
16
        1, 0, dtn_serv_da_values, NULL, NULL,
2522
16
        decode_as_default_populate_list, decode_as_default_reset,
2523
16
        decode_as_default_change, NULL, NULL, NULL
2524
16
    };
2525
16
    register_decode_as(&dtn_serv_da);
2526
2527
16
    payload_dissectors_ipn_serv = register_dissector_table("bpv7.payload.ipn_serv", "BPv7 IPN-scheme service", proto_bp, FT_UINT32, BASE_DEC);
2528
2529
16
    static build_valid_func ipn_serv_da_build_value[1] = {ipn_serv_value};
2530
16
    static decode_as_value_t ipn_serv_da_values[1] = {
2531
16
        {ipn_serv_prompt, 1, ipn_serv_da_build_value}
2532
16
    };
2533
16
    static decode_as_t ipn_serv_da = {
2534
16
        "bpv7", "bpv7.payload.ipn_serv",
2535
16
        1, 0, ipn_serv_da_values, NULL, NULL,
2536
16
        decode_as_default_populate_list, decode_as_default_reset,
2537
16
        decode_as_default_change, NULL, NULL, NULL
2538
16
    };
2539
16
    register_decode_as(&ipn_serv_da);
2540
2541
16
    module_t *module_bp = prefs_register_protocol(proto_bp, bp_reinit_config);
2542
16
    prefs_register_bool_preference(
2543
16
        module_bp,
2544
16
        "bp_compute_crc",
2545
16
        "Compute and compare CRCs",
2546
16
        "If enabled, the blocks will have CRC checks performed.",
2547
16
        &bp_compute_crc
2548
16
    );
2549
16
    prefs_register_bool_preference(
2550
16
        module_bp,
2551
16
        "bp_reassemble_payload",
2552
16
        "Reassemble fragmented payloads",
2553
16
        "Whether the dissector should reassemble fragmented bundle payloads.",
2554
16
        &bp_reassemble_payload
2555
16
    );
2556
16
    prefs_register_bool_preference(
2557
16
        module_bp,
2558
16
        "bp_payload_try_heur",
2559
16
        "Attempt heuristic dissection of BTSD/payload",
2560
16
        "When dissecting block type-specific data and payload and no destination matches, attempt heuristic dissection.",
2561
16
        &bp_payload_try_heur
2562
16
    );
2563
2564
16
    reassembly_table_register(
2565
16
        &bp_reassembly_table,
2566
16
        &bundle_reassembly_table_functions
2567
16
    );
2568
2569
16
    btsd_heur = register_heur_dissector_list_with_description("bpv7.btsd", "BPv7 block data fallback", proto_bp);
2570
2571
16
    bp_tap = register_tap("bpv7");
2572
16
    register_conversation_table(proto_bp, true, bp_conv_packet, bp_endp_packet);
2573
16
    register_conversation_filter("bpv7", "BPv7", bp_filter_valid, bp_build_filter, NULL);
2574
2575
16
    proto_bp_admin = proto_register_protocol("BPv7 Administrative Record", "BPv7 Admin", "bpv7.admin_rec");
2576
16
    handle_admin = register_dissector("bpv7.admin_rec", dissect_payload_admin, proto_bp_admin);
2577
16
    admin_dissectors = register_custom_dissector_table("bpv7.admin_record_type", "BPv7 Administrative Record Type", proto_bp_admin, g_int64_hash, g_int64_equal, g_free);
2578
16
    proto_admintype = proto_register_protocol_in_name_only("BPv7 Administrative Record Type", "Admin Type", "bpv7.admin_record_type", proto_bp, FT_PROTOCOL);
2579
16
}
2580
2581
16
void proto_reg_handoff_bpv7(void) {
2582
16
    const int proto_cbor = proto_get_id_by_filter_name("cbor");
2583
16
    heur_dissector_add("bpv7.btsd", cbor_heuristic, "CBOR in Bundle BTSD", "cbor_bpv7", proto_cbor, HEURISTIC_ENABLE);
2584
2585
16
    handle_cbor = find_dissector("cbor");
2586
16
    handle_cborseq = find_dissector("cborseq");
2587
2588
16
    stats_tree_register("bpv7", "bpv7", "DTN" STATS_TREE_MENU_SEPARATOR "BPv7", 0, bpv7_stats_tree_packet, bpv7_stats_tree_init, NULL);
2589
2590
    /* Packaged extensions */
2591
16
    {
2592
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_eid_dtn, proto_bp, NULL, "DTN");
2593
16
        dissector_add_uint("bpv7.eid", EID_SCHEME_DTN, hdl);
2594
16
    }
2595
16
    {
2596
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_eid_ipn, proto_bp, NULL, "IPN");
2597
16
        dissector_add_uint("bpv7.eid", EID_SCHEME_IPN, hdl);
2598
16
    }
2599
16
    {
2600
16
        uint64_t *key = g_new(uint64_t, 1);
2601
16
        *key = BP_BLOCKTYPE_PAYLOAD;
2602
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_payload, proto_blocktype, NULL, "Payload");
2603
16
        dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2604
16
    }
2605
16
    {
2606
16
        uint64_t *key = g_new(uint64_t, 1);
2607
16
        *key = BP_BLOCKTYPE_PREV_NODE;
2608
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_prev_node, proto_blocktype, NULL, "Previous Node");
2609
16
        dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2610
16
    }
2611
16
    {
2612
16
        uint64_t *key = g_new(uint64_t, 1);
2613
16
        *key = BP_BLOCKTYPE_BUNDLE_AGE;
2614
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_bundle_age, proto_blocktype, NULL, "Bundle Age");
2615
16
        dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2616
16
    }
2617
16
    {
2618
16
        uint64_t *key = g_new(uint64_t, 1);
2619
16
        *key = BP_BLOCKTYPE_HOP_COUNT;
2620
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_hop_count, proto_blocktype, NULL, "Hop Count");
2621
16
        dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2622
16
    }
2623
16
    {
2624
16
        uint64_t *key = g_new(uint64_t, 1);
2625
16
        *key = BP_ADMINTYPE_BUNDLE_STATUS;
2626
16
        dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_status_report, proto_admintype, NULL, "Bundle Status Report");
2627
16
        dissector_add_custom_table_handle("bpv7.admin_record_type", key, hdl);
2628
16
    }
2629
2630
16
    bp_reinit_config();
2631
16
}