kerberos: remember account details from dissect_krb5_PAC_UPN_DNS_INFO on EncTicketPar...
[wireshark-sm.git] / wiretap / erf.c
blob5d610a083cc6777f283c8ee58c64d3ce210394f2
1 /*
2 * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
3 * All rights reserved.
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)
15 * Rev A:
16 * http://web.archive.org/web/20050829051042/http://www.endace.com/support/EndaceRecordFormat.pdf
17 * Version 1:
18 * http://web.archive.org/web/20061111014023/http://www.endace.com/support/EndaceRecordFormat.pdf
19 * Version 8:
20 * https://gitlab.com/wireshark/wireshark/uploads/f694bfee494784425b6545892180a8b2/Endace_ERF_Types.pdf
21 * (bug #4484)
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
28 * Machine.
31 #include "config.h"
33 #include <stdlib.h>
34 #include <string.h>
36 #include <glib.h>
38 #include <wsutil/crc32.h>
39 #include <wsutil/strtoi.h>
40 #include <wsutil/glib-compat.h>
42 #include "wtap-int.h"
43 #include "file_wrappers.h"
44 #include "erf.h"
45 #include "erf_record.h"
46 #include "erf-common.h"
48 struct erf_anchor_mapping {
49 uint64_t host_id;
50 uint64_t anchor_id;
51 uint64_t gen_time;
52 char *comment;
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,
61 wtap_rec *rec,
62 erf_header_t *erf_header,
63 int *err,
64 char **err_info,
65 uint32_t *bytes_read,
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);
80 typedef struct {
81 bool write_next_extra_meta;
82 bool last_meta_periodic;
83 uint64_t host_id;
84 uint64_t implicit_host_id;
85 uint64_t prev_frame_ts;
86 uint8_t prev_erf_type;
87 uint64_t gen_time;
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;
93 GRand *rand;
94 } erf_dump_t;
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 {
106 int erf_encap_value;
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)
126 struct erf_if_info {
127 int if_index;
128 char *name;
129 char *descr;
130 int stream_num;
131 struct {
132 unsigned filter:1;
133 unsigned fcs_len:1;
134 unsigned snaplen:1;
135 } set_flags;
138 struct erf_if_mapping {
139 uint64_t host_id;
140 uint8_t source_id;
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 {
154 uint16_t type;
155 uint16_t section_id;
156 uint16_t section_length;
157 GPtrArray *tags;
160 struct erf_meta_tag {
161 uint16_t type;
162 uint16_t length;
163 uint8_t *value;
166 struct erf_meta_read_state {
167 uint8_t *tag_ptr;
168 uint32_t remaining_len;
170 struct erf_if_mapping *if_map;
172 uint16_t sectiontype;
173 uint16_t sectionid;
174 uint16_t parentsectiontype;
175 uint16_t parentsectionid;
177 uint64_t gen_time;
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;
212 g_free(anchor_map);
213 anchor_map = 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)
233 int i = 0;
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);
242 g_free(if_map);
245 static struct erf_if_mapping* erf_if_mapping_create(uint64_t host_id, uint8_t source_id)
247 int i = 0;
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*/
264 return if_map;
268 erf_t *erf_priv_create(void)
270 erf_t *erf_priv;
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;
279 return erf_priv;
282 erf_t* erf_priv_free(erf_t* erf_priv)
284 if (erf_priv)
286 g_hash_table_destroy(erf_priv->anchor_map);
287 g_hash_table_destroy(erf_priv->if_map);
288 g_free(erf_priv);
291 return NULL;
294 static void erf_dump_priv_free(erf_dump_t *dump_priv) {
295 if(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);
308 g_free(dump_priv);
313 static void erf_meta_section_free(void *data) {
314 struct erf_meta_section *section_ptr = (struct erf_meta_section*) data;
315 if (section_ptr) {
316 g_ptr_array_free(section_ptr->tags, true);
317 section_ptr->tags = NULL;
319 g_free(section_ptr);
322 static void erf_meta_tag_free(void *data) {
323 struct erf_meta_tag *tag_ptr = (struct erf_meta_tag*) data;
324 if (tag_ptr) {
325 g_free(tag_ptr->value);
326 tag_ptr->value = NULL;
328 g_free(tag_ptr);
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;
334 bool ret = true;
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
339 * some metadata. */
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);
345 } else {
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
348 * erf_dump_open) */
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;
356 /* Clean up */
357 erf_dump_priv_free(dump_priv);
358 /* Avoid double freeing by setting it to NULL*/
359 wdh->priv = NULL;
361 return ret;
365 static void
366 erf_free_data(void *data, void *user_data _U_)
368 g_free(data);
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;
375 int valid_prev = 0;
376 char *s;
377 erf_timestamp_t prevts,ts;
378 erf_header_t header;
379 uint32_t mc_hdr;
380 struct erf_eth_hdr eth_hdr;
381 uint32_t packet_size;
382 uint16_t rlen;
383 uint64_t erf_ext_header;
384 unsigned erf_ext_header_size = (unsigned)sizeof(erf_ext_header);
385 uint8_t type;
387 prevts = 0;
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
399 * records.
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)) {
405 if (*err == 0) {
406 /* EOF - all records have been successfully checked, accept the file */
407 break;
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;
414 } else {
415 /* BREAK, the last record is too short, and will be ignored */
416 break;
418 } else {
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 */
428 if (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) {
445 /* A real error */
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;
454 continue;
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;
475 prevts = ts;
477 /* Read over the extension headers */
478 type = header.type;
479 while (type & 0x80){
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;
514 break;
515 case ERF_TYPE_ETH:
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,&eth_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;
529 break;
530 default:
531 break;
534 if (!wtap_read_bytes(wth->fh, NULL, packet_size, err, err_info)) {
535 if (*err != WTAP_ERR_SHORT_READ) {
536 /* A real error */
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;
546 valid_prev = 1;
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);
585 do {
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);
590 return false;
593 if (!wtap_read_packet_bytes(wth->fh, buf, packet_size, err, err_info)) {
594 g_ptr_array_free(anchor_mappings_to_update, true);
595 return false;
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
601 * frame.
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);
607 return false;
611 } while ( erf_header.type == ERF_TYPE_PAD );
613 g_ptr_array_free(anchor_mappings_to_update, true);
615 return 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)
627 return false;
629 anchor_mappings_to_update = g_ptr_array_new_with_free_func(erf_anchor_mapping_destroy);
631 do {
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);
635 return false;
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,
642 err, err_info);
645 static struct erf_anchor_mapping* erf_find_anchor_mapping(erf_t *priv,
646 uint64_t host_id,
647 uint64_t anchor_id)
649 struct erf_anchor_mapping mapping = {
650 host_id,
651 anchor_id,
653 NULL
656 if (!priv) {
657 return NULL;
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,
665 wtap_rec *rec,
666 erf_header_t *erf_header,
667 int *err,
668 char **err_info,
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;
676 uint8_t type = 0;
677 uint32_t mc_hdr;
678 uint32_t aal2_hdr;
679 struct wtap_erf_eth_hdr eth_hdr;
680 uint32_t skiplen = 0;
681 int i = 0;
682 int max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
683 erf_t *priv = (erf_t*)wth->priv;
684 int interface_id;
686 uint64_t host_id = ERF_META_HOST_ID_IMPLICIT;
687 uint8_t source_id = 0;
688 uint8_t if_num = 0;
689 bool host_id_found = false;
691 if (!wtap_read_bytes_or_eof(fh, erf_header, sizeof(*erf_header), err, err_info)) {
692 return false;
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);
708 return false;
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");
717 return false;
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.
735 #if 0
736 } else {
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? */
747 #endif
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;
755 rec->ts.secs += 1;
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;
772 while (type & 0x80){
773 if (!wtap_read_bytes(fh, &erf_exhdr, sizeof(erf_exhdr),
774 err, err_info))
775 return false;
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);
781 if (i < max)
782 memcpy(&pseudo_header->erf.ehdr_list[i].ehdr, &erf_exhdr_sw, sizeof(erf_exhdr_sw));
783 type = erf_exhdr[0];
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:
791 if (!host_id_found)
792 host_id = erf_exhdr_sw & ERF_EHDR_HOST_ID_MASK;
794 host_id_found = true;
795 /* Fall through */
796 case ERF_EXT_HDR_TYPE_FLOW_ID:
797 /* Source ID is present in both Flow ID and Host ID extension headers */
798 if (!source_id)
799 source_id = (erf_exhdr_sw >> 48) & 0xff;
800 break;
801 case ERF_EXT_HDR_TYPE_ANCHOR_ID:
802 /* handled below*/
803 break;
805 i++;
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) {
810 return false;
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) {
818 case ERF_TYPE_IPV4:
819 case ERF_TYPE_IPV6:
820 case ERF_TYPE_RAW_LINK:
821 case ERF_TYPE_INFINIBAND:
822 case ERF_TYPE_INFINIBAND_LINK:
823 case ERF_TYPE_META:
824 case ERF_TYPE_OPA_SNC:
825 case ERF_TYPE_OPA_9B:
826 #if 0
828 rec->rec_header.packet_header.len = g_htons(erf_header->wlen);
829 rec->rec_header.packet_header.caplen = g_htons(erf_header->wlen);
831 return true;
832 #endif
833 break;
834 case ERF_TYPE_PAD:
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:
839 case ERF_TYPE_ATM:
840 case ERF_TYPE_AAL5:
841 break;
843 case ERF_TYPE_ETH:
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, &eth_hdr, sizeof(eth_hdr), err, err_info))
848 return false;
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;
854 break;
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))
864 return false;
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);
870 break;
872 case ERF_TYPE_AAL2:
873 if (!wtap_read_bytes(fh, &aal2_hdr, sizeof(aal2_hdr), err, err_info))
874 return false;
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);
880 break;
882 case ERF_TYPE_IP_COUNTER:
883 case ERF_TYPE_TCP_FLOW_COUNTER:
884 /* unsupported, continue with default: */
885 default:
886 /* let the dissector dissect as unknown record type for forwards compatibility */
887 break;
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);
904 return false;
907 return true;
910 static int wtap_wtap_encap_to_erf_encap(int encap)
912 unsigned int i;
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;
917 return -1;
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];
925 size_t size = 0;
926 size_t subhdr_size = 0;
927 int i = 0;
928 uint8_t has_more = 0;
930 switch(encap){
931 case WTAP_ENCAP_ERF:
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);
951 break;
952 case ERF_TYPE_AAL2:
953 phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.aal2_hdr);
954 subhdr_size += (int)sizeof(struct erf_aal2_hdr);
955 break;
956 case ERF_TYPE_ETH:
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;
962 break;
963 default:
964 break;
966 break;
967 default:
968 return false;
971 if (!wtap_dump_file_write(wdh, erf_hdr, size, err))
972 return false;
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;
981 i++;
982 }while(has_more && i < MAX_ERF_EHDR);
983 if (!wtap_dump_file_write(wdh, ehdr, 8*i, err))
984 return false;
987 if(!wtap_dump_file_write(wdh, erf_subhdr, subhdr_size, err))
988 return false;
990 return true;
994 static void erf_dump_priv_init_gen_time(erf_dump_t *dump_priv) {
995 int64_t real_time;
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_,
1004 unsigned option_id,
1005 wtap_opttype_e option_type _U_,
1006 wtap_optval_t *optval,
1007 void* user_data) {
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);
1014 switch(option_id) {
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);
1019 break;
1020 case OPT_COMMENT:
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);
1024 break;
1025 default:
1026 erf_meta_tag_free(tag_ptr);
1027 tag_ptr = NULL;
1028 break;
1031 if (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_,
1038 unsigned option_id,
1039 wtap_opttype_e option_type _U_,
1040 wtap_optval_t *optval,
1041 void* user_data) {
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);
1048 switch(option_id) {
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);
1053 break;
1054 case OPT_SHB_OS:
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);
1058 break;
1059 default:
1060 erf_meta_tag_free(tag_ptr);
1061 tag_ptr = NULL;
1062 break;
1065 if (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_,
1072 unsigned option_id,
1073 wtap_opttype_e option_type _U_,
1074 wtap_optval_t *optval,
1075 void* user_data) {
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);
1082 switch(option_id) {
1083 case OPT_COMMENT:
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);
1087 break;
1088 case OPT_IDB_NAME:
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);
1092 break;
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);
1097 break;
1098 case OPT_IDB_OS:
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);
1102 break;
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);
1109 break;
1110 case OPT_IDB_SPEED:
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);
1115 break;
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));
1121 break;
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));
1127 break;
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);
1139 break;
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);
1145 break;
1146 /* TODO: Don't know what to do with these yet */
1147 case OPT_IDB_EUIADDR:
1148 #if 0
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));
1153 break;
1154 #endif
1155 case OPT_IDB_MACADDR:
1156 #if 0
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));
1163 break;
1164 #endif
1165 case OPT_IDB_TSRESOL:
1166 case OPT_IDB_TZONE:
1167 /* Fall through */
1168 default:
1169 erf_meta_tag_free(tag_ptr);
1170 tag_ptr = NULL;
1171 break;
1174 if (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) {
1181 unsigned i = 0;
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) {
1205 return false;
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);
1219 return true;
1223 static bool erf_meta_write_tag(wtap_dumper *wdh, struct erf_meta_tag *tag_ptr, int *err) {
1225 uint16_t data[2];
1226 unsigned pad = 0;
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;
1238 if(pad) {
1239 if(!wtap_dump_file_write(wdh, &padbuf, pad, err)) return false;
1242 return true;
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;
1249 unsigned i;
1250 uint16_t data[4];
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;
1264 return true;
1268 static bool erf_wtap_info_to_sections(wtap_dumper *wdh, GPtrArray *sections) {
1269 wtap_block_t block;
1270 unsigned i = 0;
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);
1285 return true;
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
1305 * invalidated) */
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();
1311 if (user) {
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);
1324 return true;
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) {
1332 uint64_t ext_hdr;
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;
1338 return ext_hdr;
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 */
1345 unsigned i = 0;
1346 struct erf_ehdr ehdr_tmp;
1347 struct erf_ehdr *ehdr_ptr = NULL;
1349 if (!extra_ehdrs)
1350 return false;
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)) {
1362 return true;
1366 /* set more flag on last extension header */
1367 if (ehdr_ptr) {
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);
1374 return true;
1377 static inline bool erf_append_ext_hdr_to_list(uint64_t ext_hdr, GArray *extra_ehdrs) {
1378 struct erf_ehdr ehdr_tmp;
1380 if (!extra_ehdrs)
1381 return false;
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);
1392 return true;
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) {
1396 uint8_t type;
1397 uint8_t erf_type;
1398 int has_more;
1399 uint64_t hdr;
1400 int i = 0;
1401 uint8_t source_id = 0;
1402 uint64_t host_id = 0;
1403 bool host_id_found = false;
1405 if (!extra_ehdrs)
1406 return 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;
1436 break;
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;
1440 break;
1443 has_more = type & 0x80;
1444 i++;
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;
1455 return true;
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) {
1471 GArray *meta_ehdrs;
1472 GPtrArray* sections = NULL;
1473 uint8_t has_more;
1474 uint8_t i = 0;
1475 uint8_t ext_hdr_count = 0;
1476 uint8_t j = 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;
1487 bool ret = false;
1488 uint64_t implicit_host_id = dump_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT ? 0 : dump_priv->implicit_host_id;
1489 char *pkt_comment;
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
1509 * record.
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)
1525 insert_idx = i;
1527 if ((hdr & ERF_EHDR_HOST_ID_MASK) == dump_priv->host_id){
1528 found_own_host_id = true;
1531 if (!found_host_id)
1532 host_id_src_hdr = hdr;
1534 found_host_id = true;
1535 break;
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*/
1540 if (source_id == 0)
1541 source_id = (uint8_t)(hdr >> 48);
1543 if (!found_flow_id)
1544 flow_id_hdr = hdr;
1546 found_flow_id = true;
1547 break;
1550 has_more = type & 0x80;
1551 i += 1;
1554 ext_hdr_count = i;
1556 if (!insert_idx)
1557 insert_idx = i;
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;
1573 return false;
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 */
1627 /*Required*/
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)) {
1657 pkt_comment = NULL;
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);
1667 return ret;
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;
1677 unsigned i;
1678 unsigned num_extra_ehdrs = 0;
1680 if(!sections || sections->len <= 0)
1681 return false;
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 */
1694 if (extra_ehdrs) {
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;
1709 return false;
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);
1737 /* Section(s) */
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;
1750 return 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();
1772 return dump_priv;
1775 static bool erf_dump(
1776 wtap_dumper *wdh,
1777 const wtap_rec *rec,
1778 const uint8_t *pd,
1779 int *err,
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;
1784 int erf_type;
1785 int64_t alignbytes = 0;
1786 unsigned padbytes = 0;
1787 int round_down = 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;
1798 return false;
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
1818 * support later.
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;
1832 return false;
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;
1838 return false;
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){
1855 case ERF_TYPE_ETH:
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*/
1865 total_wlen += 4;
1866 must_add_crc = true;
1869 break;
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*/
1875 total_wlen += 4;
1876 must_add_crc = true; /* XXX - these never have an FCS? */
1878 break;
1879 default:
1880 break;
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;
1886 total_rlen += 8;
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;
1892 }else{
1893 total_rlen += padbytes;
1896 if (total_rlen > UINT16_MAX || total_wlen > UINT16_MAX) {
1897 *err = WTAP_ERR_PACKET_TOO_LARGE;
1898 return false;
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.
1907 time_t secs;
1908 int nsecs;
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;
1917 secs += 1;
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
1964 * read. */
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
1974 * erf_dump_open) */
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
1997 * modified)? */
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;
2003 } else {
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*/
2017 if(must_add_crc){
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;
2034 return true;
2037 static int erf_dump_can_write_encap(int encap)
2040 if(encap == WTAP_ENCAP_PER_PACKET)
2041 return 0;
2043 if (wtap_wtap_encap_to_erf_encap(encap) == -1)
2044 return WTAP_ERR_UNWRITABLE_ENCAP;
2046 return 0;
2049 static bool erf_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_)
2051 erf_dump_t *dump_priv;
2052 char *s;
2053 uint64_t host_id;
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;
2080 return true;
2083 static int erf_get_source_from_header(union wtap_pseudo_header *pseudo_header, uint64_t *host_id, uint8_t *source_id)
2085 uint8_t type;
2086 uint8_t has_more;
2087 uint64_t hdr;
2088 int i = 0;
2089 bool host_id_found = false;
2091 if (!pseudo_header || !host_id || !source_id)
2092 return -1;
2094 *host_id = ERF_META_HOST_ID_IMPLICIT;
2095 *source_id = 0;
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:
2109 if (!host_id_found)
2110 *host_id = hdr & ERF_EHDR_HOST_ID_MASK;
2112 host_id_found = true;
2113 /* Fall through */
2114 case ERF_EXT_HDR_TYPE_FLOW_ID:
2115 if (*source_id == 0)
2116 *source_id = (hdr >> 48) & 0xff;
2117 break;
2120 if (host_id_found)
2121 break;
2123 has_more = type & 0x80;
2124 i += 1;
2127 return 0;
2130 int erf_populate_interface_from_header(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, int *err, char **err_info)
2132 uint64_t host_id;
2133 uint8_t source_id;
2134 uint8_t if_num;
2136 if (!pseudo_header)
2137 return -1;
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 */
2151 if (!erf_priv)
2152 return NULL;
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)
2162 /* Source XXX,*/
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) {
2172 host_id = 0;
2175 if (host_id > 0) {
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);
2183 if (descr) {
2184 wtap_block_set_string_option_value_format(block, option_id, "%s (ERF%s%s Interface %d)", descr, hostid_buf, sourceid_buf, if_num);
2185 } else {
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)
2192 uint8_t type;
2193 uint8_t has_more;
2194 uint64_t hdr;
2195 uint64_t comment_gen_time = 0;
2196 uint64_t host_id_current;
2197 uint64_t anchor_id_current = 0;
2198 int i = 0;
2199 char *comment = NULL;
2201 if (!rec || !pseudo_header)
2202 return -1;
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;
2217 break;
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.
2233 * empty string)! */
2234 if (lookup_result->comment && lookup_result->comment[0] != '\0') {
2235 comment = lookup_result->comment;
2237 comment_gen_time = lookup_result->gen_time;
2241 else {
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
2246 * the metadata tags
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);
2257 break;
2261 has_more = type & 0x80;
2262 i += 1;
2265 if (comment) {
2266 wtap_block_add_string_option(rec->block, OPT_COMMENT, comment, strlen(comment));
2269 return 0;
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;
2278 void *iter_value;
2279 GList* implicit_list = NULL;
2280 GList* item = 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];
2289 int i;
2291 if (!erf_priv)
2292 return -1;
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);
2315 } else {
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);
2351 g_free(oldstr);
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;
2364 do {
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
2405 * gen_time.
2407 g_hash_table_iter_remove(&iter);
2408 } else {
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;
2421 do {
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;
2431 return 0;
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;
2440 if (!wth) {
2441 *err = WTAP_ERR_INTERNAL;
2442 *err_info = ws_strdup_printf("erf: erf_populate_interface called with wth NULL");
2443 return -1;
2445 if (!pseudo_header) {
2446 *err = WTAP_ERR_INTERNAL;
2447 *err_info = ws_strdup_printf("erf: erf_populate_interface called with pseudo_header NULL");
2448 return -1;
2450 if (!erf_priv) {
2451 *err = WTAP_ERR_INTERNAL;
2452 *err_info = ws_strdup_printf("erf: erf_populate_interface called with erf_priv NULL");
2453 return -1;
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);
2459 return -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
2470 * Implicit Host ID.
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);
2479 if (!if_map) {
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)
2524 uint16_t tagtype;
2525 uint16_t taglength;
2526 uint32_t tagtotallength;
2528 if (!tag_ptr || !tag || remaining_len < ERF_META_TAG_HEADERLEN)
2529 return 0;
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) {
2540 return 0;
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;
2555 char* tmp;
2556 char* app_name = NULL;
2557 char* app_version = NULL;
2558 char* model = NULL;
2559 char* descr = NULL;
2560 char* cpu = NULL;
2561 char* modelcpu = NULL;
2562 uint32_t tagtotallength;
2564 if (!wth) {
2565 *err = WTAP_ERR_INTERNAL;
2566 *err_info = ws_strdup_printf("erf: populate_capture_host_info called with wth NULL");
2567 return -1;
2569 if (!state) {
2570 *err = WTAP_ERR_INTERNAL;
2571 *err_info = ws_strdup_printf("erf: populate_capture_host_info called with state NULL");
2572 return -1;
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");
2577 return -1;
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");
2582 return -1;
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) {
2594 return 0;
2597 switch (tag.type) {
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);
2606 } else {
2607 wtap_block_add_string_option(shb_hdr, OPT_COMMENT, tag.value, tag.length);
2609 break;
2613 /* Fall through */
2614 case ERF_META_SECTION_HOST:
2616 if (erf_priv->host_gentime > state->gen_time) {
2617 return 0;
2620 switch (tag.type) {
2621 case ERF_META_TAG_model:
2622 g_free(model);
2623 model = g_strndup((char*) tag.value, tag.length);
2624 break;
2625 case ERF_META_TAG_cpu:
2626 g_free(cpu);
2627 cpu = g_strndup((char*) tag.value, tag.length);
2628 break;
2629 case ERF_META_TAG_descr:
2630 g_free(descr);
2631 descr = g_strndup((char*) tag.value, tag.length);
2632 break;
2633 case ERF_META_TAG_os:
2634 wtap_block_set_string_option_value(shb_hdr, OPT_SHB_OS, tag.value, tag.length);
2635 break;
2636 case ERF_META_TAG_app_name:
2637 g_free(app_name);
2638 app_name = g_strndup((char*) tag.value, tag.length);
2639 break;
2640 case ERF_META_TAG_app_version:
2641 g_free(app_version);
2642 app_version = g_strndup((char*) tag.value, tag.length);
2643 break;
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);
2649 break;
2650 default:
2651 break;
2654 break;
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));
2671 g_free(tmp);
2673 g_free(app_name);
2674 g_free(app_version);
2675 app_name = NULL;
2676 app_version = NULL;
2679 /* For the hardware field show description followed by (model; cpu) */
2680 /* Build "Model; CPU" part */
2681 if (model || cpu) {
2682 /* g_strjoin() would be nice to use here if the API didn't stop on the first NULL... */
2683 if (model && cpu) {
2684 modelcpu = g_strconcat(model, "; ", cpu, NULL);
2685 } else if (cpu) {
2686 modelcpu = cpu;
2687 /* avoid double-free */
2688 cpu = NULL;
2689 } else {
2690 modelcpu = model;
2691 /* avoid double-free */
2692 model = NULL;
2696 /* Combine into "Description (Model; CPU)" */
2697 if (state->sectiontype == ERF_META_SECTION_HOST && descr) {
2698 if (modelcpu) {
2699 wtap_block_set_string_option_value_format(shb_hdr, OPT_SHB_HARDWARE, "%s (%s)", descr, modelcpu);
2700 } else {
2701 wtap_block_set_string_option_value(shb_hdr, OPT_SHB_HARDWARE, descr, strlen(descr));
2702 /*descr = NULL;*/
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 */
2710 g_free(modelcpu);
2711 g_free(model);
2712 g_free(descr);
2713 g_free(cpu);
2715 if (state->sectiontype == ERF_META_SECTION_CAPTURE) {
2716 erf_priv->capture_gentime = state->gen_time;
2717 } else {
2718 erf_priv->host_gentime = state->gen_time;
2721 return 1;
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;
2730 if (!wth) {
2731 *err = WTAP_ERR_INTERNAL;
2732 *err_info = ws_strdup_printf("erf: populate_module_info called with wth NULL");
2733 return -1;
2735 if (!state) {
2736 *err = WTAP_ERR_INTERNAL;
2737 *err_info = ws_strdup_printf("erf: populate_module_info called with stat NULL");
2738 return -1;
2741 if (state->if_map->module_gentime > state->gen_time) {
2742 return 0;
2745 while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
2746 switch (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);
2751 break;
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);
2757 break;
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);
2761 break;
2764 state->tag_ptr += tagtotallength;
2765 state->remaining_len -= tagtotallength;
2768 state->if_map->module_gentime = state->gen_time;
2770 return 1;
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;
2784 if (!wth) {
2785 *err = WTAP_ERR_INTERNAL;
2786 *err_info = ws_strdup_printf("erf: populate_interface_info called with wth NULL");
2787 return -1;
2789 if (!state) {
2790 *err = WTAP_ERR_INTERNAL;
2791 *err_info = ws_strdup_printf("erf: populate_interface_info called with state NULL");
2792 return -1;
2794 if (!pseudo_header) {
2795 *err = WTAP_ERR_INTERNAL;
2796 *err_info = ws_strdup_printf("erf: populate_interface_info called with pseudo_header NULL");
2797 return -1;
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");
2802 return -1;
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) {
2849 return -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 */
2860 return 0;
2861 } else {
2862 *err = WTAP_ERR_INTERNAL;
2863 *err_info = ws_strdup_printf("erf: populate_interface_info got interface_index %d < 0 and != -2", interface_index);
2864 return -1;
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.
2873 if (!int_data)
2874 return 0;
2876 if (state->if_map->interface_gentime > state->gen_time && state->if_map->interface_metadata & (1 << if_num))
2877 return 0;
2879 while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
2880 switch (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);
2892 break;
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);
2903 break;
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));
2907 break;
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 */
2915 break;
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;
2921 break;
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;
2928 break;
2929 case ERF_META_TAG_comment:
2930 wtap_block_add_string_option(int_data, OPT_COMMENT, tag.value, tag.length);
2931 break;
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;
2938 break;
2939 default:
2940 break;
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
2950 * if not already.
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;
2987 return 1;
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;
3004 if (!wth) {
3005 *err = WTAP_ERR_INTERNAL;
3006 *err_info = ws_strdup_printf("erf: populate_stream_info called with wth NULL");
3007 return -1;
3009 if (!pseudo_header) {
3010 *err = WTAP_ERR_INTERNAL;
3011 *err_info = ws_strdup_printf("erf: populate_stream_info called with pseudo_header NULL");
3012 return -1;
3014 if (!state) {
3015 *err = WTAP_ERR_INTERNAL;
3016 *err_info = ws_strdup_printf("erf: populate_stream_info called with state NULL");
3017 return -1;
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");
3022 return -1;
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;
3038 } else {
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))) {
3062 continue;
3065 if (if_info->stream_num != -1
3066 && if_info->stream_num != stream_num) {
3067 continue;
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);
3077 if (!int_data) {
3078 continue;
3081 while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
3082 switch (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;
3097 break;
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;
3103 break;
3105 default:
3106 /* "shouldn't happen" */
3107 break;
3110 break;
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;
3121 break;
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;
3131 break;
3132 default:
3133 break;
3136 tag_ptr_tmp += tagtotallength;
3137 remaining_len_tmp -= tagtotallength;
3140 state->tag_ptr = tag_ptr_tmp;
3141 state->remaining_len = remaining_len_tmp;
3143 return 1;
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;
3150 unsigned i = 0;
3152 if (!wth) {
3153 *err = WTAP_ERR_INTERNAL;
3154 *err_info = ws_strdup_printf("erf: populate_anchor_info called with wth NULL");
3155 return -1;
3157 if (!state) {
3158 *err = WTAP_ERR_INTERNAL;
3159 *err_info = ws_strdup_printf("erf: populate_anchor_info called with state NULL");
3160 return -1;
3162 if (!pseudo_header) {
3163 *err = WTAP_ERR_INTERNAL;
3164 *err_info = ws_strdup_printf("erf: populate_anchor_info called with pseudo_header NULL");
3165 return -1;
3168 if (!anchor_mappings_to_update || anchor_mappings_to_update->len == 0)
3169 return 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 */
3173 switch(tag.type) {
3174 case ERF_META_TAG_comment:
3175 if(!comment_ptr) {
3176 comment_ptr = g_strndup((char*)tag.value, tag.length);
3178 break;
3179 default:
3180 break;
3183 state->tag_ptr += tagtotallength;
3184 state->remaining_len -= tagtotallength;
3187 if(comment_ptr) {
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
3196 * record. */
3197 if(lookup_result) {
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);
3204 else {
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);
3219 return 1;
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;
3227 uint64_t host_id;
3228 uint8_t source_id;
3229 GList *post_list = NULL;
3230 GList *item = NULL;
3232 struct erf_meta_tag tag = {0, 0, NULL};
3233 uint32_t tagtotallength;
3235 if (!wth) {
3236 *err = WTAP_ERR_INTERNAL;
3237 *err_info = ws_strdup_printf("erf: populate_summary_info called with wth NULL");
3238 return -1;
3240 if (!pseudo_header) {
3241 *err = WTAP_ERR_INTERNAL;
3242 *err_info = ws_strdup_printf("erf: populate_summary_info called with pseudo_header NULL");
3243 return -1;
3245 if (!erf_priv) {
3246 *err = WTAP_ERR_INTERNAL;
3247 *err_info = ws_strdup_printf("erf: populate_summary_info called with erf_priv NULL");
3248 return -1;
3251 erf_get_source_from_header(pseudo_header, &host_id, &source_id);
3253 if (host_id == 0) {
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:
3290 * comments;
3291 * IPv4 and IPv6 addresses for an interface;
3292 * hash values for a packet;
3293 * custom options.
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)) {
3316 return 0;
3320 * Skip until we get to the next section tag (which could be the current tag
3321 * after an empty section or successful parsing).
3323 /* adjust offset */
3324 state.tag_ptr += tagtotallength;
3325 state.remaining_len -= tagtotallength;
3326 continue;
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)) {
3338 /* do nothing */
3339 } else {
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);
3349 } else {
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
3360 * the section).
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)) {
3370 continue;
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) {
3383 return -1;
3386 continue;
3390 * Start at first tag in section, makes loop
3391 * simpler in called functions too. Also makes iterating after failure
3392 * much simpler.
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) {
3399 return -1;
3401 break;
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) {
3405 return -1;
3407 break;
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) {
3411 return -1;
3413 break;
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
3418 * data.
3420 post_list = g_list_append(post_list, g_memdup2(&state, sizeof(struct erf_meta_read_state)));
3421 break;
3422 case ERF_META_SECTION_SOURCE:
3423 case ERF_META_SECTION_DNS:
3424 default:
3425 /* TODO: Not yet implemented */
3426 break;
3430 /* Process streams last */
3431 if (post_list) {
3432 item = post_list;
3433 do {
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);
3440 return -1;
3442 break;
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;
3459 return 0;
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;
3466 wtap_block = NULL;
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) {
3475 return false;
3479 return true;
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);
3498 } else {
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;
3511 } else {
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 */
3515 if(!comment_ptr) {
3516 comment_ptr = g_strndup((char*)tag.value, tag.length);
3518 break;
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);
3536 return true;
3537 } else {
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() */
3561 wth->priv = NULL;
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
3632 * Local Variables:
3633 * c-basic-offset: 2
3634 * tab-width: 8
3635 * indent-tabs-mode: nil
3636 * End:
3638 * vi: set shiftwidth=2 tabstop=8 expandtab:
3639 * :indentSize=2:tabSize=8:noTabs=true: