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