get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/132353/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 132353,
    "url": "https://patches.dpdk.org/api/patches/132353/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20231005230648.68244-5-stephen@networkplumber.org/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20231005230648.68244-5-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20231005230648.68244-5-stephen@networkplumber.org",
    "date": "2023-10-05T23:06:48",
    "name": "[v2,4/4] test: cleanups to pcapng test",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "b847be0c5ffdd1e291f80fce5d8984704da8c718",
    "submitter": {
        "id": 27,
        "url": "https://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20231005230648.68244-5-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 29747,
            "url": "https://patches.dpdk.org/api/series/29747/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=29747",
            "date": "2023-10-05T23:06:44",
            "name": "dumpcap and pcapng fixes",
            "version": 2,
            "mbox": "https://patches.dpdk.org/series/29747/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/132353/comments/",
    "check": "success",
    "checks": "https://patches.dpdk.org/api/patches/132353/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 608BD426C7;\n\tFri,  6 Oct 2023 01:07:28 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 3276D4069F;\n\tFri,  6 Oct 2023 01:07:06 +0200 (CEST)",
            "from mail-pf1-f179.google.com (mail-pf1-f179.google.com\n [209.85.210.179])\n by mails.dpdk.org (Postfix) with ESMTP id 4CEDD402EE\n for <dev@dpdk.org>; Fri,  6 Oct 2023 01:07:04 +0200 (CEST)",
            "by mail-pf1-f179.google.com with SMTP id\n d2e1a72fcca58-694ed84c981so1266710b3a.3\n for <dev@dpdk.org>; Thu, 05 Oct 2023 16:07:04 -0700 (PDT)",
            "from hermes.local (204-195-126-68.wavecable.com. [204.195.126.68])\n by smtp.gmail.com with ESMTPSA id\n d13-20020aa7814d000000b00690cd981652sm112665pfn.61.2023.10.05.16.07.02\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 05 Oct 2023 16:07:02 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1696547223;\n x=1697152023; darn=dpdk.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=VMaKyI0IENDGadTP+YUwf+x9YiYroLfz1rbVGhjhuJk=;\n b=KOaX415tDt1rvgbQ1AHh6gW+ymjfbJWZD2/e72Afjt9Iu8NS88aTo7J29hXi0hjNMf\n +iwaLiPVtacKjqbDJafDcuKywxTVZK/w1BDt5nVEXo8pLQ3l7f4N7LzNvvADeIVwBPTE\n 6e+JDNpBtgqiXUgFGbGA5GjaMaHIEnGBN3relXEcnAw4Uf6Wlp6+uzvuscL7HUYgrSGZ\n gBgQoe7w4JDEYETvJzaDMwWzx8+0DiDM1ZqFxfl6JhwxQvNbsDHF2DLqnW73sxdTlz0v\n 1F9/37TroVU0d9OoRi62/xnPRqdCtw+BrIkyDtu2n3kePfAhV+ij1BAffKbtRZwIAyRW\n jm5w==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1696547223; x=1697152023;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=VMaKyI0IENDGadTP+YUwf+x9YiYroLfz1rbVGhjhuJk=;\n b=ci8KneFYXzLBt8g9EPfZkKHjIareHZO+As1esbpNyQTGzbCulCLxfuXyv/DEp2IIV1\n +/1HXj5+QJUPcPB6UfB7s9ymj/xeR9eoD+hvydvK1dIv0wRmYSW0jSyE41nerCiD208x\n 99WiAz5M5A8IIRbcGac8uZk8HQ6t1qQLPOuvU1AFU7NhVkHUTIYBQCBWkmnS41En9QvI\n xs9m1/uv/dExZ/0WvY/sf7Ql+wXnZfJEMr8P3LPHY+SCPxZiTStU65ko/vPZXWV1Bpc+\n CEFYRz4OwhcnYQJk/Jvr4u+pVvEERdbGPbhtTvts0G8DpPW2WMmeZ0FBjZsH/6TDCUTZ\n eiZA==",
        "X-Gm-Message-State": "AOJu0Yy7bSIiByU0hjCAhq63Zztt6PsxiRcwKiFWciTeMab2LLezbWWY\n OZhAtfnKmKkuog5KefXMxBdvBFDvt3XhiAbDn3Y=",
        "X-Google-Smtp-Source": "\n AGHT+IECoaPaYPd9wzZXw7EpTC31Eo9yCuTxDvyiaJ0Cx3QLhPRrRK+HpNWubfNPwQI0SZKoPEFHGQ==",
        "X-Received": "by 2002:a05:6a20:1004:b0:14c:628b:4b98 with SMTP id\n gs4-20020a056a20100400b0014c628b4b98mr6014359pzc.4.1696547223118;\n Thu, 05 Oct 2023 16:07:03 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <stephen@networkplumber.org>,\n Reshma Pattan <reshma.pattan@intel.com>",
        "Subject": "[PATCH v2 4/4] test: cleanups to pcapng test",
        "Date": "Thu,  5 Oct 2023 16:06:48 -0700",
        "Message-Id": "<20231005230648.68244-5-stephen@networkplumber.org>",
        "X-Mailer": "git-send-email 2.39.2",
        "In-Reply-To": "<20231005230648.68244-1-stephen@networkplumber.org>",
        "References": "<20230921042349.104150-1-stephen@networkplumber.org>\n <20231005230648.68244-1-stephen@networkplumber.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Overhaul of the pcapng test:\n  - promote it to be a fast test so it gets regularly run.\n  - create null device and use i.\n  - use UDP discard packets that are valid so that for debugging\n    the resulting pcapng file can be looked at with wireshark.\n  - do basic checks on resulting pcap file that lengths and\n    timestamps are in range.\n\nSigned-off-by: Stephen Hemminger <stephen@networkplumber.org>\n---\n app/test/meson.build   |   2 +-\n app/test/test_pcapng.c | 378 ++++++++++++++++++++++++++---------------\n 2 files changed, 242 insertions(+), 138 deletions(-)",
    "diff": "diff --git a/app/test/meson.build b/app/test/meson.build\nindex bf9fc906128f..81d7c41a07cb 100644\n--- a/app/test/meson.build\n+++ b/app/test/meson.build\n@@ -124,7 +124,7 @@ source_file_deps = {\n     'test_meter.c': ['meter'],\n     'test_metrics.c': ['metrics'],\n     'test_mp_secondary.c': ['hash', 'lpm'],\n-    'test_pcapng.c': ['ethdev', 'net', 'pcapng'],\n+    'test_pcapng.c': ['ethdev', 'net', 'pcapng', 'bus_vdev'],\n     'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'],\n     'test_pdump.c': ['pdump'] + sample_packet_forward_deps,\n     'test_per_lcore.c': [],\ndiff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c\nindex 55aa2cf93666..45223ef38240 100644\n--- a/app/test/test_pcapng.c\n+++ b/app/test/test_pcapng.c\n@@ -6,25 +6,34 @@\n #include <stdlib.h>\n #include <unistd.h>\n \n+#include <rte_bus_vdev.h>\n #include <rte_ethdev.h>\n #include <rte_ether.h>\n+#include <rte_ip.h>\n #include <rte_mbuf.h>\n #include <rte_mempool.h>\n #include <rte_net.h>\n #include <rte_pcapng.h>\n+#include <rte_random.h>\n+#include <rte_reciprocal.h>\n+#include <rte_time.h>\n+#include <rte_udp.h>\n \n #include <pcap/pcap.h>\n \n #include \"test.h\"\n \n-#define NUM_PACKETS    10\n-#define DUMMY_MBUF_NUM 3\n+#define PCAPNG_TEST_DEBUG 0\n+\n+#define TOTAL_PACKETS\t4096\n+#define MAX_BURST\t64\n+#define MAX_GAP_US\t100000\n+#define DUMMY_MBUF_NUM\t3\n \n-static rte_pcapng_t *pcapng;\n static struct rte_mempool *mp;\n static const uint32_t pkt_len = 200;\n static uint16_t port_id;\n-static char file_name[] = \"/tmp/pcapng_test_XXXXXX.pcapng\";\n+static const char null_dev[] = \"net_null0\";\n \n /* first mbuf in the packet, should always be at offset 0 */\n struct dummy_mbuf {\n@@ -61,6 +70,7 @@ mbuf1_prepare(struct dummy_mbuf *dm, uint32_t plen)\n \tstruct {\n \t\tstruct rte_ether_hdr eth;\n \t\tstruct rte_ipv4_hdr ip;\n+\t\tstruct rte_udp_hdr udp;\n \t} pkt = {\n \t\t.eth = {\n \t\t\t.dst_addr.addr_bytes = \"\\xff\\xff\\xff\\xff\\xff\\xff\",\n@@ -68,149 +78,226 @@ mbuf1_prepare(struct dummy_mbuf *dm, uint32_t plen)\n \t\t},\n \t\t.ip = {\n \t\t\t.version_ihl = RTE_IPV4_VHL_DEF,\n-\t\t\t.total_length = rte_cpu_to_be_16(plen),\n-\t\t\t.time_to_live = IPDEFTTL,\n-\t\t\t.next_proto_id = IPPROTO_RAW,\n+\t\t\t.time_to_live = 1,\n+\t\t\t.next_proto_id = IPPROTO_UDP,\n \t\t\t.src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK),\n \t\t\t.dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),\n-\t\t}\n+\t\t},\n+\t\t.udp = {\n+\t\t\t.dst_port = rte_cpu_to_be_16(9), /* Discard port */\n+\t\t},\n \t};\n \n \tmemset(dm, 0, sizeof(*dm));\n \tdummy_mbuf_prep(&dm->mb[0], dm->buf[0], sizeof(dm->buf[0]), plen);\n \n \trte_eth_random_addr(pkt.eth.src_addr.addr_bytes);\n-\tmemcpy(rte_pktmbuf_mtod(dm->mb, void *), &pkt, RTE_MIN(sizeof(pkt), plen));\n+\tplen -= sizeof(struct rte_ether_hdr);\n+\n+\tpkt.ip.total_length = rte_cpu_to_be_16(plen);\n+\tpkt.ip.hdr_checksum = rte_ipv4_cksum(&pkt.ip);\n+\n+\tplen -= sizeof(struct rte_ipv4_hdr);\n+\tpkt.udp.src_port = rte_rand();\n+\tpkt.udp.dgram_len = rte_cpu_to_be_16(plen);\n+\n+\tmemcpy(rte_pktmbuf_mtod(dm->mb, void *), &pkt, sizeof(pkt));\n }\n \n-static int\n-test_setup(void)\n+/*\n+ * Make a timestamp value as used by PCAPNG file format\n+ * The library uses nanosecond time resolution so this is\n+ * time elapsed since 1970-01-01 00:00:00 UTC.\n+ *\n+ * Use the same way of calculating as in pdump library.\n+ */\n+static struct {\n+\tuint64_t offset_ns;\t/* ns since 1/1/1970 when initialized */\n+\tuint64_t tsc_base;\t/* TSC when initialized */\n+\tuint64_t tsc_hz;\t/* copy of rte_tsc_hz() */\n+\tstruct rte_reciprocal_u64 tsc_hz_inverse; /* inverse of tsc_hz */\n+} time_base;\n+\n+static void timestamp_init(void)\n {\n-\tint tmp_fd;\n+\tstruct timespec ts;\n+\tuint64_t cycles;\n \n-\tport_id = rte_eth_find_next(0);\n-\tif (port_id >= RTE_MAX_ETHPORTS) {\n-\t\tfprintf(stderr, \"No valid Ether port\\n\");\n-\t\treturn -1;\n-\t}\n+\t/* Compute time base offsets */\n+\tcycles = rte_get_tsc_cycles();\n+\tclock_gettime(CLOCK_REALTIME, &ts);\n \n-\ttmp_fd = mkstemps(file_name, strlen(\".pcapng\"));\n-\tif (tmp_fd == -1) {\n-\t\tperror(\"mkstemps() failure\");\n-\t\treturn -1;\n-\t}\n-\tprintf(\"pcapng: output file %s\\n\", file_name);\n+\t/* put initial TSC value in middle of clock_gettime() call */\n+\ttime_base.tsc_base = (cycles + rte_get_tsc_cycles()) / 2;\n+\ttime_base.offset_ns = rte_timespec_to_ns(&ts);\n \n-\t/* open a test capture file */\n-\tpcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, \"pcapng_test\", NULL);\n-\tif (pcapng == NULL) {\n-\t\tfprintf(stderr, \"rte_pcapng_fdopen failed\\n\");\n-\t\tclose(tmp_fd);\n-\t\treturn -1;\n-\t}\n+\ttime_base.tsc_hz = rte_get_tsc_hz();\n+\ttime_base.tsc_hz_inverse = rte_reciprocal_value_u64(time_base.tsc_hz);\n+}\n \n-\t/* Add interface to the file */\n-\tif (rte_pcapng_add_interface(pcapng, port_id,\n-\t\t\t\t     NULL, NULL, NULL) != 0) {\n-\t\tfprintf(stderr, \"can not add port %u\\n\", port_id);\n-\t\treturn -1;\n+static int\n+test_setup(void)\n+{\n+\tport_id = rte_eth_dev_count_avail();\n+\n+\t/* Make a dummy null device to snoop on */\n+\tif (rte_vdev_init(null_dev, NULL) != 0) {\n+\t\tfprintf(stderr, \"Failed to create vdev '%s'\\n\", null_dev);\n+\t\tgoto fail;\n \t}\n \n \t/* Make a pool for cloned packets */\n-\tmp = rte_pktmbuf_pool_create_by_ops(\"pcapng_test_pool\", IOV_MAX + NUM_PACKETS,\n-\t\t\t\t\t    0, 0,\n-\t\t\t\t\t    rte_pcapng_mbuf_size(pkt_len),\n+\tmp = rte_pktmbuf_pool_create_by_ops(\"pcapng_test_pool\",\n+\t\t\t\t\t    MAX_BURST, 0, 0,\n+\t\t\t\t\t    rte_pcapng_mbuf_size(pkt_len) + 128,\n \t\t\t\t\t    SOCKET_ID_ANY, \"ring_mp_sc\");\n \tif (mp == NULL) {\n \t\tfprintf(stderr, \"Cannot create mempool\\n\");\n-\t\treturn -1;\n+\t\tgoto fail;\n \t}\n+\n+\ttimestamp_init();\n \treturn 0;\n+\n+fail:\n+\trte_vdev_uninit(null_dev);\n+\trte_mempool_free(mp);\n+\treturn -1;\n }\n \n static int\n-test_write_packets(void)\n+fill_pcapng_file(rte_pcapng_t *pcapng, unsigned int num_packets)\n {\n-\tstruct rte_mbuf *orig;\n-\tstruct rte_mbuf *clones[NUM_PACKETS] = { };\n \tstruct dummy_mbuf mbfs;\n-\tunsigned int i;\n+\tstruct rte_mbuf *orig;\n+\tunsigned int burst_size;\n+\tunsigned int count;\n \tssize_t len;\n \n \t/* make a dummy packet */\n \tmbuf1_prepare(&mbfs, pkt_len);\n-\n-\t/* clone them */\n \torig  = &mbfs.mb[0];\n-\tfor (i = 0; i < NUM_PACKETS; i++) {\n-\t\tstruct rte_mbuf *mc;\n \n-\t\tmc = rte_pcapng_copy(port_id, 0, orig, mp, pkt_len,\n-\t\t\t\trte_get_tsc_cycles(), 0, NULL);\n-\t\tif (mc == NULL) {\n-\t\t\tfprintf(stderr, \"Cannot copy packet\\n\");\n+\tfor (count = 0; count < num_packets; count += burst_size) {\n+\t\tstruct rte_mbuf *clones[MAX_BURST];\n+\t\tunsigned int i;\n+\n+\t\t/* put 1 .. MAX_BURST packets in one write call */\n+\t\tburst_size = rte_rand_max(MAX_BURST) + 1;\n+\t\tfor (i = 0; i < burst_size; i++) {\n+\t\t\tstruct rte_mbuf *mc;\n+\n+\t\t\tmc = rte_pcapng_copy(port_id, 0, orig, mp, pkt_len,\n+\t\t\t\t\t     RTE_PCAPNG_DIRECTION_IN,\n+\t\t\t\t\t     NULL);\n+\t\t\tif (mc == NULL) {\n+\t\t\t\tfprintf(stderr, \"Cannot copy packet\\n\");\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tclones[i] = mc;\n+\t\t}\n+\n+\t\t/* write it to capture file */\n+\t\tlen = rte_pcapng_write_packets(pcapng, clones, burst_size);\n+\t\trte_pktmbuf_free_bulk(clones, burst_size);\n+\n+\t\tif (len <= 0) {\n+\t\t\tfprintf(stderr, \"Write of packets failed: %s\\n\",\n+\t\t\t\trte_strerror(rte_errno));\n \t\t\treturn -1;\n \t\t}\n-\t\tclones[i] = mc;\n+\n+\t\t/* Leave a small gap between packets to test for time wrap */\n+\t\tusleep(rte_rand_max(MAX_GAP_US));\n \t}\n \n-\t/* write it to capture file */\n-\tlen = rte_pcapng_write_packets(pcapng, clones, NUM_PACKETS);\n+\treturn count;\n+}\n \n-\trte_pktmbuf_free_bulk(clones, NUM_PACKETS);\n+static char *\n+fmt_time(char *buf, size_t size, uint64_t ts_ns)\n+{\n+\ttime_t sec;\n+\tsize_t len;\n \n-\tif (len <= 0) {\n-\t\tfprintf(stderr, \"Write of packets failed\\n\");\n-\t\treturn -1;\n-\t}\n+\tsec = ts_ns / NS_PER_S;\n+\tlen = strftime(buf, size, \"%X\", localtime(&sec));\n+\tsnprintf(buf + len, size - len, \".%09lu\",\n+\t\t (unsigned long)(ts_ns % NS_PER_S));\n \n-\treturn 0;\n+\treturn buf;\n }\n \n-static int\n-test_write_stats(void)\n+/* Context for the pcap_loop callback */\n+struct pkt_print_ctx {\n+\tpcap_t *pcap;\n+\tunsigned int count;\n+};\n+\n+static void\n+print_packet(uint64_t ts_ns, const struct rte_ether_hdr *eh, size_t len)\n {\n-\tssize_t len;\n+\tchar tbuf[128], src[64], dst[64];\n \n-\t/* write a statistics block */\n-\tlen = rte_pcapng_write_stats(pcapng, port_id, NULL,\n-\t\t\t\t     0, 0, 0,\n-\t\t\t\t     NUM_PACKETS, 0);\n-\tif (len <= 0) {\n-\t\tfprintf(stderr, \"Write of statistics failed\\n\");\n-\t\treturn -1;\n-\t}\n-\treturn 0;\n+\tfmt_time(tbuf, sizeof(tbuf), ts_ns);\n+\trte_ether_format_addr(dst, sizeof(dst), &eh->dst_addr);\n+\trte_ether_format_addr(src, sizeof(src), &eh->src_addr);\n+\tprintf(\"%s: %s -> %s type %x length %zu\\n\",\n+\t       tbuf, src, dst, rte_be_to_cpu_16(eh->ether_type), len);\n }\n \n+/* Callback from pcap_loop used to validate packets in the file */\n static void\n-pkt_print(u_char *user, const struct pcap_pkthdr *h,\n-\t  const u_char *bytes)\n+parse_pcap_packet(u_char *user, const struct pcap_pkthdr *h,\n+\t\t  const u_char *bytes)\n {\n-\tunsigned int *countp = (unsigned int *)user;\n+\tstruct pkt_print_ctx *ctx = (struct pkt_print_ctx *)user;\n \tconst struct rte_ether_hdr *eh;\n-\tstruct tm *tm;\n-\tchar tbuf[128], src[64], dst[64];\n+\tconst struct rte_ipv4_hdr *ip;\n+\tstruct timespec ts;\n+\tuint64_t ns, now;\n \n-\ttm = localtime(&h->ts.tv_sec);\n-\tif (tm == NULL) {\n-\t\tperror(\"localtime\");\n-\t\treturn;\n+\teh = (const struct rte_ether_hdr *)bytes;\n+\tip = (const struct rte_ipv4_hdr *)(eh + 1);\n+\n+\tctx->count += 1;\n+\n+\tclock_gettime(CLOCK_REALTIME, &ts);\n+\tnow = rte_timespec_to_ns(&ts);\n+\n+\t/* The pcap library is misleading in reporting timestamp.\n+\t * packet header struct gives timestamp as a timeval (ie. usec);\n+\t * but the file is open in nanonsecond mode therefore\n+\t * the timestamp is really in timespec (ie. nanoseconds).\n+\t */\n+\tns = h->ts.tv_sec * NS_PER_S + h->ts.tv_usec;\n+\tif (ns < time_base.offset_ns || ns > now) {\n+\t\tchar tstart[128], tend[128];\n+\n+\t\tfmt_time(tstart, sizeof(tstart), time_base.offset_ns);\n+\t\tfmt_time(tend, sizeof(tend), now);\n+\t\tfprintf(stderr, \"Timestamp out of range [%s .. %s]\\n\",\n+\t\t\ttstart, tend);\n+\t\tgoto error;\n \t}\n \n-\tif (strftime(tbuf, sizeof(tbuf), \"%X\", tm) == 0) {\n-\t\tfprintf(stderr, \"strftime returned 0!\\n\");\n-\t\treturn;\n+\tif (!rte_is_broadcast_ether_addr(&eh->dst_addr)) {\n+\t\tfprintf(stderr, \"Destination is not broadcast\\n\");\n+\t\tgoto error;\n \t}\n \n-\teh = (const struct rte_ether_hdr *)bytes;\n-\trte_ether_format_addr(dst, sizeof(dst), &eh->dst_addr);\n-\trte_ether_format_addr(src, sizeof(src), &eh->src_addr);\n-\tprintf(\"%s.%06lu: %s -> %s type %x length %u\\n\",\n-\t       tbuf, (unsigned long)h->ts.tv_usec,\n-\t       src, dst, rte_be_to_cpu_16(eh->ether_type), h->len);\n+\tif (rte_ipv4_cksum(ip) != 0) {\n+\t\tfprintf(stderr, \"Bad IPv4 checksum\\n\");\n+\t\tgoto error;\n+\t}\n+\n+\treturn;\t\t/* packet is normal */\n \n-\t*countp += 1;\n+error:\n+\tprint_packet(ns, eh, h->len);\n+\n+\t/* Stop parsing at first error */\n+\tpcap_breakloop(ctx->pcap);\n }\n \n /*\n@@ -219,78 +306,98 @@ pkt_print(u_char *user, const struct pcap_pkthdr *h,\n  * but that creates an unwanted dependency.\n  */\n static int\n-test_validate(void)\n+valid_pcapng_file(const char *file_name, unsigned int expected)\n {\n \tchar errbuf[PCAP_ERRBUF_SIZE];\n-\tunsigned int count = 0;\n-\tpcap_t *pcap;\n+\tstruct pkt_print_ctx ctx;\n \tint ret;\n \n-\tpcap = pcap_open_offline(file_name, errbuf);\n-\tif (pcap == NULL) {\n+\tctx.count = 0;\n+\tctx.pcap = pcap_open_offline_with_tstamp_precision(file_name,\n+\t\t\t\t\t\t\t   PCAP_TSTAMP_PRECISION_NANO,\n+\t\t\t\t\t\t\t   errbuf);\n+\tif (ctx.pcap == NULL) {\n \t\tfprintf(stderr, \"pcap_open_offline('%s') failed: %s\\n\",\n \t\t\tfile_name, errbuf);\n \t\treturn -1;\n \t}\n \n-\tret = pcap_loop(pcap, 0, pkt_print, (u_char *)&count);\n-\tif (ret == 0)\n-\t\tprintf(\"Saw %u packets\\n\", count);\n-\telse\n+\tret = pcap_loop(ctx.pcap, 0, parse_pcap_packet, (u_char *)&ctx);\n+\tif (ret != 0) {\n \t\tfprintf(stderr, \"pcap_dispatch: failed: %s\\n\",\n-\t\t\tpcap_geterr(pcap));\n-\tpcap_close(pcap);\n+\t\t\tpcap_geterr(ctx.pcap));\n+\t} else if (ctx.count != expected) {\n+\t\tprintf(\"Only %u packets, expected %u\\n\",\n+\t\t       ctx.count, expected);\n+\t\tret = -1;\n+\t}\n+\n+\tpcap_close(ctx.pcap);\n \n \treturn ret;\n }\n \n static int\n-test_write_over_limit_iov_max(void)\n+test_write_packets(void)\n {\n-\tstruct rte_mbuf *orig;\n-\tstruct rte_mbuf *clones[IOV_MAX + NUM_PACKETS] = { };\n-\tstruct dummy_mbuf mbfs;\n-\tunsigned int i;\n-\tssize_t len;\n-\n-\t/* make a dummy packet */\n-\tmbuf1_prepare(&mbfs, pkt_len);\n+\tchar file_name[] = \"/tmp/pcapng_test_XXXXXX.pcapng\";\n+\tstatic rte_pcapng_t *pcapng;\n+\tint ret, tmp_fd, count;\n \n-\t/* clone them */\n-\torig  = &mbfs.mb[0];\n-\tfor (i = 0; i < IOV_MAX + NUM_PACKETS; i++) {\n-\t\tstruct rte_mbuf *mc;\n+\ttmp_fd = mkstemps(file_name, strlen(\".pcapng\"));\n+\tif (tmp_fd == -1) {\n+\t\tperror(\"mkstemps() failure\");\n+\t\tgoto fail;\n+\t}\n+\tprintf(\"pcapng: output file %s\\n\", file_name);\n \n-\t\tmc = rte_pcapng_copy(port_id, 0, orig, mp, pkt_len,\n-\t\t\t\trte_get_tsc_cycles(), 0, NULL);\n-\t\tif (mc == NULL) {\n-\t\t\tfprintf(stderr, \"Cannot copy packet\\n\");\n-\t\t\treturn -1;\n-\t\t}\n-\t\tclones[i] = mc;\n+\t/* open a test capture file */\n+\tpcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, \"pcapng_test\", NULL);\n+\tif (pcapng == NULL) {\n+\t\tfprintf(stderr, \"rte_pcapng_fdopen failed\\n\");\n+\t\tclose(tmp_fd);\n+\t\tgoto fail;\n \t}\n \n-\t/* write it to capture file */\n-\tlen = rte_pcapng_write_packets(pcapng, clones, IOV_MAX + NUM_PACKETS);\n+\t/* Add interface to the file */\n+\tret = rte_pcapng_add_interface(pcapng, port_id,\n+\t\t\t\t       NULL, NULL, NULL);\n+\tif (ret < 0) {\n+\t\tfprintf(stderr, \"can not add port %u\\n\", port_id);\n+\t\tgoto fail;\n+\t}\n \n-\trte_pktmbuf_free_bulk(clones, IOV_MAX + NUM_PACKETS);\n+\tcount = fill_pcapng_file(pcapng, TOTAL_PACKETS);\n+\tif (count < 0)\n+\t\tgoto fail;\n \n-\tif (len <= 0) {\n-\t\tfprintf(stderr, \"Write of packets failed\\n\");\n-\t\treturn -1;\n+\t/* write a statistics block */\n+\tret = rte_pcapng_write_stats(pcapng, port_id,\n+\t\t\t\t     count, 0, \"end of test\");\n+\tif (ret <= 0) {\n+\t\tfprintf(stderr, \"Write of statistics failed\\n\");\n+\t\tgoto fail;\n \t}\n \n-\treturn 0;\n+\trte_pcapng_close(pcapng);\n+\n+\tret = valid_pcapng_file(file_name, count);\n+\t/* if test fails want to investigate the file */\n+\tif (ret == 0)\n+\t\tunlink(file_name);\n+\n+\treturn ret;\n+\n+fail:\n+\trte_pcapng_close(pcapng);\n+\treturn -1;\n }\n \n static void\n test_cleanup(void)\n {\n \trte_mempool_free(mp);\n-\n-\tif (pcapng)\n-\t\trte_pcapng_close(pcapng);\n-\n+\trte_vdev_uninit(null_dev);\n }\n \n static struct\n@@ -300,9 +407,6 @@ unit_test_suite test_pcapng_suite  = {\n \t.suite_name = \"Test Pcapng Unit Test Suite\",\n \t.unit_test_cases = {\n \t\tTEST_CASE(test_write_packets),\n-\t\tTEST_CASE(test_write_stats),\n-\t\tTEST_CASE(test_validate),\n-\t\tTEST_CASE(test_write_over_limit_iov_max),\n \t\tTEST_CASES_END()\n \t}\n };\n@@ -313,4 +417,4 @@ test_pcapng(void)\n \treturn unit_test_suite_runner(&test_pcapng_suite);\n }\n \n-REGISTER_TEST_COMMAND(pcapng_autotest, test_pcapng);\n+REGISTER_FAST_TEST(pcapng_autotest, true, true, test_pcapng);\n",
    "prefixes": [
        "v2",
        "4/4"
    ]
}