18d23ce8fSStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause 28d23ce8fSStephen Hemminger * Copyright(c) 2019 Microsoft Corporation 38d23ce8fSStephen Hemminger */ 48d23ce8fSStephen Hemminger 58d23ce8fSStephen Hemminger #include <errno.h> 68d23ce8fSStephen Hemminger #include <net/if.h> 78d23ce8fSStephen Hemminger #include <stdio.h> 88d23ce8fSStephen Hemminger #include <stdlib.h> 98d23ce8fSStephen Hemminger #include <string.h> 108d23ce8fSStephen Hemminger #include <sys/uio.h> 118d23ce8fSStephen Hemminger #include <sys/utsname.h> 128d23ce8fSStephen Hemminger #include <time.h> 138d23ce8fSStephen Hemminger #include <unistd.h> 148d23ce8fSStephen Hemminger 158d23ce8fSStephen Hemminger #include <rte_common.h> 168d23ce8fSStephen Hemminger #include <rte_cycles.h> 178d23ce8fSStephen Hemminger #include <rte_dev.h> 188d23ce8fSStephen Hemminger #include <rte_errno.h> 198d23ce8fSStephen Hemminger #include <rte_ethdev.h> 208d23ce8fSStephen Hemminger #include <rte_ether.h> 218d23ce8fSStephen Hemminger #include <rte_mbuf.h> 228d23ce8fSStephen Hemminger #include <rte_pcapng.h> 238d23ce8fSStephen Hemminger #include <rte_time.h> 248d23ce8fSStephen Hemminger 258d23ce8fSStephen Hemminger #include "pcapng_proto.h" 268d23ce8fSStephen Hemminger 278d23ce8fSStephen Hemminger /* conversion from DPDK speed to PCAPNG */ 288d23ce8fSStephen Hemminger #define PCAPNG_MBPS_SPEED 1000000ull 298d23ce8fSStephen Hemminger 308d23ce8fSStephen Hemminger /* Format of the capture file handle */ 318d23ce8fSStephen Hemminger struct rte_pcapng { 328d23ce8fSStephen Hemminger int outfd; /* output file */ 338d23ce8fSStephen Hemminger /* DPDK port id to interface index in file */ 348d23ce8fSStephen Hemminger uint32_t port_index[RTE_MAX_ETHPORTS]; 358d23ce8fSStephen Hemminger }; 368d23ce8fSStephen Hemminger 378d23ce8fSStephen Hemminger /* For converting TSC cycles to PCAPNG ns format */ 388d23ce8fSStephen Hemminger struct pcapng_time { 398d23ce8fSStephen Hemminger uint64_t ns; 408d23ce8fSStephen Hemminger uint64_t cycles; 418d23ce8fSStephen Hemminger } pcapng_time; 428d23ce8fSStephen Hemminger 438d23ce8fSStephen Hemminger RTE_INIT(pcapng_init) 448d23ce8fSStephen Hemminger { 458d23ce8fSStephen Hemminger struct timespec ts; 468d23ce8fSStephen Hemminger 478d23ce8fSStephen Hemminger pcapng_time.cycles = rte_get_tsc_cycles(); 488d23ce8fSStephen Hemminger clock_gettime(CLOCK_REALTIME, &ts); 498d23ce8fSStephen Hemminger pcapng_time.ns = rte_timespec_to_ns(&ts); 508d23ce8fSStephen Hemminger } 518d23ce8fSStephen Hemminger 528d23ce8fSStephen Hemminger /* PCAPNG timestamps are in nanoseconds */ 538d23ce8fSStephen Hemminger static uint64_t pcapng_tsc_to_ns(uint64_t cycles) 548d23ce8fSStephen Hemminger { 558d23ce8fSStephen Hemminger uint64_t delta; 568d23ce8fSStephen Hemminger 578d23ce8fSStephen Hemminger delta = cycles - pcapng_time.cycles; 588d23ce8fSStephen Hemminger return pcapng_time.ns + (delta * NSEC_PER_SEC) / rte_get_tsc_hz(); 598d23ce8fSStephen Hemminger } 608d23ce8fSStephen Hemminger 618d23ce8fSStephen Hemminger /* length of option including padding */ 628d23ce8fSStephen Hemminger static uint16_t pcapng_optlen(uint16_t len) 638d23ce8fSStephen Hemminger { 648d23ce8fSStephen Hemminger return RTE_ALIGN(sizeof(struct pcapng_option) + len, 658d23ce8fSStephen Hemminger sizeof(uint32_t)); 668d23ce8fSStephen Hemminger } 678d23ce8fSStephen Hemminger 688d23ce8fSStephen Hemminger /* build TLV option and return location of next */ 698d23ce8fSStephen Hemminger static struct pcapng_option * 708d23ce8fSStephen Hemminger pcapng_add_option(struct pcapng_option *popt, uint16_t code, 718d23ce8fSStephen Hemminger const void *data, uint16_t len) 728d23ce8fSStephen Hemminger { 738d23ce8fSStephen Hemminger popt->code = code; 748d23ce8fSStephen Hemminger popt->length = len; 758d23ce8fSStephen Hemminger memcpy(popt->data, data, len); 768d23ce8fSStephen Hemminger 778d23ce8fSStephen Hemminger return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len)); 788d23ce8fSStephen Hemminger } 798d23ce8fSStephen Hemminger 808d23ce8fSStephen Hemminger /* 818d23ce8fSStephen Hemminger * Write required initial section header describing the capture 828d23ce8fSStephen Hemminger */ 838d23ce8fSStephen Hemminger static int 848d23ce8fSStephen Hemminger pcapng_section_block(rte_pcapng_t *self, 858d23ce8fSStephen Hemminger const char *os, const char *hw, 868d23ce8fSStephen Hemminger const char *app, const char *comment) 878d23ce8fSStephen Hemminger { 888d23ce8fSStephen Hemminger struct pcapng_section_header *hdr; 898d23ce8fSStephen Hemminger struct pcapng_option *opt; 908d23ce8fSStephen Hemminger void *buf; 918d23ce8fSStephen Hemminger uint32_t len; 928d23ce8fSStephen Hemminger ssize_t cc; 938d23ce8fSStephen Hemminger 948d23ce8fSStephen Hemminger len = sizeof(*hdr); 958d23ce8fSStephen Hemminger if (hw) 968d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(hw)); 978d23ce8fSStephen Hemminger if (os) 988d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(os)); 998d23ce8fSStephen Hemminger if (app) 1008d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(app)); 1018d23ce8fSStephen Hemminger if (comment) 1028d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(comment)); 1038d23ce8fSStephen Hemminger 1048d23ce8fSStephen Hemminger /* reserve space for OPT_END */ 1058d23ce8fSStephen Hemminger len += pcapng_optlen(0); 1068d23ce8fSStephen Hemminger len += sizeof(uint32_t); 1078d23ce8fSStephen Hemminger 1088d23ce8fSStephen Hemminger buf = calloc(1, len); 1098d23ce8fSStephen Hemminger if (!buf) 1108d23ce8fSStephen Hemminger return -1; 1118d23ce8fSStephen Hemminger 1128d23ce8fSStephen Hemminger hdr = (struct pcapng_section_header *)buf; 1138d23ce8fSStephen Hemminger *hdr = (struct pcapng_section_header) { 1148d23ce8fSStephen Hemminger .block_type = PCAPNG_SECTION_BLOCK, 1158d23ce8fSStephen Hemminger .block_length = len, 1168d23ce8fSStephen Hemminger .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC, 1178d23ce8fSStephen Hemminger .major_version = PCAPNG_MAJOR_VERS, 1188d23ce8fSStephen Hemminger .minor_version = PCAPNG_MINOR_VERS, 1198d23ce8fSStephen Hemminger .section_length = UINT64_MAX, 1208d23ce8fSStephen Hemminger }; 1218d23ce8fSStephen Hemminger 1228d23ce8fSStephen Hemminger /* After the section header insert variable length options. */ 1238d23ce8fSStephen Hemminger opt = (struct pcapng_option *)(hdr + 1); 1248d23ce8fSStephen Hemminger if (comment) 1258d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, 1268d23ce8fSStephen Hemminger comment, strlen(comment)); 1278d23ce8fSStephen Hemminger if (hw) 1288d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE, 1298d23ce8fSStephen Hemminger hw, strlen(hw)); 1308d23ce8fSStephen Hemminger if (os) 1318d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_SHB_OS, 1328d23ce8fSStephen Hemminger os, strlen(os)); 1338d23ce8fSStephen Hemminger if (app) 1348d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL, 1358d23ce8fSStephen Hemminger app, strlen(app)); 1368d23ce8fSStephen Hemminger 1378d23ce8fSStephen Hemminger /* The standard requires last option to be OPT_END */ 1388d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); 1398d23ce8fSStephen Hemminger 1408d23ce8fSStephen Hemminger /* clone block_length after option */ 1418d23ce8fSStephen Hemminger memcpy(opt, &hdr->block_length, sizeof(uint32_t)); 1428d23ce8fSStephen Hemminger 1438d23ce8fSStephen Hemminger cc = write(self->outfd, buf, len); 1448d23ce8fSStephen Hemminger free(buf); 1458d23ce8fSStephen Hemminger 1468d23ce8fSStephen Hemminger return cc; 1478d23ce8fSStephen Hemminger } 1488d23ce8fSStephen Hemminger 1498d23ce8fSStephen Hemminger /* Write an interface block for a DPDK port */ 1508d23ce8fSStephen Hemminger static int 1518d23ce8fSStephen Hemminger pcapng_add_interface(rte_pcapng_t *self, uint16_t port) 1528d23ce8fSStephen Hemminger { 1538d23ce8fSStephen Hemminger struct pcapng_interface_block *hdr; 1548d23ce8fSStephen Hemminger struct rte_eth_dev_info dev_info; 1558d23ce8fSStephen Hemminger struct rte_ether_addr *ea, macaddr; 1568d23ce8fSStephen Hemminger const struct rte_device *dev; 1578d23ce8fSStephen Hemminger struct rte_eth_link link; 1588d23ce8fSStephen Hemminger struct pcapng_option *opt; 1598d23ce8fSStephen Hemminger const uint8_t tsresol = 9; /* nanosecond resolution */ 1608d23ce8fSStephen Hemminger uint32_t len; 1618d23ce8fSStephen Hemminger void *buf; 1628d23ce8fSStephen Hemminger char ifname[IF_NAMESIZE]; 1638d23ce8fSStephen Hemminger char ifhw[256]; 1648d23ce8fSStephen Hemminger uint64_t speed = 0; 1658d23ce8fSStephen Hemminger 1668d23ce8fSStephen Hemminger if (rte_eth_dev_info_get(port, &dev_info) < 0) 1678d23ce8fSStephen Hemminger return -1; 1688d23ce8fSStephen Hemminger 1698d23ce8fSStephen Hemminger /* make something like an interface name */ 1708d23ce8fSStephen Hemminger if (if_indextoname(dev_info.if_index, ifname) == NULL) 1718d23ce8fSStephen Hemminger snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port); 1728d23ce8fSStephen Hemminger 1738d23ce8fSStephen Hemminger /* make a useful device hardware string */ 1748d23ce8fSStephen Hemminger dev = dev_info.device; 1758d23ce8fSStephen Hemminger if (dev) 1768d23ce8fSStephen Hemminger snprintf(ifhw, sizeof(ifhw), 1778d23ce8fSStephen Hemminger "%s-%s", dev->bus->name, dev->name); 1788d23ce8fSStephen Hemminger 1798d23ce8fSStephen Hemminger /* DPDK reports in units of Mbps */ 1808d23ce8fSStephen Hemminger rte_eth_link_get(port, &link); 1818d23ce8fSStephen Hemminger if (link.link_status == ETH_LINK_UP) 1828d23ce8fSStephen Hemminger speed = link.link_speed * PCAPNG_MBPS_SPEED; 1838d23ce8fSStephen Hemminger 1848d23ce8fSStephen Hemminger if (rte_eth_macaddr_get(port, &macaddr) < 0) 1858d23ce8fSStephen Hemminger ea = NULL; 1868d23ce8fSStephen Hemminger else 1878d23ce8fSStephen Hemminger ea = &macaddr; 1888d23ce8fSStephen Hemminger 1898d23ce8fSStephen Hemminger /* Compute length of interface block options */ 1908d23ce8fSStephen Hemminger len = sizeof(*hdr); 1918d23ce8fSStephen Hemminger 1928d23ce8fSStephen Hemminger len += pcapng_optlen(sizeof(tsresol)); /* timestamp */ 1938d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(ifname)); /* ifname */ 1948d23ce8fSStephen Hemminger 1958d23ce8fSStephen Hemminger if (ea) 1968d23ce8fSStephen Hemminger len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */ 1978d23ce8fSStephen Hemminger if (speed != 0) 1988d23ce8fSStephen Hemminger len += pcapng_optlen(sizeof(uint64_t)); 1998d23ce8fSStephen Hemminger if (dev) 2008d23ce8fSStephen Hemminger len += pcapng_optlen(strlen(ifhw)); 2018d23ce8fSStephen Hemminger 2028d23ce8fSStephen Hemminger len += pcapng_optlen(0); 2038d23ce8fSStephen Hemminger len += sizeof(uint32_t); 2048d23ce8fSStephen Hemminger 2058d23ce8fSStephen Hemminger buf = alloca(len); 2068d23ce8fSStephen Hemminger if (!buf) 2078d23ce8fSStephen Hemminger return -1; 2088d23ce8fSStephen Hemminger 2098d23ce8fSStephen Hemminger hdr = (struct pcapng_interface_block *)buf; 2108d23ce8fSStephen Hemminger *hdr = (struct pcapng_interface_block) { 2118d23ce8fSStephen Hemminger .block_type = PCAPNG_INTERFACE_BLOCK, 2128d23ce8fSStephen Hemminger .link_type = 1, /* DLT_EN10MB - Ethernet */ 2138d23ce8fSStephen Hemminger .block_length = len, 2148d23ce8fSStephen Hemminger }; 2158d23ce8fSStephen Hemminger 2168d23ce8fSStephen Hemminger opt = (struct pcapng_option *)(hdr + 1); 2178d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL, 2188d23ce8fSStephen Hemminger &tsresol, sizeof(tsresol)); 2198d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_IFB_NAME, 2208d23ce8fSStephen Hemminger ifname, strlen(ifname)); 2218d23ce8fSStephen Hemminger if (ea) 2228d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR, 2238d23ce8fSStephen Hemminger ea, RTE_ETHER_ADDR_LEN); 2248d23ce8fSStephen Hemminger if (speed != 0) 2258d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED, 2268d23ce8fSStephen Hemminger &speed, sizeof(uint64_t)); 2278d23ce8fSStephen Hemminger if (dev) 2288d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE, 2298d23ce8fSStephen Hemminger ifhw, strlen(ifhw)); 2308d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); 2318d23ce8fSStephen Hemminger 2328d23ce8fSStephen Hemminger /* clone block_length after optionsa */ 2338d23ce8fSStephen Hemminger memcpy(opt, &hdr->block_length, sizeof(uint32_t)); 2348d23ce8fSStephen Hemminger 2358d23ce8fSStephen Hemminger return write(self->outfd, buf, len); 2368d23ce8fSStephen Hemminger } 2378d23ce8fSStephen Hemminger 2388d23ce8fSStephen Hemminger /* 2398d23ce8fSStephen Hemminger * Write the list of possible interfaces at the start 2408d23ce8fSStephen Hemminger * of the file. 2418d23ce8fSStephen Hemminger */ 2428d23ce8fSStephen Hemminger static int 2438d23ce8fSStephen Hemminger pcapng_interfaces(rte_pcapng_t *self) 2448d23ce8fSStephen Hemminger { 2458d23ce8fSStephen Hemminger uint16_t port_id; 2468d23ce8fSStephen Hemminger uint16_t index = 0; 2478d23ce8fSStephen Hemminger 2488d23ce8fSStephen Hemminger RTE_ETH_FOREACH_DEV(port_id) { 2498d23ce8fSStephen Hemminger /* The list if ports in pcapng needs to be contiguous */ 2508d23ce8fSStephen Hemminger self->port_index[port_id] = index++; 2518d23ce8fSStephen Hemminger if (pcapng_add_interface(self, port_id) < 0) 2528d23ce8fSStephen Hemminger return -1; 2538d23ce8fSStephen Hemminger } 2548d23ce8fSStephen Hemminger return 0; 2558d23ce8fSStephen Hemminger } 2568d23ce8fSStephen Hemminger 2578d23ce8fSStephen Hemminger /* 2588d23ce8fSStephen Hemminger * Write an Interface statistics block at the end of capture. 2598d23ce8fSStephen Hemminger */ 2608d23ce8fSStephen Hemminger ssize_t 2618d23ce8fSStephen Hemminger rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id, 2628d23ce8fSStephen Hemminger const char *comment, 2638d23ce8fSStephen Hemminger uint64_t start_time, uint64_t end_time, 2648d23ce8fSStephen Hemminger uint64_t ifrecv, uint64_t ifdrop) 2658d23ce8fSStephen Hemminger { 2668d23ce8fSStephen Hemminger struct pcapng_statistics *hdr; 2678d23ce8fSStephen Hemminger struct pcapng_option *opt; 2688d23ce8fSStephen Hemminger uint32_t optlen, len; 2698d23ce8fSStephen Hemminger uint8_t *buf; 2708d23ce8fSStephen Hemminger uint64_t ns; 2718d23ce8fSStephen Hemminger 2728d23ce8fSStephen Hemminger RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL); 2738d23ce8fSStephen Hemminger 2748d23ce8fSStephen Hemminger optlen = 0; 2758d23ce8fSStephen Hemminger 2768d23ce8fSStephen Hemminger if (ifrecv != UINT64_MAX) 2778d23ce8fSStephen Hemminger optlen += pcapng_optlen(sizeof(ifrecv)); 2788d23ce8fSStephen Hemminger if (ifdrop != UINT64_MAX) 2798d23ce8fSStephen Hemminger optlen += pcapng_optlen(sizeof(ifdrop)); 2808d23ce8fSStephen Hemminger if (start_time != 0) 2818d23ce8fSStephen Hemminger optlen += pcapng_optlen(sizeof(start_time)); 2828d23ce8fSStephen Hemminger if (end_time != 0) 2838d23ce8fSStephen Hemminger optlen += pcapng_optlen(sizeof(end_time)); 2848d23ce8fSStephen Hemminger if (comment) 2858d23ce8fSStephen Hemminger optlen += pcapng_optlen(strlen(comment)); 2868d23ce8fSStephen Hemminger if (optlen != 0) 2878d23ce8fSStephen Hemminger optlen += pcapng_optlen(0); 2888d23ce8fSStephen Hemminger 2898d23ce8fSStephen Hemminger len = sizeof(*hdr) + optlen + sizeof(uint32_t); 2908d23ce8fSStephen Hemminger buf = alloca(len); 2918d23ce8fSStephen Hemminger if (buf == NULL) 2928d23ce8fSStephen Hemminger return -1; 2938d23ce8fSStephen Hemminger 2948d23ce8fSStephen Hemminger hdr = (struct pcapng_statistics *)buf; 2958d23ce8fSStephen Hemminger opt = (struct pcapng_option *)(hdr + 1); 2968d23ce8fSStephen Hemminger 2978d23ce8fSStephen Hemminger if (comment) 2988d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, 2998d23ce8fSStephen Hemminger comment, strlen(comment)); 3008d23ce8fSStephen Hemminger if (start_time != 0) 3018d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME, 3028d23ce8fSStephen Hemminger &start_time, sizeof(start_time)); 3038d23ce8fSStephen Hemminger if (end_time != 0) 3048d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_ISB_ENDTIME, 3058d23ce8fSStephen Hemminger &end_time, sizeof(end_time)); 3068d23ce8fSStephen Hemminger if (ifrecv != UINT64_MAX) 3078d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV, 3088d23ce8fSStephen Hemminger &ifrecv, sizeof(ifrecv)); 3098d23ce8fSStephen Hemminger if (ifdrop != UINT64_MAX) 3108d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP, 3118d23ce8fSStephen Hemminger &ifdrop, sizeof(ifdrop)); 3128d23ce8fSStephen Hemminger if (optlen != 0) 3138d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); 3148d23ce8fSStephen Hemminger 3158d23ce8fSStephen Hemminger hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK; 3168d23ce8fSStephen Hemminger hdr->block_length = len; 3178d23ce8fSStephen Hemminger hdr->interface_id = self->port_index[port_id]; 3188d23ce8fSStephen Hemminger 3198d23ce8fSStephen Hemminger ns = pcapng_tsc_to_ns(rte_get_tsc_cycles()); 3208d23ce8fSStephen Hemminger hdr->timestamp_hi = ns >> 32; 3218d23ce8fSStephen Hemminger hdr->timestamp_lo = (uint32_t)ns; 3228d23ce8fSStephen Hemminger 3238d23ce8fSStephen Hemminger /* clone block_length after option */ 3248d23ce8fSStephen Hemminger memcpy(opt, &len, sizeof(uint32_t)); 3258d23ce8fSStephen Hemminger 3268d23ce8fSStephen Hemminger return write(self->outfd, buf, len); 3278d23ce8fSStephen Hemminger } 3288d23ce8fSStephen Hemminger 3298d23ce8fSStephen Hemminger uint32_t 3308d23ce8fSStephen Hemminger rte_pcapng_mbuf_size(uint32_t length) 3318d23ce8fSStephen Hemminger { 3328d23ce8fSStephen Hemminger /* The VLAN and EPB header must fit in the mbuf headroom. */ 3338d23ce8fSStephen Hemminger RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) + 3348d23ce8fSStephen Hemminger sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM); 3358d23ce8fSStephen Hemminger 3368d23ce8fSStephen Hemminger /* The flags and queue information are added at the end. */ 3378d23ce8fSStephen Hemminger return sizeof(struct rte_mbuf) 3388d23ce8fSStephen Hemminger + RTE_ALIGN(length, sizeof(uint32_t)) 3398d23ce8fSStephen Hemminger + pcapng_optlen(sizeof(uint32_t)) /* flag option */ 3408d23ce8fSStephen Hemminger + pcapng_optlen(sizeof(uint32_t)) /* queue option */ 3418d23ce8fSStephen Hemminger + sizeof(uint32_t); /* length */ 3428d23ce8fSStephen Hemminger } 3438d23ce8fSStephen Hemminger 3448d23ce8fSStephen Hemminger /* More generalized version rte_vlan_insert() */ 3458d23ce8fSStephen Hemminger static int 3468d23ce8fSStephen Hemminger pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci) 3478d23ce8fSStephen Hemminger { 3488d23ce8fSStephen Hemminger struct rte_ether_hdr *nh, *oh; 3498d23ce8fSStephen Hemminger struct rte_vlan_hdr *vh; 3508d23ce8fSStephen Hemminger 3518d23ce8fSStephen Hemminger if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1) 3528d23ce8fSStephen Hemminger return -EINVAL; 3538d23ce8fSStephen Hemminger 3548d23ce8fSStephen Hemminger if (rte_pktmbuf_data_len(m) < sizeof(*oh)) 3558d23ce8fSStephen Hemminger return -EINVAL; 3568d23ce8fSStephen Hemminger 3578d23ce8fSStephen Hemminger oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); 3588d23ce8fSStephen Hemminger nh = (struct rte_ether_hdr *) 3598d23ce8fSStephen Hemminger rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr)); 3608d23ce8fSStephen Hemminger if (nh == NULL) 3618d23ce8fSStephen Hemminger return -ENOSPC; 3628d23ce8fSStephen Hemminger 3638d23ce8fSStephen Hemminger memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN); 3648d23ce8fSStephen Hemminger nh->ether_type = rte_cpu_to_be_16(ether_type); 3658d23ce8fSStephen Hemminger 3668d23ce8fSStephen Hemminger vh = (struct rte_vlan_hdr *) (nh + 1); 3678d23ce8fSStephen Hemminger vh->vlan_tci = rte_cpu_to_be_16(tci); 3688d23ce8fSStephen Hemminger 3698d23ce8fSStephen Hemminger return 0; 3708d23ce8fSStephen Hemminger } 3718d23ce8fSStephen Hemminger 3728d23ce8fSStephen Hemminger /* 3738d23ce8fSStephen Hemminger * The mbufs created use the Pcapng standard enhanced packet block. 3748d23ce8fSStephen Hemminger * 3758d23ce8fSStephen Hemminger * 1 2 3 3768d23ce8fSStephen Hemminger * 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 3778d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3788d23ce8fSStephen Hemminger * 0 | Block Type = 0x00000006 | 3798d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3808d23ce8fSStephen Hemminger * 4 | Block Total Length | 3818d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3828d23ce8fSStephen Hemminger * 8 | Interface ID | 3838d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3848d23ce8fSStephen Hemminger * 12 | Timestamp (High) | 3858d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3868d23ce8fSStephen Hemminger * 16 | Timestamp (Low) | 3878d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3888d23ce8fSStephen Hemminger * 20 | Captured Packet Length | 3898d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3908d23ce8fSStephen Hemminger * 24 | Original Packet Length | 3918d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3928d23ce8fSStephen Hemminger * 28 / / 3938d23ce8fSStephen Hemminger * / Packet Data / 3948d23ce8fSStephen Hemminger * / variable length, padded to 32 bits / 3958d23ce8fSStephen Hemminger * / / 3968d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3978d23ce8fSStephen Hemminger * | Option Code = 0x0002 | Option Length = 0x004 | 3988d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 3998d23ce8fSStephen Hemminger * | Flags (direction) | 4008d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4018d23ce8fSStephen Hemminger * | Option Code = 0x0006 | Option Length = 0x002 | 4028d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4038d23ce8fSStephen Hemminger * | Queue id | 4048d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4058d23ce8fSStephen Hemminger * | Block Total Length | 4068d23ce8fSStephen Hemminger * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4078d23ce8fSStephen Hemminger */ 4088d23ce8fSStephen Hemminger 4098d23ce8fSStephen Hemminger /* Make a copy of original mbuf with pcapng header and options */ 4108d23ce8fSStephen Hemminger struct rte_mbuf * 4118d23ce8fSStephen Hemminger rte_pcapng_copy(uint16_t port_id, uint32_t queue, 4128d23ce8fSStephen Hemminger const struct rte_mbuf *md, 4138d23ce8fSStephen Hemminger struct rte_mempool *mp, 4148d23ce8fSStephen Hemminger uint32_t length, uint64_t cycles, 4158d23ce8fSStephen Hemminger enum rte_pcapng_direction direction) 4168d23ce8fSStephen Hemminger { 4178d23ce8fSStephen Hemminger struct pcapng_enhance_packet_block *epb; 4188d23ce8fSStephen Hemminger uint32_t orig_len, data_len, padding, flags; 4198d23ce8fSStephen Hemminger struct pcapng_option *opt; 4208d23ce8fSStephen Hemminger const uint16_t optlen = pcapng_optlen(sizeof(flags)) + pcapng_optlen(sizeof(queue)); 4218d23ce8fSStephen Hemminger struct rte_mbuf *mc; 4228d23ce8fSStephen Hemminger uint64_t ns; 4238d23ce8fSStephen Hemminger 4248d23ce8fSStephen Hemminger #ifdef RTE_LIBRTE_ETHDEV_DEBUG 4258d23ce8fSStephen Hemminger RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL); 4268d23ce8fSStephen Hemminger #endif 4278d23ce8fSStephen Hemminger ns = pcapng_tsc_to_ns(cycles); 4288d23ce8fSStephen Hemminger 4298d23ce8fSStephen Hemminger orig_len = rte_pktmbuf_pkt_len(md); 4308d23ce8fSStephen Hemminger 4318d23ce8fSStephen Hemminger /* Take snapshot of the data */ 4328d23ce8fSStephen Hemminger mc = rte_pktmbuf_copy(md, mp, 0, length); 4338d23ce8fSStephen Hemminger if (unlikely(mc == NULL)) 4348d23ce8fSStephen Hemminger return NULL; 4358d23ce8fSStephen Hemminger 4368d23ce8fSStephen Hemminger /* Expand any offloaded VLAN information */ 4378d23ce8fSStephen Hemminger if ((direction == RTE_PCAPNG_DIRECTION_IN && 438*daa02b5cSOlivier Matz (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) || 4398d23ce8fSStephen Hemminger (direction == RTE_PCAPNG_DIRECTION_OUT && 440*daa02b5cSOlivier Matz (md->ol_flags & RTE_MBUF_F_TX_VLAN))) { 4418d23ce8fSStephen Hemminger if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN, 4428d23ce8fSStephen Hemminger md->vlan_tci) != 0) 4438d23ce8fSStephen Hemminger goto fail; 4448d23ce8fSStephen Hemminger } 4458d23ce8fSStephen Hemminger 4468d23ce8fSStephen Hemminger if ((direction == RTE_PCAPNG_DIRECTION_IN && 447*daa02b5cSOlivier Matz (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) || 4488d23ce8fSStephen Hemminger (direction == RTE_PCAPNG_DIRECTION_OUT && 449*daa02b5cSOlivier Matz (md->ol_flags & RTE_MBUF_F_TX_QINQ))) { 4508d23ce8fSStephen Hemminger if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ, 4518d23ce8fSStephen Hemminger md->vlan_tci_outer) != 0) 4528d23ce8fSStephen Hemminger goto fail; 4538d23ce8fSStephen Hemminger } 4548d23ce8fSStephen Hemminger 4558d23ce8fSStephen Hemminger /* pad the packet to 32 bit boundary */ 4568d23ce8fSStephen Hemminger data_len = rte_pktmbuf_data_len(mc); 4578d23ce8fSStephen Hemminger padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len; 4588d23ce8fSStephen Hemminger if (padding > 0) { 4598d23ce8fSStephen Hemminger void *tail = rte_pktmbuf_append(mc, padding); 4608d23ce8fSStephen Hemminger 4618d23ce8fSStephen Hemminger if (tail == NULL) 4628d23ce8fSStephen Hemminger goto fail; 4638d23ce8fSStephen Hemminger memset(tail, 0, padding); 4648d23ce8fSStephen Hemminger } 4658d23ce8fSStephen Hemminger 4668d23ce8fSStephen Hemminger /* reserve trailing options and block length */ 4678d23ce8fSStephen Hemminger opt = (struct pcapng_option *) 4688d23ce8fSStephen Hemminger rte_pktmbuf_append(mc, optlen + sizeof(uint32_t)); 4698d23ce8fSStephen Hemminger if (unlikely(opt == NULL)) 4708d23ce8fSStephen Hemminger goto fail; 4718d23ce8fSStephen Hemminger 4728d23ce8fSStephen Hemminger switch (direction) { 4738d23ce8fSStephen Hemminger case RTE_PCAPNG_DIRECTION_IN: 4748d23ce8fSStephen Hemminger flags = PCAPNG_IFB_INBOUND; 4758d23ce8fSStephen Hemminger break; 4768d23ce8fSStephen Hemminger case RTE_PCAPNG_DIRECTION_OUT: 4778d23ce8fSStephen Hemminger flags = PCAPNG_IFB_OUTBOUND; 4788d23ce8fSStephen Hemminger break; 4798d23ce8fSStephen Hemminger default: 4808d23ce8fSStephen Hemminger flags = 0; 4818d23ce8fSStephen Hemminger } 4828d23ce8fSStephen Hemminger 4838d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS, 4848d23ce8fSStephen Hemminger &flags, sizeof(flags)); 4858d23ce8fSStephen Hemminger 4868d23ce8fSStephen Hemminger opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE, 4878d23ce8fSStephen Hemminger &queue, sizeof(queue)); 4888d23ce8fSStephen Hemminger 4898d23ce8fSStephen Hemminger /* Note: END_OPT necessary here. Wireshark doesn't do it. */ 4908d23ce8fSStephen Hemminger 4918d23ce8fSStephen Hemminger /* Add PCAPNG packet header */ 4928d23ce8fSStephen Hemminger epb = (struct pcapng_enhance_packet_block *) 4938d23ce8fSStephen Hemminger rte_pktmbuf_prepend(mc, sizeof(*epb)); 4948d23ce8fSStephen Hemminger if (unlikely(epb == NULL)) 4958d23ce8fSStephen Hemminger goto fail; 4968d23ce8fSStephen Hemminger 4978d23ce8fSStephen Hemminger epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK; 4988d23ce8fSStephen Hemminger epb->block_length = rte_pktmbuf_data_len(mc); 4998d23ce8fSStephen Hemminger 5008d23ce8fSStephen Hemminger /* Interface index is filled in later during write */ 5018d23ce8fSStephen Hemminger mc->port = port_id; 5028d23ce8fSStephen Hemminger 5038d23ce8fSStephen Hemminger epb->timestamp_hi = ns >> 32; 5048d23ce8fSStephen Hemminger epb->timestamp_lo = (uint32_t)ns; 5058d23ce8fSStephen Hemminger epb->capture_length = data_len; 5068d23ce8fSStephen Hemminger epb->original_length = orig_len; 5078d23ce8fSStephen Hemminger 5088d23ce8fSStephen Hemminger /* set trailer of block length */ 5098d23ce8fSStephen Hemminger *(uint32_t *)opt = epb->block_length; 5108d23ce8fSStephen Hemminger 5118d23ce8fSStephen Hemminger return mc; 5128d23ce8fSStephen Hemminger 5138d23ce8fSStephen Hemminger fail: 5148d23ce8fSStephen Hemminger rte_pktmbuf_free(mc); 5158d23ce8fSStephen Hemminger return NULL; 5168d23ce8fSStephen Hemminger } 5178d23ce8fSStephen Hemminger 5188d23ce8fSStephen Hemminger /* Count how many segments are in this array of mbufs */ 5198d23ce8fSStephen Hemminger static unsigned int 5208d23ce8fSStephen Hemminger mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n) 5218d23ce8fSStephen Hemminger { 5228d23ce8fSStephen Hemminger unsigned int i, iovcnt; 5238d23ce8fSStephen Hemminger 5248d23ce8fSStephen Hemminger for (iovcnt = 0, i = 0; i < n; i++) { 5258d23ce8fSStephen Hemminger const struct rte_mbuf *m = pkts[i]; 5268d23ce8fSStephen Hemminger 5278d23ce8fSStephen Hemminger __rte_mbuf_sanity_check(m, 1); 5288d23ce8fSStephen Hemminger 5298d23ce8fSStephen Hemminger iovcnt += m->nb_segs; 5308d23ce8fSStephen Hemminger } 5318d23ce8fSStephen Hemminger return iovcnt; 5328d23ce8fSStephen Hemminger } 5338d23ce8fSStephen Hemminger 5348d23ce8fSStephen Hemminger /* Write pre-formatted packets to file. */ 5358d23ce8fSStephen Hemminger ssize_t 5368d23ce8fSStephen Hemminger rte_pcapng_write_packets(rte_pcapng_t *self, 5378d23ce8fSStephen Hemminger struct rte_mbuf *pkts[], uint16_t nb_pkts) 5388d23ce8fSStephen Hemminger { 5398d23ce8fSStephen Hemminger int iovcnt = mbuf_burst_segs(pkts, nb_pkts); 5408d23ce8fSStephen Hemminger struct iovec iov[iovcnt]; 5418d23ce8fSStephen Hemminger unsigned int i, cnt; 5428d23ce8fSStephen Hemminger ssize_t ret; 5438d23ce8fSStephen Hemminger 5448d23ce8fSStephen Hemminger for (i = cnt = 0; i < nb_pkts; i++) { 5458d23ce8fSStephen Hemminger struct rte_mbuf *m = pkts[i]; 5468d23ce8fSStephen Hemminger struct pcapng_enhance_packet_block *epb; 5478d23ce8fSStephen Hemminger 5488d23ce8fSStephen Hemminger /* sanity check that is really a pcapng mbuf */ 5498d23ce8fSStephen Hemminger epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *); 5508d23ce8fSStephen Hemminger if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK || 5518d23ce8fSStephen Hemminger epb->block_length != rte_pktmbuf_data_len(m))) { 5528d23ce8fSStephen Hemminger rte_errno = EINVAL; 5538d23ce8fSStephen Hemminger return -1; 5548d23ce8fSStephen Hemminger } 5558d23ce8fSStephen Hemminger 5568d23ce8fSStephen Hemminger /* 5578d23ce8fSStephen Hemminger * The DPDK port is recorded during pcapng_copy. 5588d23ce8fSStephen Hemminger * Map that to PCAPNG interface in file. 5598d23ce8fSStephen Hemminger */ 5608d23ce8fSStephen Hemminger epb->interface_id = self->port_index[m->port]; 5618d23ce8fSStephen Hemminger do { 5628d23ce8fSStephen Hemminger iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *); 5638d23ce8fSStephen Hemminger iov[cnt].iov_len = rte_pktmbuf_data_len(m); 5648d23ce8fSStephen Hemminger ++cnt; 5658d23ce8fSStephen Hemminger } while ((m = m->next)); 5668d23ce8fSStephen Hemminger } 5678d23ce8fSStephen Hemminger 5688d23ce8fSStephen Hemminger ret = writev(self->outfd, iov, iovcnt); 5698d23ce8fSStephen Hemminger if (unlikely(ret < 0)) 5708d23ce8fSStephen Hemminger rte_errno = errno; 5718d23ce8fSStephen Hemminger return ret; 5728d23ce8fSStephen Hemminger } 5738d23ce8fSStephen Hemminger 5748d23ce8fSStephen Hemminger /* Create new pcapng writer handle */ 5758d23ce8fSStephen Hemminger rte_pcapng_t * 5768d23ce8fSStephen Hemminger rte_pcapng_fdopen(int fd, 5778d23ce8fSStephen Hemminger const char *osname, const char *hardware, 5788d23ce8fSStephen Hemminger const char *appname, const char *comment) 5798d23ce8fSStephen Hemminger { 5808d23ce8fSStephen Hemminger rte_pcapng_t *self; 5818d23ce8fSStephen Hemminger 5828d23ce8fSStephen Hemminger self = malloc(sizeof(*self)); 5838d23ce8fSStephen Hemminger if (!self) { 5848d23ce8fSStephen Hemminger rte_errno = ENOMEM; 5858d23ce8fSStephen Hemminger return NULL; 5868d23ce8fSStephen Hemminger } 5878d23ce8fSStephen Hemminger 5888d23ce8fSStephen Hemminger self->outfd = fd; 5898d23ce8fSStephen Hemminger 5908d23ce8fSStephen Hemminger if (pcapng_section_block(self, osname, hardware, appname, comment) < 0) 5918d23ce8fSStephen Hemminger goto fail; 5928d23ce8fSStephen Hemminger 5938d23ce8fSStephen Hemminger if (pcapng_interfaces(self) < 0) 5948d23ce8fSStephen Hemminger goto fail; 5958d23ce8fSStephen Hemminger 5968d23ce8fSStephen Hemminger return self; 5978d23ce8fSStephen Hemminger fail: 5988d23ce8fSStephen Hemminger free(self); 5998d23ce8fSStephen Hemminger return NULL; 6008d23ce8fSStephen Hemminger } 6018d23ce8fSStephen Hemminger 6028d23ce8fSStephen Hemminger void 6038d23ce8fSStephen Hemminger rte_pcapng_close(rte_pcapng_t *self) 6048d23ce8fSStephen Hemminger { 6058d23ce8fSStephen Hemminger close(self->outfd); 6068d23ce8fSStephen Hemminger free(self); 6078d23ce8fSStephen Hemminger } 608