xref: /dpdk/lib/pcapng/rte_pcapng.c (revision 0cbf27521b0d6e7cb79f41a5e699d82562b09c03)
18d23ce8fSStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause
28d23ce8fSStephen Hemminger  * Copyright(c) 2019 Microsoft Corporation
38d23ce8fSStephen Hemminger  */
48d23ce8fSStephen Hemminger 
58d23ce8fSStephen Hemminger #include <errno.h>
6d2e3c4b8SStephen Hemminger #include <stdbool.h>
78d23ce8fSStephen Hemminger #include <stdio.h>
88d23ce8fSStephen Hemminger #include <stdlib.h>
98d23ce8fSStephen Hemminger #include <string.h>
108d23ce8fSStephen Hemminger #include <time.h>
118d23ce8fSStephen Hemminger #include <unistd.h>
128d23ce8fSStephen Hemminger 
13beb43124SStephen Hemminger #ifndef RTE_EXEC_ENV_WINDOWS
14beb43124SStephen Hemminger #include <net/if.h>
15beb43124SStephen Hemminger #include <sys/uio.h>
16beb43124SStephen Hemminger #endif
17beb43124SStephen Hemminger 
18a04322f6SDavid Marchand #include <bus_driver.h>
198d23ce8fSStephen Hemminger #include <rte_common.h>
208d23ce8fSStephen Hemminger #include <rte_cycles.h>
211acb7f54SDavid Marchand #include <dev_driver.h>
228d23ce8fSStephen Hemminger #include <rte_errno.h>
238d23ce8fSStephen Hemminger #include <rte_ethdev.h>
248d23ce8fSStephen Hemminger #include <rte_ether.h>
258d23ce8fSStephen Hemminger #include <rte_mbuf.h>
26beb43124SStephen Hemminger #include <rte_os_shim.h>
278d23ce8fSStephen Hemminger #include <rte_pcapng.h>
28c882eb54SQuentin Armitage #include <rte_reciprocal.h>
298d23ce8fSStephen Hemminger #include <rte_time.h>
308d23ce8fSStephen Hemminger 
318d23ce8fSStephen Hemminger #include "pcapng_proto.h"
328d23ce8fSStephen Hemminger 
338d23ce8fSStephen Hemminger /* conversion from DPDK speed to PCAPNG */
348d23ce8fSStephen Hemminger #define PCAPNG_MBPS_SPEED 1000000ull
358d23ce8fSStephen Hemminger 
36*0cbf2752SStephen Hemminger /* upper bound for section, stats and interface blocks (in uint32_t) */
37*0cbf2752SStephen Hemminger #define PCAPNG_BLKSIZ	(2048 / sizeof(uint32_t))
38dc2d6d20SStephen Hemminger 
398d23ce8fSStephen Hemminger /* Format of the capture file handle */
408d23ce8fSStephen Hemminger struct rte_pcapng {
418d23ce8fSStephen Hemminger 	int  outfd;		/* output file */
42d1da6d0dSStephen Hemminger 	unsigned int ports;	/* number of interfaces added */
4316659193SStephen Hemminger 	uint64_t offset_ns;	/* ns since 1/1/1970 when initialized */
4416659193SStephen Hemminger 	uint64_t tsc_base;	/* TSC when started */
45d1da6d0dSStephen Hemminger 
468d23ce8fSStephen Hemminger 	/* DPDK port id to interface index in file */
478d23ce8fSStephen Hemminger 	uint32_t port_index[RTE_MAX_ETHPORTS];
488d23ce8fSStephen Hemminger };
498d23ce8fSStephen Hemminger 
50beb43124SStephen Hemminger #ifdef RTE_EXEC_ENV_WINDOWS
51beb43124SStephen Hemminger /*
52beb43124SStephen Hemminger  * Windows does not have writev() call.
53beb43124SStephen Hemminger  * Emulate this by copying to a new buffer.
54beb43124SStephen Hemminger  * The copy is necessary since pcapng needs to be thread-safe
55beb43124SStephen Hemminger  * and do atomic write operations.
56beb43124SStephen Hemminger  */
57beb43124SStephen Hemminger 
58beb43124SStephen Hemminger #define IOV_MAX 128
59beb43124SStephen Hemminger struct iovec {
60beb43124SStephen Hemminger 	void   *iov_base;
61beb43124SStephen Hemminger 	size_t  iov_len;
62beb43124SStephen Hemminger };
63beb43124SStephen Hemminger 
64beb43124SStephen Hemminger static ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
65beb43124SStephen Hemminger {
66beb43124SStephen Hemminger 	size_t bytes = 0;
67beb43124SStephen Hemminger 	uint8_t *ptr;
68beb43124SStephen Hemminger 	void *tmp_buf;
69beb43124SStephen Hemminger 	ssize_t ret;
70beb43124SStephen Hemminger 	int i;
71beb43124SStephen Hemminger 
72beb43124SStephen Hemminger 	for (i = 0; i < iovcnt; i++)
73beb43124SStephen Hemminger 		bytes += iov[i].iov_len;
74beb43124SStephen Hemminger 
75beb43124SStephen Hemminger 	if (unlikely(bytes == 0))
76beb43124SStephen Hemminger 		return 0;
77beb43124SStephen Hemminger 
78beb43124SStephen Hemminger 	tmp_buf = malloc(bytes);
79beb43124SStephen Hemminger 	if (unlikely(tmp_buf == NULL)) {
80beb43124SStephen Hemminger 		errno = ENOMEM;
81beb43124SStephen Hemminger 		return -1;
82beb43124SStephen Hemminger 	}
83beb43124SStephen Hemminger 
84beb43124SStephen Hemminger 	ptr = tmp_buf;
85beb43124SStephen Hemminger 	for (i = 0; i < iovcnt; i++) {
86beb43124SStephen Hemminger 		rte_memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
87beb43124SStephen Hemminger 		ptr += iov[i].iov_len;
88beb43124SStephen Hemminger 	}
89beb43124SStephen Hemminger 
90beb43124SStephen Hemminger 	ret = write(fd, tmp_buf, bytes);
91beb43124SStephen Hemminger 	free(tmp_buf);
92beb43124SStephen Hemminger 	return ret;
93beb43124SStephen Hemminger }
94beb43124SStephen Hemminger 
95beb43124SStephen Hemminger #define IF_NAMESIZE	16
96beb43124SStephen Hemminger /* compatibility wrapper because name is optional */
97beb43124SStephen Hemminger #define if_indextoname(ifindex, ifname) NULL
98beb43124SStephen Hemminger #endif
99beb43124SStephen Hemminger 
10016659193SStephen Hemminger /* Convert from TSC (CPU cycles) to nanoseconds */
10116659193SStephen Hemminger static uint64_t
10216659193SStephen Hemminger pcapng_timestamp(const rte_pcapng_t *self, uint64_t cycles)
1038d23ce8fSStephen Hemminger {
10416659193SStephen Hemminger 	uint64_t delta, rem, secs, ns;
10516659193SStephen Hemminger 	const uint64_t hz = rte_get_tsc_hz();
1068d23ce8fSStephen Hemminger 
10716659193SStephen Hemminger 	delta = cycles - self->tsc_base;
108c882eb54SQuentin Armitage 
10916659193SStephen Hemminger 	/* Avoid numeric wraparound by computing seconds first */
11016659193SStephen Hemminger 	secs = delta / hz;
11116659193SStephen Hemminger 	rem = delta % hz;
11216659193SStephen Hemminger 	ns = (rem * NS_PER_S) / hz;
1138d23ce8fSStephen Hemminger 
11416659193SStephen Hemminger 	return secs * NS_PER_S + ns + self->offset_ns;
1158d23ce8fSStephen Hemminger }
1168d23ce8fSStephen Hemminger 
1178d23ce8fSStephen Hemminger /* length of option including padding */
1188d23ce8fSStephen Hemminger static uint16_t pcapng_optlen(uint16_t len)
1198d23ce8fSStephen Hemminger {
1208d23ce8fSStephen Hemminger 	return RTE_ALIGN(sizeof(struct pcapng_option) + len,
1218d23ce8fSStephen Hemminger 			 sizeof(uint32_t));
1228d23ce8fSStephen Hemminger }
1238d23ce8fSStephen Hemminger 
1248d23ce8fSStephen Hemminger /* build TLV option and return location of next */
1258d23ce8fSStephen Hemminger static struct pcapng_option *
1268d23ce8fSStephen Hemminger pcapng_add_option(struct pcapng_option *popt, uint16_t code,
1278d23ce8fSStephen Hemminger 		  const void *data, uint16_t len)
1288d23ce8fSStephen Hemminger {
1298d23ce8fSStephen Hemminger 	popt->code = code;
1308d23ce8fSStephen Hemminger 	popt->length = len;
13106f69f8fSStephen Hemminger 	if (len > 0)
1328d23ce8fSStephen Hemminger 		memcpy(popt->data, data, len);
1338d23ce8fSStephen Hemminger 
1348d23ce8fSStephen Hemminger 	return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
1358d23ce8fSStephen Hemminger }
1368d23ce8fSStephen Hemminger 
1378d23ce8fSStephen Hemminger /*
1388d23ce8fSStephen Hemminger  * Write required initial section header describing the capture
1398d23ce8fSStephen Hemminger  */
1408d23ce8fSStephen Hemminger static int
1418d23ce8fSStephen Hemminger pcapng_section_block(rte_pcapng_t *self,
1428d23ce8fSStephen Hemminger 		    const char *os, const char *hw,
1438d23ce8fSStephen Hemminger 		    const char *app, const char *comment)
1448d23ce8fSStephen Hemminger {
1458d23ce8fSStephen Hemminger 	struct pcapng_section_header *hdr;
1468d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
147*0cbf2752SStephen Hemminger 	uint32_t buf[PCAPNG_BLKSIZ];
1488d23ce8fSStephen Hemminger 	uint32_t len;
1498d23ce8fSStephen Hemminger 
1508d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
1518d23ce8fSStephen Hemminger 	if (hw)
1528d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(hw));
1538d23ce8fSStephen Hemminger 	if (os)
1548d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(os));
1558d23ce8fSStephen Hemminger 	if (app)
1568d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(app));
1578d23ce8fSStephen Hemminger 	if (comment)
1588d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(comment));
1598d23ce8fSStephen Hemminger 
1608d23ce8fSStephen Hemminger 	/* reserve space for OPT_END */
1618d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
1628d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
1638d23ce8fSStephen Hemminger 
164dc2d6d20SStephen Hemminger 	if (len > sizeof(buf))
1658d23ce8fSStephen Hemminger 		return -1;
1668d23ce8fSStephen Hemminger 
1678d23ce8fSStephen Hemminger 	hdr = (struct pcapng_section_header *)buf;
1688d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_section_header) {
1698d23ce8fSStephen Hemminger 		.block_type = PCAPNG_SECTION_BLOCK,
1708d23ce8fSStephen Hemminger 		.block_length = len,
1718d23ce8fSStephen Hemminger 		.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
1728d23ce8fSStephen Hemminger 		.major_version = PCAPNG_MAJOR_VERS,
1738d23ce8fSStephen Hemminger 		.minor_version = PCAPNG_MINOR_VERS,
1748d23ce8fSStephen Hemminger 		.section_length = UINT64_MAX,
1758d23ce8fSStephen Hemminger 	};
1768d23ce8fSStephen Hemminger 
1778d23ce8fSStephen Hemminger 	/* After the section header insert variable length options. */
1788d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
1798d23ce8fSStephen Hemminger 	if (comment)
1808d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
1818d23ce8fSStephen Hemminger 					comment, strlen(comment));
1828d23ce8fSStephen Hemminger 	if (hw)
1838d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
1848d23ce8fSStephen Hemminger 					hw, strlen(hw));
1858d23ce8fSStephen Hemminger 	if (os)
1868d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
1878d23ce8fSStephen Hemminger 					os, strlen(os));
1888d23ce8fSStephen Hemminger 	if (app)
1898d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
1908d23ce8fSStephen Hemminger 					app, strlen(app));
1918d23ce8fSStephen Hemminger 
1928d23ce8fSStephen Hemminger 	/* The standard requires last option to be OPT_END */
1938d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
1948d23ce8fSStephen Hemminger 
1958d23ce8fSStephen Hemminger 	/* clone block_length after option */
1968d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
1978d23ce8fSStephen Hemminger 
198dc2d6d20SStephen Hemminger 	return write(self->outfd, buf, len);
1998d23ce8fSStephen Hemminger }
2008d23ce8fSStephen Hemminger 
2018d23ce8fSStephen Hemminger /* Write an interface block for a DPDK port */
202d1da6d0dSStephen Hemminger int
203d1da6d0dSStephen Hemminger rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
204d1da6d0dSStephen Hemminger 			 const char *ifname, const char *ifdescr,
205d1da6d0dSStephen Hemminger 			 const char *filter)
2068d23ce8fSStephen Hemminger {
2078d23ce8fSStephen Hemminger 	struct pcapng_interface_block *hdr;
2088d23ce8fSStephen Hemminger 	struct rte_eth_dev_info dev_info;
2098d23ce8fSStephen Hemminger 	struct rte_ether_addr *ea, macaddr;
2108d23ce8fSStephen Hemminger 	const struct rte_device *dev;
2118d23ce8fSStephen Hemminger 	struct rte_eth_link link;
2128d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
2138d23ce8fSStephen Hemminger 	const uint8_t tsresol = 9;	/* nanosecond resolution */
2148d23ce8fSStephen Hemminger 	uint32_t len;
215*0cbf2752SStephen Hemminger 	uint32_t buf[PCAPNG_BLKSIZ];
216d1da6d0dSStephen Hemminger 	char ifname_buf[IF_NAMESIZE];
2178d23ce8fSStephen Hemminger 	char ifhw[256];
2188d23ce8fSStephen Hemminger 	uint64_t speed = 0;
2198d23ce8fSStephen Hemminger 
2208d23ce8fSStephen Hemminger 	if (rte_eth_dev_info_get(port, &dev_info) < 0)
2218d23ce8fSStephen Hemminger 		return -1;
2228d23ce8fSStephen Hemminger 
2238d23ce8fSStephen Hemminger 	/* make something like an interface name */
224d1da6d0dSStephen Hemminger 	if (ifname == NULL) {
225d1da6d0dSStephen Hemminger 		/* Use kernel name if available */
226d1da6d0dSStephen Hemminger 		ifname = if_indextoname(dev_info.if_index, ifname_buf);
227d1da6d0dSStephen Hemminger 		if (ifname == NULL) {
228d1da6d0dSStephen Hemminger 			snprintf(ifname_buf, IF_NAMESIZE, "dpdk:%u", port);
229d1da6d0dSStephen Hemminger 			ifname = ifname_buf;
230d1da6d0dSStephen Hemminger 		}
231d1da6d0dSStephen Hemminger 	}
2328d23ce8fSStephen Hemminger 
2338d23ce8fSStephen Hemminger 	/* make a useful device hardware string */
2348d23ce8fSStephen Hemminger 	dev = dev_info.device;
2358d23ce8fSStephen Hemminger 	if (dev)
2368d23ce8fSStephen Hemminger 		snprintf(ifhw, sizeof(ifhw),
2378d23ce8fSStephen Hemminger 			 "%s-%s", dev->bus->name, dev->name);
2388d23ce8fSStephen Hemminger 
2398d23ce8fSStephen Hemminger 	/* DPDK reports in units of Mbps */
240442878dbSStephen Hemminger 	if (rte_eth_link_get(port, &link) == 0 &&
241442878dbSStephen Hemminger 	    link.link_status == RTE_ETH_LINK_UP)
2428d23ce8fSStephen Hemminger 		speed = link.link_speed * PCAPNG_MBPS_SPEED;
2438d23ce8fSStephen Hemminger 
2448d23ce8fSStephen Hemminger 	if (rte_eth_macaddr_get(port, &macaddr) < 0)
2458d23ce8fSStephen Hemminger 		ea = NULL;
2468d23ce8fSStephen Hemminger 	else
2478d23ce8fSStephen Hemminger 		ea = &macaddr;
2488d23ce8fSStephen Hemminger 
2498d23ce8fSStephen Hemminger 	/* Compute length of interface block options */
2508d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
2518d23ce8fSStephen Hemminger 
2528d23ce8fSStephen Hemminger 	len += pcapng_optlen(sizeof(tsresol));	/* timestamp */
2538d23ce8fSStephen Hemminger 	len += pcapng_optlen(strlen(ifname));	/* ifname */
2548d23ce8fSStephen Hemminger 
255d1da6d0dSStephen Hemminger 	if (ifdescr)
256d1da6d0dSStephen Hemminger 		len += pcapng_optlen(strlen(ifdescr));
2578d23ce8fSStephen Hemminger 	if (ea)
2588d23ce8fSStephen Hemminger 		len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
2598d23ce8fSStephen Hemminger 	if (speed != 0)
2608d23ce8fSStephen Hemminger 		len += pcapng_optlen(sizeof(uint64_t));
261d1da6d0dSStephen Hemminger 	if (filter)
262d1da6d0dSStephen Hemminger 		len += pcapng_optlen(strlen(filter) + 1);
2638d23ce8fSStephen Hemminger 	if (dev)
2648d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(ifhw));
2658d23ce8fSStephen Hemminger 
2668d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
2678d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
2688d23ce8fSStephen Hemminger 
269dc2d6d20SStephen Hemminger 	if (len > sizeof(buf))
2708d23ce8fSStephen Hemminger 		return -1;
2718d23ce8fSStephen Hemminger 
2728d23ce8fSStephen Hemminger 	hdr = (struct pcapng_interface_block *)buf;
2738d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_interface_block) {
2748d23ce8fSStephen Hemminger 		.block_type = PCAPNG_INTERFACE_BLOCK,
2758d23ce8fSStephen Hemminger 		.link_type = 1,		/* DLT_EN10MB - Ethernet */
2768d23ce8fSStephen Hemminger 		.block_length = len,
2778d23ce8fSStephen Hemminger 	};
2788d23ce8fSStephen Hemminger 
2798d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
2808d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
2818d23ce8fSStephen Hemminger 				&tsresol, sizeof(tsresol));
2828d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
2838d23ce8fSStephen Hemminger 				ifname, strlen(ifname));
284d1da6d0dSStephen Hemminger 	if (ifdescr)
285d1da6d0dSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_DESCRIPTION,
286d1da6d0dSStephen Hemminger 					ifdescr, strlen(ifdescr));
2878d23ce8fSStephen Hemminger 	if (ea)
2888d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
2898d23ce8fSStephen Hemminger 					ea, RTE_ETHER_ADDR_LEN);
2908d23ce8fSStephen Hemminger 	if (speed != 0)
2918d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
2928d23ce8fSStephen Hemminger 					 &speed, sizeof(uint64_t));
2938d23ce8fSStephen Hemminger 	if (dev)
2948d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
2958d23ce8fSStephen Hemminger 					 ifhw, strlen(ifhw));
296d1da6d0dSStephen Hemminger 	if (filter) {
297d1da6d0dSStephen Hemminger 		size_t len;
298d1da6d0dSStephen Hemminger 
299d1da6d0dSStephen Hemminger 		len = strlen(filter) + 1;
300dc2d6d20SStephen Hemminger 		opt->code = PCAPNG_IFB_FILTER;
301dc2d6d20SStephen Hemminger 		opt->length = len;
302dc2d6d20SStephen Hemminger 		/* Encoding is that the first octet indicates string vs BPF */
303dc2d6d20SStephen Hemminger 		opt->data[0] = 0;
304dc2d6d20SStephen Hemminger 		memcpy(opt->data + 1, filter, strlen(filter));
305d1da6d0dSStephen Hemminger 
306dc2d6d20SStephen Hemminger 		opt = (struct pcapng_option *)((uint8_t *)opt + pcapng_optlen(len));
307d1da6d0dSStephen Hemminger 	}
308d1da6d0dSStephen Hemminger 
3098d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
3108d23ce8fSStephen Hemminger 
3118d23ce8fSStephen Hemminger 	/* clone block_length after optionsa */
3128d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
3138d23ce8fSStephen Hemminger 
314d1da6d0dSStephen Hemminger 	/* remember the file index */
315d1da6d0dSStephen Hemminger 	self->port_index[port] = self->ports++;
316d1da6d0dSStephen Hemminger 
3178d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
3188d23ce8fSStephen Hemminger }
3198d23ce8fSStephen Hemminger 
3208d23ce8fSStephen Hemminger /*
3218d23ce8fSStephen Hemminger  * Write an Interface statistics block at the end of capture.
3228d23ce8fSStephen Hemminger  */
3238d23ce8fSStephen Hemminger ssize_t
3248d23ce8fSStephen Hemminger rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
32516659193SStephen Hemminger 		       uint64_t ifrecv, uint64_t ifdrop,
32616659193SStephen Hemminger 		       const char *comment)
3278d23ce8fSStephen Hemminger {
3288d23ce8fSStephen Hemminger 	struct pcapng_statistics *hdr;
3298d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
33016659193SStephen Hemminger 	uint64_t start_time = self->offset_ns;
33116659193SStephen Hemminger 	uint64_t sample_time;
3328d23ce8fSStephen Hemminger 	uint32_t optlen, len;
333*0cbf2752SStephen Hemminger 	uint32_t buf[PCAPNG_BLKSIZ];
3348d23ce8fSStephen Hemminger 
3358d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
3368d23ce8fSStephen Hemminger 
3378d23ce8fSStephen Hemminger 	optlen = 0;
3388d23ce8fSStephen Hemminger 
3398d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
3408d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifrecv));
3418d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
3428d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifdrop));
34316659193SStephen Hemminger 
3448d23ce8fSStephen Hemminger 	if (start_time != 0)
3458d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(start_time));
34616659193SStephen Hemminger 
3478d23ce8fSStephen Hemminger 	if (comment)
3488d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(strlen(comment));
3498d23ce8fSStephen Hemminger 	if (optlen != 0)
3508d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(0);
3518d23ce8fSStephen Hemminger 
3528d23ce8fSStephen Hemminger 	len = sizeof(*hdr) + optlen + sizeof(uint32_t);
353dc2d6d20SStephen Hemminger 	if (len > sizeof(buf))
3548d23ce8fSStephen Hemminger 		return -1;
3558d23ce8fSStephen Hemminger 
3568d23ce8fSStephen Hemminger 	hdr = (struct pcapng_statistics *)buf;
3578d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
3588d23ce8fSStephen Hemminger 
3598d23ce8fSStephen Hemminger 	if (comment)
3608d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
3618d23ce8fSStephen Hemminger 					comment, strlen(comment));
3628d23ce8fSStephen Hemminger 	if (start_time != 0)
3638d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
3648d23ce8fSStephen Hemminger 					 &start_time, sizeof(start_time));
3658d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
3668d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
3678d23ce8fSStephen Hemminger 				&ifrecv, sizeof(ifrecv));
3688d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
3698d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
3708d23ce8fSStephen Hemminger 				&ifdrop, sizeof(ifdrop));
3718d23ce8fSStephen Hemminger 	if (optlen != 0)
3728d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
3738d23ce8fSStephen Hemminger 
3748d23ce8fSStephen Hemminger 	hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
3758d23ce8fSStephen Hemminger 	hdr->block_length = len;
3768d23ce8fSStephen Hemminger 	hdr->interface_id = self->port_index[port_id];
3778d23ce8fSStephen Hemminger 
37816659193SStephen Hemminger 	sample_time = pcapng_timestamp(self, rte_get_tsc_cycles());
37916659193SStephen Hemminger 	hdr->timestamp_hi = sample_time >> 32;
38016659193SStephen Hemminger 	hdr->timestamp_lo = (uint32_t)sample_time;
3818d23ce8fSStephen Hemminger 
3828d23ce8fSStephen Hemminger 	/* clone block_length after option */
3838d23ce8fSStephen Hemminger 	memcpy(opt, &len, sizeof(uint32_t));
3848d23ce8fSStephen Hemminger 
3858d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
3868d23ce8fSStephen Hemminger }
3878d23ce8fSStephen Hemminger 
3888d23ce8fSStephen Hemminger uint32_t
3898d23ce8fSStephen Hemminger rte_pcapng_mbuf_size(uint32_t length)
3908d23ce8fSStephen Hemminger {
3918d23ce8fSStephen Hemminger 	/* The VLAN and EPB header must fit in the mbuf headroom. */
3928d23ce8fSStephen Hemminger 	RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
3938d23ce8fSStephen Hemminger 		   sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
3948d23ce8fSStephen Hemminger 
3958d23ce8fSStephen Hemminger 	/* The flags and queue information are added at the end. */
3968d23ce8fSStephen Hemminger 	return sizeof(struct rte_mbuf)
3978d23ce8fSStephen Hemminger 		+ RTE_ALIGN(length, sizeof(uint32_t))
3988d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* flag option */
3998d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* queue option */
4008d23ce8fSStephen Hemminger 		+ sizeof(uint32_t);		  /*  length */
4018d23ce8fSStephen Hemminger }
4028d23ce8fSStephen Hemminger 
4038d23ce8fSStephen Hemminger /* More generalized version rte_vlan_insert() */
4048d23ce8fSStephen Hemminger static int
4058d23ce8fSStephen Hemminger pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
4068d23ce8fSStephen Hemminger {
4078d23ce8fSStephen Hemminger 	struct rte_ether_hdr *nh, *oh;
4088d23ce8fSStephen Hemminger 	struct rte_vlan_hdr *vh;
4098d23ce8fSStephen Hemminger 
4108d23ce8fSStephen Hemminger 	if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
4118d23ce8fSStephen Hemminger 		return -EINVAL;
4128d23ce8fSStephen Hemminger 
4138d23ce8fSStephen Hemminger 	if (rte_pktmbuf_data_len(m) < sizeof(*oh))
4148d23ce8fSStephen Hemminger 		return -EINVAL;
4158d23ce8fSStephen Hemminger 
4168d23ce8fSStephen Hemminger 	oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
4178d23ce8fSStephen Hemminger 	nh = (struct rte_ether_hdr *)
4188d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
4198d23ce8fSStephen Hemminger 	if (nh == NULL)
4208d23ce8fSStephen Hemminger 		return -ENOSPC;
4218d23ce8fSStephen Hemminger 
4228d23ce8fSStephen Hemminger 	memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
4238d23ce8fSStephen Hemminger 	nh->ether_type = rte_cpu_to_be_16(ether_type);
4248d23ce8fSStephen Hemminger 
4258d23ce8fSStephen Hemminger 	vh = (struct rte_vlan_hdr *) (nh + 1);
4268d23ce8fSStephen Hemminger 	vh->vlan_tci = rte_cpu_to_be_16(tci);
4278d23ce8fSStephen Hemminger 
4288d23ce8fSStephen Hemminger 	return 0;
4298d23ce8fSStephen Hemminger }
4308d23ce8fSStephen Hemminger 
4318d23ce8fSStephen Hemminger /*
4328d23ce8fSStephen Hemminger  *   The mbufs created use the Pcapng standard enhanced packet  block.
4338d23ce8fSStephen Hemminger  *
4348d23ce8fSStephen Hemminger  *                         1                   2                   3
4358d23ce8fSStephen 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
4368d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4378d23ce8fSStephen Hemminger  *  0 |                    Block Type = 0x00000006                    |
4388d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4398d23ce8fSStephen Hemminger  *  4 |                      Block Total Length                       |
4408d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4418d23ce8fSStephen Hemminger  *  8 |                         Interface ID                          |
4428d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4438d23ce8fSStephen Hemminger  * 12 |                        Timestamp (High)                       |
4448d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4458d23ce8fSStephen Hemminger  * 16 |                        Timestamp (Low)                        |
4468d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4478d23ce8fSStephen Hemminger  * 20 |                    Captured Packet Length                     |
4488d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4498d23ce8fSStephen Hemminger  * 24 |                    Original Packet Length                     |
4508d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4518d23ce8fSStephen Hemminger  * 28 /                                                               /
4528d23ce8fSStephen Hemminger  *    /                          Packet Data                          /
4538d23ce8fSStephen Hemminger  *    /              variable length, padded to 32 bits               /
4548d23ce8fSStephen Hemminger  *    /                                                               /
4558d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4568d23ce8fSStephen Hemminger  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
4578d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4588d23ce8fSStephen Hemminger  *    |              Flags (direction)                                |
4598d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4608d23ce8fSStephen Hemminger  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
4618d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4628d23ce8fSStephen Hemminger  *    |              Queue id                                         |
4638d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4648d23ce8fSStephen Hemminger  *    |                      Block Total Length                       |
4658d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4668d23ce8fSStephen Hemminger  */
4678d23ce8fSStephen Hemminger 
4688d23ce8fSStephen Hemminger /* Make a copy of original mbuf with pcapng header and options */
4698d23ce8fSStephen Hemminger struct rte_mbuf *
4708d23ce8fSStephen Hemminger rte_pcapng_copy(uint16_t port_id, uint32_t queue,
4718d23ce8fSStephen Hemminger 		const struct rte_mbuf *md,
4728d23ce8fSStephen Hemminger 		struct rte_mempool *mp,
47316659193SStephen Hemminger 		uint32_t length,
474c1abd1e9SAmit Prakash Shukla 		enum rte_pcapng_direction direction,
475c1abd1e9SAmit Prakash Shukla 		const char *comment)
4768d23ce8fSStephen Hemminger {
4778d23ce8fSStephen Hemminger 	struct pcapng_enhance_packet_block *epb;
4786db35853SOleksandr Nahnybida 	uint32_t orig_len, pkt_len, padding, flags;
4798d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
48016659193SStephen Hemminger 	uint64_t timestamp;
481d2e3c4b8SStephen Hemminger 	uint16_t optlen;
4828d23ce8fSStephen Hemminger 	struct rte_mbuf *mc;
483d2e3c4b8SStephen Hemminger 	bool rss_hash;
4848d23ce8fSStephen Hemminger 
4858d23ce8fSStephen Hemminger #ifdef RTE_LIBRTE_ETHDEV_DEBUG
4868d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
4878d23ce8fSStephen Hemminger #endif
4888d23ce8fSStephen Hemminger 	orig_len = rte_pktmbuf_pkt_len(md);
4898d23ce8fSStephen Hemminger 
4908d23ce8fSStephen Hemminger 	/* Take snapshot of the data */
4918d23ce8fSStephen Hemminger 	mc = rte_pktmbuf_copy(md, mp, 0, length);
4928d23ce8fSStephen Hemminger 	if (unlikely(mc == NULL))
4938d23ce8fSStephen Hemminger 		return NULL;
4948d23ce8fSStephen Hemminger 
4958d23ce8fSStephen Hemminger 	/* Expand any offloaded VLAN information */
4968d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
497daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
4988d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
499daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
5008d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
5018d23ce8fSStephen Hemminger 				       md->vlan_tci) != 0)
5028d23ce8fSStephen Hemminger 			goto fail;
5038d23ce8fSStephen Hemminger 	}
5048d23ce8fSStephen Hemminger 
5058d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
506daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
5078d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
508daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
5098d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
5108d23ce8fSStephen Hemminger 				       md->vlan_tci_outer) != 0)
5118d23ce8fSStephen Hemminger 			goto fail;
5128d23ce8fSStephen Hemminger 	}
5138d23ce8fSStephen Hemminger 
514d2e3c4b8SStephen Hemminger 	/* record HASH on incoming packets */
515d2e3c4b8SStephen Hemminger 	rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
516d2e3c4b8SStephen Hemminger 		    (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
517d2e3c4b8SStephen Hemminger 
5188d23ce8fSStephen Hemminger 	/* pad the packet to 32 bit boundary */
5196db35853SOleksandr Nahnybida 	pkt_len = rte_pktmbuf_pkt_len(mc);
5206db35853SOleksandr Nahnybida 	padding = RTE_ALIGN(pkt_len, sizeof(uint32_t)) - pkt_len;
5218d23ce8fSStephen Hemminger 	if (padding > 0) {
5228d23ce8fSStephen Hemminger 		void *tail = rte_pktmbuf_append(mc, padding);
5238d23ce8fSStephen Hemminger 
5248d23ce8fSStephen Hemminger 		if (tail == NULL)
5258d23ce8fSStephen Hemminger 			goto fail;
5268d23ce8fSStephen Hemminger 		memset(tail, 0, padding);
5278d23ce8fSStephen Hemminger 	}
5288d23ce8fSStephen Hemminger 
529d2e3c4b8SStephen Hemminger 	optlen = pcapng_optlen(sizeof(flags));
530d2e3c4b8SStephen Hemminger 	optlen += pcapng_optlen(sizeof(queue));
531d2e3c4b8SStephen Hemminger 	if (rss_hash)
532d2e3c4b8SStephen Hemminger 		optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
533d2e3c4b8SStephen Hemminger 
534c1abd1e9SAmit Prakash Shukla 	if (comment)
535c1abd1e9SAmit Prakash Shukla 		optlen += pcapng_optlen(strlen(comment));
536c1abd1e9SAmit Prakash Shukla 
5378d23ce8fSStephen Hemminger 	/* reserve trailing options and block length */
5388d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)
5398d23ce8fSStephen Hemminger 		rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
5408d23ce8fSStephen Hemminger 	if (unlikely(opt == NULL))
5418d23ce8fSStephen Hemminger 		goto fail;
5428d23ce8fSStephen Hemminger 
5438d23ce8fSStephen Hemminger 	switch (direction) {
5448d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_IN:
5458d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_INBOUND;
5468d23ce8fSStephen Hemminger 		break;
5478d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_OUT:
5488d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_OUTBOUND;
5498d23ce8fSStephen Hemminger 		break;
5508d23ce8fSStephen Hemminger 	default:
5518d23ce8fSStephen Hemminger 		flags = 0;
5528d23ce8fSStephen Hemminger 	}
5538d23ce8fSStephen Hemminger 
5548d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
5558d23ce8fSStephen Hemminger 				&flags, sizeof(flags));
5568d23ce8fSStephen Hemminger 
5578d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
5588d23ce8fSStephen Hemminger 				&queue, sizeof(queue));
5598d23ce8fSStephen Hemminger 
560d2e3c4b8SStephen Hemminger 	if (rss_hash) {
561d2e3c4b8SStephen Hemminger 		uint8_t hash_opt[5];
562d2e3c4b8SStephen Hemminger 
563d2e3c4b8SStephen Hemminger 		/* The algorithm could be something else if
564d2e3c4b8SStephen Hemminger 		 * using rte_flow_action_rss; but the current API does not
565d2e3c4b8SStephen Hemminger 		 * have a way for ethdev to report  this on a per-packet basis.
566d2e3c4b8SStephen Hemminger 		 */
567d2e3c4b8SStephen Hemminger 		hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
568d2e3c4b8SStephen Hemminger 
569d2e3c4b8SStephen Hemminger 		memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
570d2e3c4b8SStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
571d2e3c4b8SStephen Hemminger 					&hash_opt, sizeof(hash_opt));
572d2e3c4b8SStephen Hemminger 	}
573d2e3c4b8SStephen Hemminger 
574c1abd1e9SAmit Prakash Shukla 	if (comment)
575c1abd1e9SAmit Prakash Shukla 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, comment,
576c1abd1e9SAmit Prakash Shukla 					strlen(comment));
577c1abd1e9SAmit Prakash Shukla 
5788d23ce8fSStephen Hemminger 	/* Note: END_OPT necessary here. Wireshark doesn't do it. */
5798d23ce8fSStephen Hemminger 
5808d23ce8fSStephen Hemminger 	/* Add PCAPNG packet header */
5818d23ce8fSStephen Hemminger 	epb = (struct pcapng_enhance_packet_block *)
5828d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(mc, sizeof(*epb));
5838d23ce8fSStephen Hemminger 	if (unlikely(epb == NULL))
5848d23ce8fSStephen Hemminger 		goto fail;
5858d23ce8fSStephen Hemminger 
5868d23ce8fSStephen Hemminger 	epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
5876db35853SOleksandr Nahnybida 	epb->block_length = rte_pktmbuf_pkt_len(mc);
5888d23ce8fSStephen Hemminger 
5898d23ce8fSStephen Hemminger 	/* Interface index is filled in later during write */
5908d23ce8fSStephen Hemminger 	mc->port = port_id;
5918d23ce8fSStephen Hemminger 
59216659193SStephen Hemminger 	/* Put timestamp in cycles here - adjust in packet write */
59316659193SStephen Hemminger 	timestamp = rte_get_tsc_cycles();
59416659193SStephen Hemminger 	epb->timestamp_hi = timestamp >> 32;
59516659193SStephen Hemminger 	epb->timestamp_lo = (uint32_t)timestamp;
5966db35853SOleksandr Nahnybida 	epb->capture_length = pkt_len;
5978d23ce8fSStephen Hemminger 	epb->original_length = orig_len;
5988d23ce8fSStephen Hemminger 
5998d23ce8fSStephen Hemminger 	/* set trailer of block length */
6008d23ce8fSStephen Hemminger 	*(uint32_t *)opt = epb->block_length;
6018d23ce8fSStephen Hemminger 
6028d23ce8fSStephen Hemminger 	return mc;
6038d23ce8fSStephen Hemminger 
6048d23ce8fSStephen Hemminger fail:
6058d23ce8fSStephen Hemminger 	rte_pktmbuf_free(mc);
6068d23ce8fSStephen Hemminger 	return NULL;
6078d23ce8fSStephen Hemminger }
6088d23ce8fSStephen Hemminger 
6098d23ce8fSStephen Hemminger /* Write pre-formatted packets to file. */
6108d23ce8fSStephen Hemminger ssize_t
6118d23ce8fSStephen Hemminger rte_pcapng_write_packets(rte_pcapng_t *self,
6128d23ce8fSStephen Hemminger 			 struct rte_mbuf *pkts[], uint16_t nb_pkts)
6138d23ce8fSStephen Hemminger {
6140744f1c9SMário Kuka 	struct iovec iov[IOV_MAX];
6150744f1c9SMário Kuka 	unsigned int i, cnt = 0;
6160744f1c9SMário Kuka 	ssize_t ret, total = 0;
6178d23ce8fSStephen Hemminger 
6180744f1c9SMário Kuka 	for (i = 0; i < nb_pkts; i++) {
6198d23ce8fSStephen Hemminger 		struct rte_mbuf *m = pkts[i];
6208d23ce8fSStephen Hemminger 		struct pcapng_enhance_packet_block *epb;
62116659193SStephen Hemminger 		uint64_t cycles, timestamp;
6228d23ce8fSStephen Hemminger 
6238d23ce8fSStephen Hemminger 		/* sanity check that is really a pcapng mbuf */
6248d23ce8fSStephen Hemminger 		epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
6258d23ce8fSStephen Hemminger 		if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
6266db35853SOleksandr Nahnybida 			     epb->block_length != rte_pktmbuf_pkt_len(m))) {
6278d23ce8fSStephen Hemminger 			rte_errno = EINVAL;
6288d23ce8fSStephen Hemminger 			return -1;
6298d23ce8fSStephen Hemminger 		}
6308d23ce8fSStephen Hemminger 
631d1da6d0dSStephen Hemminger 		/* check that this interface was added. */
632d1da6d0dSStephen Hemminger 		epb->interface_id = self->port_index[m->port];
633d1da6d0dSStephen Hemminger 		if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) {
634d1da6d0dSStephen Hemminger 			rte_errno = EINVAL;
635d1da6d0dSStephen Hemminger 			return -1;
636d1da6d0dSStephen Hemminger 		}
637d1da6d0dSStephen Hemminger 
63816659193SStephen Hemminger 		/* adjust timestamp recorded in packet */
63916659193SStephen Hemminger 		cycles = (uint64_t)epb->timestamp_hi << 32;
64016659193SStephen Hemminger 		cycles += epb->timestamp_lo;
64116659193SStephen Hemminger 		timestamp = pcapng_timestamp(self, cycles);
64216659193SStephen Hemminger 		epb->timestamp_hi = timestamp >> 32;
64316659193SStephen Hemminger 		epb->timestamp_lo = (uint32_t)timestamp;
64416659193SStephen Hemminger 
6458d23ce8fSStephen Hemminger 		/*
6460744f1c9SMário Kuka 		 * Handle case of highly fragmented and large burst size
6470744f1c9SMário Kuka 		 * Note: this assumes that max segments per mbuf < IOV_MAX
6480744f1c9SMário Kuka 		 */
6490744f1c9SMário Kuka 		if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
6500744f1c9SMário Kuka 			ret = writev(self->outfd, iov, cnt);
6510744f1c9SMário Kuka 			if (unlikely(ret < 0)) {
6520744f1c9SMário Kuka 				rte_errno = errno;
6530744f1c9SMário Kuka 				return -1;
6540744f1c9SMário Kuka 			}
6550744f1c9SMário Kuka 			total += ret;
6560744f1c9SMário Kuka 			cnt = 0;
6570744f1c9SMário Kuka 		}
6580744f1c9SMário Kuka 
6590744f1c9SMário Kuka 		/*
6608d23ce8fSStephen Hemminger 		 * The DPDK port is recorded during pcapng_copy.
6618d23ce8fSStephen Hemminger 		 * Map that to PCAPNG interface in file.
6628d23ce8fSStephen Hemminger 		 */
6638d23ce8fSStephen Hemminger 		do {
6648d23ce8fSStephen Hemminger 			iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
6658d23ce8fSStephen Hemminger 			iov[cnt].iov_len = rte_pktmbuf_data_len(m);
6668d23ce8fSStephen Hemminger 			++cnt;
6678d23ce8fSStephen Hemminger 		} while ((m = m->next));
6688d23ce8fSStephen Hemminger 	}
6698d23ce8fSStephen Hemminger 
6700744f1c9SMário Kuka 	ret = writev(self->outfd, iov, cnt);
6710744f1c9SMário Kuka 	if (unlikely(ret < 0)) {
6728d23ce8fSStephen Hemminger 		rte_errno = errno;
6730744f1c9SMário Kuka 		return -1;
6740744f1c9SMário Kuka 	}
6750744f1c9SMário Kuka 	return total + ret;
6768d23ce8fSStephen Hemminger }
6778d23ce8fSStephen Hemminger 
6788d23ce8fSStephen Hemminger /* Create new pcapng writer handle */
6798d23ce8fSStephen Hemminger rte_pcapng_t *
6808d23ce8fSStephen Hemminger rte_pcapng_fdopen(int fd,
6818d23ce8fSStephen Hemminger 		  const char *osname, const char *hardware,
6828d23ce8fSStephen Hemminger 		  const char *appname, const char *comment)
6838d23ce8fSStephen Hemminger {
684d1da6d0dSStephen Hemminger 	unsigned int i;
6858d23ce8fSStephen Hemminger 	rte_pcapng_t *self;
68616659193SStephen Hemminger 	struct timespec ts;
68716659193SStephen Hemminger 	uint64_t cycles;
6888d23ce8fSStephen Hemminger 
6898d23ce8fSStephen Hemminger 	self = malloc(sizeof(*self));
6908d23ce8fSStephen Hemminger 	if (!self) {
6918d23ce8fSStephen Hemminger 		rte_errno = ENOMEM;
6928d23ce8fSStephen Hemminger 		return NULL;
6938d23ce8fSStephen Hemminger 	}
6948d23ce8fSStephen Hemminger 
6958d23ce8fSStephen Hemminger 	self->outfd = fd;
696d1da6d0dSStephen Hemminger 	self->ports = 0;
69716659193SStephen Hemminger 
69816659193SStephen Hemminger 	/* record start time in ns since 1/1/1970 */
69916659193SStephen Hemminger 	cycles = rte_get_tsc_cycles();
70016659193SStephen Hemminger 	clock_gettime(CLOCK_REALTIME, &ts);
70116659193SStephen Hemminger 	self->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
70216659193SStephen Hemminger 	self->offset_ns = rte_timespec_to_ns(&ts);
70316659193SStephen Hemminger 
704d1da6d0dSStephen Hemminger 	for (i = 0; i < RTE_MAX_ETHPORTS; i++)
705d1da6d0dSStephen Hemminger 		self->port_index[i] = UINT32_MAX;
7068d23ce8fSStephen Hemminger 
7078d23ce8fSStephen Hemminger 	if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
7088d23ce8fSStephen Hemminger 		goto fail;
7098d23ce8fSStephen Hemminger 
7108d23ce8fSStephen Hemminger 	return self;
7118d23ce8fSStephen Hemminger fail:
7128d23ce8fSStephen Hemminger 	free(self);
7138d23ce8fSStephen Hemminger 	return NULL;
7148d23ce8fSStephen Hemminger }
7158d23ce8fSStephen Hemminger 
7168d23ce8fSStephen Hemminger void
7178d23ce8fSStephen Hemminger rte_pcapng_close(rte_pcapng_t *self)
7188d23ce8fSStephen Hemminger {
7198d23ce8fSStephen Hemminger 	close(self->outfd);
7208d23ce8fSStephen Hemminger 	free(self);
7218d23ce8fSStephen Hemminger }
722