xref: /dpdk/lib/pcapng/rte_pcapng.c (revision d2e3c4b8a20b5040219e6be0938a1752bd3b47be)
18d23ce8fSStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause
28d23ce8fSStephen Hemminger  * Copyright(c) 2019 Microsoft Corporation
38d23ce8fSStephen Hemminger  */
48d23ce8fSStephen Hemminger 
58d23ce8fSStephen Hemminger #include <errno.h>
68d23ce8fSStephen Hemminger #include <net/if.h>
7*d2e3c4b8SStephen Hemminger #include <stdbool.h>
88d23ce8fSStephen Hemminger #include <stdio.h>
98d23ce8fSStephen Hemminger #include <stdlib.h>
108d23ce8fSStephen Hemminger #include <string.h>
118d23ce8fSStephen Hemminger #include <sys/uio.h>
128d23ce8fSStephen Hemminger #include <time.h>
138d23ce8fSStephen Hemminger #include <unistd.h>
148d23ce8fSStephen Hemminger 
15a04322f6SDavid Marchand #include <bus_driver.h>
168d23ce8fSStephen Hemminger #include <rte_common.h>
178d23ce8fSStephen Hemminger #include <rte_cycles.h>
181acb7f54SDavid Marchand #include <dev_driver.h>
198d23ce8fSStephen Hemminger #include <rte_errno.h>
208d23ce8fSStephen Hemminger #include <rte_ethdev.h>
218d23ce8fSStephen Hemminger #include <rte_ether.h>
228d23ce8fSStephen Hemminger #include <rte_mbuf.h>
238d23ce8fSStephen Hemminger #include <rte_pcapng.h>
24c882eb54SQuentin Armitage #include <rte_reciprocal.h>
258d23ce8fSStephen Hemminger #include <rte_time.h>
268d23ce8fSStephen Hemminger 
278d23ce8fSStephen Hemminger #include "pcapng_proto.h"
288d23ce8fSStephen Hemminger 
298d23ce8fSStephen Hemminger /* conversion from DPDK speed to PCAPNG */
308d23ce8fSStephen Hemminger #define PCAPNG_MBPS_SPEED 1000000ull
318d23ce8fSStephen Hemminger 
328d23ce8fSStephen Hemminger /* Format of the capture file handle */
338d23ce8fSStephen Hemminger struct rte_pcapng {
348d23ce8fSStephen Hemminger 	int  outfd;		/* output file */
358d23ce8fSStephen Hemminger 	/* DPDK port id to interface index in file */
368d23ce8fSStephen Hemminger 	uint32_t port_index[RTE_MAX_ETHPORTS];
378d23ce8fSStephen Hemminger };
388d23ce8fSStephen Hemminger 
398d23ce8fSStephen Hemminger /* For converting TSC cycles to PCAPNG ns format */
40c882eb54SQuentin Armitage static struct pcapng_time {
418d23ce8fSStephen Hemminger 	uint64_t ns;
428d23ce8fSStephen Hemminger 	uint64_t cycles;
43c882eb54SQuentin Armitage 	uint64_t tsc_hz;
44c882eb54SQuentin Armitage 	struct rte_reciprocal_u64 tsc_hz_inverse;
458d23ce8fSStephen Hemminger } pcapng_time;
468d23ce8fSStephen Hemminger 
47c882eb54SQuentin Armitage static inline void
48c882eb54SQuentin Armitage pcapng_init(void)
498d23ce8fSStephen Hemminger {
508d23ce8fSStephen Hemminger 	struct timespec ts;
518d23ce8fSStephen Hemminger 
528d23ce8fSStephen Hemminger 	pcapng_time.cycles = rte_get_tsc_cycles();
538d23ce8fSStephen Hemminger 	clock_gettime(CLOCK_REALTIME, &ts);
54c882eb54SQuentin Armitage 	pcapng_time.cycles = (pcapng_time.cycles + rte_get_tsc_cycles()) / 2;
558d23ce8fSStephen Hemminger 	pcapng_time.ns = rte_timespec_to_ns(&ts);
56c882eb54SQuentin Armitage 
57c882eb54SQuentin Armitage 	pcapng_time.tsc_hz = rte_get_tsc_hz();
58c882eb54SQuentin Armitage 	pcapng_time.tsc_hz_inverse = rte_reciprocal_value_u64(pcapng_time.tsc_hz);
598d23ce8fSStephen Hemminger }
608d23ce8fSStephen Hemminger 
618d23ce8fSStephen Hemminger /* PCAPNG timestamps are in nanoseconds */
628d23ce8fSStephen Hemminger static uint64_t pcapng_tsc_to_ns(uint64_t cycles)
638d23ce8fSStephen Hemminger {
64c882eb54SQuentin Armitage 	uint64_t delta, secs;
658d23ce8fSStephen Hemminger 
66c882eb54SQuentin Armitage 	if (!pcapng_time.tsc_hz)
67c882eb54SQuentin Armitage 		pcapng_init();
68c882eb54SQuentin Armitage 
69c882eb54SQuentin Armitage 	/* In essence the calculation is:
70c882eb54SQuentin Armitage 	 *   delta = (cycles - pcapng_time.cycles) * NSEC_PRE_SEC / rte_get_tsc_hz()
71c882eb54SQuentin Armitage 	 * but this overflows within 4 to 8 seconds depending on TSC frequency.
72c882eb54SQuentin Armitage 	 * Instead, if delta >= pcapng_time.tsc_hz:
73c882eb54SQuentin Armitage 	 *   Increase pcapng_time.ns and pcapng_time.cycles by the number of
74c882eb54SQuentin Armitage 	 *   whole seconds in delta and reduce delta accordingly.
75c882eb54SQuentin Armitage 	 * delta will therefore always lie in the interval [0, pcapng_time.tsc_hz),
76c882eb54SQuentin Armitage 	 * which will not overflow when multiplied by NSEC_PER_SEC provided the
77c882eb54SQuentin Armitage 	 * TSC frequency < approx 18.4GHz.
78c882eb54SQuentin Armitage 	 *
79c882eb54SQuentin Armitage 	 * Currently all TSCs operate below 5GHz.
80c882eb54SQuentin Armitage 	 */
818d23ce8fSStephen Hemminger 	delta = cycles - pcapng_time.cycles;
82c882eb54SQuentin Armitage 	if (unlikely(delta >= pcapng_time.tsc_hz)) {
83c882eb54SQuentin Armitage 		if (likely(delta < pcapng_time.tsc_hz * 2)) {
84c882eb54SQuentin Armitage 			delta -= pcapng_time.tsc_hz;
85c882eb54SQuentin Armitage 			pcapng_time.cycles += pcapng_time.tsc_hz;
86c882eb54SQuentin Armitage 			pcapng_time.ns += NSEC_PER_SEC;
87c882eb54SQuentin Armitage 		} else {
88c882eb54SQuentin Armitage 			secs = rte_reciprocal_divide_u64(delta, &pcapng_time.tsc_hz_inverse);
89c882eb54SQuentin Armitage 			delta -= secs * pcapng_time.tsc_hz;
90c882eb54SQuentin Armitage 			pcapng_time.cycles += secs * pcapng_time.tsc_hz;
91c882eb54SQuentin Armitage 			pcapng_time.ns += secs * NSEC_PER_SEC;
92c882eb54SQuentin Armitage 		}
93c882eb54SQuentin Armitage 	}
94c882eb54SQuentin Armitage 
95c882eb54SQuentin Armitage 	return pcapng_time.ns + rte_reciprocal_divide_u64(delta * NSEC_PER_SEC,
96c882eb54SQuentin Armitage 							  &pcapng_time.tsc_hz_inverse);
978d23ce8fSStephen Hemminger }
988d23ce8fSStephen Hemminger 
998d23ce8fSStephen Hemminger /* length of option including padding */
1008d23ce8fSStephen Hemminger static uint16_t pcapng_optlen(uint16_t len)
1018d23ce8fSStephen Hemminger {
1028d23ce8fSStephen Hemminger 	return RTE_ALIGN(sizeof(struct pcapng_option) + len,
1038d23ce8fSStephen Hemminger 			 sizeof(uint32_t));
1048d23ce8fSStephen Hemminger }
1058d23ce8fSStephen Hemminger 
1068d23ce8fSStephen Hemminger /* build TLV option and return location of next */
1078d23ce8fSStephen Hemminger static struct pcapng_option *
1088d23ce8fSStephen Hemminger pcapng_add_option(struct pcapng_option *popt, uint16_t code,
1098d23ce8fSStephen Hemminger 		  const void *data, uint16_t len)
1108d23ce8fSStephen Hemminger {
1118d23ce8fSStephen Hemminger 	popt->code = code;
1128d23ce8fSStephen Hemminger 	popt->length = len;
1138d23ce8fSStephen Hemminger 	memcpy(popt->data, data, len);
1148d23ce8fSStephen Hemminger 
1158d23ce8fSStephen Hemminger 	return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
1168d23ce8fSStephen Hemminger }
1178d23ce8fSStephen Hemminger 
1188d23ce8fSStephen Hemminger /*
1198d23ce8fSStephen Hemminger  * Write required initial section header describing the capture
1208d23ce8fSStephen Hemminger  */
1218d23ce8fSStephen Hemminger static int
1228d23ce8fSStephen Hemminger pcapng_section_block(rte_pcapng_t *self,
1238d23ce8fSStephen Hemminger 		    const char *os, const char *hw,
1248d23ce8fSStephen Hemminger 		    const char *app, const char *comment)
1258d23ce8fSStephen Hemminger {
1268d23ce8fSStephen Hemminger 	struct pcapng_section_header *hdr;
1278d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
1288d23ce8fSStephen Hemminger 	void *buf;
1298d23ce8fSStephen Hemminger 	uint32_t len;
1308d23ce8fSStephen Hemminger 	ssize_t cc;
1318d23ce8fSStephen Hemminger 
1328d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
1338d23ce8fSStephen Hemminger 	if (hw)
1348d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(hw));
1358d23ce8fSStephen Hemminger 	if (os)
1368d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(os));
1378d23ce8fSStephen Hemminger 	if (app)
1388d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(app));
1398d23ce8fSStephen Hemminger 	if (comment)
1408d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(comment));
1418d23ce8fSStephen Hemminger 
1428d23ce8fSStephen Hemminger 	/* reserve space for OPT_END */
1438d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
1448d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
1458d23ce8fSStephen Hemminger 
1468d23ce8fSStephen Hemminger 	buf = calloc(1, len);
1478d23ce8fSStephen Hemminger 	if (!buf)
1488d23ce8fSStephen Hemminger 		return -1;
1498d23ce8fSStephen Hemminger 
1508d23ce8fSStephen Hemminger 	hdr = (struct pcapng_section_header *)buf;
1518d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_section_header) {
1528d23ce8fSStephen Hemminger 		.block_type = PCAPNG_SECTION_BLOCK,
1538d23ce8fSStephen Hemminger 		.block_length = len,
1548d23ce8fSStephen Hemminger 		.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
1558d23ce8fSStephen Hemminger 		.major_version = PCAPNG_MAJOR_VERS,
1568d23ce8fSStephen Hemminger 		.minor_version = PCAPNG_MINOR_VERS,
1578d23ce8fSStephen Hemminger 		.section_length = UINT64_MAX,
1588d23ce8fSStephen Hemminger 	};
1598d23ce8fSStephen Hemminger 
1608d23ce8fSStephen Hemminger 	/* After the section header insert variable length options. */
1618d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
1628d23ce8fSStephen Hemminger 	if (comment)
1638d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
1648d23ce8fSStephen Hemminger 					comment, strlen(comment));
1658d23ce8fSStephen Hemminger 	if (hw)
1668d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
1678d23ce8fSStephen Hemminger 					hw, strlen(hw));
1688d23ce8fSStephen Hemminger 	if (os)
1698d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
1708d23ce8fSStephen Hemminger 					os, strlen(os));
1718d23ce8fSStephen Hemminger 	if (app)
1728d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
1738d23ce8fSStephen Hemminger 					app, strlen(app));
1748d23ce8fSStephen Hemminger 
1758d23ce8fSStephen Hemminger 	/* The standard requires last option to be OPT_END */
1768d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
1778d23ce8fSStephen Hemminger 
1788d23ce8fSStephen Hemminger 	/* clone block_length after option */
1798d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
1808d23ce8fSStephen Hemminger 
1818d23ce8fSStephen Hemminger 	cc = write(self->outfd, buf, len);
1828d23ce8fSStephen Hemminger 	free(buf);
1838d23ce8fSStephen Hemminger 
1848d23ce8fSStephen Hemminger 	return cc;
1858d23ce8fSStephen Hemminger }
1868d23ce8fSStephen Hemminger 
1878d23ce8fSStephen Hemminger /* Write an interface block for a DPDK port */
1888d23ce8fSStephen Hemminger static int
1898d23ce8fSStephen Hemminger pcapng_add_interface(rte_pcapng_t *self, uint16_t port)
1908d23ce8fSStephen Hemminger {
1918d23ce8fSStephen Hemminger 	struct pcapng_interface_block *hdr;
1928d23ce8fSStephen Hemminger 	struct rte_eth_dev_info dev_info;
1938d23ce8fSStephen Hemminger 	struct rte_ether_addr *ea, macaddr;
1948d23ce8fSStephen Hemminger 	const struct rte_device *dev;
1958d23ce8fSStephen Hemminger 	struct rte_eth_link link;
1968d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
1978d23ce8fSStephen Hemminger 	const uint8_t tsresol = 9;	/* nanosecond resolution */
1988d23ce8fSStephen Hemminger 	uint32_t len;
1998d23ce8fSStephen Hemminger 	void *buf;
2008d23ce8fSStephen Hemminger 	char ifname[IF_NAMESIZE];
2018d23ce8fSStephen Hemminger 	char ifhw[256];
2028d23ce8fSStephen Hemminger 	uint64_t speed = 0;
2038d23ce8fSStephen Hemminger 
2048d23ce8fSStephen Hemminger 	if (rte_eth_dev_info_get(port, &dev_info) < 0)
2058d23ce8fSStephen Hemminger 		return -1;
2068d23ce8fSStephen Hemminger 
2078d23ce8fSStephen Hemminger 	/* make something like an interface name */
2088d23ce8fSStephen Hemminger 	if (if_indextoname(dev_info.if_index, ifname) == NULL)
2098d23ce8fSStephen Hemminger 		snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port);
2108d23ce8fSStephen Hemminger 
2118d23ce8fSStephen Hemminger 	/* make a useful device hardware string */
2128d23ce8fSStephen Hemminger 	dev = dev_info.device;
2138d23ce8fSStephen Hemminger 	if (dev)
2148d23ce8fSStephen Hemminger 		snprintf(ifhw, sizeof(ifhw),
2158d23ce8fSStephen Hemminger 			 "%s-%s", dev->bus->name, dev->name);
2168d23ce8fSStephen Hemminger 
2178d23ce8fSStephen Hemminger 	/* DPDK reports in units of Mbps */
218442878dbSStephen Hemminger 	if (rte_eth_link_get(port, &link) == 0 &&
219442878dbSStephen Hemminger 	    link.link_status == RTE_ETH_LINK_UP)
2208d23ce8fSStephen Hemminger 		speed = link.link_speed * PCAPNG_MBPS_SPEED;
2218d23ce8fSStephen Hemminger 
2228d23ce8fSStephen Hemminger 	if (rte_eth_macaddr_get(port, &macaddr) < 0)
2238d23ce8fSStephen Hemminger 		ea = NULL;
2248d23ce8fSStephen Hemminger 	else
2258d23ce8fSStephen Hemminger 		ea = &macaddr;
2268d23ce8fSStephen Hemminger 
2278d23ce8fSStephen Hemminger 	/* Compute length of interface block options */
2288d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
2298d23ce8fSStephen Hemminger 
2308d23ce8fSStephen Hemminger 	len += pcapng_optlen(sizeof(tsresol));	/* timestamp */
2318d23ce8fSStephen Hemminger 	len += pcapng_optlen(strlen(ifname));	/* ifname */
2328d23ce8fSStephen Hemminger 
2338d23ce8fSStephen Hemminger 	if (ea)
2348d23ce8fSStephen Hemminger 		len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
2358d23ce8fSStephen Hemminger 	if (speed != 0)
2368d23ce8fSStephen Hemminger 		len += pcapng_optlen(sizeof(uint64_t));
2378d23ce8fSStephen Hemminger 	if (dev)
2388d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(ifhw));
2398d23ce8fSStephen Hemminger 
2408d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
2418d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
2428d23ce8fSStephen Hemminger 
2438d23ce8fSStephen Hemminger 	buf = alloca(len);
2448d23ce8fSStephen Hemminger 	if (!buf)
2458d23ce8fSStephen Hemminger 		return -1;
2468d23ce8fSStephen Hemminger 
2478d23ce8fSStephen Hemminger 	hdr = (struct pcapng_interface_block *)buf;
2488d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_interface_block) {
2498d23ce8fSStephen Hemminger 		.block_type = PCAPNG_INTERFACE_BLOCK,
2508d23ce8fSStephen Hemminger 		.link_type = 1,		/* DLT_EN10MB - Ethernet */
2518d23ce8fSStephen Hemminger 		.block_length = len,
2528d23ce8fSStephen Hemminger 	};
2538d23ce8fSStephen Hemminger 
2548d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
2558d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
2568d23ce8fSStephen Hemminger 				&tsresol, sizeof(tsresol));
2578d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
2588d23ce8fSStephen Hemminger 				ifname, strlen(ifname));
2598d23ce8fSStephen Hemminger 	if (ea)
2608d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
2618d23ce8fSStephen Hemminger 					ea, RTE_ETHER_ADDR_LEN);
2628d23ce8fSStephen Hemminger 	if (speed != 0)
2638d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
2648d23ce8fSStephen Hemminger 					 &speed, sizeof(uint64_t));
2658d23ce8fSStephen Hemminger 	if (dev)
2668d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
2678d23ce8fSStephen Hemminger 					 ifhw, strlen(ifhw));
2688d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
2698d23ce8fSStephen Hemminger 
2708d23ce8fSStephen Hemminger 	/* clone block_length after optionsa */
2718d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
2728d23ce8fSStephen Hemminger 
2738d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
2748d23ce8fSStephen Hemminger }
2758d23ce8fSStephen Hemminger 
2768d23ce8fSStephen Hemminger /*
2778d23ce8fSStephen Hemminger  * Write the list of possible interfaces at the start
2788d23ce8fSStephen Hemminger  * of the file.
2798d23ce8fSStephen Hemminger  */
2808d23ce8fSStephen Hemminger static int
2818d23ce8fSStephen Hemminger pcapng_interfaces(rte_pcapng_t *self)
2828d23ce8fSStephen Hemminger {
2838d23ce8fSStephen Hemminger 	uint16_t port_id;
2848d23ce8fSStephen Hemminger 	uint16_t index = 0;
2858d23ce8fSStephen Hemminger 
2868d23ce8fSStephen Hemminger 	RTE_ETH_FOREACH_DEV(port_id) {
2878d23ce8fSStephen Hemminger 		/* The list if ports in pcapng needs to be contiguous */
2888d23ce8fSStephen Hemminger 		self->port_index[port_id] = index++;
2898d23ce8fSStephen Hemminger 		if (pcapng_add_interface(self, port_id) < 0)
2908d23ce8fSStephen Hemminger 			return -1;
2918d23ce8fSStephen Hemminger 	}
2928d23ce8fSStephen Hemminger 	return 0;
2938d23ce8fSStephen Hemminger }
2948d23ce8fSStephen Hemminger 
2958d23ce8fSStephen Hemminger /*
2968d23ce8fSStephen Hemminger  * Write an Interface statistics block at the end of capture.
2978d23ce8fSStephen Hemminger  */
2988d23ce8fSStephen Hemminger ssize_t
2998d23ce8fSStephen Hemminger rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
3008d23ce8fSStephen Hemminger 		       const char *comment,
3018d23ce8fSStephen Hemminger 		       uint64_t start_time, uint64_t end_time,
3028d23ce8fSStephen Hemminger 		       uint64_t ifrecv, uint64_t ifdrop)
3038d23ce8fSStephen Hemminger {
3048d23ce8fSStephen Hemminger 	struct pcapng_statistics *hdr;
3058d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
3068d23ce8fSStephen Hemminger 	uint32_t optlen, len;
3078d23ce8fSStephen Hemminger 	uint8_t *buf;
3088d23ce8fSStephen Hemminger 	uint64_t ns;
3098d23ce8fSStephen Hemminger 
3108d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
3118d23ce8fSStephen Hemminger 
3128d23ce8fSStephen Hemminger 	optlen = 0;
3138d23ce8fSStephen Hemminger 
3148d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
3158d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifrecv));
3168d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
3178d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifdrop));
3188d23ce8fSStephen Hemminger 	if (start_time != 0)
3198d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(start_time));
3208d23ce8fSStephen Hemminger 	if (end_time != 0)
3218d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(end_time));
3228d23ce8fSStephen Hemminger 	if (comment)
3238d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(strlen(comment));
3248d23ce8fSStephen Hemminger 	if (optlen != 0)
3258d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(0);
3268d23ce8fSStephen Hemminger 
3278d23ce8fSStephen Hemminger 	len = sizeof(*hdr) + optlen + sizeof(uint32_t);
3288d23ce8fSStephen Hemminger 	buf = alloca(len);
3298d23ce8fSStephen Hemminger 	if (buf == NULL)
3308d23ce8fSStephen Hemminger 		return -1;
3318d23ce8fSStephen Hemminger 
3328d23ce8fSStephen Hemminger 	hdr = (struct pcapng_statistics *)buf;
3338d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
3348d23ce8fSStephen Hemminger 
3358d23ce8fSStephen Hemminger 	if (comment)
3368d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
3378d23ce8fSStephen Hemminger 					comment, strlen(comment));
3388d23ce8fSStephen Hemminger 	if (start_time != 0)
3398d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
3408d23ce8fSStephen Hemminger 					 &start_time, sizeof(start_time));
3418d23ce8fSStephen Hemminger 	if (end_time != 0)
3428d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_ENDTIME,
3438d23ce8fSStephen Hemminger 					 &end_time, sizeof(end_time));
3448d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
3458d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
3468d23ce8fSStephen Hemminger 				&ifrecv, sizeof(ifrecv));
3478d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
3488d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
3498d23ce8fSStephen Hemminger 				&ifdrop, sizeof(ifdrop));
3508d23ce8fSStephen Hemminger 	if (optlen != 0)
3518d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
3528d23ce8fSStephen Hemminger 
3538d23ce8fSStephen Hemminger 	hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
3548d23ce8fSStephen Hemminger 	hdr->block_length = len;
3558d23ce8fSStephen Hemminger 	hdr->interface_id = self->port_index[port_id];
3568d23ce8fSStephen Hemminger 
3578d23ce8fSStephen Hemminger 	ns = pcapng_tsc_to_ns(rte_get_tsc_cycles());
3588d23ce8fSStephen Hemminger 	hdr->timestamp_hi = ns >> 32;
3598d23ce8fSStephen Hemminger 	hdr->timestamp_lo = (uint32_t)ns;
3608d23ce8fSStephen Hemminger 
3618d23ce8fSStephen Hemminger 	/* clone block_length after option */
3628d23ce8fSStephen Hemminger 	memcpy(opt, &len, sizeof(uint32_t));
3638d23ce8fSStephen Hemminger 
3648d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
3658d23ce8fSStephen Hemminger }
3668d23ce8fSStephen Hemminger 
3678d23ce8fSStephen Hemminger uint32_t
3688d23ce8fSStephen Hemminger rte_pcapng_mbuf_size(uint32_t length)
3698d23ce8fSStephen Hemminger {
3708d23ce8fSStephen Hemminger 	/* The VLAN and EPB header must fit in the mbuf headroom. */
3718d23ce8fSStephen Hemminger 	RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
3728d23ce8fSStephen Hemminger 		   sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
3738d23ce8fSStephen Hemminger 
3748d23ce8fSStephen Hemminger 	/* The flags and queue information are added at the end. */
3758d23ce8fSStephen Hemminger 	return sizeof(struct rte_mbuf)
3768d23ce8fSStephen Hemminger 		+ RTE_ALIGN(length, sizeof(uint32_t))
3778d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* flag option */
3788d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* queue option */
3798d23ce8fSStephen Hemminger 		+ sizeof(uint32_t);		  /*  length */
3808d23ce8fSStephen Hemminger }
3818d23ce8fSStephen Hemminger 
3828d23ce8fSStephen Hemminger /* More generalized version rte_vlan_insert() */
3838d23ce8fSStephen Hemminger static int
3848d23ce8fSStephen Hemminger pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
3858d23ce8fSStephen Hemminger {
3868d23ce8fSStephen Hemminger 	struct rte_ether_hdr *nh, *oh;
3878d23ce8fSStephen Hemminger 	struct rte_vlan_hdr *vh;
3888d23ce8fSStephen Hemminger 
3898d23ce8fSStephen Hemminger 	if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
3908d23ce8fSStephen Hemminger 		return -EINVAL;
3918d23ce8fSStephen Hemminger 
3928d23ce8fSStephen Hemminger 	if (rte_pktmbuf_data_len(m) < sizeof(*oh))
3938d23ce8fSStephen Hemminger 		return -EINVAL;
3948d23ce8fSStephen Hemminger 
3958d23ce8fSStephen Hemminger 	oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
3968d23ce8fSStephen Hemminger 	nh = (struct rte_ether_hdr *)
3978d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
3988d23ce8fSStephen Hemminger 	if (nh == NULL)
3998d23ce8fSStephen Hemminger 		return -ENOSPC;
4008d23ce8fSStephen Hemminger 
4018d23ce8fSStephen Hemminger 	memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
4028d23ce8fSStephen Hemminger 	nh->ether_type = rte_cpu_to_be_16(ether_type);
4038d23ce8fSStephen Hemminger 
4048d23ce8fSStephen Hemminger 	vh = (struct rte_vlan_hdr *) (nh + 1);
4058d23ce8fSStephen Hemminger 	vh->vlan_tci = rte_cpu_to_be_16(tci);
4068d23ce8fSStephen Hemminger 
4078d23ce8fSStephen Hemminger 	return 0;
4088d23ce8fSStephen Hemminger }
4098d23ce8fSStephen Hemminger 
4108d23ce8fSStephen Hemminger /*
4118d23ce8fSStephen Hemminger  *   The mbufs created use the Pcapng standard enhanced packet  block.
4128d23ce8fSStephen Hemminger  *
4138d23ce8fSStephen Hemminger  *                         1                   2                   3
4148d23ce8fSStephen Hemminger  *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4158d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4168d23ce8fSStephen Hemminger  *  0 |                    Block Type = 0x00000006                    |
4178d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4188d23ce8fSStephen Hemminger  *  4 |                      Block Total Length                       |
4198d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4208d23ce8fSStephen Hemminger  *  8 |                         Interface ID                          |
4218d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4228d23ce8fSStephen Hemminger  * 12 |                        Timestamp (High)                       |
4238d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4248d23ce8fSStephen Hemminger  * 16 |                        Timestamp (Low)                        |
4258d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4268d23ce8fSStephen Hemminger  * 20 |                    Captured Packet Length                     |
4278d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4288d23ce8fSStephen Hemminger  * 24 |                    Original Packet Length                     |
4298d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4308d23ce8fSStephen Hemminger  * 28 /                                                               /
4318d23ce8fSStephen Hemminger  *    /                          Packet Data                          /
4328d23ce8fSStephen Hemminger  *    /              variable length, padded to 32 bits               /
4338d23ce8fSStephen Hemminger  *    /                                                               /
4348d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4358d23ce8fSStephen Hemminger  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
4368d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4378d23ce8fSStephen Hemminger  *    |              Flags (direction)                                |
4388d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4398d23ce8fSStephen Hemminger  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
4408d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4418d23ce8fSStephen Hemminger  *    |              Queue id                                         |
4428d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4438d23ce8fSStephen Hemminger  *    |                      Block Total Length                       |
4448d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4458d23ce8fSStephen Hemminger  */
4468d23ce8fSStephen Hemminger 
4478d23ce8fSStephen Hemminger /* Make a copy of original mbuf with pcapng header and options */
4488d23ce8fSStephen Hemminger struct rte_mbuf *
4498d23ce8fSStephen Hemminger rte_pcapng_copy(uint16_t port_id, uint32_t queue,
4508d23ce8fSStephen Hemminger 		const struct rte_mbuf *md,
4518d23ce8fSStephen Hemminger 		struct rte_mempool *mp,
4528d23ce8fSStephen Hemminger 		uint32_t length, uint64_t cycles,
4538d23ce8fSStephen Hemminger 		enum rte_pcapng_direction direction)
4548d23ce8fSStephen Hemminger {
4558d23ce8fSStephen Hemminger 	struct pcapng_enhance_packet_block *epb;
4568d23ce8fSStephen Hemminger 	uint32_t orig_len, data_len, padding, flags;
4578d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
458*d2e3c4b8SStephen Hemminger 	uint16_t optlen;
4598d23ce8fSStephen Hemminger 	struct rte_mbuf *mc;
4608d23ce8fSStephen Hemminger 	uint64_t ns;
461*d2e3c4b8SStephen Hemminger 	bool rss_hash;
4628d23ce8fSStephen Hemminger 
4638d23ce8fSStephen Hemminger #ifdef RTE_LIBRTE_ETHDEV_DEBUG
4648d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
4658d23ce8fSStephen Hemminger #endif
4668d23ce8fSStephen Hemminger 	ns = pcapng_tsc_to_ns(cycles);
4678d23ce8fSStephen Hemminger 
4688d23ce8fSStephen Hemminger 	orig_len = rte_pktmbuf_pkt_len(md);
4698d23ce8fSStephen Hemminger 
4708d23ce8fSStephen Hemminger 	/* Take snapshot of the data */
4718d23ce8fSStephen Hemminger 	mc = rte_pktmbuf_copy(md, mp, 0, length);
4728d23ce8fSStephen Hemminger 	if (unlikely(mc == NULL))
4738d23ce8fSStephen Hemminger 		return NULL;
4748d23ce8fSStephen Hemminger 
4758d23ce8fSStephen Hemminger 	/* Expand any offloaded VLAN information */
4768d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
477daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
4788d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
479daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
4808d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
4818d23ce8fSStephen Hemminger 				       md->vlan_tci) != 0)
4828d23ce8fSStephen Hemminger 			goto fail;
4838d23ce8fSStephen Hemminger 	}
4848d23ce8fSStephen Hemminger 
4858d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
486daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
4878d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
488daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
4898d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
4908d23ce8fSStephen Hemminger 				       md->vlan_tci_outer) != 0)
4918d23ce8fSStephen Hemminger 			goto fail;
4928d23ce8fSStephen Hemminger 	}
4938d23ce8fSStephen Hemminger 
494*d2e3c4b8SStephen Hemminger 	/* record HASH on incoming packets */
495*d2e3c4b8SStephen Hemminger 	rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
496*d2e3c4b8SStephen Hemminger 		    (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
497*d2e3c4b8SStephen Hemminger 
4988d23ce8fSStephen Hemminger 	/* pad the packet to 32 bit boundary */
4998d23ce8fSStephen Hemminger 	data_len = rte_pktmbuf_data_len(mc);
5008d23ce8fSStephen Hemminger 	padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len;
5018d23ce8fSStephen Hemminger 	if (padding > 0) {
5028d23ce8fSStephen Hemminger 		void *tail = rte_pktmbuf_append(mc, padding);
5038d23ce8fSStephen Hemminger 
5048d23ce8fSStephen Hemminger 		if (tail == NULL)
5058d23ce8fSStephen Hemminger 			goto fail;
5068d23ce8fSStephen Hemminger 		memset(tail, 0, padding);
5078d23ce8fSStephen Hemminger 	}
5088d23ce8fSStephen Hemminger 
509*d2e3c4b8SStephen Hemminger 	optlen = pcapng_optlen(sizeof(flags));
510*d2e3c4b8SStephen Hemminger 	optlen += pcapng_optlen(sizeof(queue));
511*d2e3c4b8SStephen Hemminger 	if (rss_hash)
512*d2e3c4b8SStephen Hemminger 		optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
513*d2e3c4b8SStephen Hemminger 
5148d23ce8fSStephen Hemminger 	/* reserve trailing options and block length */
5158d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)
5168d23ce8fSStephen Hemminger 		rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
5178d23ce8fSStephen Hemminger 	if (unlikely(opt == NULL))
5188d23ce8fSStephen Hemminger 		goto fail;
5198d23ce8fSStephen Hemminger 
5208d23ce8fSStephen Hemminger 	switch (direction) {
5218d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_IN:
5228d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_INBOUND;
5238d23ce8fSStephen Hemminger 		break;
5248d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_OUT:
5258d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_OUTBOUND;
5268d23ce8fSStephen Hemminger 		break;
5278d23ce8fSStephen Hemminger 	default:
5288d23ce8fSStephen Hemminger 		flags = 0;
5298d23ce8fSStephen Hemminger 	}
5308d23ce8fSStephen Hemminger 
5318d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
5328d23ce8fSStephen Hemminger 				&flags, sizeof(flags));
5338d23ce8fSStephen Hemminger 
5348d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
5358d23ce8fSStephen Hemminger 				&queue, sizeof(queue));
5368d23ce8fSStephen Hemminger 
537*d2e3c4b8SStephen Hemminger 	if (rss_hash) {
538*d2e3c4b8SStephen Hemminger 		uint8_t hash_opt[5];
539*d2e3c4b8SStephen Hemminger 
540*d2e3c4b8SStephen Hemminger 		/* The algorithm could be something else if
541*d2e3c4b8SStephen Hemminger 		 * using rte_flow_action_rss; but the current API does not
542*d2e3c4b8SStephen Hemminger 		 * have a way for ethdev to report  this on a per-packet basis.
543*d2e3c4b8SStephen Hemminger 		 */
544*d2e3c4b8SStephen Hemminger 		hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
545*d2e3c4b8SStephen Hemminger 
546*d2e3c4b8SStephen Hemminger 		memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
547*d2e3c4b8SStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
548*d2e3c4b8SStephen Hemminger 					&hash_opt, sizeof(hash_opt));
549*d2e3c4b8SStephen Hemminger 	}
550*d2e3c4b8SStephen Hemminger 
5518d23ce8fSStephen Hemminger 	/* Note: END_OPT necessary here. Wireshark doesn't do it. */
5528d23ce8fSStephen Hemminger 
5538d23ce8fSStephen Hemminger 	/* Add PCAPNG packet header */
5548d23ce8fSStephen Hemminger 	epb = (struct pcapng_enhance_packet_block *)
5558d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(mc, sizeof(*epb));
5568d23ce8fSStephen Hemminger 	if (unlikely(epb == NULL))
5578d23ce8fSStephen Hemminger 		goto fail;
5588d23ce8fSStephen Hemminger 
5598d23ce8fSStephen Hemminger 	epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
5608d23ce8fSStephen Hemminger 	epb->block_length = rte_pktmbuf_data_len(mc);
5618d23ce8fSStephen Hemminger 
5628d23ce8fSStephen Hemminger 	/* Interface index is filled in later during write */
5638d23ce8fSStephen Hemminger 	mc->port = port_id;
5648d23ce8fSStephen Hemminger 
5658d23ce8fSStephen Hemminger 	epb->timestamp_hi = ns >> 32;
5668d23ce8fSStephen Hemminger 	epb->timestamp_lo = (uint32_t)ns;
5678d23ce8fSStephen Hemminger 	epb->capture_length = data_len;
5688d23ce8fSStephen Hemminger 	epb->original_length = orig_len;
5698d23ce8fSStephen Hemminger 
5708d23ce8fSStephen Hemminger 	/* set trailer of block length */
5718d23ce8fSStephen Hemminger 	*(uint32_t *)opt = epb->block_length;
5728d23ce8fSStephen Hemminger 
5738d23ce8fSStephen Hemminger 	return mc;
5748d23ce8fSStephen Hemminger 
5758d23ce8fSStephen Hemminger fail:
5768d23ce8fSStephen Hemminger 	rte_pktmbuf_free(mc);
5778d23ce8fSStephen Hemminger 	return NULL;
5788d23ce8fSStephen Hemminger }
5798d23ce8fSStephen Hemminger 
5808d23ce8fSStephen Hemminger /* Write pre-formatted packets to file. */
5818d23ce8fSStephen Hemminger ssize_t
5828d23ce8fSStephen Hemminger rte_pcapng_write_packets(rte_pcapng_t *self,
5838d23ce8fSStephen Hemminger 			 struct rte_mbuf *pkts[], uint16_t nb_pkts)
5848d23ce8fSStephen Hemminger {
5850744f1c9SMário Kuka 	struct iovec iov[IOV_MAX];
5860744f1c9SMário Kuka 	unsigned int i, cnt = 0;
5870744f1c9SMário Kuka 	ssize_t ret, total = 0;
5888d23ce8fSStephen Hemminger 
5890744f1c9SMário Kuka 	for (i = 0; i < nb_pkts; i++) {
5908d23ce8fSStephen Hemminger 		struct rte_mbuf *m = pkts[i];
5918d23ce8fSStephen Hemminger 		struct pcapng_enhance_packet_block *epb;
5928d23ce8fSStephen Hemminger 
5938d23ce8fSStephen Hemminger 		/* sanity check that is really a pcapng mbuf */
5948d23ce8fSStephen Hemminger 		epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
5958d23ce8fSStephen Hemminger 		if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
5968d23ce8fSStephen Hemminger 			     epb->block_length != rte_pktmbuf_data_len(m))) {
5978d23ce8fSStephen Hemminger 			rte_errno = EINVAL;
5988d23ce8fSStephen Hemminger 			return -1;
5998d23ce8fSStephen Hemminger 		}
6008d23ce8fSStephen Hemminger 
6018d23ce8fSStephen Hemminger 		/*
6020744f1c9SMário Kuka 		 * Handle case of highly fragmented and large burst size
6030744f1c9SMário Kuka 		 * Note: this assumes that max segments per mbuf < IOV_MAX
6040744f1c9SMário Kuka 		 */
6050744f1c9SMário Kuka 		if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
6060744f1c9SMário Kuka 			ret = writev(self->outfd, iov, cnt);
6070744f1c9SMário Kuka 			if (unlikely(ret < 0)) {
6080744f1c9SMário Kuka 				rte_errno = errno;
6090744f1c9SMário Kuka 				return -1;
6100744f1c9SMário Kuka 			}
6110744f1c9SMário Kuka 			total += ret;
6120744f1c9SMário Kuka 			cnt = 0;
6130744f1c9SMário Kuka 		}
6140744f1c9SMário Kuka 
6150744f1c9SMário Kuka 		/*
6168d23ce8fSStephen Hemminger 		 * The DPDK port is recorded during pcapng_copy.
6178d23ce8fSStephen Hemminger 		 * Map that to PCAPNG interface in file.
6188d23ce8fSStephen Hemminger 		 */
6198d23ce8fSStephen Hemminger 		epb->interface_id = self->port_index[m->port];
6208d23ce8fSStephen Hemminger 		do {
6218d23ce8fSStephen Hemminger 			iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
6228d23ce8fSStephen Hemminger 			iov[cnt].iov_len = rte_pktmbuf_data_len(m);
6238d23ce8fSStephen Hemminger 			++cnt;
6248d23ce8fSStephen Hemminger 		} while ((m = m->next));
6258d23ce8fSStephen Hemminger 	}
6268d23ce8fSStephen Hemminger 
6270744f1c9SMário Kuka 	ret = writev(self->outfd, iov, cnt);
6280744f1c9SMário Kuka 	if (unlikely(ret < 0)) {
6298d23ce8fSStephen Hemminger 		rte_errno = errno;
6300744f1c9SMário Kuka 		return -1;
6310744f1c9SMário Kuka 	}
6320744f1c9SMário Kuka 	return total + ret;
6338d23ce8fSStephen Hemminger }
6348d23ce8fSStephen Hemminger 
6358d23ce8fSStephen Hemminger /* Create new pcapng writer handle */
6368d23ce8fSStephen Hemminger rte_pcapng_t *
6378d23ce8fSStephen Hemminger rte_pcapng_fdopen(int fd,
6388d23ce8fSStephen Hemminger 		  const char *osname, const char *hardware,
6398d23ce8fSStephen Hemminger 		  const char *appname, const char *comment)
6408d23ce8fSStephen Hemminger {
6418d23ce8fSStephen Hemminger 	rte_pcapng_t *self;
6428d23ce8fSStephen Hemminger 
6438d23ce8fSStephen Hemminger 	self = malloc(sizeof(*self));
6448d23ce8fSStephen Hemminger 	if (!self) {
6458d23ce8fSStephen Hemminger 		rte_errno = ENOMEM;
6468d23ce8fSStephen Hemminger 		return NULL;
6478d23ce8fSStephen Hemminger 	}
6488d23ce8fSStephen Hemminger 
6498d23ce8fSStephen Hemminger 	self->outfd = fd;
6508d23ce8fSStephen Hemminger 
6518d23ce8fSStephen Hemminger 	if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
6528d23ce8fSStephen Hemminger 		goto fail;
6538d23ce8fSStephen Hemminger 
6548d23ce8fSStephen Hemminger 	if (pcapng_interfaces(self) < 0)
6558d23ce8fSStephen Hemminger 		goto fail;
6568d23ce8fSStephen Hemminger 
6578d23ce8fSStephen Hemminger 	return self;
6588d23ce8fSStephen Hemminger fail:
6598d23ce8fSStephen Hemminger 	free(self);
6608d23ce8fSStephen Hemminger 	return NULL;
6618d23ce8fSStephen Hemminger }
6628d23ce8fSStephen Hemminger 
6638d23ce8fSStephen Hemminger void
6648d23ce8fSStephen Hemminger rte_pcapng_close(rte_pcapng_t *self)
6658d23ce8fSStephen Hemminger {
6668d23ce8fSStephen Hemminger 	close(self->outfd);
6678d23ce8fSStephen Hemminger 	free(self);
6688d23ce8fSStephen Hemminger }
669