2 * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
5 * This software and documentation has been developed by Endace Technology Ltd.
6 * along with the DAG PCI network capture cards. For further information please
7 * visit http://www.endace.com/.
9 * SPDX-License-Identifier: BSD-3-Clause
13 * erf - Endace ERF (Extensible Record Format)
16 * http://web.archive.org/web/20050829051042/http://www.endace.com/support/EndaceRecordFormat.pdf
18 * http://web.archive.org/web/20061111014023/http://www.endace.com/support/EndaceRecordFormat.pdf
20 * https://gitlab.com/wireshark/wireshark/uploads/f694bfee494784425b6545892180a8b2/Endace_ERF_Types.pdf
22 * Current version (version 21, as of 2023-03-28):
23 * https://www.endace.com/erf-extensible-record-format-types.pdf
25 * Note that version 17 drops descriptions of records for no-longer-supported
26 * DAG cards. Version 16 is probably the best version to use for those
27 * older record types, but it's not in any obvious location in the Wayback
38 #include <wsutil/crc32.h>
39 #include <wsutil/strtoi.h>
40 #include <wsutil/glib-compat.h>
43 #include "file_wrappers.h"
45 #include "erf_record.h"
46 #include "erf-common.h"
48 struct erf_anchor_mapping
{
55 static const unsigned erf_header_size
= (unsigned)sizeof(erf_header_t
);
56 static const unsigned erf_mc_header_size
= (unsigned)sizeof(erf_mc_header_t
);
57 static const unsigned erf_eth_hdr_size
= (unsigned)sizeof(erf_eth_header_t
);
60 static bool erf_read_header(wtap
*wth
, FILE_T fh
,
62 erf_header_t
*erf_header
,
66 uint32_t *packet_size
,
67 GPtrArray
*anchor_mappings_to_update
);
68 static bool erf_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
69 int *err
, char **err_info
, int64_t *data_offset
);
70 static bool erf_seek_read(wtap
*wth
, int64_t seek_off
,
71 wtap_rec
*rec
, Buffer
*buf
,
72 int *err
, char **err_info
);
73 static void erf_close(wtap
*wth
);
75 static int populate_summary_info(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, Buffer
*buf
, uint32_t packet_size
, GPtrArray
*anchor_mappings_to_update
, int *err
, char **err_info
);
76 static int erf_update_anchors_from_header(erf_t
*erf_priv
, wtap_rec
*rec
, union wtap_pseudo_header
*pseudo_header
, uint64_t host_id
, GPtrArray
*anchor_mappings_to_update
);
77 static int erf_get_source_from_header(union wtap_pseudo_header
*pseudo_header
, uint64_t *host_id
, uint8_t *source_id
);
78 static int erf_populate_interface(erf_t
* erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, uint64_t host_id
, uint8_t source_id
, uint8_t if_num
, int *err
, char **err_info
);
81 bool write_next_extra_meta
;
82 bool last_meta_periodic
;
84 uint64_t implicit_host_id
;
85 uint64_t prev_frame_ts
;
86 uint8_t prev_erf_type
;
88 time_t first_frame_time_sec
;
89 time_t prev_inserted_time_sec
;
90 char* user_comment_ptr
;
91 GPtrArray
* periodic_sections
;
92 GArray
*periodic_extra_ehdrs
;
96 static erf_dump_t
* erf_dump_priv_create(void);
97 static void erf_dump_priv_free(erf_dump_t
*dump_priv
);
98 static bool erf_dump_priv_compare_capture_comment(wtap_dumper
*wdh
, erf_dump_t
*dump_priv
,const union wtap_pseudo_header
*pseudo_header
, const uint8_t *pd
);
99 static bool erf_comment_to_sections(wtap_dumper
*wdh
, uint16_t section_type
, uint16_t section_id
, char *comment
, GPtrArray
*sections
);
100 static bool erf_wtap_info_to_sections(wtap_dumper
*wdh
, GPtrArray
*sections
);
101 static bool get_user_comment_string(wtap_dumper
*wdh
, char** user_comment_ptr
);
103 static bool erf_write_meta_record(wtap_dumper
*wdh
, erf_dump_t
*dump_priv
, uint64_t timestamp
, GPtrArray
*sections
, GArray
*extra_ehdrs
, int *err
);
105 static const struct {
107 int wtap_encap_value
;
108 } erf_to_wtap_map
[] = {
109 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_CHDLC
},
110 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_HHDLC
},
111 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_CHDLC_WITH_PHDR
},
112 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_PPP
},
113 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_FRELAY
},
114 { ERF_TYPE_HDLC_POS
, WTAP_ENCAP_MTP2
},
115 { ERF_TYPE_ETH
, WTAP_ENCAP_ETHERNET
},
116 { 99, WTAP_ENCAP_ERF
}, /*this type added so WTAP_ENCAP_ERF will work and then be treated at ERF->ERF*/
119 #define NUM_ERF_ENCAPS (sizeof erf_to_wtap_map / sizeof erf_to_wtap_map[0])
121 #define ERF_META_TAG_HEADERLEN 4
122 #define ERF_META_TAG_TOTAL_ALIGNED_LENGTH(taglength) ((((uint32_t)taglength + 0x3U) & ~0x3U) + ERF_META_TAG_HEADERLEN)
123 #define ERF_META_TAG_ALIGNED_LENGTH(taglength) ((((uint32_t)taglength + 0x3U) & ~0x3U))
124 #define ERF_PADDING_TO_8(len) ((8 - len % 8) % 8)
138 struct erf_if_mapping
{
141 struct erf_if_info interfaces
[ERF_MAX_INTERFACES
];
143 char *module_filter_str
;
144 /*here because we could have captures from multiple hosts in the file*/
145 char *capture_filter_str
;
146 int8_t module_fcs_len
;
147 uint32_t module_snaplen
;
148 int interface_metadata
;
149 uint64_t interface_gentime
;
150 uint64_t module_gentime
;
153 struct erf_meta_section
{
156 uint16_t section_length
;
160 struct erf_meta_tag
{
166 struct erf_meta_read_state
{
168 uint32_t remaining_len
;
170 struct erf_if_mapping
*if_map
;
172 uint16_t sectiontype
;
174 uint16_t parentsectiontype
;
175 uint16_t parentsectionid
;
179 int interface_metadata
;
182 static bool erf_wtap_blocks_to_erf_sections(wtap_block_t block
, GPtrArray
*sections
, uint16_t section_type
, uint16_t section_id
, wtap_block_foreach_func func
);
184 static uint32_t erf_meta_read_tag(struct erf_meta_tag
*, uint8_t*, uint32_t);
186 static int erf_file_type_subtype
= -1;
188 void register_erf(void);
190 static unsigned erf_anchor_mapping_hash(gconstpointer key
) {
191 const struct erf_anchor_mapping
*anchor_map
= (const struct erf_anchor_mapping
*) key
;
193 return ((uint32_t)anchor_map
->host_id
^ (uint32_t)anchor_map
->anchor_id
);
197 static gboolean
erf_anchor_mapping_equal(gconstpointer a
, gconstpointer b
) {
198 const struct erf_anchor_mapping
*anchor_map_a
= (const struct erf_anchor_mapping
*) a
;
199 const struct erf_anchor_mapping
*anchor_map_b
= (const struct erf_anchor_mapping
*) b
;
201 return (anchor_map_a
->host_id
) == (anchor_map_b
->host_id
) &&
202 (anchor_map_a
->anchor_id
& ERF_EXT_HDR_TYPE_ANCHOR_ID
) == (anchor_map_b
->anchor_id
& ERF_EXT_HDR_TYPE_ANCHOR_ID
);
205 static void erf_anchor_mapping_destroy(void *key
) {
206 struct erf_anchor_mapping
*anchor_map
= (struct erf_anchor_mapping
*) key
;
208 if(anchor_map
->comment
!= NULL
) {
209 g_free(anchor_map
->comment
);
210 anchor_map
->comment
= NULL
;
216 static gboolean
erf_if_mapping_equal(gconstpointer a
, gconstpointer b
)
218 const struct erf_if_mapping
*if_map_a
= (const struct erf_if_mapping
*) a
;
219 const struct erf_if_mapping
*if_map_b
= (const struct erf_if_mapping
*) b
;
221 return if_map_a
->source_id
== if_map_b
->source_id
&& if_map_a
->host_id
== if_map_b
->host_id
;
224 static unsigned erf_if_mapping_hash(gconstpointer key
)
226 const struct erf_if_mapping
*if_map
= (const struct erf_if_mapping
*) key
;
228 return (((unsigned) if_map
->host_id
) << 16) | if_map
->source_id
;
231 static void erf_if_mapping_destroy(void *key
)
234 struct erf_if_mapping
*if_map
= (struct erf_if_mapping
*) key
;
236 for (i
= 0; i
< ERF_MAX_INTERFACES
; i
++) {
237 g_free(if_map
->interfaces
[i
].name
);
238 g_free(if_map
->interfaces
[i
].descr
);
241 g_free(if_map
->module_filter_str
);
245 static struct erf_if_mapping
* erf_if_mapping_create(uint64_t host_id
, uint8_t source_id
)
248 struct erf_if_mapping
*if_map
= NULL
;
250 if_map
= g_new0(struct erf_if_mapping
, 1);
252 if_map
->host_id
= host_id
;
253 if_map
->source_id
= source_id
;
255 for (i
= 0; i
< ERF_MAX_INTERFACES
; i
++) {
256 if_map
->interfaces
[i
].if_index
= -1;
257 if_map
->interfaces
[i
].stream_num
= -1;
260 if_map
->module_fcs_len
= -1;
261 if_map
->module_snaplen
= (uint32_t) -1;
262 /* everything else 0 by g_malloc0*/
268 erf_t
*erf_priv_create(void)
272 erf_priv
= g_new(erf_t
, 1);
273 erf_priv
->anchor_map
= g_hash_table_new_full(erf_anchor_mapping_hash
, erf_anchor_mapping_equal
, erf_anchor_mapping_destroy
, NULL
);
274 erf_priv
->if_map
= g_hash_table_new_full(erf_if_mapping_hash
, erf_if_mapping_equal
, erf_if_mapping_destroy
, NULL
);
275 erf_priv
->implicit_host_id
= ERF_META_HOST_ID_IMPLICIT
;
276 erf_priv
->capture_gentime
= 0;
277 erf_priv
->host_gentime
= 0;
282 erf_t
* erf_priv_free(erf_t
* erf_priv
)
286 g_hash_table_destroy(erf_priv
->anchor_map
);
287 g_hash_table_destroy(erf_priv
->if_map
);
294 static void erf_dump_priv_free(erf_dump_t
*dump_priv
) {
296 if(dump_priv
->periodic_sections
) {
297 g_ptr_array_free(dump_priv
->periodic_sections
, true);
299 if(dump_priv
->periodic_extra_ehdrs
) {
300 g_array_free(dump_priv
->periodic_extra_ehdrs
, true);
302 if(dump_priv
->user_comment_ptr
) {
303 g_free(dump_priv
->user_comment_ptr
);
306 g_free(dump_priv
->rand
);
313 static void erf_meta_section_free(void *data
) {
314 struct erf_meta_section
*section_ptr
= (struct erf_meta_section
*) data
;
316 g_ptr_array_free(section_ptr
->tags
, true);
317 section_ptr
->tags
= NULL
;
322 static void erf_meta_tag_free(void *data
) {
323 struct erf_meta_tag
*tag_ptr
= (struct erf_meta_tag
*) data
;
325 g_free(tag_ptr
->value
);
326 tag_ptr
->value
= NULL
;
332 static bool erf_dump_finish(struct wtap_dumper
*wdh
, int *err
, char **err_info _U_
) {
333 erf_dump_t
*dump_priv
= (erf_dump_t
*)wdh
->priv
;
336 /* Write final metadata record. There are some corner cases where we should
337 * do this (file <1 second, last record was ERF_TYPE_META with an out of date
338 * comment) and there is no harm doing this always if we have already written
340 if(dump_priv
->write_next_extra_meta
) {
341 if (!dump_priv
->periodic_sections
) {
342 dump_priv
->periodic_sections
= g_ptr_array_new_with_free_func(erf_meta_section_free
);
343 if (dump_priv
->prev_erf_type
== ERF_TYPE_META
&& dump_priv
->last_meta_periodic
) {
344 erf_comment_to_sections(wdh
, ERF_META_SECTION_CAPTURE
, 0, dump_priv
->user_comment_ptr
, dump_priv
->periodic_sections
);
346 /* If we get here, metadata record was not found in the first ~1 sec
347 * but we have either a capture comment or a non-ERF file (see
349 erf_wtap_info_to_sections(wdh
, dump_priv
->periodic_sections
);
353 if (!erf_write_meta_record(wdh
, dump_priv
, dump_priv
->prev_frame_ts
, dump_priv
->periodic_sections
, dump_priv
->periodic_extra_ehdrs
, err
)) ret
= false;
357 erf_dump_priv_free(dump_priv
);
358 /* Avoid double freeing by setting it to NULL*/
366 erf_free_data(void *data
, void *user_data _U_
)
372 extern wtap_open_return_val
erf_open(wtap
*wth
, int *err
, char **err_info
)
374 int i
, n
, records_for_erf_check
= RECORDS_FOR_ERF_CHECK
;
377 erf_timestamp_t prevts
,ts
;
380 struct erf_eth_hdr eth_hdr
;
381 uint32_t packet_size
;
383 uint64_t erf_ext_header
;
384 unsigned erf_ext_header_size
= (unsigned)sizeof(erf_ext_header
);
389 /* number of records to scan before deciding if this really is ERF */
390 if ((s
= getenv("ERF_RECORDS_TO_CHECK")) != NULL
) {
391 if (ws_strtoi32(s
, NULL
, &n
) && n
>= 0 && n
< 101) {
392 records_for_erf_check
= n
;
397 * ERF is a little hard because there's no magic number; we look at
398 * the first few records and see if they look enough like ERF
402 for (i
= 0; i
< records_for_erf_check
; i
++) { /* records_for_erf_check */
404 if (!wtap_read_bytes_or_eof(wth
->fh
,&header
,erf_header_size
,err
,err_info
)) {
406 /* EOF - all records have been successfully checked, accept the file */
409 if (*err
== WTAP_ERR_SHORT_READ
) {
410 /* ERF header too short accept the file,
411 only if the very first records have been successfully checked */
412 if (i
< MIN_RECORDS_FOR_ERF_CHECK
) {
413 return WTAP_OPEN_NOT_MINE
;
415 /* BREAK, the last record is too short, and will be ignored */
419 return WTAP_OPEN_ERROR
;
423 rlen
=g_ntohs(header
.rlen
);
425 /* fail on invalid record type, invalid rlen, timestamps decreasing, or incrementing too far */
427 /* Test valid rlen >= 16 */
429 return WTAP_OPEN_NOT_MINE
;
432 packet_size
= rlen
- erf_header_size
;
433 if (packet_size
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
435 * Probably a corrupt capture file or a file that's not an ERF file
436 * but that passed earlier tests.
438 return WTAP_OPEN_NOT_MINE
;
441 /* Skip PAD records, timestamps may not be set */
442 if ((header
.type
& 0x7F) == ERF_TYPE_PAD
) {
443 if (!wtap_read_bytes(wth
->fh
, NULL
, packet_size
, err
, err_info
)) {
444 if (*err
!= WTAP_ERR_SHORT_READ
) {
446 return WTAP_OPEN_ERROR
;
448 /* ERF record too short, accept the file,
449 only if the very first records have been successfully checked */
450 if (i
< MIN_RECORDS_FOR_ERF_CHECK
) {
451 return WTAP_OPEN_NOT_MINE
;
457 /* ERF Type 0 is reserved for ancient legacy records which are not supported, probably not ERF */
458 if ((header
.type
& 0x7F) == 0) {
459 return WTAP_OPEN_NOT_MINE
;
462 /* fail on decreasing timestamps */
463 if ((ts
= pletoh64(&header
.ts
)) < prevts
) {
464 /* reassembled AALx records may not be in time order, also records are not in strict time order between physical interfaces, so allow 1 sec fudge */
465 if ( ((prevts
-ts
)>>32) > 1 ) {
466 return WTAP_OPEN_NOT_MINE
;
470 /* Check to see if timestamp increment is > 1 year */
471 if ( (valid_prev
) && (ts
> prevts
) && (((ts
-prevts
)>>32) > 3600*24*365) ) {
472 return WTAP_OPEN_NOT_MINE
;
477 /* Read over the extension headers */
480 if (!wtap_read_bytes(wth
->fh
,&erf_ext_header
,erf_ext_header_size
,err
,err_info
)) {
481 if (*err
== WTAP_ERR_SHORT_READ
) {
482 /* Extension header missing, not an ERF file */
483 return WTAP_OPEN_NOT_MINE
;
485 return WTAP_OPEN_ERROR
;
487 if (packet_size
< erf_ext_header_size
)
488 return WTAP_OPEN_NOT_MINE
;
489 packet_size
-= erf_ext_header_size
;
490 memcpy(&type
, &erf_ext_header
, sizeof(type
));
494 /* Read over MC or ETH subheader */
495 switch(header
.type
& 0x7F) {
496 case ERF_TYPE_MC_HDLC
:
497 case ERF_TYPE_MC_RAW
:
498 case ERF_TYPE_MC_ATM
:
499 case ERF_TYPE_MC_RAW_CHANNEL
:
500 case ERF_TYPE_MC_AAL5
:
501 case ERF_TYPE_MC_AAL2
:
502 case ERF_TYPE_COLOR_MC_HDLC_POS
:
503 case ERF_TYPE_AAL2
: /* not an MC type but has a similar 'AAL2 ext' header */
504 if (!wtap_read_bytes(wth
->fh
,&mc_hdr
,erf_mc_header_size
,err
,err_info
)) {
505 if (*err
== WTAP_ERR_SHORT_READ
) {
506 /* Subheader missing, not an ERF file */
507 return WTAP_OPEN_NOT_MINE
;
509 return WTAP_OPEN_ERROR
;
511 if (packet_size
< erf_mc_header_size
)
512 return WTAP_OPEN_NOT_MINE
;
513 packet_size
-= erf_mc_header_size
;
516 case ERF_TYPE_COLOR_ETH
:
517 case ERF_TYPE_DSM_COLOR_ETH
:
518 case ERF_TYPE_COLOR_HASH_ETH
:
519 if (!wtap_read_bytes(wth
->fh
,ð_hdr
,erf_eth_hdr_size
,err
,err_info
)) {
520 if (*err
== WTAP_ERR_SHORT_READ
) {
521 /* Subheader missing, not an ERF file */
522 return WTAP_OPEN_NOT_MINE
;
524 return WTAP_OPEN_ERROR
;
526 if (packet_size
< erf_eth_hdr_size
)
527 return WTAP_OPEN_NOT_MINE
;
528 packet_size
-= erf_eth_hdr_size
;
534 if (!wtap_read_bytes(wth
->fh
, NULL
, packet_size
, err
, err_info
)) {
535 if (*err
!= WTAP_ERR_SHORT_READ
) {
537 return WTAP_OPEN_ERROR
;
539 /* ERF record too short, accept the file,
540 only if the very first records have been successfully checked */
541 if (i
< MIN_RECORDS_FOR_ERF_CHECK
) {
542 return WTAP_OPEN_NOT_MINE
;
548 } /* records_for_erf_check */
550 if (file_seek(wth
->fh
, 0L, SEEK_SET
, err
) == -1) { /* rewind */
551 return WTAP_OPEN_ERROR
;
554 /* This is an ERF file */
555 wth
->file_type_subtype
= erf_file_type_subtype
;
556 wth
->snapshot_length
= 0; /* not available in header, only in frame */
559 * Use the encapsulation for ERF records.
561 wth
->file_encap
= WTAP_ENCAP_ERF
;
563 wth
->subtype_read
= erf_read
;
564 wth
->subtype_seek_read
= erf_seek_read
;
565 wth
->subtype_close
= erf_close
;
566 wth
->file_tsprec
= WTAP_TSPREC_NSEC
;
568 wth
->priv
= erf_priv_create();
570 return WTAP_OPEN_MINE
;
573 /* Read the next packet */
574 static bool erf_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
575 int *err
, char **err_info
, int64_t *data_offset
)
577 erf_header_t erf_header
;
578 uint32_t packet_size
, bytes_read
;
579 GPtrArray
*anchor_mappings_to_update
;
581 *data_offset
= file_tell(wth
->fh
);
583 anchor_mappings_to_update
= g_ptr_array_new_with_free_func(erf_anchor_mapping_destroy
);
586 if (!erf_read_header(wth
, wth
->fh
, rec
, &erf_header
,
587 err
, err_info
, &bytes_read
, &packet_size
,
588 anchor_mappings_to_update
)) {
589 g_ptr_array_free(anchor_mappings_to_update
, true);
593 if (!wtap_read_packet_bytes(wth
->fh
, buf
, packet_size
, err
, err_info
)) {
594 g_ptr_array_free(anchor_mappings_to_update
, true);
599 * If Provenance metadata record, frame buffer could hold the meta erf tags.
600 * It can also contain per packet comments which can be associated to another
603 if ((erf_header
.type
& 0x7F) == ERF_TYPE_META
&& packet_size
> 0)
605 if (populate_summary_info((erf_t
*) wth
->priv
, wth
, &rec
->rec_header
.packet_header
.pseudo_header
, buf
, packet_size
, anchor_mappings_to_update
, err
, err_info
) < 0) {
606 g_ptr_array_free(anchor_mappings_to_update
, true);
611 } while ( erf_header
.type
== ERF_TYPE_PAD
);
613 g_ptr_array_free(anchor_mappings_to_update
, true);
618 static bool erf_seek_read(wtap
*wth
, int64_t seek_off
,
619 wtap_rec
*rec
, Buffer
*buf
,
620 int *err
, char **err_info
)
622 erf_header_t erf_header
;
623 uint32_t packet_size
;
624 GPtrArray
*anchor_mappings_to_update
;
626 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
629 anchor_mappings_to_update
= g_ptr_array_new_with_free_func(erf_anchor_mapping_destroy
);
632 if (!erf_read_header(wth
, wth
->random_fh
, rec
, &erf_header
,
633 err
, err_info
, NULL
, &packet_size
, anchor_mappings_to_update
)) {
634 g_ptr_array_free(anchor_mappings_to_update
, true);
637 } while ( erf_header
.type
== ERF_TYPE_PAD
);
639 g_ptr_array_free(anchor_mappings_to_update
, true);
641 return wtap_read_packet_bytes(wth
->random_fh
, buf
, packet_size
,
645 static struct erf_anchor_mapping
* erf_find_anchor_mapping(erf_t
*priv
,
649 struct erf_anchor_mapping mapping
= {
660 return (struct erf_anchor_mapping
*)g_hash_table_lookup(priv
->anchor_map
, &mapping
);
664 static bool erf_read_header(wtap
*wth
, FILE_T fh
,
666 erf_header_t
*erf_header
,
669 uint32_t *bytes_read
,
670 uint32_t *packet_size
,
671 GPtrArray
*anchor_mappings_to_update
)
673 union wtap_pseudo_header
*pseudo_header
= &rec
->rec_header
.packet_header
.pseudo_header
;
674 uint8_t erf_exhdr
[8];
675 uint64_t erf_exhdr_sw
;
679 struct wtap_erf_eth_hdr eth_hdr
;
680 uint32_t skiplen
= 0;
682 int max
= sizeof(pseudo_header
->erf
.ehdr_list
)/sizeof(struct erf_ehdr
);
683 erf_t
*priv
= (erf_t
*)wth
->priv
;
686 uint64_t host_id
= ERF_META_HOST_ID_IMPLICIT
;
687 uint8_t source_id
= 0;
689 bool host_id_found
= false;
691 if (!wtap_read_bytes_or_eof(fh
, erf_header
, sizeof(*erf_header
), err
, err_info
)) {
694 if (bytes_read
!= NULL
) {
695 *bytes_read
= sizeof(*erf_header
);
698 *packet_size
= g_ntohs(erf_header
->rlen
) - (uint32_t)sizeof(*erf_header
);
700 if (*packet_size
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
702 * Probably a corrupt capture file; don't blow up trying
703 * to allocate space for an immensely-large packet.
705 *err
= WTAP_ERR_BAD_FILE
;
706 *err_info
= ws_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
707 *packet_size
, WTAP_MAX_PACKET_SIZE_STANDARD
);
711 if (*packet_size
== 0) {
712 /* If this isn't a pad record, it's a corrupt packet; bail out */
713 if ((erf_header
->type
& 0x7F) != ERF_TYPE_PAD
) {
714 *err
= WTAP_ERR_BAD_FILE
;
715 *err_info
= g_strdup("erf: File has 0 byte packet");
722 uint64_t ts
= pletoh64(&erf_header
->ts
);
724 /*if ((erf_header->type & 0x7f) != ERF_TYPE_META || wth->file_type_subtype != file_type_subtype_erf) {*/
725 rec
->rec_type
= REC_TYPE_PACKET
;
726 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
728 * XXX: ERF_TYPE_META records should ideally be FT_SPECIFIC for display
729 * purposes, but currently ft_specific_record_phdr clashes with erf_mc_phdr
730 * and the pcapng dumper assumes it is a pcapng block type. Ideally we
731 * would register a block handler with pcapng and write out the closest
732 * pcapng block, or a custom block/Provenance record.
738 * TODO: how to identify, distinguish and timestamp events?
739 * What to do about ENCAP_ERF in pcap/pcapng? Filetype dissector is
740 * chosen by wth->file_type_subtype?
742 /* For now just treat all Provenance records as reports */
743 rec
->rec_type
= REC_TYPE_FT_SPECIFIC_REPORT
;
744 rec
->block
= wtap_block_create(WTAP_BLOCK_FT_SPECIFIC_REPORT
);
745 /* XXX: phdr ft_specific_record_phdr? */
748 rec
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
|WTAP_HAS_INTERFACE_ID
;
749 rec
->ts
.secs
= (long) (ts
>> 32);
750 ts
= ((ts
& 0xffffffff) * 1000 * 1000 * 1000);
751 ts
+= (ts
& 0x80000000) << 1; /* rounding */
752 rec
->ts
.nsecs
= ((int) (ts
>> 32));
753 if (rec
->ts
.nsecs
>= 1000000000) {
754 rec
->ts
.nsecs
-= 1000000000;
758 if_num
= erf_interface_id_from_flags(erf_header
->flags
);
761 /* Copy the ERF pseudo header */
762 memset(&pseudo_header
->erf
, 0, sizeof(pseudo_header
->erf
));
763 pseudo_header
->erf
.phdr
.ts
= pletoh64(&erf_header
->ts
);
764 pseudo_header
->erf
.phdr
.type
= erf_header
->type
;
765 pseudo_header
->erf
.phdr
.flags
= erf_header
->flags
;
766 pseudo_header
->erf
.phdr
.rlen
= g_ntohs(erf_header
->rlen
);
767 pseudo_header
->erf
.phdr
.lctr
= g_ntohs(erf_header
->lctr
);
768 pseudo_header
->erf
.phdr
.wlen
= g_ntohs(erf_header
->wlen
);
770 /* Copy the ERF extension header into the pseudo header */
771 type
= erf_header
->type
;
773 if (!wtap_read_bytes(fh
, &erf_exhdr
, sizeof(erf_exhdr
),
776 if (bytes_read
!= NULL
)
777 *bytes_read
+= (uint32_t)sizeof(erf_exhdr
);
778 *packet_size
-= (uint32_t)sizeof(erf_exhdr
);
779 skiplen
+= (uint32_t)sizeof(erf_exhdr
);
780 erf_exhdr_sw
= pntoh64(erf_exhdr
);
782 memcpy(&pseudo_header
->erf
.ehdr_list
[i
].ehdr
, &erf_exhdr_sw
, sizeof(erf_exhdr_sw
));
786 * XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
787 * erf_populate_interface)
789 switch (type
& 0x7FU
) {
790 case ERF_EXT_HDR_TYPE_HOST_ID
:
792 host_id
= erf_exhdr_sw
& ERF_EHDR_HOST_ID_MASK
;
794 host_id_found
= true;
796 case ERF_EXT_HDR_TYPE_FLOW_ID
:
797 /* Source ID is present in both Flow ID and Host ID extension headers */
799 source_id
= (erf_exhdr_sw
>> 48) & 0xff;
801 case ERF_EXT_HDR_TYPE_ANCHOR_ID
:
808 interface_id
= erf_populate_interface((erf_t
*) wth
->priv
, wth
, pseudo_header
, host_id
, source_id
, if_num
, err
, err_info
);
809 if (interface_id
< 0) {
812 rec
->rec_header
.packet_header
.interface_id
= (unsigned) interface_id
;
814 /* Try to find comment links using Anchor ID. Done here after we found the first Host ID and have updated the implicit Host ID. */
815 erf_update_anchors_from_header(priv
, rec
, pseudo_header
, host_id
, anchor_mappings_to_update
);
817 switch (erf_header
->type
& 0x7F) {
820 case ERF_TYPE_RAW_LINK
:
821 case ERF_TYPE_INFINIBAND
:
822 case ERF_TYPE_INFINIBAND_LINK
:
824 case ERF_TYPE_OPA_SNC
:
825 case ERF_TYPE_OPA_9B
:
828 rec
->rec_header
.packet_header
.len
= g_htons(erf_header
->wlen
);
829 rec
->rec_header
.packet_header
.caplen
= g_htons(erf_header
->wlen
);
835 case ERF_TYPE_HDLC_POS
:
836 case ERF_TYPE_COLOR_HDLC_POS
:
837 case ERF_TYPE_DSM_COLOR_HDLC_POS
:
838 case ERF_TYPE_COLOR_HASH_POS
:
844 case ERF_TYPE_COLOR_ETH
:
845 case ERF_TYPE_DSM_COLOR_ETH
:
846 case ERF_TYPE_COLOR_HASH_ETH
:
847 if (!wtap_read_bytes(fh
, ð_hdr
, sizeof(eth_hdr
), err
, err_info
))
849 if (bytes_read
!= NULL
)
850 *bytes_read
+= (uint32_t)sizeof(eth_hdr
);
851 *packet_size
-= (uint32_t)sizeof(eth_hdr
);
852 skiplen
+= (uint32_t)sizeof(eth_hdr
);
853 pseudo_header
->erf
.subhdr
.eth_hdr
= eth_hdr
;
856 case ERF_TYPE_MC_HDLC
:
857 case ERF_TYPE_MC_RAW
:
858 case ERF_TYPE_MC_ATM
:
859 case ERF_TYPE_MC_RAW_CHANNEL
:
860 case ERF_TYPE_MC_AAL5
:
861 case ERF_TYPE_MC_AAL2
:
862 case ERF_TYPE_COLOR_MC_HDLC_POS
:
863 if (!wtap_read_bytes(fh
, &mc_hdr
, sizeof(mc_hdr
), err
, err_info
))
865 if (bytes_read
!= NULL
)
866 *bytes_read
+= (uint32_t)sizeof(mc_hdr
);
867 *packet_size
-= (uint32_t)sizeof(mc_hdr
);
868 skiplen
+= (uint32_t)sizeof(mc_hdr
);
869 pseudo_header
->erf
.subhdr
.mc_hdr
= g_ntohl(mc_hdr
);
873 if (!wtap_read_bytes(fh
, &aal2_hdr
, sizeof(aal2_hdr
), err
, err_info
))
875 if (bytes_read
!= NULL
)
876 *bytes_read
+= (uint32_t)sizeof(aal2_hdr
);
877 *packet_size
-= (uint32_t)sizeof(aal2_hdr
);
878 skiplen
+= (uint32_t)sizeof(aal2_hdr
);
879 pseudo_header
->erf
.subhdr
.aal2_hdr
= g_ntohl(aal2_hdr
);
882 case ERF_TYPE_IP_COUNTER
:
883 case ERF_TYPE_TCP_FLOW_COUNTER
:
884 /* unsupported, continue with default: */
886 /* let the dissector dissect as unknown record type for forwards compatibility */
891 rec
->rec_header
.packet_header
.len
= g_ntohs(erf_header
->wlen
);
892 rec
->rec_header
.packet_header
.caplen
= MIN( g_ntohs(erf_header
->wlen
),
893 g_ntohs(erf_header
->rlen
) - (uint32_t)sizeof(*erf_header
) - skiplen
);
896 if (*packet_size
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
898 * Probably a corrupt capture file; don't blow up trying
899 * to allocate space for an immensely-large packet.
901 *err
= WTAP_ERR_BAD_FILE
;
902 *err_info
= ws_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
903 *packet_size
, WTAP_MAX_PACKET_SIZE_STANDARD
);
910 static int wtap_wtap_encap_to_erf_encap(int encap
)
913 for(i
= 0; i
< NUM_ERF_ENCAPS
; i
++){
914 if(erf_to_wtap_map
[i
].wtap_encap_value
== encap
)
915 return erf_to_wtap_map
[i
].erf_encap_value
;
920 static bool erf_write_phdr(wtap_dumper
*wdh
, int encap
, const union wtap_pseudo_header
*pseudo_header
, int * err
)
922 uint8_t erf_hdr
[sizeof(struct erf_mc_phdr
)];
923 uint8_t erf_subhdr
[sizeof(union erf_subhdr
)];
924 uint8_t ehdr
[8*MAX_ERF_EHDR
];
926 size_t subhdr_size
= 0;
928 uint8_t has_more
= 0;
932 memset(&erf_hdr
, 0, sizeof(erf_hdr
));
933 phtolell(&erf_hdr
[0], pseudo_header
->erf
.phdr
.ts
);
934 erf_hdr
[8] = pseudo_header
->erf
.phdr
.type
;
935 erf_hdr
[9] = pseudo_header
->erf
.phdr
.flags
;
936 phtons(&erf_hdr
[10], pseudo_header
->erf
.phdr
.rlen
);
937 phtons(&erf_hdr
[12], pseudo_header
->erf
.phdr
.lctr
);
938 phtons(&erf_hdr
[14], pseudo_header
->erf
.phdr
.wlen
);
939 size
= sizeof(struct erf_phdr
);
941 switch(pseudo_header
->erf
.phdr
.type
& 0x7F) {
942 case ERF_TYPE_MC_HDLC
:
943 case ERF_TYPE_MC_RAW
:
944 case ERF_TYPE_MC_ATM
:
945 case ERF_TYPE_MC_RAW_CHANNEL
:
946 case ERF_TYPE_MC_AAL5
:
947 case ERF_TYPE_MC_AAL2
:
948 case ERF_TYPE_COLOR_MC_HDLC_POS
:
949 phtonl(&erf_subhdr
[0], pseudo_header
->erf
.subhdr
.mc_hdr
);
950 subhdr_size
+= (int)sizeof(struct erf_mc_hdr
);
953 phtonl(&erf_subhdr
[0], pseudo_header
->erf
.subhdr
.aal2_hdr
);
954 subhdr_size
+= (int)sizeof(struct erf_aal2_hdr
);
957 case ERF_TYPE_COLOR_ETH
:
958 case ERF_TYPE_DSM_COLOR_ETH
:
959 case ERF_TYPE_COLOR_HASH_ETH
:
960 memcpy(&erf_subhdr
[0], &pseudo_header
->erf
.subhdr
.eth_hdr
, sizeof pseudo_header
->erf
.subhdr
.eth_hdr
);
961 subhdr_size
+= erf_eth_hdr_size
;
971 if (!wtap_dump_file_write(wdh
, erf_hdr
, size
, err
))
974 /*write out up to MAX_ERF_EHDR extension headers*/
975 has_more
= pseudo_header
->erf
.phdr
.type
& 0x80;
976 if(has_more
){ /*we have extension headers*/
978 phtonll(ehdr
+(i
*8), pseudo_header
->erf
.ehdr_list
[i
].ehdr
);
979 if(i
== MAX_ERF_EHDR
-1) ehdr
[i
*8] = ehdr
[i
*8] & 0x7F;
980 has_more
= ehdr
[i
*8] & 0x80;
982 }while(has_more
&& i
< MAX_ERF_EHDR
);
983 if (!wtap_dump_file_write(wdh
, ehdr
, 8*i
, err
))
987 if(!wtap_dump_file_write(wdh
, erf_subhdr
, subhdr_size
, err
))
994 static void erf_dump_priv_init_gen_time(erf_dump_t
*dump_priv
) {
997 real_time
= g_get_real_time();
998 /* Convert TimeVal to ERF timestamp */
999 dump_priv
->gen_time
= ((real_time
/ G_USEC_PER_SEC
) << 32) + ((real_time
% G_USEC_PER_SEC
) << 32) / 1000 / 1000;
1003 static bool erf_write_wtap_option_to_capture_tag(wtap_block_t block _U_
,
1005 wtap_opttype_e option_type _U_
,
1006 wtap_optval_t
*optval
,
1009 struct erf_meta_section
*section_ptr
= (struct erf_meta_section
*) user_data
;
1010 struct erf_meta_tag
*tag_ptr
= NULL
;
1012 tag_ptr
= g_new0(struct erf_meta_tag
, 1);
1015 case OPT_SHB_USERAPPL
:
1016 tag_ptr
->type
= ERF_META_TAG_app_name
;
1017 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1018 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1021 tag_ptr
->type
= ERF_META_TAG_comment
;
1022 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1023 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1026 erf_meta_tag_free(tag_ptr
);
1032 g_ptr_array_add(section_ptr
->tags
, tag_ptr
);
1034 return true; /* we always succeed */
1037 static bool erf_write_wtap_option_to_host_tag(wtap_block_t block _U_
,
1039 wtap_opttype_e option_type _U_
,
1040 wtap_optval_t
*optval
,
1043 struct erf_meta_section
*section_ptr
= (struct erf_meta_section
*) user_data
;
1044 struct erf_meta_tag
*tag_ptr
= NULL
;
1046 tag_ptr
= g_new0(struct erf_meta_tag
, 1);
1049 case OPT_SHB_HARDWARE
:
1050 tag_ptr
->type
= ERF_META_TAG_cpu
;
1051 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1052 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1055 tag_ptr
->type
= ERF_META_TAG_os
;
1056 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1057 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1060 erf_meta_tag_free(tag_ptr
);
1066 g_ptr_array_add(section_ptr
->tags
, tag_ptr
);
1068 return true; /* we always succeed */
1071 static bool erf_write_wtap_option_to_interface_tag(wtap_block_t block _U_
,
1073 wtap_opttype_e option_type _U_
,
1074 wtap_optval_t
*optval
,
1077 struct erf_meta_section
*section_ptr
= (struct erf_meta_section
*) user_data
;
1078 struct erf_meta_tag
*tag_ptr
= NULL
;
1080 tag_ptr
= g_new0(struct erf_meta_tag
, 1);
1084 tag_ptr
->type
= ERF_META_TAG_comment
;
1085 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1086 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1089 tag_ptr
->type
= ERF_META_TAG_name
;
1090 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1091 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1093 case OPT_IDB_DESCRIPTION
:
1094 tag_ptr
->type
= ERF_META_TAG_descr
;
1095 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1096 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1099 tag_ptr
->type
= ERF_META_TAG_os
;
1100 tag_ptr
->value
= (uint8_t*)g_strdup(optval
->stringval
);
1101 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1103 case OPT_IDB_TSOFFSET
:
1104 tag_ptr
->type
= ERF_META_TAG_ts_offset
;
1105 tag_ptr
->length
= 8;
1106 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->uint64val
));
1107 /* convert to relative ERF timestamp */
1108 phtolell(tag_ptr
->value
, optval
->uint64val
<< 32);
1111 tag_ptr
->type
= ERF_META_TAG_if_speed
;
1112 tag_ptr
->length
= 8;
1113 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->uint64val
));
1114 phtonll(tag_ptr
->value
, optval
->uint64val
);
1116 case OPT_IDB_IP4ADDR
:
1117 tag_ptr
->type
= ERF_META_TAG_if_ipv4
;
1118 tag_ptr
->length
= 4;
1119 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->ipv4val
));
1120 memcpy(tag_ptr
->value
, &optval
->ipv4val
, sizeof(optval
->ipv4val
));
1122 case OPT_IDB_IP6ADDR
:
1123 tag_ptr
->type
= ERF_META_TAG_if_ipv6
;
1124 tag_ptr
->length
= 16;
1125 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->ipv6val
));
1126 memcpy(tag_ptr
->value
, &optval
->ipv6val
, sizeof(optval
->ipv6val
));
1128 case OPT_IDB_FILTER
:
1130 if_filter_opt_t
*filter
;
1131 filter
= &optval
->if_filterval
;
1132 tag_ptr
->type
= 0xF800;
1133 if(filter
->type
== if_filter_pcap
) {
1134 tag_ptr
->type
= ERF_META_TAG_filter
;
1135 tag_ptr
->value
= (uint8_t*)g_strdup(filter
->data
.filter_str
);
1136 tag_ptr
->length
= (uint16_t)strlen((char*)tag_ptr
->value
);
1140 case OPT_IDB_FCSLEN
:
1141 tag_ptr
->type
= ERF_META_TAG_fcs_len
;
1142 tag_ptr
->length
= 4;
1143 tag_ptr
->value
= (uint8_t*)g_malloc(tag_ptr
->length
);
1144 phtonl(tag_ptr
->value
, (uint32_t)optval
->uint8val
);
1146 /* TODO: Don't know what to do with these yet */
1147 case OPT_IDB_EUIADDR
:
1149 tag_ptr
->type
= ERF_META_TAG_if_eui
;
1150 tag_ptr
->length
= 8;
1151 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->eui64val
));
1152 memcpy(tag_ptr
->value
, &optval
->euival
, sizeof(optval
->eui64val
));
1155 case OPT_IDB_MACADDR
:
1157 tag_ptr
->type
= ERF_META_TAG_if_mac
;
1158 tag_ptr
->length
= 6;
1159 /*value same format as pcapng (6-byte canonical, padded by write
1160 * function automatically to 32-bit boundary)*/
1161 tag_ptr
->value
= (uint8_t*)g_malloc(sizeof(optval
->macval
));
1162 memcpy(tag_ptr
->value
, &optval
->macval
, sizeof(optval
->macval
));
1165 case OPT_IDB_TSRESOL
:
1169 erf_meta_tag_free(tag_ptr
);
1175 g_ptr_array_add(section_ptr
->tags
, tag_ptr
);
1177 return true; /* we always succeed */
1180 static void erf_populate_section_length_by_tags(struct erf_meta_section
*section_ptr
) {
1182 struct erf_meta_tag
*tag_ptr
;
1184 section_ptr
->section_length
= 8;
1186 for(;i
< section_ptr
->tags
->len
; i
++) {
1187 tag_ptr
= (struct erf_meta_tag
*)g_ptr_array_index(section_ptr
->tags
, i
);
1188 section_ptr
->section_length
+= ERF_META_TAG_TOTAL_ALIGNED_LENGTH(tag_ptr
->length
);
1193 * @brief Converts a wtap_block_t block to ERF metadata sections
1194 * @param block a wtap_block_t block
1195 * @param sections pointer to a GPtrArray containing pointers to sections
1196 * @param section_type the pre-specified section_type
1197 * @param section_id Section ID to assign
1198 * @param func a wtap_block_foreach_func call back function to specify
1199 * what needs to be done on the block
1200 * @return true if success, false if failed
1202 static bool erf_wtap_blocks_to_erf_sections(wtap_block_t block
, GPtrArray
*sections
, uint16_t section_type
, uint16_t section_id
, wtap_block_foreach_func func
) {
1204 if(!block
|| !sections
|| !func
) {
1208 struct erf_meta_section
*section_ptr
;
1210 section_ptr
= g_new(struct erf_meta_section
, 1);
1211 section_ptr
->tags
= g_ptr_array_new_with_free_func(erf_meta_tag_free
);
1212 section_ptr
->type
= section_type
;
1213 section_ptr
->section_id
= section_id
;
1215 wtap_block_foreach_option(block
, func
, (void*)section_ptr
);
1216 erf_populate_section_length_by_tags(section_ptr
);
1217 g_ptr_array_add(sections
, section_ptr
);
1223 static bool erf_meta_write_tag(wtap_dumper
*wdh
, struct erf_meta_tag
*tag_ptr
, int *err
) {
1227 /* we only need to pad up to 32 bits*/
1228 uint32_t padbuf
= 0;
1230 pad
= ERF_META_TAG_ALIGNED_LENGTH(tag_ptr
->length
) - tag_ptr
->length
;
1231 data
[0] = g_htons(tag_ptr
->type
);
1232 data
[1] = g_htons(tag_ptr
->length
);
1234 if(!wtap_dump_file_write(wdh
, data
, sizeof(data
), err
)) return false;
1236 if(!wtap_dump_file_write(wdh
, tag_ptr
->value
, tag_ptr
->length
, err
)) return false;
1239 if(!wtap_dump_file_write(wdh
, &padbuf
, pad
, err
)) return false;
1246 static bool erf_meta_write_section(wtap_dumper
*wdh
, struct erf_meta_section
*section_ptr
, int *err
) {
1248 struct erf_meta_tag
*tag_ptr
;
1252 data
[0] = g_htons(section_ptr
->type
);
1253 data
[1] = g_htons(4); /*section header length*/
1254 data
[2] = g_htons(section_ptr
->section_id
);
1255 data
[3] = g_htons(section_ptr
->section_length
);
1257 if(!wtap_dump_file_write(wdh
, data
, sizeof(data
), err
)) return false;
1259 for(i
= 0; i
< section_ptr
->tags
->len
; i
++) {
1260 tag_ptr
= (struct erf_meta_tag
*)g_ptr_array_index(section_ptr
->tags
, i
);
1261 if(!erf_meta_write_tag(wdh
, tag_ptr
, err
)) return false;
1268 static bool erf_wtap_info_to_sections(wtap_dumper
*wdh
, GPtrArray
*sections
) {
1272 block
= g_array_index(wdh
->shb_hdrs
, wtap_block_t
, 0);
1273 erf_wtap_blocks_to_erf_sections(block
, sections
, ERF_META_SECTION_CAPTURE
, 0, erf_write_wtap_option_to_capture_tag
);
1275 block
= g_array_index(wdh
->shb_hdrs
, wtap_block_t
, 0);
1276 erf_wtap_blocks_to_erf_sections(block
, sections
, ERF_META_SECTION_HOST
, 0, erf_write_wtap_option_to_host_tag
);
1278 /*TODO: support >4 interfaces by using more Source IDs. Affects more than this
1279 * function as need more metadata records. Just dump them all out for now. */
1280 for(i
= 0; i
< wdh
->interface_data
->len
; i
++) {
1281 block
= g_array_index(wdh
->interface_data
, wtap_block_t
, i
);
1282 erf_wtap_blocks_to_erf_sections(block
, sections
, ERF_META_SECTION_INTERFACE
, (int16_t)i
+1, erf_write_wtap_option_to_interface_tag
);
1288 static bool erf_comment_to_sections(wtap_dumper
*wdh _U_
, uint16_t section_type
, uint16_t section_id
, char *comment
, GPtrArray
*sections
){
1289 struct erf_meta_section
*section_ptr
;
1290 struct erf_meta_tag
*comment_tag_ptr
= NULL
;
1291 struct erf_meta_tag
*user_tag_ptr
= NULL
;
1292 const char *user
= NULL
;
1294 /* Generate the section */
1295 section_ptr
= g_new(struct erf_meta_section
, 1);
1296 section_ptr
->type
= section_type
;
1297 section_ptr
->section_id
= section_id
;
1298 section_ptr
->tags
= g_ptr_array_new_with_free_func(erf_meta_tag_free
);
1300 /* Generate the comment tag */
1301 comment_tag_ptr
= g_new(struct erf_meta_tag
, 1);
1302 comment_tag_ptr
->type
= ERF_META_TAG_comment
;
1303 /* XXX: if the comment has been cleared write the empty string (which
1304 * conveniently is all a zero length tag which means the value is
1306 comment_tag_ptr
->value
= (uint8_t*)g_strdup(comment
? comment
: "");
1307 comment_tag_ptr
->length
= (uint16_t)strlen((char*)comment_tag_ptr
->value
);
1308 g_ptr_array_add(section_ptr
->tags
, comment_tag_ptr
);
1310 user
= g_get_user_name();
1312 /* Generate username tag */
1313 user_tag_ptr
= g_new(struct erf_meta_tag
, 1);
1314 user_tag_ptr
->type
= ERF_META_TAG_user
;
1315 user_tag_ptr
->value
= (uint8_t*)g_strdup(user
);
1316 user_tag_ptr
->length
= (uint16_t)strlen((char*)user_tag_ptr
->value
);
1317 g_ptr_array_add(section_ptr
->tags
, user_tag_ptr
);
1320 erf_populate_section_length_by_tags(section_ptr
);
1322 g_ptr_array_add(sections
, section_ptr
);
1327 static uint64_t erf_get_random_anchor_id(erf_dump_t
*dump_priv
) {
1328 return (((uint64_t)g_rand_int(dump_priv
->rand
) << 32) | (uint64_t)g_rand_int(dump_priv
->rand
)) >> 16;
1331 static uint64_t erf_metaid_ext_hdr(uint8_t exthdr_type
, uint64_t id
, uint8_t srcid_flags
) {
1334 ext_hdr
= id
& ERF_EHDR_HOST_ID_MASK
;
1335 ext_hdr
|= ((uint64_t)srcid_flags
) << 48;
1336 ext_hdr
|= ((uint64_t)exthdr_type
) << 56;
1340 #define erf_host_id_ext_hdr(host_id, source_id) erf_metaid_ext_hdr(ERF_EXT_HDR_TYPE_HOST_ID, host_id, source_id)
1341 #define erf_anchor_id_ext_hdr(anchor_id, flags) erf_metaid_ext_hdr(ERF_EXT_HDR_TYPE_ANCHOR_ID, anchor_id, flags)
1343 static inline bool erf_add_ext_hdr_to_list(uint64_t ext_hdr
, uint64_t comparison_mask
, GArray
*extra_ehdrs
) {
1344 /* check for existing Host ID in set and add */
1346 struct erf_ehdr ehdr_tmp
;
1347 struct erf_ehdr
*ehdr_ptr
= NULL
;
1352 ext_hdr
= ext_hdr
& ~ERF_EHDR_MORE_EXTHDR_MASK
;
1353 if (comparison_mask
== 0)
1354 comparison_mask
= UINT64_MAX
;
1356 comparison_mask
&= ~ERF_EHDR_MORE_EXTHDR_MASK
;
1358 for (i
= 0; i
< extra_ehdrs
->len
; i
++) {
1359 ehdr_ptr
= &g_array_index(extra_ehdrs
, struct erf_ehdr
, i
);
1360 /* Check if we already have this Host ID extension header */
1361 if (ext_hdr
== (ehdr_ptr
->ehdr
& comparison_mask
)) {
1366 /* set more flag on last extension header */
1368 ehdr_ptr
->ehdr
|= ERF_EHDR_MORE_EXTHDR_MASK
;
1371 ehdr_tmp
.ehdr
= ext_hdr
; /*more flag already cleared above*/
1372 g_array_append_val(extra_ehdrs
, ehdr_tmp
);
1377 static inline bool erf_append_ext_hdr_to_list(uint64_t ext_hdr
, GArray
*extra_ehdrs
) {
1378 struct erf_ehdr ehdr_tmp
;
1383 ehdr_tmp
.ehdr
= ext_hdr
& ~ERF_EHDR_MORE_EXTHDR_MASK
;
1385 /* set more flag on last extension header */
1386 if (extra_ehdrs
->len
) {
1387 g_array_index(extra_ehdrs
, struct erf_ehdr
, extra_ehdrs
->len
- 1).ehdr
|= ERF_EHDR_MORE_EXTHDR_MASK
;
1390 g_array_append_val(extra_ehdrs
, ehdr_tmp
);
1395 static bool erf_update_host_id_ext_hdrs_list(erf_dump_t
*dump_priv
, const union wtap_pseudo_header
*pseudo_header
, GArray
*extra_ehdrs
) {
1401 uint8_t source_id
= 0;
1402 uint64_t host_id
= 0;
1403 bool host_id_found
= false;
1408 erf_type
= pseudo_header
->erf
.phdr
.type
& 0x7f;
1409 has_more
= pseudo_header
->erf
.phdr
.type
& 0x80;
1411 while (has_more
&& i
< MAX_ERF_EHDR
) {
1412 hdr
= pseudo_header
->erf
.ehdr_list
[i
].ehdr
;
1413 type
= (uint8_t) (hdr
>> 56);
1415 switch (type
& 0x7f) {
1416 case ERF_EXT_HDR_TYPE_HOST_ID
:
1417 host_id
= hdr
& ERF_EHDR_HOST_ID_MASK
;
1418 source_id
= (hdr
>> 48) & 0xff;
1420 /* Don't add the wireshark Host ID Source ID 0 twice since we already add it to metadata records */
1421 if (host_id
!= dump_priv
->host_id
|| source_id
!= 0)
1422 if (!erf_add_ext_hdr_to_list(hdr
, 0, extra_ehdrs
)) return false;
1424 if (!host_id_found
) {
1425 /* XXX: Take the opportunity to update the implicit Host ID if we
1426 * don't know it yet. Ideally we should pass this through from the
1427 * reader as a custom option or similar. */
1428 if (erf_type
== ERF_TYPE_META
&& ((hdr
>> 48) & 0xff) > 0) {
1429 if (dump_priv
->implicit_host_id
== ERF_META_HOST_ID_IMPLICIT
) {
1430 dump_priv
->implicit_host_id
= host_id
;
1435 host_id_found
= true;
1437 case ERF_EXT_HDR_TYPE_FLOW_ID
:
1438 if (source_id
== 0) /* If no Host ID extension header use the first Source ID only */
1439 source_id
= (hdr
>> 48) & 0xff;
1443 has_more
= type
& 0x80;
1447 /* Add Source ID with implicit Host ID if not found */
1448 if (!host_id_found
) {
1449 uint64_t implicit_host_id
= dump_priv
->implicit_host_id
== ERF_META_HOST_ID_IMPLICIT
? 0 : dump_priv
->implicit_host_id
;
1450 /* Don't add the wireshark Host ID Source ID 0 twice since we already add it to metadata records */
1451 if (implicit_host_id
!= dump_priv
->host_id
|| source_id
!= 0)
1452 if (!erf_add_ext_hdr_to_list(erf_host_id_ext_hdr(implicit_host_id
, source_id
), 0, extra_ehdrs
)) return false;
1459 * Writes a metadata record with a randomly generated Anchor ID with the
1460 * user comment attached to its comment section, also updates the
1461 * modified frame header to include a Host ID extension header and
1462 * a Anchor ID extension header to link the records together.
1463 * @param wdh the wtap_dumper structure
1464 * @param dump_priv private data for the dump stream
1465 * @param rec record metadata from which to get user comment
1466 * @param mutable_hdr pseudo_header to update with Anchor ID for comment record
1467 * @param err the error value
1468 * @return A bool value to indicate whether the dump was successful
1470 static bool erf_write_anchor_meta_update_phdr(wtap_dumper
*wdh
, erf_dump_t
*dump_priv
, const wtap_rec
*rec
, union wtap_pseudo_header
*mutable_hdr
, int *err
) {
1472 GPtrArray
* sections
= NULL
;
1475 uint8_t ext_hdr_count
= 0;
1477 uint64_t host_id_src_hdr
= ERF_META_HOST_ID_IMPLICIT
;
1478 uint64_t host_id_own_hdr
= erf_host_id_ext_hdr(dump_priv
->host_id
, 0);
1479 uint64_t flow_id_hdr
= 0;
1480 uint64_t anchor_id_hdr
= 0;
1481 bool found_host_id
= false;
1482 bool found_own_host_id
= false;
1483 bool found_flow_id
= false;
1484 int new_ext_hdrs
= 0;
1485 uint8_t insert_idx
= 0;
1486 uint8_t source_id
= 0;
1488 uint64_t implicit_host_id
= dump_priv
->implicit_host_id
== ERF_META_HOST_ID_IMPLICIT
? 0 : dump_priv
->implicit_host_id
;
1493 * There are 3 possible scenarios:
1494 * a. The record has a source Host ID but not our Host ID. We need to add our
1495 * Host ID extension header then our Anchor ID extension header.
1496 * b. The record already has our Host ID extension header on it. We should
1497 * insert the Anchor ID at the end of the list for that Host ID just
1498 * before the next Host ID extension header.
1499 * c. The record has no Host ID extension header at all. We need to add the Host ID
1500 * extension header making the Implicit Host ID explicit before we add our
1501 * one to avoid claiming the packet was captured by us.
1505 * Extract information from the packet extension header stack
1506 * 1. original source Host ID extension header.
1507 * 2. Anchor ID extension header insertion point (see b., above).
1508 * 3. Flow ID extension header so we can add it for reference to the metadata
1510 * 4. Enough information to generate an explicit Host ID extension header if
1511 * there wasn't one (see erf_get_source_from_header).
1514 has_more
= mutable_hdr
->erf
.phdr
.type
& 0x80;
1516 while (has_more
&& (i
< MAX_ERF_EHDR
)) {
1517 uint64_t hdr
= mutable_hdr
->erf
.ehdr_list
[i
].ehdr
;
1518 uint8_t type
= (uint8_t) (hdr
>> 56);
1520 switch (type
& 0x7f) {
1521 case ERF_EXT_HDR_TYPE_HOST_ID
:
1522 /* Set insertion point of anchor ID to be at end of Host ID list (i.e.
1523 * just before the next one). */
1524 if (found_own_host_id
&& !insert_idx
)
1527 if ((hdr
& ERF_EHDR_HOST_ID_MASK
) == dump_priv
->host_id
){
1528 found_own_host_id
= true;
1532 host_id_src_hdr
= hdr
;
1534 found_host_id
= true;
1537 case ERF_EXT_HDR_TYPE_FLOW_ID
:
1538 /*XXX: we only use this when making the implicit host id explicit,
1539 * otherwise we'd need to check the one in Host ID header too*/
1541 source_id
= (uint8_t)(hdr
>> 48);
1546 found_flow_id
= true;
1550 has_more
= type
& 0x80;
1559 /* Don't need to add our own Host ID twice if it is the same as the implicit*/
1560 if (!found_host_id
&& implicit_host_id
== dump_priv
->host_id
) {
1561 found_own_host_id
= true;
1565 * Update the packet record pseudo_header with Anchor ID and extension header(s)
1567 new_ext_hdrs
= 1 /*anchor id*/ + (found_own_host_id
?0:1) + (found_host_id
?0:1);
1569 if(ext_hdr_count
+ new_ext_hdrs
> MAX_ERF_EHDR
1570 || mutable_hdr
->erf
.phdr
.rlen
+ new_ext_hdrs
* 8 > 65535) {
1571 /* Not enough extension header slots to add Anchor ID */
1572 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
1576 mutable_hdr
->erf
.phdr
.rlen
+= new_ext_hdrs
* 8;
1578 /* Set the more extension headers flag */
1579 mutable_hdr
->erf
.phdr
.type
|= 0x80;
1580 if (insert_idx
> 0) {
1581 mutable_hdr
->erf
.ehdr_list
[insert_idx
-1].ehdr
|= ERF_EHDR_MORE_EXTHDR_MASK
;
1584 /* Generate the Anchor ID extension header */
1585 anchor_id_hdr
= erf_anchor_id_ext_hdr(erf_get_random_anchor_id(dump_priv
), 0);
1587 /* Either we can insert Anchor ID at the end of the list for our Host ID or we
1588 * need to append the Host ID(s) and Anchor ID */
1589 if (insert_idx
< ext_hdr_count
) {
1590 /* shuffle up any following extension headers FIRST - we know we have room now */
1591 for (j
= ext_hdr_count
; j
> insert_idx
; j
--) {
1592 mutable_hdr
->erf
.ehdr_list
[j
].ehdr
= mutable_hdr
->erf
.ehdr_list
[j
-1].ehdr
;
1595 /* copy more extension headers bit from previous extension header */
1596 anchor_id_hdr
|= ERF_EHDR_MORE_EXTHDR_MASK
;
1599 if(!found_host_id
) {
1600 /* No Host ID extension header found and we have an implicit Host ID which
1601 * we want to make explicit */
1603 /* XXX: it is important that we know the implicit Host ID here or we end
1604 * up semi-permentantly associating the packet with Host 0 (unknown), we should
1605 * pass it through from the reader. In theory we should be on the
1606 * original capture machine if we have no Host ID extension headers. */
1607 host_id_src_hdr
= erf_host_id_ext_hdr(implicit_host_id
, source_id
);
1608 mutable_hdr
->erf
.ehdr_list
[insert_idx
++].ehdr
= ERF_EHDR_SET_MORE_EXTHDR(host_id_src_hdr
);
1611 if(!found_own_host_id
) {
1612 /* Add our Host ID extension header */
1613 mutable_hdr
->erf
.ehdr_list
[insert_idx
++].ehdr
= ERF_EHDR_SET_MORE_EXTHDR(host_id_own_hdr
);
1616 /*Add the Anchor ID extension header */
1617 mutable_hdr
->erf
.ehdr_list
[insert_idx
].ehdr
= anchor_id_hdr
;
1621 * Now construct the metadata Anchor record with the same Anchor ID
1624 meta_ehdrs
= g_array_new(false, false, sizeof(struct erf_ehdr
));
1626 /* We need up to 4 extension headers on the Provenance metadata record */
1628 /* 1. Added by erf_write_meta_record: HostID exthdr to indicate this Anchor
1629 * record was generated by this host. Source ID 0 to avoid changing the
1630 * implicit Host ID. */
1632 /* 2. AnchorID exthdr with 'unique' per-host Anchor ID assigned by this host
1633 * (in this case Wireshark). Anchor definition flag set to 1 to indicate this
1634 * record contains a definition of the ID, in this case a comment on a single
1635 * packet. Tied to above extension header by ordering like a list */
1636 erf_append_ext_hdr_to_list(anchor_id_hdr
| ERF_EHDR_ANCHOR_ID_DEFINITION_MASK
, meta_ehdrs
);
1638 /*Helpful for indexing*/
1639 /* 3. HostID exthdr with the original Source (first Host ID extension header) of the packet record */
1640 erf_append_ext_hdr_to_list(host_id_src_hdr
, meta_ehdrs
);
1642 /* Flow ID extension header from the packet record if we have one */
1643 if (found_flow_id
) {
1644 /* 4. FlowID exthdr with Flow ID from the packet so a flow search will find the comment
1645 * record too. Must come here so the (redundant here) Source ID is scoped to the
1646 * correct Host ID. */
1647 /* Clear the stack type just in case something tries to assume we're an IP
1648 * packet without looking at the ERF type. Clear Source ID too just in case
1649 * we're trying to associate with the wrong Host ID. */
1650 erf_append_ext_hdr_to_list(flow_id_hdr
& ~(ERF_EHDR_FLOW_ID_STACK_TYPE_MASK
|ERF_EHDR_FLOW_ID_SOURCE_ID_MASK
), meta_ehdrs
);
1653 /* Generate the metadata payload with the packet comment */
1654 /* XXX - can ERF have more than one comment? */
1655 sections
= g_ptr_array_new_with_free_func(erf_meta_section_free
);
1656 if (WTAP_OPTTYPE_SUCCESS
!= wtap_block_get_nth_string_option_value(rec
->block
, OPT_COMMENT
, 0, &pkt_comment
)) {
1659 erf_comment_to_sections(wdh
, ERF_META_SECTION_INFO
, 0x8000 /*local to record*/, pkt_comment
, sections
);
1661 /* Write the metadata record, but not the packet record as what we do depends
1662 * on the WTAP_ENCAP */
1663 ret
= erf_write_meta_record(wdh
, dump_priv
, mutable_hdr
->erf
.phdr
.ts
, sections
, meta_ehdrs
, err
);
1664 g_ptr_array_free(sections
, true);
1665 g_array_free(meta_ehdrs
, true);
1670 static bool erf_write_meta_record(wtap_dumper
*wdh
, erf_dump_t
*dump_priv
, uint64_t timestamp
, GPtrArray
*sections
, GArray
*extra_ehdrs
, int *err
) {
1671 union wtap_pseudo_header other_header
;
1672 struct erf_meta_tag gen_time_tag
;
1673 struct erf_meta_section
*section_ptr
;
1674 unsigned total_wlen
= 0;
1675 unsigned total_rlen
= 0;
1676 int64_t alignbytes
= 0;
1678 unsigned num_extra_ehdrs
= 0;
1680 if(!sections
|| sections
->len
<= 0)
1683 for(i
= 0; i
< sections
->len
; i
++) {
1684 section_ptr
= (struct erf_meta_section
*)g_ptr_array_index(sections
, i
);
1685 total_wlen
+= section_ptr
->section_length
;
1688 gen_time_tag
.type
= ERF_META_TAG_gen_time
;
1689 gen_time_tag
.length
= 8U;
1690 gen_time_tag
.value
= (uint8_t*)&dump_priv
->gen_time
;
1691 total_wlen
+= gen_time_tag
.length
+ 4;
1693 total_rlen
= total_wlen
+ 24; /* 24 is the header + extension header length */
1696 * These will be appended to the first extension header in
1697 * other_header.erf.ehdr_list. There are a total of MAX_ERF_EHDR
1698 * extension headers in that array, so we can append no more than
1699 * MAX_ERF_EHDR - 1 extension headeers.
1701 num_extra_ehdrs
= MIN(extra_ehdrs
->len
, MAX_ERF_EHDR
- 1);
1702 total_rlen
+= num_extra_ehdrs
* 8;
1704 /*padding to 8 byte alignment*/
1705 total_rlen
+= ERF_PADDING_TO_8(total_rlen
);
1707 if(total_rlen
> 65535) {
1708 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
1712 other_header
.erf
.phdr
.ts
= timestamp
;
1713 other_header
.erf
.phdr
.type
= ERF_TYPE_META
| 0x80;
1714 other_header
.erf
.phdr
.flags
= 0x04; /* Varying record length */
1715 other_header
.erf
.phdr
.lctr
= 0;
1716 other_header
.erf
.phdr
.wlen
= (uint16_t)total_wlen
;
1717 other_header
.erf
.phdr
.rlen
= (uint16_t)total_rlen
;
1718 /*Add our Host ID in Host ID extension header indicating we generated this
1719 * record. Source ID 0 to avoid affecting implicit Host ID. */
1720 other_header
.erf
.ehdr_list
[0].ehdr
= erf_host_id_ext_hdr(dump_priv
->host_id
, 0);
1721 /*Additional extension headers*/
1722 /*XXX: If we end up cutting the list short, erf_write_phdr will correct the
1723 * unterminated extension header list*/
1724 if (num_extra_ehdrs
> 0) {
1725 other_header
.erf
.ehdr_list
[0].ehdr
|= ERF_EHDR_MORE_EXTHDR_MASK
;
1726 memcpy(&other_header
.erf
.ehdr_list
[1], extra_ehdrs
->data
, sizeof(struct erf_ehdr
) * num_extra_ehdrs
);
1729 /* Make sure we always write out rlen, regardless of what happens */
1730 alignbytes
= wdh
->bytes_dumped
+ other_header
.erf
.phdr
.rlen
;
1732 if(!erf_write_phdr(wdh
, WTAP_ENCAP_ERF
, &other_header
, err
)) return false;
1734 /* Generation time */
1735 erf_meta_write_tag(wdh
, &gen_time_tag
, err
);
1738 for(i
= 0; i
< sections
->len
; i
++) {
1739 section_ptr
= (struct erf_meta_section
*)g_ptr_array_index(sections
, i
);
1740 erf_meta_write_section(wdh
, section_ptr
, err
);
1743 while(wdh
->bytes_dumped
< alignbytes
){
1744 if(!wtap_dump_file_write(wdh
, "", 1, err
)) return false;
1747 /* We wrote new packets, reloading is required */
1748 wdh
->needs_reload
= true;
1754 static erf_dump_t
*erf_dump_priv_create(void) {
1755 erf_dump_t
*dump_priv
;
1757 dump_priv
= g_new(erf_dump_t
, 1);
1758 dump_priv
->write_next_extra_meta
= false;
1759 dump_priv
->last_meta_periodic
= false;
1760 dump_priv
->gen_time
= 0;
1761 dump_priv
->host_id
= ERF_WS_DEFAULT_HOST_ID
;
1762 dump_priv
->implicit_host_id
= ERF_META_HOST_ID_IMPLICIT
;
1763 dump_priv
->first_frame_time_sec
= 0;
1764 dump_priv
->prev_inserted_time_sec
= 0;
1765 dump_priv
->prev_frame_ts
= 0;
1766 dump_priv
->prev_erf_type
= 0;
1767 dump_priv
->user_comment_ptr
= NULL
;
1768 dump_priv
->periodic_sections
= NULL
;
1769 dump_priv
->periodic_extra_ehdrs
= g_array_new(false, false, sizeof(struct erf_ehdr
));
1770 dump_priv
->rand
= g_rand_new();
1775 static bool erf_dump(
1777 const wtap_rec
*rec
,
1780 char **err_info _U_
)
1782 const union wtap_pseudo_header
*pseudo_header
= &rec
->rec_header
.packet_header
.pseudo_header
;
1783 union wtap_pseudo_header other_phdr
;
1785 int64_t alignbytes
= 0;
1786 unsigned padbytes
= 0;
1788 bool must_add_crc
= false;
1789 uint32_t crc32
= 0x00000000;
1790 erf_dump_t
*dump_priv
= (erf_dump_t
*)wdh
->priv
;
1791 /* Host ID extension header with Host ID 0 (unknown). For now use Source ID 1. */
1792 /* TODO: How to know if record was captured by this Wireshark? */
1793 uint64_t non_erf_host_id_ehdr
= erf_host_id_ext_hdr(0, 1);
1795 /* Don't write anything bigger than we're willing to read. */
1796 if(rec
->rec_header
.packet_header
.caplen
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
1797 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
1801 if(!dump_priv
->gen_time
) {
1802 erf_dump_priv_init_gen_time(dump_priv
);
1803 dump_priv
->first_frame_time_sec
= rec
->ts
.secs
;
1807 * ERF doesn't have a per-file encapsulation type, and it
1808 * doesn't have a pcapng-style notion of interfaces that
1809 * support a per-interface encapsulation type. Therefore,
1810 * we can just use this particular packet's encapsulation
1811 * without checking whether it's the encapsulation for the
1812 * dump file (as there isn't an encapsulation for an ERF
1813 * file, unless you count WTAP_ENCAP_ERF as the encapsulation
1814 * for all files, but we add ERF metadata if a packet is
1815 * written with an encapsulation other than WTAP_ENCAP_ERF).
1817 * We will check whether the encapsulation is something we
1820 if (rec
->rec_header
.packet_header
.pkt_encap
!= WTAP_ENCAP_ERF
) {
1821 unsigned int total_rlen
;
1822 unsigned int total_wlen
;
1824 /* Non-ERF encapsulation; generate ERF metadata */
1826 total_rlen
= rec
->rec_header
.packet_header
.caplen
+16;
1827 total_wlen
= rec
->rec_header
.packet_header
.len
;
1829 /* We can only convert packet records. */
1830 if (rec
->rec_type
!= REC_TYPE_PACKET
) {
1831 *err
= WTAP_ERR_UNWRITABLE_REC_TYPE
;
1835 erf_type
= wtap_wtap_encap_to_erf_encap(rec
->rec_header
.packet_header
.pkt_encap
);
1836 if (erf_type
== -1) {
1837 *err
= WTAP_ERR_UNWRITABLE_ENCAP
;
1841 /* Generate a fake header in other_phdr using data that we know*/
1842 memset(&other_phdr
, 0, sizeof(union wtap_pseudo_header
));
1843 /* Convert time erf timestamp format*/
1844 other_phdr
.erf
.phdr
.ts
= ((uint64_t) rec
->ts
.secs
<< 32) + (((uint64_t) rec
->ts
.nsecs
<<32) / 1000 / 1000 / 1000);
1845 other_phdr
.erf
.phdr
.type
= (uint8_t)erf_type
;
1846 /* Support up to 4 interfaces */
1847 /* TODO: use multiple Source IDs and metadata records to support >4 interfaces */
1848 other_phdr
.erf
.phdr
.flags
= rec
->rec_header
.packet_header
.interface_id
% ERF_MAX_INTERFACES
;
1849 other_phdr
.erf
.phdr
.flags
|= 0x4; /*vlen flag set because we're creating variable length records*/
1851 other_phdr
.erf
.phdr
.lctr
= 0;
1853 /*now we work out rlen, accounting for all the different headers and missing fcs(eth)*/
1854 switch(other_phdr
.erf
.phdr
.type
& 0x7F){
1856 total_rlen
+= 2; /*2 bytes for erf eth_type*/
1857 if (pseudo_header
->eth
.fcs_len
!= 4) {
1858 /* Either this packet doesn't include the FCS
1859 (pseudo_header->eth.fcs_len = 0), or we don't
1860 know whether it has an FCS (= -1). We have to
1861 synthesize an FCS.*/
1862 if(!(rec
->rec_header
.packet_header
.caplen
< rec
->rec_header
.packet_header
.len
)){ /*don't add FCS if packet has been snapped off*/
1863 crc32
= crc32_ccitt_seed(pd
, rec
->rec_header
.packet_header
.caplen
, 0xFFFFFFFF);
1864 total_rlen
+= 4; /*4 bytes for added checksum*/
1866 must_add_crc
= true;
1870 case ERF_TYPE_HDLC_POS
:
1871 /*we assume that it's missing a FCS checksum, make one up*/
1872 if(!(rec
->rec_header
.packet_header
.caplen
< rec
->rec_header
.packet_header
.len
)){ /*unless of course, the packet has been snapped off*/
1873 crc32
= crc32_ccitt_seed(pd
, rec
->rec_header
.packet_header
.caplen
, 0xFFFFFFFF);
1874 total_rlen
+= 4; /*4 bytes for added checksum*/
1876 must_add_crc
= true; /* XXX - these never have an FCS? */
1883 /* Add Host ID extension header with Host ID 0 (unknown). For now use Source ID 1. */
1884 other_phdr
.erf
.phdr
.type
|= 0x80;
1885 other_phdr
.erf
.ehdr_list
[0].ehdr
= non_erf_host_id_ehdr
;
1888 padbytes
= ERF_PADDING_TO_8(total_rlen
); /*calculate how much padding will be required */
1889 if(rec
->rec_header
.packet_header
.caplen
< rec
->rec_header
.packet_header
.len
){ /*if packet has been snapped, we need to round down what we output*/
1890 round_down
= (8 - padbytes
) % 8;
1891 total_rlen
-= round_down
;
1893 total_rlen
+= padbytes
;
1896 if (total_rlen
> UINT16_MAX
|| total_wlen
> UINT16_MAX
) {
1897 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
1901 other_phdr
.erf
.phdr
.rlen
= (uint16_t)total_rlen
;
1902 other_phdr
.erf
.phdr
.wlen
= (uint16_t)total_wlen
;
1904 pseudo_header
= &other_phdr
;
1905 } else if (rec
->presence_flags
& WTAP_HAS_TS
) {
1906 // Update timestamp if changed.
1909 uint64_t ts
= pseudo_header
->erf
.phdr
.ts
;
1911 secs
= (long) (ts
>> 32);
1912 ts
= ((ts
& 0xffffffff) * 1000 * 1000 * 1000);
1913 ts
+= (ts
& 0x80000000) << 1; /* rounding */
1914 nsecs
= ((int) (ts
>> 32));
1915 if (nsecs
>= 1000000000) {
1916 nsecs
-= 1000000000;
1920 if (secs
!= rec
->ts
.secs
|| nsecs
!= rec
->ts
.nsecs
) {
1921 other_phdr
= *pseudo_header
;
1922 other_phdr
.erf
.phdr
.ts
= ((uint64_t) rec
->ts
.secs
<< 32) + (((uint64_t) rec
->ts
.nsecs
<<32) / 1000 / 1000 / 1000);
1923 pseudo_header
= &other_phdr
;
1927 /* We now have a (real or fake) ERF record */
1928 erf_type
= pseudo_header
->erf
.phdr
.type
& 0x7FU
;
1930 /* Accumulate Host ID/Source ID to put in updated periodic metadata */
1931 /* TODO: pass these through from read interface list instead? */
1932 /* Note: this includes the one we made for the fake ERF header */
1933 erf_update_host_id_ext_hdrs_list(dump_priv
, pseudo_header
, dump_priv
->periodic_extra_ehdrs
);
1935 /* Insert new metadata record depending on whether the capture comment has
1936 * changed. Write metadata each second at boundaries. If there is metadata
1937 * write at the end of each of metadata records so we update the metadata. */
1938 if (erf_type
== ERF_TYPE_META
) {
1939 /* Check whether the capture comment string has changed */
1940 /* Updates write_next_extra_meta */
1941 dump_priv
->last_meta_periodic
= erf_dump_priv_compare_capture_comment(wdh
, dump_priv
, pseudo_header
, pd
);
1942 } else { /* don't want to insert a new metadata record while looking at another */
1943 if (dump_priv
->prev_erf_type
== ERF_TYPE_META
&& dump_priv
->last_meta_periodic
) {
1944 /* Last frame was a periodic (non-comment) metadata record (and this frame is not), check if we
1945 * need to insert one to update metadata. */
1947 if(dump_priv
->write_next_extra_meta
) {
1948 if (!dump_priv
->periodic_sections
) {
1949 /* If we've seen metadata just insert the capture comment and not the
1950 * rest of the metadata */
1951 dump_priv
->periodic_sections
= g_ptr_array_new_with_free_func(erf_meta_section_free
);
1952 erf_comment_to_sections(wdh
, ERF_META_SECTION_CAPTURE
, 0, dump_priv
->user_comment_ptr
, dump_priv
->periodic_sections
);
1955 if (!erf_write_meta_record(wdh
, dump_priv
, dump_priv
->prev_frame_ts
, dump_priv
->periodic_sections
, dump_priv
->periodic_extra_ehdrs
, err
)) return false;
1956 dump_priv
->prev_inserted_time_sec
= rec
->ts
.secs
;
1957 /*TODO: clear accumulated existing extension headers here?*/
1960 /* If we have seen a metadata record in the first ~1 second it
1961 * means that we are dealing with an ERF file with metadata already in them.
1962 * We don't want to write extra metadata if nothing has changed. We can't
1963 * trust the Wireshark representation since we massage the fields on
1965 /* restart searching for next meta record to update capture comment at */
1966 dump_priv
->write_next_extra_meta
= false;
1967 } else if (rec
->ts
.secs
> dump_priv
->first_frame_time_sec
+ 1
1968 && dump_priv
->prev_inserted_time_sec
!= rec
->ts
.secs
) {
1969 /* For compatibility, don't insert metadata for older ERF files with no changed metadata */
1970 if (dump_priv
->write_next_extra_meta
) {
1971 if (!dump_priv
->periodic_sections
) {
1972 /* If we get here, metadata record was not found in the first ~1 sec
1973 * but we have either a capture comment or a non-ERF file (see
1975 /* Start inserting metadata records from wtap data at second boundaries */
1976 dump_priv
->periodic_sections
= g_ptr_array_new_with_free_func(erf_meta_section_free
);
1977 erf_wtap_info_to_sections(wdh
, dump_priv
->periodic_sections
);
1981 /* At second boundaries insert either the updated comment (if we've seen some metadata records
1982 * already) or the full metadata */
1983 if (dump_priv
->periodic_sections
) {
1984 if (!erf_write_meta_record(wdh
, dump_priv
, (uint64_t)(rec
->ts
.secs
) << 32, dump_priv
->periodic_sections
, dump_priv
->periodic_extra_ehdrs
, err
)) return false;
1985 dump_priv
->prev_inserted_time_sec
= rec
->ts
.secs
;
1990 /* If the packet user comment has changed, we need to
1991 * construct a new header with additional Host ID and Anchor ID
1992 * and insert a metadata record before that frame */
1993 /*XXX: The user may have changed the comment to cleared! */
1994 if(rec
->block_was_modified
) {
1995 if (rec
->rec_header
.packet_header
.pkt_encap
== WTAP_ENCAP_ERF
) {
1996 /* XXX: What about ERF-in-pcapng with existing comment (that wasn't
1998 if(rec
->block_was_modified
) {
1999 memmove(&other_phdr
, pseudo_header
, sizeof(union wtap_pseudo_header
));
2000 if(!erf_write_anchor_meta_update_phdr(wdh
, dump_priv
, rec
, &other_phdr
, err
)) return false;
2001 pseudo_header
= &other_phdr
;
2004 /* Always write the comment if non-ERF */
2005 if(!erf_write_anchor_meta_update_phdr(wdh
, dump_priv
, rec
, &other_phdr
, err
)) return false;
2009 /* Make sure we always write out rlen, regardless of what happens */
2010 alignbytes
= wdh
->bytes_dumped
+ pseudo_header
->erf
.phdr
.rlen
;
2012 if(!erf_write_phdr(wdh
, WTAP_ENCAP_ERF
, pseudo_header
, err
)) return false;
2014 if(!wtap_dump_file_write(wdh
, pd
, rec
->rec_header
.packet_header
.caplen
- round_down
, err
)) return false;
2016 /*add the 4 byte CRC if necessary*/
2018 if(!wtap_dump_file_write(wdh
, &crc32
, 4, err
)) return false;
2021 /*XXX: In the case of ENCAP_ERF, this pads the record to its original length, which is fine in most
2022 * cases. However with >MAX_ERF_EHDR unnecessary padding will be added, and
2023 * if the record was truncated this will be incorrectly treated as payload.
2024 * More than 8 extension headers is unusual though, only the first 8 are
2025 * written out anyway and fixing properly would require major refactor.*/
2026 /*records should be 8byte aligned, so we add padding to our calculated rlen */
2027 while(wdh
->bytes_dumped
< alignbytes
){
2028 if(!wtap_dump_file_write(wdh
, "", 1, err
)) return false;
2031 dump_priv
->prev_erf_type
= pseudo_header
->erf
.phdr
.type
& 0x7FU
;
2032 dump_priv
->prev_frame_ts
= pseudo_header
->erf
.phdr
.ts
;
2037 static int erf_dump_can_write_encap(int encap
)
2040 if(encap
== WTAP_ENCAP_PER_PACKET
)
2043 if (wtap_wtap_encap_to_erf_encap(encap
) == -1)
2044 return WTAP_ERR_UNWRITABLE_ENCAP
;
2049 static bool erf_dump_open(wtap_dumper
*wdh
, int *err _U_
, char **err_info _U_
)
2051 erf_dump_t
*dump_priv
;
2054 char *first_shb_comment
= NULL
;
2056 dump_priv
= erf_dump_priv_create();
2058 wdh
->subtype_write
= erf_dump
;
2059 wdh
->priv
= dump_priv
;
2060 wdh
->subtype_finish
= erf_dump_finish
;
2062 /* Get the first capture comment string */
2063 get_user_comment_string(wdh
, &first_shb_comment
);
2064 /* Save a copy of it */
2065 dump_priv
->user_comment_ptr
= g_strdup(first_shb_comment
);
2066 /* XXX: If we have a capture comment or a non-ERF file assume we need to
2067 * write metadata unless we see existing metadata in the first second. */
2068 if (dump_priv
->user_comment_ptr
|| wdh
->file_encap
!= WTAP_ENCAP_ERF
)
2069 dump_priv
->write_next_extra_meta
= true;
2071 /* Read Host ID from environment variable */
2072 /* TODO: generate one from MAC address? */
2073 if ((s
= getenv("ERF_HOST_ID")) != NULL
) {
2074 /* TODO: support both decimal and hex strings (base 0)? */
2075 if (ws_hexstrtou64(s
, NULL
, &host_id
)) {
2076 dump_priv
->host_id
= host_id
& ERF_EHDR_HOST_ID_MASK
;
2083 static int erf_get_source_from_header(union wtap_pseudo_header
*pseudo_header
, uint64_t *host_id
, uint8_t *source_id
)
2089 bool host_id_found
= false;
2091 if (!pseudo_header
|| !host_id
|| !source_id
)
2094 *host_id
= ERF_META_HOST_ID_IMPLICIT
;
2097 has_more
= pseudo_header
->erf
.phdr
.type
& 0x80;
2099 while (has_more
&& (i
< MAX_ERF_EHDR
)) {
2100 hdr
= pseudo_header
->erf
.ehdr_list
[i
].ehdr
;
2101 type
= (uint8_t) (hdr
>> 56);
2104 * XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
2105 * erf_populate_interface)
2107 switch (type
& 0x7f) {
2108 case ERF_EXT_HDR_TYPE_HOST_ID
:
2110 *host_id
= hdr
& ERF_EHDR_HOST_ID_MASK
;
2112 host_id_found
= true;
2114 case ERF_EXT_HDR_TYPE_FLOW_ID
:
2115 if (*source_id
== 0)
2116 *source_id
= (hdr
>> 48) & 0xff;
2123 has_more
= type
& 0x80;
2130 int erf_populate_interface_from_header(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, int *err
, char **err_info
)
2139 if_num
= (pseudo_header
->erf
.phdr
.flags
& 0x03) | ((pseudo_header
->erf
.phdr
.flags
& 0x40)>>4);
2141 erf_get_source_from_header(pseudo_header
, &host_id
, &source_id
);
2143 return erf_populate_interface(erf_priv
, wth
, pseudo_header
, host_id
, source_id
, if_num
, err
, err_info
);
2146 static struct erf_if_mapping
* erf_find_interface_mapping(erf_t
*erf_priv
, uint64_t host_id
, uint8_t source_id
)
2148 struct erf_if_mapping if_map_lookup
;
2150 /* XXX: erf_priv should never be NULL here */
2154 if_map_lookup
.host_id
= host_id
;
2155 if_map_lookup
.source_id
= source_id
;
2157 return (struct erf_if_mapping
*) g_hash_table_lookup(erf_priv
->if_map
, &if_map_lookup
);
2160 static void erf_set_interface_descr(wtap_block_t block
, unsigned option_id
, uint64_t host_id
, uint8_t source_id
, uint8_t if_num
, const char *descr
)
2163 char sourceid_buf
[16];
2164 /* Host XXXXXXXXXXXX,*/
2165 char hostid_buf
[24];
2167 sourceid_buf
[0] = '\0';
2168 hostid_buf
[0] = '\0';
2170 /* Implicit Host ID defaults to 0 */
2171 if (host_id
== ERF_META_HOST_ID_IMPLICIT
) {
2176 snprintf(hostid_buf
, sizeof(hostid_buf
), " Host %012" PRIx64
",", host_id
);
2179 if (source_id
> 0) {
2180 snprintf(sourceid_buf
, sizeof(sourceid_buf
), " Source %u,", source_id
);
2184 wtap_block_set_string_option_value_format(block
, option_id
, "%s (ERF%s%s Interface %d)", descr
, hostid_buf
, sourceid_buf
, if_num
);
2186 wtap_block_set_string_option_value_format(block
, option_id
, "Port %c (ERF%s%s Interface %d)", 'A'+if_num
, hostid_buf
, sourceid_buf
, if_num
);
2190 static int erf_update_anchors_from_header(erf_t
*erf_priv
, wtap_rec
*rec
, union wtap_pseudo_header
*pseudo_header
, uint64_t host_id
, GPtrArray
*anchor_mappings_to_update
)
2195 uint64_t comment_gen_time
= 0;
2196 uint64_t host_id_current
;
2197 uint64_t anchor_id_current
= 0;
2199 char *comment
= NULL
;
2201 if (!rec
|| !pseudo_header
)
2204 /* Start with the first Host ID that was found on the record
2205 * as the Anchor ID isn't required to be the first extension header' */
2206 host_id_current
= host_id
== ERF_META_HOST_ID_IMPLICIT
? erf_priv
->implicit_host_id
: host_id
;
2208 has_more
= pseudo_header
->erf
.phdr
.type
& 0x80;
2210 while (has_more
&& (i
< MAX_ERF_EHDR
)) {
2211 hdr
= pseudo_header
->erf
.ehdr_list
[i
].ehdr
;
2212 type
= (uint8_t) (hdr
>> 56);
2214 switch (type
& 0x7f) {
2215 case ERF_EXT_HDR_TYPE_HOST_ID
:
2216 host_id_current
= hdr
& ERF_EHDR_HOST_ID_MASK
;
2219 case ERF_EXT_HDR_TYPE_ANCHOR_ID
:
2221 anchor_id_current
= hdr
& ERF_EHDR_ANCHOR_ID_MASK
;
2222 if (!(ERF_ANCHOR_ID_IS_DEFINITION(hdr
))) {
2224 * Anchor definition flag is 0, attempt to associate a comment with this record
2225 * XXX: currently the comment count may be wrong on the first pass!
2227 /* We may not have found the implicit Host ID yet, if so we are unlikely to find anything */
2228 struct erf_anchor_mapping
* lookup_result
;
2229 lookup_result
= erf_find_anchor_mapping(erf_priv
, host_id_current
, anchor_id_current
);
2230 if (lookup_result
) {
2231 if (lookup_result
->gen_time
> comment_gen_time
) {
2232 /* XXX: we might have a comment that clears the comment (i.e.
2234 if (lookup_result
->comment
&& lookup_result
->comment
[0] != '\0') {
2235 comment
= lookup_result
->comment
;
2237 comment_gen_time
= lookup_result
->gen_time
;
2242 if (anchor_mappings_to_update
&& (pseudo_header
->erf
.phdr
.type
& 0x7f) == ERF_TYPE_META
) {
2244 * Anchor definition flag is 1, put the mapping in an array
2245 * which we will later update when we walk through
2248 /* Only Provenance record can contain the information we need */
2249 struct erf_anchor_mapping
*mapping_ptr
=
2250 g_new0(struct erf_anchor_mapping
, 1);
2251 /* May be ERF_META_HOST_ID_IMPLICIT */
2252 mapping_ptr
->host_id
= host_id_current
;
2253 mapping_ptr
->anchor_id
= anchor_id_current
;
2254 g_ptr_array_add(anchor_mappings_to_update
, mapping_ptr
);
2261 has_more
= type
& 0x80;
2266 wtap_block_add_string_option(rec
->block
, OPT_COMMENT
, comment
, strlen(comment
));
2273 * @brief Update the implicit Host ID and Anchor Mapping information
2275 static int erf_update_implicit_host_id(erf_t
*erf_priv
, wtap
*wth
, uint64_t implicit_host_id
)
2277 GHashTableIter iter
;
2279 GList
* implicit_list
= NULL
;
2281 wtap_block_t int_data
;
2282 struct erf_if_mapping
* if_map
= NULL
;
2283 struct erf_if_mapping
* if_map_other
= NULL
;
2284 struct erf_if_info
* if_info
= NULL
;
2285 struct erf_anchor_mapping
* anchor_mapping
= NULL
;
2286 struct erf_anchor_mapping
* anchor_mapping_other
= NULL
;
2287 char *oldstr
= NULL
;
2288 char portstr_buf
[16];
2294 erf_priv
->implicit_host_id
= implicit_host_id
;
2297 * We need to update the descriptions of all the interfaces with no Host
2298 * ID to the correct Host ID.
2300 g_hash_table_iter_init(&iter
, erf_priv
->if_map
);
2302 /* Remove the implicit mappings from the mapping table */
2303 while (g_hash_table_iter_next(&iter
, &iter_value
, NULL
)) {
2304 if_map
= (struct erf_if_mapping
*) iter_value
;
2306 if (if_map
->host_id
== ERF_META_HOST_ID_IMPLICIT
) {
2307 /* Check we don't have an existing interface that matches */
2308 if_map_other
= erf_find_interface_mapping(erf_priv
, implicit_host_id
, if_map
->source_id
);
2310 if (!if_map_other
) {
2311 /* Pull mapping for update */
2312 /* XXX: Can't add while iterating hash table so use list instead */
2313 g_hash_table_iter_steal(&iter
);
2314 implicit_list
= g_list_prepend(implicit_list
, if_map
);
2317 * XXX: We have duplicate interfaces in this case, but not much else we
2318 * can do since we have already dissected the earlier packets. Expected
2319 * to be unusual as it reqires a mix of explicit and implicit Host ID
2320 * (e.g. FlowID extension header only) packets with the same effective
2321 * Host ID before the first ERF_TYPE_META record.
2325 * Update the description of the ERF_META_HOST_ID_IMPLICIT interface(s)
2326 * for the first records in one pass mode. In 2 pass mode (Wireshark
2327 * initial open, TShark in 2 pass mode) we will update the interface
2328 * mapping for the frames on the second pass. Relatively consistent
2329 * with the dissector behaviour.
2331 * TODO: Can we delete this interface on the second (or even first)
2332 * pass? Should we try to merge in other metadata?
2333 * Needs a wtap_block_copy() that supports overwriting and/or expose
2334 * custom option copy and do with wtap_block_foreach_option().
2336 for (i
= 0; i
< ERF_MAX_INTERFACES
; i
++) {
2337 if_info
= &if_map
->interfaces
[i
];
2339 if (if_info
->if_index
>= 0) {
2340 /* XXX: this is a pointer! */
2341 int_data
= g_array_index(wth
->interface_data
, wtap_block_t
, if_info
->if_index
);
2343 snprintf(portstr_buf
, sizeof(portstr_buf
), "Port %c", 'A'+i
);
2345 oldstr
= if_info
->name
;
2346 if_info
->name
= g_strconcat(oldstr
? oldstr
: portstr_buf
, " [unmatched implicit]", NULL
);
2347 g_free(oldstr
); /* probably null, but g_free doesn't care */
2349 oldstr
= if_info
->descr
;
2350 if_info
->descr
= g_strconcat(oldstr
? oldstr
: portstr_buf
, " [unmatched implicit]", NULL
);
2353 erf_set_interface_descr(int_data
, OPT_IDB_NAME
, implicit_host_id
, if_map
->source_id
, (uint8_t) i
, if_info
->name
);
2354 erf_set_interface_descr(int_data
, OPT_IDB_DESCRIPTION
, implicit_host_id
, if_map
->source_id
, (uint8_t) i
, if_info
->descr
);
2361 /* Re-add the non-clashing items under the real implicit Host ID */
2362 if (implicit_list
) {
2363 item
= implicit_list
;
2365 if_map
= (struct erf_if_mapping
*) item
->data
;
2367 for (i
= 0; i
< ERF_MAX_INTERFACES
; i
++) {
2368 if_info
= &if_map
->interfaces
[i
];
2370 if (if_info
->if_index
>= 0) {
2371 /* XXX: this is a pointer! */
2372 int_data
= g_array_index(wth
->interface_data
, wtap_block_t
, if_info
->if_index
);
2373 erf_set_interface_descr(int_data
, OPT_IDB_NAME
, implicit_host_id
, if_map
->source_id
, (uint8_t) i
, if_info
->name
);
2374 erf_set_interface_descr(int_data
, OPT_IDB_DESCRIPTION
, implicit_host_id
, if_map
->source_id
, (uint8_t) i
, if_info
->descr
);
2378 if_map
->host_id
= implicit_host_id
;
2379 /* g_hash_table_add() only exists since 2.32. */
2380 g_hash_table_replace(erf_priv
->if_map
, if_map
, if_map
);
2381 } while ((item
= g_list_next(item
)));
2383 g_list_free(implicit_list
);
2384 implicit_list
= NULL
;
2388 * We also need to update the anchor comment mappings
2389 * to the correct Host ID.
2391 g_hash_table_iter_init(&iter
, erf_priv
->anchor_map
);
2393 /* Remove the implicit mappings from the mapping table */
2394 while (g_hash_table_iter_next(&iter
, &iter_value
, NULL
)) {
2395 anchor_mapping
= (struct erf_anchor_mapping
*) iter_value
;
2397 if (anchor_mapping
->host_id
== ERF_META_HOST_ID_IMPLICIT
) {
2398 /* Check we don't have an existing anchor that matches */
2399 anchor_mapping_other
= erf_find_anchor_mapping(erf_priv
, implicit_host_id
,
2400 anchor_mapping
->anchor_id
);
2402 if (anchor_mapping_other
&& anchor_mapping_other
->gen_time
>= anchor_mapping
->gen_time
) {
2404 * XXX: Duplicate entry of anchor mapping, keep the one with newer
2407 g_hash_table_iter_remove(&iter
);
2409 /* Pull mapping for update */
2410 /* XXX: Can't add while iterating hash table so use list instead */
2411 g_hash_table_iter_steal(&iter
);
2412 implicit_list
= g_list_prepend(implicit_list
, anchor_mapping
);
2413 /* existing entry (if any) will be removed by g_hash_table_replace */
2418 /* Re-add the non-clashing items under the real implicit Host ID */
2419 if (implicit_list
) {
2420 item
= implicit_list
;
2422 anchor_mapping
= (struct erf_anchor_mapping
*) item
->data
;
2423 anchor_mapping
->host_id
= implicit_host_id
;
2424 g_hash_table_replace(erf_priv
->anchor_map
, anchor_mapping
, anchor_mapping
);
2425 } while ((item
= g_list_next(item
)));
2427 g_list_free(implicit_list
);
2428 implicit_list
= NULL
;
2434 static int erf_populate_interface(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, uint64_t host_id
, uint8_t source_id
, uint8_t if_num
, int *err
, char **err_info
)
2436 wtap_block_t int_data
;
2437 wtapng_if_descr_mandatory_t
* int_data_mand
;
2438 struct erf_if_mapping
* if_map
= NULL
;
2441 *err
= WTAP_ERR_INTERNAL
;
2442 *err_info
= ws_strdup_printf("erf: erf_populate_interface called with wth NULL");
2445 if (!pseudo_header
) {
2446 *err
= WTAP_ERR_INTERNAL
;
2447 *err_info
= ws_strdup_printf("erf: erf_populate_interface called with pseudo_header NULL");
2451 *err
= WTAP_ERR_INTERNAL
;
2452 *err_info
= ws_strdup_printf("erf: erf_populate_interface called with erf_priv NULL");
2455 if (if_num
> ERF_MAX_INTERFACES
-1) {
2456 *err
= WTAP_ERR_INTERNAL
;
2457 *err_info
= ws_strdup_printf("erf: erf_populate_interface called with if_num %u > %u",
2458 if_num
, ERF_MAX_INTERFACES
-1);
2462 if (host_id
== ERF_META_HOST_ID_IMPLICIT
) {
2463 /* Defaults to ERF_META_HOST_ID_IMPLICIT so we can update mapping later */
2464 host_id
= erf_priv
->implicit_host_id
;
2465 } else if ((pseudo_header
->erf
.phdr
.type
& 0x7f) == ERF_TYPE_META
) {
2467 * XXX: We assume there is only one Implicit Host ID. As a special case a first
2468 * Host ID extension header with Source ID 0 on a record does not change
2469 * the implicit Host ID. We respect this even though we support only one
2472 if (erf_priv
->implicit_host_id
== ERF_META_HOST_ID_IMPLICIT
&& source_id
> 0) {
2473 erf_update_implicit_host_id(erf_priv
, wth
, host_id
);
2477 if_map
= erf_find_interface_mapping(erf_priv
, host_id
, source_id
);
2480 if_map
= erf_if_mapping_create(host_id
, source_id
);
2481 /* g_hash_table_add() only exists since 2.32. */
2482 g_hash_table_replace(erf_priv
->if_map
, if_map
, if_map
);
2486 /* Return the existing interface if we have it */
2487 if (if_map
->interfaces
[if_num
].if_index
>= 0) {
2488 return if_map
->interfaces
[if_num
].if_index
;
2491 int_data
= wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO
);
2492 int_data_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(int_data
);
2494 int_data_mand
->wtap_encap
= WTAP_ENCAP_ERF
;
2495 /* int_data.time_units_per_second = (1LL<<32); ERF format resolution is 2^-32, capture resolution is unknown */
2496 int_data_mand
->time_units_per_second
= 1000000000; /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
2497 int_data_mand
->tsprecision
= WTAP_TSPREC_NSEC
;
2498 int_data_mand
->snap_len
= 65535; /* ERF max length */
2500 /* XXX: if_IPv4addr opt 4 Interface network address and netmask.*/
2501 /* XXX: if_IPv6addr opt 5 Interface network address and prefix length (stored in the last byte).*/
2502 /* XXX: if_MACaddr opt 6 Interface Hardware MAC address (48 bits).*/
2503 /* XXX: if_EUIaddr opt 7 Interface Hardware EUI address (64 bits)*/
2504 /* XXX: if_speed opt 8 Interface speed (in bits per second)*/
2505 /* int_data.if_tsresol = 0xa0; ERF format resolution is 2^-32 = 0xa0, capture resolution is unknown */
2506 wtap_block_add_uint8_option(int_data
, OPT_IDB_TSRESOL
, 0x09); /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
2507 /* XXX: if_tzone 10 Time zone for GMT support (TODO: specify better). */
2508 /* XXX if_tsoffset; opt 14 A 64 bits integer value that specifies an offset (in seconds)...*/
2509 /* Interface statistics */
2510 int_data_mand
->num_stat_entries
= 0;
2511 int_data_mand
->interface_statistics
= NULL
;
2513 erf_set_interface_descr(int_data
, OPT_IDB_NAME
, host_id
, source_id
, if_num
, NULL
);
2514 erf_set_interface_descr(int_data
, OPT_IDB_DESCRIPTION
, host_id
, source_id
, if_num
, NULL
);
2516 if_map
->interfaces
[if_num
].if_index
= (int) wth
->interface_data
->len
;
2517 wtap_add_idb(wth
, int_data
);
2519 return if_map
->interfaces
[if_num
].if_index
;
2522 static uint32_t erf_meta_read_tag(struct erf_meta_tag
* tag
, uint8_t *tag_ptr
, uint32_t remaining_len
)
2526 uint32_t tagtotallength
;
2528 if (!tag_ptr
|| !tag
|| remaining_len
< ERF_META_TAG_HEADERLEN
)
2531 /* tagtype (2 bytes) */
2532 tagtype
= pntoh16(&tag_ptr
[0]);
2534 /* length (2 bytes) */
2535 taglength
= pntoh16(&tag_ptr
[2]);
2537 tagtotallength
= ERF_META_TAG_TOTAL_ALIGNED_LENGTH(taglength
);
2539 if (remaining_len
< tagtotallength
) {
2543 tag
->type
= tagtype
;
2544 tag
->length
= taglength
;
2545 tag
->value
= &tag_ptr
[4];
2547 return tagtotallength
;
2550 static int populate_capture_host_info(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header _U_
, struct erf_meta_read_state
*state
, int *err
, char **err_info
)
2552 struct erf_meta_tag tag
= {0, 0, NULL
};
2554 wtap_block_t shb_hdr
;
2556 char* app_name
= NULL
;
2557 char* app_version
= NULL
;
2561 char* modelcpu
= NULL
;
2562 uint32_t tagtotallength
;
2565 *err
= WTAP_ERR_INTERNAL
;
2566 *err_info
= ws_strdup_printf("erf: populate_capture_host_info called with wth NULL");
2570 *err
= WTAP_ERR_INTERNAL
;
2571 *err_info
= ws_strdup_printf("erf: populate_capture_host_info called with state NULL");
2574 if (!wth
->shb_hdrs
) {
2575 *err
= WTAP_ERR_INTERNAL
;
2576 *err_info
= ws_strdup_printf("erf: populate_capture_host_info called with wth->shb_hdrs NULL");
2579 if (wth
->shb_hdrs
->len
== 0) {
2580 *err
= WTAP_ERR_INTERNAL
;
2581 *err_info
= ws_strdup_printf("erf: populate_capture_host_info called with wth->shb_hdrs->len 0");
2585 /* XXX: wth->shb_hdr is already created by different layer, using directly for now. */
2586 /* XXX: Only one section header is supported at this time */
2587 shb_hdr
= g_array_index(wth
->shb_hdrs
, wtap_block_t
, 0);
2589 while ((tagtotallength
= erf_meta_read_tag(&tag
, state
->tag_ptr
, state
->remaining_len
)) && !ERF_META_IS_SECTION(tag
.type
)) {
2590 switch (state
->sectiontype
) {
2591 case ERF_META_SECTION_CAPTURE
:
2593 if (erf_priv
->capture_gentime
> state
->gen_time
) {
2598 case ERF_META_TAG_comment
:
2600 char *existing_comment
= NULL
;
2601 /*XXX: hack to make changing capture comment work since Wireshark only
2602 * displays one. For now just overwrite the comment as we won't
2603 * pick up all of them yet due to the gen_time check above */
2604 if (wtap_block_get_nth_string_option_value(shb_hdr
, OPT_COMMENT
, 0, &existing_comment
) == WTAP_OPTTYPE_SUCCESS
) {
2605 wtap_block_set_nth_string_option_value(shb_hdr
, OPT_COMMENT
, 0, tag
.value
, tag
.length
);
2607 wtap_block_add_string_option(shb_hdr
, OPT_COMMENT
, tag
.value
, tag
.length
);
2614 case ERF_META_SECTION_HOST
:
2616 if (erf_priv
->host_gentime
> state
->gen_time
) {
2621 case ERF_META_TAG_model
:
2623 model
= g_strndup((char*) tag
.value
, tag
.length
);
2625 case ERF_META_TAG_cpu
:
2627 cpu
= g_strndup((char*) tag
.value
, tag
.length
);
2629 case ERF_META_TAG_descr
:
2631 descr
= g_strndup((char*) tag
.value
, tag
.length
);
2633 case ERF_META_TAG_os
:
2634 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_OS
, tag
.value
, tag
.length
);
2636 case ERF_META_TAG_app_name
:
2638 app_name
= g_strndup((char*) tag
.value
, tag
.length
);
2640 case ERF_META_TAG_app_version
:
2641 g_free(app_version
);
2642 app_version
= g_strndup((char*) tag
.value
, tag
.length
);
2644 /* TODO: dag_version? */
2645 /* TODO: could concatenate comment(s)? */
2646 case ERF_META_TAG_filter
:
2647 g_free(state
->if_map
->capture_filter_str
);
2648 state
->if_map
->capture_filter_str
= g_strndup((char*) tag
.value
, tag
.length
);
2657 state
->tag_ptr
+= tagtotallength
;
2658 state
->remaining_len
-= tagtotallength
;
2661 /* Post processing */
2663 if (app_name
|| app_version
) {
2665 * If we have no app_name, we use "(Unknown applicaton)".
2667 * If we have no app_version, this will just use app_name.
2669 tmp
= g_strjoin(" ", app_name
? app_name
: "(Unknown application)", app_version
, NULL
);
2670 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_USERAPPL
, tmp
, strlen(tmp
));
2674 g_free(app_version
);
2679 /* For the hardware field show description followed by (model; cpu) */
2680 /* Build "Model; CPU" part */
2682 /* g_strjoin() would be nice to use here if the API didn't stop on the first NULL... */
2684 modelcpu
= g_strconcat(model
, "; ", cpu
, NULL
);
2687 /* avoid double-free */
2691 /* avoid double-free */
2696 /* Combine into "Description (Model; CPU)" */
2697 if (state
->sectiontype
== ERF_META_SECTION_HOST
&& descr
) {
2699 wtap_block_set_string_option_value_format(shb_hdr
, OPT_SHB_HARDWARE
, "%s (%s)", descr
, modelcpu
);
2701 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_HARDWARE
, descr
, strlen(descr
));
2704 } else if (modelcpu
) {
2705 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_HARDWARE
, modelcpu
, strlen(modelcpu
));
2706 /*modelcpu = NULL;*/
2709 /* Free the fields we didn't end up using */
2715 if (state
->sectiontype
== ERF_META_SECTION_CAPTURE
) {
2716 erf_priv
->capture_gentime
= state
->gen_time
;
2718 erf_priv
->host_gentime
= state
->gen_time
;
2724 static int populate_module_info(erf_t
*erf_priv _U_
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header _U_
, struct erf_meta_read_state
*state
, int *err
, char **err_info
)
2726 struct erf_meta_tag tag
= {0, 0, NULL
};
2728 uint32_t tagtotallength
;
2731 *err
= WTAP_ERR_INTERNAL
;
2732 *err_info
= ws_strdup_printf("erf: populate_module_info called with wth NULL");
2736 *err
= WTAP_ERR_INTERNAL
;
2737 *err_info
= ws_strdup_printf("erf: populate_module_info called with stat NULL");
2741 if (state
->if_map
->module_gentime
> state
->gen_time
) {
2745 while ((tagtotallength
= erf_meta_read_tag(&tag
, state
->tag_ptr
, state
->remaining_len
)) && !ERF_META_IS_SECTION(tag
.type
)) {
2747 case ERF_META_TAG_fcs_len
:
2748 if (tag
.length
>= 4) {
2749 state
->if_map
->module_fcs_len
= (int8_t) pntoh32(tag
.value
);
2752 case ERF_META_TAG_snaplen
:
2753 /* XXX: this is generally per stream */
2754 if (tag
.length
>= 4) {
2755 state
->if_map
->module_snaplen
= pntoh32(tag
.value
);
2758 case ERF_META_TAG_filter
:
2759 g_free(state
->if_map
->module_filter_str
);
2760 state
->if_map
->module_filter_str
= g_strndup((char*) tag
.value
, tag
.length
);
2764 state
->tag_ptr
+= tagtotallength
;
2765 state
->remaining_len
-= tagtotallength
;
2768 state
->if_map
->module_gentime
= state
->gen_time
;
2773 static int populate_interface_info(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, struct erf_meta_read_state
*state
, int *err
, char **err_info
)
2775 struct erf_meta_tag tag
= {0, 0, NULL
};
2776 uint32_t tagtotallength
;
2777 int interface_index
= -1;
2778 wtap_block_t int_data
= NULL
;
2779 wtapng_if_descr_mandatory_t
* int_data_mand
= NULL
;
2780 if_filter_opt_t if_filter
;
2781 uint32_t if_num
= 0;
2782 struct erf_if_info
* if_info
= NULL
;
2785 *err
= WTAP_ERR_INTERNAL
;
2786 *err_info
= ws_strdup_printf("erf: populate_interface_info called with wth NULL");
2790 *err
= WTAP_ERR_INTERNAL
;
2791 *err_info
= ws_strdup_printf("erf: populate_interface_info called with state NULL");
2794 if (!pseudo_header
) {
2795 *err
= WTAP_ERR_INTERNAL
;
2796 *err_info
= ws_strdup_printf("erf: populate_interface_info called with pseudo_header NULL");
2799 if (!state
->if_map
) {
2800 *err
= WTAP_ERR_INTERNAL
;
2801 *err_info
= ws_strdup_printf("erf: populate_interface_info called with state->if_map NULL");
2805 /* Section ID of interface is defined to match ERF interface id. */
2806 if_num
= state
->sectionid
- 1;
2808 * Get or create the interface (there can be multiple interfaces in
2809 * a Provenance record).
2811 if (if_num
< ERF_MAX_INTERFACES
) { /* Note: -1u > ERF_MAX_INTERFACES */
2812 if_info
= &state
->if_map
->interfaces
[if_num
];
2813 interface_index
= if_info
->if_index
;
2815 /* Check if the interface information is still uninitialized */
2816 if (interface_index
== -1) {
2817 uint8_t *tag_ptr_tmp
= state
->tag_ptr
;
2818 uint32_t remaining_len_tmp
= state
->remaining_len
;
2820 /* First iterate tags, checking we aren't looking at a timing port */
2822 * XXX: we deliberately only do this logic here rather than the per-packet
2823 * population function so that if somehow we do see packets for an
2824 * 'invalid' port the interface will be created at that time.
2826 while ((tagtotallength
= erf_meta_read_tag(&tag
, tag_ptr_tmp
, remaining_len_tmp
)) && !ERF_META_IS_SECTION(tag
.type
)) {
2827 if (tag
.type
== ERF_META_TAG_if_port_type
) {
2828 if (tag
.length
>= 4 && pntoh32(tag
.value
) == 2) {
2829 /* This is a timing port, skip it from now on */
2830 /* XXX: should we skip all non-capture ports instead? */
2832 if_info
->if_index
= -2;
2833 interface_index
= -2;
2835 } else if (tag
.type
== ERF_META_TAG_stream_num
) {
2836 if (tag
.length
>= 4) {
2837 if_info
->stream_num
= (int32_t) pntoh32(tag
.value
);
2841 tag_ptr_tmp
+= tagtotallength
;
2842 remaining_len_tmp
-= tagtotallength
;
2845 /* If the interface is valid but uninitialized, create it */
2846 if (interface_index
== -1) {
2847 interface_index
= erf_populate_interface(erf_priv
, wth
, pseudo_header
, state
->if_map
->host_id
, state
->if_map
->source_id
, (uint8_t) if_num
, err
, err_info
);
2848 if (interface_index
== -1) {
2854 /* Get the wiretap interface metadata */
2855 if (interface_index
>= 0) {
2856 int_data
= g_array_index(wth
->interface_data
, wtap_block_t
, interface_index
);
2857 int_data_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(int_data
);
2858 } else if (interface_index
== -2) {
2859 /* timing/unknown port */
2862 *err
= WTAP_ERR_INTERNAL
;
2863 *err_info
= ws_strdup_printf("erf: populate_interface_info got interface_index %d < 0 and != -2", interface_index
);
2869 * Bail if already have interface metadata or no interface to associate with.
2870 * We also don't support metadata for >ERF_MAX_INTERFACES interfaces per Host + Source
2871 * as we only use interface ID.
2876 if (state
->if_map
->interface_gentime
> state
->gen_time
&& state
->if_map
->interface_metadata
& (1 << if_num
))
2879 while ((tagtotallength
= erf_meta_read_tag(&tag
, state
->tag_ptr
, state
->remaining_len
)) && !ERF_META_IS_SECTION(tag
.type
)) {
2881 case ERF_META_TAG_name
:
2882 /* TODO: fall back to module "dev_name Port N"? */
2883 if (!if_info
->name
) {
2884 if_info
->name
= g_strndup((char*) tag
.value
, tag
.length
);
2885 erf_set_interface_descr(int_data
, OPT_IDB_NAME
, state
->if_map
->host_id
, state
->if_map
->source_id
, (uint8_t) if_num
, if_info
->name
);
2887 /* If we have no description, also copy to wtap if_description */
2888 if (!if_info
->descr
) {
2889 erf_set_interface_descr(int_data
, OPT_IDB_DESCRIPTION
, state
->if_map
->host_id
, state
->if_map
->source_id
, (uint8_t) if_num
, if_info
->name
);
2893 case ERF_META_TAG_descr
:
2894 if (!if_info
->descr
) {
2895 if_info
->descr
= g_strndup((char*) tag
.value
, tag
.length
);
2896 erf_set_interface_descr(int_data
, OPT_IDB_DESCRIPTION
, state
->if_map
->host_id
, state
->if_map
->source_id
, (uint8_t) if_num
, if_info
->descr
);
2898 /* If we have no name, also copy to wtap if_name */
2899 if (!if_info
->name
) {
2900 erf_set_interface_descr(int_data
, OPT_IDB_NAME
, state
->if_map
->host_id
, state
->if_map
->source_id
, (uint8_t) if_num
, if_info
->descr
);
2904 case ERF_META_TAG_if_speed
:
2905 if (tag
.length
>= 8)
2906 wtap_block_add_uint64_option(int_data
, OPT_IDB_SPEED
, pntoh64(tag
.value
));
2908 case ERF_META_TAG_if_num
:
2910 * XXX: We ignore this as Section ID must match the ERF ifid and
2911 * that is all we care about/have space for at the moment. if_num
2912 * is only really useful with >ERF_MAX_INTERFACES interfaces.
2914 /* TODO: might want to put this number in description */
2916 case ERF_META_TAG_fcs_len
:
2917 if (tag
.length
>= 4) {
2918 wtap_block_add_uint8_option(int_data
, OPT_IDB_FCSLEN
, (uint8_t) pntoh32(tag
.value
));
2919 if_info
->set_flags
.fcs_len
= 1;
2922 case ERF_META_TAG_snaplen
:
2923 /* XXX: this generally per stream */
2924 if (tag
.length
>= 4) {
2925 int_data_mand
->snap_len
= pntoh32(tag
.value
);
2926 if_info
->set_flags
.snaplen
= 1;
2929 case ERF_META_TAG_comment
:
2930 wtap_block_add_string_option(int_data
, OPT_COMMENT
, tag
.value
, tag
.length
);
2932 case ERF_META_TAG_filter
:
2933 if_filter
.type
= if_filter_pcap
;
2934 if_filter
.data
.filter_str
= g_strndup((char*) tag
.value
, tag
.length
);
2935 wtap_block_add_if_filter_option(int_data
, OPT_IDB_FILTER
, &if_filter
);
2936 g_free(if_filter
.data
.filter_str
);
2937 if_info
->set_flags
.filter
= 1;
2943 state
->tag_ptr
+= tagtotallength
;
2944 state
->remaining_len
-= tagtotallength
;
2947 /* Post processing */
2949 * XXX: Assumes module defined first. It is higher in hierarchy so only set
2954 * XXX: Missing exposed existence/type-check. No way currently to check if
2955 * been set in the optionblock.
2957 if (!if_info
->set_flags
.filter
) {
2958 if (state
->if_map
->module_filter_str
) {
2959 /* Duplicate because might use with multiple interfaces */
2960 if_filter
.type
= if_filter_pcap
;
2961 if_filter
.data
.filter_str
= state
->if_map
->module_filter_str
;
2962 wtap_block_add_if_filter_option(int_data
, OPT_IDB_FILTER
, &if_filter
);
2964 * Don't set flag because stream is more specific than module.
2966 } else if (state
->if_map
->capture_filter_str
) {
2967 /* TODO: display separately? Note that we could have multiple captures
2968 * from multiple hosts in the file */
2969 if_filter
.type
= if_filter_pcap
;
2970 if_filter
.data
.filter_str
= state
->if_map
->capture_filter_str
;
2971 wtap_block_add_if_filter_option(int_data
, OPT_IDB_FILTER
, &if_filter
);
2975 if (state
->if_map
->module_fcs_len
!= -1 && !if_info
->set_flags
.fcs_len
) {
2976 wtap_block_add_uint8_option(int_data
, OPT_IDB_FCSLEN
, (uint8_t) state
->if_map
->module_fcs_len
);
2977 if_info
->set_flags
.fcs_len
= 1;
2980 if (state
->if_map
->module_snaplen
!= (uint32_t) -1 && !if_info
->set_flags
.snaplen
) {
2981 int_data_mand
->snap_len
= pntoh32(tag
.value
);
2982 if_info
->set_flags
.snaplen
= 1;
2985 state
->interface_metadata
|= 1 << if_num
;
2990 static int populate_stream_info(erf_t
*erf_priv _U_
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, struct erf_meta_read_state
*state
, int *err
, char **err_info
)
2992 struct erf_meta_tag tag
= {0, 0, NULL
};
2993 uint32_t tagtotallength
;
2994 int interface_index
= -1;
2995 wtap_block_t int_data
= NULL
;
2996 wtapng_if_descr_mandatory_t
* int_data_mand
= NULL
;
2997 if_filter_opt_t if_filter
;
2998 uint32_t if_num
= 0;
2999 int32_t stream_num
= -1;
3000 uint8_t *tag_ptr_tmp
;
3001 uint32_t remaining_len_tmp
;
3002 struct erf_if_info
* if_info
= NULL
;
3005 *err
= WTAP_ERR_INTERNAL
;
3006 *err_info
= ws_strdup_printf("erf: populate_stream_info called with wth NULL");
3009 if (!pseudo_header
) {
3010 *err
= WTAP_ERR_INTERNAL
;
3011 *err_info
= ws_strdup_printf("erf: populate_stream_info called with pseudo_header NULL");
3015 *err
= WTAP_ERR_INTERNAL
;
3016 *err_info
= ws_strdup_printf("erf: populate_stream_info called with state NULL");
3019 if (!state
->if_map
) {
3020 *err
= WTAP_ERR_INTERNAL
;
3021 *err_info
= ws_strdup_printf("erf: populate_stream_info called with state->if_map NULL");
3025 tag_ptr_tmp
= state
->tag_ptr
;
3026 remaining_len_tmp
= state
->remaining_len
;
3029 * XXX: We ignore parent section ID because it doesn't represent the
3030 * many-to-many relationship of interfaces and streams very well. The stream is
3031 * associated with all interfaces in the record that don't have a stream_num
3032 * that says otherwise.
3035 if (state
->sectionid
> 0 && state
->sectionid
!= 0x7fff) {
3036 /* Section ID of stream is supposed to match stream_num. */
3037 stream_num
= state
->sectionid
- 1;
3039 /* First iterate tags, looking for the stream number interfaces might associate with. */
3040 while ((tagtotallength
= erf_meta_read_tag(&tag
, tag_ptr_tmp
, remaining_len_tmp
)) && !ERF_META_IS_SECTION(tag
.type
)) {
3041 if (tag
.type
== ERF_META_TAG_stream_num
) {
3042 if (tag
.length
>= 4) {
3043 stream_num
= (int32_t) pntoh32(tag
.value
);
3047 tag_ptr_tmp
+= tagtotallength
;
3048 remaining_len_tmp
-= tagtotallength
;
3051 /* Otherwise assume the stream applies to all interfaces in the record */
3053 for (if_num
= 0; if_num
< ERF_MAX_INTERFACES
; if_num
++) {
3054 tag_ptr_tmp
= state
->tag_ptr
;
3055 remaining_len_tmp
= state
->remaining_len
;
3056 if_info
= &state
->if_map
->interfaces
[if_num
];
3058 /* Check if we should be handling this interface */
3059 /* XXX: currently skips interfaces that are not in the record. */
3060 if (state
->if_map
->interface_metadata
& (1 << if_num
)
3061 || !(state
->interface_metadata
& (1 << if_num
))) {
3065 if (if_info
->stream_num
!= -1
3066 && if_info
->stream_num
!= stream_num
) {
3070 interface_index
= if_info
->if_index
;
3071 /* Get the wiretap interface metadata */
3072 if (interface_index
>= 0) {
3073 int_data
= g_array_index(wth
->interface_data
, wtap_block_t
, interface_index
);
3074 int_data_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(int_data
);
3081 while ((tagtotallength
= erf_meta_read_tag(&tag
, tag_ptr_tmp
, remaining_len_tmp
)) && !ERF_META_IS_SECTION(tag
.type
)) {
3083 case ERF_META_TAG_fcs_len
:
3084 if (tag
.length
>= 4) {
3085 /* Use the largest fcslen of matching streams */
3086 int8_t fcs_len
= (int8_t) pntoh32(tag
.value
);
3087 uint8_t old_fcs_len
= 0;
3089 switch (wtap_block_get_uint8_option_value(int_data
, OPT_IDB_FCSLEN
, &old_fcs_len
)) {
3091 case WTAP_OPTTYPE_SUCCESS
:
3092 /* We already have an FCS length option; update it. */
3093 if (fcs_len
> old_fcs_len
|| !if_info
->set_flags
.fcs_len
) {
3094 wtap_block_set_uint8_option_value(int_data
, OPT_IDB_FCSLEN
, (uint8_t) pntoh32(tag
.value
));
3095 if_info
->set_flags
.fcs_len
= 1;
3099 case WTAP_OPTTYPE_NOT_FOUND
:
3100 /* We don't have an FCS length option; add it. */
3101 wtap_block_add_uint8_option(int_data
, OPT_IDB_FCSLEN
, (uint8_t) pntoh32(tag
.value
));
3102 if_info
->set_flags
.fcs_len
= 1;
3106 /* "shouldn't happen" */
3111 case ERF_META_TAG_snaplen
:
3112 if (tag
.length
>= 4) {
3113 /* Use the largest snaplen of matching streams */
3114 uint32_t snaplen
= pntoh32(tag
.value
);
3116 if (snaplen
> int_data_mand
->snap_len
|| !if_info
->set_flags
.snaplen
) {
3117 int_data_mand
->snap_len
= pntoh32(tag
.value
);
3118 if_info
->set_flags
.snaplen
= 1;
3122 case ERF_META_TAG_filter
:
3123 /* Override only if not set */
3124 if (!if_info
->set_flags
.filter
) {
3125 if_filter
.type
= if_filter_pcap
;
3126 if_filter
.data
.filter_str
= g_strndup((char*) tag
.value
, tag
.length
);
3127 wtap_block_add_if_filter_option(int_data
, OPT_IDB_FILTER
, &if_filter
);
3128 g_free(if_filter
.data
.filter_str
);
3129 if_info
->set_flags
.filter
= 1;
3136 tag_ptr_tmp
+= tagtotallength
;
3137 remaining_len_tmp
-= tagtotallength
;
3140 state
->tag_ptr
= tag_ptr_tmp
;
3141 state
->remaining_len
= remaining_len_tmp
;
3146 static int populate_anchor_info(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, struct erf_meta_read_state
*state
, GPtrArray
*anchor_mappings_to_update
, int *err
, char **err_info
) {
3147 struct erf_meta_tag tag
= {0, 0, NULL
};
3148 uint32_t tagtotallength
;
3149 char *comment_ptr
= NULL
;
3153 *err
= WTAP_ERR_INTERNAL
;
3154 *err_info
= ws_strdup_printf("erf: populate_anchor_info called with wth NULL");
3158 *err
= WTAP_ERR_INTERNAL
;
3159 *err_info
= ws_strdup_printf("erf: populate_anchor_info called with state NULL");
3162 if (!pseudo_header
) {
3163 *err
= WTAP_ERR_INTERNAL
;
3164 *err_info
= ws_strdup_printf("erf: populate_anchor_info called with pseudo_header NULL");
3168 if (!anchor_mappings_to_update
|| anchor_mappings_to_update
->len
== 0)
3171 while ((tagtotallength
= erf_meta_read_tag(&tag
, state
->tag_ptr
, state
->remaining_len
)) && !ERF_META_IS_SECTION(tag
.type
)) {
3172 /* XXX:Always gets the first comment tag in the section */
3174 case ERF_META_TAG_comment
:
3176 comment_ptr
= g_strndup((char*)tag
.value
, tag
.length
);
3183 state
->tag_ptr
+= tagtotallength
;
3184 state
->remaining_len
-= tagtotallength
;
3188 for(i
= 0; i
< anchor_mappings_to_update
->len
; i
++) {
3189 struct erf_anchor_mapping
*mapping
;
3190 struct erf_anchor_mapping
*lookup_result
;
3192 mapping
= (struct erf_anchor_mapping
*)g_ptr_array_index(anchor_mappings_to_update
, i
);
3193 lookup_result
= (struct erf_anchor_mapping
*)g_hash_table_lookup(erf_priv
->anchor_map
, mapping
);
3195 /* Use the most recent comment, across all anchors associated with the
3198 if(lookup_result
->gen_time
< state
->gen_time
) {
3199 lookup_result
->gen_time
= state
->gen_time
;
3200 g_free(lookup_result
->comment
);
3201 lookup_result
->comment
= g_strdup(comment_ptr
);
3205 /* !lookup_result */
3206 struct erf_anchor_mapping
*new_mapping
;
3207 new_mapping
= g_new0(struct erf_anchor_mapping
, 1);
3208 new_mapping
->anchor_id
= mapping
->anchor_id
;
3209 new_mapping
->host_id
= mapping
->host_id
;
3210 new_mapping
->gen_time
= state
->gen_time
;
3211 new_mapping
->comment
= g_strdup(comment_ptr
);
3212 g_hash_table_replace(erf_priv
->anchor_map
, new_mapping
, new_mapping
);
3217 g_free(comment_ptr
);
3222 /* Populates the capture and interface information for display on the Capture File Properties */
3223 static int populate_summary_info(erf_t
*erf_priv
, wtap
*wth
, union wtap_pseudo_header
*pseudo_header
, Buffer
*buf
, uint32_t packet_size
, GPtrArray
*anchor_mappings_to_update
, int *err
, char **err_info
)
3225 struct erf_meta_read_state state
= {0};
3226 struct erf_meta_read_state
*state_post
= NULL
;
3229 GList
*post_list
= NULL
;
3232 struct erf_meta_tag tag
= {0, 0, NULL
};
3233 uint32_t tagtotallength
;
3236 *err
= WTAP_ERR_INTERNAL
;
3237 *err_info
= ws_strdup_printf("erf: populate_summary_info called with wth NULL");
3240 if (!pseudo_header
) {
3241 *err
= WTAP_ERR_INTERNAL
;
3242 *err_info
= ws_strdup_printf("erf: populate_summary_info called with pseudo_header NULL");
3246 *err
= WTAP_ERR_INTERNAL
;
3247 *err_info
= ws_strdup_printf("erf: populate_summary_info called with erf_priv NULL");
3251 erf_get_source_from_header(pseudo_header
, &host_id
, &source_id
);
3254 host_id
= erf_priv
->implicit_host_id
;
3257 state
.if_map
= erf_find_interface_mapping(erf_priv
, host_id
, source_id
);
3259 if (!state
.if_map
) {
3260 state
.if_map
= erf_if_mapping_create(host_id
, source_id
);
3261 /* g_hash_table_add() only exists since 2.32. */
3262 g_hash_table_replace(erf_priv
->if_map
, state
.if_map
, state
.if_map
);
3267 state
.tag_ptr
= buf
->data
;
3268 state
.remaining_len
= packet_size
;
3270 /* Read until see next section tag */
3271 while ((tagtotallength
= erf_meta_read_tag(&tag
, state
.tag_ptr
, state
.remaining_len
))) {
3273 * Obtain the gen_time from the non-section at the beginning of the record
3275 if (!ERF_META_IS_SECTION(tag
.type
)) {
3276 if(state
.gen_time
== 0U
3277 && tag
.type
== ERF_META_TAG_gen_time
3279 memcpy(&state
.gen_time
, tag
.value
, sizeof(state
.gen_time
));
3282 * Since wireshark doesn't have a concept of different summary metadata
3283 * over time, skip the record if metadata is older than what we already have.
3285 /* TODO: This doesn't work very well for some tags that map to
3286 * pcapng options where the pcapng specification only allows one
3287 * instance per block, which is the case for most options. The
3288 * only current exxceptions are:
3291 * IPv4 and IPv6 addresses for an interface;
3292 * hash values for a packet;
3295 * For options where only one instance is allowed per block,
3296 * wtap_block_add_XXX_option() is currently used to add a new
3297 * instance of an option to a block that has no instance (it
3298 * fails if there's already an instance), and
3299 * wtap_block_set_XXX_optin() is currently used to change the
3300 * value of an option in a block that has one instance (it fails
3301 * if there isn't already an instance).
3303 * For options where more than one instance is allowed per block,
3304 * wtap_block_add_XXX_option() is used to add a new instance to
3305 * a block, no matter how many instances it currently has, and
3306 * wtap_block_set_nth_XXX_option() is used to change the value
3307 * of the Nth instance of an option in a block (the block must
3308 * *have* an Nth instance).
3310 * Currently we only particularly care about updating the capture comment
3311 * and a few counters anyway.
3313 if ((state
.if_map
->interface_metadata
& 0xff)
3314 && state
.gen_time
< erf_priv
->host_gentime
&& state
.gen_time
< erf_priv
->capture_gentime
3315 && (!anchor_mappings_to_update
|| !anchor_mappings_to_update
->len
)) {
3320 * Skip until we get to the next section tag (which could be the current tag
3321 * after an empty section or successful parsing).
3324 state
.tag_ptr
+= tagtotallength
;
3325 state
.remaining_len
-= tagtotallength
;
3330 * We are now looking at the next section (and would have exited the loop
3331 * if we reached the end).
3334 /* Update parent section. Implicit grouping is by a change in section except Interface and Stream. */
3335 if (tag
.type
!= state
.sectiontype
) {
3336 if ((tag
.type
== ERF_META_SECTION_STREAM
&& state
.sectiontype
== ERF_META_SECTION_INTERFACE
) ||
3337 (tag
.type
== ERF_META_SECTION_INTERFACE
&& state
.sectiontype
== ERF_META_SECTION_STREAM
)) {
3340 state
.parentsectiontype
= state
.sectiontype
;
3341 state
.parentsectionid
= state
.sectionid
;
3345 /* Update with new sectiontype */
3346 state
.sectiontype
= tag
.type
;
3347 if (tag
.length
>= 4) {
3348 state
.sectionid
= pntoh16(tag
.value
);
3350 state
.sectionid
= 0;
3353 /* Adjust offset to that of first tag in section */
3354 state
.tag_ptr
+= tagtotallength
;
3355 state
.remaining_len
-= tagtotallength
;
3357 if (erf_meta_read_tag(&tag
, state
.tag_ptr
, state
.remaining_len
)) {
3359 * Process parent section tag if present (which must be the first tag in
3362 if (tag
.type
== ERF_META_TAG_parent_section
&& tag
.length
>= 4) {
3363 state
.parentsectiontype
= pntoh16(tag
.value
);
3364 state
.parentsectionid
= pntoh16(&tag
.value
[2]);
3368 /* Skip empty sections (includes if above read fails) */
3369 if (ERF_META_IS_SECTION(tag
.type
)) {
3374 * Skip sections that don't apply to the general set of records
3375 * (extension point for per-packet/event metadata).
3376 * Unless we need to update the anchor info
3377 * in which case, read into it
3379 if (state
.sectionid
& 0x8000) {
3380 if(state
.sectiontype
& (ERF_META_SECTION_INFO
)) {
3381 /* TODO: do we care if it returns 0 or 1? */
3382 if (populate_anchor_info(erf_priv
, wth
, pseudo_header
, &state
, anchor_mappings_to_update
, err
, err_info
) < 0) {
3390 * Start at first tag in section, makes loop
3391 * simpler in called functions too. Also makes iterating after failure
3394 switch (state
.sectiontype
) {
3395 case ERF_META_SECTION_CAPTURE
:
3396 case ERF_META_SECTION_HOST
:
3397 /* TODO: do we care if it returns 0 or 1? */
3398 if (populate_capture_host_info(erf_priv
, wth
, pseudo_header
, &state
, err
, err_info
) < 0) {
3402 case ERF_META_SECTION_MODULE
:
3403 /* TODO: do we care if it returns 0 or 1? */
3404 if (populate_module_info(erf_priv
, wth
, pseudo_header
, &state
, err
, err_info
) < 0) {
3408 case ERF_META_SECTION_INTERFACE
:
3409 /* TODO: do we care if it returns 0 or 1? */
3410 if (populate_interface_info(erf_priv
, wth
, pseudo_header
, &state
, err
, err_info
) < 0) {
3414 case ERF_META_SECTION_STREAM
:
3416 * XXX: Treat streams specially in case the stream information appears
3417 * before the interface information, as we associate them to interface
3420 post_list
= g_list_append(post_list
, g_memdup2(&state
, sizeof(struct erf_meta_read_state
)));
3422 case ERF_META_SECTION_SOURCE
:
3423 case ERF_META_SECTION_DNS
:
3425 /* TODO: Not yet implemented */
3430 /* Process streams last */
3434 state_post
= (struct erf_meta_read_state
*) item
->data
;
3435 switch (state_post
->sectiontype
) {
3436 case ERF_META_SECTION_STREAM
:
3437 if (populate_stream_info(erf_priv
, wth
, pseudo_header
, state_post
, err
, err_info
) < 0) {
3438 g_list_foreach(post_list
, erf_free_data
, NULL
);
3439 g_list_free(post_list
);
3444 } while ((item
= g_list_next(item
)));
3445 /* g_list_free_full() only exists since 2.28. */
3446 g_list_foreach(post_list
, erf_free_data
, NULL
);
3447 g_list_free(post_list
);
3451 * Update known metadata so we only examine the first set of metadata. Need to
3452 * do this here so can have interface and stream in same record.
3454 if (state
.interface_metadata
) {
3455 state
.if_map
->interface_metadata
|= state
.interface_metadata
;
3456 state
.if_map
->interface_gentime
= state
.gen_time
;
3462 static bool get_user_comment_string(wtap_dumper
*wdh
, char** user_comment_ptr
) {
3463 wtap_block_t wtap_block
;
3464 wtap_opttype_return_val ret
;
3468 if(wdh
->shb_hdrs
&& (wdh
->shb_hdrs
->len
> 0)) {
3469 wtap_block
= g_array_index(wdh
->shb_hdrs
, wtap_block_t
, 0);
3472 if(wtap_block
!= NULL
) {
3473 ret
= wtap_block_get_nth_string_option_value(wtap_block
, OPT_COMMENT
, 0, user_comment_ptr
);
3474 if(ret
!= WTAP_OPTTYPE_SUCCESS
) {
3482 static bool erf_dump_priv_compare_capture_comment(wtap_dumper
*wdh _U_
, erf_dump_t
*dump_priv
, const union wtap_pseudo_header
*pseudo_header
, const uint8_t *pd
){
3483 struct erf_meta_read_state state
= {0};
3484 struct erf_meta_tag tag
= {0, 0, NULL
};
3485 uint32_t tagtotallength
;
3486 bool found_capture_section
= false;
3487 bool found_normal_section
= false;
3488 char* comment_ptr
= NULL
;
3490 state
.remaining_len
= pseudo_header
->erf
.phdr
.wlen
;
3491 memcpy(&(state
.tag_ptr
), &pd
, sizeof(pd
));
3493 while((tagtotallength
= erf_meta_read_tag(&tag
, state
.tag_ptr
, state
.remaining_len
))) {
3494 if (ERF_META_IS_SECTION(tag
.type
)) {
3495 state
.sectiontype
= tag
.type
;
3496 if (tag
.length
>= 4) {
3497 state
.sectionid
= pntoh16(tag
.value
);
3499 state
.sectionid
= 0;
3502 /* Skip sections that don't apply to the general set of records */
3503 if (!(state
.sectionid
& 0x8000)) {
3504 found_normal_section
= true;
3506 if(tag
.type
== ERF_META_SECTION_CAPTURE
) {
3507 /* Found the Capture Section */
3508 found_capture_section
= true;
3512 if (state
.sectiontype
== ERF_META_SECTION_CAPTURE
&& !(state
.sectionid
& 0x8000)) {
3513 if (tag
.type
== ERF_META_TAG_comment
) {
3514 /* XXX: Only compare the first comment tag */
3516 comment_ptr
= g_strndup((char*)tag
.value
, tag
.length
);
3523 /* Read until we have the Capture section */
3524 state
.tag_ptr
+= tagtotallength
;
3525 state
.remaining_len
-= tagtotallength
;
3528 if(found_capture_section
&& (comment_ptr
|| dump_priv
->user_comment_ptr
)) {
3529 if(g_strcmp0(comment_ptr
, dump_priv
->user_comment_ptr
)
3530 && !(dump_priv
->user_comment_ptr
== NULL
&& comment_ptr
&& comment_ptr
[0] == '\0')) {
3531 /* Also treat "" in ERF as equivalent to NULL as that is how we clear the comment on write. */
3533 /* Comments are different, we should write extra metadata record at the end of the list */
3534 dump_priv
->write_next_extra_meta
= true;
3535 g_free(comment_ptr
);
3538 /* We have a capture comment but there is no change, we don't
3539 * need to insert the 'changed' comment. This most likely happened
3540 * because we were looking at list of periodic records and got up to the
3541 * one where the comment was last set. */
3542 dump_priv
->write_next_extra_meta
= false;
3544 /* Otherwise no effect on whether we need to write extra metadata record */
3546 /* We didn't find a capture section (e.g. looking at a comment Anchor
3547 * record), or the comment hadn't changed. */
3549 g_free(comment_ptr
);
3550 /* Return whether we found any non-local metadata (i.e. whether the record has
3551 * metadata that is more than just packet 'comments') */
3552 return found_normal_section
;
3555 static void erf_close(wtap
*wth
)
3557 erf_t
* erf_priv
= (erf_t
*)wth
->priv
;
3559 erf_priv_free(erf_priv
);
3560 /* XXX: Prevent double free by wtap_close() */
3564 static const struct supported_option_type section_block_options_supported
[] = {
3565 { OPT_COMMENT
, ONE_OPTION_SUPPORTED
}, /* XXX - multiple? */
3566 { OPT_SHB_USERAPPL
, ONE_OPTION_SUPPORTED
}
3569 static const struct supported_option_type interface_block_options_supported
[] = {
3570 { OPT_COMMENT
, ONE_OPTION_SUPPORTED
}, /* XXX - multiple? */
3571 { OPT_IDB_NAME
, ONE_OPTION_SUPPORTED
},
3572 { OPT_IDB_DESCRIPTION
, ONE_OPTION_SUPPORTED
},
3573 { OPT_IDB_OS
, ONE_OPTION_SUPPORTED
},
3574 { OPT_IDB_TSOFFSET
, ONE_OPTION_SUPPORTED
},
3575 { OPT_IDB_SPEED
, ONE_OPTION_SUPPORTED
},
3576 { OPT_IDB_IP4ADDR
, ONE_OPTION_SUPPORTED
}, /* XXX - multiple? */
3577 { OPT_IDB_IP6ADDR
, ONE_OPTION_SUPPORTED
}, /* XXX - multiple? */
3578 { OPT_IDB_FILTER
, ONE_OPTION_SUPPORTED
},
3579 { OPT_IDB_FCSLEN
, ONE_OPTION_SUPPORTED
}
3582 static const struct supported_option_type packet_block_options_supported
[] = {
3583 { OPT_COMMENT
, ONE_OPTION_SUPPORTED
} /* XXX - multiple? */
3586 static const struct supported_block_type erf_blocks_supported
[] = {
3588 * Per-file comments and application supported; section blocks
3589 * are used for that.
3590 * ERF files have only one section. (XXX - true?)
3592 { WTAP_BLOCK_SECTION
, ONE_BLOCK_SUPPORTED
, OPTION_TYPES_SUPPORTED(section_block_options_supported
) },
3595 * ERF supports multiple interfaces, with information, and
3596 * supports associating packets with interfaces. Interface
3597 * description blocks are used for that.
3599 { WTAP_BLOCK_IF_ID_AND_INFO
, MULTIPLE_BLOCKS_SUPPORTED
, OPTION_TYPES_SUPPORTED(interface_block_options_supported
) },
3602 * Name resolution is supported, but we don't support comments.
3604 { WTAP_BLOCK_NAME_RESOLUTION
, ONE_BLOCK_SUPPORTED
, NO_OPTIONS_SUPPORTED
},
3607 * ERF is a capture format, so it obviously supports packets.
3609 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, OPTION_TYPES_SUPPORTED(packet_block_options_supported
) }
3612 static const struct file_type_subtype_info erf_info
= {
3613 "Endace ERF capture", "erf", "erf", NULL
,
3614 false, BLOCKS_SUPPORTED(erf_blocks_supported
),
3615 erf_dump_can_write_encap
, erf_dump_open
, NULL
3618 void register_erf(void)
3620 erf_file_type_subtype
= wtap_register_file_type_subtype(&erf_info
);
3623 * Register name for backwards compatibility with the
3624 * wtap_filetypes table in Lua.
3626 wtap_register_backwards_compatibility_lua_name("ERF", erf_file_type_subtype
);
3630 * Editor modelines - https://www.wireshark.org/tools/modelines.html
3635 * indent-tabs-mode: nil
3638 * vi: set shiftwidth=2 tabstop=8 expandtab:
3639 * :indentSize=2:tabSize=8:noTabs=true: