Introduce rte_pcapng_copy_ts() alongside the existing rte_pcapng_copy()
so that callers with a hardware PTP or pre-captured timestamp can inject
an exact epoch-ns value directly into the packet record.

Timestamp handling in rte_pcapng_copy_ts():
 - ts != 0: caller-supplied nanoseconds since the Unix epoch, stored as-is.
 - ts == 0: TSC captured at copy time with bit 63 set as a sentinel.
   rte_pcapng_write_packets() detects the sentinel and converts the TSC to
   epoch ns using the file's calibrated clock.  The TSC will not reach
   bit 63 for centuries, and epoch-ns values stay below bit 63 until 2554,
   so the bit is safe to use as a disambiguation flag.

rte_pcapng_copy() is retained as a real exported function (not an inline
wrapper) so the stable ABI symbol is preserved.  It simply calls
rte_pcapng_copy_ts(..., 0) to capture the current TSC.

rte_pcapng_tsc_to_ns() is added as a new experimental helper (addressing
review requests from Stephen Hemminger and Morten Brørup).  It exposes the
same calibrated, drift-compensated, divide-free TSC-to-epoch-ns conversion
used internally by rte_pcapng_write_packets(), allowing callers to convert
a TSC captured at packet arrival time before passing it to
rte_pcapng_copy_ts().

Signed-off-by: Marek Kasiewicz <[email protected]>
Signed-off-by: Dawid Wesierski <[email protected]>
---

Hi Stephen, Morten,
Thank you for the feedback on the previous versions,
In this version i added unit Tests test case in app/test/test_pcapng.c to
verify the TSC-to-NS conversion and the custom timestamp injection.


Regards,
Dawid Węsierski

 .mailmap                |   2 +
 app/test/test_pcapng.c  | 108 ++++++++++++++++++++++++++++++++++++++++
 lib/pcapng/rte_pcapng.c |  42 +++++++++++++---
 lib/pcapng/rte_pcapng.h |  53 ++++++++++++++++++++
 4 files changed, 199 insertions(+), 6 deletions(-)

diff --git a/.mailmap b/.mailmap
index 4001e5fb0e..a7d97a631e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -366,6 +366,7 @@ David Zeng <[email protected]>
 Davide Caratti <[email protected]>
 Dawid Gorecki <[email protected]>
 Dawid Jurczak <[email protected]>
+Dawid Wesierski <[email protected]> Wesierski, Dawid 
<[email protected]>
 Dawid Zielinski <[email protected]>
 Dawid Łukwiński <[email protected]>
 Daxue Gao <[email protected]>
@@ -1014,6 +1015,7 @@ Marcin Wilk <[email protected]>
 Marcin Wojtas <[email protected]>
 Marcin Zapolski <[email protected]>
 Marco Varlese <[email protected]>
+Marek Kasiewicz <[email protected]>
 Marek Mical <[email protected]>
 Marek Zalfresso-jundzillo <[email protected]>
 Maria Lingemark <[email protected]>
diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 298bcbd31f..0554c33369 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -672,6 +672,113 @@ test_write_before_open(void)
        return -1;
 }
 
+static int
+test_pcapng_timestamp(void)
+{
+       char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
+       rte_pcapng_t *pcapng = NULL;
+       int ret, tmp_fd;
+       struct dummy_mbuf mbfs;
+       struct rte_mbuf *orig, *mc;
+       uint64_t now_ns, tsc, ns_from_tsc, pcap_ts;
+
+       tmp_fd = mkstemps(file_name, strlen(".pcapng"));
+       if (tmp_fd == -1) {
+               perror("mkstemps() failure");
+               goto fail;
+       }
+
+       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;
+       }
+
+       /* Test 1: rte_pcapng_tsc_to_ns */
+       tsc = rte_get_tsc_cycles();
+       now_ns = current_timestamp();
+       ns_from_tsc = rte_pcapng_tsc_to_ns(pcapng, tsc);
+
+       /* Check if TSC-derived NS is reasonably close to wall clock NS (within 
100ms) */
+       if (ns_from_tsc > now_ns + 100000000 || ns_from_tsc < now_ns - 
100000000) {
+               printf("TSC to NS conversion failed: tsc=%"PRIu64
+                      " ns_from_tsc=%"PRIu64" now_ns=%"PRIu64"\n",
+                      tsc, ns_from_tsc, now_ns);
+               goto fail;
+       }
+
+       /* Test 2: rte_pcapng_copy_ts with explicit timestamp */
+       mbuf1_prepare(&mbfs);
+       orig = &mbfs.mb[0];
+       pcap_ts = now_ns + 1000000000; /* 1 second in future to be distinct */
+
+       mc = rte_pcapng_copy_ts(port_id, 0, orig, mp, rte_pktmbuf_pkt_len(orig),
+                               RTE_PCAPNG_DIRECTION_IN, "custom_ts", pcap_ts);
+       if (mc == NULL) {
+               printf("rte_pcapng_copy_ts failed\n");
+               goto fail;
+       }
+
+       /* Write it */
+       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);
+
+       /* Validate the file using libpcap */
+       /* We expect 1 packet with timestamp exactly pcap_ts */
+       {
+               char errbuf[PCAP_ERRBUF_SIZE];
+               pcap_t *pcap;
+               struct pcap_pkthdr h;
+               const u_char *bytes;
+               uint64_t ns;
+
+               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;
+               }
+
+               bytes = pcap_next(pcap, &h);
+               if (bytes == NULL) {
+                       printf("No packets in file\n");
+                       pcap_close(pcap);
+                       goto fail;
+               }
+
+               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);
+                       pcap_close(pcap);
+                       goto fail;
+               }
+               pcap_close(pcap);
+       }
+
+       remove(file_name);
+       return 0;
+
+fail:
+       if (pcapng)
+               rte_pcapng_close(pcapng);
+       return -1;
+}
+
 static void
 test_cleanup(void)
 {
@@ -688,6 +795,7 @@ unit_test_suite test_pcapng_suite  = {
                TEST_CASE(test_add_interface),
                TEST_CASE(test_write_packets),
                TEST_CASE(test_write_before_open),
+               TEST_CASE(test_pcapng_timestamp),
                TEST_CASES_END()
        }
 };
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index b5d1026891..84c427fb2d 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -37,6 +37,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)
+
 /*
  * Converter from TSC values to nanoseconds since Unix epoch.
  * Uses reciprocal multiply to avoid runtime division.
@@ -480,6 +483,13 @@ rte_pcapng_mbuf_size(uint32_t length)
                + sizeof(uint32_t);               /*  length */
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_tsc_to_ns, 26.07)
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc)
+{
+       return tsc_to_ns_epoch(&self->clock, tsc);
+}
+
 /* More generalized version rte_vlan_insert() */
 static int
 pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
@@ -554,11 +564,24 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
                uint32_t length,
                enum rte_pcapng_direction direction,
                const char *comment)
+{
+       return rte_pcapng_copy_ts(port_id, queue, md, mp, length, direction,
+                                 comment, 0);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pcapng_copy_ts, 26.07)
+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)
 {
        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;
@@ -690,8 +713,13 @@ 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();
+       /*
+        * Use caller provided timestamp.
+        * If none provided, use current TSC and set flag.
+        */
+       if (timestamp == 0)
+               timestamp = rte_get_tsc_cycles() | PCAPNG_TSC_FLAG;
+
        epb->timestamp_hi = timestamp >> 32;
        epb->timestamp_lo = (uint32_t)timestamp;
        epb->capture_length = pkt_len;
@@ -743,9 +771,11 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
                 */
                cycles = (uint64_t)epb->timestamp_hi << 32;
                cycles += epb->timestamp_lo;
-               timestamp = tsc_to_ns_epoch(&self->clock, cycles);
-               epb->timestamp_hi = timestamp >> 32;
-               epb->timestamp_lo = (uint32_t)timestamp;
+               if (cycles & PCAPNG_TSC_FLAG) {
+                       timestamp = tsc_to_ns_epoch(&self->clock, 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..42c42ca60c 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -140,6 +140,59 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
                uint32_t length,
                enum rte_pcapng_direction direction, const char *comment);
 
+/**
+ * Format an mbuf for writing to file with a custom timestamp.
+ *
+ * @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
+ *   Nanoseconds since the Unix epoch. If zero, TSC is captured and
+ *   converted at write time.
+ *
+ * @return
+ *   - The pointer to the new mbuf formatted for pcapng_write
+ *   - NULL on error such as invalid port or out of memory.
+ */
+__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);
+
+/**
+ * Convert a TSC value to nanoseconds since the Unix epoch.
+ *
+ * Uses the calibrated clock of the capture file.
+ *
+ * @param self
+ *  The handle to the packet capture file
+ * @param tsc
+ *  The TSC value to convert
+ * @return
+ *  Nanoseconds since Unix epoch
+ */
+__rte_experimental
+uint64_t
+rte_pcapng_tsc_to_ns(const rte_pcapng_t *self, uint64_t tsc);
+
 
 /**
  * Determine optimum mbuf data size.
-- 
2.47.3

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial 
Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | 
Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z 
dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach 
handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i 
moze zawierac informacje poufne. W razie przypadkowego otrzymania tej 
wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; 
jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole 
use of the intended recipient(s). If you are not the intended recipient, please 
contact the sender and delete all copies; any review or distribution by others 
is strictly prohibited.

Reply via email to