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 : : #include <rte_net.h>
50 : :
51 : : #include "testpmd.h"
52 : :
53 : : #define IP_DEFTTL 64 /* from RFC 1340. */
54 : :
55 : : #define GRE_CHECKSUM_PRESENT 0x8000
56 : : #define GRE_KEY_PRESENT 0x2000
57 : : #define GRE_SEQUENCE_PRESENT 0x1000
58 : : #define GRE_EXT_LEN 4
59 : : #define GRE_SUPPORTED_FIELDS (GRE_CHECKSUM_PRESENT | GRE_KEY_PRESENT |\
60 : : GRE_SEQUENCE_PRESENT)
61 : :
62 : : #define MAX_VLAN_HEADERS 8
63 : :
64 : : /* We cannot use rte_cpu_to_be_16() on a constant in a switch/case */
65 : : #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
66 : : #define _htons(x) ((uint16_t)((((x) & 0x00ffU) << 8) | (((x) & 0xff00U) >> 8)))
67 : : #else
68 : : #define _htons(x) (x)
69 : : #endif
70 : :
71 : : uint16_t vxlan_gpe_udp_port = RTE_VXLAN_GPE_DEFAULT_PORT;
72 : : uint16_t geneve_udp_port = RTE_GENEVE_DEFAULT_PORT;
73 : :
74 : : /* structure that caches offload info for the current packet */
75 : : struct testpmd_offload_info {
76 : : uint16_t ethertype;
77 : : #ifdef RTE_LIB_GSO
78 : : uint8_t gso_enable;
79 : : #endif
80 : : uint16_t l2_len;
81 : : uint16_t l3_len;
82 : : uint16_t l4_len;
83 : : uint8_t l4_proto;
84 : : uint8_t is_tunnel;
85 : : uint16_t outer_ethertype;
86 : : uint16_t outer_l2_len;
87 : : uint16_t outer_l3_len;
88 : : uint8_t outer_l4_proto;
89 : : uint16_t tso_segsz;
90 : : uint16_t tunnel_tso_segsz;
91 : : uint32_t pkt_len;
92 : : };
93 : :
94 : : /* simplified GRE header */
95 : : struct simple_gre_hdr {
96 : : uint16_t flags;
97 : : uint16_t proto;
98 : : };
99 : :
100 : : static uint16_t
101 : 0 : get_udptcp_checksum(struct rte_mbuf *m, void *l3_hdr, uint16_t l4_off,
102 : : uint16_t ethertype)
103 : : {
104 : 0 : if (ethertype == _htons(RTE_ETHER_TYPE_IPV4))
105 : 0 : return rte_ipv4_udptcp_cksum_mbuf(m, l3_hdr, l4_off);
106 : : else /* assume ethertype == RTE_ETHER_TYPE_IPV6 */
107 : 0 : return rte_ipv6_udptcp_cksum_mbuf(m, l3_hdr, l4_off);
108 : : }
109 : :
110 : : /* Fill in outer layers length */
111 : : static void
112 : : update_tunnel_outer(struct testpmd_offload_info *info)
113 : : {
114 : : info->is_tunnel = 1;
115 : 0 : info->outer_ethertype = info->ethertype;
116 : 0 : info->outer_l2_len = info->l2_len;
117 : 0 : info->outer_l3_len = info->l3_len;
118 : 0 : info->outer_l4_proto = info->l4_proto;
119 : 0 : info->l4_proto = 0;
120 : : }
121 : :
122 : : /* if possible, calculate the checksum of a packet in hw or sw,
123 : : * depending on the testpmd command line configuration */
124 : : static uint64_t
125 : 0 : process_inner_cksums(void *l3_hdr, const struct testpmd_offload_info *info,
126 : : uint64_t tx_offloads, struct rte_mbuf *m)
127 : : {
128 : : struct rte_ipv4_hdr *ipv4_hdr = l3_hdr;
129 : : struct rte_udp_hdr *udp_hdr;
130 : : struct rte_tcp_hdr *tcp_hdr;
131 : : struct rte_sctp_hdr *sctp_hdr;
132 : : uint64_t ol_flags = 0;
133 : : uint32_t max_pkt_len, tso_segsz = 0;
134 : : uint16_t l4_off;
135 : : uint64_t all_tunnel_tso = RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
136 : : RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO |
137 : : RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO |
138 : : RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO |
139 : : RTE_ETH_TX_OFFLOAD_IP_TNL_TSO |
140 : : RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO;
141 : :
142 : : /* ensure packet is large enough to require tso */
143 : 0 : if (!info->is_tunnel) {
144 : 0 : max_pkt_len = info->l2_len + info->l3_len + info->l4_len +
145 : 0 : info->tso_segsz;
146 : 0 : if (info->tso_segsz != 0 && info->pkt_len > max_pkt_len)
147 : 0 : tso_segsz = info->tso_segsz;
148 : : } else {
149 : 0 : max_pkt_len = info->outer_l2_len + info->outer_l3_len +
150 : 0 : info->l2_len + info->l3_len + info->l4_len +
151 : 0 : info->tunnel_tso_segsz;
152 : 0 : if (info->tunnel_tso_segsz != 0 && info->pkt_len > max_pkt_len)
153 : 0 : tso_segsz = info->tunnel_tso_segsz;
154 : : }
155 : :
156 : 0 : if (info->ethertype == _htons(RTE_ETHER_TYPE_IPV4)) {
157 : : ipv4_hdr = l3_hdr;
158 : :
159 : : ol_flags |= RTE_MBUF_F_TX_IPV4;
160 : 0 : if (info->l4_proto == IPPROTO_TCP && tso_segsz) {
161 : : ol_flags |= RTE_MBUF_F_TX_IP_CKSUM;
162 : : } else {
163 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {
164 : : ol_flags |= RTE_MBUF_F_TX_IP_CKSUM;
165 : : } else {
166 : 0 : ipv4_hdr->hdr_checksum = 0;
167 : 0 : ipv4_hdr->hdr_checksum =
168 : : rte_ipv4_cksum(ipv4_hdr);
169 : : }
170 : : }
171 : 0 : } else if (info->ethertype == _htons(RTE_ETHER_TYPE_IPV6))
172 : : ol_flags |= RTE_MBUF_F_TX_IPV6;
173 : : else
174 : : return 0; /* packet type not supported, nothing to do */
175 : :
176 : 0 : if (info->l4_proto == IPPROTO_UDP) {
177 : 0 : udp_hdr = (struct rte_udp_hdr *)((char *)l3_hdr + info->l3_len);
178 : : /* do not recalculate udp cksum if it was 0 */
179 : 0 : if (udp_hdr->dgram_cksum != 0) {
180 : 0 : if (tso_segsz && (tx_offloads & RTE_ETH_TX_OFFLOAD_UDP_TSO))
181 : 0 : ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
182 : 0 : else if (tx_offloads & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {
183 : 0 : ol_flags |= RTE_MBUF_F_TX_UDP_CKSUM;
184 : : } else {
185 : 0 : if (info->is_tunnel)
186 : 0 : l4_off = info->outer_l2_len +
187 : 0 : info->outer_l3_len +
188 : 0 : info->l2_len + info->l3_len;
189 : : else
190 : 0 : l4_off = info->l2_len + info->l3_len;
191 : 0 : udp_hdr->dgram_cksum = 0;
192 : 0 : udp_hdr->dgram_cksum =
193 : 0 : get_udptcp_checksum(m, l3_hdr, l4_off,
194 : : info->ethertype);
195 : : }
196 : : }
197 : : #ifdef RTE_LIB_GSO
198 : 0 : if (info->gso_enable)
199 : 0 : ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
200 : : #endif
201 : 0 : } else if (info->l4_proto == IPPROTO_TCP) {
202 : 0 : tcp_hdr = (struct rte_tcp_hdr *)((char *)l3_hdr + info->l3_len);
203 : 0 : if (tso_segsz &&
204 : 0 : (tx_offloads & (RTE_ETH_TX_OFFLOAD_TCP_TSO | all_tunnel_tso)))
205 : 0 : ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
206 : 0 : else if (tx_offloads & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {
207 : 0 : ol_flags |= RTE_MBUF_F_TX_TCP_CKSUM;
208 : : } else {
209 : 0 : if (info->is_tunnel)
210 : 0 : l4_off = info->outer_l2_len + info->outer_l3_len +
211 : 0 : info->l2_len + info->l3_len;
212 : : else
213 : 0 : l4_off = info->l2_len + info->l3_len;
214 : 0 : tcp_hdr->cksum = 0;
215 : 0 : tcp_hdr->cksum =
216 : 0 : get_udptcp_checksum(m, l3_hdr, l4_off,
217 : : info->ethertype);
218 : : }
219 : : #ifdef RTE_LIB_GSO
220 : 0 : if (info->gso_enable)
221 : 0 : ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
222 : : #endif
223 : 0 : } else if (info->l4_proto == IPPROTO_SCTP) {
224 : 0 : sctp_hdr = (struct rte_sctp_hdr *)
225 : 0 : ((char *)l3_hdr + info->l3_len);
226 : : /* sctp payload must be a multiple of 4 to be
227 : : * offloaded */
228 : 0 : if ((tx_offloads & RTE_ETH_TX_OFFLOAD_SCTP_CKSUM) &&
229 : 0 : ((ipv4_hdr->total_length & 0x3) == 0)) {
230 : 0 : ol_flags |= RTE_MBUF_F_TX_SCTP_CKSUM;
231 : : } else {
232 : 0 : sctp_hdr->cksum = 0;
233 : : /* XXX implement CRC32c, example available in
234 : : * RFC3309 */
235 : : }
236 : : }
237 : :
238 : : return ol_flags;
239 : : }
240 : :
241 : : /* Calculate the checksum of outer header */
242 : : static uint64_t
243 : 0 : process_outer_cksums(void *outer_l3_hdr, struct testpmd_offload_info *info,
244 : : uint64_t tx_offloads, int tso_enabled, struct rte_mbuf *m)
245 : : {
246 : : struct rte_udp_hdr *udp_hdr;
247 : : uint64_t ol_flags = 0;
248 : :
249 : 0 : if (info->outer_ethertype == _htons(RTE_ETHER_TYPE_IPV4)) {
250 : : ol_flags |= RTE_MBUF_F_TX_OUTER_IPV4;
251 : :
252 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) {
253 : : ol_flags |= RTE_MBUF_F_TX_OUTER_IP_CKSUM;
254 : : } else {
255 : : struct rte_ipv4_hdr *ipv4_hdr = outer_l3_hdr;
256 : :
257 : 0 : ipv4_hdr->hdr_checksum = 0;
258 : 0 : ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
259 : : }
260 : : } else {
261 : : ol_flags |= RTE_MBUF_F_TX_OUTER_IPV6;
262 : : }
263 : :
264 : 0 : if (info->outer_l4_proto != IPPROTO_UDP)
265 : : return ol_flags;
266 : :
267 : 0 : udp_hdr = (struct rte_udp_hdr *)
268 : 0 : ((char *)outer_l3_hdr + info->outer_l3_len);
269 : :
270 : 0 : if (tso_enabled && info->l4_proto == IPPROTO_TCP)
271 : 0 : ol_flags |= RTE_MBUF_F_TX_TCP_SEG;
272 : 0 : else if (tso_enabled && info->l4_proto == IPPROTO_UDP)
273 : 0 : ol_flags |= RTE_MBUF_F_TX_UDP_SEG;
274 : :
275 : : /* Skip SW outer UDP checksum generation if HW supports it */
276 : 0 : if (tx_offloads & RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) {
277 : 0 : ol_flags |= RTE_MBUF_F_TX_OUTER_UDP_CKSUM;
278 : 0 : return ol_flags;
279 : : }
280 : :
281 : : /* Outer UDP checksum is done in software.
282 : : *
283 : : * If a packet will be TSOed into small packets by NIC, we cannot
284 : : * set/calculate a non-zero checksum, because it will be a wrong
285 : : * value after the packet be split into several small packets.
286 : : */
287 : 0 : if (!tso_enabled && udp_hdr->dgram_cksum != 0) {
288 : 0 : udp_hdr->dgram_cksum = 0;
289 : 0 : udp_hdr->dgram_cksum = get_udptcp_checksum(m, outer_l3_hdr,
290 : 0 : info->outer_l2_len + info->outer_l3_len,
291 : : info->outer_ethertype);
292 : : }
293 : :
294 : : return ol_flags;
295 : : }
296 : :
297 : : /*
298 : : * Helper function.
299 : : * Performs actual copying.
300 : : * Returns number of segments in the destination mbuf on success,
301 : : * or negative error code on failure.
302 : : */
303 : : static int
304 : 0 : mbuf_copy_split(const struct rte_mbuf *ms, struct rte_mbuf *md[],
305 : : uint16_t seglen[], uint8_t nb_seg)
306 : : {
307 : : uint32_t dlen, slen, tlen;
308 : : uint32_t i, len;
309 : : const struct rte_mbuf *m;
310 : : const uint8_t *src;
311 : : uint8_t *dst;
312 : :
313 : : dlen = 0;
314 : : slen = 0;
315 : : tlen = 0;
316 : :
317 : : dst = NULL;
318 : : src = NULL;
319 : :
320 : : m = ms;
321 : : i = 0;
322 : 0 : while (ms != NULL && i != nb_seg) {
323 : :
324 : 0 : if (slen == 0) {
325 : 0 : slen = rte_pktmbuf_data_len(ms);
326 : 0 : src = rte_pktmbuf_mtod(ms, const uint8_t *);
327 : : }
328 : :
329 : 0 : if (dlen == 0) {
330 : 0 : dlen = RTE_MIN(seglen[i], slen);
331 : 0 : md[i]->data_len = dlen;
332 : 0 : md[i]->next = (i + 1 == nb_seg) ? NULL : md[i + 1];
333 : 0 : dst = rte_pktmbuf_mtod(md[i], uint8_t *);
334 : : }
335 : :
336 : 0 : len = RTE_MIN(slen, dlen);
337 : 0 : memcpy(dst, src, len);
338 : 0 : tlen += len;
339 : 0 : slen -= len;
340 : 0 : dlen -= len;
341 : 0 : src += len;
342 : 0 : dst += len;
343 : :
344 : 0 : if (slen == 0)
345 : 0 : ms = ms->next;
346 : 0 : if (dlen == 0)
347 : 0 : i++;
348 : : }
349 : :
350 : 0 : if (ms != NULL)
351 : : return -ENOBUFS;
352 : 0 : else if (tlen != m->pkt_len)
353 : : return -EINVAL;
354 : :
355 : 0 : md[0]->nb_segs = nb_seg;
356 : 0 : md[0]->pkt_len = tlen;
357 : 0 : md[0]->vlan_tci = m->vlan_tci;
358 : 0 : md[0]->vlan_tci_outer = m->vlan_tci_outer;
359 : 0 : md[0]->ol_flags = m->ol_flags;
360 : 0 : md[0]->tx_offload = m->tx_offload;
361 : :
362 : 0 : return nb_seg;
363 : : }
364 : :
365 : : /*
366 : : * Allocate a new mbuf with up to tx_pkt_nb_segs segments.
367 : : * Copy packet contents and offload information into the new segmented mbuf.
368 : : */
369 : : static struct rte_mbuf *
370 : 0 : pkt_copy_split(const struct rte_mbuf *pkt)
371 : : {
372 : : int32_t n, rc;
373 : : uint32_t i, len, nb_seg;
374 : : struct rte_mempool *mp;
375 : : uint16_t seglen[RTE_MAX_SEGS_PER_PKT];
376 : : struct rte_mbuf *p, *md[RTE_MAX_SEGS_PER_PKT];
377 : :
378 : 0 : mp = current_fwd_lcore()->mbp;
379 : :
380 : 0 : if (tx_pkt_split == TX_PKT_SPLIT_RND)
381 : 0 : nb_seg = rte_rand() % tx_pkt_nb_segs + 1;
382 : : else
383 : 0 : nb_seg = tx_pkt_nb_segs;
384 : :
385 : 0 : memcpy(seglen, tx_pkt_seg_lengths, nb_seg * sizeof(seglen[0]));
386 : :
387 : : /* calculate number of segments to use and their length. */
388 : : len = 0;
389 : 0 : for (i = 0; i != nb_seg && len < pkt->pkt_len; i++) {
390 : 0 : len += seglen[i];
391 : 0 : md[i] = NULL;
392 : : }
393 : :
394 : 0 : n = pkt->pkt_len - len;
395 : :
396 : : /* update size of the last segment to fit rest of the packet */
397 : 0 : if (n >= 0) {
398 : 0 : seglen[i - 1] += n;
399 : : len += n;
400 : : }
401 : :
402 : : nb_seg = i;
403 : 0 : while (i != 0) {
404 : 0 : p = rte_pktmbuf_alloc(mp);
405 : 0 : if (p == NULL) {
406 : 0 : TESTPMD_LOG(ERR,
407 : : "failed to allocate %u-th of %u mbuf "
408 : : "from mempool: %s\n",
409 : : nb_seg - i, nb_seg, mp->name);
410 : 0 : break;
411 : : }
412 : :
413 : 0 : md[--i] = p;
414 : 0 : if (rte_pktmbuf_tailroom(md[i]) < seglen[i]) {
415 : 0 : TESTPMD_LOG(ERR, "mempool %s, %u-th segment: "
416 : : "expected seglen: %u, "
417 : : "actual mbuf tailroom: %u\n",
418 : : mp->name, i, seglen[i],
419 : : rte_pktmbuf_tailroom(md[i]));
420 : 0 : break;
421 : : }
422 : : }
423 : :
424 : : /* all mbufs successfully allocated, do copy */
425 : 0 : if (i == 0) {
426 : 0 : rc = mbuf_copy_split(pkt, md, seglen, nb_seg);
427 : 0 : if (rc < 0)
428 : 0 : TESTPMD_LOG(ERR,
429 : : "mbuf_copy_split for %p(len=%u, nb_seg=%u) "
430 : : "into %u segments failed with error code: %d\n",
431 : : pkt, pkt->pkt_len, pkt->nb_segs, nb_seg, rc);
432 : :
433 : : /* figure out how many mbufs to free. */
434 : 0 : i = RTE_MAX(rc, 0);
435 : : }
436 : :
437 : : /* free unused mbufs */
438 : 0 : for (; i != nb_seg; i++) {
439 : 0 : rte_pktmbuf_free_seg(md[i]);
440 : 0 : md[i] = NULL;
441 : : }
442 : :
443 : 0 : return md[0];
444 : : }
445 : :
446 : : #if defined(RTE_LIB_GRO) || defined(RTE_LIB_GSO)
447 : : /*
448 : : * Re-calculate IP checksum for merged/fragmented packets.
449 : : */
450 : : static void
451 : 0 : pkts_ip_csum_recalc(struct rte_mbuf **pkts_burst, const uint16_t nb_pkts, uint64_t tx_offloads)
452 : : {
453 : : int i;
454 : : struct rte_ipv4_hdr *ipv4_hdr;
455 : 0 : for (i = 0; i < nb_pkts; i++) {
456 : 0 : if ((pkts_burst[i]->ol_flags & RTE_MBUF_F_TX_IPV4) &&
457 : 0 : (tx_offloads & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) == 0) {
458 : 0 : ipv4_hdr = rte_pktmbuf_mtod_offset(pkts_burst[i],
459 : : struct rte_ipv4_hdr *,
460 : : pkts_burst[i]->l2_len);
461 : 0 : ipv4_hdr->hdr_checksum = 0;
462 : 0 : ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
463 : : }
464 : : }
465 : 0 : }
466 : : #endif
467 : :
468 : : static uint32_t
469 : 0 : get_ethertype_by_ptype(struct rte_ether_hdr *eth_hdr, uint32_t ptype)
470 : : {
471 : : struct rte_vlan_hdr *vlan_hdr, *max_vlans;
472 : : uint16_t ethertype;
473 : :
474 : 0 : switch (ptype) {
475 : : case RTE_PTYPE_L3_IPV4:
476 : : case RTE_PTYPE_L3_IPV4_EXT:
477 : : case RTE_PTYPE_L3_IPV4_EXT_UNKNOWN:
478 : : case RTE_PTYPE_INNER_L3_IPV4:
479 : : case RTE_PTYPE_INNER_L3_IPV4_EXT:
480 : : case RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN:
481 : : return _htons(RTE_ETHER_TYPE_IPV4);
482 : 0 : case RTE_PTYPE_L3_IPV6:
483 : : case RTE_PTYPE_L3_IPV6_EXT:
484 : : case RTE_PTYPE_L3_IPV6_EXT_UNKNOWN:
485 : : case RTE_PTYPE_INNER_L3_IPV6:
486 : : case RTE_PTYPE_INNER_L3_IPV6_EXT:
487 : : case RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN:
488 : 0 : return _htons(RTE_ETHER_TYPE_IPV6);
489 : 0 : default:
490 : 0 : ethertype = eth_hdr->ether_type;
491 : 0 : vlan_hdr = RTE_PTR_ADD(eth_hdr, offsetof(struct rte_ether_hdr, ether_type));
492 : 0 : max_vlans = vlan_hdr + MAX_VLAN_HEADERS;
493 : 0 : while ((ethertype == _htons(RTE_ETHER_TYPE_VLAN) ||
494 : 0 : ethertype == _htons(RTE_ETHER_TYPE_QINQ)) &&
495 : : vlan_hdr < max_vlans) {
496 : 0 : vlan_hdr++;
497 : 0 : ethertype = vlan_hdr->eth_proto;
498 : : }
499 : 0 : return ethertype;
500 : : }
501 : : }
502 : :
503 : : static uint64_t
504 : 0 : get_tunnel_ol_flags_by_ptype(uint32_t ptype)
505 : : {
506 : 0 : switch ((ptype & RTE_PTYPE_TUNNEL_MASK)) {
507 : : case RTE_PTYPE_TUNNEL_GTPC:
508 : : case RTE_PTYPE_TUNNEL_GTPU:
509 : : return RTE_MBUF_F_TX_TUNNEL_GTP;
510 : 0 : case RTE_PTYPE_TUNNEL_VXLAN_GPE:
511 : 0 : return RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE;
512 : 0 : case RTE_PTYPE_TUNNEL_VXLAN:
513 : 0 : return RTE_MBUF_F_TX_TUNNEL_VXLAN;
514 : 0 : case RTE_PTYPE_TUNNEL_GENEVE:
515 : 0 : return RTE_MBUF_F_TX_TUNNEL_GENEVE;
516 : 0 : case RTE_PTYPE_TUNNEL_NVGRE:
517 : : case RTE_PTYPE_TUNNEL_GRE:
518 : 0 : return RTE_MBUF_F_TX_TUNNEL_GRE;
519 : 0 : case RTE_PTYPE_TUNNEL_IP:
520 : 0 : return RTE_MBUF_F_TX_TUNNEL_IPIP;
521 : 0 : default:
522 : : printf("unrecognized tunnel ptype: %x\n",
523 : : (ptype & RTE_PTYPE_TUNNEL_MASK));
524 : 0 : return 0;
525 : : }
526 : : }
527 : :
528 : : static void
529 : : parse_inner_l4_proto(void *outer_l3_hdr,
530 : : struct testpmd_offload_info *info)
531 : : {
532 : : struct rte_ipv4_hdr *ipv4_hdr = outer_l3_hdr;
533 : : struct rte_ipv6_hdr *ipv6_hdr = outer_l3_hdr;
534 : 0 : if (info->ethertype == _htons(RTE_ETHER_TYPE_IPV4))
535 : 0 : info->l4_proto = ipv4_hdr->next_proto_id;
536 : : else
537 : 0 : info->l4_proto = ipv6_hdr->proto;
538 : : }
539 : :
540 : : static uint8_t
541 : 0 : parse_l4_proto(const struct rte_mbuf *m, uint32_t off, uint32_t ptype)
542 : : {
543 : 0 : int frag = 0, ret;
544 : :
545 : 0 : if (RTE_ETH_IS_IPV4_HDR(ptype)) {
546 : : const struct rte_ipv4_hdr *ip4h;
547 : : struct rte_ipv4_hdr ip4h_copy;
548 : 0 : ip4h = rte_pktmbuf_read(m, off, sizeof(*ip4h), &ip4h_copy);
549 : 0 : if (unlikely(ip4h == NULL))
550 : : return 0;
551 : :
552 : 0 : return ip4h->next_proto_id;
553 : 0 : } else if (RTE_ETH_IS_IPV6_HDR(ptype)) {
554 : : const struct rte_ipv6_hdr *ip6h;
555 : : struct rte_ipv6_hdr ip6h_copy;
556 : 0 : ip6h = rte_pktmbuf_read(m, off, sizeof(*ip6h), &ip6h_copy);
557 : 0 : if (unlikely(ip6h == NULL))
558 : : return 0;
559 : :
560 : 0 : if ((ptype & RTE_PTYPE_INNER_L3_MASK) ==
561 : : RTE_PTYPE_INNER_L3_IPV6_EXT) {
562 : 0 : ret = rte_net_skip_ip6_ext(ip6h->proto, m, &off, &frag);
563 : 0 : if (ret < 0)
564 : : return 0;
565 : 0 : return ret;
566 : : }
567 : :
568 : 0 : return ip6h->proto;
569 : : }
570 : : return 0;
571 : : }
572 : :
573 : : /*
574 : : * Receive a burst of packets, and for each packet:
575 : : * - parse packet, and try to recognize a supported packet type (1)
576 : : * - if it's not a supported packet type, don't touch the packet, else:
577 : : * - reprocess the checksum of all supported layers. This is done in SW
578 : : * or HW, depending on testpmd command line configuration
579 : : * - if TSO is enabled in testpmd command line, also flag the mbuf for TCP
580 : : * segmentation offload (this implies HW TCP checksum)
581 : : * Then transmit packets on the output port.
582 : : *
583 : : * (1) Supported packets are:
584 : : * Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
585 : : * Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
586 : : * UDP|TCP|SCTP
587 : : * Ether / (vlan) / outer IP|IP6 / outer UDP / VXLAN-GPE / Ether / IP|IP6 /
588 : : * UDP|TCP|SCTP
589 : : * Ether / (vlan) / outer IP|IP6 / outer UDP / VXLAN-GPE / IP|IP6 /
590 : : * UDP|TCP|SCTP
591 : : * Ether / (vlan) / outer IP / outer UDP / GTP / IP|IP6 / UDP|TCP|SCTP
592 : : * Ether / (vlan) / outer IP|IP6 / GRE / Ether / IP|IP6 / UDP|TCP|SCTP
593 : : * Ether / (vlan) / outer IP|IP6 / GRE / IP|IP6 / UDP|TCP|SCTP
594 : : * Ether / (vlan) / outer IP|IP6 / IP|IP6 / UDP|TCP|SCTP
595 : : *
596 : : * The testpmd command line for this forward engine sets the flags
597 : : * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
598 : : * whether a checksum must be calculated in software or in hardware. The
599 : : * IP, UDP, TCP and SCTP flags always concern the inner layer. The
600 : : * OUTER_IP is only useful for tunnel packets.
601 : : */
602 : : static bool
603 : 0 : pkt_burst_checksum_forward(struct fwd_stream *fs)
604 : : {
605 : : struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
606 : : #ifdef RTE_LIB_GSO
607 : : struct rte_mbuf *gso_segments[GSO_MAX_PKT_BURST];
608 : : struct rte_gso_ctx *gso_ctx;
609 : : #endif
610 : : struct rte_mbuf **tx_pkts_burst;
611 : : struct rte_port *txp;
612 : : struct rte_mbuf *m, *p;
613 : : struct rte_ether_hdr *eth_hdr;
614 : : void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
615 : : #ifdef RTE_LIB_GRO
616 : : void **gro_ctx;
617 : : uint16_t gro_pkts_num;
618 : : uint8_t gro_enable;
619 : : #endif
620 : : uint16_t nb_rx;
621 : : uint16_t nb_prep;
622 : : uint16_t i;
623 : : uint64_t rx_ol_flags, tx_ol_flags;
624 : : uint64_t tx_offloads;
625 : : uint32_t rx_bad_ip_csum;
626 : : uint32_t rx_bad_l4_csum;
627 : : uint32_t rx_bad_outer_l4_csum;
628 : : uint32_t rx_bad_outer_ip_csum;
629 : : struct testpmd_offload_info info;
630 : 0 : struct rte_net_hdr_lens hdr_lens = {0};
631 : : uint32_t ptype;
632 : :
633 : : /* receive a burst of packet */
634 : 0 : nb_rx = common_fwd_stream_receive(fs, pkts_burst, nb_pkt_per_burst);
635 : 0 : if (unlikely(nb_rx == 0)) {
636 : : #ifndef RTE_LIB_GRO
637 : : return false;
638 : : #else
639 : 0 : gro_enable = gro_ports[fs->rx_port].enable;
640 : : /*
641 : : * Check if packets need to be flushed in the GRO context
642 : : * due to a timeout.
643 : : *
644 : : * Continue only in GRO heavyweight mode and if there are
645 : : * packets in the GRO context.
646 : : */
647 : 0 : if (!gro_enable || (gro_flush_cycles == GRO_DEFAULT_FLUSH_CYCLES) ||
648 : 0 : (rte_gro_get_pkt_count(current_fwd_lcore()->gro_ctx) == 0))
649 : 0 : return false;
650 : : #endif
651 : : }
652 : :
653 : : rx_bad_ip_csum = 0;
654 : : rx_bad_l4_csum = 0;
655 : : rx_bad_outer_l4_csum = 0;
656 : : rx_bad_outer_ip_csum = 0;
657 : :
658 : 0 : txp = &ports[fs->tx_port];
659 : 0 : tx_offloads = txp->dev_conf.txmode.offloads;
660 : : memset(&info, 0, sizeof(info));
661 : 0 : info.tso_segsz = txp->tso_segsz;
662 : 0 : info.tunnel_tso_segsz = txp->tunnel_tso_segsz;
663 : : #ifdef RTE_LIB_GSO
664 : 0 : if (gso_ports[fs->tx_port].enable)
665 : 0 : info.gso_enable = 1;
666 : : #endif
667 : :
668 : 0 : for (i = 0; i < nb_rx; i++) {
669 : 0 : if (likely(i < nb_rx - 1))
670 : 0 : rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[i + 1],
671 : : void *));
672 : :
673 : 0 : m = pkts_burst[i];
674 : 0 : info.is_tunnel = 0;
675 : 0 : info.pkt_len = rte_pktmbuf_pkt_len(m);
676 : 0 : tx_ol_flags = m->ol_flags &
677 : : (RTE_MBUF_F_INDIRECT | RTE_MBUF_F_EXTERNAL);
678 : : rx_ol_flags = m->ol_flags;
679 : :
680 : : /* Update the L3/L4 checksum error packet statistics */
681 : 0 : if ((rx_ol_flags & RTE_MBUF_F_RX_IP_CKSUM_MASK) == RTE_MBUF_F_RX_IP_CKSUM_BAD)
682 : 0 : rx_bad_ip_csum += 1;
683 : 0 : if ((rx_ol_flags & RTE_MBUF_F_RX_L4_CKSUM_MASK) == RTE_MBUF_F_RX_L4_CKSUM_BAD)
684 : 0 : rx_bad_l4_csum += 1;
685 : 0 : if (rx_ol_flags & RTE_MBUF_F_RX_OUTER_L4_CKSUM_BAD)
686 : 0 : rx_bad_outer_l4_csum += 1;
687 : 0 : if (rx_ol_flags & RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD)
688 : 0 : rx_bad_outer_ip_csum += 1;
689 : :
690 : : /* step 1: dissect packet, parsing optional vlan, ip4/ip6, vxlan
691 : : * and inner headers */
692 : :
693 : 0 : eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
694 : 0 : if (ports[fs->tx_port].fwd_mac_swap) {
695 : 0 : rte_ether_addr_copy(&peer_eth_addrs[fs->peer_addr],
696 : : ð_hdr->dst_addr);
697 : : rte_ether_addr_copy(&ports[fs->tx_port].eth_addr,
698 : : ð_hdr->src_addr);
699 : : }
700 : :
701 : 0 : ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
702 : 0 : info.l2_len = hdr_lens.l2_len;
703 : 0 : info.l3_len = hdr_lens.l3_len;
704 : 0 : info.l4_len = hdr_lens.l4_len;
705 : 0 : info.ethertype = get_ethertype_by_ptype(eth_hdr,
706 : : ptype & RTE_PTYPE_L3_MASK);
707 : 0 : info.l4_proto = parse_l4_proto(m, info.l2_len, ptype);
708 : :
709 : 0 : l3_hdr = (char *)eth_hdr + info.l2_len;
710 : : /* check if it's a supported tunnel */
711 : 0 : if (txp->parse_tunnel && RTE_ETH_IS_TUNNEL_PKT(ptype) != 0) {
712 : 0 : info.is_tunnel = 1;
713 : : update_tunnel_outer(&info);
714 : 0 : info.l2_len = hdr_lens.inner_l2_len + hdr_lens.tunnel_len;
715 : 0 : info.l3_len = hdr_lens.inner_l3_len;
716 : 0 : info.l4_len = hdr_lens.inner_l4_len;
717 : 0 : eth_hdr = (struct rte_ether_hdr *)(char *)l3_hdr +
718 : 0 : info.outer_l3_len + hdr_lens.tunnel_len;
719 : 0 : info.ethertype = get_ethertype_by_ptype(eth_hdr,
720 : : ptype & RTE_PTYPE_INNER_L3_MASK);
721 : 0 : tx_ol_flags |= get_tunnel_ol_flags_by_ptype(ptype);
722 : : }
723 : : /* update l3_hdr and outer_l3_hdr if a tunnel was parsed */
724 : 0 : if (info.is_tunnel) {
725 : : outer_l3_hdr = l3_hdr;
726 : 0 : l3_hdr = (char *)l3_hdr + info.outer_l3_len + info.l2_len;
727 : : parse_inner_l4_proto(l3_hdr, &info);
728 : : }
729 : : /* step 2: depending on user command line configuration,
730 : : * recompute checksum either in software or flag the
731 : : * mbuf to offload the calculation to the NIC. If TSO
732 : : * is configured, prepare the mbuf for TCP segmentation. */
733 : :
734 : : /* process checksums of inner headers first */
735 : 0 : tx_ol_flags |= process_inner_cksums(l3_hdr, &info,
736 : : tx_offloads, m);
737 : :
738 : : /* Then process outer headers if any. Note that the software
739 : : * checksum will be wrong if one of the inner checksums is
740 : : * processed in hardware. */
741 : 0 : if (info.is_tunnel == 1) {
742 : 0 : tx_ol_flags |= process_outer_cksums(outer_l3_hdr, &info,
743 : : tx_offloads,
744 : 0 : !!(tx_ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
745 : : RTE_MBUF_F_TX_UDP_SEG)),
746 : : m);
747 : : }
748 : :
749 : : /* step 3: fill the mbuf meta data (flags and header lengths) */
750 : :
751 : 0 : m->tx_offload = 0;
752 : 0 : if (info.is_tunnel == 1) {
753 : 0 : if (info.tunnel_tso_segsz ||
754 : : (tx_offloads &
755 : 0 : RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) ||
756 : : (tx_offloads &
757 : : RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM)) {
758 : 0 : m->outer_l2_len = info.outer_l2_len;
759 : 0 : m->outer_l3_len = info.outer_l3_len;
760 : 0 : m->l2_len = info.l2_len;
761 : 0 : m->l3_len = info.l3_len;
762 : 0 : m->l4_len = info.l4_len;
763 : 0 : m->tso_segsz = info.tunnel_tso_segsz;
764 : : }
765 : : else {
766 : : /* if there is a outer UDP cksum
767 : : processed in sw and the inner in hw,
768 : : the outer checksum will be wrong as
769 : : the payload will be modified by the
770 : : hardware */
771 : 0 : m->l2_len = info.outer_l2_len +
772 : 0 : info.outer_l3_len + info.l2_len;
773 : 0 : m->l3_len = info.l3_len;
774 : 0 : m->l4_len = info.l4_len;
775 : : }
776 : : } else {
777 : : /* this is only useful if an offload flag is
778 : : * set, but it does not hurt to fill it in any
779 : : * case */
780 : 0 : m->l2_len = info.l2_len;
781 : 0 : m->l3_len = info.l3_len;
782 : 0 : m->l4_len = info.l4_len;
783 : 0 : m->tso_segsz = info.tso_segsz;
784 : : }
785 : 0 : m->ol_flags = tx_ol_flags;
786 : :
787 : : /* Do split & copy for the packet. */
788 : 0 : if (tx_pkt_split != TX_PKT_SPLIT_OFF) {
789 : 0 : p = pkt_copy_split(m);
790 : 0 : if (p != NULL) {
791 : 0 : rte_pktmbuf_free(m);
792 : : m = p;
793 : 0 : pkts_burst[i] = m;
794 : : }
795 : : }
796 : :
797 : : /* if verbose mode is enabled, dump debug info */
798 : 0 : if (verbose_level > 0) {
799 : : char buf[256];
800 : :
801 : : printf("-----------------\n");
802 : 0 : printf("port=%u, mbuf=%p, pkt_len=%u, nb_segs=%u:\n",
803 : 0 : fs->rx_port, m, m->pkt_len, m->nb_segs);
804 : : /* dump rx parsed packet info */
805 : 0 : rte_get_rx_ol_flag_list(rx_ol_flags, buf, sizeof(buf));
806 : 0 : printf("rx: l2_len=%d ethertype=%x l3_len=%d "
807 : : "l4_proto=%d l4_len=%d flags=%s\n",
808 : 0 : info.l2_len, rte_be_to_cpu_16(info.ethertype),
809 : 0 : info.l3_len, info.l4_proto, info.l4_len, buf);
810 : 0 : if (rx_ol_flags & RTE_MBUF_F_RX_LRO)
811 : 0 : printf("rx: m->lro_segsz=%u\n", m->tso_segsz);
812 : 0 : if (info.is_tunnel == 1)
813 : 0 : printf("rx: outer_l2_len=%d outer_ethertype=%x "
814 : 0 : "outer_l3_len=%d\n", info.outer_l2_len,
815 : 0 : rte_be_to_cpu_16(info.outer_ethertype),
816 : 0 : info.outer_l3_len);
817 : : /* dump tx packet info */
818 : 0 : if ((tx_offloads & (RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
819 : : RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
820 : : RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
821 : 0 : RTE_ETH_TX_OFFLOAD_SCTP_CKSUM)) ||
822 : 0 : info.tso_segsz != 0)
823 : 0 : printf("tx: m->l2_len=%d m->l3_len=%d "
824 : : "m->l4_len=%d\n",
825 : 0 : m->l2_len, m->l3_len, m->l4_len);
826 : 0 : if (info.is_tunnel == 1) {
827 : 0 : if ((tx_offloads &
828 : 0 : RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) ||
829 : : (tx_offloads &
830 : 0 : RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) ||
831 : 0 : (tx_ol_flags & RTE_MBUF_F_TX_OUTER_IPV6))
832 : 0 : printf("tx: m->outer_l2_len=%d "
833 : : "m->outer_l3_len=%d\n",
834 : 0 : m->outer_l2_len,
835 : 0 : m->outer_l3_len);
836 : 0 : if (info.tunnel_tso_segsz != 0 &&
837 : 0 : (m->ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
838 : : RTE_MBUF_F_TX_UDP_SEG)))
839 : 0 : printf("tx: m->tso_segsz=%d\n",
840 : 0 : m->tso_segsz);
841 : 0 : } else if (info.tso_segsz != 0 &&
842 : 0 : (m->ol_flags & (RTE_MBUF_F_TX_TCP_SEG |
843 : : RTE_MBUF_F_TX_UDP_SEG)))
844 : 0 : printf("tx: m->tso_segsz=%d\n", m->tso_segsz);
845 : 0 : rte_get_tx_ol_flag_list(m->ol_flags, buf, sizeof(buf));
846 : : printf("tx: flags=%s", buf);
847 : : printf("\n");
848 : : }
849 : : }
850 : :
851 : : #ifdef RTE_LIB_GRO
852 : 0 : gro_enable = gro_ports[fs->rx_port].enable;
853 : 0 : if (unlikely(gro_enable)) {
854 : 0 : if (gro_flush_cycles == GRO_DEFAULT_FLUSH_CYCLES) {
855 : 0 : nb_rx = rte_gro_reassemble_burst(pkts_burst, nb_rx,
856 : 0 : &(gro_ports[fs->rx_port].param));
857 : : } else {
858 : 0 : gro_ctx = current_fwd_lcore()->gro_ctx;
859 : 0 : nb_rx = rte_gro_reassemble(pkts_burst, nb_rx, gro_ctx);
860 : :
861 : 0 : if (++fs->gro_times >= gro_flush_cycles) {
862 : 0 : gro_pkts_num = rte_gro_get_pkt_count(gro_ctx);
863 : 0 : if (gro_pkts_num > MAX_PKT_BURST - nb_rx)
864 : 0 : gro_pkts_num = MAX_PKT_BURST - nb_rx;
865 : :
866 : 0 : nb_rx += rte_gro_timeout_flush(gro_ctx, 0,
867 : : RTE_GRO_TCP_IPV4,
868 : : &pkts_burst[nb_rx],
869 : : gro_pkts_num);
870 : 0 : fs->gro_times = 0;
871 : : }
872 : 0 : if (nb_rx == 0)
873 : : return false;
874 : : }
875 : :
876 : 0 : pkts_ip_csum_recalc(pkts_burst, nb_rx, tx_offloads);
877 : : }
878 : : #endif
879 : :
880 : : #ifdef RTE_LIB_GSO
881 : 0 : if (gso_ports[fs->tx_port].enable != 0) {
882 : : uint16_t nb_segments = 0;
883 : :
884 : 0 : gso_ctx = &(current_fwd_lcore()->gso_ctx);
885 : 0 : gso_ctx->gso_size = gso_max_segment_size;
886 : 0 : for (i = 0; i < nb_rx; i++) {
887 : : int ret;
888 : :
889 : 0 : ret = rte_gso_segment(pkts_burst[i], gso_ctx,
890 : 0 : &gso_segments[nb_segments],
891 : 0 : GSO_MAX_PKT_BURST - nb_segments);
892 : 0 : if (ret >= 1) {
893 : : /* pkts_burst[i] can be freed safely here. */
894 : 0 : rte_pktmbuf_free(pkts_burst[i]);
895 : 0 : nb_segments += ret;
896 : 0 : } else if (ret == 0) {
897 : : /* 0 means it can be transmitted directly
898 : : * without gso.
899 : : */
900 : 0 : gso_segments[nb_segments] = pkts_burst[i];
901 : 0 : nb_segments += 1;
902 : : } else {
903 : 0 : TESTPMD_LOG(DEBUG, "Unable to segment packet");
904 : 0 : rte_pktmbuf_free(pkts_burst[i]);
905 : : }
906 : : }
907 : :
908 : : tx_pkts_burst = gso_segments;
909 : : nb_rx = nb_segments;
910 : :
911 : 0 : pkts_ip_csum_recalc(tx_pkts_burst, nb_rx, tx_offloads);
912 : : } else
913 : : #endif
914 : : tx_pkts_burst = pkts_burst;
915 : :
916 : 0 : nb_prep = rte_eth_tx_prepare(fs->tx_port, fs->tx_queue,
917 : : tx_pkts_burst, nb_rx);
918 : 0 : if (nb_prep != nb_rx) {
919 : 0 : fprintf(stderr,
920 : : "Preparing packet burst to transmit failed: %s\n",
921 : : rte_strerror(rte_errno));
922 : 0 : fs->fwd_dropped += (nb_rx - nb_prep);
923 : 0 : rte_pktmbuf_free_bulk(&tx_pkts_burst[nb_prep], nb_rx - nb_prep);
924 : : }
925 : :
926 : 0 : common_fwd_stream_transmit(fs, tx_pkts_burst, nb_prep);
927 : :
928 : 0 : fs->rx_bad_ip_csum += rx_bad_ip_csum;
929 : 0 : fs->rx_bad_l4_csum += rx_bad_l4_csum;
930 : 0 : fs->rx_bad_outer_l4_csum += rx_bad_outer_l4_csum;
931 : 0 : fs->rx_bad_outer_ip_csum += rx_bad_outer_ip_csum;
932 : :
933 : 0 : return true;
934 : : }
935 : :
936 : : struct fwd_engine csum_fwd_engine = {
937 : : .fwd_mode_name = "csum",
938 : : .stream_init = common_fwd_stream_init,
939 : : .packet_fwd = pkt_burst_checksum_forward,
940 : : };
|