Add a timestamp parameter to 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:
 - 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.

Adding the parameter changes the ABI, so rte_pcapng_copy() is versioned.

rte_pcapng_tsc_to_ns() is added as a new experimental helper.  It exposes the
same calibrated, drift-compensated, divide-free TSC-to-epoch-ns conversion
used internally by rte_pcapng_write_packets(), allows callers to convert
a TSC captured at packet arrival time before passing it to rte_pcapng_copy().

Signed-off-by: Marek Kasiewicz <[email protected]>
Signed-off-by: Dawid Wesierski <[email protected]>
---
 .mailmap                |   2 +
 app/test/test_pcapng.c  | 112 +++++++++++++++++++++++++++++++++++++++-
 lib/graph/graph_pcap.c  |   2 +-
 lib/pcapng/meson.build  |   1 +
 lib/pcapng/rte_pcapng.c |  59 +++++++++++++++++----
 lib/pcapng/rte_pcapng.h |  22 +++++++-
 lib/pdump/rte_pdump.c   |   2 +-
 7 files changed, 185 insertions(+), 15 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..1dd2ae6cdd 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -302,7 +302,7 @@ fill_pcapng_file(rte_pcapng_t *pcapng)
                        mbuf1_resize(&mbfs, rte_rand_max(MAX_DATA_SIZE));
 
                        mc = rte_pcapng_copy(port_id, 0, orig, mp, 
rte_pktmbuf_pkt_len(orig),
-                                            RTE_PCAPNG_DIRECTION_IN, comment);
+                                            RTE_PCAPNG_DIRECTION_IN, comment, 
0);
                        if (mc == NULL) {
                                printf("Cannot copy packet\n");
                                return -1;
@@ -612,7 +612,7 @@ test_write_before_open(void)
        for (i = 0; i < (int)count; i++) {
                clones[i] = rte_pcapng_copy(port_id, 0, &mbfs.mb[0], mp,
                                            rte_pktmbuf_pkt_len(&mbfs.mb[0]),
-                                           RTE_PCAPNG_DIRECTION_IN, NULL);
+                                           RTE_PCAPNG_DIRECTION_IN, NULL, 0);
                if (clones[i] == NULL) {
                        fprintf(stderr, "Cannot copy packet before open\n");
                        rte_pktmbuf_free_bulk(clones, i);
@@ -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 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(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 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/graph/graph_pcap.c b/lib/graph/graph_pcap.c
index 78859ec2d4..4d907c78aa 100644
--- a/lib/graph/graph_pcap.c
+++ b/lib/graph/graph_pcap.c
@@ -216,7 +216,7 @@ graph_pcap_dispatch(struct rte_graph *graph,
                struct rte_mbuf *mc;
                mbuf = (struct rte_mbuf *)objs[i];
 
-               mc = rte_pcapng_copy(mbuf->port, 0, mbuf, pkt_mp, 
mbuf->pkt_len, 0, comment);
+               mc = rte_pcapng_copy(mbuf->port, 0, mbuf, pkt_mp, 
mbuf->pkt_len, 0, comment, 0);
                if (mc == NULL)
                        break;
 
diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build
index 4549925d41..3cfaddbd6e 100644
--- a/lib/pcapng/meson.build
+++ b/lib/pcapng/meson.build
@@ -3,5 +3,6 @@
 
 sources = files('rte_pcapng.c')
 headers = files('rte_pcapng.h')
+use_function_versioning = true
 
 deps += ['ethdev']
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index b5d1026891..b8ebb0c288 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)
@@ -546,19 +556,18 @@ 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,
+RTE_DEFAULT_SYMBOL(26, 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)
+               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 +699,20 @@ 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.
+        * Timestamps shouldn't naturally have the PCAPNG_TSC_FLAG set for
+        * centuries, so if it is set, treat it as an error.
+        */
+       if (timestamp != 0 && (timestamp & PCAPNG_TSC_FLAG)) {
+               rte_errno = EINVAL;
+               goto fail;
+       }
+
+       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;
@@ -707,6 +728,22 @@ rte_pcapng_copy(uint16_t port_id, uint32_t queue,
        return NULL;
 }
 
+/*
+ * Original ABI: no caller-supplied timestamp. Capture the current TSC
+ * (default path) and forward to the timestamped implementation.
+ */
+RTE_VERSION_SYMBOL(25, 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))
+{
+       return rte_pcapng_copy(port_id, queue, md, mp, length, direction,
+                              comment, 0);
+}
+
 /* Write pre-formatted packets to file. */
 RTE_EXPORT_SYMBOL(rte_pcapng_write_packets)
 ssize_t
@@ -743,9 +780,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..97f83c5a88 100644
--- a/lib/pcapng/rte_pcapng.h
+++ b/lib/pcapng/rte_pcapng.h
@@ -129,6 +129,9 @@ enum rte_pcapng_direction {
  * @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
@@ -138,7 +141,24 @@ 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, 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);
 
 
 /**
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index ac94efe7ff..a85a95a808 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -176,7 +176,7 @@ pdump_copy_burst(uint16_t port_id, uint16_t queue_id,
                 */
                if (cbs->ver == V2)
                        p = rte_pcapng_copy(port_id, queue_id, pkts[i], mp, 
cbs->snaplen,
-                                           direction, NULL);
+                                           direction, NULL, 0);
                else
                        p = rte_pktmbuf_copy(pkts[i], mp, 0, cbs->snaplen);
 
-- 
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