DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Dawid Wesierski <dawid.wesierski@intel.com>,
	Marek Kasiewicz <marek.kasiewicz@intel.com>,
	Stephen Hemminger <stephen@networkplumber.org>,
	Reshma Pattan <reshma.pattan@intel.com>
Subject: [PATCH 3/3] pcapng: add user-supplied timestamp support
Date: Tue, 20 Oct 2026 10:55:44 -0700	[thread overview]
Message-ID: <20261020175732.1552406-4-stephen@networkplumber.org> (raw)
In-Reply-To: <20261020175732.1552406-1-stephen@networkplumber.org>

From: Dawid Wesierski <dawid.wesierski@intel.com>

An application may have a better time for a packet than the TSC at
copy time, for example a hardware Rx timestamp, but rte_pcapng_copy()
always timestamps with the TSC.

Add rte_pcapng_copy_ts(), which takes the time of the packet in
nanoseconds since the Unix epoch. Both entry points share one
implementation: rte_pcapng_copy() reads the TSC and sets bit 63 to
mark it, and rte_pcapng_write_packets() converts a marked value with
rte_tsc_to_ns(). Epoch time in nanoseconds sets bit 63 only in the
year 2262, so rte_pcapng_copy_ts() rejects a timestamp that sets it.

rte_pcapng_copy() keeps its signature, so no API or ABI is broken and
no deprecation notice is needed. The new function is experimental.

Signed-off-by: Marek Kasiewicz <marek.kasiewicz@intel.com>
Signed-off-by: Dawid Wesierski <dawid.wesierski@intel.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test/test_pcapng.c                 | 100 +++++++++++++++++++++++++
 doc/guides/prog_guide/pcapng_lib.rst   |   4 +
 doc/guides/rel_notes/release_26_11.rst |   7 ++
 lib/pcapng/rte_pcapng.c                |  72 +++++++++++++++---
 lib/pcapng/rte_pcapng.h                |  54 ++++++++++++-
 5 files changed, 224 insertions(+), 13 deletions(-)

diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 060c479ba2..4d5ce65f23 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -18,6 +18,7 @@
 #endif
 
 #include <rte_bus_vdev.h>
+#include <rte_errno.h>
 #include <rte_ethdev.h>
 #include <rte_ether.h>
 #include <rte_ip.h>
@@ -714,6 +715,104 @@ test_write_before_open(void)
 	return -1;
 }
 
+static int
+test_pcapng_timestamp(void)
+{
+	char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
+	char errbuf[PCAP_ERRBUF_SIZE];
+	rte_pcapng_t *pcapng = NULL;
+	struct dummy_mbuf mbfs;
+	struct pcap_pkthdr h;
+	struct rte_mbuf *mc;
+	uint64_t pcap_ts, ns;
+	pcap_t *pcap;
+	int ret, tmp_fd;
+
+	tmp_fd = mkstemps(file_name, strlen(".pcapng"));
+	if (tmp_fd == -1) {
+		perror("mkstemps() failure");
+		return -1;
+	}
+
+	pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_ts_test", NULL);
+	if (pcapng == NULL) {
+		printf("rte_pcapng_fdopen failed\n");
+		close(tmp_fd);
+		goto fail;
+	}
+
+	ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB, NULL, NULL, NULL);
+	if (ret < 0) {
+		printf("can not add port %u\n", port_id);
+		goto fail;
+	}
+
+	mbuf1_prepare(&mbfs);
+
+	/* Bit 63 marks a TSC value, so a caller timestamp must not set it. */
+	mc = rte_pcapng_copy_ts(port_id, 0, &mbfs.mb[0], mp,
+				rte_pktmbuf_pkt_len(&mbfs.mb[0]),
+				RTE_PCAPNG_DIRECTION_IN, NULL,
+				UINT64_C(1) << 63);
+	if (mc != NULL || rte_errno != EINVAL) {
+		printf("rte_pcapng_copy_ts accepted a timestamp with bit 63 set\n");
+		rte_pktmbuf_free(mc);
+		goto fail;
+	}
+
+	/* One second ahead, so that it cannot be the TSC of the copy. */
+	pcap_ts = current_timestamp() + NS_PER_S;
+	mc = rte_pcapng_copy_ts(port_id, 0, &mbfs.mb[0], mp,
+				rte_pktmbuf_pkt_len(&mbfs.mb[0]),
+				RTE_PCAPNG_DIRECTION_IN, "custom_ts", pcap_ts);
+	if (mc == NULL) {
+		printf("rte_pcapng_copy_ts failed\n");
+		goto fail;
+	}
+
+	ret = rte_pcapng_write_packets(pcapng, &mc, 1);
+	rte_pktmbuf_free(mc);
+	if (ret <= 0) {
+		printf("Write of custom timestamp packet failed\n");
+		goto fail;
+	}
+
+	rte_pcapng_close(pcapng);
+	pcapng = NULL;
+
+	pcap = pcap_open_offline_with_tstamp_precision(file_name,
+						       PCAP_TSTAMP_PRECISION_NANO,
+						       errbuf);
+	if (pcap == NULL) {
+		printf("pcap_open_offline failed: %s\n", errbuf);
+		goto fail;
+	}
+
+	if (pcap_next(pcap, &h) == NULL) {
+		printf("No packets in file\n");
+		pcap_close(pcap);
+		goto fail;
+	}
+	pcap_close(pcap);
+
+	/* With nanosecond precision, tv_usec holds nanoseconds. */
+	ns = (uint64_t)h.ts.tv_sec * NS_PER_S + h.ts.tv_usec;
+	if (ns != pcap_ts) {
+		printf("Timestamp mismatch: expected %"PRIu64" got %"PRIu64"\n",
+		       pcap_ts, ns);
+		goto fail;
+	}
+
+	remove(file_name);
+	return 0;
+
+fail:
+	if (pcapng)
+		rte_pcapng_close(pcapng);
+	remove(file_name);
+	return -1;
+}
+
 static void
 test_cleanup(void)
 {
@@ -826,6 +925,7 @@ unit_test_suite test_pcapng_suite  = {
 		TEST_CASE(test_write_packets),
 		TEST_CASE(test_write_before_open),
 		TEST_CASE(test_long_timestamp),
+		TEST_CASE(test_pcapng_timestamp),
 		TEST_CASES_END()
 	}
 };
diff --git a/doc/guides/prog_guide/pcapng_lib.rst b/doc/guides/prog_guide/pcapng_lib.rst
index d18ebdbd1f..598cc6c93b 100644
--- a/doc/guides/prog_guide/pcapng_lib.rst
+++ b/doc/guides/prog_guide/pcapng_lib.rst
@@ -32,6 +32,10 @@ to determine the lower bound based on MTU.
 Collecting packets is done in two parts.
 The function ``rte_pcapng_copy`` is used to format and copy mbuf data
 and ``rte_pcapng_write_packets`` writes a burst of packets to the output file.
+The timestamp of each packet is the TSC at the time of ``rte_pcapng_copy``.
+An application that has a better time for the packet,
+for example a hardware timestamp,
+can give it in nanoseconds to ``rte_pcapng_copy_ts`` instead.
 
 The function ``rte_pcapng_write_stats`` can be used
 to write statistics information into the output file.
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 5f960692e2..4ff5fca0c3 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -79,6 +79,13 @@ New Features
   to convert a TSC value into nanoseconds since 1/1/1970,
   using a reference point recorded during EAL initialization.
 
+* **Added pcapng support for user-supplied timestamps.**
+
+  Added the experimental ``rte_pcapng_copy_ts()`` function,
+  which takes the time of the packet in nanoseconds since the Unix epoch,
+  for example a hardware Rx timestamp.
+  ``rte_pcapng_copy()`` is unchanged and still uses the TSC at copy time.
+
 * **Added pile stack and mempool driver.**
 
   Introduced pile, a lock-free, stack-like implementation,
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index f910896a61..952cc24ce4 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -36,6 +36,9 @@
 /* upper bound for strings in pcapng option data */
 #define PCAPNG_STR_MAX	UINT16_MAX
 
+/* Flag to indicate timestamp is in TSC cycles (bit 63) */
+#define PCAPNG_TSC_FLAG	(1ULL << 63)
+
 /* Format of the capture file handle */
 struct rte_pcapng {
 	int  outfd;		/* output file */
@@ -473,20 +476,24 @@ pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
 
-/* Make a copy of original mbuf with pcapng header and options */
-RTE_EXPORT_SYMBOL(rte_pcapng_copy)
-struct rte_mbuf *
-rte_pcapng_copy(uint16_t port_id, uint32_t queue,
+/*
+ * Make a copy of original mbuf with pcapng header and options.
+ *
+ * The timestamp is stored as given: either epoch nanoseconds from the
+ * caller, or the TSC with PCAPNG_TSC_FLAG set for conversion at write.
+ */
+static struct rte_mbuf *
+pcapng_copy(uint16_t port_id, uint32_t queue,
 		const struct rte_mbuf *md,
 		struct rte_mempool *mp,
 		uint32_t length,
 		enum rte_pcapng_direction direction,
-		const char *comment)
+		const char *comment,
+		uint64_t timestamp)
 {
 	struct pcapng_enhance_packet_block *epb;
 	uint32_t orig_len, pkt_len, padding, flags;
 	struct pcapng_option *opt;
-	uint64_t timestamp;
 	uint16_t optlen;
 	struct rte_mbuf *mc;
 	bool rss_hash;
@@ -618,8 +625,6 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 	/* Interface index is filled in later during write */
 	mc->port = port_id;
 
-	/* Put timestamp in cycles here - adjust in packet write */
-	timestamp = rte_get_tsc_cycles();
 	epb->timestamp_hi = timestamp >> 32;
 	epb->timestamp_lo = (uint32_t)timestamp;
 	epb->capture_length = pkt_len;
@@ -635,6 +640,43 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 	return NULL;
 }
 
+RTE_EXPORT_SYMBOL(rte_pcapng_copy)
+struct rte_mbuf *
+rte_pcapng_copy(uint16_t port_id, uint32_t queue,
+		const struct rte_mbuf *md,
+		struct rte_mempool *mp,
+		uint32_t length,
+		enum rte_pcapng_direction direction,
+		const char *comment)
+{
+	/*
+	 * Record the TSC and flag it, so rte_pcapng_write_packets() knows
+	 * to convert it rather than treat it as an epoch time.
+	 */
+	return pcapng_copy(port_id, queue, md, mp, length, direction, comment,
+			   rte_get_tsc_cycles() | PCAPNG_TSC_FLAG);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_copy_ts, 26.11)
+struct rte_mbuf *
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
+		   const struct rte_mbuf *md,
+		   struct rte_mempool *mp,
+		   uint32_t length,
+		   enum rte_pcapng_direction direction,
+		   const char *comment,
+		   uint64_t timestamp)
+{
+	/* Bit 63 is reserved to mark a TSC, see rte_pcapng_copy(). */
+	if (unlikely(timestamp & PCAPNG_TSC_FLAG)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	return pcapng_copy(port_id, queue, md, mp, length, direction, comment,
+			   timestamp);
+}
+
 /* Write pre-formatted packets to file. */
 RTE_EXPORT_SYMBOL(rte_pcapng_write_packets)
 ssize_t
@@ -665,12 +707,18 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
 			return -1;
 		}
 
-		/* adjust timestamp recorded in packet */
+		/*
+		 * If rte_pcapng_copy() stored the TSC (flag bit set),
+		 * convert it to epoch nanoseconds. Else the caller gave
+		 * the time in nanoseconds since the epoch: keep it.
+		 */
 		cycles = (uint64_t)epb->timestamp_hi << 32;
 		cycles += epb->timestamp_lo;
-		timestamp = rte_tsc_to_ns(cycles);
-		epb->timestamp_hi = timestamp >> 32;
-		epb->timestamp_lo = (uint32_t)timestamp;
+		if (cycles & PCAPNG_TSC_FLAG) {
+			timestamp = rte_tsc_to_ns(cycles & ~PCAPNG_TSC_FLAG);
+			epb->timestamp_hi = timestamp >> 32;
+			epb->timestamp_lo = (uint32_t)timestamp;
+		}
 
 		/*
 		 * Handle case of highly fragmented and large burst size
diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h
index d8d328f710..792f34acb1 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -133,12 +133,64 @@ enum rte_pcapng_direction {
  * @return
  *   - The pointer to the new mbuf formatted for pcapng_write
  *   - NULL on error such as invalid port or out of memory.
+ *
+ * The packet is timestamped with the TSC, which
+ * rte_pcapng_write_packets() converts to epoch time.
+ * Use rte_pcapng_copy_ts() to give the time of the packet directly.
  */
 struct rte_mbuf *
 rte_pcapng_copy(uint16_t port_id, uint32_t queue,
 		const struct rte_mbuf *m, struct rte_mempool *mp,
 		uint32_t length,
-		enum rte_pcapng_direction direction, const char *comment);
+		enum rte_pcapng_direction direction,
+		const char *comment);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Format an mbuf for writing to file, with the time of the packet.
+ *
+ * Same as rte_pcapng_copy(), except that the caller gives the time of
+ * the packet instead of it being taken from the TSC at copy time.
+ *
+ * @param port_id
+ *   The Ethernet port on which packet was received
+ *   or is going to be transmitted.
+ * @param queue
+ *   The queue on the Ethernet port where packet was received
+ *   or is going to be transmitted.
+ * @param mp
+ *   The mempool from which the "clone" mbufs are allocated.
+ * @param m
+ *   The mbuf to copy
+ * @param length
+ *   The upper limit on bytes to copy.  Passing UINT32_MAX
+ *   means all data (after offset).
+ * @param direction
+ *   The direction of the packer: receive, transmit or unknown.
+ * @param comment
+ *   Optional per packet comment.
+ *   Truncated to UINT16_MAX characters.
+ * @param timestamp
+ *   Time of the packet in nanoseconds since the Unix epoch,
+ *   for example a hardware timestamp.
+ *   Bit 63 is reserved and must be zero;
+ *   epoch time in nanoseconds sets it only in the year 2262.
+ *
+ * @return
+ *   - The pointer to the new mbuf formatted for pcapng_write
+ *   - NULL on error such as invalid port, invalid timestamp
+ *     or out of memory. rte_errno is EINVAL if bit 63 of
+ *     the timestamp is set.
+ */
+__rte_experimental
+struct rte_mbuf *
+rte_pcapng_copy_ts(uint16_t port_id, uint32_t queue,
+		   const struct rte_mbuf *m, struct rte_mempool *mp,
+		   uint32_t length,
+		   enum rte_pcapng_direction direction,
+		   const char *comment, uint64_t timestamp);
 
 
 /**
-- 
2.53.0


  parent reply	other threads:[~2026-10-20 17:57 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-18 14:38 [PATCH v3 1/1] pcapng: add user-supplied timestamp support Dawid Wesierski
2026-06-18 15:22 ` Stephen Hemminger
2026-06-18 16:45   ` Morten Brørup
2026-06-18 18:51     ` Stephen Hemminger
2026-06-23 14:10 ` [PATCH v4] " Dawid Wesierski
2026-06-23 13:53   ` Stephen Hemminger
2026-06-24 21:57   ` [PATCH v5] " Dawid Wesierski
2026-06-24 22:07     ` Stephen Hemminger
2026-06-24 22:17     ` Stephen Hemminger
2026-06-26 10:22     ` [v5] " Dawid Wesierski
2026-06-26 15:04       ` Stephen Hemminger
2026-06-29  9:37     ` [PATCH v6 1/1] " Dawid Wesierski
2026-06-29 16:50       ` Stephen Hemminger
2026-10-14 17:21       ` [PATCH v7] " Dawid Wesierski
2026-10-14 17:38         ` Stephen Hemminger
2026-10-20 15:22           ` Dawid Wesierski
2026-10-20 15:29           ` [PATCH v8] " Dawid Wesierski
2026-10-20 17:55             ` [PATCH 0/3] pcapng timestamp conversion and user-supplied timestamps Stephen Hemminger
2026-10-20 17:55               ` [PATCH 1/3] eal: add function to convert TSC to epoch time Stephen Hemminger
2026-10-20 17:55               ` [PATCH 2/3] pcapng: use EAL TSC to epoch conversion Stephen Hemminger
2026-10-20 17:55               ` Stephen Hemminger [this message]
2026-10-21  2:56               ` [PATCH 0/3] pcapng timestamp conversion and user-supplied timestamps Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261020175732.1552406-4-stephen@networkplumber.org \
    --to=stephen@networkplumber.org \
    --cc=dawid.wesierski@intel.com \
    --cc=dev@dpdk.org \
    --cc=marek.kasiewicz@intel.com \
    --cc=reshma.pattan@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox