pcapng: fix timestamp wrapping in output files
[dpdk.git] / lib / pcapng / rte_pcapng.c
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 }