pcapng: support the if_tsoffset option.

When calculating packet timestamps, add the timestamp offset to the
seconds value of the timestamp.

At least one DPDK developer considered using if_tsoffset, but abandoned
the idea because of the lack of Wireshark support:

https://lore.kernel.org/all/36758990.XM6RcZxFsP@thomas/t/#mafbe5280ba947327be1798bf15e0781d63b049a7

and the pcapng code in the Rust pcap_parser crate:

https://docs.rs/pcap-parser/latest/pcap_parser/

supports if_tsoffset.

libpcap doesn't currently support it, but I'll be changing it to do so.

While we're at it, update comments to reflect the deprecation of
if_tzone (never well defined, and, unless you can map them to IANA
timezone names or something such as that, far from sufficient to allow
converting UTC to the appropriate local time), and the clarification of
the definition of if_tsoffset, in the pcapng spec.
This commit is contained in:
Guy Harris 2023-11-28 22:43:48 -08:00
parent 2cab4226ef
commit 1c2093eec8
5 changed files with 165 additions and 21 deletions

View File

@ -249,6 +249,7 @@ typedef struct interface_info_s {
guint32 snap_len;
guint64 time_units_per_second;
int tsprecision;
gint64 tsoffset;
int fcslen;
} interface_info_t;
@ -802,6 +803,58 @@ pcapng_process_uint64_option(wtapng_block_t *wblock,
}
}
void
pcapng_process_int64_option(wtapng_block_t *wblock,
const section_info_t *section_info,
pcapng_opt_byte_order_e byte_order,
guint16 option_code, guint16 option_length,
const guint8 *option_content)
{
gint64 int64;
if (option_length == 8) {
/* Don't cast a gint8 * into a gint64 *--the
* guint8 * may not point to something that's
* aligned correctly.
*/
memcpy(&int64, option_content, sizeof(gint64));
switch (byte_order) {
case OPT_SECTION_BYTE_ORDER:
if (section_info->byte_swapped) {
int64 = GUINT64_SWAP_LE_BE(int64);
}
break;
case OPT_BIG_ENDIAN:
int64 = GUINT64_FROM_BE(int64);
break;
case OPT_LITTLE_ENDIAN:
int64 = GUINT64_FROM_LE(int64);
break;
default:
/*
* This should not happen - this is called by pcapng_process_options(),
* which returns an error for an invalid byte_order argument, and
* otherwise passes the known-to-be-valid byte_order argument to
* us.
*
* Just ignore the option.
*/
return;
}
/*
* If this option can appear only once in a block, this call
* will fail on the second and later occurrences of the option;
* we silently ignore the failure.
*/
wtap_block_add_int64_option(wblock->block, option_code, int64);
}
}
void
pcapng_process_string_option(wtapng_block_t *wblock, guint16 option_code,
guint16 option_length, const guint8 *option_content)
@ -1509,22 +1562,29 @@ pcapng_process_if_descr_block_option(wtapng_block_t *wblock,
break;
case(OPT_IDB_TZONE):
/*
* Time zone for GMT support. TODO: specify better.
* TODO: give a good example.
* Time zone for GMT support. This option has never been
* specified in greater detail and, unless it were to identify
* something such as an IANA time zone database timezone,
* would be insufficient for converting between UTC and local
* time. Therefore, it SHOULD NOT be used; instead, the
* if_iana_tzname option SHOULD be used if time zone
* information is to be specified.
*
* Given that, we don't do anything with it.
*/
break;
case(OPT_IDB_TSOFFSET):
/*
* A 64 bits integer value that specifies an offset (in
* A 64-bit integer value that specifies an offset (in
* seconds) that must be added to the timestamp of each packet
* to obtain the absolute timestamp of a packet. If the option
* is missing, the timestamps stored in the packet must be
* considered absolute timestamps. The time zone of the offset
* can be specified with the option if_tzone.
*
* TODO: won't a if_tsoffset_low for fractional second offsets
* be useful for highly synchronized capture systems? 1234
* to obtain the absolute timestamp of a packet. If this optio
* is not present, an offset of 0 is assumed (i.e., timestamps
* in blocks are absolute timestamps.)
*/
pcapng_process_int64_option(wblock, section_info,
OPT_SECTION_BYTE_ORDER,
option_code, option_length,
option_content);
break;
default:
if (!pcapng_process_unhandled_option(wblock, BT_INDEX_IDB,
@ -2244,9 +2304,14 @@ pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh,
/* Combine the two 32-bit pieces of the timestamp into one 64-bit value */
ts = (((guint64)packet.ts_high) << 32) | ((guint64)packet.ts_low);
/* Convert it to seconds and nanoseconds. */
wblock->rec->ts.secs = (time_t)(ts / iface_info.time_units_per_second);
wblock->rec->ts.nsecs = (int)(((ts % iface_info.time_units_per_second) * 1000000000) / iface_info.time_units_per_second);
/* Add the time stamp offset. */
wblock->rec->ts.secs = (time_t)(wblock->rec->ts.secs + iface_info.tsoffset);
/* "(Enhanced) Packet Block" read capture data */
if (!wtap_read_packet_bytes(fh, wblock->frame_buffer,
packet.cap_len - pseudo_header_len, err, err_info))
@ -3616,11 +3681,42 @@ pcapng_process_idb(wtap *wth, section_info_t *section_info,
iface_info.time_units_per_second = wblock_if_descr_mand->time_units_per_second;
iface_info.tsprecision = wblock_if_descr_mand->tsprecision;
/*
* Did we get an FCS length option?
*/
if (wtap_block_get_uint8_option_value(wblock->block, OPT_IDB_FCSLEN,
&if_fcslen) == WTAP_OPTTYPE_SUCCESS)
&if_fcslen) == WTAP_OPTTYPE_SUCCESS) {
/*
* Yes.
*/
iface_info.fcslen = if_fcslen;
else
} else {
/*
* No. Mark the FCS length as unknown.
*/
iface_info.fcslen = -1;
}
/*
* Did we get a time stamp offset option?
*/
if (wtap_block_get_int64_option_value(wblock->block, OPT_IDB_TSOFFSET,
&iface_info.tsoffset) == WTAP_OPTTYPE_SUCCESS) {
/*
* Yes.
*
* Remove the option, as the time stamps we provide will be
* absolute time stamps, with the offset added in, so it will
* appear as if there were no such option.
*/
wtap_block_remove_option(wblock->block, OPT_IDB_TSOFFSET);
} else {
/*
* No. Default to 0, meahing that time stamps in the file are
* absolute time stamps.
*/
iface_info.tsoffset = 0;
}
g_array_append_val(section_info->interfaces, iface_info);
}
@ -5245,12 +5341,13 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec,
return FALSE;
/* write block fixed content */
/* Calculate the time stamp as a 64-bit integer. */
ts = ((guint64)rec->ts.secs) * int_data_mand->time_units_per_second +
(((guint64)rec->ts.nsecs) * int_data_mand->time_units_per_second) / 1000000000;
/*
* 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);
epb.timestamp_low = (guint32)ts;
epb.captured_len = rec->rec_header.packet_header.caplen + phdr_len;
@ -6147,6 +6244,19 @@ static guint32 compute_idb_option_size(wtap_block_t block _U_, guint option_id,
case OPT_IDB_FCSLEN:
size = 1;
break;
case OPT_IDB_TSOFFSET:
/*
* The time stamps handed to us when writing a file are
* absolute time staps, so the time stamp offset is
* zero.
*
* We do not adjust them when writing, so we should not
* write if_tsoffset options; that is interpreted as
* the offset is zero, i.e. the time stamps in the file
* are absolute.
*/
size = 0;
break;
default:
/* Unknown options - size by datatype? */
size = 0;
@ -6182,6 +6292,11 @@ static gboolean write_wtap_idb_option(wtap_dumper *wdh, wtap_block_t block _U_,
if (!pcapng_write_uint8_option(wdh, option_id, optval, err))
return FALSE;
break;
case OPT_IDB_TSOFFSET:
/*
* As noted above, we discard these.
*/
break;
default:
/* Unknown options - size by datatype? */
break;

View File

@ -193,6 +193,13 @@ void pcapng_process_uint64_option(wtapng_block_t *wblock,
guint16 option_code, guint16 option_length,
const guint8 *option_content);
WS_DLL_PUBLIC
void pcapng_process_int64_option(wtapng_block_t *wblock,
const section_info_t *section_info,
pcapng_opt_byte_order_e byte_order,
guint16 option_code, guint16 option_length,
const guint8 *option_content);
WS_DLL_PUBLIC
void pcapng_process_string_option(wtapng_block_t *wblock, guint16 option_code,
guint16 option_length, const guint8 *option_content);

View File

@ -365,6 +365,7 @@ wtap_get_debug_if_descr(const wtap_block_t if_descr,
char* tmp_content;
wtapng_if_descr_mandatory_t* if_descr_mand;
GString *info = g_string_new("");
gint64 itmp64;
guint64 tmp64;
guint8 tmp8;
if_filter_opt_t if_filter;
@ -437,6 +438,13 @@ wtap_get_debug_if_descr(const wtap_block_t if_descr,
line_end);
}
if (wtap_block_get_int64_option_value(if_descr, OPT_IDB_TSOFFSET, &itmp64) == WTAP_OPTTYPE_SUCCESS) {
g_string_append_printf(info,
"%*cTimestamp offset = %" G_GINT64_FORMAT "%s", indent, ' ',
itmp64,
line_end);
}
if (wtap_block_get_if_filter_option_value(if_descr, OPT_IDB_FILTER, &if_filter) == WTAP_OPTTYPE_SUCCESS) {
switch (if_filter.type) {

View File

@ -1965,6 +1965,12 @@ void wtap_opttypes_initialize(void)
WTAP_OPTTYPE_UINT8,
0
};
static const wtap_opttype_t if_tsoffset = {
"tsoffset",
"IDB Time Stamp Offset",
WTAP_OPTTYPE_INT64,
0
};
static const wtap_opttype_t if_hardware = {
"hardware",
"IDB Hardware",
@ -2167,6 +2173,7 @@ void wtap_opttypes_initialize(void)
wtap_opttype_option_register(&idb_block, OPT_IDB_FILTER, &if_filter);
wtap_opttype_option_register(&idb_block, OPT_IDB_OS, &if_os);
wtap_opttype_option_register(&idb_block, OPT_IDB_FCSLEN, &if_fcslen);
wtap_opttype_option_register(&idb_block, OPT_IDB_TSOFFSET, &if_tsoffset);
wtap_opttype_option_register(&idb_block, OPT_IDB_HARDWARE, &if_hardware);
/*

View File

@ -76,7 +76,12 @@ extern "C" {
* If this option is not present, a resolution of 10^-6 is assumed
* (i.e. timestamps have the same resolution of the standard 'libpcap' timestamps).
*/
#define OPT_IDB_TZONE 10 /**< XXX: if_tzone Time zone for GMT support (TODO: specify better). */
#define OPT_IDB_TZONE 10 /**< Time zone for GMT support. This option has neer been specified in
* greater detail and, unless it were to identify something such as
* an IANA time zone database timezone, would be insufficient for
* converting between UTC and local time. Therefore, it SHOULD NOT
* be used; instead, the if_iana_tzname option SHOULD be used if
* time zone information is to be specified. */
#define OPT_IDB_FILTER 11 /**< The filter (e.g. "capture only TCP traffic") used to capture traffic.
* The first byte of the Option Data keeps a code of the filter used
* (e.g. if this is a libpcap string, or BPF bytecode, and more).
@ -97,13 +102,15 @@ extern "C" {
* For link layers whose FCS length can change during time,
* the Packet Block Flags Word can be used (see Appendix A (Packet Block Flags Word))
*/
#define OPT_IDB_TSOFFSET 14 /**< XXX: A 64 bits integer value that specifies an offset (in seconds)
#define OPT_IDB_TSOFFSET 14 /**< A 64-bit signed integer value that specifies an offset (in seconds)
* that must be added to the timestamp of each packet to obtain
* the absolute timestamp of a packet. If the option is missing,
* the timestamps stored in the packet must be considered absolute
* timestamps. The time zone of the offset can be specified with the
* option if_tzone. TODO: won't a if_tsoffset_low for fractional
* second offsets be useful for highly synchronized capture systems?
* the absolute timestamp of a packet. If the option is not present,
* an offst of 0 is assumed (i.e., timestamps in blocks are absolute
* timestamps).
*
* This offset is not intended to be used as an offset between local
* time and UTC; for this purpose, the if_iana_tzname option SHOULD be
* used to specify a timezone.
*/
#define OPT_IDB_HARDWARE 15 /**< A UTF-8 string containing the description
* of the hardware of the device used