LCOV - code coverage report
Current view: top level - lib/pcapng - rte_pcapng.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 170 235 72.3 %
Date: 2024-12-01 18:57:19 Functions: 8 9 88.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 81 181 44.8 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2019 Microsoft Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <errno.h>
       6                 :            : #include <stdbool.h>
       7                 :            : #include <stdio.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <time.h>
      11                 :            : #include <unistd.h>
      12                 :            : 
      13                 :            : #ifndef RTE_EXEC_ENV_WINDOWS
      14                 :            : #include <net/if.h>
      15                 :            : #include <sys/uio.h>
      16                 :            : #endif
      17                 :            : 
      18                 :            : #include <bus_driver.h>
      19                 :            : #include <rte_common.h>
      20                 :            : #include <rte_cycles.h>
      21                 :            : #include <dev_driver.h>
      22                 :            : #include <rte_errno.h>
      23                 :            : #include <rte_ethdev.h>
      24                 :            : #include <rte_ether.h>
      25                 :            : #include <rte_mbuf.h>
      26                 :            : #include <rte_os_shim.h>
      27                 :            : #include <rte_pcapng.h>
      28                 :            : #include <rte_reciprocal.h>
      29                 :            : #include <rte_time.h>
      30                 :            : 
      31                 :            : #include "pcapng_proto.h"
      32                 :            : 
      33                 :            : /* conversion from DPDK speed to PCAPNG */
      34                 :            : #define PCAPNG_MBPS_SPEED 1000000ull
      35                 :            : 
      36                 :            : /* upper bound for section, stats and interface blocks (in uint32_t) */
      37                 :            : #define PCAPNG_BLKSIZ   (2048 / sizeof(uint32_t))
      38                 :            : 
      39                 :            : /* Format of the capture file handle */
      40                 :            : struct rte_pcapng {
      41                 :            :         int  outfd;             /* output file */
      42                 :            :         unsigned int ports;     /* number of interfaces added */
      43                 :            :         uint64_t offset_ns;     /* ns since 1/1/1970 when initialized */
      44                 :            :         uint64_t tsc_base;      /* TSC when started */
      45                 :            : 
      46                 :            :         /* DPDK port id to interface index in file */
      47                 :            :         uint32_t port_index[RTE_MAX_ETHPORTS];
      48                 :            : };
      49                 :            : 
      50                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
      51                 :            : /*
      52                 :            :  * Windows does not have writev() call.
      53                 :            :  * Emulate this by copying to a new buffer.
      54                 :            :  * The copy is necessary since pcapng needs to be thread-safe
      55                 :            :  * and do atomic write operations.
      56                 :            :  */
      57                 :            : 
      58                 :            : #define IOV_MAX 128
      59                 :            : struct iovec {
      60                 :            :         void   *iov_base;
      61                 :            :         size_t  iov_len;
      62                 :            : };
      63                 :            : 
      64                 :            : static ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
      65                 :            : {
      66                 :            :         size_t bytes = 0;
      67                 :            :         uint8_t *ptr;
      68                 :            :         void *tmp_buf;
      69                 :            :         ssize_t ret;
      70                 :            :         int i;
      71                 :            : 
      72                 :            :         for (i = 0; i < iovcnt; i++)
      73                 :            :                 bytes += iov[i].iov_len;
      74                 :            : 
      75                 :            :         if (unlikely(bytes == 0))
      76                 :            :                 return 0;
      77                 :            : 
      78                 :            :         tmp_buf = malloc(bytes);
      79                 :            :         if (unlikely(tmp_buf == NULL)) {
      80                 :            :                 errno = ENOMEM;
      81                 :            :                 return -1;
      82                 :            :         }
      83                 :            : 
      84                 :            :         ptr = tmp_buf;
      85                 :            :         for (i = 0; i < iovcnt; i++) {
      86                 :            :                 rte_memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
      87                 :            :                 ptr += iov[i].iov_len;
      88                 :            :         }
      89                 :            : 
      90                 :            :         ret = write(fd, tmp_buf, bytes);
      91                 :            :         free(tmp_buf);
      92                 :            :         return ret;
      93                 :            : }
      94                 :            : 
      95                 :            : #define IF_NAMESIZE     16
      96                 :            : /* compatibility wrapper because name is optional */
      97                 :            : #define if_indextoname(ifindex, ifname) NULL
      98                 :            : #endif
      99                 :            : 
     100                 :            : /* Convert from TSC (CPU cycles) to nanoseconds */
     101                 :            : static uint64_t
     102                 :            : pcapng_timestamp(const rte_pcapng_t *self, uint64_t cycles)
     103                 :            : {
     104                 :            :         uint64_t delta, rem, secs, ns;
     105                 :          1 :         const uint64_t hz = rte_get_tsc_hz();
     106                 :            : 
     107                 :       4129 :         delta = cycles - self->tsc_base;
     108                 :            : 
     109                 :            :         /* Avoid numeric wraparound by computing seconds first */
     110                 :       4129 :         secs = delta / hz;
     111                 :       4129 :         rem = delta % hz;
     112                 :       4129 :         ns = (rem * NS_PER_S) / hz;
     113                 :            : 
     114                 :       4129 :         return secs * NS_PER_S + ns + self->offset_ns;
     115                 :            : }
     116                 :            : 
     117                 :            : /* length of option including padding */
     118                 :            : static uint16_t pcapng_optlen(uint16_t len)
     119                 :            : {
     120                 :         26 :         return RTE_ALIGN(sizeof(struct pcapng_option) + len,
     121                 :            :                          sizeof(uint32_t));
     122                 :            : }
     123                 :            : 
     124                 :            : /* build TLV option and return location of next */
     125                 :            : static struct pcapng_option *
     126                 :            : pcapng_add_option(struct pcapng_option *popt, uint16_t code,
     127                 :            :                   const void *data, uint16_t len)
     128                 :            : {
     129                 :       4154 :         popt->code = code;
     130                 :       4154 :         popt->length = len;
     131                 :         12 :         if (len > 0)
     132         [ -  + ]:       4142 :                 memcpy(popt->data, data, len);
     133                 :            : 
     134                 :         20 :         return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
     135                 :            : }
     136                 :            : 
     137                 :            : /*
     138                 :            :  * Write required initial section header describing the capture
     139                 :            :  */
     140                 :            : static int
     141                 :          2 : pcapng_section_block(rte_pcapng_t *self,
     142                 :            :                     const char *os, const char *hw,
     143                 :            :                     const char *app, const char *comment)
     144                 :            : {
     145                 :            :         struct pcapng_section_header *hdr;
     146                 :            :         struct pcapng_option *opt;
     147                 :            :         uint32_t buf[PCAPNG_BLKSIZ];
     148                 :            :         uint32_t len;
     149                 :            : 
     150                 :            :         len = sizeof(*hdr);
     151         [ -  + ]:          2 :         if (hw)
     152                 :          0 :                 len += pcapng_optlen(strlen(hw));
     153         [ -  + ]:          2 :         if (os)
     154                 :          0 :                 len += pcapng_optlen(strlen(os));
     155         [ +  - ]:          2 :         if (app)
     156                 :          2 :                 len += pcapng_optlen(strlen(app));
     157         [ -  + ]:          2 :         if (comment)
     158                 :          0 :                 len += pcapng_optlen(strlen(comment));
     159                 :            : 
     160                 :            :         /* reserve space for OPT_END */
     161                 :            :         len += pcapng_optlen(0);
     162                 :          2 :         len += sizeof(uint32_t);
     163                 :            : 
     164         [ +  - ]:          2 :         if (len > sizeof(buf))
     165                 :            :                 return -1;
     166                 :            : 
     167                 :            :         hdr = (struct pcapng_section_header *)buf;
     168                 :          2 :         *hdr = (struct pcapng_section_header) {
     169                 :            :                 .block_type = PCAPNG_SECTION_BLOCK,
     170                 :            :                 .block_length = len,
     171                 :            :                 .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
     172                 :            :                 .major_version = PCAPNG_MAJOR_VERS,
     173                 :            :                 .minor_version = PCAPNG_MINOR_VERS,
     174                 :            :                 .section_length = UINT64_MAX,
     175                 :            :         };
     176                 :            : 
     177                 :            :         /* After the section header insert variable length options. */
     178                 :            :         opt = (struct pcapng_option *)(hdr + 1);
     179         [ -  + ]:          2 :         if (comment)
     180                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
     181         [ #  # ]:          0 :                                         comment, strlen(comment));
     182         [ -  + ]:          2 :         if (hw)
     183                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
     184         [ #  # ]:          0 :                                         hw, strlen(hw));
     185         [ -  + ]:          2 :         if (os)
     186                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
     187         [ #  # ]:          0 :                                         os, strlen(os));
     188         [ +  - ]:          2 :         if (app)
     189                 :          2 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
     190         [ +  - ]:          2 :                                         app, strlen(app));
     191                 :            : 
     192                 :            :         /* The standard requires last option to be OPT_END */
     193                 :            :         opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     194                 :            : 
     195                 :            :         /* clone block_length after option */
     196                 :            :         memcpy(opt, &hdr->block_length, sizeof(uint32_t));
     197                 :            : 
     198                 :          2 :         return write(self->outfd, buf, len);
     199                 :            : }
     200                 :            : 
     201                 :            : /* Write an interface block for a DPDK port */
     202                 :            : int
     203                 :          4 : rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
     204                 :            :                          const char *ifname, const char *ifdescr,
     205                 :            :                          const char *filter)
     206                 :            : {
     207                 :            :         struct pcapng_interface_block *hdr;
     208                 :            :         struct rte_eth_dev_info dev_info;
     209                 :            :         struct rte_ether_addr *ea, macaddr;
     210                 :            :         const struct rte_device *dev;
     211                 :            :         struct rte_eth_link link;
     212                 :            :         struct pcapng_option *opt;
     213                 :          4 :         const uint8_t tsresol = 9;      /* nanosecond resolution */
     214                 :            :         uint32_t len;
     215                 :            :         uint32_t buf[PCAPNG_BLKSIZ];
     216                 :            :         char ifname_buf[IF_NAMESIZE];
     217                 :            :         char ifhw[256];
     218                 :          4 :         uint64_t speed = 0;
     219                 :            : 
     220         [ +  - ]:          4 :         if (rte_eth_dev_info_get(port, &dev_info) < 0)
     221                 :            :                 return -1;
     222                 :            : 
     223                 :            :         /* make something like an interface name */
     224         [ +  + ]:          4 :         if (ifname == NULL) {
     225                 :            :                 /* Use kernel name if available */
     226                 :          3 :                 ifname = if_indextoname(dev_info.if_index, ifname_buf);
     227         [ +  - ]:          3 :                 if (ifname == NULL) {
     228                 :            :                         snprintf(ifname_buf, IF_NAMESIZE, "dpdk:%u", port);
     229                 :            :                         ifname = ifname_buf;
     230                 :            :                 }
     231                 :            :         }
     232                 :            : 
     233                 :            :         /* make a useful device hardware string */
     234                 :          4 :         dev = dev_info.device;
     235         [ +  - ]:          4 :         if (dev)
     236                 :            :                 snprintf(ifhw, sizeof(ifhw),
     237                 :          4 :                          "%s-%s", dev->bus->name, dev->name);
     238                 :            : 
     239                 :            :         /* DPDK reports in units of Mbps */
     240         [ +  - ]:          4 :         if (rte_eth_link_get(port, &link) == 0 &&
     241         [ -  + ]:          4 :             link.link_status == RTE_ETH_LINK_UP)
     242                 :          0 :                 speed = link.link_speed * PCAPNG_MBPS_SPEED;
     243                 :            : 
     244         [ +  - ]:          4 :         if (rte_eth_macaddr_get(port, &macaddr) < 0)
     245                 :            :                 ea = NULL;
     246                 :            :         else
     247                 :            :                 ea = &macaddr;
     248                 :            : 
     249                 :            :         /* Compute length of interface block options */
     250                 :            :         len = sizeof(*hdr);
     251                 :            : 
     252                 :            :         len += pcapng_optlen(sizeof(tsresol));  /* timestamp */
     253                 :          4 :         len += pcapng_optlen(strlen(ifname));   /* ifname */
     254                 :            : 
     255         [ +  + ]:          4 :         if (ifdescr)
     256                 :          1 :                 len += pcapng_optlen(strlen(ifdescr));
     257         [ +  - ]:          4 :         if (ea)
     258                 :          4 :                 len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
     259         [ -  + ]:          4 :         if (speed != 0)
     260                 :          0 :                 len += pcapng_optlen(sizeof(uint64_t));
     261         [ +  + ]:          4 :         if (filter)
     262                 :          1 :                 len += pcapng_optlen(strlen(filter) + 1);
     263         [ +  - ]:          4 :         if (dev)
     264                 :          4 :                 len += pcapng_optlen(strlen(ifhw));
     265                 :            : 
     266                 :            :         len += pcapng_optlen(0);
     267                 :          4 :         len += sizeof(uint32_t);
     268                 :            : 
     269         [ +  - ]:          4 :         if (len > sizeof(buf))
     270                 :            :                 return -1;
     271                 :            : 
     272                 :            :         hdr = (struct pcapng_interface_block *)buf;
     273         [ +  - ]:          4 :         *hdr = (struct pcapng_interface_block) {
     274                 :            :                 .block_type = PCAPNG_INTERFACE_BLOCK,
     275                 :            :                 .link_type = 1,         /* DLT_EN10MB - Ethernet */
     276                 :            :                 .block_length = len,
     277                 :            :         };
     278                 :            : 
     279                 :            :         opt = (struct pcapng_option *)(hdr + 1);
     280                 :            :         opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
     281                 :            :                                 &tsresol, sizeof(tsresol));
     282                 :          4 :         opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
     283         [ +  - ]:          4 :                                 ifname, strlen(ifname));
     284         [ +  + ]:          4 :         if (ifdescr)
     285                 :          1 :                 opt = pcapng_add_option(opt, PCAPNG_IFB_DESCRIPTION,
     286         [ +  - ]:          1 :                                         ifdescr, strlen(ifdescr));
     287         [ +  - ]:          4 :         if (ea)
     288                 :            :                 opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
     289                 :            :                                         ea, RTE_ETHER_ADDR_LEN);
     290         [ -  + ]:          4 :         if (speed != 0)
     291                 :            :                 opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
     292                 :            :                                          &speed, sizeof(uint64_t));
     293         [ +  - ]:          4 :         if (dev)
     294                 :          4 :                 opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
     295         [ +  - ]:          4 :                                          ifhw, strlen(ifhw));
     296         [ +  + ]:          4 :         if (filter) {
     297                 :            :                 size_t len;
     298                 :            : 
     299                 :          1 :                 len = strlen(filter) + 1;
     300                 :          1 :                 opt->code = PCAPNG_IFB_FILTER;
     301                 :          1 :                 opt->length = len;
     302                 :            :                 /* Encoding is that the first octet indicates string vs BPF */
     303                 :          1 :                 opt->data[0] = 0;
     304                 :          1 :                 memcpy(opt->data + 1, filter, strlen(filter));
     305                 :            : 
     306                 :          1 :                 opt = (struct pcapng_option *)((uint8_t *)opt + pcapng_optlen(len));
     307                 :            :         }
     308                 :            : 
     309                 :            :         opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     310                 :            : 
     311                 :            :         /* clone block_length after optionsa */
     312                 :            :         memcpy(opt, &hdr->block_length, sizeof(uint32_t));
     313                 :            : 
     314                 :            :         /* remember the file index */
     315                 :          4 :         self->port_index[port] = self->ports++;
     316                 :            : 
     317                 :          4 :         return write(self->outfd, buf, len);
     318                 :            : }
     319                 :            : 
     320                 :            : /*
     321                 :            :  * Write an Interface statistics block at the end of capture.
     322                 :            :  */
     323                 :            : ssize_t
     324                 :          1 : rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
     325                 :            :                        uint64_t ifrecv, uint64_t ifdrop,
     326                 :            :                        const char *comment)
     327                 :            : {
     328                 :            :         struct pcapng_statistics *hdr;
     329                 :            :         struct pcapng_option *opt;
     330                 :          1 :         uint64_t start_time = self->offset_ns;
     331                 :            :         uint64_t sample_time;
     332                 :            :         uint32_t optlen, len;
     333                 :            :         uint32_t buf[PCAPNG_BLKSIZ];
     334                 :            : 
     335         [ -  + ]:          1 :         RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
     336                 :            : 
     337                 :            :         optlen = 0;
     338                 :            : 
     339         [ +  - ]:          1 :         if (ifrecv != UINT64_MAX)
     340                 :            :                 optlen += pcapng_optlen(sizeof(ifrecv));
     341         [ +  - ]:          1 :         if (ifdrop != UINT64_MAX)
     342                 :          1 :                 optlen += pcapng_optlen(sizeof(ifdrop));
     343                 :            : 
     344         [ +  - ]:          1 :         if (start_time != 0)
     345                 :          1 :                 optlen += pcapng_optlen(sizeof(start_time));
     346                 :            : 
     347         [ +  - ]:          1 :         if (comment)
     348                 :          1 :                 optlen += pcapng_optlen(strlen(comment));
     349         [ +  - ]:          1 :         if (optlen != 0)
     350                 :          1 :                 optlen += pcapng_optlen(0);
     351                 :            : 
     352                 :          1 :         len = sizeof(*hdr) + optlen + sizeof(uint32_t);
     353         [ +  - ]:          1 :         if (len > sizeof(buf))
     354                 :            :                 return -1;
     355                 :            : 
     356                 :            :         hdr = (struct pcapng_statistics *)buf;
     357                 :            :         opt = (struct pcapng_option *)(hdr + 1);
     358                 :            : 
     359         [ +  - ]:          1 :         if (comment)
     360                 :          1 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
     361         [ +  - ]:          1 :                                         comment, strlen(comment));
     362         [ +  - ]:          1 :         if (start_time != 0)
     363                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
     364                 :            :                                          &start_time, sizeof(start_time));
     365         [ +  - ]:          1 :         if (ifrecv != UINT64_MAX)
     366                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
     367                 :            :                                 &ifrecv, sizeof(ifrecv));
     368         [ +  - ]:          1 :         if (ifdrop != UINT64_MAX)
     369                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
     370                 :            :                                 &ifdrop, sizeof(ifdrop));
     371         [ +  - ]:          1 :         if (optlen != 0)
     372                 :            :                 opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     373                 :            : 
     374                 :          1 :         hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
     375                 :          1 :         hdr->block_length = len;
     376                 :          1 :         hdr->interface_id = self->port_index[port_id];
     377                 :            : 
     378                 :            :         sample_time = pcapng_timestamp(self, rte_get_tsc_cycles());
     379                 :          1 :         hdr->timestamp_hi = sample_time >> 32;
     380                 :          1 :         hdr->timestamp_lo = (uint32_t)sample_time;
     381                 :            : 
     382                 :            :         /* clone block_length after option */
     383                 :            :         memcpy(opt, &len, sizeof(uint32_t));
     384                 :            : 
     385                 :          1 :         return write(self->outfd, buf, len);
     386                 :            : }
     387                 :            : 
     388                 :            : uint32_t
     389                 :          1 : rte_pcapng_mbuf_size(uint32_t length)
     390                 :            : {
     391                 :            :         /* The VLAN and EPB header must fit in the mbuf headroom. */
     392                 :            :         RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
     393                 :            :                    sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
     394                 :            : 
     395                 :            :         /* The flags and queue information are added at the end. */
     396                 :            :         return sizeof(struct rte_mbuf)
     397                 :          1 :                 + RTE_ALIGN(length, sizeof(uint32_t))
     398                 :            :                 + pcapng_optlen(sizeof(uint32_t)) /* flag option */
     399                 :            :                 + pcapng_optlen(sizeof(uint32_t)) /* queue option */
     400                 :          1 :                 + sizeof(uint32_t);               /*  length */
     401                 :            : }
     402                 :            : 
     403                 :            : /* More generalized version rte_vlan_insert() */
     404                 :            : static int
     405                 :          0 : pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
     406                 :            : {
     407                 :            :         struct rte_ether_hdr *nh, *oh;
     408                 :            :         struct rte_vlan_hdr *vh;
     409                 :            : 
     410   [ #  #  #  # ]:          0 :         if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
     411                 :          0 :                 return -EINVAL;
     412                 :            : 
     413         [ #  # ]:          0 :         if (rte_pktmbuf_data_len(m) < sizeof(*oh))
     414                 :            :                 return -EINVAL;
     415                 :            : 
     416         [ #  # ]:          0 :         oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
     417                 :            :         nh = (struct rte_ether_hdr *)
     418                 :            :                 rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
     419         [ #  # ]:          0 :         if (nh == NULL)
     420                 :          0 :                 return -ENOSPC;
     421                 :            : 
     422                 :            :         memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
     423         [ #  # ]:          0 :         nh->ether_type = rte_cpu_to_be_16(ether_type);
     424                 :            : 
     425                 :            :         vh = (struct rte_vlan_hdr *) (nh + 1);
     426         [ #  # ]:          0 :         vh->vlan_tci = rte_cpu_to_be_16(tci);
     427                 :            : 
     428                 :          0 :         return 0;
     429                 :            : }
     430                 :            : 
     431                 :            : /*
     432                 :            :  *   The mbufs created use the Pcapng standard enhanced packet  block.
     433                 :            :  *
     434                 :            :  *                         1                   2                   3
     435                 :            :  *     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
     436                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     437                 :            :  *  0 |                    Block Type = 0x00000006                    |
     438                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     439                 :            :  *  4 |                      Block Total Length                       |
     440                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     441                 :            :  *  8 |                         Interface ID                          |
     442                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     443                 :            :  * 12 |                        Timestamp (High)                       |
     444                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     445                 :            :  * 16 |                        Timestamp (Low)                        |
     446                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     447                 :            :  * 20 |                    Captured Packet Length                     |
     448                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     449                 :            :  * 24 |                    Original Packet Length                     |
     450                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     451                 :            :  * 28 /                                                               /
     452                 :            :  *    /                          Packet Data                          /
     453                 :            :  *    /              variable length, padded to 32 bits               /
     454                 :            :  *    /                                                               /
     455                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     456                 :            :  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
     457                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     458                 :            :  *    |              Flags (direction)                                |
     459                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     460                 :            :  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
     461                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     462                 :            :  *    |              Queue id                                         |
     463                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     464                 :            :  *    |                      Block Total Length                       |
     465                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     466                 :            :  */
     467                 :            : 
     468                 :            : /* Make a copy of original mbuf with pcapng header and options */
     469                 :            : struct rte_mbuf *
     470                 :       4128 : rte_pcapng_copy(uint16_t port_id, uint32_t queue,
     471                 :            :                 const struct rte_mbuf *md,
     472                 :            :                 struct rte_mempool *mp,
     473                 :            :                 uint32_t length,
     474                 :            :                 enum rte_pcapng_direction direction,
     475                 :            :                 const char *comment)
     476                 :            : {
     477                 :            :         struct pcapng_enhance_packet_block *epb;
     478                 :            :         uint32_t orig_len, pkt_len, padding, flags;
     479                 :            :         struct pcapng_option *opt;
     480                 :            :         uint64_t timestamp;
     481                 :            :         uint16_t optlen;
     482                 :            :         struct rte_mbuf *mc;
     483                 :            :         bool rss_hash;
     484                 :            : 
     485                 :            : #ifdef RTE_LIBRTE_ETHDEV_DEBUG
     486                 :            :         RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
     487                 :            : #endif
     488                 :       4128 :         orig_len = rte_pktmbuf_pkt_len(md);
     489                 :            : 
     490                 :            :         /* Take snapshot of the data */
     491                 :       4128 :         mc = rte_pktmbuf_copy(md, mp, 0, length);
     492         [ +  - ]:       4128 :         if (unlikely(mc == NULL))
     493                 :            :                 return NULL;
     494                 :            : 
     495                 :            :         /* Expand any offloaded VLAN information */
     496         [ +  - ]:       4128 :         if ((direction == RTE_PCAPNG_DIRECTION_IN &&
     497   [ +  -  -  + ]:       4128 :              (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
     498                 :          0 :             (direction == RTE_PCAPNG_DIRECTION_OUT &&
     499         [ #  # ]:          0 :              (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
     500         [ #  # ]:          0 :                 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
     501                 :          0 :                                        md->vlan_tci) != 0)
     502                 :          0 :                         goto fail;
     503                 :            :         }
     504                 :            : 
     505         [ +  - ]:       4128 :         if ((direction == RTE_PCAPNG_DIRECTION_IN &&
     506   [ +  -  -  + ]:       4128 :              (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
     507                 :          0 :             (direction == RTE_PCAPNG_DIRECTION_OUT &&
     508         [ #  # ]:          0 :              (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
     509         [ #  # ]:          0 :                 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
     510                 :          0 :                                        md->vlan_tci_outer) != 0)
     511                 :          0 :                         goto fail;
     512                 :            :         }
     513                 :            : 
     514                 :            :         /* record HASH on incoming packets */
     515         [ +  - ]:       4128 :         rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
     516         [ +  - ]:       4128 :                     (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
     517                 :            : 
     518                 :            :         /* pad the packet to 32 bit boundary */
     519                 :       4128 :         pkt_len = rte_pktmbuf_pkt_len(mc);
     520                 :       4128 :         padding = RTE_ALIGN(pkt_len, sizeof(uint32_t)) - pkt_len;
     521         [ -  + ]:       4128 :         if (padding > 0) {
     522                 :          0 :                 void *tail = rte_pktmbuf_append(mc, padding);
     523                 :            : 
     524         [ #  # ]:          0 :                 if (tail == NULL)
     525                 :          0 :                         goto fail;
     526                 :          0 :                 memset(tail, 0, padding);
     527                 :            :         }
     528                 :            : 
     529                 :            :         optlen = pcapng_optlen(sizeof(flags));
     530                 :            :         optlen += pcapng_optlen(sizeof(queue));
     531         [ -  + ]:       4128 :         if (rss_hash)
     532                 :            :                 optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
     533                 :            : 
     534         [ -  + ]:       4128 :         if (comment)
     535                 :          0 :                 optlen += pcapng_optlen(strlen(comment));
     536                 :            : 
     537                 :            :         /* reserve trailing options and block length */
     538                 :            :         opt = (struct pcapng_option *)
     539                 :       4128 :                 rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
     540         [ -  + ]:       4128 :         if (unlikely(opt == NULL))
     541                 :          0 :                 goto fail;
     542                 :            : 
     543      [ +  -  - ]:       4128 :         switch (direction) {
     544                 :       4128 :         case RTE_PCAPNG_DIRECTION_IN:
     545                 :       4128 :                 flags = PCAPNG_IFB_INBOUND;
     546                 :       4128 :                 break;
     547                 :          0 :         case RTE_PCAPNG_DIRECTION_OUT:
     548                 :          0 :                 flags = PCAPNG_IFB_OUTBOUND;
     549                 :          0 :                 break;
     550                 :          0 :         default:
     551                 :          0 :                 flags = 0;
     552                 :            :         }
     553                 :            : 
     554                 :            :         opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
     555                 :            :                                 &flags, sizeof(flags));
     556                 :            : 
     557                 :            :         opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
     558                 :            :                                 &queue, sizeof(queue));
     559                 :            : 
     560         [ -  + ]:       4128 :         if (rss_hash) {
     561                 :            :                 uint8_t hash_opt[5];
     562                 :            : 
     563                 :            :                 /* The algorithm could be something else if
     564                 :            :                  * using rte_flow_action_rss; but the current API does not
     565                 :            :                  * have a way for ethdev to report  this on a per-packet basis.
     566                 :            :                  */
     567                 :          0 :                 hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
     568                 :            : 
     569                 :            :                 memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
     570                 :            :                 opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
     571                 :            :                                         &hash_opt, sizeof(hash_opt));
     572                 :            :         }
     573                 :            : 
     574         [ -  + ]:       4128 :         if (comment)
     575                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, comment,
     576         [ #  # ]:          0 :                                         strlen(comment));
     577                 :            : 
     578                 :            :         /* Note: END_OPT necessary here. Wireshark doesn't do it. */
     579                 :            : 
     580                 :            :         /* Add PCAPNG packet header */
     581                 :            :         epb = (struct pcapng_enhance_packet_block *)
     582                 :            :                 rte_pktmbuf_prepend(mc, sizeof(*epb));
     583         [ -  + ]:       4128 :         if (unlikely(epb == NULL))
     584                 :          0 :                 goto fail;
     585                 :            : 
     586                 :       4128 :         epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
     587                 :       4128 :         epb->block_length = rte_pktmbuf_pkt_len(mc);
     588                 :            : 
     589                 :            :         /* Interface index is filled in later during write */
     590                 :       4128 :         mc->port = port_id;
     591                 :            : 
     592                 :            :         /* Put timestamp in cycles here - adjust in packet write */
     593                 :            :         timestamp = rte_get_tsc_cycles();
     594                 :       4128 :         epb->timestamp_hi = timestamp >> 32;
     595                 :       4128 :         epb->timestamp_lo = (uint32_t)timestamp;
     596                 :       4128 :         epb->capture_length = pkt_len;
     597                 :       4128 :         epb->original_length = orig_len;
     598                 :            : 
     599                 :            :         /* set trailer of block length */
     600                 :       4128 :         *(uint32_t *)opt = epb->block_length;
     601                 :            : 
     602                 :       4128 :         return mc;
     603                 :            : 
     604                 :          0 : fail:
     605                 :          0 :         rte_pktmbuf_free(mc);
     606                 :          0 :         return NULL;
     607                 :            : }
     608                 :            : 
     609                 :            : /* Write pre-formatted packets to file. */
     610                 :            : ssize_t
     611                 :        123 : rte_pcapng_write_packets(rte_pcapng_t *self,
     612                 :            :                          struct rte_mbuf *pkts[], uint16_t nb_pkts)
     613                 :            : {
     614                 :            :         struct iovec iov[IOV_MAX];
     615                 :            :         unsigned int i, cnt = 0;
     616                 :            :         ssize_t ret, total = 0;
     617                 :            : 
     618         [ +  + ]:       4251 :         for (i = 0; i < nb_pkts; i++) {
     619                 :       4128 :                 struct rte_mbuf *m = pkts[i];
     620                 :            :                 struct pcapng_enhance_packet_block *epb;
     621                 :            :                 uint64_t cycles, timestamp;
     622                 :            : 
     623                 :            :                 /* sanity check that is really a pcapng mbuf */
     624                 :       4128 :                 epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
     625   [ +  -  -  + ]:       4128 :                 if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
     626                 :            :                              epb->block_length != rte_pktmbuf_pkt_len(m))) {
     627                 :          0 :                         rte_errno = EINVAL;
     628                 :          0 :                         return -1;
     629                 :            :                 }
     630                 :            : 
     631                 :            :                 /* check that this interface was added. */
     632                 :       4128 :                 epb->interface_id = self->port_index[m->port];
     633         [ -  + ]:       4128 :                 if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) {
     634                 :          0 :                         rte_errno = EINVAL;
     635                 :          0 :                         return -1;
     636                 :            :                 }
     637                 :            : 
     638                 :            :                 /* adjust timestamp recorded in packet */
     639                 :       4128 :                 cycles = (uint64_t)epb->timestamp_hi << 32;
     640                 :       4128 :                 cycles += epb->timestamp_lo;
     641                 :            :                 timestamp = pcapng_timestamp(self, cycles);
     642                 :       4128 :                 epb->timestamp_hi = timestamp >> 32;
     643                 :       4128 :                 epb->timestamp_lo = (uint32_t)timestamp;
     644                 :            : 
     645                 :            :                 /*
     646                 :            :                  * Handle case of highly fragmented and large burst size
     647                 :            :                  * Note: this assumes that max segments per mbuf < IOV_MAX
     648                 :            :                  */
     649         [ -  + ]:       4128 :                 if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
     650                 :          0 :                         ret = writev(self->outfd, iov, cnt);
     651         [ #  # ]:          0 :                         if (unlikely(ret < 0)) {
     652                 :          0 :                                 rte_errno = errno;
     653                 :          0 :                                 return -1;
     654                 :            :                         }
     655                 :          0 :                         total += ret;
     656                 :            :                         cnt = 0;
     657                 :            :                 }
     658                 :            : 
     659                 :            :                 /*
     660                 :            :                  * The DPDK port is recorded during pcapng_copy.
     661                 :            :                  * Map that to PCAPNG interface in file.
     662                 :            :                  */
     663                 :            :                 do {
     664                 :       8256 :                         iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
     665                 :       8256 :                         iov[cnt].iov_len = rte_pktmbuf_data_len(m);
     666                 :       8256 :                         ++cnt;
     667         [ +  + ]:       8256 :                 } while ((m = m->next));
     668                 :            :         }
     669                 :            : 
     670                 :        123 :         ret = writev(self->outfd, iov, cnt);
     671         [ -  + ]:        123 :         if (unlikely(ret < 0)) {
     672                 :          0 :                 rte_errno = errno;
     673                 :          0 :                 return -1;
     674                 :            :         }
     675                 :        123 :         return total + ret;
     676                 :            : }
     677                 :            : 
     678                 :            : /* Create new pcapng writer handle */
     679                 :            : rte_pcapng_t *
     680                 :          2 : rte_pcapng_fdopen(int fd,
     681                 :            :                   const char *osname, const char *hardware,
     682                 :            :                   const char *appname, const char *comment)
     683                 :            : {
     684                 :            :         unsigned int i;
     685                 :            :         rte_pcapng_t *self;
     686                 :            :         struct timespec ts;
     687                 :            :         uint64_t cycles;
     688                 :            : 
     689                 :          2 :         self = malloc(sizeof(*self));
     690         [ -  + ]:          2 :         if (!self) {
     691                 :          0 :                 rte_errno = ENOMEM;
     692                 :          0 :                 return NULL;
     693                 :            :         }
     694                 :            : 
     695                 :          2 :         self->outfd = fd;
     696                 :          2 :         self->ports = 0;
     697                 :            : 
     698                 :            :         /* record start time in ns since 1/1/1970 */
     699                 :            :         cycles = rte_get_tsc_cycles();
     700                 :          2 :         clock_gettime(CLOCK_REALTIME, &ts);
     701                 :          2 :         self->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
     702                 :          2 :         self->offset_ns = rte_timespec_to_ns(&ts);
     703                 :            : 
     704         [ +  + ]:         66 :         for (i = 0; i < RTE_MAX_ETHPORTS; i++)
     705                 :         64 :                 self->port_index[i] = UINT32_MAX;
     706                 :            : 
     707         [ -  + ]:          2 :         if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
     708                 :          0 :                 goto fail;
     709                 :            : 
     710                 :            :         return self;
     711                 :            : fail:
     712                 :          0 :         free(self);
     713                 :          0 :         return NULL;
     714                 :            : }
     715                 :            : 
     716                 :            : void
     717                 :          2 : rte_pcapng_close(rte_pcapng_t *self)
     718                 :            : {
     719                 :          2 :         close(self->outfd);
     720                 :          2 :         free(self);
     721                 :          2 : }

Generated by: LCOV version 1.14