Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-bpv7.c
blob0ef3a88be515ac411847f992fc4244c7427429cb
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
6 * Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: LGPL-2.1-or-later
14 #include "config.h"
15 #define WS_LOG_DOMAIN "packet-bpv7"
17 #include <inttypes.h>
19 #include "packet-bpv7.h"
20 #include "epan/wscbor.h"
21 #include <epan/proto.h>
22 #include <epan/packet.h>
23 #include <epan/prefs.h>
24 #include <epan/expert.h>
25 #include <epan/to_str.h>
26 #include <epan/reassemble.h>
27 #include <epan/decode_as.h>
28 #include <epan/proto_data.h>
29 #include <epan/conversation_table.h>
30 #include <epan/conversation_filter.h>
31 #include <epan/exceptions.h>
32 #include <epan/ftypes/ftypes.h>
33 #include <epan/unit_strings.h>
34 #include <epan/tfs.h>
35 #include <wsutil/crc16.h>
36 #include <wsutil/array.h>
37 #include <wsutil/crc32.h>
39 void proto_register_bpv7(void);
40 void proto_reg_handoff_bpv7(void);
42 /// Protocol column name
43 static const char *const proto_name_bp = "BPv7";
44 static const char *const proto_name_bp_admin = "BPv7 Admin";
46 /// Protocol preferences and defaults
47 static bool bp_compute_crc = true;
48 static bool bp_reassemble_payload = true;
49 static bool bp_payload_try_heur = false;
51 /// Protocol handles
52 static int proto_bp;
53 static int bp_tap;
54 static int proto_blocktype;
55 static int proto_bp_admin;
56 static int proto_admintype;
57 /// Protocol-level data
58 static bp_history_t *bp_history;
60 static dissector_handle_t handle_admin;
61 /// Dissect opaque CBOR data
62 static dissector_handle_t handle_cbor;
63 static dissector_handle_t handle_cborseq;
64 /// Extension sub-dissectors
65 static dissector_table_t block_dissectors;
66 static dissector_table_t payload_dissectors_dtn_wkssp;
67 static dissector_table_t payload_dissectors_dtn_serv;
68 static dissector_table_t payload_dissectors_ipn_serv;
69 static dissector_table_t admin_dissectors;
70 /// BTSD heuristic
71 static heur_dissector_list_t btsd_heur;
73 /// Fragment reassembly
74 static reassembly_table bp_reassembly_table;
76 static const val64_string eid_schemes[] = {
77 {EID_SCHEME_DTN, "dtn"},
78 {EID_SCHEME_IPN, "ipn"},
79 {0, NULL},
82 static const val64_string crc_vals[] = {
83 {BP_CRC_NONE, "None"},
84 {BP_CRC_16, "CRC-16"},
85 {BP_CRC_32, "CRC-32C"},
86 {0, NULL},
89 typedef struct {
90 /// Type of block
91 uint64_t type_code;
92 /// Limit on total count
93 uint64_t limit;
94 } blocktype_limit;
95 /// Block type count limits
96 static const blocktype_limit blocktype_limits[] = {
97 {BP_BLOCKTYPE_PAYLOAD, 1},
98 {BP_BLOCKTYPE_PREV_NODE, 1},
99 {BP_BLOCKTYPE_BUNDLE_AGE, 1},
100 {BP_BLOCKTYPE_HOP_COUNT, 1},
101 // Mandatory last row
102 {BP_BLOCKTYPE_INVALID, 0},
105 /// Dissection order by block type
106 static int blocktype_order(const bp_block_canonical_t *block) {
107 if (block->type_code) {
108 switch (*(block->type_code)) {
109 case BP_BLOCKTYPE_BCB:
110 return -2;
111 case BP_BLOCKTYPE_BIB:
112 return -1;
113 case BP_BLOCKTYPE_PAYLOAD:
114 return 1; // last block
115 default:
116 return 0;
119 return 0;
122 static const val64_string status_report_reason_vals[] = {
123 {0, "No additional information"},
124 {1, "Lifetime expired"},
125 {2, "Forwarded over unidirectional link"},
126 {3, "Transmission canceled"},
127 {4, "Depleted storage"},
128 {5, "Destination endpoint ID unintelligible"},
129 {6, "No known route to destination from here"},
130 {7, "No timely contact with next node on route"},
131 {8, "Block unintelligible"},
132 {9, "Hop limit exceeded"},
133 {10, "Traffic pared"},
134 {11, "Block unsupported"},
135 {12, "Missing Security Operation"},
136 {13, "Unknown Security Operation"},
137 {14, "Unexpected Security Operation"},
138 {15, "Failed Security Operation"},
139 {16, "Conflicting Security Operation"},
140 {0, NULL},
143 enum {
144 /// The last bundle in the frame (as a bp_bundle_t*)
145 PROTO_DATA_BUNDLE = 1,
148 static int hf_bundle_head;
149 static int hf_bundle_break;
150 static int hf_block;
152 static int hf_crc_type;
153 static int hf_crc_field_uint16;
154 static int hf_crc_field_uint32;
155 static int hf_crc_status;
157 static int hf_time_dtntime;
158 static int hf_time_utctime;
160 static int hf_create_ts_time;
161 static int hf_create_ts_seqno;
163 static int hf_eid_scheme;
164 static int hf_eid_dtn_ssp_code;
165 static int hf_eid_dtn_ssp_text;
166 static int hf_eid_ipn_node;
167 static int hf_eid_ipn_service;
168 static int hf_eid_dtn_wkssp;
169 static int hf_eid_dtn_serv;
171 static int hf_primary_version;
172 static int hf_primary_bundle_flags;
173 static int hf_primary_bundle_flags_deletion_report;
174 static int hf_primary_bundle_flags_delivery_report;
175 static int hf_primary_bundle_flags_forwarding_report;
176 static int hf_primary_bundle_flags_reception_report;
177 static int hf_primary_bundle_flags_req_status_time;
178 static int hf_primary_bundle_flags_user_app_ack;
179 static int hf_primary_bundle_flags_no_fragment;
180 static int hf_primary_bundle_flags_payload_admin;
181 static int hf_primary_bundle_flags_is_fragment;
182 static int hf_primary_dst_eid;
183 static int hf_primary_dst_uri;
184 static int hf_primary_src_nodeid;
185 static int hf_primary_src_uri;
186 static int hf_primary_srcdst_uri;
187 static int hf_primary_report_nodeid;
188 static int hf_primary_report_uri;
189 static int hf_primary_create_ts;
190 static int hf_primary_lifetime;
191 static int hf_primary_lifetime_exp;
192 static int hf_primary_expire_ts;
193 static int hf_primary_frag_offset;
194 static int hf_primary_total_length;
196 static int hf_bundle_ident;
197 static int hf_bundle_first_seen;
198 static int hf_bundle_retrans_seen;
199 static int hf_bundle_seen_time_diff;
200 static int hf_bundle_dst_dtn_srv;
201 static int hf_bundle_dst_ipn_srv;
202 static int hf_bundle_status_ref;
204 static int hf_canonical_type_code;
205 static int hf_canonical_block_num;
206 static int hf_canonical_block_flags;
207 static int hf_canonical_block_flags_delete_no_process;
208 static int hf_canonical_block_flags_status_no_process;
209 static int hf_canonical_block_flags_remove_no_process;
210 static int hf_canonical_block_flags_replicate_in_fragment;
211 static int hf_canonical_data_size;
212 static int hf_canonical_data;
214 static int hf_previous_node_nodeid;
215 static int hf_previous_node_uri;
216 static int hf_bundle_age_time;
217 static int hf_hop_count_limit;
218 static int hf_hop_count_current;
220 static int hf_admin_record_type;
221 static int hf_status_rep;
222 static int hf_status_rep_status_info;
223 static int hf_status_assert_val;
224 static int hf_status_assert_time;
225 static int hf_status_rep_received;
226 static int hf_status_rep_forwarded;
227 static int hf_status_rep_delivered;
228 static int hf_status_rep_deleted;
229 static int hf_status_rep_reason_code;
230 static int hf_status_rep_subj_src_nodeid;
231 static int hf_status_rep_subj_src_uri;
232 static int hf_status_rep_subj_ts;
233 static int hf_status_rep_subj_frag_offset;
234 static int hf_status_rep_subj_payload_len;
235 static int hf_status_rep_subj_ident;
236 static int hf_status_rep_subj_ref;
237 static int hf_status_time_diff;
239 static int hf_payload_fragments;
240 static int hf_payload_fragment;
241 static int hf_payload_fragment_overlap;
242 static int hf_payload_fragment_overlap_conflicts;
243 static int hf_payload_fragment_multiple_tails;
244 static int hf_payload_fragment_too_long_fragment;
245 static int hf_payload_fragment_error;
246 static int hf_payload_fragment_count;
247 static int hf_payload_reassembled_in;
248 static int hf_payload_reassembled_length;
249 static int hf_payload_reassembled_data;
250 static int ett_payload_fragment;
251 static int ett_payload_fragments;
253 /// Field definitions
254 static hf_register_info fields[] = {
255 {&hf_bundle_head, {"Indefinite Array", "bpv7.bundle_head", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
256 {&hf_bundle_break, {"Indefinite Break", "bpv7.bundle_break", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
258 {&hf_block, {"Block", "bpv7.block", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
260 {&hf_crc_type, {"CRC Type", "bpv7.crc_type", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(crc_vals), 0x0, NULL, HFILL}},
261 {&hf_crc_field_uint16, {"CRC Field Integer", "bpv7.crc_field", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
262 {&hf_crc_field_uint32, {"CRC field Integer", "bpv7.crc_field", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
263 {&hf_crc_status, {"CRC Status", "bpv7.crc_status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL}},
265 {&hf_time_dtntime, {"DTN Time", "bpv7.time.dtntime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
266 {&hf_time_utctime, {"UTC Time", "bpv7.time.utctime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL}},
268 {&hf_create_ts_time, {"Time", "bpv7.create_ts.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
269 {&hf_create_ts_seqno, {"Sequence Number", "bpv7.create_ts.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
270 {&hf_eid_scheme, {"Scheme Code", "bpv7.eid.scheme", FT_UINT64, BASE_DEC | BASE_VAL64_STRING, VALS64(eid_schemes), 0x0, NULL, HFILL}},
271 {&hf_eid_dtn_ssp_code, {"DTN SSP", "bpv7.eid.dtn_ssp_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
272 {&hf_eid_dtn_ssp_text, {"DTN SSP", "bpv7.eid.dtn_ssp_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
273 {&hf_eid_ipn_node, {"IPN Node Number", "bpv7.eid.ipn_node", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
274 {&hf_eid_ipn_service, {"IPN Service Number", "bpv7.eid.ipn_service", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
275 {&hf_eid_dtn_wkssp, {"Well-known SSP", "bpv7.eid.wkssp", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
276 {&hf_eid_dtn_serv, {"Service Name", "bpv7.eid.serv", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
278 {&hf_primary_version, {"Version", "bpv7.primary.version", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
279 {&hf_primary_bundle_flags, {"Bundle Flags", "bpv7.primary.bundle_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
280 {&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}},
281 {&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}},
282 {&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}},
283 {&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}},
284 {&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}},
285 {&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}},
286 {&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}},
287 {&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}},
288 {&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}},
289 {&hf_primary_dst_eid, {"Destination Endpoint ID", "bpv7.primary.dst_eid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
290 {&hf_primary_dst_uri, {"Destination URI", "bpv7.primary.dst_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
291 {&hf_primary_src_nodeid, {"Source Node ID", "bpv7.primary.src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
292 {&hf_primary_src_uri, {"Source URI", "bpv7.primary.src_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
293 {&hf_primary_srcdst_uri, {"Source or Destination URI", "bpv7.primary.srcdst_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
294 {&hf_primary_report_nodeid, {"Report-to Node ID", "bpv7.primary.report_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
295 {&hf_primary_report_uri, {"Report-to URI", "bpv7.primary.report_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
296 {&hf_primary_create_ts, {"Creation Timestamp", "bpv7.primary.create_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
297 {&hf_primary_lifetime, {"Lifetime", "bpv7.primary.lifetime", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
298 {&hf_primary_lifetime_exp, {"Lifetime Expanded", "bpv7.primary.lifetime_exp", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
299 {&hf_primary_expire_ts, {"Expire Time", "bpv7.primary.expire_time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0, NULL, HFILL}},
300 {&hf_primary_frag_offset, {"Fragment Offset", "bpv7.primary.frag_offset", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_octet_octets), 0x0, NULL, HFILL}},
301 {&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}},
303 {&hf_bundle_ident, {"Bundle Identity", "bpv7.bundle.identity", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
304 {&hf_bundle_first_seen, {"First Seen", "bpv7.bundle.first_seen", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_PREV), 0x0, NULL, HFILL}},
305 {&hf_bundle_retrans_seen, {"Retransmit Seen", "bpv7.bundle.retransmit_seen", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_NEXT), 0x0, NULL, HFILL}},
306 {&hf_bundle_seen_time_diff, {"Seen Time", "bpv7.bundle.seen_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
307 {&hf_bundle_dst_dtn_srv, {"Destination Service", "bpv7.bundle.dst_dtn_srv", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
308 {&hf_bundle_dst_ipn_srv, {"Destination Service", "bpv7.bundle.dst_ipn_srv", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
310 {&hf_bundle_status_ref, {"Status Bundle", "bpv7.bundle.status_ref", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
312 {&hf_canonical_type_code, {"Type Code", "bpv7.canonical.type_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
313 {&hf_canonical_block_num, {"Block Number", "bpv7.canonical.block_num", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
314 {&hf_canonical_block_flags, {"Block Flags", "bpv7.canonical.block_flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
315 {&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}},
316 {&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}},
317 {&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}},
318 {&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}},
319 {&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}},
320 {&hf_canonical_data, {"Block Type-Specific Data", "bpv7.canonical.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
322 {&hf_payload_fragments,
323 {"Payload fragments", "bpv7.payload.fragments",
324 FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
325 {&hf_payload_fragment,
326 {"Payload fragment", "bpv7.payload.fragment",
327 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
328 {&hf_payload_fragment_overlap,
329 {"Payload fragment overlap", "bpv7.payload.fragment.overlap",
330 FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
331 {&hf_payload_fragment_overlap_conflicts,
332 {"Payload fragment overlapping with conflicting data",
333 "bpv7.payload.fragment.overlap.conflicts",
334 FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
335 {&hf_payload_fragment_multiple_tails,
336 {"Message has multiple tail fragments",
337 "bpv7.payload.fragment.multiple_tails",
338 FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
339 {&hf_payload_fragment_too_long_fragment,
340 {"Payload fragment too long", "bpv7.payload.fragment.too_long_fragment",
341 FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
342 {&hf_payload_fragment_error,
343 {"Payload defragmentation error", "bpv7.payload.fragment.error",
344 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
345 {&hf_payload_fragment_count,
346 {"Payload fragment count", "bpv7.payload.fragment.count",
347 FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
348 {&hf_payload_reassembled_in,
349 {"Reassembled in", "bpv7.payload.reassembled.in",
350 FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
351 {&hf_payload_reassembled_length,
352 {"Reassembled length", "bpv7.payload.reassembled.length",
353 FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
354 {&hf_payload_reassembled_data,
355 {"Reassembled data", "bpv7.payload.reassembled.data",
356 FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
358 {&hf_previous_node_nodeid, {"Previous Node ID", "bpv7.previous_node.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
359 {&hf_previous_node_uri, {"Previous URI", "bpv7.previous_node.uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
361 {&hf_bundle_age_time, {"Bundle Age", "bpv7.bundle_age.time", FT_UINT64, BASE_DEC | BASE_UNIT_STRING, UNS(&units_milliseconds), 0x0, NULL, HFILL}},
363 {&hf_hop_count_limit, {"Hop Limit", "bpv7.hop_count.limit", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
364 {&hf_hop_count_current, {"Hop Count", "bpv7.hop_count.current", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
366 {&hf_admin_record_type, {"Record Type Code", "bpv7.admin_rec.type_code", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
368 {&hf_status_rep, {"Status Report", "bpv7.status_rep", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
369 {&hf_status_rep_status_info, {"Status Information", "bpv7.status_rep.status_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
370 {&hf_status_assert_val, {"Status Value", "bpv7.status_assert.val", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}},
371 {&hf_status_assert_time, {"Status at", "bpv7.status_assert.time", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
372 {&hf_status_rep_received, {"Reporting node received bundle", "bpv7.status_rep.received", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
373 {&hf_status_rep_forwarded, {"Reporting node forwarded bundle", "bpv7.status_rep.forwarded", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
374 {&hf_status_rep_delivered, {"Reporting node delivered bundle", "bpv7.status_rep.delivered", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
375 {&hf_status_rep_deleted, {"Reporting node deleted bundle", "bpv7.status_rep.deleted", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
376 {&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}},
377 {&hf_status_rep_subj_src_nodeid, {"Subject Source Node ID", "bpv7.status_rep.subj_src_nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
378 {&hf_status_rep_subj_src_uri, {"Subject Source URI", "bpv7.status_rep.subj_src_uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
379 {&hf_status_rep_subj_ts, {"Subject Creation Timestamp", "bpv7.status_rep.subj_ts", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
380 {&hf_status_rep_subj_frag_offset, {"Subject Fragment Offset", "bpv7.status_rep.subj_frag_offset", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
381 {&hf_status_rep_subj_payload_len, {"Subject Payload Length", "bpv7.status_rep.subj_payload_len", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
382 {&hf_status_rep_subj_ident, {"Subject Identity", "bpv7.status_rep.identity", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
383 {&hf_status_rep_subj_ref, {"Subject Bundle", "bpv7.status_rep.subj_ref", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL}},
384 {&hf_status_time_diff, {"Status Time", "bpv7.status_rep.subj_time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
387 static int *const bundle_flags[] = {
388 &hf_primary_bundle_flags_deletion_report,
389 &hf_primary_bundle_flags_delivery_report,
390 &hf_primary_bundle_flags_forwarding_report,
391 &hf_primary_bundle_flags_reception_report,
392 &hf_primary_bundle_flags_req_status_time,
393 &hf_primary_bundle_flags_user_app_ack,
394 &hf_primary_bundle_flags_no_fragment,
395 &hf_primary_bundle_flags_payload_admin,
396 &hf_primary_bundle_flags_is_fragment,
397 NULL
400 static int *const block_flags[] = {
401 &hf_canonical_block_flags_remove_no_process,
402 &hf_canonical_block_flags_delete_no_process,
403 &hf_canonical_block_flags_status_no_process,
404 &hf_canonical_block_flags_replicate_in_fragment,
405 NULL
408 static int ett_bundle;
409 static int ett_bundle_flags;
410 static int ett_block;
411 static int ett_eid;
412 static int ett_time;
413 static int ett_create_ts;
414 static int ett_ident;
415 static int ett_block_flags;
416 static int ett_canonical_data;
417 static int ett_payload;
418 static int ett_admin;
419 static int ett_status_rep;
420 static int ett_status_info;
421 static int ett_status_assert;
422 /// Tree structures
423 static int *ett[] = {
424 &ett_bundle,
425 &ett_bundle_flags,
426 &ett_block,
427 &ett_eid,
428 &ett_time,
429 &ett_create_ts,
430 &ett_ident,
431 &ett_block_flags,
432 &ett_canonical_data,
433 &ett_payload,
434 &ett_admin,
435 &ett_status_rep,
436 &ett_status_info,
437 &ett_status_assert,
438 &ett_payload_fragment,
439 &ett_payload_fragments,
442 static const fragment_items payload_frag_items = {
443 /* Fragment subtrees */
444 &ett_payload_fragment,
445 &ett_payload_fragments,
446 /* Fragment fields */
447 &hf_payload_fragments,
448 &hf_payload_fragment,
449 &hf_payload_fragment_overlap,
450 &hf_payload_fragment_overlap_conflicts,
451 &hf_payload_fragment_multiple_tails,
452 &hf_payload_fragment_too_long_fragment,
453 &hf_payload_fragment_error,
454 &hf_payload_fragment_count,
455 /* Reassembled in field */
456 &hf_payload_reassembled_in,
457 &hf_payload_reassembled_length,
458 &hf_payload_reassembled_data,
459 /* Tag */
460 "Payload fragments"
463 static expert_field ei_invalid_framing;
464 static expert_field ei_invalid_bp_version;
465 static expert_field ei_eid_scheme_unknown;
466 static expert_field ei_eid_ssp_type_invalid;
467 static expert_field ei_eid_wkssp_unknown;
468 static expert_field ei_block_type_dupe;
469 static expert_field ei_sub_type_unknown;
470 static expert_field ei_sub_partial_decode;
471 static expert_field ei_crc_type_unknown;
472 static expert_field ei_block_failed_crc;
473 static expert_field ei_block_num_dupe;
474 static expert_field ei_block_payload_index;
475 static expert_field ei_block_payload_num;
476 static expert_field ei_fragment_reassemble_size;
477 static expert_field ei_fragment_tot_mismatch;
478 static expert_field ei_block_sec_bib_tgt;
479 static expert_field ei_block_sec_bcb_tgt;
480 static ei_register_info expertitems[] = {
481 {&ei_invalid_framing, {"bpv7.invalid_framing", PI_MALFORMED, PI_WARN, "Invalid framing", EXPFILL}},
482 {&ei_invalid_bp_version, {"bpv7.invalid_bp_version", PI_MALFORMED, PI_ERROR, "Invalid BP version", EXPFILL}},
483 {&ei_eid_scheme_unknown, {"bpv7.eid_scheme_unknown", PI_UNDECODED, PI_WARN, "Unknown Node ID scheme code", EXPFILL}},
484 {&ei_eid_ssp_type_invalid, {"bpv7.eid_ssp_type_invalid", PI_UNDECODED, PI_WARN, "Invalid scheme-specific part major type", EXPFILL}},
485 {&ei_eid_wkssp_unknown, {"bpv7.eid_wkssp_unknown", PI_UNDECODED, PI_WARN, "Unknown well-known scheme-specific code point", EXPFILL}},
486 {&ei_block_type_dupe, {"bpv7.block_type_dupe", PI_PROTOCOL, PI_WARN, "Too many blocks of this type", EXPFILL}},
487 {&ei_sub_type_unknown, {"bpv7.sub_type_unknown", PI_UNDECODED, PI_WARN, "Unknown type code", EXPFILL}},
488 {&ei_sub_partial_decode, {"bpv7.sub_partial_decode", PI_UNDECODED, PI_WARN, "Data not fully dissected", EXPFILL}},
489 {&ei_crc_type_unknown, {"bpv7.crc_type_unknown", PI_UNDECODED, PI_WARN, "Unknown CRC Type code", EXPFILL}},
490 {&ei_block_failed_crc, {"bpv7.block_failed_crc", PI_CHECKSUM, PI_WARN, "Block failed CRC", EXPFILL}},
491 {&ei_block_num_dupe, {"bpv7.block_num_dupe", PI_PROTOCOL, PI_WARN, "Duplicate block number", EXPFILL}},
492 {&ei_block_payload_index, {"bpv7.block_payload_index", PI_PROTOCOL, PI_WARN, "Payload must be the last block", EXPFILL}},
493 {&ei_block_payload_num, {"bpv7.block_payload_num", PI_PROTOCOL, PI_WARN, "Invalid payload block number", EXPFILL}},
494 {&ei_fragment_reassemble_size, {"bpv7.fragment_reassemble_size", PI_REASSEMBLE, PI_ERROR, "Cannot defragment this size (wireshark limitation)", EXPFILL}},
495 {&ei_fragment_tot_mismatch, {"bpv7.fragment_tot_mismatch", PI_REASSEMBLE, PI_ERROR, "Inconsistent total length between fragments", EXPFILL}},
496 {&ei_block_sec_bib_tgt, {"bpv7.bpsec.bib_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is an integrity target", EXPFILL}},
497 {&ei_block_sec_bcb_tgt, {"bpv7.bpsec.bcb_target", PI_COMMENTS_GROUP, PI_COMMENT, "Block is a confidentiality target", EXPFILL}},
500 /** Delete an arbitrary object allocated under this file scope.
502 * @param ptr The object to delete.
504 static void file_scope_delete(void *ptr) {
505 wmem_free(wmem_file_scope(), ptr);
508 int bp_creation_ts_compare(const void *a, const void *b, void *user_data _U_) {
509 const bp_creation_ts_t *ats = a;
510 const bp_creation_ts_t *bts = b;
511 if (ats->abstime.dtntime < bts->abstime.dtntime) {
512 return -1;
514 else if (ats->abstime.dtntime > bts->abstime.dtntime) {
515 return 1;
518 if (ats->seqno < bts->seqno) {
519 return -1;
521 else if (ats->seqno > bts->seqno) {
522 return 1;
525 return 0;
528 bp_eid_t * bp_eid_new(wmem_allocator_t *alloc) {
529 bp_eid_t *obj = wmem_new0(alloc, bp_eid_t);
530 clear_address(&(obj->uri));
531 return obj;
534 void bp_eid_free(wmem_allocator_t *alloc, bp_eid_t *obj) {
535 wmem_free(alloc, (char *)(obj->dtn_wkssp));
536 wmem_free(alloc, (char *)(obj->dtn_serv));
537 wmem_free(alloc, (void *)(obj->ipn_serv));
538 wmem_free(alloc, obj);
541 bool bp_eid_equal(const void *a, const void *b) {
542 const bp_eid_t *aobj = a;
543 const bp_eid_t *bobj = b;
544 return addresses_equal(&(aobj->uri), &(bobj->uri));
547 bp_block_primary_t * bp_block_primary_new(wmem_allocator_t *alloc) {
548 bp_block_primary_t *obj = wmem_new0(alloc, bp_block_primary_t);
549 obj->dst_eid = bp_eid_new(alloc);
550 obj->src_nodeid = bp_eid_new(alloc);
551 obj->rep_nodeid = bp_eid_new(alloc);
552 obj->frag_offset = NULL;
553 obj->total_len = NULL;
554 obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
555 obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
556 return obj;
559 void bp_block_primary_free(wmem_allocator_t *alloc, bp_block_primary_t *obj) {
560 if (!obj) {
561 return;
563 bp_eid_free(alloc, obj->dst_eid);
564 bp_eid_free(alloc, obj->src_nodeid);
565 bp_eid_free(alloc, obj->rep_nodeid);
566 wmem_free(alloc, obj->frag_offset);
567 wmem_free(alloc, obj->total_len);
568 wmem_free(alloc, obj->sec.data_i);
569 wmem_free(alloc, obj->sec.data_c);
570 wmem_free(alloc, obj);
573 bp_block_canonical_t * bp_block_canonical_new(wmem_allocator_t *alloc, uint64_t blk_ix) {
574 bp_block_canonical_t *obj = wmem_new0(alloc, bp_block_canonical_t);
575 obj->blk_ix = blk_ix;
576 obj->sec.data_i = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
577 obj->sec.data_c = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
578 return obj;
581 static uint64_t * uint64_new(wmem_allocator_t *alloc, const uint64_t val) {
582 uint64_t *obj = wmem_new(alloc, uint64_t);
583 *obj = val;
584 return obj;
587 bp_bundle_t * bp_bundle_new(wmem_allocator_t *alloc) {
588 bp_bundle_t *obj = wmem_new0(alloc, bp_bundle_t);
589 obj->primary = bp_block_primary_new(alloc);
590 obj->blocks = wmem_list_new(alloc);
591 obj->block_nums = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
592 obj->block_types = wmem_map_new(alloc, g_int64_hash, g_int64_equal);
593 return obj;
596 void bp_bundle_free(wmem_allocator_t *alloc, bp_bundle_t *obj) {
597 bp_bundle_ident_free(alloc, obj->ident);
598 bp_block_primary_free(alloc, obj->primary);
599 wmem_destroy_list(obj->blocks);
600 wmem_free(alloc, obj);
603 /** Function to match the GCompareFunc signature.
605 static int bp_bundle_frameloc_compare(const void *a, const void *b) {
606 const bp_bundle_t *aobj = a;
607 const bp_bundle_t *bobj = b;
608 if (aobj->frame_num < bobj->frame_num) {
609 return -1;
611 if (aobj->frame_num > bobj->frame_num) {
612 return 1;
614 if (aobj->layer_num < bobj->layer_num) {
615 return -1;
617 if (aobj->layer_num > bobj->layer_num) {
618 return 1;
620 return 0;
623 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) {
624 DISSECTOR_ASSERT(src != NULL);
625 DISSECTOR_ASSERT(ts != NULL);
626 bp_bundle_ident_t *ident = wmem_new(alloc, bp_bundle_ident_t);
627 copy_address_wmem(alloc, &(ident->src), &(src->uri));
628 ident->ts = *ts;
629 ident->frag_offset = off;
630 ident->total_len = len;
631 return ident;
634 void bp_bundle_ident_free(wmem_allocator_t *alloc, bp_bundle_ident_t *obj) {
635 wmem_free(alloc, obj);
638 /** Either both values are defined and equal or both are null.
640 static bool optional_uint64_equal(const uint64_t *a, const uint64_t *b) {
641 if (a && b) {
642 return (*a == *b);
644 else {
645 return (a == NULL) && (b == NULL);
649 gboolean bp_bundle_ident_equal(const void *a, const void *b) {
650 const bp_bundle_ident_t *aobj = a;
651 const bp_bundle_ident_t *bobj = b;
652 return (
653 addresses_equal(&(aobj->src), &(bobj->src))
654 && (aobj->ts.abstime.dtntime == bobj->ts.abstime.dtntime)
655 && (aobj->ts.seqno == bobj->ts.seqno)
656 && optional_uint64_equal(aobj->frag_offset, bobj->frag_offset)
657 && optional_uint64_equal(aobj->total_len, bobj->total_len)
661 unsigned bp_bundle_ident_hash(const void *key) {
662 const bp_bundle_ident_t *obj = key;
663 return (
664 add_address_to_hash(0, &(obj->src))
665 ^ g_int64_hash(&(obj->ts.abstime.dtntime))
666 ^ g_int64_hash(&(obj->ts.seqno))
670 /** Convert DTN time to time delta.
671 * DTN Time is defined in Section 4.1.6.
673 * @param dtntime Number of milliseconds from an epoch.
674 * @return The associated absolute time.
676 static nstime_t dtn_to_delta(const int64_t dtntime) {
677 nstime_t utctime;
678 utctime.secs = dtntime / 1000;
679 utctime.nsecs = 1000000 * (dtntime % 1000);
680 return utctime;
683 /** Convert DTN time to absolute time.
684 * DTN Time is defined in Section 4.1.6.
686 * @param dtntime Number of milliseconds from an epoch.
687 * @return The associated absolute time.
689 static nstime_t dtn_to_utctime(const int64_t dtntime) {
690 nstime_t utctime;
691 utctime.secs = 946684800 + dtntime / 1000;
692 utctime.nsecs = 1000000 * (dtntime % 1000);
693 return utctime;
696 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) {
697 wmem_allocator_t *alloc_eid = wmem_file_scope();
698 proto_item *item_eid = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
699 proto_tree *tree_eid = proto_item_add_subtree(item_eid, ett_eid);
700 const int eid_start = *offset;
702 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
703 wscbor_require_array_size(chunk, 2, 2);
704 if (!chunk) {
705 proto_item_set_len(item_eid, *offset - eid_start);
706 return item_eid;
709 chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
710 const uint64_t *scheme = wscbor_require_uint64(alloc_eid, chunk);
711 proto_item *item_scheme = proto_tree_add_cbor_uint64(tree_eid, hf_eid_scheme, pinfo, tvb, chunk, scheme);
712 if (!scheme) {
713 wscbor_skip_next_item(pinfo->pool, tvb, offset);
714 return item_eid;
717 wmem_strbuf_t *uribuf = wmem_strbuf_new(alloc_eid, NULL);
718 const char *dtn_wkssp = NULL;
719 const char *dtn_serv = NULL;
720 uint64_t *ipn_serv = NULL;
721 switch (*scheme) {
722 case EID_SCHEME_DTN: {
723 chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
724 switch (chunk->type_major) {
725 case CBOR_TYPE_UINT: {
726 const uint64_t *ssp_code = wscbor_require_uint64(pinfo->pool, chunk);
727 proto_item *item = proto_tree_add_cbor_uint64(tree_eid, hf_eid_dtn_ssp_code, pinfo, tvb, chunk, ssp_code);
729 switch (*ssp_code) {
730 case 0:
731 dtn_wkssp = wmem_strdup(alloc_eid, "none");
732 break;
733 default:
734 expert_add_info(pinfo, item, &ei_eid_wkssp_unknown);
735 break;
737 if (dtn_wkssp) {
738 wmem_strbuf_append_printf(uribuf, "dtn:%s", dtn_wkssp);
740 break;
742 case CBOR_TYPE_STRING: {
743 char *ssp = wscbor_require_tstr(pinfo->pool, chunk);
744 proto_tree_add_cbor_tstr(tree_eid, hf_eid_dtn_ssp_text, pinfo, tvb, chunk);
745 wmem_strbuf_append_printf(uribuf, "dtn:%s", ssp);
747 char *path_sep;
748 if ((path_sep = strrchr(ssp, '/')) != NULL) {
749 dtn_serv = wmem_strdup(alloc_eid, path_sep + 1);
751 else {
752 // no separator also means no authority part, so it's well-known
753 dtn_wkssp = wmem_strdup(alloc_eid, ssp);
756 wmem_free(pinfo->pool, ssp);
757 break;
759 default: {
760 *offset = chunk->start;
761 wscbor_skip_next_item(pinfo->pool, tvb, offset);
762 tvbuff_t *sub_tvb = tvb_new_subset_length(tvb, chunk->start, *offset);
763 call_dissector(handle_cbor, sub_tvb, pinfo, tree_eid);
764 expert_add_info(pinfo, item_eid, &ei_eid_ssp_type_invalid);
765 break;
769 break;
771 case EID_SCHEME_IPN: {
772 chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
773 wscbor_require_array_size(chunk, 2, 2);
774 if (!wscbor_skip_if_errors(pinfo->pool, tvb, offset, chunk)) {
775 chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
776 const uint64_t *node = wscbor_require_uint64(pinfo->pool, chunk);
777 proto_tree_add_cbor_uint64(tree_eid, hf_eid_ipn_node, pinfo, tvb, chunk, node);
779 chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
780 ipn_serv = wscbor_require_uint64(wmem_file_scope(), chunk);
781 proto_tree_add_cbor_uint64(tree_eid, hf_eid_ipn_service, pinfo, tvb, chunk, ipn_serv);
783 wmem_strbuf_append_printf(uribuf, "ipn:%" PRIu64 ".%" PRIu64, node ? *node : 0, ipn_serv ? *ipn_serv : 0);
785 break;
787 default:
788 wscbor_skip_next_item(pinfo->pool, tvb, offset);
789 expert_add_info(pinfo, item_scheme, &ei_eid_scheme_unknown);
790 break;
793 if (dtn_wkssp) {
794 proto_item *item = proto_tree_add_string(tree_eid, hf_eid_dtn_wkssp, tvb, eid_start, *offset - eid_start, dtn_wkssp);
795 proto_item_set_generated(item);
797 if (dtn_serv) {
798 proto_item *item = proto_tree_add_string(tree_eid, hf_eid_dtn_serv, tvb, eid_start, *offset - eid_start, dtn_serv);
799 proto_item_set_generated(item);
802 char *uri = NULL;
803 if (wmem_strbuf_get_len(uribuf) > 0) {
804 uri = wmem_strbuf_finalize(uribuf);
806 proto_item *item_uri = proto_tree_add_string(tree_eid, hfindex_uri, tvb, eid_start, *offset - eid_start, uri);
807 proto_item_set_generated(item_uri);
809 proto_item_append_text(item_eid, ": %s", uri);
812 if (eid) {
813 eid->scheme = (scheme ? *scheme : 0);
814 if (uri) {
815 set_address(&(eid->uri), AT_STRINGZ, (int)strlen(uri) + 1, uri);
817 else {
818 clear_address(&(eid->uri));
820 eid->dtn_wkssp = dtn_wkssp;
821 eid->dtn_serv = dtn_serv;
822 eid->ipn_serv = ipn_serv;
824 else {
825 file_scope_delete(uri);
826 file_scope_delete((char *)dtn_wkssp);
827 file_scope_delete((char *)dtn_serv);
828 file_scope_delete(ipn_serv);
831 proto_item_set_len(item_eid, *offset - eid_start);
832 return item_eid;
835 static void dissect_dtn_time(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, int *offset, bp_dtn_time_t *out)
837 proto_item *item_time = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
838 proto_tree *tree_time = proto_item_add_subtree(item_time, ett_time);
839 const int offset_start = *offset;
841 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
842 if (chunk) {
843 const uint64_t *dtntime = wscbor_require_uint64(pinfo->pool, chunk);
844 proto_tree_add_cbor_uint64(tree_time, hf_time_dtntime, pinfo, tvb, chunk, dtntime);
846 if (dtntime) {
847 if (out) {
848 out->dtntime = *dtntime;
851 if (*dtntime > 0) {
852 const nstime_t utctime = dtn_to_utctime(*dtntime);
853 proto_item *item_utctime = proto_tree_add_time(tree_time, hf_time_utctime, tvb, chunk->start, chunk->data_length, &utctime);
854 proto_item_set_generated(item_utctime);
856 char *time_text = abs_time_to_str(pinfo->pool, &utctime, ABSOLUTE_TIME_UTC, true);
857 proto_item_append_text(item_time, ": %s", time_text);
859 if (out) {
860 out->utctime = utctime;
863 else {
864 proto_item_append_text(item_time, ": undefined");
867 else if (out) {
868 out->dtntime = 0;
869 nstime_set_zero(&(out->utctime));
872 proto_item_set_len(item_time, *offset - offset_start);
875 /** Extract a timestamp.
877 * @param tree The tree to write items under.
878 * @param hfindex The root item field.
879 * @param pinfo Packet info to update.
880 * @param tvb Buffer to read from.
881 * @param[in,out] offset Starting offset within @c tvb.
882 * @param[out] ts If non-null, the timestamp to write to.
884 static void dissect_cbor_timestamp(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, int *offset, bp_creation_ts_t *ts)
886 proto_item *item_ts = proto_tree_add_item(tree, hfindex, tvb, *offset, -1, ENC_NA);
887 proto_tree *tree_ts = proto_item_add_subtree(item_ts, ett_create_ts);
889 wscbor_chunk_t *chunk_ts = wscbor_chunk_read(pinfo->pool, tvb, offset);
890 wscbor_require_array_size(chunk_ts, 2, 2);
891 wscbor_chunk_mark_errors(pinfo, item_ts, chunk_ts);
892 if (!wscbor_skip_if_errors(pinfo->pool, tvb, offset, chunk_ts)) {
893 bp_dtn_time_t abstime;
894 dissect_dtn_time(tree_ts, hf_create_ts_time, pinfo, tvb, offset, &abstime);
896 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
897 const uint64_t *seqno = wscbor_require_uint64(wmem_file_scope(), chunk);
898 proto_tree_add_cbor_uint64(tree_ts, hf_create_ts_seqno, pinfo, tvb, chunk, seqno);
900 if (ts) {
901 ts->abstime = abstime;
902 ts->seqno = (seqno ? *seqno : 0);
905 proto_item_set_len(item_ts, *offset - chunk_ts->start);
908 /** Label type-field items with sub-dissector name.
909 * This is similar to using val64_string labels but based on dissector name.
911 * @param type_code The dissected value, which must not be null.
912 * @param type_dissect The associated sub-dissector, which may be null.
913 * @param[in,out] item_type The item associated with the type field.
914 * @param[in,out] item_parent The parent item to label.
916 static void label_type_field(const uint64_t *type_code, dissector_handle_t type_dissect, proto_item *item_type, proto_item *item_parent)
918 if (!item_type || !item_parent) {
919 return;
921 const char *type_name = dissector_handle_get_description(type_dissect);
922 if (type_name) {
923 proto_item_append_text(item_parent, ": %s", type_name);
925 else {
926 proto_item_append_text(item_parent, ": Type %" PRIu64, *type_code);
927 type_name = "Unknown";
929 proto_item_set_text(item_type, "%s: %s (%" PRIu64 ")", PITEM_HFINFO(item_type)->name, type_name, *type_code);
932 /** Show read-in and actual CRC information.
934 * @param tvb The single-block data.
935 * @param crc_type Type of CRC to compute.
936 * @param crc_field The read-in field value.
938 static void show_crc_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block, const uint64_t *crc_type, tvbuff_t *crc_field) {
939 if (!crc_type || !crc_field) {
940 return;
943 // Display the data field information
944 int hf_crc_field;
945 switch (*crc_type) {
946 case BP_CRC_16:
947 hf_crc_field = hf_crc_field_uint16;
948 break;
949 case BP_CRC_32:
950 hf_crc_field = hf_crc_field_uint32;
951 break;
952 default:
953 hf_crc_field = -1;
954 break;
957 // Compare against expected result
958 uint32_t crc_actual = 0;
959 unsigned chksum_flags = PROTO_CHECKSUM_NO_FLAGS;
960 if (bp_compute_crc) {
961 if (*crc_type == BP_CRC_NONE) {
962 chksum_flags |= PROTO_CHECKSUM_NOT_PRESENT;
964 else {
965 const unsigned block_len = tvb_reported_length(tvb);
966 uint8_t *crcbuf = tvb_memdup(pinfo->pool, tvb, 0, block_len);
967 switch (*crc_type) {
968 case BP_CRC_16:
969 memset(crcbuf + block_len - 2, 0, 2);
970 crc_actual = crc16_ccitt(crcbuf, block_len);
971 break;
972 case BP_CRC_32:
973 memset(crcbuf + block_len - 4, 0, 4);
974 crc_actual = ~crc32c_calculate_no_swap(crcbuf, block_len, CRC32C_PRELOAD);
975 break;
976 default:
977 break;
979 wmem_free(pinfo->pool, crcbuf);
981 chksum_flags |= PROTO_CHECKSUM_VERIFY;
984 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);
987 static proto_item * proto_tree_add_ident(packet_info *pinfo, proto_tree *tree, int hfindex, tvbuff_t *tvb, const bp_bundle_ident_t *ident) {
988 wmem_strbuf_t *ident_text = wmem_strbuf_new(pinfo->pool, NULL);
989 wmem_strbuf_append_printf(
990 ident_text,
991 "Source: %s, DTN Time: %" PRIu64 ", Seq: %" PRIu64,
992 address_to_name(&(ident->src)),
993 ident->ts.abstime.dtntime,
994 ident->ts.seqno
996 if (ident->frag_offset) {
997 wmem_strbuf_append_printf(ident_text, ", Frag Offset: %" PRIu64, *(ident->frag_offset));
999 if (ident->total_len) {
1000 wmem_strbuf_append_printf(ident_text, ", Total Length: %" PRIu64, *(ident->total_len));
1003 proto_item *item_ident = proto_tree_add_string(tree, hfindex, tvb, 0, 0, wmem_strbuf_get_str(ident_text));
1004 proto_item_set_generated(item_ident);
1005 wmem_strbuf_finalize(ident_text);
1006 return item_ident;
1010 static int dissect_block_primary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block,
1011 int start, bp_block_primary_t *block,
1012 bp_bundle_t *bundle _U_) {
1013 proto_item *item_block = proto_tree_get_parent(tree_block);
1014 int field_ix = 0;
1015 int offset = start;
1016 block->item_block = item_block;
1018 wscbor_chunk_t *chunk_block = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1019 wscbor_require_array_size(chunk_block, 8, 11);
1020 wscbor_chunk_mark_errors(pinfo, item_block, chunk_block);
1021 if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_block)) {
1022 return offset - start;
1024 #if 0
1025 proto_item_append_text(item_block, ", Items: %" PRIu64, chunk_block->head_value);
1026 #endif
1028 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1029 const uint64_t *version = wscbor_require_uint64(pinfo->pool, chunk);
1030 proto_item *item_version = proto_tree_add_cbor_uint64(tree_block, hf_primary_version, pinfo, tvb, chunk, version);
1031 field_ix++;
1032 if (version && (*version != 7)) {
1033 expert_add_info(pinfo, item_version, &ei_invalid_bp_version);
1036 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1037 const uint64_t *flags = wscbor_require_uint64(pinfo->pool, chunk);
1038 proto_tree_add_cbor_bitmask(tree_block, hf_primary_bundle_flags, ett_bundle_flags, bundle_flags, pinfo, tvb, chunk, flags);
1039 field_ix++;
1040 block->flags = (flags ? *flags : 0);
1042 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1043 uint64_t *crc_type = wscbor_require_uint64(pinfo->pool, chunk);
1044 proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type);
1045 field_ix++;
1046 block->crc_type = (crc_type ? *crc_type : BP_CRC_NONE);
1047 if (crc_type) {
1048 proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str(*crc_type, crc_vals, "%" PRIu64));
1051 proto_tree_add_cbor_eid(tree_block, hf_primary_dst_eid, hf_primary_dst_uri, pinfo, tvb, &offset, block->dst_eid);
1052 field_ix++;
1053 if (block->dst_eid->uri.type != AT_NONE) {
1054 proto_item_set_hidden(
1055 proto_tree_add_string(tree_block, hf_primary_srcdst_uri, tvb, 0, 0, address_to_name(&(block->dst_eid->uri)))
1059 proto_tree_add_cbor_eid(tree_block, hf_primary_src_nodeid, hf_primary_src_uri, pinfo, tvb, &offset, block->src_nodeid);
1060 field_ix++;
1061 if (block->src_nodeid->uri.type != AT_NONE) {
1062 proto_item_set_hidden(
1063 proto_tree_add_string(tree_block, hf_primary_srcdst_uri, tvb, 0, 0, address_to_name(&(block->src_nodeid->uri)))
1067 proto_tree_add_cbor_eid(tree_block, hf_primary_report_nodeid, hf_primary_report_uri, pinfo, tvb, &offset, block->rep_nodeid);
1068 field_ix++;
1070 // Complex type
1071 dissect_cbor_timestamp(tree_block, hf_primary_create_ts, pinfo, tvb, &offset, &(block->ts));
1072 field_ix++;
1074 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1075 const uint64_t *lifetime = wscbor_require_uint64(pinfo->pool, chunk);
1076 proto_tree_add_cbor_uint64(tree_block, hf_primary_lifetime, pinfo, tvb, chunk, lifetime);
1077 if (lifetime) {
1078 nstime_t lifetime_exp = dtn_to_delta(*lifetime);
1079 proto_item *item_lifetime_exp = proto_tree_add_time(tree_block, hf_primary_lifetime_exp, tvb, chunk->start, chunk->head_length, &lifetime_exp);
1080 proto_item_set_generated(item_lifetime_exp);
1082 if (block->ts.abstime.dtntime > 0) {
1083 nstime_t expiretime;
1084 nstime_sum(&expiretime, &(block->ts.abstime.utctime), &lifetime_exp);
1085 proto_item *item_expiretime = proto_tree_add_time(tree_block, hf_primary_expire_ts, tvb, 0, 0, &expiretime);
1086 proto_item_set_generated(item_expiretime);
1089 field_ix++;
1091 // optional items
1092 if (flags && (*flags & BP_BUNDLE_IS_FRAGMENT)) {
1093 if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 3)) {
1094 // Skip whole array
1095 offset = start;
1096 wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1098 return offset - start;
1101 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1102 block->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk);
1103 proto_tree_add_cbor_uint64(tree_block, hf_primary_frag_offset, pinfo, tvb, chunk, block->frag_offset);
1104 field_ix++;
1106 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1107 block->total_len = wscbor_require_uint64(wmem_file_scope(), chunk);
1108 proto_tree_add_cbor_uint64(tree_block, hf_primary_total_length, pinfo, tvb, chunk, block->total_len);
1109 field_ix++;
1112 switch (block->crc_type) {
1113 case BP_CRC_NONE:
1114 break;
1115 case BP_CRC_16:
1116 case BP_CRC_32: {
1117 if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) {
1118 // Skip whole array
1119 offset = start;
1120 wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1122 return offset - start;
1125 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1126 tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk);
1127 field_ix++;
1128 block->crc_field = crc_field;
1130 tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start);
1131 show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field);
1132 break;
1134 default:
1135 expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown);
1136 break;
1139 return offset - start;
1142 static int dissect_block_canonical(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree_block,
1143 int start, bp_block_canonical_t *block,
1144 bp_bundle_t *bundle _U_) {
1145 proto_item *item_block = proto_tree_get_parent(tree_block);
1146 int field_ix = 0;
1147 int offset = start;
1148 block->item_block = item_block;
1150 wscbor_chunk_t *chunk_block = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1151 wscbor_require_array_size(chunk_block, 5, 6);
1152 wscbor_chunk_mark_errors(pinfo, item_block, chunk_block);
1153 if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_block)) {
1154 return offset - start;
1156 #if 0
1157 proto_item_append_text(item_block, ", Items: %" PRIu64, chunk_block->head_value);
1158 #endif
1160 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1161 uint64_t *type_code = wscbor_require_uint64(wmem_file_scope(), chunk);
1162 proto_item *item_type = proto_tree_add_cbor_uint64(tree_block, hf_canonical_type_code, pinfo, tvb, chunk, type_code);
1163 field_ix++;
1164 block->type_code = type_code;
1166 if (type_code) {
1167 dissector_handle_t type_dissect = dissector_get_custom_table_handle(block_dissectors, type_code);
1168 label_type_field(type_code, type_dissect, item_type, item_block);
1170 // Check duplicate of this type
1171 uint64_t limit = UINT64_MAX;
1172 for (int ix = 0; ; ++ix) {
1173 const blocktype_limit *row = blocktype_limits + ix;
1174 if (row->type_code == BP_BLOCKTYPE_INVALID) {
1175 break;
1177 if (row->type_code == *type_code) {
1178 limit = row->limit;
1179 break;
1183 uint64_t count = 1; // this block counts regardless of presence in the map
1184 wmem_list_t *list_found = wmem_map_lookup(bundle->block_types, type_code);
1185 if (list_found) {
1186 for (wmem_list_frame_t *it = wmem_list_head(list_found); it;
1187 it = wmem_list_frame_next(it)) {
1188 bp_block_canonical_t *block_found = wmem_list_frame_data(it);
1189 if (block == block_found) {
1190 continue;
1192 ++count;
1195 if (count > limit) {
1196 // First non-identical block triggers the error
1197 expert_add_info(pinfo, item_type, &ei_block_type_dupe);
1201 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1202 uint64_t *block_num = wscbor_require_uint64(wmem_file_scope(), chunk);
1203 proto_item *item_block_num = proto_tree_add_cbor_uint64(tree_block, hf_canonical_block_num, pinfo, tvb, chunk, block_num);
1204 field_ix++;
1205 block->block_number = block_num;
1206 if (block_num) {
1207 proto_item_append_text(item_block, ", Block Num: %" PRIu64, *block_num);
1210 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1211 const uint64_t *flags = wscbor_require_uint64(wmem_file_scope(), chunk);
1212 proto_tree_add_cbor_bitmask(tree_block, hf_canonical_block_flags, ett_block_flags, block_flags, pinfo, tvb, chunk, flags);
1213 field_ix++;
1214 block->flags = (flags ? *flags : 0);
1216 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1217 uint64_t *crc_type = wscbor_require_uint64(wmem_file_scope(), chunk);
1218 proto_item *item_crc_type = proto_tree_add_cbor_uint64(tree_block, hf_crc_type, pinfo, tvb, chunk, crc_type);
1219 field_ix++;
1220 block->crc_type = (crc_type ? *crc_type : BP_CRC_NONE);
1221 if (crc_type) {
1222 proto_item_append_text(item_block, ", CRC Type: %s", val64_to_str(*crc_type, crc_vals, "%" PRIu64));
1225 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1226 tvbuff_t *tvb_data = wscbor_require_bstr(wmem_file_scope(), chunk);
1227 field_ix++;
1228 block->data = tvb_data;
1230 proto_item_set_generated(
1231 proto_tree_add_cbor_strlen(tree_block, hf_canonical_data_size, pinfo, tvb, chunk)
1233 proto_item *item_data = proto_tree_add_cbor_bstr(tree_block, hf_canonical_data, pinfo, tvb, chunk);
1234 proto_tree *tree_data = proto_item_add_subtree(item_data, ett_canonical_data);
1235 block->tree_data = tree_data;
1237 switch (block->crc_type) {
1238 case BP_CRC_NONE:
1239 break;
1240 case BP_CRC_16:
1241 case BP_CRC_32: {
1242 if (!wscbor_require_array_size(chunk_block, field_ix + 1, field_ix + 1)) {
1243 // Skip whole array
1244 offset = start;
1245 wscbor_skip_next_item(pinfo->pool, tvb, &offset);
1247 return offset - start;
1250 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1251 tvbuff_t *crc_field = wscbor_require_bstr(wmem_file_scope(), chunk);
1252 field_ix++;
1253 block->crc_field = crc_field;
1255 tvbuff_t *tvb_block = tvb_new_subset_length(tvb, start, offset - start);
1256 show_crc_info(tvb_block, pinfo, tree_block, crc_type, crc_field);
1257 break;
1259 default:
1260 expert_add_info(pinfo, item_crc_type, &ei_crc_type_unknown);
1261 break;
1264 wmem_list_append(bundle->blocks, block);
1266 if (block->type_code) {
1267 wmem_list_t *type_list = wmem_map_lookup(bundle->block_types, block->type_code);
1268 if (!type_list) {
1269 uint64_t *key = uint64_new(wmem_file_scope(), *(block->type_code));
1270 type_list = wmem_list_new(wmem_file_scope());
1271 wmem_map_insert(bundle->block_types, key, type_list);
1273 wmem_list_append(type_list, block);
1275 if (block->block_number) {
1276 bp_block_canonical_t *found = wmem_map_lookup(bundle->block_nums, block->block_number);
1277 if (found) {
1278 expert_add_info(pinfo, item_block_num, &ei_block_num_dupe);
1280 else {
1281 uint64_t *key = uint64_new(wmem_file_scope(), *(block->block_number));
1282 wmem_map_insert(bundle->block_nums, key, block);
1285 // Payload block requirements
1286 if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1287 // must have index zero
1288 if (block->block_number && (*(block->block_number) != 1)) {
1289 expert_add_info(pinfo, item_block_num, &ei_block_payload_num);
1293 return offset - start;
1296 typedef struct {
1297 packet_info *pinfo;
1298 proto_item *pi;
1299 expert_field *eiindex;
1300 const char *sectype;
1301 } bpsec_block_mark_t;
1302 /// Mark blocks with BPSec expert info
1303 static void mark_target_block(void *key, void *value _U_, void *user_data) {
1304 const uint64_t *blk_num = (uint64_t *)key;
1305 const bpsec_block_mark_t *mark = (bpsec_block_mark_t *)user_data;
1306 expert_add_info_format(
1307 mark->pinfo, mark->pi, mark->eiindex,
1308 "Block is targed by %s block number %" PRIu64, mark->sectype, *blk_num
1311 static void apply_bpsec_mark(const security_mark_t *sec, packet_info *pinfo, proto_item *pi) {
1313 bpsec_block_mark_t mark;
1314 mark.pinfo = pinfo;
1315 mark.pi = pi;
1316 mark.eiindex = &ei_block_sec_bib_tgt;
1317 mark.sectype = "BIB";
1318 wmem_map_foreach(sec->data_i, mark_target_block, &mark);
1321 bpsec_block_mark_t mark;
1322 mark.pinfo = pinfo;
1323 mark.pi = pi;
1324 mark.eiindex = &ei_block_sec_bcb_tgt;
1325 mark.sectype = "BCB";
1326 wmem_map_foreach(sec->data_c, mark_target_block, &mark);
1330 /** Extract data from a block (including payload and admin).
1332 * @param dissector The optional dissector to call.
1333 * @param context Context for the @c dissector.
1334 * @param tvb Buffer to read from.
1335 * @param pinfo Packet info to update.
1336 * @param tree The tree to write items under.
1337 * @param type_exp True if the type code is in the private/experimental range.
1338 * @return The number of dissected octets.
1340 static int dissect_carried_data(dissector_handle_t dissector, void *context, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool type_exp) {
1341 int sublen = 0;
1342 if (dissector) {
1343 sublen = call_dissector_only(dissector, tvb, pinfo, tree, context);
1344 if ((sublen < 0) ||
1345 ((sublen > 0) && ((unsigned)sublen < tvb_reported_length(tvb)))) {
1346 expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_partial_decode);
1349 else if (!type_exp) {
1350 expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_sub_type_unknown);
1353 if ((sublen <= 0) && bp_payload_try_heur) {
1354 heur_dtbl_entry_t *entry = NULL;
1355 if (dissector_try_heuristic(btsd_heur, tvb, pinfo, tree, &entry, context)) {
1356 sublen = tvb_reported_length(tvb);
1359 if (sublen == 0) {
1360 sublen = call_data_dissector(tvb, pinfo, tree);
1362 return sublen;
1365 /** Handle iteration over status subject set.
1368 static void show_status_subj_ref(void *key, void *val _U_, void *data) {
1369 bp_bundle_ident_t *status_ident = key;
1370 proto_tree *tree_bundle = data;
1371 const wmem_list_t *subj_list = wmem_map_lookup(bp_history->bundles, status_ident);
1372 const wmem_list_frame_t *subj_it = subj_list ? wmem_list_head(subj_list) : NULL;
1373 const bp_bundle_t *subj_found = subj_it ? wmem_list_frame_data(subj_it) : NULL;
1374 if (subj_found) {
1375 proto_item *item_subj_ref = proto_tree_add_uint(tree_bundle, hf_bundle_status_ref, NULL, 0, 0, subj_found->frame_num);
1376 proto_item_set_generated(item_subj_ref);
1380 /// Stable sort, preserving relative order of same priority
1381 static int block_dissect_sort(const void *a, const void *b) {
1382 DISSECTOR_ASSERT(a && b);
1383 const bp_block_canonical_t *aobj = *(bp_block_canonical_t **)a;
1384 const bp_block_canonical_t *bobj = *(bp_block_canonical_t **)b;
1385 const int aord = blocktype_order(aobj);
1386 const int bord = blocktype_order(bobj);
1387 if (aord < bord) {
1388 return -1;
1390 else if (aord > bord) {
1391 return 1;
1394 return g_int_equal(&(aobj->blk_ix), &(bobj->blk_ix));
1397 /// Top-level protocol dissector
1398 static int dissect_bp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1400 const char *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL);
1401 if (g_strcmp0(proto_name, proto_name_bp) != 0) {
1402 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp);
1403 col_clear(pinfo->cinfo, COL_INFO);
1406 int offset = 0;
1408 proto_item *item_bundle = proto_tree_add_item(tree, proto_bp, tvb, 0, -1, ENC_NA);
1409 proto_tree *tree_bundle = proto_item_add_subtree(item_bundle, ett_bundle);
1411 bp_bundle_t *bundle = bp_bundle_new(wmem_file_scope());
1412 bundle->frame_num = pinfo->num;
1413 bundle->layer_num = pinfo->curr_layer_num;
1414 bundle->frame_time = pinfo->abs_ts;
1416 // Read blocks directly from buffer with same addresses as #tvb
1417 const unsigned buflen = tvb_reported_length(tvb);
1419 // Require indefinite-length array type
1420 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1421 proto_item *item_head = proto_tree_add_item(tree_bundle, hf_bundle_head, tvb, chunk->start, chunk->data_length, ENC_NA);
1422 wscbor_require_array(chunk);
1423 if (wscbor_chunk_mark_errors(pinfo, item_head, chunk)) {
1424 return 0;
1426 else if (chunk->type_minor != 31) {
1427 expert_add_info_format(pinfo, item_head, &ei_invalid_framing, "Expected indefinite length array");
1428 // continue on even for definite-length array
1431 uint64_t block_ix = 0;
1432 while (true) {
1433 if (offset >= (int)buflen) {
1434 proto_item *item_break = proto_tree_add_item(tree_bundle, hf_bundle_break, tvb, offset, -1, ENC_NA);
1435 expert_add_info_format(pinfo, item_break, &ei_invalid_framing, "Array break missing");
1436 break;
1439 // Either detect BREAK or decode block
1440 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1441 if (wscbor_is_indefinite_break(chunk)) {
1442 proto_tree_add_cbor_ctrl(tree_bundle, hf_bundle_break, pinfo, tvb, chunk);
1443 break;
1445 offset = chunk->start;
1447 // Load just the array start
1448 const int block_start = offset;
1449 proto_item *item_block = proto_tree_add_item(tree_bundle, hf_block, tvb, block_start, -1, ENC_NA);
1450 proto_tree *tree_block = proto_item_add_subtree(item_block, ett_block);
1452 if (block_ix == 0) {
1453 // Primary block
1454 proto_item_prepend_text(item_block, "Primary ");
1455 const int sublen = dissect_block_primary(tvb, pinfo, tree_block, offset, bundle->primary, bundle);
1456 if (sublen <= 0) {
1457 break;
1459 offset += sublen;
1461 if (!(bundle->ident)) {
1462 bundle->ident = bp_bundle_ident_new(
1463 wmem_file_scope(),
1464 bundle->primary->src_nodeid,
1465 &(bundle->primary->ts),
1466 bundle->primary->frag_offset,
1467 bundle->primary->total_len
1469 proto_item *item_ident = proto_tree_add_ident(pinfo, tree_bundle, hf_bundle_ident, tvb, bundle->ident);
1470 proto_tree *tree_ident = proto_item_add_subtree(item_ident, ett_ident);
1472 const wmem_list_t *seen_list = wmem_map_lookup(bp_history->bundles, bundle->ident);
1473 wmem_list_frame_t *seen_it = seen_list ? wmem_list_head(seen_list) : NULL;
1474 const bp_bundle_t *seen_first = seen_it ? wmem_list_frame_data(seen_it) : NULL;
1475 // show first occurrence if not this one
1476 if (seen_first && (seen_first->frame_num != pinfo->num)) {
1477 proto_item *item_seen = proto_tree_add_uint(tree_ident, hf_bundle_first_seen, tvb, 0, 0, seen_first->frame_num);
1478 proto_item_set_generated(item_seen);
1480 nstime_t td;
1481 nstime_delta(&td, &(bundle->frame_time), &(seen_first->frame_time));
1482 proto_item *item_td = proto_tree_add_time(tree_ident, hf_bundle_seen_time_diff, tvb, 0, 0, &td);
1483 proto_item_set_generated(item_td);
1485 // show any retransmits
1486 else if (seen_it) {
1487 for (seen_it = wmem_list_frame_next(seen_it); seen_it != NULL; seen_it = wmem_list_frame_next(seen_it)) {
1488 const bp_bundle_t *seen_re = wmem_list_frame_data(seen_it);
1489 if (seen_re && (seen_re->frame_num != pinfo->num)) {
1490 proto_item *item_seen = proto_tree_add_uint(tree_ident, hf_bundle_retrans_seen, tvb, 0, 0, seen_re->frame_num);
1491 proto_item_set_generated(item_seen);
1496 // Indicate related status (may be multiple)
1497 wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, bundle->ident);
1498 if (status_set) {
1499 wmem_map_foreach(status_set, show_status_subj_ref, tree_ident);
1503 const bp_eid_t *dst = bundle->primary->dst_eid;
1504 if (dst) {
1505 proto_item *item_dst = NULL;
1506 if (dst->dtn_serv) {
1507 item_dst = proto_tree_add_string(tree_bundle, hf_bundle_dst_dtn_srv, tvb, 0, 0, dst->dtn_serv);
1509 else if (dst->ipn_serv) {
1510 item_dst = proto_tree_add_uint64(tree_bundle, hf_bundle_dst_ipn_srv, tvb, 0, 0, *(dst->ipn_serv));
1512 if (item_dst) {
1513 proto_item_set_generated(item_dst);
1518 else {
1519 // Non-primary block
1520 proto_item_prepend_text(item_block, "Canonical ");
1521 bp_block_canonical_t *block = bp_block_canonical_new(wmem_file_scope(), block_ix);
1522 const int sublen = dissect_block_canonical(tvb, pinfo, tree_block, offset, block, bundle);
1523 if (sublen <= 0) {
1524 break;
1526 offset += sublen;
1529 proto_item_set_len(item_block, offset - block_start);
1530 block_ix++;
1533 { // Extract primary block info first
1534 const bp_block_primary_t *primary = bundle->primary;
1536 proto_item_append_text(item_bundle, ", Src: %s", address_to_name(&(primary->src_nodeid->uri)));
1537 proto_item_append_text(item_bundle, ", Dst: %s", address_to_name(&(primary->dst_eid->uri)));
1538 if (bundle->ident) {
1539 proto_item_append_text(item_bundle, ", Time: %" PRIu64, bundle->ident->ts.abstime.dtntime);
1540 proto_item_append_text(item_bundle, ", Seq: %" PRIu64, bundle->ident->ts.seqno);
1542 proto_item_append_text(item_bundle, ", Blocks: %" PRIu64, block_ix);
1544 if ((primary->src_nodeid->uri.type != AT_NONE)
1545 && (primary->dst_eid->uri.type != AT_NONE)) {
1546 // conversation starts in either order
1547 address *addra, *addrb;
1548 if (cmp_address(&(primary->src_nodeid->uri), &(primary->dst_eid->uri)) < 0) {
1549 addra = &(primary->src_nodeid->uri);
1550 addrb = &(primary->dst_eid->uri);
1552 else {
1553 addra = &(primary->dst_eid->uri);
1554 addrb = &(primary->src_nodeid->uri);
1557 copy_address_shallow(&(pinfo->src), &(primary->src_nodeid->uri));
1558 copy_address_shallow(&(pinfo->dst), &(primary->dst_eid->uri));
1559 pinfo->ptype = PT_NONE;
1561 conversation_element_t *conv_el = wmem_alloc_array(pinfo->pool, conversation_element_t, 3);
1562 conv_el[0].type=CE_ADDRESS;
1563 copy_address_shallow(&(conv_el[0].addr_val), addra);
1564 conv_el[1].type=CE_ADDRESS;
1565 copy_address_shallow(&(conv_el[1].addr_val), addrb);
1566 conv_el[2].type=CE_CONVERSATION_TYPE;
1567 conv_el[2].conversation_type_val=CONVERSATION_BP;
1569 pinfo->use_conv_addr_port_endpoints = false;
1570 pinfo->conv_addr_port_endpoints = NULL;
1571 pinfo->conv_elements = conv_el;
1572 find_or_create_conversation(pinfo);
1576 // Extract pre-dissection block info
1577 for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1578 it = wmem_list_frame_next(it)) {
1579 bp_block_canonical_t *block = wmem_list_frame_data(it);
1580 if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1581 // must be last block (i.e. next is NULL)
1582 if (wmem_list_frame_next(it)) {
1583 expert_add_info(pinfo, block->item_block, &ei_block_payload_index);
1586 if (block->data) {
1587 bundle->pyld_start = wmem_new(wmem_file_scope(), unsigned);
1588 *(bundle->pyld_start) = tvb_raw_offset(block->data) - tvb_raw_offset(tvb);
1589 bundle->pyld_len = wmem_new(wmem_file_scope(), unsigned);
1590 *(bundle->pyld_len) = tvb_reported_length(block->data);
1595 // Handle block-type-specific data after all blocks are present
1596 wmem_array_t *sorted = wmem_array_sized_new(
1597 pinfo->pool, sizeof(bp_block_canonical_t*),
1598 wmem_list_count(bundle->blocks)
1600 unsigned ix = 0;
1601 for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1602 it = wmem_list_frame_next(it), ++ix) {
1603 bp_block_canonical_t *block = wmem_list_frame_data(it);
1604 wmem_array_append_one(sorted, block);
1606 wmem_array_sort(sorted, block_dissect_sort);
1608 // Dissect in sorted order (payload last)
1609 for (ix = 0; ix < wmem_array_get_count(sorted); ++ix) {
1610 bp_block_canonical_t *block = *(bp_block_canonical_t **)wmem_array_index(sorted, ix);
1612 // Show payload block info before BTSD dissection (if possible)
1613 if (block->type_code && (*(block->type_code) == BP_BLOCKTYPE_PAYLOAD)) {
1614 if (block->data) {
1615 proto_item_append_text(item_bundle, ", Payload-Size: %d", *(bundle->pyld_len));
1618 col_append_str(pinfo->cinfo, COL_INFO, "Payload Size: ");
1619 if (block->data) {
1620 col_append_fstr(pinfo->cinfo, COL_INFO, "%d", *(bundle->pyld_len));
1622 else {
1623 col_append_str(pinfo->cinfo, COL_INFO, "N/A");
1625 if (wmem_map_size(block->sec.data_c) > 0) {
1626 col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "BPSec BCB target");
1628 if (wmem_map_size(block->sec.data_i) > 0) {
1629 col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "BPSec BIB target");
1632 const bool is_admin = bundle->primary->flags & BP_BUNDLE_PAYLOAD_ADMIN;
1633 if (is_admin) {
1634 col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Admin. Record");
1638 // Ignore when data is absent or is a
1639 // confidentiality target (i.e. ciphertext)
1640 if (!(block->data) || (wmem_map_size(block->sec.data_c) > 0)) {
1641 continue;
1644 // sub-dissect after all is read
1645 dissector_handle_t data_dissect = NULL;
1646 bool type_exp = false;
1647 if (block->type_code) {
1648 data_dissect = dissector_get_custom_table_handle(block_dissectors, block->type_code);
1649 type_exp = (*(block->type_code) >= 192) && (*(block->type_code) <= 255);
1652 bp_dissector_data_t dissect_data;
1653 dissect_data.bundle = bundle;
1654 dissect_data.block = block;
1655 dissect_carried_data(data_dissect, &dissect_data, block->data, pinfo, block->tree_data, type_exp);
1658 // Block-data-derived markings
1659 apply_bpsec_mark(&(bundle->primary->sec), pinfo, bundle->primary->item_block);
1660 for (wmem_list_frame_t *it = wmem_list_head(bundle->blocks); it;
1661 it = wmem_list_frame_next(it)) {
1662 bp_block_canonical_t *block = wmem_list_frame_data(it);
1663 apply_bpsec_mark(&(block->sec), pinfo, block->item_block);
1666 if (bundle->pyld_start && bundle->pyld_len) {
1667 // Treat payload as non-protocol data
1668 const unsigned trailer_start = *(bundle->pyld_start) + *(bundle->pyld_len);
1669 proto_item_set_len(item_bundle, *(bundle->pyld_start));
1670 proto_tree_set_appendix(tree_bundle, tvb, trailer_start, offset - trailer_start);
1672 else {
1673 proto_item_set_len(item_bundle, offset);
1676 // Tap handles after all blocks are dissected
1677 bp_bundle_t *unique_bundle = bundle;
1678 if (bundle->ident) {
1679 // Keep bundle metadata around for the whole file
1680 wmem_list_t *found_list = wmem_map_lookup(bp_history->bundles, bundle->ident);
1681 if (!found_list) {
1682 found_list = wmem_list_new(wmem_file_scope());
1683 wmem_map_insert(bp_history->bundles, bundle->ident, found_list);
1686 const wmem_list_frame_t *found_it = wmem_list_find_custom(found_list, bundle, bp_bundle_frameloc_compare);
1687 bp_bundle_t *found_item = found_it ? wmem_list_frame_data(found_it) : NULL;
1688 if (!found_item) {
1689 wmem_list_append(found_list, bundle);
1691 else {
1692 bp_bundle_free(wmem_file_scope(), bundle);
1693 unique_bundle = found_item;
1696 p_add_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE, unique_bundle);
1697 tap_queue_packet(bp_tap, pinfo, unique_bundle);
1699 return offset;
1702 static bool dissect_status_assertion(proto_tree *tree, int hfassert, packet_info *pinfo, tvbuff_t *tvb, int *offset)
1704 proto_item *item_assert = proto_tree_add_item(tree, hfassert, tvb, *offset, -1, ENC_NA);
1706 bool result = false;
1708 wscbor_chunk_t *chunk_assert = wscbor_chunk_read(pinfo->pool, tvb, offset);
1709 wscbor_require_array_size(chunk_assert, 1, 2);
1710 wscbor_chunk_mark_errors(pinfo, item_assert, chunk_assert);
1711 if (!wscbor_skip_if_errors(pinfo->pool, tvb, offset, chunk_assert)) {
1712 proto_tree *tree_assert = proto_item_add_subtree(item_assert, ett_status_assert);
1714 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, offset);
1715 bool *status_val = wscbor_require_boolean(pinfo->pool, chunk);
1716 proto_tree_add_cbor_boolean(tree_assert, hf_status_assert_val, pinfo, tvb, chunk, status_val);
1717 if (status_val) {
1718 result = *status_val;
1721 if (chunk_assert->head_value > 1) {
1722 bp_dtn_time_t abstime;
1723 dissect_dtn_time(tree_assert, hf_status_assert_time, pinfo, tvb, offset, &abstime);
1727 proto_item_set_len(item_assert, *offset - chunk_assert->start);
1728 return result;
1731 static int dissect_payload_admin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
1732 bp_dissector_data_t *context = (bp_dissector_data_t *)data;
1733 DISSECTOR_ASSERT(context);
1735 const char *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL);
1736 if (g_strcmp0(proto_name, proto_name_bp_admin) != 0) {
1737 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_bp_admin);
1738 col_clear(pinfo->cinfo, COL_INFO);
1741 proto_item *item_rec = proto_tree_add_item(tree, proto_bp_admin, tvb, 0, -1, ENC_NA);
1742 int offset = 0;
1744 wscbor_chunk_t *chunk_rec = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1745 wscbor_require_array_size(chunk_rec, 1, 2);
1746 wscbor_chunk_mark_errors(pinfo, item_rec, chunk_rec);
1747 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_rec)) {
1748 proto_tree *tree_rec = proto_item_add_subtree(item_rec, ett_admin);
1750 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1751 uint64_t *type_code = wscbor_require_uint64(pinfo->pool, chunk);
1752 proto_item *item_type = proto_tree_add_cbor_uint64(tree_rec, hf_admin_record_type, pinfo, tvb, chunk, type_code);
1754 dissector_handle_t type_dissect = NULL;
1755 bool type_exp = false;
1756 if (type_code) {
1757 type_dissect = dissector_get_custom_table_handle(admin_dissectors, type_code);
1758 label_type_field(type_code, type_dissect, item_type, item_rec);
1759 type_exp = (*type_code >= 65536);
1761 tvbuff_t *tvb_record = tvb_new_subset_remaining(tvb, offset);
1762 int sublen = dissect_carried_data(type_dissect, context, tvb_record, pinfo, tree_rec, type_exp);
1763 offset += sublen;
1766 proto_item_set_len(item_rec, offset);
1767 return offset;
1770 static int dissect_status_report(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
1771 bp_dissector_data_t *context = (bp_dissector_data_t *)data;
1772 if (!context) {
1773 return -1;
1775 int offset = 0;
1777 // Status Information array head
1778 proto_item *item_status = proto_tree_add_item(tree, hf_status_rep, tvb, offset, -1, ENC_NA);
1779 proto_tree *tree_status = proto_item_add_subtree(item_status, ett_status_rep);
1780 unsigned status_field_ix = 0;
1782 wscbor_chunk_t *chunk_status = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1783 wscbor_require_array_size(chunk_status, 4, 6);
1784 wscbor_chunk_mark_errors(pinfo, item_status, chunk_status);
1785 if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_status)) {
1786 proto_item_set_len(item_status, offset - chunk_status->start);
1787 return 0;
1790 wscbor_chunk_t *chunk;
1791 bool status_received = false;
1792 bool status_forwarded = false;
1793 bool status_delivered = false;
1794 bool status_deleted = false;
1796 wscbor_chunk_t *chunk_info = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1797 wscbor_require_array_size(chunk_info, 4, 4);
1799 proto_item *item_info = proto_tree_add_item(tree_status, hf_status_rep_status_info, tvb, offset, -1, ENC_NA);
1800 wscbor_chunk_mark_errors(pinfo, item_info, chunk_info);
1801 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_info)) {
1802 proto_tree *tree_info = proto_item_add_subtree(item_info, ett_status_info);
1804 status_received = dissect_status_assertion(tree_info, hf_status_rep_received, pinfo, tvb, &offset);
1805 status_forwarded = dissect_status_assertion(tree_info, hf_status_rep_forwarded, pinfo, tvb, &offset);
1806 status_delivered = dissect_status_assertion(tree_info, hf_status_rep_delivered, pinfo, tvb, &offset);
1807 status_deleted = dissect_status_assertion(tree_info, hf_status_rep_deleted, pinfo, tvb, &offset);
1810 proto_item_set_len(item_info, offset - chunk_info->start);
1811 status_field_ix++;
1814 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1815 uint64_t *reason_code = wscbor_require_uint64(pinfo->pool, chunk);
1816 proto_tree_add_cbor_uint64(tree_status, hf_status_rep_reason_code, pinfo, tvb, chunk, reason_code);
1817 status_field_ix++;
1819 bp_eid_t *subj_eid = bp_eid_new(pinfo->pool);
1820 proto_tree_add_cbor_eid(tree_status, hf_status_rep_subj_src_nodeid, hf_status_rep_subj_src_uri, pinfo, tvb, &offset, subj_eid);
1821 status_field_ix++;
1823 bp_creation_ts_t subj_ts = {0};
1824 dissect_cbor_timestamp(tree_status, hf_status_rep_subj_ts, pinfo, tvb, &offset, &subj_ts);
1825 status_field_ix++;
1827 bp_bundle_ident_t *subj = bp_bundle_ident_new(wmem_file_scope(), subj_eid, &subj_ts, NULL, NULL);
1829 if (chunk_info->head_value > status_field_ix) {
1830 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1831 subj->frag_offset = wscbor_require_uint64(wmem_file_scope(), chunk);
1832 proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_frag_offset, pinfo, tvb, chunk, subj->frag_offset);
1833 status_field_ix++;
1836 if (chunk_info->head_value > status_field_ix) {
1837 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
1838 subj->total_len = wscbor_require_uint64(wmem_file_scope(), chunk);
1839 proto_tree_add_cbor_uint64(tree_status, hf_status_rep_subj_payload_len, pinfo, tvb, chunk, subj->total_len);
1840 status_field_ix++;
1843 proto_tree_add_ident(pinfo, tree_status, hf_status_rep_subj_ident, tvb, subj);
1846 // Pointer back to subject
1847 const wmem_list_t *subj_list = wmem_map_lookup(bp_history->bundles, subj);
1848 const wmem_list_frame_t *subj_it = subj_list ? wmem_list_head(subj_list) : NULL;
1849 const bp_bundle_t *subj_found = subj_it ? wmem_list_frame_data(subj_it) : NULL;
1850 if (subj_found) {
1851 proto_item *item_subj_ref = proto_tree_add_uint(tree_status, hf_status_rep_subj_ref, tvb, 0, 0, subj_found->frame_num);
1852 proto_item_set_generated(item_subj_ref);
1854 nstime_t td;
1855 nstime_delta(&td, &(context->bundle->frame_time), &(subj_found->frame_time));
1856 proto_item *item_td = proto_tree_add_time(tree_status, hf_status_time_diff, tvb, 0, 0, &td);
1857 proto_item_set_generated(item_td);
1861 // Pointers from subject to this status
1862 wmem_map_t *status_set = wmem_map_lookup(bp_history->admin_status, subj);
1863 if (!status_set) {
1864 status_set = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
1865 wmem_map_insert(bp_history->admin_status, subj, status_set);
1867 else {
1868 bp_bundle_ident_free(wmem_file_scope(), subj);
1871 // Back-references to this status
1872 if (!wmem_map_contains(status_set, context->bundle->ident)) {
1873 ws_debug("status for %p in frame %d", (void*)context->bundle, context->bundle->frame_num);
1874 wmem_map_insert(status_set, context->bundle->ident, NULL);
1878 proto_item *item_admin = proto_tree_get_parent(tree);
1880 wmem_strbuf_t *status_text = wmem_strbuf_new(pinfo->pool, NULL);
1881 bool sep = false;
1882 if (status_received) {
1883 if (sep) {
1884 wmem_strbuf_append(status_text, "|");
1886 wmem_strbuf_append(status_text, "RECEIVED");
1887 sep = true;
1889 if (status_forwarded) {
1890 if (sep) {
1891 wmem_strbuf_append(status_text, "|");
1893 wmem_strbuf_append(status_text, "FORWARDED");
1894 sep = true;
1896 if (status_delivered) {
1897 if (sep) {
1898 wmem_strbuf_append(status_text, "|");
1900 wmem_strbuf_append(status_text, "DELIVERED");
1901 sep = true;
1903 if (status_deleted) {
1904 if (sep) {
1905 wmem_strbuf_append(status_text, "|");
1907 wmem_strbuf_append(status_text, "DELETED");
1909 const char *status_buf = wmem_strbuf_finalize(status_text);
1910 proto_item_append_text(item_admin, ", Status: %s", status_buf);
1911 col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Status: %s",
1912 status_buf);
1914 if (reason_code) {
1915 proto_item_append_text(item_admin, ", Reason: %s", val64_to_str(*reason_code, status_report_reason_vals, "%" PRIu64));
1918 proto_item_set_len(item_status, offset - chunk_status->start);
1919 return offset;
1922 /** Dissector for Bundle Payload block.
1924 static int dissect_block_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
1925 bp_dissector_data_t *context = (bp_dissector_data_t *)data;
1926 if (!context) {
1927 return -1;
1929 const bp_bundle_t *bundle = context->bundle;
1931 // Parent bundle tree
1932 proto_tree *tree_block = proto_tree_get_parent_tree(tree);
1933 proto_tree *tree_bundle = proto_tree_get_parent_tree(tree_block);
1934 // Back up to top-level
1935 proto_item *tree_top = proto_tree_get_parent_tree(tree_bundle);
1937 const bool is_fragment = bundle->primary->flags & BP_BUNDLE_IS_FRAGMENT;
1938 const bool is_admin = bundle->primary->flags & BP_BUNDLE_PAYLOAD_ADMIN;
1939 const unsigned payload_len = tvb_reported_length(tvb);
1941 // Set if the payload is fully defragmented
1942 tvbuff_t *tvb_payload = NULL;
1943 const char *col_suffix = NULL;
1944 if (is_fragment) {
1945 col_suffix = " (fragment)";
1947 if (bp_reassemble_payload) {
1948 if (!(bundle->primary->frag_offset
1949 && bundle->primary->total_len)) {
1950 return -1;
1952 // correlate by non-fragment bundle identity hash
1953 bp_bundle_ident_t *corr_ident = bp_bundle_ident_new(
1954 pinfo->pool,
1955 bundle->primary->src_nodeid,
1956 &(bundle->primary->ts),
1957 NULL,
1958 NULL
1961 if (
1962 (UINT32_MAX < *(bundle->primary->frag_offset))
1963 || (UINT32_MAX < *(bundle->primary->total_len))) {
1964 expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_reassemble_size);
1966 else {
1967 const uint32_t frag_offset = (uint32_t)*(bundle->primary->frag_offset);
1968 const uint32_t total_len = (uint32_t)*(bundle->primary->total_len);
1969 fragment_head *payload_frag_msg = fragment_add_check(
1970 &bp_reassembly_table,
1971 tvb, 0,
1972 pinfo, 0, corr_ident,
1973 frag_offset,
1974 payload_len,
1975 true
1977 const uint32_t old_total_len = fragment_get_tot_len(
1978 &bp_reassembly_table,
1979 pinfo, 0, corr_ident
1981 if (old_total_len > 0) {
1982 if (total_len != old_total_len) {
1983 expert_add_info(pinfo, bundle->primary->item_block, &ei_fragment_tot_mismatch);
1986 else {
1987 fragment_set_tot_len(
1988 &bp_reassembly_table,
1989 pinfo, 0, corr_ident,
1990 total_len
1993 tvb_payload = process_reassembled_data(
1994 tvb, 0, pinfo,
1995 "Reassembled Payload",
1996 payload_frag_msg,
1997 &payload_frag_items,
1998 NULL,
1999 tree_bundle
2001 if (tvb_payload) {
2002 col_suffix = " (reassembled)";
2005 bp_bundle_ident_free(pinfo->pool, corr_ident);
2008 else {
2009 tvb_payload = tvb;
2012 if (col_suffix) {
2013 col_append_str(pinfo->cinfo, COL_INFO, col_suffix);
2015 if (!tvb_payload) {
2016 return payload_len;
2019 // Payload is known to be administrative, independent of destination EID
2020 if (is_admin) {
2021 const int sublen = call_dissector_only(handle_admin, tvb_payload, pinfo, tree_top, context);
2022 if (sublen > 0) {
2023 return sublen;
2027 // an EID shouldn't have multiple of these set
2028 dissector_handle_t payload_dissect = NULL;
2029 if (bundle->primary->dst_eid->dtn_wkssp) {
2030 payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_wkssp, bundle->primary->dst_eid->dtn_wkssp);
2032 else if (bundle->primary->dst_eid->dtn_serv) {
2033 payload_dissect = dissector_get_string_handle(payload_dissectors_dtn_serv, bundle->primary->dst_eid->dtn_serv);
2035 else if (bundle->primary->dst_eid->ipn_serv &&
2036 (*(bundle->primary->dst_eid->ipn_serv) <= UINT_MAX)) {
2037 payload_dissect = dissector_get_uint_handle(payload_dissectors_ipn_serv, (unsigned)(*(bundle->primary->dst_eid->ipn_serv)));
2040 return dissect_carried_data(payload_dissect, context, tvb_payload, pinfo, tree_top, true);
2043 /** Dissector for Previous Node block.
2045 static int dissect_block_prev_node(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2046 int offset = 0;
2048 proto_tree_add_cbor_eid(tree, hf_previous_node_nodeid, hf_previous_node_uri, pinfo, tvb, &offset, NULL);
2050 return offset;
2053 /** Dissector for Bundle Age block.
2055 static int dissect_block_bundle_age(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2056 int offset = 0;
2058 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2059 const uint64_t *age = wscbor_require_uint64(pinfo->pool, chunk);
2060 proto_tree_add_cbor_uint64(tree, hf_bundle_age_time, pinfo, tvb, chunk, age);
2062 return offset;
2065 /** Dissector for Hop Count block.
2067 static int dissect_block_hop_count(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2068 int offset = 0;
2070 wscbor_chunk_t *chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2071 wscbor_require_array_size(chunk, 2, 2);
2072 if (wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk)) {
2073 return 0;
2076 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2077 const uint64_t *limit = wscbor_require_uint64(pinfo->pool, chunk);
2078 proto_tree_add_cbor_uint64(tree, hf_hop_count_limit, pinfo, tvb, chunk, limit);
2080 chunk = wscbor_chunk_read(pinfo->pool, tvb, &offset);
2081 const uint64_t *current = wscbor_require_uint64(pinfo->pool, chunk);
2082 proto_tree_add_cbor_uint64(tree, hf_hop_count_current, pinfo, tvb, chunk, current);
2084 return offset;
2087 static bool btsd_heur_cbor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
2088 int offset = 0;
2089 volatile int count = 0;
2091 while ((unsigned)offset < tvb_reported_length(tvb)) {
2092 volatile bool valid = false;
2093 TRY {
2094 valid = wscbor_skip_next_item(pinfo->pool, tvb, &offset);
2096 CATCH_BOUNDS_AND_DISSECTOR_ERRORS {}
2097 ENDTRY;
2098 if (!valid) {
2099 break;
2101 ++count;
2104 // Anything went wrong with any part of the data
2105 if ((count == 0) || ((unsigned)offset != tvb_reported_length(tvb))) {
2106 return false;
2109 if (count == 1) {
2110 call_dissector(handle_cbor, tvb, pinfo, tree);
2112 else {
2113 call_dissector(handle_cborseq, tvb, pinfo, tree);
2115 return true;
2118 /// Clear state when new file scope is entered
2119 static void bp_init(void) {
2120 bp_history = wmem_new0(wmem_file_scope(), bp_history_t);
2121 // ident keys are owned by the respective bundles
2122 bp_history->bundles = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
2123 // subject ident key is not kept
2124 bp_history->admin_status = wmem_map_new(wmem_file_scope(), bp_bundle_ident_hash, bp_bundle_ident_equal);
2127 static void bp_cleanup(void) {}
2129 /// Re-initialize after a configuration change
2130 static void bp_reinit_config(void) {}
2133 static void *fragment_bundle_ident_temporary_key(
2134 const packet_info *pinfo _U_, const uint32_t id _U_, const void *data) {
2135 return (bp_bundle_ident_t *)data;
2137 static void *fragment_bundle_ident_persistent_key(
2138 const packet_info *pinfo _U_, const uint32_t id _U_, const void *data) {
2139 const bp_bundle_ident_t *ident = (const bp_bundle_ident_t *)data;
2141 bp_bundle_ident_t *key = g_slice_new0(bp_bundle_ident_t);
2143 copy_address(&(key->src), &(ident->src));
2144 key->ts = ident->ts;
2145 if (ident->frag_offset) {
2146 key->frag_offset = g_slice_new(uint64_t);
2147 key->frag_offset = ident->frag_offset;
2149 if (ident->total_len) {
2150 key->total_len = g_slice_new(uint64_t);
2151 key->total_len = ident->total_len;
2153 return key;
2155 static void fragment_bundle_ident_free_temporary_key(void *ptr _U_) {}
2156 static void fragment_bundle_ident_free_persistent_key(void *ptr) {
2157 bp_bundle_ident_t *key = (bp_bundle_ident_t *)ptr;
2159 free_address(&(key->src));
2160 g_slice_free(uint64_t, (void *)(key->frag_offset));
2161 g_slice_free(uint64_t, (void *)(key->total_len));
2163 g_slice_free(bp_bundle_ident_t, key);
2165 static const reassembly_table_functions bundle_reassembly_table_functions = {
2166 bp_bundle_ident_hash,
2167 bp_bundle_ident_equal,
2168 fragment_bundle_ident_temporary_key,
2169 fragment_bundle_ident_persistent_key,
2170 fragment_bundle_ident_free_temporary_key,
2171 fragment_bundle_ident_free_persistent_key
2174 static void dtn_serv_prompt(packet_info *pinfo, char *result) {
2175 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2177 const char *serv = NULL;
2178 if (bundle && bundle->primary->dst_eid->dtn_serv) {
2179 serv = bundle->primary->dst_eid->dtn_serv;
2182 snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "dst (%s)", serv);
2185 static void *dtn_serv_value(packet_info *pinfo) {
2186 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2187 if (bundle && bundle->primary->dst_eid->dtn_serv) {
2188 const char *serv = bundle->primary->dst_eid->dtn_serv;
2189 return (char *)serv;
2191 return 0;
2194 static void ipn_serv_prompt(packet_info *pinfo, char *result) {
2195 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2197 uint32_t serv = 0;
2198 if (bundle && bundle->primary->dst_eid->ipn_serv
2199 && (*(bundle->primary->dst_eid->ipn_serv) <= UINT_MAX)) {
2200 serv = (unsigned int) *(bundle->primary->dst_eid->ipn_serv);
2203 snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "dst (%u)", serv);
2206 static void *ipn_serv_value(packet_info *pinfo) {
2207 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2208 if (bundle && bundle->primary->dst_eid->ipn_serv
2209 && (*(bundle->primary->dst_eid->ipn_serv) <= UINT_MAX)) {
2210 uint64_t serv = *(bundle->primary->dst_eid->ipn_serv);
2211 return GUINT_TO_POINTER(serv);
2213 return 0;
2216 static const char * bp_conv_get_filter_type(conv_item_t *item, conv_filter_type_e filter) {
2217 if ((filter == CONV_FT_SRC_ADDRESS) && (item->src_address.type == AT_STRINGZ)) {
2218 return "bpv7.primary.src_uri";
2220 if ((filter == CONV_FT_DST_ADDRESS) && (item->dst_address.type == AT_STRINGZ)) {
2221 return "bpv7.primary.dst_uri";
2223 if ((filter == CONV_FT_ANY_ADDRESS) && (
2224 (item->src_address.type == AT_STRINGZ)
2225 || (item->dst_address.type == AT_STRINGZ))) {
2226 return "bpv7.primary.srcdst_uri";
2228 return CONV_FILTER_INVALID;
2231 static ct_dissector_info_t bp_ct_dissector_info = {&bp_conv_get_filter_type};
2233 static tap_packet_status bp_conv_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) {
2234 conv_hash_t *hash = (conv_hash_t*) pct;
2235 const bp_bundle_t *bundle = (const bp_bundle_t *)vip;
2236 hash->flags = flags;
2238 add_conversation_table_data(
2239 hash,
2240 &(bundle->primary->src_nodeid->uri),
2241 &(bundle->primary->dst_eid->uri),
2242 0, 0,
2243 1, pinfo->fd->pkt_len,
2244 &pinfo->rel_ts, &pinfo->abs_ts,
2245 &bp_ct_dissector_info, CONVERSATION_NONE
2248 return TAP_PACKET_REDRAW;
2251 static const char * bp_endp_get_filter_type(endpoint_item_t *item, conv_filter_type_e filter) {
2252 if ((filter == CONV_FT_SRC_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2253 return "bpv7.primary.src_uri";
2255 if ((filter == CONV_FT_DST_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2256 return "bpv7.primary.dst_uri";
2258 if ((filter == CONV_FT_ANY_ADDRESS) && (item->myaddress.type == AT_STRINGZ)) {
2259 return "bpv7.primary.srcdst_uri";
2261 return CONV_FILTER_INVALID;
2264 static et_dissector_info_t bp_endp_dissector_info = {&bp_endp_get_filter_type};
2266 static tap_packet_status bp_endp_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) {
2267 conv_hash_t *hash = (conv_hash_t*)pit;
2268 const bp_bundle_t *bundle = (const bp_bundle_t *)vip;
2269 hash->flags = flags;
2271 if (bundle->primary->src_nodeid->uri.type == AT_STRINGZ) {
2272 add_endpoint_table_data(
2273 hash, &(bundle->primary->src_nodeid->uri), 0, true,
2274 1, pinfo->fd->pkt_len,
2275 &bp_endp_dissector_info, ENDPOINT_NONE
2278 if (bundle->primary->dst_eid->uri.type == AT_STRINGZ) {
2279 add_endpoint_table_data(
2280 hash, &(bundle->primary->dst_eid->uri), 0, false,
2281 1, pinfo->fd->pkt_len,
2282 &bp_endp_dissector_info, ENDPOINT_NONE
2285 return TAP_PACKET_REDRAW;
2288 static bool bp_filter_valid(packet_info *pinfo, void *user_data _U_) {
2289 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2290 return bundle != NULL;
2293 static char * bp_build_filter(packet_info *pinfo, void *user_data _U_) {
2294 const bp_bundle_t *bundle = p_get_proto_data(pinfo->pool, pinfo, proto_bp, PROTO_DATA_BUNDLE);
2295 if (!bundle) {
2296 return NULL;
2299 return ws_strdup_printf(
2300 "bpv7.primary.srcdst_uri == \"%s\" and bpv7.primary.srcdst_uri == \"%s\"",
2301 address_to_name(&(bundle->primary->src_nodeid->uri)),
2302 address_to_name(&(bundle->primary->dst_eid->uri))
2306 /// Overall registration of the protocol
2307 void proto_register_bpv7(void) {
2308 proto_bp = proto_register_protocol("DTN Bundle Protocol Version 7", "BPv7", "bpv7");
2309 register_init_routine(&bp_init);
2310 register_cleanup_routine(&bp_cleanup);
2312 proto_register_field_array(proto_bp, fields, array_length(fields));
2313 proto_register_subtree_array(ett, array_length(ett));
2314 expert_module_t *expert = expert_register_protocol(proto_bp);
2315 expert_register_field_array(expert, expertitems, array_length(expertitems));
2317 register_dissector("bpv7", dissect_bp, proto_bp);
2318 block_dissectors = register_custom_dissector_table("bpv7.block_type", "BPv7 Block", proto_bp, g_int64_hash, g_int64_equal, g_free);
2319 // Blocks don't count as protocol layers
2320 proto_blocktype = proto_register_protocol_in_name_only("BPv7 Block Type", "Block Type", "bpv7.block_type", proto_bp, FT_PROTOCOL);
2322 // case-sensitive string matching
2323 payload_dissectors_dtn_wkssp = register_dissector_table("bpv7.payload.dtn_wkssp", "BPv7 DTN-scheme well-known SSP", proto_bp, FT_STRING, STRING_CASE_SENSITIVE);
2325 payload_dissectors_dtn_serv = register_dissector_table("bpv7.payload.dtn_serv", "BPv7 DTN-scheme service", proto_bp, FT_STRING, STRING_CASE_SENSITIVE);
2326 dissector_table_allow_decode_as(payload_dissectors_dtn_serv);
2328 static build_valid_func dtn_serv_da_build_value[1] = {dtn_serv_value};
2329 static decode_as_value_t dtn_serv_da_values[1] = {
2330 {dtn_serv_prompt, 1, dtn_serv_da_build_value}
2332 static decode_as_t dtn_serv_da = {
2333 "bpv7", "bpv7.payload.dtn_serv",
2334 1, 0, dtn_serv_da_values, NULL, NULL,
2335 decode_as_default_populate_list, decode_as_default_reset,
2336 decode_as_default_change, NULL
2338 register_decode_as(&dtn_serv_da);
2340 payload_dissectors_ipn_serv = register_dissector_table("bpv7.payload.ipn_serv", "BPv7 IPN-scheme service", proto_bp, FT_UINT32, BASE_DEC);
2341 dissector_table_allow_decode_as(payload_dissectors_ipn_serv);
2343 static build_valid_func ipn_serv_da_build_value[1] = {ipn_serv_value};
2344 static decode_as_value_t ipn_serv_da_values[1] = {
2345 {ipn_serv_prompt, 1, ipn_serv_da_build_value}
2347 static decode_as_t ipn_serv_da = {
2348 "bpv7", "bpv7.payload.ipn_serv",
2349 1, 0, ipn_serv_da_values, NULL, NULL,
2350 decode_as_default_populate_list, decode_as_default_reset,
2351 decode_as_default_change, NULL
2353 register_decode_as(&ipn_serv_da);
2355 module_t *module_bp = prefs_register_protocol(proto_bp, bp_reinit_config);
2356 prefs_register_bool_preference(
2357 module_bp,
2358 "bp_compute_crc",
2359 "Compute and compare CRCs",
2360 "If enabled, the blocks will have CRC checks performed.",
2361 &bp_compute_crc
2363 prefs_register_bool_preference(
2364 module_bp,
2365 "bp_reassemble_payload",
2366 "Reassemble fragmented payloads",
2367 "Whether the dissector should reassemble fragmented bundle payloads.",
2368 &bp_reassemble_payload
2370 prefs_register_bool_preference(
2371 module_bp,
2372 "bp_payload_try_heur",
2373 "Attempt heuristic dissection of BTSD/payload",
2374 "When dissecting block type-specific data and payload and no destination matches, attempt heuristic dissection.",
2375 &bp_payload_try_heur
2378 reassembly_table_register(
2379 &bp_reassembly_table,
2380 &bundle_reassembly_table_functions
2383 btsd_heur = register_heur_dissector_list_with_description("bpv7.btsd", "BPv7 block data fallback", proto_bp);
2385 bp_tap = register_tap("bpv7");
2386 register_conversation_table(proto_bp, true, bp_conv_packet, bp_endp_packet);
2387 register_conversation_filter("bpv7", "BPv7", bp_filter_valid, bp_build_filter, NULL);
2389 proto_bp_admin = proto_register_protocol("BPv7 Administrative Record", "BPv7 Admin", "bpv7.admin_rec");
2390 handle_admin = register_dissector("bpv7.admin_rec", dissect_payload_admin, proto_bp_admin);
2391 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);
2392 proto_admintype = proto_register_protocol_in_name_only("BPv7 Administrative Record Type", "Admin Type", "bpv7.admin_record_type", proto_bp, FT_PROTOCOL);
2395 void proto_reg_handoff_bpv7(void) {
2396 const int proto_cbor = proto_get_id_by_filter_name("cbor");
2397 heur_dissector_add("bpv7.btsd", btsd_heur_cbor, "CBOR in Bundle BTSD", "cbor_bpv7", proto_cbor, HEURISTIC_ENABLE);
2399 handle_cbor = find_dissector("cbor");
2400 handle_cborseq = find_dissector("cborseq");
2402 /* Packaged extensions */
2404 uint64_t *key = g_new(uint64_t, 1);
2405 *key = BP_BLOCKTYPE_PAYLOAD;
2406 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_payload, proto_blocktype, NULL, "Payload");
2407 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2410 uint64_t *key = g_new(uint64_t, 1);
2411 *key = BP_BLOCKTYPE_PREV_NODE;
2412 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_prev_node, proto_blocktype, NULL, "Previous Node");
2413 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2416 uint64_t *key = g_new(uint64_t, 1);
2417 *key = BP_BLOCKTYPE_BUNDLE_AGE;
2418 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_bundle_age, proto_blocktype, NULL, "Bundle Age");
2419 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2422 uint64_t *key = g_new(uint64_t, 1);
2423 *key = BP_BLOCKTYPE_HOP_COUNT;
2424 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_hop_count, proto_blocktype, NULL, "Hop Count");
2425 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
2428 uint64_t *key = g_new(uint64_t, 1);
2429 *key = BP_ADMINTYPE_BUNDLE_STATUS;
2430 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_status_report, proto_admintype, NULL, "Bundle Status Report");
2431 dissector_add_custom_table_handle("bpv7.admin_record_type", key, hdl);
2434 bp_reinit_config();