LCOV - code coverage report
Current view: top level - app/test-pmd - csumonly.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 445 0.0 %
Date: 2024-12-01 18:57:19 Functions: 0 14 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2014 Intel Corporation.
       3                 :            :  * Copyright 2014 6WIND S.A.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <stdarg.h>
       7                 :            : #include <stdio.h>
       8                 :            : #include <errno.h>
       9                 :            : #include <stdint.h>
      10                 :            : #include <unistd.h>
      11                 :            : #include <inttypes.h>
      12                 :            : 
      13                 :            : #include <sys/queue.h>
      14                 :            : #include <sys/stat.h>
      15                 :            : 
      16                 :            : #include <rte_common.h>
      17                 :            : #include <rte_byteorder.h>
      18                 :            : #include <rte_log.h>
      19                 :            : #include <rte_debug.h>
      20                 :            : #include <rte_cycles.h>
      21                 :            : #include <rte_memory.h>
      22                 :            : #include <rte_memcpy.h>
      23                 :            : #include <rte_launch.h>
      24                 :            : #include <rte_eal.h>
      25                 :            : #include <rte_per_lcore.h>
      26                 :            : #include <rte_lcore.h>
      27                 :            : #include <rte_branch_prediction.h>
      28                 :            : #include <rte_mempool.h>
      29                 :            : #include <rte_mbuf.h>
      30                 :            : #include <rte_interrupts.h>
      31                 :            : #include <rte_ether.h>
      32                 :            : #include <rte_ethdev.h>
      33                 :            : #include <rte_ip.h>
      34                 :            : #include <rte_tcp.h>
      35                 :            : #include <rte_udp.h>
      36                 :            : #include <rte_vxlan.h>
      37                 :            : #include <rte_sctp.h>
      38                 :            : #include <rte_gtp.h>
      39                 :            : #include <rte_prefetch.h>
      40                 :            : #include <rte_string_fns.h>
      41                 :            : #include <rte_flow.h>
      42                 :            : #ifdef RTE_LIB_GRO
      43                 :            : #include <rte_gro.h>
      44                 :            : #endif
      45                 :            : #ifdef RTE_LIB_GSO
      46                 :            : #include <rte_gso.h>
      47                 :            : #endif
      48                 :            : #include <rte_geneve.h>
      49                 :            : 
      50                 :            : #include "testpmd.h"
      51                 :            : 
      52                 :            : #define IP_DEFTTL  64   /* from RFC 1340. */
      53                 :            : 
      54                 :            : #define GRE_CHECKSUM_PRESENT    0x8000
      55                 :            : #define GRE_KEY_PRESENT         0x2000
      56                 :            : #define GRE_SEQUENCE_PRESENT    0x1000
      57                 :            : #define GRE_EXT_LEN             4
      58                 :            : #define GRE_SUPPORTED_FIELDS    (GRE_CHECKSUM_PRESENT | GRE_KEY_PRESENT |\
      59                 :            :                                  GRE_SEQUENCE_PRESENT)
      60                 :            : 
      61                 :            : /* We cannot use rte_cpu_to_be_16() on a constant in a switch/case */
      62                 :            : #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
      63                 :            : #define _htons(x) ((uint16_t)((((x) & 0x00ffU) << 8) | (((x) & 0xff00U) >> 8)))
      64                 :            : #else
      65                 :            : #define _htons(x) (x)
      66                 :            : #endif
      67                 :            : 
      68                 :            : uint16_t vxlan_gpe_udp_port = RTE_VXLAN_GPE_DEFAULT_PORT;
      69                 :            : uint16_t geneve_udp_port = RTE_GENEVE_DEFAULT_PORT;
      70                 :            : 
      71                 :            : /* structure that caches offload info for the current packet */
      72                 :            : struct testpmd_offload_info {
      73                 :            :         uint16_t ethertype;
      74                 :            : #ifdef RTE_LIB_GSO
      75                 :            :         uint8_t gso_enable;
      76                 :            : #endif
      77                 :            :         uint16_t l2_len;
      78                 :            :         uint16_t l3_len;
      79                 :            :         uint16_t l4_len;
      80                 :            :         uint8_t l4_proto;
      81                 :            :         uint8_t is_tunnel;
      82                 :            :         uint16_t outer_ethertype;
      83                 :            :         uint16_t outer_l2_len;
      84                 :            :         uint16_t outer_l3_len;
      85                 :            :         uint8_t outer_l4_proto;
      86                 :            :         uint16_t tso_segsz;
      87                 :            :         uint16_t tunnel_tso_segsz;
      88                 :            :         uint32_t pkt_len;
      89                 :            : };
      90                 :            : 
      91                 :            : /* simplified GRE header */
      92                 :            : struct simple_gre_hdr {
      93                 :            :         uint16_t flags;
      94                 :            :         uint16_t proto;
      95                 :            : } __rte_packed;
      96                 :            : 
      97                 :            : static uint16_t
      98                 :          0 : get_udptcp_checksum(struct rte_mbuf *m, void *l3_hdr, uint16_t l4_off,
      99                 :            :                     uint16_t ethertype)
     100                 :            : {
     101                 :          0 :         if (ethertype == _htons(RTE_ETHER_TYPE_IPV4))
     102                 :          0 :                 return rte_ipv4_udptcp_cksum_mbuf(m, l3_hdr, l4_off);
     103                 :            :         else /* assume ethertype == RTE_ETHER_TYPE_IPV6 */
     104                 :          0 :                 return rte_ipv6_udptcp_cksum_mbuf(m, l3_hdr, l4_off);
     105                 :            : }
     106                 :            : 
     107                 :            : /* Parse an IPv4 header to fill l3_len, l4_len, and l4_proto */
     108                 :            : static void
     109                 :            : parse_ipv4(struct rte_ipv4_hdr *ipv4_hdr, struct testpmd_offload_info *info)
     110                 :            : {
     111                 :            :         struct rte_tcp_hdr *tcp_hdr;
     112                 :            : 
     113                 :          0 :         info->l3_len = rte_ipv4_hdr_len(ipv4_hdr);
     114                 :          0 :         info->l4_proto = ipv4_hdr->next_proto_id;
     115                 :            : 
     116                 :            :         /* only fill l4_len for TCP, it's useful for TSO */
     117                 :          0 :         if (info->l4_proto == IPPROTO_TCP) {
     118                 :          0 :                 tcp_hdr = (struct rte_tcp_hdr *)
     119                 :          0 :                         ((char *)ipv4_hdr + info->l3_len);
     120                 :          0 :                 info->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
     121                 :          0 :         } else if (info->l4_proto == IPPROTO_UDP)
     122                 :          0 :                 info->l4_len = sizeof(struct rte_udp_hdr);
     123                 :            :         else
     124                 :          0 :                 info->l4_len = 0;
     125                 :            : }
     126                 :            : 
     127                 :            : /* Parse an IPv6 header to fill l3_len, l4_len, and l4_proto */
     128                 :            : static void
     129                 :            : parse_ipv6(struct rte_ipv6_hdr *ipv6_hdr, struct testpmd_offload_info *info)
     130                 :            : {
     131                 :            :         struct rte_tcp_hdr *tcp_hdr;
     132                 :            : 
     133                 :          0 :         info->l3_len = sizeof(struct rte_ipv6_hdr);
     134                 :          0 :         info->l4_proto = ipv6_hdr->proto;
     135                 :            : 
     136                 :            :         /* only fill l4_len for TCP, it's useful for TSO */
     137                 :          0 :         if (info->l4_proto == IPPROTO_TCP) {
     138                 :            :                 tcp_hdr = (struct rte_tcp_hdr *)
     139                 :            :                         ((char *)ipv6_hdr + info->l3_len);
     140                 :          0 :                 info->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
     141                 :          0 :         } else if (info->l4_proto == IPPROTO_UDP)
     142                 :          0 :                 info->l4_len = sizeof(struct rte_udp_hdr);
     143                 :            :         else
     144                 :          0 :                 info->l4_len = 0;
     145                 :            : }
     146                 :            : 
     147                 :            : /*
     148                 :            :  * Parse an ethernet header to fill the ethertype, l2_len, l3_len and
     149                 :            :  * ipproto. This function is able to recognize IPv4/IPv6 with optional VLAN
     150                 :            :  * headers. The l4_len argument is only set in case of TCP (useful for TSO).
     151                 :            :  */
     152                 :            : static void
     153                 :          0 : parse_ethernet(struct rte_ether_hdr *eth_hdr, struct testpmd_offload_info *info)
     154                 :            : {
     155                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     156                 :            :         struct rte_ipv6_hdr *ipv6_hdr;
     157                 :            :         struct rte_vlan_hdr *vlan_hdr;
     158                 :            : 
     159                 :          0 :         info->l2_len = sizeof(struct rte_ether_hdr);
     160                 :          0 :         info->ethertype = eth_hdr->ether_type;
     161                 :            : 
     162                 :          0 :         while (info->ethertype == _htons(RTE_ETHER_TYPE_VLAN) ||
     163                 :            :                info->ethertype == _htons(RTE_ETHER_TYPE_QINQ)) {
     164                 :          0 :                 vlan_hdr = (struct rte_vlan_hdr *)
     165                 :          0 :                         ((char *)eth_hdr + info->l2_len);
     166                 :          0 :                 info->l2_len  += sizeof(struct rte_vlan_hdr);
     167                 :          0 :                 info->ethertype = vlan_hdr->eth_proto;
     168                 :            :         }
     169                 :            : 
     170                 :          0 :         switch (info->ethertype) {
     171                 :          0 :         case _htons(RTE_ETHER_TYPE_IPV4):
     172                 :          0 :                 ipv4_hdr = (struct rte_ipv4_hdr *)
     173                 :          0 :                         ((char *)eth_hdr + info->l2_len);
     174                 :            :                 parse_ipv4(ipv4_hdr, info);
     175                 :            :                 break;
     176                 :          0 :         case _htons(RTE_ETHER_TYPE_IPV6):
     177                 :          0 :                 ipv6_hdr = (struct rte_ipv6_hdr *)
     178                 :          0 :                         ((char *)eth_hdr + info->l2_len);
     179                 :            :                 parse_ipv6(ipv6_hdr, info);
     180                 :            :                 break;
     181                 :          0 :         default:
     182                 :          0 :                 info->l4_len = 0;
     183                 :          0 :                 info->l3_len = 0;
     184                 :          0 :                 info->l4_proto = 0;
     185                 :          0 :                 break;
     186                 :            :         }
     187                 :          0 : }
     188                 :            : 
     189                 :            : /* Fill in outer layers length */
     190                 :            : static void
     191                 :            : update_tunnel_outer(struct testpmd_offload_info *info)
     192                 :            : {
     193                 :          0 :         info->is_tunnel = 1;
     194                 :          0 :         info->outer_ethertype = info->ethertype;
     195                 :          0 :         info->outer_l2_len = info->l2_len;
     196                 :          0 :         info->outer_l3_len = info->l3_len;
     197                 :          0 :         info->outer_l4_proto = info->l4_proto;
     198                 :            : }
     199                 :            : 
     200                 :            : /*
     201                 :            :  * Parse a GTP protocol header.
     202                 :            :  * No optional fields and next extension header type.
     203                 :            :  */
     204                 :            : static void
     205                 :          0 : parse_gtp(struct rte_udp_hdr *udp_hdr,
     206                 :            :           struct testpmd_offload_info *info)
     207                 :            : {
     208                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     209                 :            :         struct rte_ipv6_hdr *ipv6_hdr;
     210                 :            :         struct rte_gtp_hdr *gtp_hdr;
     211                 :            :         uint8_t gtp_len = sizeof(*gtp_hdr);
     212                 :            :         uint8_t ip_ver;
     213                 :            : 
     214                 :            :         /* Check udp destination port. */
     215                 :          0 :         if (udp_hdr->dst_port != _htons(RTE_GTPC_UDP_PORT) &&
     216                 :          0 :             udp_hdr->src_port != _htons(RTE_GTPC_UDP_PORT) &&
     217                 :            :             udp_hdr->dst_port != _htons(RTE_GTPU_UDP_PORT))
     218                 :            :                 return;
     219                 :            : 
     220                 :            :         update_tunnel_outer(info);
     221                 :          0 :         info->l2_len = 0;
     222                 :            : 
     223                 :          0 :         gtp_hdr = (struct rte_gtp_hdr *)((char *)udp_hdr +
     224                 :            :                   sizeof(struct rte_udp_hdr));
     225                 :          0 :         if (gtp_hdr->e || gtp_hdr->s || gtp_hdr->pn)
     226                 :            :                 gtp_len += sizeof(struct rte_gtp_hdr_ext_word);
     227                 :            :         /*
     228                 :            :          * Check message type. If message type is 0xff, it is
     229                 :            :          * a GTP data packet. If not, it is a GTP control packet
     230                 :            :          */
     231                 :          0 :         if (gtp_hdr->msg_type == 0xff) {
     232                 :          0 :                 ip_ver = *(uint8_t *)((char *)gtp_hdr + gtp_len);
     233                 :          0 :                 ip_ver = (ip_ver) & 0xf0;
     234                 :            : 
     235                 :          0 :                 if (ip_ver == RTE_GTP_TYPE_IPV4) {
     236                 :            :                         ipv4_hdr = (struct rte_ipv4_hdr *)((char *)gtp_hdr +
     237                 :            :                                    gtp_len);
     238                 :          0 :                         info->ethertype = _htons(RTE_ETHER_TYPE_IPV4);
     239                 :            :                         parse_ipv4(ipv4_hdr, info);
     240                 :          0 :                 } else if (ip_ver == RTE_GTP_TYPE_IPV6) {
     241                 :            :                         ipv6_hdr = (struct rte_ipv6_hdr *)((char *)gtp_hdr +
     242                 :            :                                    gtp_len);
     243                 :          0 :                         info->ethertype = _htons(RTE_ETHER_TYPE_IPV6);
     244                 :            :                         parse_ipv6(ipv6_hdr, info);
     245                 :            :                 }
     246                 :            :         } else {
     247                 :          0 :                 info->ethertype = 0;
     248                 :          0 :                 info->l4_len = 0;
     249                 :          0 :                 info->l3_len = 0;
     250                 :          0 :                 info->l4_proto = 0;
     251                 :            :         }
     252                 :            : 
     253                 :          0 :         info->l2_len += gtp_len + sizeof(*udp_hdr);
     254                 :            : }
     255                 :            : 
     256                 :            : /* Parse a vxlan header */
     257                 :            : static void
     258                 :          0 : parse_vxlan(struct rte_udp_hdr *udp_hdr,
     259                 :            :             struct testpmd_offload_info *info)
     260                 :            : {
     261                 :            :         struct rte_ether_hdr *eth_hdr;
     262                 :            : 
     263                 :            :         /* check udp destination port, RTE_VXLAN_DEFAULT_PORT (4789) is the
     264                 :            :          * default vxlan port (rfc7348) or that the rx offload flag is set
     265                 :            :          * (i40e only currently)
     266                 :            :          */
     267                 :          0 :         if (udp_hdr->dst_port != _htons(RTE_VXLAN_DEFAULT_PORT))
     268                 :            :                 return;
     269                 :            : 
     270                 :            :         update_tunnel_outer(info);
     271                 :            : 
     272                 :          0 :         eth_hdr = (struct rte_ether_hdr *)((char *)udp_hdr +
     273                 :            :                 sizeof(struct rte_udp_hdr) +
     274                 :            :                 sizeof(struct rte_vxlan_hdr));
     275                 :            : 
     276                 :          0 :         parse_ethernet(eth_hdr, info);
     277                 :          0 :         info->l2_len += RTE_ETHER_VXLAN_HLEN; /* add udp + vxlan */
     278                 :            : }
     279                 :            : 
     280                 :            : /* Parse a vxlan-gpe header */
     281                 :            : static void
     282                 :          0 : parse_vxlan_gpe(struct rte_udp_hdr *udp_hdr,
     283                 :            :             struct testpmd_offload_info *info)
     284                 :            : {
     285                 :            :         struct rte_ether_hdr *eth_hdr;
     286                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     287                 :            :         struct rte_ipv6_hdr *ipv6_hdr;
     288                 :            :         struct rte_vxlan_gpe_hdr *vxlan_gpe_hdr;
     289                 :            :         uint8_t vxlan_gpe_len = sizeof(*vxlan_gpe_hdr);
     290                 :            : 
     291                 :            :         /* Check udp destination port. */
     292                 :          0 :         if (udp_hdr->dst_port != _htons(vxlan_gpe_udp_port))
     293                 :            :                 return;
     294                 :            : 
     295                 :            :         vxlan_gpe_hdr = (struct rte_vxlan_gpe_hdr *)((char *)udp_hdr +
     296                 :            :                                 sizeof(struct rte_udp_hdr));
     297                 :            : 
     298                 :          0 :         if (!vxlan_gpe_hdr->proto || vxlan_gpe_hdr->proto ==
     299                 :            :             RTE_VXLAN_GPE_TYPE_IPV4) {
     300                 :            :                 update_tunnel_outer(info);
     301                 :            : 
     302                 :          0 :                 ipv4_hdr = (struct rte_ipv4_hdr *)((char *)vxlan_gpe_hdr +
     303                 :            :                            vxlan_gpe_len);
     304                 :            : 
     305                 :            :                 parse_ipv4(ipv4_hdr, info);
     306                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV4);
     307                 :          0 :                 info->l2_len = 0;
     308                 :            : 
     309                 :          0 :         } else if (vxlan_gpe_hdr->proto == RTE_VXLAN_GPE_TYPE_IPV6) {
     310                 :            :                 update_tunnel_outer(info);
     311                 :            : 
     312                 :            :                 ipv6_hdr = (struct rte_ipv6_hdr *)((char *)vxlan_gpe_hdr +
     313                 :            :                            vxlan_gpe_len);
     314                 :            : 
     315                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV6);
     316                 :            :                 parse_ipv6(ipv6_hdr, info);
     317                 :          0 :                 info->l2_len = 0;
     318                 :            : 
     319                 :          0 :         } else if (vxlan_gpe_hdr->proto == RTE_VXLAN_GPE_TYPE_ETH) {
     320                 :            :                 update_tunnel_outer(info);
     321                 :            : 
     322                 :          0 :                 eth_hdr = (struct rte_ether_hdr *)((char *)vxlan_gpe_hdr +
     323                 :            :                           vxlan_gpe_len);
     324                 :            : 
     325                 :          0 :                 parse_ethernet(eth_hdr, info);
     326                 :            :         } else
     327                 :            :                 return;
     328                 :            : 
     329                 :          0 :         info->l2_len += RTE_ETHER_VXLAN_GPE_HLEN;
     330                 :            : }
     331                 :            : 
     332                 :            : /* Parse a geneve header */
     333                 :            : static void
     334                 :          0 : parse_geneve(struct rte_udp_hdr *udp_hdr,
     335                 :            :             struct testpmd_offload_info *info)
     336                 :            : {
     337                 :            :         struct rte_ether_hdr *eth_hdr;
     338                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     339                 :            :         struct rte_ipv6_hdr *ipv6_hdr;
     340                 :            :         struct rte_geneve_hdr *geneve_hdr;
     341                 :            :         uint16_t geneve_len;
     342                 :            : 
     343                 :            :         /* Check udp destination port. */
     344                 :          0 :         if (udp_hdr->dst_port != _htons(geneve_udp_port))
     345                 :            :                 return;
     346                 :            : 
     347                 :          0 :         geneve_hdr = (struct rte_geneve_hdr *)((char *)udp_hdr +
     348                 :            :                                 sizeof(struct rte_udp_hdr));
     349                 :          0 :         geneve_len = sizeof(struct rte_geneve_hdr) + geneve_hdr->opt_len * 4;
     350                 :          0 :         if (!geneve_hdr->proto || geneve_hdr->proto ==
     351                 :            :             _htons(RTE_ETHER_TYPE_IPV4)) {
     352                 :            :                 update_tunnel_outer(info);
     353                 :          0 :                 ipv4_hdr = (struct rte_ipv4_hdr *)((char *)geneve_hdr +
     354                 :            :                            geneve_len);
     355                 :            :                 parse_ipv4(ipv4_hdr, info);
     356                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV4);
     357                 :          0 :                 info->l2_len = 0;
     358                 :          0 :         } else if (geneve_hdr->proto == _htons(RTE_ETHER_TYPE_IPV6)) {
     359                 :            :                 update_tunnel_outer(info);
     360                 :          0 :                 ipv6_hdr = (struct rte_ipv6_hdr *)((char *)geneve_hdr +
     361                 :            :                            geneve_len);
     362                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV6);
     363                 :            :                 parse_ipv6(ipv6_hdr, info);
     364                 :          0 :                 info->l2_len = 0;
     365                 :            : 
     366                 :          0 :         } else if (geneve_hdr->proto == _htons(RTE_GENEVE_TYPE_ETH)) {
     367                 :            :                 update_tunnel_outer(info);
     368                 :          0 :                 eth_hdr = (struct rte_ether_hdr *)((char *)geneve_hdr +
     369                 :            :                           geneve_len);
     370                 :          0 :                 parse_ethernet(eth_hdr, info);
     371                 :            :         } else
     372                 :            :                 return;
     373                 :            : 
     374                 :          0 :         info->l2_len +=
     375                 :            :                 (sizeof(struct rte_udp_hdr) + sizeof(struct rte_geneve_hdr) +
     376                 :            :                 ((struct rte_geneve_hdr *)geneve_hdr)->opt_len * 4);
     377                 :            : }
     378                 :            : 
     379                 :            : /* Parse a gre header */
     380                 :            : static void
     381                 :          0 : parse_gre(struct simple_gre_hdr *gre_hdr, struct testpmd_offload_info *info)
     382                 :            : {
     383                 :            :         struct rte_ether_hdr *eth_hdr;
     384                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     385                 :            :         struct rte_ipv6_hdr *ipv6_hdr;
     386                 :            :         uint8_t gre_len = 0;
     387                 :            : 
     388                 :            :         gre_len += sizeof(struct simple_gre_hdr);
     389                 :            : 
     390                 :          0 :         if (gre_hdr->flags & _htons(GRE_KEY_PRESENT))
     391                 :            :                 gre_len += GRE_EXT_LEN;
     392                 :          0 :         if (gre_hdr->flags & _htons(GRE_SEQUENCE_PRESENT))
     393                 :          0 :                 gre_len += GRE_EXT_LEN;
     394                 :          0 :         if (gre_hdr->flags & _htons(GRE_CHECKSUM_PRESENT))
     395                 :          0 :                 gre_len += GRE_EXT_LEN;
     396                 :            : 
     397                 :          0 :         if (gre_hdr->proto == _htons(RTE_ETHER_TYPE_IPV4)) {
     398                 :            :                 update_tunnel_outer(info);
     399                 :            : 
     400                 :          0 :                 ipv4_hdr = (struct rte_ipv4_hdr *)((char *)gre_hdr + gre_len);
     401                 :            : 
     402                 :            :                 parse_ipv4(ipv4_hdr, info);
     403                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV4);
     404                 :          0 :                 info->l2_len = 0;
     405                 :            : 
     406                 :          0 :         } else if (gre_hdr->proto == _htons(RTE_ETHER_TYPE_IPV6)) {
     407                 :            :                 update_tunnel_outer(info);
     408                 :            : 
     409                 :          0 :                 ipv6_hdr = (struct rte_ipv6_hdr *)((char *)gre_hdr + gre_len);
     410                 :            : 
     411                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV6);
     412                 :            :                 parse_ipv6(ipv6_hdr, info);
     413                 :          0 :                 info->l2_len = 0;
     414                 :            : 
     415                 :          0 :         } else if (gre_hdr->proto == _htons(RTE_ETHER_TYPE_TEB)) {
     416                 :            :                 update_tunnel_outer(info);
     417                 :            : 
     418                 :          0 :                 eth_hdr = (struct rte_ether_hdr *)((char *)gre_hdr + gre_len);
     419                 :            : 
     420                 :          0 :                 parse_ethernet(eth_hdr, info);
     421                 :            :         } else
     422                 :            :                 return;
     423                 :            : 
     424                 :          0 :         info->l2_len += gre_len;
     425                 :            : }
     426                 :            : 
     427                 :            : 
     428                 :            : /* Parse an encapsulated ip or ipv6 header */
     429                 :            : static void
     430                 :          0 : parse_encap_ip(void *encap_ip, struct testpmd_offload_info *info)
     431                 :            : {
     432                 :            :         struct rte_ipv4_hdr *ipv4_hdr = encap_ip;
     433                 :            :         struct rte_ipv6_hdr *ipv6_hdr = encap_ip;
     434                 :            :         uint8_t ip_version;
     435                 :            : 
     436                 :          0 :         ip_version = (ipv4_hdr->version_ihl & 0xf0) >> 4;
     437                 :            : 
     438                 :          0 :         if (ip_version != 4 && ip_version != 6)
     439                 :            :                 return;
     440                 :            : 
     441                 :          0 :         info->is_tunnel = 1;
     442                 :          0 :         info->outer_ethertype = info->ethertype;
     443                 :          0 :         info->outer_l2_len = info->l2_len;
     444                 :          0 :         info->outer_l3_len = info->l3_len;
     445                 :            : 
     446                 :          0 :         if (ip_version == 4) {
     447                 :            :                 parse_ipv4(ipv4_hdr, info);
     448                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV4);
     449                 :            :         } else {
     450                 :            :                 parse_ipv6(ipv6_hdr, info);
     451                 :          0 :                 info->ethertype = _htons(RTE_ETHER_TYPE_IPV6);
     452                 :            :         }
     453                 :          0 :         info->l2_len = 0;
     454                 :            : }
     455                 :            : 
     456                 :            : /* if possible, calculate the checksum of a packet in hw or sw,
     457                 :            :  * depending on the testpmd command line configuration */
     458                 :            : static uint64_t
     459                 :          0 : process_inner_cksums(void *l3_hdr, const struct testpmd_offload_info *info,
     460                 :            :         uint64_t tx_offloads, struct rte_mbuf *m)
     461                 :            : {
     462                 :            :         struct rte_ipv4_hdr *ipv4_hdr = l3_hdr;
     463                 :            :         struct rte_udp_hdr *udp_hdr;
     464                 :            :         struct rte_tcp_hdr *tcp_hdr;
     465                 :            :         struct rte_sctp_hdr *sctp_hdr;
     466                 :            :         uint64_t ol_flags = 0;
     467                 :            :         uint32_t max_pkt_len, tso_segsz = 0;
     468                 :            :         uint16_t l4_off;
     469                 :            :         uint64_t all_tunnel_tso = RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
     470                 :            :                                 RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO |
     471                 :            :                                 RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO |
     472                 :            :                                 RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO |
     473                 :            :                                 RTE_ETH_TX_OFFLOAD_IP_TNL_TSO |
     474                 :            :                                 RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO;
     475                 :            : 
     476                 :            :         /* ensure packet is large enough to require tso */
     477                 :          0 :         if (!info->is_tunnel) {
     478                 :          0 :                 max_pkt_len = info->l2_len + info->l3_len + info->l4_len +
     479                 :          0 :                         info->tso_segsz;
     480                 :          0 :                 if (info->tso_segsz != 0 && info->pkt_len > max_pkt_len)
     481                 :          0 :                         tso_segsz = info->tso_segsz;
     482                 :            :         } else {
     483                 :          0 :                 max_pkt_len = info->outer_l2_len + info->outer_l3_len +
     484                 :          0 :                         info->l2_len + info->l3_len + info->l4_len +
     485                 :          0 :                         info->tunnel_tso_segsz;
     486                 :          0 :                 if (info->tunnel_tso_segsz != 0 && info->pkt_len > max_pkt_len)
     487                 :          0 :                         tso_segsz = info->tunnel_tso_segsz;
     488                 :            :         }
     489                 :            : 
     490                 :          0 :         if (info->ethertype == _htons(RTE_ETHER_TYPE_IPV4)) {
     491                 :            :                 ipv4_hdr = l3_hdr;
     492                 :            : 
     493                 :            :                 ol_flags |= RTE_MBUF_F_TX_IPV4;
     494                 :          0 :                 if (info->l4_proto == IPPROTO_TCP && tso_segsz) {
     495                 :            :                         ol_flags |= RTE_MBUF_F_TX_IP_CKSUM;
     496                 :            :                 } else {
     497                 :          0 :                         if (tx_offloads & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {
     498                 :            :                                 ol_flags |= RTE_MBUF_F_TX_IP_CKSUM;
     499                 :            :                         } else {
     500                 :          0 :                                 ipv4_hdr->hdr_checksum = 0;
     501                 :          0 :                                 ipv4_hdr->hdr_checksum =
     502                 :            :                                         rte_ipv4_cksum(ipv4_hdr);
     503                 :            :                         }
     504                 :            :                 }
     505                 :          0 :         } else if (info->ethertype == _htons(RTE_ETHER_TYPE_IPV6))
     506                 :            :                 ol_flags |= RTE_MBUF_F_TX_IPV6;
     507                 :            :         else
     508                 :            :                 return 0; /* packet type not supported, nothing to do */
     509                 :            : 
     510                 :          0 :         if (info->l4_proto == IPPROTO_UDP) {
     511                 :          0 :                 udp_hdr = (struct rte_udp_hdr *)((char *)l3_hdr + info->l3_len);
     512                 :            :                 /* do not recalculate udp cksum if it was 0 */
     513                 :          0 :                 if (udp_hdr->dgram_cksum != 0) {
     514                 :          0 :                         if (tso_segsz && (tx_offloads & RTE_ETH_TX_OFFLOAD_UDP_TSO))
     515                 :          0 :                                 ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
     516                 :          0 :                         else if (tx_offloads & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {
     517                 :          0 :                                 ol_flags |= RTE_MBUF_F_TX_UDP_CKSUM;
     518                 :            :                         } else {
     519                 :          0 :                                 if (info->is_tunnel)
     520                 :          0 :                                         l4_off = info->outer_l2_len +
     521                 :          0 :                                                  info->outer_l3_len +
     522                 :          0 :                                                  info->l2_len + info->l3_len;
     523                 :            :                                 else
     524                 :          0 :                                         l4_off = info->l2_len +      info->l3_len;
     525                 :          0 :                                 udp_hdr->dgram_cksum = 0;
     526                 :          0 :                                 udp_hdr->dgram_cksum =
     527                 :          0 :                                         get_udptcp_checksum(m, l3_hdr, l4_off,
     528                 :            :                                                 info->ethertype);
     529                 :            :                         }
     530                 :            :                 }
     531                 :            : #ifdef RTE_LIB_GSO
     532                 :          0 :                 if (info->gso_enable)
     533                 :          0 :                         ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
     534                 :            : #endif
     535                 :          0 :         } else if (info->l4_proto == IPPROTO_TCP) {
     536                 :          0 :                 tcp_hdr = (struct rte_tcp_hdr *)((char *)l3_hdr + info->l3_len);
     537                 :          0 :                 if (tso_segsz &&
     538                 :          0 :                     (tx_offloads & (RTE_ETH_TX_OFFLOAD_TCP_TSO | all_tunnel_tso)))
     539                 :          0 :                         ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
     540                 :          0 :                 else if (tx_offloads & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {
     541                 :          0 :                         ol_flags |= RTE_MBUF_F_TX_TCP_CKSUM;
     542                 :            :                 } else {
     543                 :          0 :                         if (info->is_tunnel)
     544                 :          0 :                                 l4_off = info->outer_l2_len + info->outer_l3_len +
     545                 :          0 :                                          info->l2_len + info->l3_len;
     546                 :            :                         else
     547                 :          0 :                                 l4_off = info->l2_len + info->l3_len;
     548                 :          0 :                         tcp_hdr->cksum = 0;
     549                 :          0 :                         tcp_hdr->cksum =
     550                 :          0 :                                 get_udptcp_checksum(m, l3_hdr, l4_off,
     551                 :            :                                         info->ethertype);
     552                 :            :                 }
     553                 :            : #ifdef RTE_LIB_GSO
     554                 :          0 :                 if (info->gso_enable)
     555                 :          0 :                         ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
     556                 :            : #endif
     557                 :          0 :         } else if (info->l4_proto == IPPROTO_SCTP) {
     558                 :          0 :                 sctp_hdr = (struct rte_sctp_hdr *)
     559                 :          0 :                         ((char *)l3_hdr + info->l3_len);
     560                 :            :                 /* sctp payload must be a multiple of 4 to be
     561                 :            :                  * offloaded */
     562                 :          0 :                 if ((tx_offloads & RTE_ETH_TX_OFFLOAD_SCTP_CKSUM) &&
     563                 :          0 :                         ((ipv4_hdr->total_length & 0x3) == 0)) {
     564                 :          0 :                         ol_flags |= RTE_MBUF_F_TX_SCTP_CKSUM;
     565                 :            :                 } else {
     566                 :          0 :                         sctp_hdr->cksum = 0;
     567                 :            :                         /* XXX implement CRC32c, example available in
     568                 :            :                          * RFC3309 */
     569                 :            :                 }
     570                 :            :         }
     571                 :            : 
     572                 :            :         return ol_flags;
     573                 :            : }
     574                 :            : 
     575                 :            : /* Calculate the checksum of outer header */
     576                 :            : static uint64_t
     577                 :          0 : process_outer_cksums(void *outer_l3_hdr, struct testpmd_offload_info *info,
     578                 :            :         uint64_t tx_offloads, int tso_enabled, struct rte_mbuf *m)
     579                 :            : {
     580                 :            :         struct rte_udp_hdr *udp_hdr;
     581                 :            :         uint64_t ol_flags = 0;
     582                 :            : 
     583                 :          0 :         if (info->outer_ethertype == _htons(RTE_ETHER_TYPE_IPV4)) {
     584                 :            :                 ol_flags |= RTE_MBUF_F_TX_OUTER_IPV4;
     585                 :            : 
     586                 :          0 :                 if (tx_offloads & RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) {
     587                 :            :                         ol_flags |= RTE_MBUF_F_TX_OUTER_IP_CKSUM;
     588                 :            :                 } else {
     589                 :            :                         struct rte_ipv4_hdr *ipv4_hdr = outer_l3_hdr;
     590                 :            : 
     591                 :          0 :                         ipv4_hdr->hdr_checksum = 0;
     592                 :          0 :                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
     593                 :            :                 }
     594                 :            :         } else {
     595                 :            :                 ol_flags |= RTE_MBUF_F_TX_OUTER_IPV6;
     596                 :            :         }
     597                 :            : 
     598                 :          0 :         if (info->outer_l4_proto != IPPROTO_UDP)
     599                 :            :                 return ol_flags;
     600                 :            : 
     601                 :          0 :         udp_hdr = (struct rte_udp_hdr *)
     602                 :          0 :                 ((char *)outer_l3_hdr + info->outer_l3_len);
     603                 :            : 
     604                 :          0 :         if (tso_enabled && info->l4_proto == IPPROTO_TCP)
     605                 :          0 :                 ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
     606                 :          0 :         else if (tso_enabled && info->l4_proto == IPPROTO_UDP)
     607                 :          0 :                 ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
     608                 :            : 
     609                 :            :         /* Skip SW outer UDP checksum generation if HW supports it */
     610                 :          0 :         if (tx_offloads & RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) {
     611                 :          0 :                 ol_flags |= RTE_MBUF_F_TX_OUTER_UDP_CKSUM;
     612                 :          0 :                 return ol_flags;
     613                 :            :         }
     614                 :            : 
     615                 :            :         /* Outer UDP checksum is done in software.
     616                 :            :          *
     617                 :            :          * If a packet will be TSOed into small packets by NIC, we cannot
     618                 :            :          * set/calculate a non-zero checksum, because it will be a wrong
     619                 :            :          * value after the packet be split into several small packets.
     620                 :            :          */
     621                 :          0 :         if (!tso_enabled && udp_hdr->dgram_cksum != 0) {
     622                 :          0 :                 udp_hdr->dgram_cksum = 0;
     623                 :          0 :                 udp_hdr->dgram_cksum = get_udptcp_checksum(m, outer_l3_hdr,
     624                 :          0 :                                         info->outer_l2_len + info->outer_l3_len,
     625                 :            :                                         info->outer_ethertype);
     626                 :            :         }
     627                 :            : 
     628                 :            :         return ol_flags;
     629                 :            : }
     630                 :            : 
     631                 :            : /*
     632                 :            :  * Helper function.
     633                 :            :  * Performs actual copying.
     634                 :            :  * Returns number of segments in the destination mbuf on success,
     635                 :            :  * or negative error code on failure.
     636                 :            :  */
     637                 :            : static int
     638                 :          0 : mbuf_copy_split(const struct rte_mbuf *ms, struct rte_mbuf *md[],
     639                 :            :         uint16_t seglen[], uint8_t nb_seg)
     640                 :            : {
     641                 :            :         uint32_t dlen, slen, tlen;
     642                 :            :         uint32_t i, len;
     643                 :            :         const struct rte_mbuf *m;
     644                 :            :         const uint8_t *src;
     645                 :            :         uint8_t *dst;
     646                 :            : 
     647                 :            :         dlen = 0;
     648                 :            :         slen = 0;
     649                 :            :         tlen = 0;
     650                 :            : 
     651                 :            :         dst = NULL;
     652                 :            :         src = NULL;
     653                 :            : 
     654                 :            :         m = ms;
     655                 :            :         i = 0;
     656                 :          0 :         while (ms != NULL && i != nb_seg) {
     657                 :            : 
     658                 :          0 :                 if (slen == 0) {
     659                 :          0 :                         slen = rte_pktmbuf_data_len(ms);
     660                 :          0 :                         src = rte_pktmbuf_mtod(ms, const uint8_t *);
     661                 :            :                 }
     662                 :            : 
     663                 :          0 :                 if (dlen == 0) {
     664                 :          0 :                         dlen = RTE_MIN(seglen[i], slen);
     665                 :          0 :                         md[i]->data_len = dlen;
     666                 :          0 :                         md[i]->next = (i + 1 == nb_seg) ? NULL : md[i + 1];
     667                 :          0 :                         dst = rte_pktmbuf_mtod(md[i], uint8_t *);
     668                 :            :                 }
     669                 :            : 
     670                 :          0 :                 len = RTE_MIN(slen, dlen);
     671                 :          0 :                 memcpy(dst, src, len);
     672                 :          0 :                 tlen += len;
     673                 :          0 :                 slen -= len;
     674                 :          0 :                 dlen -= len;
     675                 :          0 :                 src += len;
     676                 :          0 :                 dst += len;
     677                 :            : 
     678                 :          0 :                 if (slen == 0)
     679                 :          0 :                         ms = ms->next;
     680                 :          0 :                 if (dlen == 0)
     681                 :          0 :                         i++;
     682                 :            :         }
     683                 :            : 
     684                 :          0 :         if (ms != NULL)
     685                 :            :                 return -ENOBUFS;
     686                 :          0 :         else if (tlen != m->pkt_len)
     687                 :            :                 return -EINVAL;
     688                 :            : 
     689                 :          0 :         md[0]->nb_segs = nb_seg;
     690                 :          0 :         md[0]->pkt_len = tlen;
     691                 :          0 :         md[0]->vlan_tci = m->vlan_tci;
     692                 :          0 :         md[0]->vlan_tci_outer = m->vlan_tci_outer;
     693                 :          0 :         md[0]->ol_flags = m->ol_flags;
     694                 :          0 :         md[0]->tx_offload = m->tx_offload;
     695                 :            : 
     696                 :          0 :         return nb_seg;
     697                 :            : }
     698                 :            : 
     699                 :            : /*
     700                 :            :  * Allocate a new mbuf with up to tx_pkt_nb_segs segments.
     701                 :            :  * Copy packet contents and offload information into the new segmented mbuf.
     702                 :            :  */
     703                 :            : static struct rte_mbuf *
     704                 :          0 : pkt_copy_split(const struct rte_mbuf *pkt)
     705                 :            : {
     706                 :            :         int32_t n, rc;
     707                 :            :         uint32_t i, len, nb_seg;
     708                 :            :         struct rte_mempool *mp;
     709                 :            :         uint16_t seglen[RTE_MAX_SEGS_PER_PKT];
     710                 :            :         struct rte_mbuf *p, *md[RTE_MAX_SEGS_PER_PKT];
     711                 :            : 
     712                 :          0 :         mp = current_fwd_lcore()->mbp;
     713                 :            : 
     714                 :          0 :         if (tx_pkt_split == TX_PKT_SPLIT_RND)
     715                 :          0 :                 nb_seg = rte_rand() % tx_pkt_nb_segs + 1;
     716                 :            :         else
     717                 :          0 :                 nb_seg = tx_pkt_nb_segs;
     718                 :            : 
     719                 :          0 :         memcpy(seglen, tx_pkt_seg_lengths, nb_seg * sizeof(seglen[0]));
     720                 :            : 
     721                 :            :         /* calculate number of segments to use and their length. */
     722                 :            :         len = 0;
     723                 :          0 :         for (i = 0; i != nb_seg && len < pkt->pkt_len; i++) {
     724                 :          0 :                 len += seglen[i];
     725                 :          0 :                 md[i] = NULL;
     726                 :            :         }
     727                 :            : 
     728                 :          0 :         n = pkt->pkt_len - len;
     729                 :            : 
     730                 :            :         /* update size of the last segment to fit rest of the packet */
     731                 :          0 :         if (n >= 0) {
     732                 :          0 :                 seglen[i - 1] += n;
     733                 :            :                 len += n;
     734                 :            :         }
     735                 :            : 
     736                 :            :         nb_seg = i;
     737                 :          0 :         while (i != 0) {
     738                 :          0 :                 p = rte_pktmbuf_alloc(mp);
     739                 :          0 :                 if (p == NULL) {
     740                 :          0 :                         TESTPMD_LOG(ERR,
     741                 :            :                                 "failed to allocate %u-th of %u mbuf "
     742                 :            :                                 "from mempool: %s\n",
     743                 :            :                                 nb_seg - i, nb_seg, mp->name);
     744                 :          0 :                         break;
     745                 :            :                 }
     746                 :            : 
     747                 :          0 :                 md[--i] = p;
     748                 :          0 :                 if (rte_pktmbuf_tailroom(md[i]) < seglen[i]) {
     749                 :          0 :                         TESTPMD_LOG(ERR, "mempool %s, %u-th segment: "
     750                 :            :                                 "expected seglen: %u, "
     751                 :            :                                 "actual mbuf tailroom: %u\n",
     752                 :            :                                 mp->name, i, seglen[i],
     753                 :            :                                 rte_pktmbuf_tailroom(md[i]));
     754                 :          0 :                         break;
     755                 :            :                 }
     756                 :            :         }
     757                 :            : 
     758                 :            :         /* all mbufs successfully allocated, do copy */
     759                 :          0 :         if (i == 0) {
     760                 :          0 :                 rc = mbuf_copy_split(pkt, md, seglen, nb_seg);
     761                 :          0 :                 if (rc < 0)
     762                 :          0 :                         TESTPMD_LOG(ERR,
     763                 :            :                                 "mbuf_copy_split for %p(len=%u, nb_seg=%u) "
     764                 :            :                                 "into %u segments failed with error code: %d\n",
     765                 :            :                                 pkt, pkt->pkt_len, pkt->nb_segs, nb_seg, rc);
     766                 :            : 
     767                 :            :                 /* figure out how many mbufs to free. */
     768                 :          0 :                 i = RTE_MAX(rc, 0);
     769                 :            :         }
     770                 :            : 
     771                 :            :         /* free unused mbufs */
     772                 :          0 :         for (; i != nb_seg; i++) {
     773                 :          0 :                 rte_pktmbuf_free_seg(md[i]);
     774                 :          0 :                 md[i] = NULL;
     775                 :            :         }
     776                 :            : 
     777                 :          0 :         return md[0];
     778                 :            : }
     779                 :            : 
     780                 :            : #if defined(RTE_LIB_GRO) || defined(RTE_LIB_GSO)
     781                 :            : /*
     782                 :            :  * Re-calculate IP checksum for merged/fragmented packets.
     783                 :            :  */
     784                 :            : static void
     785                 :          0 : pkts_ip_csum_recalc(struct rte_mbuf **pkts_burst, const uint16_t nb_pkts, uint64_t tx_offloads)
     786                 :            : {
     787                 :            :         int i;
     788                 :            :         struct rte_ipv4_hdr *ipv4_hdr;
     789                 :          0 :         for (i = 0; i < nb_pkts; i++) {
     790                 :          0 :                 if ((pkts_burst[i]->ol_flags & RTE_MBUF_F_TX_IPV4) &&
     791                 :          0 :                         (tx_offloads & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) == 0) {
     792                 :          0 :                         ipv4_hdr = rte_pktmbuf_mtod_offset(pkts_burst[i],
     793                 :            :                                                 struct rte_ipv4_hdr *,
     794                 :            :                                                 pkts_burst[i]->l2_len);
     795                 :          0 :                         ipv4_hdr->hdr_checksum = 0;
     796                 :          0 :                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
     797                 :            :                 }
     798                 :            :         }
     799                 :          0 : }
     800                 :            : #endif
     801                 :            : 
     802                 :            : /*
     803                 :            :  * Receive a burst of packets, and for each packet:
     804                 :            :  *  - parse packet, and try to recognize a supported packet type (1)
     805                 :            :  *  - if it's not a supported packet type, don't touch the packet, else:
     806                 :            :  *  - reprocess the checksum of all supported layers. This is done in SW
     807                 :            :  *    or HW, depending on testpmd command line configuration
     808                 :            :  *  - if TSO is enabled in testpmd command line, also flag the mbuf for TCP
     809                 :            :  *    segmentation offload (this implies HW TCP checksum)
     810                 :            :  * Then transmit packets on the output port.
     811                 :            :  *
     812                 :            :  * (1) Supported packets are:
     813                 :            :  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
     814                 :            :  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
     815                 :            :  *           UDP|TCP|SCTP
     816                 :            :  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VXLAN-GPE / Ether / IP|IP6 /
     817                 :            :  *           UDP|TCP|SCTP
     818                 :            :  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VXLAN-GPE / IP|IP6 /
     819                 :            :  *           UDP|TCP|SCTP
     820                 :            :  *   Ether / (vlan) / outer IP / outer UDP / GTP / IP|IP6 / UDP|TCP|SCTP
     821                 :            :  *   Ether / (vlan) / outer IP|IP6 / GRE / Ether / IP|IP6 / UDP|TCP|SCTP
     822                 :            :  *   Ether / (vlan) / outer IP|IP6 / GRE / IP|IP6 / UDP|TCP|SCTP
     823                 :            :  *   Ether / (vlan) / outer IP|IP6 / IP|IP6 / UDP|TCP|SCTP
     824                 :            :  *
     825                 :            :  * The testpmd command line for this forward engine sets the flags
     826                 :            :  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
     827                 :            :  * whether a checksum must be calculated in software or in hardware. The
     828                 :            :  * IP, UDP, TCP and SCTP flags always concern the inner layer. The
     829                 :            :  * OUTER_IP is only useful for tunnel packets.
     830                 :            :  */
     831                 :            : static bool
     832                 :          0 : pkt_burst_checksum_forward(struct fwd_stream *fs)
     833                 :            : {
     834                 :            :         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
     835                 :            : #ifdef RTE_LIB_GSO
     836                 :            :         struct rte_mbuf *gso_segments[GSO_MAX_PKT_BURST];
     837                 :            :         struct rte_gso_ctx *gso_ctx;
     838                 :            : #endif
     839                 :            :         struct rte_mbuf **tx_pkts_burst;
     840                 :            :         struct rte_port *txp;
     841                 :            :         struct rte_mbuf *m, *p;
     842                 :            :         struct rte_ether_hdr *eth_hdr;
     843                 :            :         void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
     844                 :            : #ifdef RTE_LIB_GRO
     845                 :            :         void **gro_ctx;
     846                 :            :         uint16_t gro_pkts_num;
     847                 :            :         uint8_t gro_enable;
     848                 :            : #endif
     849                 :            :         uint16_t nb_rx;
     850                 :            :         uint16_t nb_prep;
     851                 :            :         uint16_t i;
     852                 :            :         uint64_t rx_ol_flags, tx_ol_flags;
     853                 :            :         uint64_t tx_offloads;
     854                 :            :         uint32_t rx_bad_ip_csum;
     855                 :            :         uint32_t rx_bad_l4_csum;
     856                 :            :         uint32_t rx_bad_outer_l4_csum;
     857                 :            :         uint32_t rx_bad_outer_ip_csum;
     858                 :            :         struct testpmd_offload_info info;
     859                 :            : 
     860                 :            :         /* receive a burst of packet */
     861                 :          0 :         nb_rx = common_fwd_stream_receive(fs, pkts_burst, nb_pkt_per_burst);
     862                 :          0 :         if (unlikely(nb_rx == 0)) {
     863                 :            : #ifndef RTE_LIB_GRO
     864                 :            :                 return false;
     865                 :            : #else
     866                 :          0 :                 gro_enable = gro_ports[fs->rx_port].enable;
     867                 :            :                 /*
     868                 :            :                  * Check if packets need to be flushed in the GRO context
     869                 :            :                  * due to a timeout.
     870                 :            :                  *
     871                 :            :                  * Continue only in GRO heavyweight mode and if there are
     872                 :            :                  * packets in the GRO context.
     873                 :            :                  */
     874                 :          0 :                 if (!gro_enable || (gro_flush_cycles == GRO_DEFAULT_FLUSH_CYCLES) ||
     875                 :          0 :                         (rte_gro_get_pkt_count(current_fwd_lcore()->gro_ctx) == 0))
     876                 :          0 :                         return false;
     877                 :            : #endif
     878                 :            :         }
     879                 :            : 
     880                 :            :         rx_bad_ip_csum = 0;
     881                 :            :         rx_bad_l4_csum = 0;
     882                 :            :         rx_bad_outer_l4_csum = 0;
     883                 :            :         rx_bad_outer_ip_csum = 0;
     884                 :            : 
     885                 :          0 :         txp = &ports[fs->tx_port];
     886                 :          0 :         tx_offloads = txp->dev_conf.txmode.offloads;
     887                 :            :         memset(&info, 0, sizeof(info));
     888                 :          0 :         info.tso_segsz = txp->tso_segsz;
     889                 :          0 :         info.tunnel_tso_segsz = txp->tunnel_tso_segsz;
     890                 :            : #ifdef RTE_LIB_GSO
     891                 :          0 :         if (gso_ports[fs->tx_port].enable)
     892                 :          0 :                 info.gso_enable = 1;
     893                 :            : #endif
     894                 :            : 
     895                 :          0 :         for (i = 0; i < nb_rx; i++) {
     896                 :          0 :                 if (likely(i < nb_rx - 1))
     897                 :          0 :                         rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[i + 1],
     898                 :            :                                                        void *));
     899                 :            : 
     900                 :          0 :                 m = pkts_burst[i];
     901                 :          0 :                 info.is_tunnel = 0;
     902                 :          0 :                 info.pkt_len = rte_pktmbuf_pkt_len(m);
     903                 :          0 :                 tx_ol_flags = m->ol_flags &
     904                 :            :                               (RTE_MBUF_F_INDIRECT | RTE_MBUF_F_EXTERNAL);
     905                 :            :                 rx_ol_flags = m->ol_flags;
     906                 :            : 
     907                 :            :                 /* Update the L3/L4 checksum error packet statistics */
     908                 :          0 :                 if ((rx_ol_flags & RTE_MBUF_F_RX_IP_CKSUM_MASK) == RTE_MBUF_F_RX_IP_CKSUM_BAD)
     909                 :          0 :                         rx_bad_ip_csum += 1;
     910                 :          0 :                 if ((rx_ol_flags & RTE_MBUF_F_RX_L4_CKSUM_MASK) == RTE_MBUF_F_RX_L4_CKSUM_BAD)
     911                 :          0 :                         rx_bad_l4_csum += 1;
     912                 :          0 :                 if (rx_ol_flags & RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD)
     913                 :          0 :                         rx_bad_outer_l4_csum += 1;
     914                 :          0 :                 if (rx_ol_flags & RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD)
     915                 :          0 :                         rx_bad_outer_ip_csum += 1;
     916                 :            : 
     917                 :            :                 /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
     918                 :            :                  * and inner headers */
     919                 :            : 
     920                 :          0 :                 eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
     921                 :          0 :                 if (ports[fs->tx_port].fwd_mac_swap) {
     922                 :          0 :                         rte_ether_addr_copy(&peer_eth_addrs[fs->peer_addr],
     923                 :            :                                             &eth_hdr->dst_addr);
     924                 :            :                         rte_ether_addr_copy(&ports[fs->tx_port].eth_addr,
     925                 :            :                                             &eth_hdr->src_addr);
     926                 :            :                 }
     927                 :          0 :                 parse_ethernet(eth_hdr, &info);
     928                 :          0 :                 l3_hdr = (char *)eth_hdr + info.l2_len;
     929                 :            : 
     930                 :            :                 /* check if it's a supported tunnel */
     931                 :          0 :                 if (txp->parse_tunnel) {
     932                 :          0 :                         if (info.l4_proto == IPPROTO_UDP) {
     933                 :            :                                 struct rte_udp_hdr *udp_hdr;
     934                 :            : 
     935                 :          0 :                                 udp_hdr = (struct rte_udp_hdr *)
     936                 :          0 :                                         ((char *)l3_hdr + info.l3_len);
     937                 :          0 :                                 parse_gtp(udp_hdr, &info);
     938                 :          0 :                                 if (info.is_tunnel) {
     939                 :          0 :                                         tx_ol_flags |= RTE_MBUF_F_TX_TUNNEL_GTP;
     940                 :          0 :                                         goto tunnel_update;
     941                 :            :                                 }
     942                 :          0 :                                 parse_vxlan_gpe(udp_hdr, &info);
     943                 :          0 :                                 if (info.is_tunnel) {
     944                 :          0 :                                         tx_ol_flags |=
     945                 :            :                                                 RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE;
     946                 :          0 :                                         goto tunnel_update;
     947                 :            :                                 }
     948                 :          0 :                                 parse_vxlan(udp_hdr, &info);
     949                 :          0 :                                 if (info.is_tunnel) {
     950                 :          0 :                                         tx_ol_flags |=
     951                 :            :                                                 RTE_MBUF_F_TX_TUNNEL_VXLAN;
     952                 :          0 :                                         goto tunnel_update;
     953                 :            :                                 }
     954                 :          0 :                                 parse_geneve(udp_hdr, &info);
     955                 :          0 :                                 if (info.is_tunnel) {
     956                 :          0 :                                         tx_ol_flags |=
     957                 :            :                                                 RTE_MBUF_F_TX_TUNNEL_GENEVE;
     958                 :          0 :                                         goto tunnel_update;
     959                 :            :                                 }
     960                 :            :                                 /* Always keep last. */
     961                 :          0 :                                 if (unlikely(RTE_ETH_IS_TUNNEL_PKT(
     962                 :            :                                                         m->packet_type) != 0)) {
     963                 :          0 :                                         TESTPMD_LOG(DEBUG, "Unknown tunnel packet. UDP dst port: %hu",
     964                 :            :                                                 udp_hdr->dst_port);
     965                 :            :                                 }
     966                 :          0 :                         } else if (info.l4_proto == IPPROTO_GRE) {
     967                 :            :                                 struct simple_gre_hdr *gre_hdr;
     968                 :            : 
     969                 :          0 :                                 gre_hdr = (struct simple_gre_hdr *)
     970                 :          0 :                                         ((char *)l3_hdr + info.l3_len);
     971                 :          0 :                                 parse_gre(gre_hdr, &info);
     972                 :          0 :                                 if (info.is_tunnel)
     973                 :          0 :                                         tx_ol_flags |= RTE_MBUF_F_TX_TUNNEL_GRE;
     974                 :          0 :                         } else if (info.l4_proto == IPPROTO_IPIP) {
     975                 :            :                                 void *encap_ip_hdr;
     976                 :            : 
     977                 :          0 :                                 encap_ip_hdr = (char *)l3_hdr + info.l3_len;
     978                 :          0 :                                 parse_encap_ip(encap_ip_hdr, &info);
     979                 :          0 :                                 if (info.is_tunnel)
     980                 :          0 :                                         tx_ol_flags |= RTE_MBUF_F_TX_TUNNEL_IPIP;
     981                 :            :                         }
     982                 :            :                 }
     983                 :            : 
     984                 :          0 : tunnel_update:
     985                 :            :                 /* update l3_hdr and outer_l3_hdr if a tunnel was parsed */
     986                 :          0 :                 if (info.is_tunnel) {
     987                 :            :                         outer_l3_hdr = l3_hdr;
     988                 :          0 :                         l3_hdr = (char *)l3_hdr + info.outer_l3_len + info.l2_len;
     989                 :            :                 }
     990                 :            : 
     991                 :            :                 /* step 2: depending on user command line configuration,
     992                 :            :                  * recompute checksum either in software or flag the
     993                 :            :                  * mbuf to offload the calculation to the NIC. If TSO
     994                 :            :                  * is configured, prepare the mbuf for TCP segmentation. */
     995                 :            : 
     996                 :            :                 /* process checksums of inner headers first */
     997                 :          0 :                 tx_ol_flags |= process_inner_cksums(l3_hdr, &info,
     998                 :            :                         tx_offloads, m);
     999                 :            : 
    1000                 :            :                 /* Then process outer headers if any. Note that the software
    1001                 :            :                  * checksum will be wrong if one of the inner checksums is
    1002                 :            :                  * processed in hardware. */
    1003                 :          0 :                 if (info.is_tunnel == 1) {
    1004                 :          0 :                         tx_ol_flags |= process_outer_cksums(outer_l3_hdr, &info,
    1005                 :            :                                         tx_offloads,
    1006                 :          0 :                                         !!(tx_ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
    1007                 :            :                                                 RTE_MBUF_F_TX_UDP_SEG)),
    1008                 :            :                                         m);
    1009                 :            :                 }
    1010                 :            : 
    1011                 :            :                 /* step 3: fill the mbuf meta data (flags and header lengths) */
    1012                 :            : 
    1013                 :          0 :                 m->tx_offload = 0;
    1014                 :          0 :                 if (info.is_tunnel == 1) {
    1015                 :          0 :                         if (info.tunnel_tso_segsz ||
    1016                 :            :                             (tx_offloads &
    1017                 :          0 :                              RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) ||
    1018                 :            :                             (tx_offloads &
    1019                 :            :                              RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM)) {
    1020                 :          0 :                                 m->outer_l2_len = info.outer_l2_len;
    1021                 :          0 :                                 m->outer_l3_len = info.outer_l3_len;
    1022                 :          0 :                                 m->l2_len = info.l2_len;
    1023                 :          0 :                                 m->l3_len = info.l3_len;
    1024                 :          0 :                                 m->l4_len = info.l4_len;
    1025                 :          0 :                                 m->tso_segsz = info.tunnel_tso_segsz;
    1026                 :            :                         }
    1027                 :            :                         else {
    1028                 :            :                                 /* if there is a outer UDP cksum
    1029                 :            :                                    processed in sw and the inner in hw,
    1030                 :            :                                    the outer checksum will be wrong as
    1031                 :            :                                    the payload will be modified by the
    1032                 :            :                                    hardware */
    1033                 :          0 :                                 m->l2_len = info.outer_l2_len +
    1034                 :          0 :                                         info.outer_l3_len + info.l2_len;
    1035                 :          0 :                                 m->l3_len = info.l3_len;
    1036                 :          0 :                                 m->l4_len = info.l4_len;
    1037                 :            :                         }
    1038                 :            :                 } else {
    1039                 :            :                         /* this is only useful if an offload flag is
    1040                 :            :                          * set, but it does not hurt to fill it in any
    1041                 :            :                          * case */
    1042                 :          0 :                         m->l2_len = info.l2_len;
    1043                 :          0 :                         m->l3_len = info.l3_len;
    1044                 :          0 :                         m->l4_len = info.l4_len;
    1045                 :          0 :                         m->tso_segsz = info.tso_segsz;
    1046                 :            :                 }
    1047                 :          0 :                 m->ol_flags = tx_ol_flags;
    1048                 :            : 
    1049                 :            :                 /* Do split & copy for the packet. */
    1050                 :          0 :                 if (tx_pkt_split != TX_PKT_SPLIT_OFF) {
    1051                 :          0 :                         p = pkt_copy_split(m);
    1052                 :          0 :                         if (p != NULL) {
    1053                 :          0 :                                 rte_pktmbuf_free(m);
    1054                 :            :                                 m = p;
    1055                 :          0 :                                 pkts_burst[i] = m;
    1056                 :            :                         }
    1057                 :            :                 }
    1058                 :            : 
    1059                 :            :                 /* if verbose mode is enabled, dump debug info */
    1060                 :          0 :                 if (verbose_level > 0) {
    1061                 :            :                         char buf[256];
    1062                 :            : 
    1063                 :            :                         printf("-----------------\n");
    1064                 :          0 :                         printf("port=%u, mbuf=%p, pkt_len=%u, nb_segs=%u:\n",
    1065                 :          0 :                                 fs->rx_port, m, m->pkt_len, m->nb_segs);
    1066                 :            :                         /* dump rx parsed packet info */
    1067                 :          0 :                         rte_get_rx_ol_flag_list(rx_ol_flags, buf, sizeof(buf));
    1068                 :          0 :                         printf("rx: l2_len=%d ethertype=%x l3_len=%d "
    1069                 :            :                                 "l4_proto=%d l4_len=%d flags=%s\n",
    1070                 :          0 :                                 info.l2_len, rte_be_to_cpu_16(info.ethertype),
    1071                 :          0 :                                 info.l3_len, info.l4_proto, info.l4_len, buf);
    1072                 :          0 :                         if (rx_ol_flags & RTE_MBUF_F_RX_LRO)
    1073                 :          0 :                                 printf("rx: m->lro_segsz=%u\n", m->tso_segsz);
    1074                 :          0 :                         if (info.is_tunnel == 1)
    1075                 :          0 :                                 printf("rx: outer_l2_len=%d outer_ethertype=%x "
    1076                 :          0 :                                         "outer_l3_len=%d\n", info.outer_l2_len,
    1077                 :          0 :                                         rte_be_to_cpu_16(info.outer_ethertype),
    1078                 :          0 :                                         info.outer_l3_len);
    1079                 :            :                         /* dump tx packet info */
    1080                 :          0 :                         if ((tx_offloads & (RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
    1081                 :            :                                             RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
    1082                 :            :                                             RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
    1083                 :          0 :                                             RTE_ETH_TX_OFFLOAD_SCTP_CKSUM)) ||
    1084                 :          0 :                                 info.tso_segsz != 0)
    1085                 :          0 :                                 printf("tx: m->l2_len=%d m->l3_len=%d "
    1086                 :            :                                         "m->l4_len=%d\n",
    1087                 :          0 :                                         m->l2_len, m->l3_len, m->l4_len);
    1088                 :          0 :                         if (info.is_tunnel == 1) {
    1089                 :          0 :                                 if ((tx_offloads &
    1090                 :          0 :                                     RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) ||
    1091                 :            :                                     (tx_offloads &
    1092                 :          0 :                                     RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) ||
    1093                 :          0 :                                     (tx_ol_flags & RTE_MBUF_F_TX_OUTER_IPV6))
    1094                 :          0 :                                         printf("tx: m->outer_l2_len=%d "
    1095                 :            :                                                 "m->outer_l3_len=%d\n",
    1096                 :          0 :                                                 m->outer_l2_len,
    1097                 :          0 :                                                 m->outer_l3_len);
    1098                 :          0 :                                 if (info.tunnel_tso_segsz != 0 &&
    1099                 :          0 :                                                 (m->ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
    1100                 :            :                                                         RTE_MBUF_F_TX_UDP_SEG)))
    1101                 :          0 :                                         printf("tx: m->tso_segsz=%d\n",
    1102                 :          0 :                                                 m->tso_segsz);
    1103                 :          0 :                         } else if (info.tso_segsz != 0 &&
    1104                 :          0 :                                         (m->ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
    1105                 :            :                                                 RTE_MBUF_F_TX_UDP_SEG)))
    1106                 :          0 :                                 printf("tx: m->tso_segsz=%d\n", m->tso_segsz);
    1107                 :          0 :                         rte_get_tx_ol_flag_list(m->ol_flags, buf, sizeof(buf));
    1108                 :            :                         printf("tx: flags=%s", buf);
    1109                 :            :                         printf("\n");
    1110                 :            :                 }
    1111                 :            :         }
    1112                 :            : 
    1113                 :            : #ifdef RTE_LIB_GRO
    1114                 :          0 :         gro_enable = gro_ports[fs->rx_port].enable;
    1115                 :          0 :         if (unlikely(gro_enable)) {
    1116                 :          0 :                 if (gro_flush_cycles == GRO_DEFAULT_FLUSH_CYCLES) {
    1117                 :          0 :                         nb_rx = rte_gro_reassemble_burst(pkts_burst, nb_rx,
    1118                 :          0 :                                         &(gro_ports[fs->rx_port].param));
    1119                 :            :                 } else {
    1120                 :          0 :                         gro_ctx = current_fwd_lcore()->gro_ctx;
    1121                 :          0 :                         nb_rx = rte_gro_reassemble(pkts_burst, nb_rx, gro_ctx);
    1122                 :            : 
    1123                 :          0 :                         if (++fs->gro_times >= gro_flush_cycles) {
    1124                 :          0 :                                 gro_pkts_num = rte_gro_get_pkt_count(gro_ctx);
    1125                 :          0 :                                 if (gro_pkts_num > MAX_PKT_BURST - nb_rx)
    1126                 :          0 :                                         gro_pkts_num = MAX_PKT_BURST - nb_rx;
    1127                 :            : 
    1128                 :          0 :                                 nb_rx += rte_gro_timeout_flush(gro_ctx, 0,
    1129                 :            :                                                 RTE_GRO_TCP_IPV4,
    1130                 :            :                                                 &pkts_burst[nb_rx],
    1131                 :            :                                                 gro_pkts_num);
    1132                 :          0 :                                 fs->gro_times = 0;
    1133                 :            :                         }
    1134                 :          0 :                         if (nb_rx == 0)
    1135                 :            :                                 return false;
    1136                 :            :                 }
    1137                 :            : 
    1138                 :          0 :                 pkts_ip_csum_recalc(pkts_burst, nb_rx, tx_offloads);
    1139                 :            :         }
    1140                 :            : #endif
    1141                 :            : 
    1142                 :            : #ifdef RTE_LIB_GSO
    1143                 :          0 :         if (gso_ports[fs->tx_port].enable != 0) {
    1144                 :            :                 uint16_t nb_segments = 0;
    1145                 :            : 
    1146                 :          0 :                 gso_ctx = &(current_fwd_lcore()->gso_ctx);
    1147                 :          0 :                 gso_ctx->gso_size = gso_max_segment_size;
    1148                 :          0 :                 for (i = 0; i < nb_rx; i++) {
    1149                 :            :                         int ret;
    1150                 :            : 
    1151                 :          0 :                         ret = rte_gso_segment(pkts_burst[i], gso_ctx,
    1152                 :          0 :                                         &gso_segments[nb_segments],
    1153                 :          0 :                                         GSO_MAX_PKT_BURST - nb_segments);
    1154                 :          0 :                         if (ret >= 1) {
    1155                 :            :                                 /* pkts_burst[i] can be freed safely here. */
    1156                 :          0 :                                 rte_pktmbuf_free(pkts_burst[i]);
    1157                 :          0 :                                 nb_segments += ret;
    1158                 :          0 :                         } else if (ret == 0) {
    1159                 :            :                                 /* 0 means it can be transmitted directly
    1160                 :            :                                  * without gso.
    1161                 :            :                                  */
    1162                 :          0 :                                 gso_segments[nb_segments] = pkts_burst[i];
    1163                 :          0 :                                 nb_segments += 1;
    1164                 :            :                         } else {
    1165                 :          0 :                                 TESTPMD_LOG(DEBUG, "Unable to segment packet");
    1166                 :          0 :                                 rte_pktmbuf_free(pkts_burst[i]);
    1167                 :            :                         }
    1168                 :            :                 }
    1169                 :            : 
    1170                 :            :                 tx_pkts_burst = gso_segments;
    1171                 :            :                 nb_rx = nb_segments;
    1172                 :            : 
    1173                 :          0 :                 pkts_ip_csum_recalc(tx_pkts_burst, nb_rx, tx_offloads);
    1174                 :            :         } else
    1175                 :            : #endif
    1176                 :            :                 tx_pkts_burst = pkts_burst;
    1177                 :            : 
    1178                 :          0 :         nb_prep = rte_eth_tx_prepare(fs->tx_port, fs->tx_queue,
    1179                 :            :                         tx_pkts_burst, nb_rx);
    1180                 :          0 :         if (nb_prep != nb_rx) {
    1181                 :          0 :                 fprintf(stderr,
    1182                 :            :                         "Preparing packet burst to transmit failed: %s\n",
    1183                 :            :                         rte_strerror(rte_errno));
    1184                 :          0 :                 fs->fwd_dropped += (nb_rx - nb_prep);
    1185                 :          0 :                 rte_pktmbuf_free_bulk(&tx_pkts_burst[nb_prep], nb_rx - nb_prep);
    1186                 :            :         }
    1187                 :            : 
    1188                 :          0 :         common_fwd_stream_transmit(fs, tx_pkts_burst, nb_prep);
    1189                 :            : 
    1190                 :          0 :         fs->rx_bad_ip_csum += rx_bad_ip_csum;
    1191                 :          0 :         fs->rx_bad_l4_csum += rx_bad_l4_csum;
    1192                 :          0 :         fs->rx_bad_outer_l4_csum += rx_bad_outer_l4_csum;
    1193                 :          0 :         fs->rx_bad_outer_ip_csum += rx_bad_outer_ip_csum;
    1194                 :            : 
    1195                 :          0 :         return true;
    1196                 :            : }
    1197                 :            : 
    1198                 :            : struct fwd_engine csum_fwd_engine = {
    1199                 :            :         .fwd_mode_name  = "csum",
    1200                 :            :         .stream_init    = common_fwd_stream_init,
    1201                 :            :         .packet_fwd     = pkt_burst_checksum_forward,
    1202                 :            : };

Generated by: LCOV version 1.14