xref: /dpdk/lib/pcapng/rte_pcapng.c (revision 87d396163c005deb8d9f72ec0977f19e5edd8f47)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019 Microsoft Corporation
3  */
4 
5 #include <errno.h>
6 #include <net/if.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/uio.h>
11 #include <time.h>
12 #include <unistd.h>
13 
14 #include <bus_driver.h>
15 #include <rte_common.h>
16 #include <rte_cycles.h>
17 #include <dev_driver.h>
18 #include <rte_errno.h>
19 #include <rte_ethdev.h>
20 #include <rte_ether.h>
21 #include <rte_mbuf.h>
22 #include <rte_pcapng.h>
23 #include <rte_reciprocal.h>
24 #include <rte_time.h>
25 
26 #include "pcapng_proto.h"
27 
28 /* conversion from DPDK speed to PCAPNG */
29 #define PCAPNG_MBPS_SPEED 1000000ull
30 
31 /* Format of the capture file handle */
32 struct rte_pcapng {
33 	int  outfd;		/* output file */
34 	/* DPDK port id to interface index in file */
35 	uint32_t port_index[RTE_MAX_ETHPORTS];
36 };
37 
38 /* For converting TSC cycles to PCAPNG ns format */
39 static struct pcapng_time {
40 	uint64_t ns;
41 	uint64_t cycles;
42 	uint64_t tsc_hz;
43 	struct rte_reciprocal_u64 tsc_hz_inverse;
44 } pcapng_time;
45 
46 static inline void
47 pcapng_init(void)
48 {
49 	struct timespec ts;
50 
51 	pcapng_time.cycles = rte_get_tsc_cycles();
52 	clock_gettime(CLOCK_REALTIME, &ts);
53 	pcapng_time.cycles = (pcapng_time.cycles + rte_get_tsc_cycles()) / 2;
54 	pcapng_time.ns = rte_timespec_to_ns(&ts);
55 
56 	pcapng_time.tsc_hz = rte_get_tsc_hz();
57 	pcapng_time.tsc_hz_inverse = rte_reciprocal_value_u64(pcapng_time.tsc_hz);
58 }
59 
60 /* PCAPNG timestamps are in nanoseconds */
61 static uint64_t pcapng_tsc_to_ns(uint64_t cycles)
62 {
63 	uint64_t delta, secs;
64 
65 	if (!pcapng_time.tsc_hz)
66 		pcapng_init();
67 
68 	/* In essence the calculation is:
69 	 *   delta = (cycles - pcapng_time.cycles) * NSEC_PRE_SEC / rte_get_tsc_hz()
70 	 * but this overflows within 4 to 8 seconds depending on TSC frequency.
71 	 * Instead, if delta >= pcapng_time.tsc_hz:
72 	 *   Increase pcapng_time.ns and pcapng_time.cycles by the number of
73 	 *   whole seconds in delta and reduce delta accordingly.
74 	 * delta will therefore always lie in the interval [0, pcapng_time.tsc_hz),
75 	 * which will not overflow when multiplied by NSEC_PER_SEC provided the
76 	 * TSC frequency < approx 18.4GHz.
77 	 *
78 	 * Currently all TSCs operate below 5GHz.
79 	 */
80 	delta = cycles - pcapng_time.cycles;
81 	if (unlikely(delta >= pcapng_time.tsc_hz)) {
82 		if (likely(delta < pcapng_time.tsc_hz * 2)) {
83 			delta -= pcapng_time.tsc_hz;
84 			pcapng_time.cycles += pcapng_time.tsc_hz;
85 			pcapng_time.ns += NSEC_PER_SEC;
86 		} else {
87 			secs = rte_reciprocal_divide_u64(delta, &pcapng_time.tsc_hz_inverse);
88 			delta -= secs * pcapng_time.tsc_hz;
89 			pcapng_time.cycles += secs * pcapng_time.tsc_hz;
90 			pcapng_time.ns += secs * NSEC_PER_SEC;
91 		}
92 	}
93 
94 	return pcapng_time.ns + rte_reciprocal_divide_u64(delta * NSEC_PER_SEC,
95 							  &pcapng_time.tsc_hz_inverse);
96 }
97 
98 /* length of option including padding */
99 static uint16_t pcapng_optlen(uint16_t len)
100 {
101 	return RTE_ALIGN(sizeof(struct pcapng_option) + len,
102 			 sizeof(uint32_t));
103 }
104 
105 /* build TLV option and return location of next */
106 static struct pcapng_option *
107 pcapng_add_option(struct pcapng_option *popt, uint16_t code,
108 		  const void *data, uint16_t len)
109 {
110 	popt->code = code;
111 	popt->length = len;
112 	memcpy(popt->data, data, len);
113 
114 	return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
115 }
116 
117 /*
118  * Write required initial section header describing the capture
119  */
120 static int
121 pcapng_section_block(rte_pcapng_t *self,
122 		    const char *os, const char *hw,
123 		    const char *app, const char *comment)
124 {
125 	struct pcapng_section_header *hdr;
126 	struct pcapng_option *opt;
127 	void *buf;
128 	uint32_t len;
129 	ssize_t cc;
130 
131 	len = sizeof(*hdr);
132 	if (hw)
133 		len += pcapng_optlen(strlen(hw));
134 	if (os)
135 		len += pcapng_optlen(strlen(os));
136 	if (app)
137 		len += pcapng_optlen(strlen(app));
138 	if (comment)
139 		len += pcapng_optlen(strlen(comment));
140 
141 	/* reserve space for OPT_END */
142 	len += pcapng_optlen(0);
143 	len += sizeof(uint32_t);
144 
145 	buf = calloc(1, len);
146 	if (!buf)
147 		return -1;
148 
149 	hdr = (struct pcapng_section_header *)buf;
150 	*hdr = (struct pcapng_section_header) {
151 		.block_type = PCAPNG_SECTION_BLOCK,
152 		.block_length = len,
153 		.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
154 		.major_version = PCAPNG_MAJOR_VERS,
155 		.minor_version = PCAPNG_MINOR_VERS,
156 		.section_length = UINT64_MAX,
157 	};
158 
159 	/* After the section header insert variable length options. */
160 	opt = (struct pcapng_option *)(hdr + 1);
161 	if (comment)
162 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
163 					comment, strlen(comment));
164 	if (hw)
165 		opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
166 					hw, strlen(hw));
167 	if (os)
168 		opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
169 					os, strlen(os));
170 	if (app)
171 		opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
172 					app, strlen(app));
173 
174 	/* The standard requires last option to be OPT_END */
175 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
176 
177 	/* clone block_length after option */
178 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
179 
180 	cc = write(self->outfd, buf, len);
181 	free(buf);
182 
183 	return cc;
184 }
185 
186 /* Write an interface block for a DPDK port */
187 static int
188 pcapng_add_interface(rte_pcapng_t *self, uint16_t port)
189 {
190 	struct pcapng_interface_block *hdr;
191 	struct rte_eth_dev_info dev_info;
192 	struct rte_ether_addr *ea, macaddr;
193 	const struct rte_device *dev;
194 	struct rte_eth_link link;
195 	struct pcapng_option *opt;
196 	const uint8_t tsresol = 9;	/* nanosecond resolution */
197 	uint32_t len;
198 	void *buf;
199 	char ifname[IF_NAMESIZE];
200 	char ifhw[256];
201 	uint64_t speed = 0;
202 
203 	if (rte_eth_dev_info_get(port, &dev_info) < 0)
204 		return -1;
205 
206 	/* make something like an interface name */
207 	if (if_indextoname(dev_info.if_index, ifname) == NULL)
208 		snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port);
209 
210 	/* make a useful device hardware string */
211 	dev = dev_info.device;
212 	if (dev)
213 		snprintf(ifhw, sizeof(ifhw),
214 			 "%s-%s", dev->bus->name, dev->name);
215 
216 	/* DPDK reports in units of Mbps */
217 	if (rte_eth_link_get(port, &link) == 0 &&
218 	    link.link_status == RTE_ETH_LINK_UP)
219 		speed = link.link_speed * PCAPNG_MBPS_SPEED;
220 
221 	if (rte_eth_macaddr_get(port, &macaddr) < 0)
222 		ea = NULL;
223 	else
224 		ea = &macaddr;
225 
226 	/* Compute length of interface block options */
227 	len = sizeof(*hdr);
228 
229 	len += pcapng_optlen(sizeof(tsresol));	/* timestamp */
230 	len += pcapng_optlen(strlen(ifname));	/* ifname */
231 
232 	if (ea)
233 		len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
234 	if (speed != 0)
235 		len += pcapng_optlen(sizeof(uint64_t));
236 	if (dev)
237 		len += pcapng_optlen(strlen(ifhw));
238 
239 	len += pcapng_optlen(0);
240 	len += sizeof(uint32_t);
241 
242 	buf = alloca(len);
243 	if (!buf)
244 		return -1;
245 
246 	hdr = (struct pcapng_interface_block *)buf;
247 	*hdr = (struct pcapng_interface_block) {
248 		.block_type = PCAPNG_INTERFACE_BLOCK,
249 		.link_type = 1,		/* DLT_EN10MB - Ethernet */
250 		.block_length = len,
251 	};
252 
253 	opt = (struct pcapng_option *)(hdr + 1);
254 	opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
255 				&tsresol, sizeof(tsresol));
256 	opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
257 				ifname, strlen(ifname));
258 	if (ea)
259 		opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
260 					ea, RTE_ETHER_ADDR_LEN);
261 	if (speed != 0)
262 		opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
263 					 &speed, sizeof(uint64_t));
264 	if (dev)
265 		opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
266 					 ifhw, strlen(ifhw));
267 	opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
268 
269 	/* clone block_length after optionsa */
270 	memcpy(opt, &hdr->block_length, sizeof(uint32_t));
271 
272 	return write(self->outfd, buf, len);
273 }
274 
275 /*
276  * Write the list of possible interfaces at the start
277  * of the file.
278  */
279 static int
280 pcapng_interfaces(rte_pcapng_t *self)
281 {
282 	uint16_t port_id;
283 	uint16_t index = 0;
284 
285 	RTE_ETH_FOREACH_DEV(port_id) {
286 		/* The list if ports in pcapng needs to be contiguous */
287 		self->port_index[port_id] = index++;
288 		if (pcapng_add_interface(self, port_id) < 0)
289 			return -1;
290 	}
291 	return 0;
292 }
293 
294 /*
295  * Write an Interface statistics block at the end of capture.
296  */
297 ssize_t
298 rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
299 		       const char *comment,
300 		       uint64_t start_time, uint64_t end_time,
301 		       uint64_t ifrecv, uint64_t ifdrop)
302 {
303 	struct pcapng_statistics *hdr;
304 	struct pcapng_option *opt;
305 	uint32_t optlen, len;
306 	uint8_t *buf;
307 	uint64_t ns;
308 
309 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
310 
311 	optlen = 0;
312 
313 	if (ifrecv != UINT64_MAX)
314 		optlen += pcapng_optlen(sizeof(ifrecv));
315 	if (ifdrop != UINT64_MAX)
316 		optlen += pcapng_optlen(sizeof(ifdrop));
317 	if (start_time != 0)
318 		optlen += pcapng_optlen(sizeof(start_time));
319 	if (end_time != 0)
320 		optlen += pcapng_optlen(sizeof(end_time));
321 	if (comment)
322 		optlen += pcapng_optlen(strlen(comment));
323 	if (optlen != 0)
324 		optlen += pcapng_optlen(0);
325 
326 	len = sizeof(*hdr) + optlen + sizeof(uint32_t);
327 	buf = alloca(len);
328 	if (buf == NULL)
329 		return -1;
330 
331 	hdr = (struct pcapng_statistics *)buf;
332 	opt = (struct pcapng_option *)(hdr + 1);
333 
334 	if (comment)
335 		opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
336 					comment, strlen(comment));
337 	if (start_time != 0)
338 		opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
339 					 &start_time, sizeof(start_time));
340 	if (end_time != 0)
341 		opt = pcapng_add_option(opt, PCAPNG_ISB_ENDTIME,
342 					 &end_time, sizeof(end_time));
343 	if (ifrecv != UINT64_MAX)
344 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
345 				&ifrecv, sizeof(ifrecv));
346 	if (ifdrop != UINT64_MAX)
347 		opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
348 				&ifdrop, sizeof(ifdrop));
349 	if (optlen != 0)
350 		opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
351 
352 	hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
353 	hdr->block_length = len;
354 	hdr->interface_id = self->port_index[port_id];
355 
356 	ns = pcapng_tsc_to_ns(rte_get_tsc_cycles());
357 	hdr->timestamp_hi = ns >> 32;
358 	hdr->timestamp_lo = (uint32_t)ns;
359 
360 	/* clone block_length after option */
361 	memcpy(opt, &len, sizeof(uint32_t));
362 
363 	return write(self->outfd, buf, len);
364 }
365 
366 uint32_t
367 rte_pcapng_mbuf_size(uint32_t length)
368 {
369 	/* The VLAN and EPB header must fit in the mbuf headroom. */
370 	RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
371 		   sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
372 
373 	/* The flags and queue information are added at the end. */
374 	return sizeof(struct rte_mbuf)
375 		+ RTE_ALIGN(length, sizeof(uint32_t))
376 		+ pcapng_optlen(sizeof(uint32_t)) /* flag option */
377 		+ pcapng_optlen(sizeof(uint32_t)) /* queue option */
378 		+ sizeof(uint32_t);		  /*  length */
379 }
380 
381 /* More generalized version rte_vlan_insert() */
382 static int
383 pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
384 {
385 	struct rte_ether_hdr *nh, *oh;
386 	struct rte_vlan_hdr *vh;
387 
388 	if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
389 		return -EINVAL;
390 
391 	if (rte_pktmbuf_data_len(m) < sizeof(*oh))
392 		return -EINVAL;
393 
394 	oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
395 	nh = (struct rte_ether_hdr *)
396 		rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
397 	if (nh == NULL)
398 		return -ENOSPC;
399 
400 	memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
401 	nh->ether_type = rte_cpu_to_be_16(ether_type);
402 
403 	vh = (struct rte_vlan_hdr *) (nh + 1);
404 	vh->vlan_tci = rte_cpu_to_be_16(tci);
405 
406 	return 0;
407 }
408 
409 /*
410  *   The mbufs created use the Pcapng standard enhanced packet  block.
411  *
412  *                         1                   2                   3
413  *     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
414  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415  *  0 |                    Block Type = 0x00000006                    |
416  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
417  *  4 |                      Block Total Length                       |
418  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419  *  8 |                         Interface ID                          |
420  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421  * 12 |                        Timestamp (High)                       |
422  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423  * 16 |                        Timestamp (Low)                        |
424  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
425  * 20 |                    Captured Packet Length                     |
426  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427  * 24 |                    Original Packet Length                     |
428  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429  * 28 /                                                               /
430  *    /                          Packet Data                          /
431  *    /              variable length, padded to 32 bits               /
432  *    /                                                               /
433  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
435  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436  *    |              Flags (direction)                                |
437  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
438  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
439  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440  *    |              Queue id                                         |
441  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
442  *    |                      Block Total Length                       |
443  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
444  */
445 
446 /* Make a copy of original mbuf with pcapng header and options */
447 struct rte_mbuf *
448 rte_pcapng_copy(uint16_t port_id, uint32_t queue,
449 		const struct rte_mbuf *md,
450 		struct rte_mempool *mp,
451 		uint32_t length, uint64_t cycles,
452 		enum rte_pcapng_direction direction)
453 {
454 	struct pcapng_enhance_packet_block *epb;
455 	uint32_t orig_len, data_len, padding, flags;
456 	struct pcapng_option *opt;
457 	const uint16_t optlen = pcapng_optlen(sizeof(flags)) + pcapng_optlen(sizeof(queue));
458 	struct rte_mbuf *mc;
459 	uint64_t ns;
460 
461 #ifdef RTE_LIBRTE_ETHDEV_DEBUG
462 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
463 #endif
464 	ns = pcapng_tsc_to_ns(cycles);
465 
466 	orig_len = rte_pktmbuf_pkt_len(md);
467 
468 	/* Take snapshot of the data */
469 	mc = rte_pktmbuf_copy(md, mp, 0, length);
470 	if (unlikely(mc == NULL))
471 		return NULL;
472 
473 	/* Expand any offloaded VLAN information */
474 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
475 	     (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
476 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
477 	     (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
478 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
479 				       md->vlan_tci) != 0)
480 			goto fail;
481 	}
482 
483 	if ((direction == RTE_PCAPNG_DIRECTION_IN &&
484 	     (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
485 	    (direction == RTE_PCAPNG_DIRECTION_OUT &&
486 	     (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
487 		if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
488 				       md->vlan_tci_outer) != 0)
489 			goto fail;
490 	}
491 
492 	/* pad the packet to 32 bit boundary */
493 	data_len = rte_pktmbuf_data_len(mc);
494 	padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len;
495 	if (padding > 0) {
496 		void *tail = rte_pktmbuf_append(mc, padding);
497 
498 		if (tail == NULL)
499 			goto fail;
500 		memset(tail, 0, padding);
501 	}
502 
503 	/* reserve trailing options and block length */
504 	opt = (struct pcapng_option *)
505 		rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
506 	if (unlikely(opt == NULL))
507 		goto fail;
508 
509 	switch (direction) {
510 	case RTE_PCAPNG_DIRECTION_IN:
511 		flags = PCAPNG_IFB_INBOUND;
512 		break;
513 	case RTE_PCAPNG_DIRECTION_OUT:
514 		flags = PCAPNG_IFB_OUTBOUND;
515 		break;
516 	default:
517 		flags = 0;
518 	}
519 
520 	opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
521 				&flags, sizeof(flags));
522 
523 	opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
524 				&queue, sizeof(queue));
525 
526 	/* Note: END_OPT necessary here. Wireshark doesn't do it. */
527 
528 	/* Add PCAPNG packet header */
529 	epb = (struct pcapng_enhance_packet_block *)
530 		rte_pktmbuf_prepend(mc, sizeof(*epb));
531 	if (unlikely(epb == NULL))
532 		goto fail;
533 
534 	epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
535 	epb->block_length = rte_pktmbuf_data_len(mc);
536 
537 	/* Interface index is filled in later during write */
538 	mc->port = port_id;
539 
540 	epb->timestamp_hi = ns >> 32;
541 	epb->timestamp_lo = (uint32_t)ns;
542 	epb->capture_length = data_len;
543 	epb->original_length = orig_len;
544 
545 	/* set trailer of block length */
546 	*(uint32_t *)opt = epb->block_length;
547 
548 	return mc;
549 
550 fail:
551 	rte_pktmbuf_free(mc);
552 	return NULL;
553 }
554 
555 /* Count how many segments are in this array of mbufs */
556 static unsigned int
557 mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n)
558 {
559 	unsigned int i, iovcnt;
560 
561 	for (iovcnt = 0, i = 0; i < n; i++) {
562 		const struct rte_mbuf *m = pkts[i];
563 
564 		__rte_mbuf_sanity_check(m, 1);
565 
566 		iovcnt += m->nb_segs;
567 	}
568 	return iovcnt;
569 }
570 
571 /* Write pre-formatted packets to file. */
572 ssize_t
573 rte_pcapng_write_packets(rte_pcapng_t *self,
574 			 struct rte_mbuf *pkts[], uint16_t nb_pkts)
575 {
576 	int iovcnt = mbuf_burst_segs(pkts, nb_pkts);
577 	struct iovec iov[iovcnt];
578 	unsigned int i, cnt;
579 	ssize_t ret;
580 
581 	for (i = cnt = 0; i < nb_pkts; i++) {
582 		struct rte_mbuf *m = pkts[i];
583 		struct pcapng_enhance_packet_block *epb;
584 
585 		/* sanity check that is really a pcapng mbuf */
586 		epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
587 		if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
588 			     epb->block_length != rte_pktmbuf_data_len(m))) {
589 			rte_errno = EINVAL;
590 			return -1;
591 		}
592 
593 		/*
594 		 * The DPDK port is recorded during pcapng_copy.
595 		 * Map that to PCAPNG interface in file.
596 		 */
597 		epb->interface_id = self->port_index[m->port];
598 		do {
599 			iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
600 			iov[cnt].iov_len = rte_pktmbuf_data_len(m);
601 			++cnt;
602 		} while ((m = m->next));
603 	}
604 
605 	ret = writev(self->outfd, iov, iovcnt);
606 	if (unlikely(ret < 0))
607 		rte_errno = errno;
608 	return ret;
609 }
610 
611 /* Create new pcapng writer handle */
612 rte_pcapng_t *
613 rte_pcapng_fdopen(int fd,
614 		  const char *osname, const char *hardware,
615 		  const char *appname, const char *comment)
616 {
617 	rte_pcapng_t *self;
618 
619 	self = malloc(sizeof(*self));
620 	if (!self) {
621 		rte_errno = ENOMEM;
622 		return NULL;
623 	}
624 
625 	self->outfd = fd;
626 
627 	if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
628 		goto fail;
629 
630 	if (pcapng_interfaces(self) < 0)
631 		goto fail;
632 
633 	return self;
634 fail:
635 	free(self);
636 	return NULL;
637 }
638 
639 void
640 rte_pcapng_close(rte_pcapng_t *self)
641 {
642 	close(self->outfd);
643 	free(self);
644 }
645