LCOV - code coverage report
Current view: top level - root/contrail/vrouter/dpdk - vr_dpdk_gso.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 37 193 19.2 %
Date: 2026-08-03 02:19:58 Functions: 2 11 18.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2016 Juniper Networks.
       3             :  *
       4             :  * This program is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU General Public License as
       6             :  * published by the Free Software Foundation version 2.
       7             :  *
       8             :  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
       9             :  * kind, whether express or implied; without even the implied warranty
      10             :  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      11             :  * GNU General Public License for more details.
      12             :  *
      13             :  * vr_dpdk_gso.c -- TCP Offloads on the sender
      14             :  *
      15             :  * This is an adaptation of FreeBSD's patch for gso
      16             :  * Copyright (C) 2014, Stefano Garzarella - Universita` di Pisa
      17             :  * All rights reserved.
      18             :  * BSD LICENSE
      19             :  *
      20             :  *
      21             :  */
      22             : 
      23             : #include "vr_dpdk.h"
      24             : #include "vr_dpdk_netlink.h"
      25             : #include "vr_dpdk_usocket.h"
      26             : #include "vr_dpdk_virtio.h"
      27             : #include "vr_dpdk_gro.h"
      28             : #include "vr_packet.h"
      29             : #include "vr_datapath.h"
      30             : 
      31             : #include <netinet/ip.h>
      32             : #include <netinet/tcp.h>
      33             : 
      34             : #include <rte_errno.h>
      35             : #include <rte_ethdev.h>
      36             : #include <rte_ip_frag.h>
      37             : #include <rte_ip.h>
      38             : #include <rte_port_ethdev.h>
      39             : #include <rte_hash.h>
      40             : #include <rte_cycles.h>
      41             : #include <rte_malloc.h>
      42             : #include <rte_tcp.h>
      43             : #include <rte_eth_bond.h>
      44             : 
      45             : /*
      46             :  * Structure that contains the state during the TCP segmentation
      47             :  */
      48             : struct dpdk_gso_state {
      49             :    void    (*update)
      50             :        (struct rte_mbuf*, struct dpdk_gso_state*);
      51             :    void    (*internal)
      52             :        (struct rte_mbuf*, struct dpdk_gso_state*);
      53             :    union {
      54             :        struct rte_ipv4_hdr *ip;
      55             :        struct rte_ipv6_hdr *ip6;
      56             :    };
      57             :    struct rte_tcp_hdr *tcp;
      58             :    int mac_hlen;
      59             :    int ip_hlen;
      60             :    int tcp_hlen;
      61             :    int hlen;
      62             :    int pay_len;
      63             :    uint32_t tcp_seq;
      64             :    uint16_t ip_id;
      65             :    uint16_t ip_off;
      66             :    int tx_cksum_offload;
      67             : };
      68             : 
      69             : /**
      70             :  * Process the IPv6 UDP or TCP checksum in a **chained** mbuf.
      71             :  *
      72             :  * Layer 4 checksum must be set to 0 in the packet by the caller.
      73             :  *
      74             :  * @param ipv6_hdr
      75             :  *   The pointer to the contiguous IPv4 header.
      76             :  * @param l4_hdr
      77             :  *   The pointer to the beginning of the L4 header.
      78             :  * @return
      79             :  *   The complemented checksum to set in the IP packet.
      80             :  */
      81             : inline uint16_t
      82           8 : dpdk_ipv6_udptcp_cksum(struct rte_mbuf *m, const struct rte_ipv6_hdr *ipv6_hdr,
      83             :                                                                     uint8_t *l4_hdr)
      84             : {
      85           8 :     uint32_t cksum = 0;
      86             :     uint32_t l4_len;
      87           8 :     uint32_t data_len = 0, rem_len = 0;
      88           8 :     uint8_t *data_ptr = NULL;
      89             : 
      90           8 :     l4_len = rte_be_to_cpu_16(ipv6_hdr->payload_len);
      91             : 
      92             :     do {
      93           8 :         data_ptr = likely(!!data_ptr)? rte_pktmbuf_mtod(m, uint8_t*):l4_hdr;
      94           8 :         data_len = likely(!!data_len)? rte_pktmbuf_data_len(m):
      95           8 :                    rte_pktmbuf_mtod(m, uint8_t*) + rte_pktmbuf_data_len(m) - l4_hdr ;
      96           8 :         if (rem_len + data_len > l4_len)
      97           0 :             data_len = l4_len - rem_len;
      98           8 :         cksum += rte_raw_cksum(data_ptr, data_len);
      99           8 :         rem_len += data_len;
     100           8 :         m = m->next;
     101           8 :     } while (m && rem_len < l4_len);
     102             : 
     103           8 :     cksum += rte_ipv6_phdr_cksum(ipv6_hdr, 0);
     104           8 :     cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
     105           8 :     cksum = (~cksum) & 0xffff;
     106           8 :     if (cksum == 0)
     107           0 :         cksum = 0xffff;
     108             : 
     109           8 :     return cksum;
     110             : }
     111             : 
     112             : /**
     113             :  * Process the IPv4 UDP or TCP checksum in a **chained** mbuf.
     114             :  *
     115             :  * The IPv4 header should not contains options. The IP and layer 4
     116             :  * checksum must be set to 0 in the packet by the caller.
     117             :  *
     118             :  * @param ipv4_hdr
     119             :  *   The pointer to the contiguous IPv4 header.
     120             :  * @param l4_hdr
     121             :  *   The pointer to the beginning of the L4 header.
     122             :  * @return
     123             :  *   The complemented checksum to set in the IP packet.
     124             :  */
     125             : inline uint16_t
     126          36 : dpdk_ipv4_udptcp_cksum(struct rte_mbuf *m,
     127             :                        const struct rte_ipv4_hdr *ipv4_hdr,
     128             :                        uint8_t *l4_hdr)
     129             : {
     130          36 :     uint32_t cksum = 0;
     131             :     uint32_t l4_len;
     132          36 :     uint32_t data_len = 0, rem_len = 0;
     133          36 :     uint8_t *data_ptr = NULL;
     134             : 
     135          36 :     l4_len = rte_be_to_cpu_16(ipv4_hdr->total_length) -
     136          36 :         ((ipv4_hdr->version_ihl & 0xf) << 2);
     137             : 
     138             :     do {
     139          40 :         data_ptr = likely(!!data_ptr)? rte_pktmbuf_mtod(m, uint8_t*):l4_hdr;
     140          40 :         data_len = likely(!!data_len)? rte_pktmbuf_data_len(m):
     141          36 :                    rte_pktmbuf_mtod(m, uint8_t*) + rte_pktmbuf_data_len(m) - l4_hdr ;
     142          40 :         if (rem_len + data_len > l4_len)
     143           0 :             data_len = l4_len - rem_len;
     144          40 :         cksum += rte_raw_cksum(data_ptr, data_len);
     145          40 :         rem_len += data_len;
     146          40 :         m = m->next;
     147          40 :     } while (m && rem_len < l4_len);
     148             : 
     149          36 :     cksum += rte_ipv4_phdr_cksum(ipv4_hdr, 0);
     150          36 :     cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
     151          36 :     cksum = (~cksum) & 0xffff;
     152          36 :     if (cksum == 0)
     153           0 :         cksum = 0xffff;
     154             : 
     155          36 :     return cksum;
     156             : }
     157             : 
     158             : /* Split chained mbuf to individual mbufs and save the pointers in pkts_out */
     159             : static int32_t
     160           0 : dpdk_split_chained_mbuf(struct rte_mbuf *pkt_in, struct rte_mbuf **pkts_out,
     161             :                uint16_t nb_pkts_out, uint16_t hdr_len, uint16_t frag_size)
     162             : {
     163           0 :     struct rte_mbuf *curr_pkt = pkt_in, *next_pkt;
     164           0 :     uint32_t nb_segs = 0, total_segs = pkt_in->nb_segs, i = 0, j;
     165           0 :     char *in_hdr = rte_pktmbuf_mtod(pkt_in, char*);
     166             :     char *pkt_addr;
     167             : 
     168           0 :     while (curr_pkt) {
     169           0 :         next_pkt = curr_pkt->next;
     170           0 :         curr_pkt->next = NULL;
     171           0 :         curr_pkt->nb_segs = 1;
     172           0 :         curr_pkt->pkt_len = curr_pkt->data_len;
     173             :         /* 1st mbuf in the chain already has all the headers */
     174           0 :         if (likely(nb_segs > 0)) {
     175           0 :             pkt_addr = rte_pktmbuf_prepend(curr_pkt, hdr_len);
     176           0 :             if (unlikely(pkt_addr == NULL)) {
     177             :                 /* pkts_out[0] will be freed in the caller
     178             :                  * when -1 is returned
     179             :                  */
     180           0 :                 for(j=1;j<i;j++)
     181             :                     /* We cannot use dpdk_pfree() since the
     182             :                      * fragments may not have vr_packet info
     183             :                      */
     184           0 :                     rte_pktmbuf_free(pkts_out[j]);
     185           0 :                 rte_pktmbuf_free(curr_pkt);
     186           0 :                 rte_pktmbuf_free(next_pkt);
     187           0 :                 return -1;
     188             :             }
     189           0 :             rte_memcpy(pkt_addr, in_hdr, hdr_len);
     190             :         }
     191           0 :         pkts_out[nb_segs++] = curr_pkt;
     192           0 :         if ((nb_segs >= nb_pkts_out) || (i >= total_segs)) {
     193             :             /* pkts_out[0] will be freed in the caller
     194             :              * when -1 is returned
     195             :              */
     196           0 :             for(j=1;j<(i+1);j++)
     197             :                 /* We cannot use dpdk_pfree() since the
     198             :                  * fragments may not have vr_packet info
     199             :                  */
     200           0 :                 rte_pktmbuf_free(pkts_out[j]);
     201           0 :             rte_pktmbuf_free(next_pkt);
     202           0 :             return -1;
     203             :         }
     204           0 :         curr_pkt = next_pkt;
     205           0 :         i++;
     206             :     }
     207           0 :     return nb_segs;
     208             : }
     209             : 
     210             : /*
     211             :  * Updates the pointers to TCP and IPv6 headers
     212             :  */
     213             : static inline void
     214           0 : dpdk_gso_ipv6_tcp_update(struct rte_mbuf *m, struct dpdk_gso_state *state)
     215             : {
     216           0 :    state->ip6 = (struct rte_ipv6_hdr *)(rte_pktmbuf_mtod(m, uint8_t *) + state->mac_hlen);
     217           0 :    state->tcp = (struct rte_tcp_hdr *)((uint8_t*)(state->ip6) + state->ip_hlen);
     218           0 :    state->pay_len = m->pkt_len - state->hlen;
     219           0 : }
     220             : 
     221             : /*
     222             :  * Update the pointers to TCP and IPv4 headers
     223             :  */
     224             : static inline void
     225           0 : dpdk_gso_ipv4_tcp_update(struct rte_mbuf *m, struct dpdk_gso_state *state)
     226             : {
     227           0 :    state->ip = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(m, uint8_t *) + state->mac_hlen);
     228           0 :    state->tcp = (struct rte_tcp_hdr *)((uint8_t*)(state->ip) + state->ip_hlen);
     229           0 :    state->pay_len = m->pkt_len - state->hlen;
     230           0 : }
     231             : 
     232             : /*
     233             :  * Sets properly the TCP and IPv6 headers
     234             :  */
     235             : static inline void
     236           0 : dpdk_gso_ipv6_tcp_internal(struct rte_mbuf *m, struct dpdk_gso_state *state)
     237             : {
     238           0 :    state->ip6->payload_len = rte_cpu_to_be_16(m->pkt_len -
     239             :                    state->mac_hlen - state->ip_hlen);
     240             : 
     241             :     /* TCP Sequence number */
     242           0 :    state->tcp->sent_seq = htonl(state->tcp_seq);
     243             : 
     244           0 :    if (state->tx_cksum_offload) {
     245           0 :        m->l2_len = state->mac_hlen;
     246           0 :        m->l3_len = state->ip_hlen;
     247           0 :        state->tcp->cksum = 0;
     248           0 :        state->tcp->cksum = rte_ipv6_phdr_cksum((struct rte_ipv6_hdr *)state->ip6,
     249             :                                                                      m->ol_flags);
     250           0 :        m->ol_flags |= PKT_TX_IPV6;
     251           0 :        m->ol_flags |= PKT_TX_TCP_CKSUM;
     252             :    } else {
     253             :        /* TCP Checksum */
     254           0 :        state->tcp->cksum = 0;
     255           0 :        state->tcp->cksum = rte_ipv6_udptcp_cksum((struct rte_ipv6_hdr *)state->ip6,
     256           0 :                                                             (uint8_t*)state->tcp);
     257             :    }
     258           0 :    state->tcp_seq += state->pay_len;
     259           0 : }
     260             : 
     261             : /*
     262             :  * Set properly the TCP and IPv4 headers
     263             :  */
     264             : static inline void
     265           0 : dpdk_gso_ipv4_tcp_internal(struct rte_mbuf *m, struct dpdk_gso_state *state)
     266             : {
     267             :     /* Update IP header */
     268           0 :    state->ip->packet_id = rte_cpu_to_be_16((state->ip_id)++);
     269           0 :    state->ip->total_length = rte_cpu_to_be_16(m->pkt_len - state->mac_hlen);
     270             : 
     271             :     /* TCP Sequence number */
     272           0 :    state->tcp->sent_seq = htonl(state->tcp_seq);
     273             : 
     274           0 :    if (state->tx_cksum_offload) {
     275           0 :        m->l2_len = state->mac_hlen;
     276           0 :        m->l3_len = state->ip_hlen;
     277           0 :        state->ip->hdr_checksum = 0;
     278           0 :        state->tcp->cksum = 0;
     279           0 :        state->tcp->cksum = rte_ipv4_phdr_cksum((struct rte_ipv4_hdr *)state->ip,
     280             :                                                                      m->ol_flags);
     281           0 :        m->ol_flags |= PKT_TX_IP_CKSUM | PKT_TX_IPV4;
     282           0 :        m->ol_flags |= PKT_TX_TCP_CKSUM;
     283             :    } else {
     284             :        /* TCP Checksum */
     285           0 :        state->tcp->cksum = 0;
     286           0 :        state->tcp->cksum = rte_ipv4_udptcp_cksum((struct rte_ipv4_hdr *)state->ip,
     287           0 :                                                             (uint8_t*)state->tcp);
     288             :        /* IP Checksum */
     289           0 :        state->ip->hdr_checksum = 0;
     290           0 :        state->ip->hdr_checksum = rte_ipv4_cksum(state->ip);
     291             :    }
     292           0 :    state->tcp_seq += state->pay_len;
     293           0 : }
     294             : 
     295             : /*
     296             :  * Init the state during the TCP segmentation
     297             :  */
     298             : static int
     299           0 : dpdk_gso_init_state(struct dpdk_gso_state *state,
     300             :                    struct rte_mbuf *m, int mac_hlen, int isipv6, int hw_cksum)
     301             : {
     302             :    uint32_t ip_hlen;
     303             : 
     304           0 :    if (isipv6) {
     305           0 :        ip_hlen = sizeof(struct rte_ipv6_hdr);
     306           0 :        state->ip6 = (struct rte_ipv6_hdr *)(rte_pktmbuf_mtod(m, uint8_t *) + mac_hlen);
     307           0 :        if (state->ip6->proto != VR_IP_PROTO_TCP)
     308           0 :            return -1;
     309           0 :        state->tcp = (struct rte_tcp_hdr *)((uint8_t*)(state->ip6) + ip_hlen);
     310           0 :        state->update = dpdk_gso_ipv6_tcp_update;
     311           0 :        state->internal = dpdk_gso_ipv6_tcp_internal;
     312             :    } else {
     313           0 :        state->ip = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(m, uint8_t *) + mac_hlen);
     314           0 :        state->ip_id = rte_be_to_cpu_16(state->ip->packet_id);
     315           0 :        ip_hlen = ((state->ip->version_ihl) & 0xf) << 2;
     316           0 :        if (state->ip->next_proto_id != VR_IP_PROTO_TCP)
     317           0 :            return -1;
     318           0 :        state->tcp = (struct rte_tcp_hdr *)((uint8_t*)state->ip + ip_hlen);
     319           0 :        state->update = dpdk_gso_ipv4_tcp_update;
     320           0 :        state->internal = dpdk_gso_ipv4_tcp_internal;
     321             :    }
     322             : 
     323           0 :    state->tx_cksum_offload = hw_cksum;
     324           0 :    state->mac_hlen = mac_hlen;
     325           0 :    state->ip_hlen = ip_hlen;
     326           0 :    state->tcp_hlen = ((state->tcp->data_off & 0xf0) >> 4) << 2;
     327           0 :    state->hlen = mac_hlen + ip_hlen + state->tcp_hlen;
     328           0 :    state->tcp_seq = ntohl(state->tcp->sent_seq);
     329           0 :    return 0;
     330             : }
     331             : 
     332             : /*
     333             :  * GSO on TCP/IP (v4 or v6)
     334             :  */
     335             : static int
     336           0 : dpdk_gso_segment_ip_tcp(struct rte_mbuf *pkt_in,
     337             :                         struct dpdk_gso_state *state,
     338             :                         struct rte_mbuf **mbufs_out,
     339             :                         const unsigned short nb_pkts_out,
     340             :                         uint16_t mss_size)
     341             : {
     342           0 :     int i = 0, nsegs = 0;
     343             : 
     344             :     /* Check that pkts_out is big enough to hold all segments */
     345           0 :     if (unlikely(mss_size * nb_pkts_out <
     346             :         (uint16_t)(pkt_in->pkt_len - state->hlen)))
     347           0 :         return -EINVAL;
     348             : 
     349           0 :     nsegs = dpdk_split_chained_mbuf(pkt_in, mbufs_out, nb_pkts_out,
     350           0 :                   state->hlen, mss_size);
     351             : 
     352           0 :     if (nsegs < 0)
     353           0 :         goto err;
     354             : 
     355           0 :     while (i < nsegs)
     356             :     {
     357           0 :         state->update(mbufs_out[i], state);
     358             : 
     359             :         /* Update TCP flags -
     360             :          * => Retain CWR only in the first segment and mask in the rest of
     361             :          *  the segments
     362             :          * => FIN and PSH flags are only applicable to the last segment
     363             :          */
     364           0 :         if (state->tcp) {
     365           0 :             if (i > 0)
     366           0 :                 state->tcp->tcp_flags &= ~RTE_TCP_CWR_FLAG;
     367           0 :             if (i < nsegs-1)
     368           0 :                 state->tcp->tcp_flags &= ~(RTE_TCP_FIN_FLAG | RTE_TCP_PSH_FLAG);
     369             :         }
     370             : 
     371           0 :         state->internal(mbufs_out[i], state);
     372             : 
     373           0 :         i++;
     374             :     }
     375             : 
     376           0 :     return nsegs;
     377             : 
     378           0 : err:
     379           0 :     return -1;
     380             : }
     381             : 
     382             : static void
     383           0 : dpdk_adjust_outer_header(struct rte_mbuf *m, uint16_t outer_header_len)
     384             : {
     385           0 :     struct vr_packet *pkt = vr_dpdk_mbuf_to_pkt(m);
     386           0 :     struct vr_ip *inner_ip = NULL;
     387             : 
     388           0 :     if (pkt->vp_type == VP_TYPE_IPOIP)
     389           0 :         inner_ip = rte_pktmbuf_mtod(m, struct vr_ip *);
     390             : 
     391             :     /* Outer header operations */
     392           0 :     char *outer_header_ptr = rte_pktmbuf_prepend(m, outer_header_len);
     393             : 
     394           0 :     uint16_t eth_hlen = dpdk_get_ether_header_len(outer_header_ptr);
     395           0 :     struct vr_ip *outer_ip = (struct vr_ip *)(outer_header_ptr + eth_hlen);
     396           0 :     outer_ip->ip_len = rte_cpu_to_be_16(rte_pktmbuf_pkt_len(m) - eth_hlen);
     397             : 
     398             :     /* Copy inner IP id to outer. Currently, the Agent diagnostics depends
     399             :      * on that. */
     400           0 :     if (inner_ip)
     401           0 :         outer_ip->ip_id = inner_ip->ip_id;
     402             :     else
     403           0 :         outer_ip->ip_id = rte_cpu_to_be_16(vr_generate_unique_ip_id());
     404             : 
     405             : 
     406             :     /* Adjust UDP length to match IP segment size */
     407           0 :     if (outer_ip->ip_proto == VR_IP_PROTO_UDP) {
     408           0 :         unsigned header_len = outer_ip->ip_hl * 4;
     409           0 :         struct vr_udp *udp = (struct vr_udp *)((char *)outer_ip +
     410             :                 header_len);
     411           0 :         udp->udp_length = rte_cpu_to_be_16(
     412             :                 rte_be_to_cpu_16(outer_ip->ip_len) - header_len);
     413             :     }
     414             : 
     415             :     /* Calculate the outer header IP checksum */
     416           0 :     outer_ip->ip_csum = vr_ip_csum(outer_ip);
     417           0 : }
     418             : 
     419             : int
     420           0 : dpdk_segment_packet(struct vr_packet *pkt, struct rte_mbuf *mbuf_in,
     421             :                  struct rte_mbuf **mbuf_out, const unsigned short out_num,
     422             :                  const unsigned short mss_size, bool do_outer_ip_csum)
     423             : {
     424             :     struct rte_mbuf *m;
     425             :     struct dpdk_gso_state state;
     426           0 :     int number_of_packets = 0, i;
     427             :     uint16_t outer_header_len;
     428             : 
     429           0 :     outer_header_len = pkt_get_inner_network_header_off(pkt) -
     430           0 :             pkt_head_space(pkt);
     431             : 
     432           0 :     if (dpdk_gso_init_state(&state, mbuf_in, outer_header_len,
     433           0 :                 (pkt->vp_type == VP_TYPE_IP6OIP), do_outer_ip_csum) < 0)
     434           0 :         return -1;
     435             : 
     436           0 :     number_of_packets = dpdk_gso_segment_ip_tcp(mbuf_in, &state, mbuf_out,
     437             :                                                        out_num, mss_size);
     438           0 :     if (number_of_packets < 0)
     439           0 :         return number_of_packets;
     440             : 
     441             :     /* Adjust outer and inner IP headers for each segmented packets */
     442           0 :     for (i = 0; i < number_of_packets; i++)
     443             :     {
     444           0 :         m = mbuf_out[i];
     445             :         /* Get into the inner IP header */
     446           0 :         rte_pktmbuf_adj(m, outer_header_len);
     447           0 :         dpdk_adjust_outer_header(m, outer_header_len);
     448           0 :         m->l2_len = mbuf_in->l2_len;
     449           0 :         m->l3_len = mbuf_in->l3_len;
     450           0 :         m->vlan_tci = mbuf_in->vlan_tci;
     451           0 :         m->ol_flags |= mbuf_in->ol_flags;
     452             :     }
     453             : 
     454           0 :     return number_of_packets;
     455             : }

Generated by: LCOV version 1.14