net: new checksum functions
[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 uint16_t
91 get_psd_sum(void *l3_hdr, uint16_t ethertype)
92 {
93         if (ethertype == _htons(ETHER_TYPE_IPv4))
94                 return rte_ipv4_phdr_cksum(l3_hdr);
95         else /* assume ethertype == ETHER_TYPE_IPv6 */
96                 return rte_ipv6_phdr_cksum(l3_hdr);
97 }
98
99 static uint16_t
100 get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype)
101 {
102         if (ethertype == _htons(ETHER_TYPE_IPv4))
103                 return rte_ipv4_udptcp_cksum(l3_hdr, l4_hdr);
104         else /* assume ethertype == ETHER_TYPE_IPv6 */
105                 return rte_ipv6_udptcp_cksum(l3_hdr, l4_hdr);
106 }
107
108 /*
109  * Parse an ethernet header to fill the ethertype, l2_len, l3_len and
110  * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan
111  * header.
112  */
113 static void
114 parse_ethernet(struct ether_hdr *eth_hdr, uint16_t *ethertype, uint16_t *l2_len,
115         uint16_t *l3_len, uint8_t *l4_proto)
116 {
117         struct ipv4_hdr *ipv4_hdr;
118         struct ipv6_hdr *ipv6_hdr;
119
120         *l2_len = sizeof(struct ether_hdr);
121         *ethertype = eth_hdr->ether_type;
122
123         if (*ethertype == _htons(ETHER_TYPE_VLAN)) {
124                 struct vlan_hdr *vlan_hdr = (struct vlan_hdr *)(eth_hdr + 1);
125
126                 *l2_len  += sizeof(struct vlan_hdr);
127                 *ethertype = vlan_hdr->eth_proto;
128         }
129
130         switch (*ethertype) {
131         case _htons(ETHER_TYPE_IPv4):
132                 ipv4_hdr = (struct ipv4_hdr *) ((char *)eth_hdr + *l2_len);
133                 *l3_len = (ipv4_hdr->version_ihl & 0x0f) * 4;
134                 *l4_proto = ipv4_hdr->next_proto_id;
135                 break;
136         case _htons(ETHER_TYPE_IPv6):
137                 ipv6_hdr = (struct ipv6_hdr *) ((char *)eth_hdr + *l2_len);
138                 *l3_len = sizeof(struct ipv6_hdr);
139                 *l4_proto = ipv6_hdr->proto;
140                 break;
141         default:
142                 *l3_len = 0;
143                 *l4_proto = 0;
144                 break;
145         }
146 }
147
148 /* modify the IPv4 or IPv4 source address of a packet */
149 static void
150 change_ip_addresses(void *l3_hdr, uint16_t ethertype)
151 {
152         struct ipv4_hdr *ipv4_hdr = l3_hdr;
153         struct ipv6_hdr *ipv6_hdr = l3_hdr;
154
155         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
156                 ipv4_hdr->src_addr =
157                         rte_cpu_to_be_32(rte_be_to_cpu_32(ipv4_hdr->src_addr) + 1);
158         } else if (ethertype == _htons(ETHER_TYPE_IPv6)) {
159                 ipv6_hdr->src_addr[15] = ipv6_hdr->src_addr[15] + 1;
160         }
161 }
162
163 /* if possible, calculate the checksum of a packet in hw or sw,
164  * depending on the testpmd command line configuration */
165 static uint64_t
166 process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
167         uint8_t l4_proto, uint16_t testpmd_ol_flags)
168 {
169         struct ipv4_hdr *ipv4_hdr = l3_hdr;
170         struct udp_hdr *udp_hdr;
171         struct tcp_hdr *tcp_hdr;
172         struct sctp_hdr *sctp_hdr;
173         uint64_t ol_flags = 0;
174
175         if (ethertype == _htons(ETHER_TYPE_IPv4)) {
176                 ipv4_hdr = l3_hdr;
177                 ipv4_hdr->hdr_checksum = 0;
178
179                 if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM)
180                         ol_flags |= PKT_TX_IP_CKSUM;
181                 else
182                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
183
184                 ol_flags |= PKT_TX_IPV4;
185         } else if (ethertype == _htons(ETHER_TYPE_IPv6))
186                 ol_flags |= PKT_TX_IPV6;
187         else
188                 return 0; /* packet type not supported, nothing to do */
189
190         if (l4_proto == IPPROTO_UDP) {
191                 udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
192                 /* do not recalculate udp cksum if it was 0 */
193                 if (udp_hdr->dgram_cksum != 0) {
194                         udp_hdr->dgram_cksum = 0;
195                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_UDP_CKSUM) {
196                                 ol_flags |= PKT_TX_UDP_CKSUM;
197                                 udp_hdr->dgram_cksum = get_psd_sum(l3_hdr,
198                                         ethertype);
199                         } else {
200                                 udp_hdr->dgram_cksum =
201                                         get_udptcp_checksum(l3_hdr, udp_hdr,
202                                                 ethertype);
203                         }
204                 }
205         } else if (l4_proto == IPPROTO_TCP) {
206                 tcp_hdr = (struct tcp_hdr *)((char *)l3_hdr + l3_len);
207                 tcp_hdr->cksum = 0;
208                 if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) {
209                         ol_flags |= PKT_TX_TCP_CKSUM;
210                         tcp_hdr->cksum = get_psd_sum(l3_hdr, ethertype);
211                 } else {
212                         tcp_hdr->cksum =
213                                 get_udptcp_checksum(l3_hdr, tcp_hdr, ethertype);
214                 }
215         } else if (l4_proto == IPPROTO_SCTP) {
216                 sctp_hdr = (struct sctp_hdr *)((char *)l3_hdr + l3_len);
217                 sctp_hdr->cksum = 0;
218                 /* sctp payload must be a multiple of 4 to be
219                  * offloaded */
220                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_SCTP_CKSUM) &&
221                         ((ipv4_hdr->total_length & 0x3) == 0)) {
222                         ol_flags |= PKT_TX_SCTP_CKSUM;
223                 } else {
224                         /* XXX implement CRC32c, example available in
225                          * RFC3309 */
226                 }
227         }
228
229         return ol_flags;
230 }
231
232 /* Calculate the checksum of outer header (only vxlan is supported,
233  * meaning IP + UDP). The caller already checked that it's a vxlan
234  * packet */
235 static uint64_t
236 process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
237         uint16_t outer_l3_len, uint16_t testpmd_ol_flags)
238 {
239         struct ipv4_hdr *ipv4_hdr = outer_l3_hdr;
240         struct ipv6_hdr *ipv6_hdr = outer_l3_hdr;
241         struct udp_hdr *udp_hdr;
242         uint64_t ol_flags = 0;
243
244         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
245                 ol_flags |= PKT_TX_VXLAN_CKSUM;
246
247         if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) {
248                 ipv4_hdr->hdr_checksum = 0;
249
250                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0)
251                         ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
252         }
253
254         udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
255         /* do not recalculate udp cksum if it was 0 */
256         if (udp_hdr->dgram_cksum != 0) {
257                 udp_hdr->dgram_cksum = 0;
258                 if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) {
259                         if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
260                                 udp_hdr->dgram_cksum =
261                                         rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
262                         else
263                                 udp_hdr->dgram_cksum =
264                                         rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
265                 }
266         }
267
268         return ol_flags;
269 }
270
271 /*
272  * Receive a burst of packets, and for each packet:
273  *  - parse packet, and try to recognize a supported packet type (1)
274  *  - if it's not a supported packet type, don't touch the packet, else:
275  *  - modify the IPs in inner headers and in outer headers if any
276  *  - reprocess the checksum of all supported layers. This is done in SW
277  *    or HW, depending on testpmd command line configuration
278  * Then transmit packets on the output port.
279  *
280  * (1) Supported packets are:
281  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
282  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
283  *           UDP|TCP|SCTP
284  *
285  * The testpmd command line for this forward engine sets the flags
286  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
287  * wether a checksum must be calculated in software or in hardware. The
288  * IP, UDP, TCP and SCTP flags always concern the inner layer.  The
289  * VxLAN flag concerns the outer IP and UDP layer (if packet is
290  * recognized as a vxlan packet).
291  */
292 static void
293 pkt_burst_checksum_forward(struct fwd_stream *fs)
294 {
295         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
296         struct rte_port *txp;
297         struct rte_mbuf *m;
298         struct ether_hdr *eth_hdr;
299         void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
300         struct udp_hdr *udp_hdr;
301         uint16_t nb_rx;
302         uint16_t nb_tx;
303         uint16_t i;
304         uint64_t ol_flags;
305         uint16_t testpmd_ol_flags;
306         uint8_t l4_proto;
307         uint16_t ethertype = 0, outer_ethertype = 0;
308         uint16_t  l2_len = 0, l3_len = 0, outer_l2_len = 0, outer_l3_len = 0;
309         int tunnel = 0;
310         uint32_t rx_bad_ip_csum;
311         uint32_t rx_bad_l4_csum;
312
313 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
314         uint64_t start_tsc;
315         uint64_t end_tsc;
316         uint64_t core_cycles;
317 #endif
318
319 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
320         start_tsc = rte_rdtsc();
321 #endif
322
323         /* receive a burst of packet */
324         nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
325                                  nb_pkt_per_burst);
326         if (unlikely(nb_rx == 0))
327                 return;
328
329 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
330         fs->rx_burst_stats.pkt_burst_spread[nb_rx]++;
331 #endif
332         fs->rx_packets += nb_rx;
333         rx_bad_ip_csum = 0;
334         rx_bad_l4_csum = 0;
335
336         txp = &ports[fs->tx_port];
337         testpmd_ol_flags = txp->tx_ol_flags;
338
339         for (i = 0; i < nb_rx; i++) {
340
341                 ol_flags = 0;
342                 tunnel = 0;
343                 m = pkts_burst[i];
344
345                 /* Update the L3/L4 checksum error packet statistics */
346                 rx_bad_ip_csum += ((m->ol_flags & PKT_RX_IP_CKSUM_BAD) != 0);
347                 rx_bad_l4_csum += ((m->ol_flags & PKT_RX_L4_CKSUM_BAD) != 0);
348
349                 /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
350                  * and inner headers */
351
352                 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
353                 parse_ethernet(eth_hdr, &ethertype, &l2_len, &l3_len, &l4_proto);
354                 l3_hdr = (char *)eth_hdr + l2_len;
355
356                 /* check if it's a supported tunnel (only vxlan for now) */
357                 if (l4_proto == IPPROTO_UDP) {
358                         udp_hdr = (struct udp_hdr *)((char *)l3_hdr + l3_len);
359
360                         /* currently, this flag is set by i40e only if the
361                          * packet is vxlan */
362                         if (((m->ol_flags & PKT_RX_TUNNEL_IPV4_HDR) ||
363                                         (m->ol_flags & PKT_RX_TUNNEL_IPV6_HDR)))
364                                 tunnel = 1;
365                         /* else check udp destination port, 4789 is the default
366                          * vxlan port (rfc7348) */
367                         else if (udp_hdr->dst_port == _htons(4789))
368                                 tunnel = 1;
369
370                         if (tunnel == 1) {
371                                 outer_ethertype = ethertype;
372                                 outer_l2_len = l2_len;
373                                 outer_l3_len = l3_len;
374                                 outer_l3_hdr = l3_hdr;
375
376                                 eth_hdr = (struct ether_hdr *)((char *)udp_hdr +
377                                         sizeof(struct udp_hdr) +
378                                         sizeof(struct vxlan_hdr));
379
380                                 parse_ethernet(eth_hdr, &ethertype, &l2_len,
381                                         &l3_len, &l4_proto);
382                                 l3_hdr = (char *)eth_hdr + l2_len;
383                         }
384                 }
385
386                 /* step 2: change all source IPs (v4 or v6) so we need
387                  * to recompute the chksums even if they were correct */
388
389                 change_ip_addresses(l3_hdr, ethertype);
390                 if (tunnel == 1)
391                         change_ip_addresses(outer_l3_hdr, outer_ethertype);
392
393                 /* step 3: depending on user command line configuration,
394                  * recompute checksum either in software or flag the
395                  * mbuf to offload the calculation to the NIC */
396
397                 /* process checksums of inner headers first */
398                 ol_flags |= process_inner_cksums(l3_hdr, ethertype,
399                         l3_len, l4_proto, testpmd_ol_flags);
400
401                 /* Then process outer headers if any. Note that the software
402                  * checksum will be wrong if one of the inner checksums is
403                  * processed in hardware. */
404                 if (tunnel == 1) {
405                         ol_flags |= process_outer_cksums(outer_l3_hdr,
406                                 outer_ethertype, outer_l3_len, testpmd_ol_flags);
407                 }
408
409                 /* step 4: fill the mbuf meta data (flags and header lengths) */
410
411                 if (tunnel == 1) {
412                         if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) {
413                                 m->l2_len = outer_l2_len;
414                                 m->l3_len = outer_l3_len;
415                                 m->inner_l2_len = l2_len;
416                                 m->inner_l3_len = l3_len;
417                         }
418                         else {
419                                 /* if we don't do vxlan cksum in hw,
420                                    outer checksum will be wrong because
421                                    we changed the ip, but it shows that
422                                    we can process the inner header cksum
423                                    in the nic */
424                                 m->l2_len = outer_l2_len + outer_l3_len +
425                                         sizeof(struct udp_hdr) +
426                                         sizeof(struct vxlan_hdr) + l2_len;
427                                 m->l3_len = l3_len;
428                         }
429                 } else {
430                         /* this is only useful if an offload flag is
431                          * set, but it does not hurt to fill it in any
432                          * case */
433                         m->l2_len = l2_len;
434                         m->l3_len = l3_len;
435                 }
436                 m->ol_flags = ol_flags;
437
438         }
439         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
440         fs->tx_packets += nb_tx;
441         fs->rx_bad_ip_csum += rx_bad_ip_csum;
442         fs->rx_bad_l4_csum += rx_bad_l4_csum;
443
444 #ifdef RTE_TEST_PMD_RECORD_BURST_STATS
445         fs->tx_burst_stats.pkt_burst_spread[nb_tx]++;
446 #endif
447         if (unlikely(nb_tx < nb_rx)) {
448                 fs->fwd_dropped += (nb_rx - nb_tx);
449                 do {
450                         rte_pktmbuf_free(pkts_burst[nb_tx]);
451                 } while (++nb_tx < nb_rx);
452         }
453 #ifdef RTE_TEST_PMD_RECORD_CORE_CYCLES
454         end_tsc = rte_rdtsc();
455         core_cycles = (end_tsc - start_tsc);
456         fs->core_cycles = (uint64_t) (fs->core_cycles + core_cycles);
457 #endif
458 }
459
460 struct fwd_engine csum_fwd_engine = {
461         .fwd_mode_name  = "csum",
462         .port_fwd_begin = NULL,
463         .port_fwd_end   = NULL,
464         .packet_fwd     = pkt_burst_checksum_forward,
465 };
466