xref: /dpdk/lib/pcapng/rte_pcapng.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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>
78d23ce8fSStephen Hemminger #include <stdio.h>
88d23ce8fSStephen Hemminger #include <stdlib.h>
98d23ce8fSStephen Hemminger #include <string.h>
108d23ce8fSStephen Hemminger #include <sys/uio.h>
118d23ce8fSStephen Hemminger #include <sys/utsname.h>
128d23ce8fSStephen Hemminger #include <time.h>
138d23ce8fSStephen Hemminger #include <unistd.h>
148d23ce8fSStephen Hemminger 
158d23ce8fSStephen Hemminger #include <rte_common.h>
168d23ce8fSStephen Hemminger #include <rte_cycles.h>
178d23ce8fSStephen Hemminger #include <rte_dev.h>
188d23ce8fSStephen Hemminger #include <rte_errno.h>
198d23ce8fSStephen Hemminger #include <rte_ethdev.h>
208d23ce8fSStephen Hemminger #include <rte_ether.h>
218d23ce8fSStephen Hemminger #include <rte_mbuf.h>
228d23ce8fSStephen Hemminger #include <rte_pcapng.h>
238d23ce8fSStephen Hemminger #include <rte_time.h>
248d23ce8fSStephen Hemminger 
258d23ce8fSStephen Hemminger #include "pcapng_proto.h"
268d23ce8fSStephen Hemminger 
278d23ce8fSStephen Hemminger /* conversion from DPDK speed to PCAPNG */
288d23ce8fSStephen Hemminger #define PCAPNG_MBPS_SPEED 1000000ull
298d23ce8fSStephen Hemminger 
308d23ce8fSStephen Hemminger /* Format of the capture file handle */
318d23ce8fSStephen Hemminger struct rte_pcapng {
328d23ce8fSStephen Hemminger 	int  outfd;		/* output file */
338d23ce8fSStephen Hemminger 	/* DPDK port id to interface index in file */
348d23ce8fSStephen Hemminger 	uint32_t port_index[RTE_MAX_ETHPORTS];
358d23ce8fSStephen Hemminger };
368d23ce8fSStephen Hemminger 
378d23ce8fSStephen Hemminger /* For converting TSC cycles to PCAPNG ns format */
388d23ce8fSStephen Hemminger struct pcapng_time {
398d23ce8fSStephen Hemminger 	uint64_t ns;
408d23ce8fSStephen Hemminger 	uint64_t cycles;
418d23ce8fSStephen Hemminger } pcapng_time;
428d23ce8fSStephen Hemminger 
438d23ce8fSStephen Hemminger RTE_INIT(pcapng_init)
448d23ce8fSStephen Hemminger {
458d23ce8fSStephen Hemminger 	struct timespec ts;
468d23ce8fSStephen Hemminger 
478d23ce8fSStephen Hemminger 	pcapng_time.cycles = rte_get_tsc_cycles();
488d23ce8fSStephen Hemminger 	clock_gettime(CLOCK_REALTIME, &ts);
498d23ce8fSStephen Hemminger 	pcapng_time.ns = rte_timespec_to_ns(&ts);
508d23ce8fSStephen Hemminger }
518d23ce8fSStephen Hemminger 
528d23ce8fSStephen Hemminger /* PCAPNG timestamps are in nanoseconds */
538d23ce8fSStephen Hemminger static uint64_t pcapng_tsc_to_ns(uint64_t cycles)
548d23ce8fSStephen Hemminger {
558d23ce8fSStephen Hemminger 	uint64_t delta;
568d23ce8fSStephen Hemminger 
578d23ce8fSStephen Hemminger 	delta = cycles - pcapng_time.cycles;
588d23ce8fSStephen Hemminger 	return pcapng_time.ns + (delta * NSEC_PER_SEC) / rte_get_tsc_hz();
598d23ce8fSStephen Hemminger }
608d23ce8fSStephen Hemminger 
618d23ce8fSStephen Hemminger /* length of option including padding */
628d23ce8fSStephen Hemminger static uint16_t pcapng_optlen(uint16_t len)
638d23ce8fSStephen Hemminger {
648d23ce8fSStephen Hemminger 	return RTE_ALIGN(sizeof(struct pcapng_option) + len,
658d23ce8fSStephen Hemminger 			 sizeof(uint32_t));
668d23ce8fSStephen Hemminger }
678d23ce8fSStephen Hemminger 
688d23ce8fSStephen Hemminger /* build TLV option and return location of next */
698d23ce8fSStephen Hemminger static struct pcapng_option *
708d23ce8fSStephen Hemminger pcapng_add_option(struct pcapng_option *popt, uint16_t code,
718d23ce8fSStephen Hemminger 		  const void *data, uint16_t len)
728d23ce8fSStephen Hemminger {
738d23ce8fSStephen Hemminger 	popt->code = code;
748d23ce8fSStephen Hemminger 	popt->length = len;
758d23ce8fSStephen Hemminger 	memcpy(popt->data, data, len);
768d23ce8fSStephen Hemminger 
778d23ce8fSStephen Hemminger 	return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
788d23ce8fSStephen Hemminger }
798d23ce8fSStephen Hemminger 
808d23ce8fSStephen Hemminger /*
818d23ce8fSStephen Hemminger  * Write required initial section header describing the capture
828d23ce8fSStephen Hemminger  */
838d23ce8fSStephen Hemminger static int
848d23ce8fSStephen Hemminger pcapng_section_block(rte_pcapng_t *self,
858d23ce8fSStephen Hemminger 		    const char *os, const char *hw,
868d23ce8fSStephen Hemminger 		    const char *app, const char *comment)
878d23ce8fSStephen Hemminger {
888d23ce8fSStephen Hemminger 	struct pcapng_section_header *hdr;
898d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
908d23ce8fSStephen Hemminger 	void *buf;
918d23ce8fSStephen Hemminger 	uint32_t len;
928d23ce8fSStephen Hemminger 	ssize_t cc;
938d23ce8fSStephen Hemminger 
948d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
958d23ce8fSStephen Hemminger 	if (hw)
968d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(hw));
978d23ce8fSStephen Hemminger 	if (os)
988d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(os));
998d23ce8fSStephen Hemminger 	if (app)
1008d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(app));
1018d23ce8fSStephen Hemminger 	if (comment)
1028d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(comment));
1038d23ce8fSStephen Hemminger 
1048d23ce8fSStephen Hemminger 	/* reserve space for OPT_END */
1058d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
1068d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
1078d23ce8fSStephen Hemminger 
1088d23ce8fSStephen Hemminger 	buf = calloc(1, len);
1098d23ce8fSStephen Hemminger 	if (!buf)
1108d23ce8fSStephen Hemminger 		return -1;
1118d23ce8fSStephen Hemminger 
1128d23ce8fSStephen Hemminger 	hdr = (struct pcapng_section_header *)buf;
1138d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_section_header) {
1148d23ce8fSStephen Hemminger 		.block_type = PCAPNG_SECTION_BLOCK,
1158d23ce8fSStephen Hemminger 		.block_length = len,
1168d23ce8fSStephen Hemminger 		.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
1178d23ce8fSStephen Hemminger 		.major_version = PCAPNG_MAJOR_VERS,
1188d23ce8fSStephen Hemminger 		.minor_version = PCAPNG_MINOR_VERS,
1198d23ce8fSStephen Hemminger 		.section_length = UINT64_MAX,
1208d23ce8fSStephen Hemminger 	};
1218d23ce8fSStephen Hemminger 
1228d23ce8fSStephen Hemminger 	/* After the section header insert variable length options. */
1238d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
1248d23ce8fSStephen Hemminger 	if (comment)
1258d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
1268d23ce8fSStephen Hemminger 					comment, strlen(comment));
1278d23ce8fSStephen Hemminger 	if (hw)
1288d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
1298d23ce8fSStephen Hemminger 					hw, strlen(hw));
1308d23ce8fSStephen Hemminger 	if (os)
1318d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
1328d23ce8fSStephen Hemminger 					os, strlen(os));
1338d23ce8fSStephen Hemminger 	if (app)
1348d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
1358d23ce8fSStephen Hemminger 					app, strlen(app));
1368d23ce8fSStephen Hemminger 
1378d23ce8fSStephen Hemminger 	/* The standard requires last option to be OPT_END */
1388d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
1398d23ce8fSStephen Hemminger 
1408d23ce8fSStephen Hemminger 	/* clone block_length after option */
1418d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
1428d23ce8fSStephen Hemminger 
1438d23ce8fSStephen Hemminger 	cc = write(self->outfd, buf, len);
1448d23ce8fSStephen Hemminger 	free(buf);
1458d23ce8fSStephen Hemminger 
1468d23ce8fSStephen Hemminger 	return cc;
1478d23ce8fSStephen Hemminger }
1488d23ce8fSStephen Hemminger 
1498d23ce8fSStephen Hemminger /* Write an interface block for a DPDK port */
1508d23ce8fSStephen Hemminger static int
1518d23ce8fSStephen Hemminger pcapng_add_interface(rte_pcapng_t *self, uint16_t port)
1528d23ce8fSStephen Hemminger {
1538d23ce8fSStephen Hemminger 	struct pcapng_interface_block *hdr;
1548d23ce8fSStephen Hemminger 	struct rte_eth_dev_info dev_info;
1558d23ce8fSStephen Hemminger 	struct rte_ether_addr *ea, macaddr;
1568d23ce8fSStephen Hemminger 	const struct rte_device *dev;
1578d23ce8fSStephen Hemminger 	struct rte_eth_link link;
1588d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
1598d23ce8fSStephen Hemminger 	const uint8_t tsresol = 9;	/* nanosecond resolution */
1608d23ce8fSStephen Hemminger 	uint32_t len;
1618d23ce8fSStephen Hemminger 	void *buf;
1628d23ce8fSStephen Hemminger 	char ifname[IF_NAMESIZE];
1638d23ce8fSStephen Hemminger 	char ifhw[256];
1648d23ce8fSStephen Hemminger 	uint64_t speed = 0;
1658d23ce8fSStephen Hemminger 
1668d23ce8fSStephen Hemminger 	if (rte_eth_dev_info_get(port, &dev_info) < 0)
1678d23ce8fSStephen Hemminger 		return -1;
1688d23ce8fSStephen Hemminger 
1698d23ce8fSStephen Hemminger 	/* make something like an interface name */
1708d23ce8fSStephen Hemminger 	if (if_indextoname(dev_info.if_index, ifname) == NULL)
1718d23ce8fSStephen Hemminger 		snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port);
1728d23ce8fSStephen Hemminger 
1738d23ce8fSStephen Hemminger 	/* make a useful device hardware string */
1748d23ce8fSStephen Hemminger 	dev = dev_info.device;
1758d23ce8fSStephen Hemminger 	if (dev)
1768d23ce8fSStephen Hemminger 		snprintf(ifhw, sizeof(ifhw),
1778d23ce8fSStephen Hemminger 			 "%s-%s", dev->bus->name, dev->name);
1788d23ce8fSStephen Hemminger 
1798d23ce8fSStephen Hemminger 	/* DPDK reports in units of Mbps */
1808d23ce8fSStephen Hemminger 	rte_eth_link_get(port, &link);
1818d23ce8fSStephen Hemminger 	if (link.link_status == ETH_LINK_UP)
1828d23ce8fSStephen Hemminger 		speed = link.link_speed * PCAPNG_MBPS_SPEED;
1838d23ce8fSStephen Hemminger 
1848d23ce8fSStephen Hemminger 	if (rte_eth_macaddr_get(port, &macaddr) < 0)
1858d23ce8fSStephen Hemminger 		ea = NULL;
1868d23ce8fSStephen Hemminger 	else
1878d23ce8fSStephen Hemminger 		ea = &macaddr;
1888d23ce8fSStephen Hemminger 
1898d23ce8fSStephen Hemminger 	/* Compute length of interface block options */
1908d23ce8fSStephen Hemminger 	len = sizeof(*hdr);
1918d23ce8fSStephen Hemminger 
1928d23ce8fSStephen Hemminger 	len += pcapng_optlen(sizeof(tsresol));	/* timestamp */
1938d23ce8fSStephen Hemminger 	len += pcapng_optlen(strlen(ifname));	/* ifname */
1948d23ce8fSStephen Hemminger 
1958d23ce8fSStephen Hemminger 	if (ea)
1968d23ce8fSStephen Hemminger 		len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
1978d23ce8fSStephen Hemminger 	if (speed != 0)
1988d23ce8fSStephen Hemminger 		len += pcapng_optlen(sizeof(uint64_t));
1998d23ce8fSStephen Hemminger 	if (dev)
2008d23ce8fSStephen Hemminger 		len += pcapng_optlen(strlen(ifhw));
2018d23ce8fSStephen Hemminger 
2028d23ce8fSStephen Hemminger 	len += pcapng_optlen(0);
2038d23ce8fSStephen Hemminger 	len += sizeof(uint32_t);
2048d23ce8fSStephen Hemminger 
2058d23ce8fSStephen Hemminger 	buf = alloca(len);
2068d23ce8fSStephen Hemminger 	if (!buf)
2078d23ce8fSStephen Hemminger 		return -1;
2088d23ce8fSStephen Hemminger 
2098d23ce8fSStephen Hemminger 	hdr = (struct pcapng_interface_block *)buf;
2108d23ce8fSStephen Hemminger 	*hdr = (struct pcapng_interface_block) {
2118d23ce8fSStephen Hemminger 		.block_type = PCAPNG_INTERFACE_BLOCK,
2128d23ce8fSStephen Hemminger 		.link_type = 1,		/* DLT_EN10MB - Ethernet */
2138d23ce8fSStephen Hemminger 		.block_length = len,
2148d23ce8fSStephen Hemminger 	};
2158d23ce8fSStephen Hemminger 
2168d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
2178d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
2188d23ce8fSStephen Hemminger 				&tsresol, sizeof(tsresol));
2198d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
2208d23ce8fSStephen Hemminger 				ifname, strlen(ifname));
2218d23ce8fSStephen Hemminger 	if (ea)
2228d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
2238d23ce8fSStephen Hemminger 					ea, RTE_ETHER_ADDR_LEN);
2248d23ce8fSStephen Hemminger 	if (speed != 0)
2258d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
2268d23ce8fSStephen Hemminger 					 &speed, sizeof(uint64_t));
2278d23ce8fSStephen Hemminger 	if (dev)
2288d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
2298d23ce8fSStephen Hemminger 					 ifhw, strlen(ifhw));
2308d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
2318d23ce8fSStephen Hemminger 
2328d23ce8fSStephen Hemminger 	/* clone block_length after optionsa */
2338d23ce8fSStephen Hemminger 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
2348d23ce8fSStephen Hemminger 
2358d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
2368d23ce8fSStephen Hemminger }
2378d23ce8fSStephen Hemminger 
2388d23ce8fSStephen Hemminger /*
2398d23ce8fSStephen Hemminger  * Write the list of possible interfaces at the start
2408d23ce8fSStephen Hemminger  * of the file.
2418d23ce8fSStephen Hemminger  */
2428d23ce8fSStephen Hemminger static int
2438d23ce8fSStephen Hemminger pcapng_interfaces(rte_pcapng_t *self)
2448d23ce8fSStephen Hemminger {
2458d23ce8fSStephen Hemminger 	uint16_t port_id;
2468d23ce8fSStephen Hemminger 	uint16_t index = 0;
2478d23ce8fSStephen Hemminger 
2488d23ce8fSStephen Hemminger 	RTE_ETH_FOREACH_DEV(port_id) {
2498d23ce8fSStephen Hemminger 		/* The list if ports in pcapng needs to be contiguous */
2508d23ce8fSStephen Hemminger 		self->port_index[port_id] = index++;
2518d23ce8fSStephen Hemminger 		if (pcapng_add_interface(self, port_id) < 0)
2528d23ce8fSStephen Hemminger 			return -1;
2538d23ce8fSStephen Hemminger 	}
2548d23ce8fSStephen Hemminger 	return 0;
2558d23ce8fSStephen Hemminger }
2568d23ce8fSStephen Hemminger 
2578d23ce8fSStephen Hemminger /*
2588d23ce8fSStephen Hemminger  * Write an Interface statistics block at the end of capture.
2598d23ce8fSStephen Hemminger  */
2608d23ce8fSStephen Hemminger ssize_t
2618d23ce8fSStephen Hemminger rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
2628d23ce8fSStephen Hemminger 		       const char *comment,
2638d23ce8fSStephen Hemminger 		       uint64_t start_time, uint64_t end_time,
2648d23ce8fSStephen Hemminger 		       uint64_t ifrecv, uint64_t ifdrop)
2658d23ce8fSStephen Hemminger {
2668d23ce8fSStephen Hemminger 	struct pcapng_statistics *hdr;
2678d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
2688d23ce8fSStephen Hemminger 	uint32_t optlen, len;
2698d23ce8fSStephen Hemminger 	uint8_t *buf;
2708d23ce8fSStephen Hemminger 	uint64_t ns;
2718d23ce8fSStephen Hemminger 
2728d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
2738d23ce8fSStephen Hemminger 
2748d23ce8fSStephen Hemminger 	optlen = 0;
2758d23ce8fSStephen Hemminger 
2768d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
2778d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifrecv));
2788d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
2798d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(ifdrop));
2808d23ce8fSStephen Hemminger 	if (start_time != 0)
2818d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(start_time));
2828d23ce8fSStephen Hemminger 	if (end_time != 0)
2838d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(sizeof(end_time));
2848d23ce8fSStephen Hemminger 	if (comment)
2858d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(strlen(comment));
2868d23ce8fSStephen Hemminger 	if (optlen != 0)
2878d23ce8fSStephen Hemminger 		optlen += pcapng_optlen(0);
2888d23ce8fSStephen Hemminger 
2898d23ce8fSStephen Hemminger 	len = sizeof(*hdr) + optlen + sizeof(uint32_t);
2908d23ce8fSStephen Hemminger 	buf = alloca(len);
2918d23ce8fSStephen Hemminger 	if (buf == NULL)
2928d23ce8fSStephen Hemminger 		return -1;
2938d23ce8fSStephen Hemminger 
2948d23ce8fSStephen Hemminger 	hdr = (struct pcapng_statistics *)buf;
2958d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)(hdr + 1);
2968d23ce8fSStephen Hemminger 
2978d23ce8fSStephen Hemminger 	if (comment)
2988d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
2998d23ce8fSStephen Hemminger 					comment, strlen(comment));
3008d23ce8fSStephen Hemminger 	if (start_time != 0)
3018d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
3028d23ce8fSStephen Hemminger 					 &start_time, sizeof(start_time));
3038d23ce8fSStephen Hemminger 	if (end_time != 0)
3048d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_ENDTIME,
3058d23ce8fSStephen Hemminger 					 &end_time, sizeof(end_time));
3068d23ce8fSStephen Hemminger 	if (ifrecv != UINT64_MAX)
3078d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
3088d23ce8fSStephen Hemminger 				&ifrecv, sizeof(ifrecv));
3098d23ce8fSStephen Hemminger 	if (ifdrop != UINT64_MAX)
3108d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
3118d23ce8fSStephen Hemminger 				&ifdrop, sizeof(ifdrop));
3128d23ce8fSStephen Hemminger 	if (optlen != 0)
3138d23ce8fSStephen Hemminger 		opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
3148d23ce8fSStephen Hemminger 
3158d23ce8fSStephen Hemminger 	hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
3168d23ce8fSStephen Hemminger 	hdr->block_length = len;
3178d23ce8fSStephen Hemminger 	hdr->interface_id = self->port_index[port_id];
3188d23ce8fSStephen Hemminger 
3198d23ce8fSStephen Hemminger 	ns = pcapng_tsc_to_ns(rte_get_tsc_cycles());
3208d23ce8fSStephen Hemminger 	hdr->timestamp_hi = ns >> 32;
3218d23ce8fSStephen Hemminger 	hdr->timestamp_lo = (uint32_t)ns;
3228d23ce8fSStephen Hemminger 
3238d23ce8fSStephen Hemminger 	/* clone block_length after option */
3248d23ce8fSStephen Hemminger 	memcpy(opt, &len, sizeof(uint32_t));
3258d23ce8fSStephen Hemminger 
3268d23ce8fSStephen Hemminger 	return write(self->outfd, buf, len);
3278d23ce8fSStephen Hemminger }
3288d23ce8fSStephen Hemminger 
3298d23ce8fSStephen Hemminger uint32_t
3308d23ce8fSStephen Hemminger rte_pcapng_mbuf_size(uint32_t length)
3318d23ce8fSStephen Hemminger {
3328d23ce8fSStephen Hemminger 	/* The VLAN and EPB header must fit in the mbuf headroom. */
3338d23ce8fSStephen Hemminger 	RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
3348d23ce8fSStephen Hemminger 		   sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
3358d23ce8fSStephen Hemminger 
3368d23ce8fSStephen Hemminger 	/* The flags and queue information are added at the end. */
3378d23ce8fSStephen Hemminger 	return sizeof(struct rte_mbuf)
3388d23ce8fSStephen Hemminger 		+ RTE_ALIGN(length, sizeof(uint32_t))
3398d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* flag option */
3408d23ce8fSStephen Hemminger 		+ pcapng_optlen(sizeof(uint32_t)) /* queue option */
3418d23ce8fSStephen Hemminger 		+ sizeof(uint32_t);		  /*  length */
3428d23ce8fSStephen Hemminger }
3438d23ce8fSStephen Hemminger 
3448d23ce8fSStephen Hemminger /* More generalized version rte_vlan_insert() */
3458d23ce8fSStephen Hemminger static int
3468d23ce8fSStephen Hemminger pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
3478d23ce8fSStephen Hemminger {
3488d23ce8fSStephen Hemminger 	struct rte_ether_hdr *nh, *oh;
3498d23ce8fSStephen Hemminger 	struct rte_vlan_hdr *vh;
3508d23ce8fSStephen Hemminger 
3518d23ce8fSStephen Hemminger 	if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
3528d23ce8fSStephen Hemminger 		return -EINVAL;
3538d23ce8fSStephen Hemminger 
3548d23ce8fSStephen Hemminger 	if (rte_pktmbuf_data_len(m) < sizeof(*oh))
3558d23ce8fSStephen Hemminger 		return -EINVAL;
3568d23ce8fSStephen Hemminger 
3578d23ce8fSStephen Hemminger 	oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
3588d23ce8fSStephen Hemminger 	nh = (struct rte_ether_hdr *)
3598d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
3608d23ce8fSStephen Hemminger 	if (nh == NULL)
3618d23ce8fSStephen Hemminger 		return -ENOSPC;
3628d23ce8fSStephen Hemminger 
3638d23ce8fSStephen Hemminger 	memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
3648d23ce8fSStephen Hemminger 	nh->ether_type = rte_cpu_to_be_16(ether_type);
3658d23ce8fSStephen Hemminger 
3668d23ce8fSStephen Hemminger 	vh = (struct rte_vlan_hdr *) (nh + 1);
3678d23ce8fSStephen Hemminger 	vh->vlan_tci = rte_cpu_to_be_16(tci);
3688d23ce8fSStephen Hemminger 
3698d23ce8fSStephen Hemminger 	return 0;
3708d23ce8fSStephen Hemminger }
3718d23ce8fSStephen Hemminger 
3728d23ce8fSStephen Hemminger /*
3738d23ce8fSStephen Hemminger  *   The mbufs created use the Pcapng standard enhanced packet  block.
3748d23ce8fSStephen Hemminger  *
3758d23ce8fSStephen Hemminger  *                         1                   2                   3
3768d23ce8fSStephen 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
3778d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3788d23ce8fSStephen Hemminger  *  0 |                    Block Type = 0x00000006                    |
3798d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3808d23ce8fSStephen Hemminger  *  4 |                      Block Total Length                       |
3818d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3828d23ce8fSStephen Hemminger  *  8 |                         Interface ID                          |
3838d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3848d23ce8fSStephen Hemminger  * 12 |                        Timestamp (High)                       |
3858d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3868d23ce8fSStephen Hemminger  * 16 |                        Timestamp (Low)                        |
3878d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3888d23ce8fSStephen Hemminger  * 20 |                    Captured Packet Length                     |
3898d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3908d23ce8fSStephen Hemminger  * 24 |                    Original Packet Length                     |
3918d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3928d23ce8fSStephen Hemminger  * 28 /                                                               /
3938d23ce8fSStephen Hemminger  *    /                          Packet Data                          /
3948d23ce8fSStephen Hemminger  *    /              variable length, padded to 32 bits               /
3958d23ce8fSStephen Hemminger  *    /                                                               /
3968d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3978d23ce8fSStephen Hemminger  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
3988d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3998d23ce8fSStephen Hemminger  *    |              Flags (direction)                                |
4008d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4018d23ce8fSStephen Hemminger  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
4028d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4038d23ce8fSStephen Hemminger  *    |              Queue id                                         |
4048d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4058d23ce8fSStephen Hemminger  *    |                      Block Total Length                       |
4068d23ce8fSStephen Hemminger  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4078d23ce8fSStephen Hemminger  */
4088d23ce8fSStephen Hemminger 
4098d23ce8fSStephen Hemminger /* Make a copy of original mbuf with pcapng header and options */
4108d23ce8fSStephen Hemminger struct rte_mbuf *
4118d23ce8fSStephen Hemminger rte_pcapng_copy(uint16_t port_id, uint32_t queue,
4128d23ce8fSStephen Hemminger 		const struct rte_mbuf *md,
4138d23ce8fSStephen Hemminger 		struct rte_mempool *mp,
4148d23ce8fSStephen Hemminger 		uint32_t length, uint64_t cycles,
4158d23ce8fSStephen Hemminger 		enum rte_pcapng_direction direction)
4168d23ce8fSStephen Hemminger {
4178d23ce8fSStephen Hemminger 	struct pcapng_enhance_packet_block *epb;
4188d23ce8fSStephen Hemminger 	uint32_t orig_len, data_len, padding, flags;
4198d23ce8fSStephen Hemminger 	struct pcapng_option *opt;
4208d23ce8fSStephen Hemminger 	const uint16_t optlen = pcapng_optlen(sizeof(flags)) + pcapng_optlen(sizeof(queue));
4218d23ce8fSStephen Hemminger 	struct rte_mbuf *mc;
4228d23ce8fSStephen Hemminger 	uint64_t ns;
4238d23ce8fSStephen Hemminger 
4248d23ce8fSStephen Hemminger #ifdef RTE_LIBRTE_ETHDEV_DEBUG
4258d23ce8fSStephen Hemminger 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
4268d23ce8fSStephen Hemminger #endif
4278d23ce8fSStephen Hemminger 	ns = pcapng_tsc_to_ns(cycles);
4288d23ce8fSStephen Hemminger 
4298d23ce8fSStephen Hemminger 	orig_len = rte_pktmbuf_pkt_len(md);
4308d23ce8fSStephen Hemminger 
4318d23ce8fSStephen Hemminger 	/* Take snapshot of the data */
4328d23ce8fSStephen Hemminger 	mc = rte_pktmbuf_copy(md, mp, 0, length);
4338d23ce8fSStephen Hemminger 	if (unlikely(mc == NULL))
4348d23ce8fSStephen Hemminger 		return NULL;
4358d23ce8fSStephen Hemminger 
4368d23ce8fSStephen Hemminger 	/* Expand any offloaded VLAN information */
4378d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
438*daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
4398d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
440*daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
4418d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
4428d23ce8fSStephen Hemminger 				       md->vlan_tci) != 0)
4438d23ce8fSStephen Hemminger 			goto fail;
4448d23ce8fSStephen Hemminger 	}
4458d23ce8fSStephen Hemminger 
4468d23ce8fSStephen Hemminger 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
447*daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
4488d23ce8fSStephen Hemminger 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
449*daa02b5cSOlivier Matz 	     (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
4508d23ce8fSStephen Hemminger 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
4518d23ce8fSStephen Hemminger 				       md->vlan_tci_outer) != 0)
4528d23ce8fSStephen Hemminger 			goto fail;
4538d23ce8fSStephen Hemminger 	}
4548d23ce8fSStephen Hemminger 
4558d23ce8fSStephen Hemminger 	/* pad the packet to 32 bit boundary */
4568d23ce8fSStephen Hemminger 	data_len = rte_pktmbuf_data_len(mc);
4578d23ce8fSStephen Hemminger 	padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len;
4588d23ce8fSStephen Hemminger 	if (padding > 0) {
4598d23ce8fSStephen Hemminger 		void *tail = rte_pktmbuf_append(mc, padding);
4608d23ce8fSStephen Hemminger 
4618d23ce8fSStephen Hemminger 		if (tail == NULL)
4628d23ce8fSStephen Hemminger 			goto fail;
4638d23ce8fSStephen Hemminger 		memset(tail, 0, padding);
4648d23ce8fSStephen Hemminger 	}
4658d23ce8fSStephen Hemminger 
4668d23ce8fSStephen Hemminger 	/* reserve trailing options and block length */
4678d23ce8fSStephen Hemminger 	opt = (struct pcapng_option *)
4688d23ce8fSStephen Hemminger 		rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
4698d23ce8fSStephen Hemminger 	if (unlikely(opt == NULL))
4708d23ce8fSStephen Hemminger 		goto fail;
4718d23ce8fSStephen Hemminger 
4728d23ce8fSStephen Hemminger 	switch (direction) {
4738d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_IN:
4748d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_INBOUND;
4758d23ce8fSStephen Hemminger 		break;
4768d23ce8fSStephen Hemminger 	case RTE_PCAPNG_DIRECTION_OUT:
4778d23ce8fSStephen Hemminger 		flags = PCAPNG_IFB_OUTBOUND;
4788d23ce8fSStephen Hemminger 		break;
4798d23ce8fSStephen Hemminger 	default:
4808d23ce8fSStephen Hemminger 		flags = 0;
4818d23ce8fSStephen Hemminger 	}
4828d23ce8fSStephen Hemminger 
4838d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
4848d23ce8fSStephen Hemminger 				&flags, sizeof(flags));
4858d23ce8fSStephen Hemminger 
4868d23ce8fSStephen Hemminger 	opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
4878d23ce8fSStephen Hemminger 				&queue, sizeof(queue));
4888d23ce8fSStephen Hemminger 
4898d23ce8fSStephen Hemminger 	/* Note: END_OPT necessary here. Wireshark doesn't do it. */
4908d23ce8fSStephen Hemminger 
4918d23ce8fSStephen Hemminger 	/* Add PCAPNG packet header */
4928d23ce8fSStephen Hemminger 	epb = (struct pcapng_enhance_packet_block *)
4938d23ce8fSStephen Hemminger 		rte_pktmbuf_prepend(mc, sizeof(*epb));
4948d23ce8fSStephen Hemminger 	if (unlikely(epb == NULL))
4958d23ce8fSStephen Hemminger 		goto fail;
4968d23ce8fSStephen Hemminger 
4978d23ce8fSStephen Hemminger 	epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
4988d23ce8fSStephen Hemminger 	epb->block_length = rte_pktmbuf_data_len(mc);
4998d23ce8fSStephen Hemminger 
5008d23ce8fSStephen Hemminger 	/* Interface index is filled in later during write */
5018d23ce8fSStephen Hemminger 	mc->port = port_id;
5028d23ce8fSStephen Hemminger 
5038d23ce8fSStephen Hemminger 	epb->timestamp_hi = ns >> 32;
5048d23ce8fSStephen Hemminger 	epb->timestamp_lo = (uint32_t)ns;
5058d23ce8fSStephen Hemminger 	epb->capture_length = data_len;
5068d23ce8fSStephen Hemminger 	epb->original_length = orig_len;
5078d23ce8fSStephen Hemminger 
5088d23ce8fSStephen Hemminger 	/* set trailer of block length */
5098d23ce8fSStephen Hemminger 	*(uint32_t *)opt = epb->block_length;
5108d23ce8fSStephen Hemminger 
5118d23ce8fSStephen Hemminger 	return mc;
5128d23ce8fSStephen Hemminger 
5138d23ce8fSStephen Hemminger fail:
5148d23ce8fSStephen Hemminger 	rte_pktmbuf_free(mc);
5158d23ce8fSStephen Hemminger 	return NULL;
5168d23ce8fSStephen Hemminger }
5178d23ce8fSStephen Hemminger 
5188d23ce8fSStephen Hemminger /* Count how many segments are in this array of mbufs */
5198d23ce8fSStephen Hemminger static unsigned int
5208d23ce8fSStephen Hemminger mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n)
5218d23ce8fSStephen Hemminger {
5228d23ce8fSStephen Hemminger 	unsigned int i, iovcnt;
5238d23ce8fSStephen Hemminger 
5248d23ce8fSStephen Hemminger 	for (iovcnt = 0, i = 0; i < n; i++) {
5258d23ce8fSStephen Hemminger 		const struct rte_mbuf *m = pkts[i];
5268d23ce8fSStephen Hemminger 
5278d23ce8fSStephen Hemminger 		__rte_mbuf_sanity_check(m, 1);
5288d23ce8fSStephen Hemminger 
5298d23ce8fSStephen Hemminger 		iovcnt += m->nb_segs;
5308d23ce8fSStephen Hemminger 	}
5318d23ce8fSStephen Hemminger 	return iovcnt;
5328d23ce8fSStephen Hemminger }
5338d23ce8fSStephen Hemminger 
5348d23ce8fSStephen Hemminger /* Write pre-formatted packets to file. */
5358d23ce8fSStephen Hemminger ssize_t
5368d23ce8fSStephen Hemminger rte_pcapng_write_packets(rte_pcapng_t *self,
5378d23ce8fSStephen Hemminger 			 struct rte_mbuf *pkts[], uint16_t nb_pkts)
5388d23ce8fSStephen Hemminger {
5398d23ce8fSStephen Hemminger 	int iovcnt = mbuf_burst_segs(pkts, nb_pkts);
5408d23ce8fSStephen Hemminger 	struct iovec iov[iovcnt];
5418d23ce8fSStephen Hemminger 	unsigned int i, cnt;
5428d23ce8fSStephen Hemminger 	ssize_t ret;
5438d23ce8fSStephen Hemminger 
5448d23ce8fSStephen Hemminger 	for (i = cnt = 0; i < nb_pkts; i++) {
5458d23ce8fSStephen Hemminger 		struct rte_mbuf *m = pkts[i];
5468d23ce8fSStephen Hemminger 		struct pcapng_enhance_packet_block *epb;
5478d23ce8fSStephen Hemminger 
5488d23ce8fSStephen Hemminger 		/* sanity check that is really a pcapng mbuf */
5498d23ce8fSStephen Hemminger 		epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
5508d23ce8fSStephen Hemminger 		if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
5518d23ce8fSStephen Hemminger 			     epb->block_length != rte_pktmbuf_data_len(m))) {
5528d23ce8fSStephen Hemminger 			rte_errno = EINVAL;
5538d23ce8fSStephen Hemminger 			return -1;
5548d23ce8fSStephen Hemminger 		}
5558d23ce8fSStephen Hemminger 
5568d23ce8fSStephen Hemminger 		/*
5578d23ce8fSStephen Hemminger 		 * The DPDK port is recorded during pcapng_copy.
5588d23ce8fSStephen Hemminger 		 * Map that to PCAPNG interface in file.
5598d23ce8fSStephen Hemminger 		 */
5608d23ce8fSStephen Hemminger 		epb->interface_id = self->port_index[m->port];
5618d23ce8fSStephen Hemminger 		do {
5628d23ce8fSStephen Hemminger 			iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
5638d23ce8fSStephen Hemminger 			iov[cnt].iov_len = rte_pktmbuf_data_len(m);
5648d23ce8fSStephen Hemminger 			++cnt;
5658d23ce8fSStephen Hemminger 		} while ((m = m->next));
5668d23ce8fSStephen Hemminger 	}
5678d23ce8fSStephen Hemminger 
5688d23ce8fSStephen Hemminger 	ret = writev(self->outfd, iov, iovcnt);
5698d23ce8fSStephen Hemminger 	if (unlikely(ret < 0))
5708d23ce8fSStephen Hemminger 		rte_errno = errno;
5718d23ce8fSStephen Hemminger 	return ret;
5728d23ce8fSStephen Hemminger }
5738d23ce8fSStephen Hemminger 
5748d23ce8fSStephen Hemminger /* Create new pcapng writer handle */
5758d23ce8fSStephen Hemminger rte_pcapng_t *
5768d23ce8fSStephen Hemminger rte_pcapng_fdopen(int fd,
5778d23ce8fSStephen Hemminger 		  const char *osname, const char *hardware,
5788d23ce8fSStephen Hemminger 		  const char *appname, const char *comment)
5798d23ce8fSStephen Hemminger {
5808d23ce8fSStephen Hemminger 	rte_pcapng_t *self;
5818d23ce8fSStephen Hemminger 
5828d23ce8fSStephen Hemminger 	self = malloc(sizeof(*self));
5838d23ce8fSStephen Hemminger 	if (!self) {
5848d23ce8fSStephen Hemminger 		rte_errno = ENOMEM;
5858d23ce8fSStephen Hemminger 		return NULL;
5868d23ce8fSStephen Hemminger 	}
5878d23ce8fSStephen Hemminger 
5888d23ce8fSStephen Hemminger 	self->outfd = fd;
5898d23ce8fSStephen Hemminger 
5908d23ce8fSStephen Hemminger 	if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
5918d23ce8fSStephen Hemminger 		goto fail;
5928d23ce8fSStephen Hemminger 
5938d23ce8fSStephen Hemminger 	if (pcapng_interfaces(self) < 0)
5948d23ce8fSStephen Hemminger 		goto fail;
5958d23ce8fSStephen Hemminger 
5968d23ce8fSStephen Hemminger 	return self;
5978d23ce8fSStephen Hemminger fail:
5988d23ce8fSStephen Hemminger 	free(self);
5998d23ce8fSStephen Hemminger 	return NULL;
6008d23ce8fSStephen Hemminger }
6018d23ce8fSStephen Hemminger 
6028d23ce8fSStephen Hemminger void
6038d23ce8fSStephen Hemminger rte_pcapng_close(rte_pcapng_t *self)
6048d23ce8fSStephen Hemminger {
6058d23ce8fSStephen Hemminger 	close(self->outfd);
6068d23ce8fSStephen Hemminger 	free(self);
6078d23ce8fSStephen Hemminger }
608