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