From f4723eeb7e4228a306d25f671df08ac87eeeaa23 Mon Sep 17 00:00:00 2001 From: John Thacker Date: Sat, 3 Jun 2023 11:48:37 -0400 Subject: [PATCH] wiretap: Generate IDBs from packets when necessary Add a routine to generate a dummy IDB from a packet record. When pcapng is writing enhanced packet blocks and the source doesn't provide an interface id, search through the list of intereface ids for a match. If there isn't one, generate a new one and use it. This allows pcapng to write per-packet encapsulation when the source doesn't provide IDBs. --- wiretap/file_access.c | 6 +++-- wiretap/pcapng.c | 59 +++++++++++++++++++++++++++++++------------ wiretap/wtap-int.h | 25 ++++++++++++++++++ wiretap/wtap.c | 14 ++++++++++ 4 files changed, 86 insertions(+), 18 deletions(-) diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 0bc1a12577..4c56c57951 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -2337,11 +2337,13 @@ wtap_dump_init_dumper(int file_type_subtype, wtap_compression_type compression_t * means that there are no interfaces, or they will be * provided later when reading the file in single-pass mode.) * + * For WTAP_ENCAP_PER_PACKET, we'll have to generate IDBs + * from packet records as they come in. (pcapng does this now.) + * * XXX File types should provide their own IDBs (possibly * fake ones generated by wtap_add_generated_idb()), in * order to support being used as inputs for mergecap where - * pcapng is the output. This doesn't work for files with - * WTAP_ENCAP_PER_PACKET. + * pcapng is the output. */ descr = wtap_dump_params_generate_idb(params); g_array_append_val(wdh->interface_data, descr); diff --git a/wiretap/pcapng.c b/wiretap/pcapng.c index 8c7a2cbe1d..292ab06a27 100644 --- a/wiretap/pcapng.c +++ b/wiretap/pcapng.c @@ -53,6 +53,9 @@ pcapng_close(wtap *wth); static gboolean pcapng_encap_is_ft_specific(int encap); +static gboolean +pcapng_write_if_descr_block(wtap_dumper *wdh, wtap_block_t int_data, int *err); + /* * Minimum block size = size of block header + size of block trailer. */ @@ -4944,28 +4947,39 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec, options_size = compute_options_size(rec->block, compute_epb_option_size); } - /* write (enhanced) packet block header */ - bh.block_type = BLOCK_TYPE_EPB; - bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + rec->rec_header.packet_header.caplen + pad_len + options_total_length + options_size + 4; - - if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err)) - return FALSE; - - /* write block fixed content */ + /* + * Check the interface ID. Do this before writing the header, + * in case we need to add a new IDB. + */ if (rec->presence_flags & WTAP_HAS_INTERFACE_ID) epb.interface_id = rec->rec_header.packet_header.interface_id; else { /* - * XXX - we should support writing WTAP_ENCAP_PER_PACKET - * data to pcapng files even if we *don't* have interface - * IDs. + * The source isn't sending us IDBs. See if we already have a + * matching interface, and use it if so. */ - epb.interface_id = 0; + for (epb.interface_id = 0; epb.interface_id < wdh->interface_data->len; ++epb.interface_id) { + int_data = g_array_index(wdh->interface_data, wtap_block_t, + epb.interface_id); + int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data); + if (int_data_mand->wtap_encap == rec->rec_header.packet_header.pkt_encap) { + if (int_data_mand->tsprecision == rec->tsprec || (!(rec->presence_flags & WTAP_HAS_TS))) { + break; + } + } + } + if (epb.interface_id == wdh->interface_data->len) { + /* + * We don't have a matching IDB. Generate a new one + * and write it to the file. + */ + int_data = wtap_rec_generate_idb(rec); + g_array_append_val(wdh->interface_data, int_data); + if (!pcapng_write_if_descr_block(wdh, int_data, err)) { + return FALSE; + } + } } - /* - * Split the 64-bit timestamp into two 32-bit pieces, using - * the time stamp resolution for the interface. - */ if (epb.interface_id >= wdh->interface_data->len) { /* * Our caller is doing something bad. @@ -4989,6 +5003,19 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec, rec->rec_header.packet_header.pkt_encap); return FALSE; } + + /* write (enhanced) packet block header */ + bh.block_type = BLOCK_TYPE_EPB; + bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + rec->rec_header.packet_header.caplen + pad_len + options_total_length + options_size + 4; + + if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err)) + return FALSE; + + /* write block fixed content */ + /* + * Split the 64-bit timestamp into two 32-bit pieces, using + * the time stamp resolution for the interface. + */ ts = ((guint64)rec->ts.secs) * int_data_mand->time_units_per_second + (((guint64)rec->ts.nsecs) * int_data_mand->time_units_per_second) / 1000000000; epb.timestamp_high = (guint32)(ts >> 32); diff --git a/wiretap/wtap-int.h b/wiretap/wtap-int.h index 438b885d32..3e365edbab 100644 --- a/wiretap/wtap-int.h +++ b/wiretap/wtap-int.h @@ -402,8 +402,33 @@ GArray* wtap_file_get_shb_for_new_file(wtap *wth); WS_DLL_PUBLIC void wtap_add_generated_idb(wtap *wth); +/** + * @brief Generate an IDB, given a set of dump parameters, using the + * parameters' encapsulation type, snapshot length, and time stamp + * resolution. For use when a dump file has a given encapsulation type, + * and the source is not passing IDBs. + * @note This requires that the encapsulation type and time stamp + * resolution not be per-packet; it will terminate the process + * if either of them are. + * + * @param params The wtap dump parameters. + */ + wtap_block_t wtap_dump_params_generate_idb(const wtap_dump_params *params); +/** + * @brief Generate an IDB, given a packet record, using the records's + * encapsulation type and time stamp resolution, and the default + * snap length for the encapsulation type. For use when a file has + * per-packet encapsulation, and the source is not passing along IDBs. + * @note This requires that the record type be REC_TYPE_PACKET, and the + * encapsulation type and time stamp resolution not be per-packet; + * it will terminate the process if any of them are. + * + * @param rec The packet record. + */ +wtap_block_t wtap_rec_generate_idb(const wtap_rec *rec); + /** * @brief Gets new name resolution info for new file, based on existing info. * @details Creates a new wtap_block_t of name resolution info and only diff --git a/wiretap/wtap.c b/wiretap/wtap.c index e430d37fc8..3e784c3ced 100644 --- a/wiretap/wtap.c +++ b/wiretap/wtap.c @@ -1822,6 +1822,20 @@ wtap_rec_cleanup(wtap_rec *rec) ws_buffer_free(&rec->options_buf); } +wtap_block_t +wtap_rec_generate_idb(const wtap_rec *rec) +{ + int tsprec; + ws_assert(rec->rec_type == REC_TYPE_PACKET); + if (rec->presence_flags & WTAP_HAS_TS) { + tsprec = rec->tsprec; + } else { + tsprec = WTAP_TSPREC_USEC; + /* The default */ + } + return wtap_generate_idb(rec->rec_header.packet_header.pkt_encap, tsprec, 0); +} + gboolean wtap_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)