app/testpmd: rework checksum forward engine
[dpdk.git] / app / test-pmd / csumonly.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   Copyright 2014 6WIND S.A.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <stdint.h>
39 #include <unistd.h>
40 #include <inttypes.h>
41
42 #include <sys/queue.h>
43 #include <sys/stat.h>
44
45 #include <rte_common.h>
46 #include <rte_byteorder.h>
47 #include <rte_log.h>
48 #include <rte_debug.h>
49 #include <rte_cycles.h>
50 #include <rte_memory.h>
51 #include <rte_memcpy.h>
52 #include <rte_memzone.h>
53 #include <rte_launch.h>
54 #include <rte_tailq.h>
55 #include <rte_eal.h>
56 #include <rte_per_lcore.h>
57 #include <rte_lcore.h>
58 #include <rte_atomic.h>
59 #include <rte_branch_prediction.h>
60 #include <rte_ring.h>
61 #include <rte_memory.h>
62 #include <rte_mempool.h>
63 #include <rte_mbuf.h>
64 #include <rte_memcpy.h>
65 #include <rte_interrupts.h>
66 #include <rte_pci.h>
67 #include <rte_ether.h>
68 #include <rte_ethdev.h>
69 #include <rte_ip.h>
70 #include <rte_tcp.h>
71 #include <rte_udp.h>
72 #include <rte_sctp.h>
73 #include <rte_prefetch.h>
74 #include <rte_string_fns.h>
75 #include "testpmd.h"
76
77 #define IP_DEFTTL  64   /* from RFC 1340. */
78 #define IP_VERSION 0x40
79 #define IP_HDRLEN  0x05 /* default IP header length == five 32-bits words. */
80 #define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
81
82 /* we cannot use htons() from arpa/inet.h due to name conflicts, and we
83  * cannot use rte_cpu_to_be_16() on a constant in a switch/case */
84 #if __BYTE_ORDER == __LITTLE_ENDIAN
85 #define _htons(x) ((uint16_t)((((x) & 0x00ffU) << 8) | (((x) & 0xff00U) >> 8)))
86 #else
87 #define _htons(x) (x)
88 #endif
89
90 static inline uint16_t
91 get_16b_sum(uint16_t *ptr16, uint32_t nr)
92 {
93         uint32_t sum = 0;
94         while (nr > 1)
95         {
96                 sum +=*ptr16;
97                 nr -= sizeof(uint16_t);
98                 ptr16++;
99                 if (sum > UINT16_MAX)
100                         sum -= UINT16_MAX;
101         }
102
103         /* If length is in odd bytes */
104         if (nr)
105                 sum += *((uint8_t*)ptr16);
106
107         sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
108         sum &= 0x0ffff;
109         return (uint16_t)sum;
110 }
111
112 static inline uint16_t
113 get_ipv4_cksum(struct ipv4_hdr *ipv4_hdr)
114 {
115         uint16_t cksum;
116         cksum = get_16b_sum((uint16_t*)ipv4_hdr, sizeof(struct ipv4_hdr));
117         return (uint16_t)((cksum == 0xffff)?cksum:~cksum);
118 }
119
120
121 static inline uint16_t
122 get_ipv4_psd_sum(struct ipv4_hdr *ip_hdr)
123 {
124         /* Pseudo Header for IPv4/UDP/TCP checksum */
125         union ipv4_psd_header {
126                 struct {
127                         uint32_t src_addr; /* IP address of source host. */
128                         uint32_t dst_addr; /* IP address of destination host(s). */
129                         uint8_t  zero;     /* zero. */
130                         uint8_t  proto;    /* L4 protocol type. */
131                         uint16_t len;      /* L4 length. */
132                 } __attribute__((__packed__));
133                 uint16_t u16_arr[0];
134         } psd_hdr;
135
136         psd_hdr.src_addr = ip_hdr->src_addr;
137         psd_hdr.dst_addr = ip_hdr->dst_addr;
138         psd_hdr.zero     = 0;
139         psd_hdr.proto    = ip_hdr->next_proto_id;
140         psd_hdr.len      = rte_cpu_to_be_16((uint16_t)(rte_be_to_cpu_16(ip_hdr->total_length)
141                                 - sizeof(struct ipv4_hdr)));
142         return get_16b_sum(psd_hdr.u16_arr, sizeof(psd_hdr));
143 }
144
145 static inline uint16_t
146 get_ipv6_psd_sum(struct ipv6_hdr *ip_hdr)
147 {
148         /* Pseudo Header for IPv6/UDP/TCP checksum */
149         union ipv6_psd_header {
150                 struct {
151                         uint8_t src_addr[16]; /* IP address of source host. */
152                         uint8_t dst_addr[16]; /* IP address of destination host(s). */
153                         uint32_t len;         /* L4 length. */
154                         uint32_t proto;       /* L4 protocol - top 3 bytes must be zero */
155                 } __attribute__((__packed__));
156
157                 uint16_t u16_arr[0]; /* allow use as 16-bit values with safe aliasing */
158         } psd_hdr;
159
160         rte_memcpy(&psd_hdr.src_addr, ip_hdr->src_addr,
161                         sizeof(ip_hdr->src_addr) + sizeof(ip_hdr->dst_addr));
162         psd_hdr.len       = ip_hdr->payload_len;
163         psd_hdr.proto     = (ip_hdr->proto << 24);
164
165         return get_16b_sum(psd_hdr.u16_arr, sizeof(psd_hdr));
166 }
167
168 static uint16_t
169 get_psd_sum(void *l3_hdr, uint16_t ethertype)
170 {
171         if (ethertype == _htons(ETHER_TYPE_IPv4))
172                 return get_ipv4_psd_sum(l3_hdr);
173         else /* assume ethertype == ETHER_TYPE_IPv6 */
174                 return get_ipv6_psd_sum(l3_hdr);
175 }
176
177 static inline uint16_t
178 get_ipv4_udptcp_checksum(struct ipv4_hdr *ipv4_hdr, uint16_t *l4_hdr)
179 {
180         uint32_t cksum;
181         uint32_t l4_len;
182
183         l4_len = rte_be_to_cpu_16(ipv4_hdr->total_length) - sizeof(struct ipv4_hdr);
184
185         cksum = get_16b_sum(l4_hdr, l4_len);
186         cksum += get_ipv4_psd_sum(ipv4_hdr);
187
188         cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
189         cksum = (~cksum) & 0xffff;
190         if (cksum == 0)
191                 cksum = 0xffff;
192         return (uint16_t)cksum;
193 }
194
195 static inline uint16_t
196 get_ipv6_udptcp_checksum(struct ipv6_hdr *ipv6_hdr, uint16_t *l4_hdr)
197 {
198         uint32_t cksum;
199         uint32_t l4_len;
200
201         l4_len = rte_be_to_cpu_16(ipv6_hdr->payload_len);
202
203         cksum = get_16b_sum(l4_hdr, l4_len);
204         cksum += get_ipv6_psd_sum(ipv6_hdr);
205
206         cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
207         cksum = (~cksum) & 0xffff;
208         if (cksum == 0)
209                 cksum = 0xffff;
210
211         return (uint16_t)cksum;
212 }
213
214 static uint16_t
215 get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype)
216 {
217         if (ethertype == _htons(ETHER_TYPE_IPv4))
218                 return get_ipv4_udptcp_checksum(l3_hdr, l4_hdr);
219         else /* assume ethertype == ETHER_TYPE_IPv6 */
220                 return get_ipv6_udptcp_checksum(l3_hdr, l4_hdr);
221 }
222
223 /*
224  * Parse an ethernet header to fill the ethertype, l2_len, l3_len and
225  * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan
226  * header.
227  */
228 static void
229 parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len,
230         uint16_t *l3_len, uint8_t *l4_proto)
231 {
232         struct ipv4_hdr *ipv4_hdr;
233         struct ipv6_hdr *ipv6_hdr;
234
235         *l2_len = sizeof(struct ether_hdr);
236         *ethertype = eth_hdr->ether_type;
237
238         if (*ethertype == _htons(ETHER_TYPE_VLAN)) {
239                 struct vlan_hdr *vlan_hdr = (struct vlan_hdr *)(eth_hdr + 1);
240
241                 *l2_len  += sizeof(struct vlan_hdr);
242                 *ethertype = vlan_hdr->eth_proto;
243         }
244
245         switch (*ethertype) {
246         case _htons(ETHER_TYPE_IPv4):
247                 ipv4_hdr = (struct ipv4_hdr *) ((char *)eth_hdr + *l2_len);
248                 *l3_len = (ipv4_hdr->version_ihl & 0x0f) * 4;
249                 *l4_proto = ipv4_hdr->next_proto_id;
250                 break;
251         case _htons(ETHER_TYPE_IPv6):
252                 ipv6_hdr = (struct ipv6_hdr *) ((char *)eth_hdr + *l2_len);
253                 *l3_len = sizeof(struct ipv6_hdr);
254                 *l4_proto = ipv6_hdr->proto;
255                 break;
256         default:
257                 *l3_len = 0;
258                 *l4_proto = 0;
259                 break;
260         }
261 }
262
263 /* modify the IPv4 or IPv4 source address of a packet */
264 static void
265 change_ip_addresses(void *l3_hdr, uint16_t ethertype)
266 {
267         struct ipv4_hdr *ipv4_hdr = l3_hdr;
268         struct ipv6_hdr *ipv6_hdr = l3_hdr;
269
270         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
271                 ipv4_hdr->src_addr =
272                         rte_cpu_to_be_32(rte_be_to_cpu_32(ipv4_hdr->src_addr) + 1);
273         } else if (ethertype == _htons(ETHER_TYPE_IPv6)) {
274                 ipv6_hdr->src_addr[15] = ipv6_hdr->src_addr[15] + 1;
275         }
276 }
277
278 /* if possible, calculate the checksum of a packet in hw or sw,
279  * depending on the testpmd command line configuration */
280 static uint64_t
281 process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
282         uint8_t l4_proto, uint16_t testpmd_ol_flags)
283 {
284         struct ipv4_hdr *ipv4_hdr = l3_hdr;
285         struct udp_hdr *udp_hdr;
286         struct tcp_hdr *tcp_hdr;
287         struct sctp_hdr *sctp_hdr;
288         uint64_t ol_flags = 0;
289
290         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
291                 ipv4_hdr = l3_hdr;
292                 ipv4_hdr->hdr_checksum = 0;
293
294                 if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM)
295                         ol_flags |= PKT_TX_IP_CKSUM;
296                 else
297                         ipv4_hdr->hdr_checksum = get_ipv4_cksum(ipv4_hdr);
298
299                 ol_flags |= PKT_TX_IPV4;
300         } else if (ethertype == _htons(ETHER_TYPE_IPv6))
301                 ol_flags |= PKT_TX_IPV6;
302         else
303                 return 0; /* packet type not supported, nothing to do */
304
305         if (l4_proto == IPPROTO_UDP) {
306                 udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
307                 /* do not recalculate udp cksum if it was 0 */
308                 if (udp_hdr->dgram_cksum != 0) {
309                         udp_hdr->dgram_cksum = 0;
310                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_UDP_CKSUM) {
311                                 ol_flags |= PKT_TX_UDP_CKSUM;
312                                 udp_hdr->dgram_cksum = get_psd_sum(l3_hdr,
313                                         ethertype);
314                         } else {
315                                 udp_hdr->dgram_cksum =
316                                         get_udptcp_checksum(l3_hdr, udp_hdr,
317                                                 ethertype);
318                         }
319                 }
320         } else if (l4_proto == IPPROTO_TCP) {
321                 tcp_hdr = (struct tcp_hdr *)((char *)l3_hdr + l3_len);
322                 tcp_hdr->cksum = 0;
323                 if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) {
324                         ol_flags |= PKT_TX_TCP_CKSUM;
325                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype);
326                 } else {
327                         tcp_hdr->cksum =
328                                 get_udptcp_checksum(l3_hdr, tcp_hdr, ethertype);
329                 }
330         } else if (l4_proto == IPPROTO_SCTP) {
331                 sctp_hdr = (struct sctp_hdr *)((char *)l3_hdr + l3_len);
332                 sctp_hdr->cksum = 0;
333                 /* sctp payload must be a multiple of 4 to be
334                  * offloaded */
335                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_SCTP_CKSUM) &&
336                         ((ipv4_hdr->total_length & 0x3) == 0)) {
337                         ol_flags |= PKT_TX_SCTP_CKSUM;
338                 } else {
339                         /* XXX implement CRC32c, example available in
340                          * RFC3309 */
341                 }
342         }
343
344         return ol_flags;
345 }
346
347 /* Calculate the checksum of outer header (only vxlan is supported,
348  * meaning IP + UDP). The caller already checked that it's a vxlan
349  * packet */
350 static uint64_t
351 process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
352         uint16_t outer_l3_len, uint16_t testpmd_ol_flags)
353 {
354         struct ipv4_hdr *ipv4_hdr = outer_l3_hdr;
355         struct ipv6_hdr *ipv6_hdr = outer_l3_hdr;
356         struct udp_hdr *udp_hdr;
357         uint64_t ol_flags = 0;
358
359         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
360                 ol_flags |= PKT_TX_VXLAN_CKSUM;
361
362         if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) {
363                 ipv4_hdr->hdr_checksum = 0;
364
365                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0)
366                         ipv4_hdr->hdr_checksum = get_ipv4_cksum(ipv4_hdr);
367         }
368
369         udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
370         /* do not recalculate udp cksum if it was 0 */
371         if (udp_hdr->dgram_cksum != 0) {
372                 udp_hdr->dgram_cksum = 0;
373                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) {
374                         if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
375                                 udp_hdr->dgram_cksum =
376                                         get_ipv4_udptcp_checksum(ipv4_hdr,
377                                                 (uint16_t *)udp_hdr);
378                         else
379                                 udp_hdr->dgram_cksum =
380                                         get_ipv6_udptcp_checksum(ipv6_hdr,
381                                                 (uint16_t *)udp_hdr);
382                 }
383         }
384
385         return ol_flags;
386 }
387
388 /*
389  * Receive a burst of packets, and for each packet:
390  *  - parse packet, and try to recognize a supported packet type (1)
391  *  - if it's not a supported packet type, don't touch the packet, else:
392  *  - modify the IPs in inner headers and in outer headers if any
393  *  - reprocess the checksum of all supported layers. This is done in SW
394  *    or HW, depending on testpmd command line configuration
395  * Then transmit packets on the output port.
396  *
397  * (1) Supported packets are:
398  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
399  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
400  *           UDP|TCP|SCTP
401  *
402  * The testpmd command line for this forward engine sets the flags
403  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
404  * wether a checksum must be calculated in software or in hardware. The
405  * IP, UDP, TCP and SCTP flags always concern the inner layer.  The
406  * VxLAN flag concerns the outer IP and UDP layer (if packet is
407  * recognized as a vxlan packet).
408  */
409 static void
410 pkt_burst_checksum_forward(struct fwd_stream *fs)
411 {
412         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
413         struct rte_port *txp;
414         struct rte_mbuf *m;
415         struct ether_hdr *eth_hdr;
416         void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
417         struct udp_hdr *udp_hdr;
418         uint16_t nb_rx;
419         uint16_t nb_tx;
420         uint16_t i;
421         uint64_t ol_flags;
422         uint16_t testpmd_ol_flags;
423         uint8_t l4_proto;
424         uint16_t ethertype = 0, outer_ethertype = 0;
425         uint16_t  l2_len = 0, l3_len = 0, outer_l2_len = 0, outer_l3_len = 0;
426         int tunnel = 0;
427         uint32_t rx_bad_ip_csum;
428         uint32_t rx_bad_l4_csum;
429
430 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
431         uint64_t start_tsc;
432         uint64_t end_tsc;
433         uint64_t core_cycles;
434 #endif
435
436 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
437         start_tsc = rte_rdtsc();
438 #endif
439
440         /* receive a burst of packet */
441         nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
442                                  nb_pkt_per_burst);
443         if (unlikely(nb_rx == 0))
444                 return;
445
446 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
447         fs->rx_burst_stats.pkt_burst_spread[nb_rx]++;
448 #endif
449         fs->rx_packets += nb_rx;
450         rx_bad_ip_csum = 0;
451         rx_bad_l4_csum = 0;
452
453         txp = &ports[fs->tx_port];
454         testpmd_ol_flags = txp->tx_ol_flags;
455
456         for (i = 0; i < nb_rx; i++) {
457
458                 ol_flags = 0;
459                 tunnel = 0;
460                 m = pkts_burst[i];
461
462                 /* Update the L3/L4 checksum error packet statistics */
463                 rx_bad_ip_csum += ((m->ol_flags & PKT_RX_IP_CKSUM_BAD) != 0);
464                 rx_bad_l4_csum += ((m->ol_flags & PKT_RX_L4_CKSUM_BAD) != 0);
465
466                 /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
467                  * and inner headers */
468
469                 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
470                 parse_ethernet(eth_hdr, &ethertype, &l2_len, &l3_len, &l4_proto);
471                 l3_hdr = (char *)eth_hdr + l2_len;
472
473                 /* check if it's a supported tunnel (only vxlan for now) */
474                 if (l4_proto == IPPROTO_UDP) {
475                         udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
476
477                         /* currently, this flag is set by i40e only if the
478                          * packet is vxlan */
479                         if (((m->ol_flags & PKT_RX_TUNNEL_IPV4_HDR) ||
480                                         (m->ol_flags & PKT_RX_TUNNEL_IPV6_HDR)))
481                                 tunnel = 1;
482                         /* else check udp destination port, 4789 is the default
483                          * vxlan port (rfc7348) */
484                         else if (udp_hdr->dst_port == _htons(4789))
485                                 tunnel = 1;
486
487                         if (tunnel == 1) {
488                                 outer_ethertype = ethertype;
489                                 outer_l2_len = l2_len;
490                                 outer_l3_len = l3_len;
491                                 outer_l3_hdr = l3_hdr;
492
493                                 eth_hdr = (struct ether_hdr *)((char *)udp_hdr +
494                                         sizeof(struct udp_hdr) +
495                                         sizeof(struct vxlan_hdr));
496
497                                 parse_ethernet(eth_hdr, &ethertype, &l2_len,
498                                         &l3_len, &l4_proto);
499                                 l3_hdr = (char *)eth_hdr + l2_len;
500                         }
501                 }
502
503                 /* step 2: change all source IPs (v4 or v6) so we need
504                  * to recompute the chksums even if they were correct */
505
506                 change_ip_addresses(l3_hdr, ethertype);
507                 if (tunnel == 1)
508                         change_ip_addresses(outer_l3_hdr, outer_ethertype);
509
510                 /* step 3: depending on user command line configuration,
511                  * recompute checksum either in software or flag the
512                  * mbuf to offload the calculation to the NIC */
513
514                 /* process checksums of inner headers first */
515                 ol_flags |= process_inner_cksums(l3_hdr, ethertype,
516                         l3_len, l4_proto, testpmd_ol_flags);
517
518                 /* Then process outer headers if any. Note that the software
519                  * checksum will be wrong if one of the inner checksums is
520                  * processed in hardware. */
521                 if (tunnel == 1) {
522                         ol_flags |= process_outer_cksums(outer_l3_hdr,
523                                 outer_ethertype, outer_l3_len, testpmd_ol_flags);
524                 }
525
526                 /* step 4: fill the mbuf meta data (flags and header lengths) */
527
528                 if (tunnel == 1) {
529                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) {
530                                 m->l2_len = outer_l2_len;
531                                 m->l3_len = outer_l3_len;
532                                 m->inner_l2_len = l2_len;
533                                 m->inner_l3_len = l3_len;
534                         }
535                         else {
536                                 /* if we don't do vxlan cksum in hw,
537                                    outer checksum will be wrong because
538                                    we changed the ip, but it shows that
539                                    we can process the inner header cksum
540                                    in the nic */
541                                 m->l2_len = outer_l2_len + outer_l3_len +
542                                         sizeof(struct udp_hdr) +
543                                         sizeof(struct vxlan_hdr) + l2_len;
544                                 m->l3_len = l3_len;
545                         }
546                 } else {
547                         /* this is only useful if an offload flag is
548                          * set, but it does not hurt to fill it in any
549                          * case */
550                         m->l2_len = l2_len;
551                         m->l3_len = l3_len;
552                 }
553                 m->ol_flags = ol_flags;
554
555         }
556         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
557         fs->tx_packets += nb_tx;
558         fs->rx_bad_ip_csum += rx_bad_ip_csum;
559         fs->rx_bad_l4_csum += rx_bad_l4_csum;
560
561 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
562         fs->tx_burst_stats.pkt_burst_spread[nb_tx]++;
563 #endif
564         if (unlikely(nb_tx < nb_rx)) {
565                 fs->fwd_dropped += (nb_rx - nb_tx);
566                 do {
567                         rte_pktmbuf_free(pkts_burst[nb_tx]);
568                 } while (++nb_tx < nb_rx);
569         }
570 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
571         end_tsc = rte_rdtsc();
572         core_cycles = (end_tsc - start_tsc);
573         fs->core_cycles = (uint64_t) (fs->core_cycles + core_cycles);
574 #endif
575 }
576
577 struct fwd_engine csum_fwd_engine = {
578         .fwd_mode_name  = "csum",
579         .port_fwd_begin = NULL,
580         .port_fwd_end   = NULL,
581         .packet_fwd     = pkt_burst_checksum_forward,
582 };
583