2 * Routines for Bundle Protocol Version 7 dissection
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
15 #define WS_LOG_DOMAIN "packet-bpv7"
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>
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;
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
;
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"},
82 static const val64_string crc_vals
[] = {
83 {BP_CRC_NONE
, "None"},
84 {BP_CRC_16
, "CRC-16"},
85 {BP_CRC_32
, "CRC-32C"},
92 /// Limit on total count
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
:
111 case BP_BLOCKTYPE_BIB
:
113 case BP_BLOCKTYPE_PAYLOAD
:
114 return 1; // last block
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"},
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
;
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
,
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
,
408 static int ett_bundle
;
409 static int ett_bundle_flags
;
410 static int ett_block
;
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
;
423 static int *ett
[] = {
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
,
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
) {
514 else if (ats
->abstime
.dtntime
> bts
->abstime
.dtntime
) {
518 if (ats
->seqno
< bts
->seqno
) {
521 else if (ats
->seqno
> bts
->seqno
) {
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
));
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
);
559 void bp_block_primary_free(wmem_allocator_t
*alloc
, bp_block_primary_t
*obj
) {
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
);
581 static uint64_t * uint64_new(wmem_allocator_t
*alloc
, const uint64_t val
) {
582 uint64_t *obj
= wmem_new(alloc
, uint64_t);
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
);
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
) {
611 if (aobj
->frame_num
> bobj
->frame_num
) {
614 if (aobj
->layer_num
< bobj
->layer_num
) {
617 if (aobj
->layer_num
> bobj
->layer_num
) {
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
));
629 ident
->frag_offset
= off
;
630 ident
->total_len
= len
;
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
) {
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
;
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
;
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
) {
678 utctime
.secs
= dtntime
/ 1000;
679 utctime
.nsecs
= 1000000 * (dtntime
% 1000);
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
) {
691 utctime
.secs
= 946684800 + dtntime
/ 1000;
692 utctime
.nsecs
= 1000000 * (dtntime
% 1000);
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);
705 proto_item_set_len(item_eid
, *offset
- eid_start
);
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
);
713 wscbor_skip_next_item(pinfo
->pool
, tvb
, offset
);
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
;
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
);
731 dtn_wkssp
= wmem_strdup(alloc_eid
, "none");
734 expert_add_info(pinfo
, item
, &ei_eid_wkssp_unknown
);
738 wmem_strbuf_append_printf(uribuf
, "dtn:%s", dtn_wkssp
);
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
);
748 if ((path_sep
= strrchr(ssp
, '/')) != NULL
) {
749 dtn_serv
= wmem_strdup(alloc_eid
, path_sep
+ 1);
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
);
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
);
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);
788 wscbor_skip_next_item(pinfo
->pool
, tvb
, offset
);
789 expert_add_info(pinfo
, item_scheme
, &ei_eid_scheme_unknown
);
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
);
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
);
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
);
813 eid
->scheme
= (scheme
? *scheme
: 0);
815 set_address(&(eid
->uri
), AT_STRINGZ
, (int)strlen(uri
) + 1, uri
);
818 clear_address(&(eid
->uri
));
820 eid
->dtn_wkssp
= dtn_wkssp
;
821 eid
->dtn_serv
= dtn_serv
;
822 eid
->ipn_serv
= ipn_serv
;
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
);
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
);
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
);
848 out
->dtntime
= *dtntime
;
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
);
860 out
->utctime
= utctime
;
864 proto_item_append_text(item_time
, ": undefined");
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
);
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
) {
921 const char *type_name
= dissector_handle_get_description(type_dissect
);
923 proto_item_append_text(item_parent
, ": %s", type_name
);
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
) {
943 // Display the data field information
947 hf_crc_field
= hf_crc_field_uint16
;
950 hf_crc_field
= hf_crc_field_uint32
;
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
;
965 const unsigned block_len
= tvb_reported_length(tvb
);
966 uint8_t *crcbuf
= tvb_memdup(pinfo
->pool
, tvb
, 0, block_len
);
969 memset(crcbuf
+ block_len
- 2, 0, 2);
970 crc_actual
= crc16_ccitt(crcbuf
, block_len
);
973 memset(crcbuf
+ block_len
- 4, 0, 4);
974 crc_actual
= ~crc32c_calculate_no_swap(crcbuf
, block_len
, CRC32C_PRELOAD
);
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(
991 "Source: %s, DTN Time: %" PRIu64
", Seq: %" PRIu64
,
992 address_to_name(&(ident
->src
)),
993 ident
->ts
.abstime
.dtntime
,
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
);
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
);
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
;
1025 proto_item_append_text(item_block
, ", Items: %" PRIu64
, chunk_block
->head_value
);
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
);
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
);
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
);
1046 block
->crc_type
= (crc_type
? *crc_type
: BP_CRC_NONE
);
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
);
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
);
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
);
1071 dissect_cbor_timestamp(tree_block
, hf_primary_create_ts
, pinfo
, tvb
, &offset
, &(block
->ts
));
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
);
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
);
1092 if (flags
&& (*flags
& BP_BUNDLE_IS_FRAGMENT
)) {
1093 if (!wscbor_require_array_size(chunk_block
, field_ix
+ 1, field_ix
+ 3)) {
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
);
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
);
1112 switch (block
->crc_type
) {
1117 if (!wscbor_require_array_size(chunk_block
, field_ix
+ 1, field_ix
+ 1)) {
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
);
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
);
1135 expert_add_info(pinfo
, item_crc_type
, &ei_crc_type_unknown
);
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
);
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
;
1157 proto_item_append_text(item_block
, ", Items: %" PRIu64
, chunk_block
->head_value
);
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
);
1164 block
->type_code
= 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
) {
1177 if (row
->type_code
== *type_code
) {
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
);
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
) {
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
);
1205 block
->block_number
= 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
);
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
);
1220 block
->crc_type
= (crc_type
? *crc_type
: BP_CRC_NONE
);
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
);
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
) {
1242 if (!wscbor_require_array_size(chunk_block
, field_ix
+ 1, field_ix
+ 1)) {
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
);
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
);
1260 expert_add_info(pinfo
, item_crc_type
, &ei_crc_type_unknown
);
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
);
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
);
1278 expert_add_info(pinfo
, item_block_num
, &ei_block_num_dupe
);
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
;
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
;
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
;
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
) {
1343 sublen
= call_dissector_only(dissector
, tvb
, pinfo
, tree
, context
);
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
);
1360 sublen
= call_data_dissector(tvb
, pinfo
, tree
);
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
;
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
);
1390 else if (aord
> bord
) {
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
);
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
)) {
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;
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");
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
);
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) {
1454 proto_item_prepend_text(item_block
, "Primary ");
1455 const int sublen
= dissect_block_primary(tvb
, pinfo
, tree_block
, offset
, bundle
->primary
, bundle
);
1461 if (!(bundle
->ident
)) {
1462 bundle
->ident
= bp_bundle_ident_new(
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
);
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
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
);
1499 wmem_map_foreach(status_set
, show_status_subj_ref
, tree_ident
);
1503 const bp_eid_t
*dst
= bundle
->primary
->dst_eid
;
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
));
1513 proto_item_set_generated(item_dst
);
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
);
1529 proto_item_set_len(item_block
, offset
- block_start
);
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
);
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
);
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
)
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
)) {
1615 proto_item_append_text(item_bundle
, ", Payload-Size: %d", *(bundle
->pyld_len
));
1618 col_append_str(pinfo
->cinfo
, COL_INFO
, "Payload Size: ");
1620 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%d", *(bundle
->pyld_len
));
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
;
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)) {
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
);
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
);
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
;
1689 wmem_list_append(found_list
, bundle
);
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
);
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
);
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
);
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
);
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;
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
);
1766 proto_item_set_len(item_rec
, 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
;
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
);
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
);
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
);
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
);
1823 bp_creation_ts_t subj_ts
= {0};
1824 dissect_cbor_timestamp(tree_status
, hf_status_rep_subj_ts
, pinfo
, tvb
, &offset
, &subj_ts
);
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
);
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
);
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
;
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
);
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
);
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
);
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
);
1882 if (status_received
) {
1884 wmem_strbuf_append(status_text
, "|");
1886 wmem_strbuf_append(status_text
, "RECEIVED");
1889 if (status_forwarded
) {
1891 wmem_strbuf_append(status_text
, "|");
1893 wmem_strbuf_append(status_text
, "FORWARDED");
1896 if (status_delivered
) {
1898 wmem_strbuf_append(status_text
, "|");
1900 wmem_strbuf_append(status_text
, "DELIVERED");
1903 if (status_deleted
) {
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",
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
);
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
;
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
;
1945 col_suffix
= " (fragment)";
1947 if (bp_reassemble_payload
) {
1948 if (!(bundle
->primary
->frag_offset
1949 && bundle
->primary
->total_len
)) {
1952 // correlate by non-fragment bundle identity hash
1953 bp_bundle_ident_t
*corr_ident
= bp_bundle_ident_new(
1955 bundle
->primary
->src_nodeid
,
1956 &(bundle
->primary
->ts
),
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
);
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
,
1972 pinfo
, 0, corr_ident
,
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
);
1987 fragment_set_tot_len(
1988 &bp_reassembly_table
,
1989 pinfo
, 0, corr_ident
,
1993 tvb_payload
= process_reassembled_data(
1995 "Reassembled Payload",
1997 &payload_frag_items
,
2002 col_suffix
= " (reassembled)";
2005 bp_bundle_ident_free(pinfo
->pool
, corr_ident
);
2013 col_append_str(pinfo
->cinfo
, COL_INFO
, col_suffix
);
2019 // Payload is known to be administrative, independent of destination EID
2021 const int sublen
= call_dissector_only(handle_admin
, tvb_payload
, pinfo
, tree_top
, context
);
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_
) {
2048 proto_tree_add_cbor_eid(tree
, hf_previous_node_nodeid
, hf_previous_node_uri
, pinfo
, tvb
, &offset
, NULL
);
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_
) {
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
);
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_
) {
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
)) {
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
);
2087 static bool btsd_heur_cbor(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
2089 volatile int count
= 0;
2091 while ((unsigned)offset
< tvb_reported_length(tvb
)) {
2092 volatile bool valid
= false;
2094 valid
= wscbor_skip_next_item(pinfo
->pool
, tvb
, &offset
);
2096 CATCH_BOUNDS_AND_DISSECTOR_ERRORS
{}
2104 // Anything went wrong with any part of the data
2105 if ((count
== 0) || ((unsigned)offset
!= tvb_reported_length(tvb
))) {
2110 call_dissector(handle_cbor
, tvb
, pinfo
, tree
);
2113 call_dissector(handle_cborseq
, tvb
, pinfo
, tree
);
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
;
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
;
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
);
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
);
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(
2240 &(bundle
->primary
->src_nodeid
->uri
),
2241 &(bundle
->primary
->dst_eid
->uri
),
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
);
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(
2359 "Compute and compare CRCs",
2360 "If enabled, the blocks will have CRC checks performed.",
2363 prefs_register_bool_preference(
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(
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
);