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