1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2015 Intel Corporation
7 #include <rte_hash_crc.h>
8 #include <rte_byteorder.h>
17 get_psd_sum(void *l3_hdr
, uint16_t ethertype
, uint64_t ol_flags
)
19 if (ethertype
== ETHER_TYPE_IPv4
)
20 return rte_ipv4_phdr_cksum(l3_hdr
, ol_flags
);
21 else /* assume ethertype == ETHER_TYPE_IPv6 */
22 return rte_ipv6_phdr_cksum(l3_hdr
, ol_flags
);
26 * Parse an ethernet header to fill the ethertype, outer_l2_len, outer_l3_len and
27 * ipproto. This function is able to recognize IPv4/IPv6 with one optional vlan
31 parse_ethernet(struct ether_hdr
*eth_hdr
, union tunnel_offload_info
*info
,
34 struct ipv4_hdr
*ipv4_hdr
;
35 struct ipv6_hdr
*ipv6_hdr
;
38 info
->outer_l2_len
= sizeof(struct ether_hdr
);
39 ethertype
= rte_be_to_cpu_16(eth_hdr
->ether_type
);
41 if (ethertype
== ETHER_TYPE_VLAN
) {
42 struct vlan_hdr
*vlan_hdr
= (struct vlan_hdr
*)(eth_hdr
+ 1);
43 info
->outer_l2_len
+= sizeof(struct vlan_hdr
);
44 ethertype
= rte_be_to_cpu_16(vlan_hdr
->eth_proto
);
49 ipv4_hdr
= (struct ipv4_hdr
*)
50 ((char *)eth_hdr
+ info
->outer_l2_len
);
51 info
->outer_l3_len
= sizeof(struct ipv4_hdr
);
52 *l4_proto
= ipv4_hdr
->next_proto_id
;
55 ipv6_hdr
= (struct ipv6_hdr
*)
56 ((char *)eth_hdr
+ info
->outer_l2_len
);
57 info
->outer_l3_len
= sizeof(struct ipv6_hdr
);
58 *l4_proto
= ipv6_hdr
->proto
;
61 info
->outer_l3_len
= 0;
68 * Calculate the checksum of a packet in hardware
71 process_inner_cksums(struct ether_hdr
*eth_hdr
, union tunnel_offload_info
*info
)
76 struct ipv4_hdr
*ipv4_hdr
;
77 struct ipv6_hdr
*ipv6_hdr
;
78 struct udp_hdr
*udp_hdr
;
79 struct tcp_hdr
*tcp_hdr
;
80 struct sctp_hdr
*sctp_hdr
;
81 uint64_t ol_flags
= 0;
83 info
->l2_len
= sizeof(struct ether_hdr
);
84 ethertype
= rte_be_to_cpu_16(eth_hdr
->ether_type
);
86 if (ethertype
== ETHER_TYPE_VLAN
) {
87 struct vlan_hdr
*vlan_hdr
= (struct vlan_hdr
*)(eth_hdr
+ 1);
88 info
->l2_len
+= sizeof(struct vlan_hdr
);
89 ethertype
= rte_be_to_cpu_16(vlan_hdr
->eth_proto
);
92 l3_hdr
= (char *)eth_hdr
+ info
->l2_len
;
94 if (ethertype
== ETHER_TYPE_IPv4
) {
95 ipv4_hdr
= (struct ipv4_hdr
*)l3_hdr
;
96 ipv4_hdr
->hdr_checksum
= 0;
97 ol_flags
|= PKT_TX_IPV4
;
98 ol_flags
|= PKT_TX_IP_CKSUM
;
99 info
->l3_len
= sizeof(struct ipv4_hdr
);
100 l4_proto
= ipv4_hdr
->next_proto_id
;
101 } else if (ethertype
== ETHER_TYPE_IPv6
) {
102 ipv6_hdr
= (struct ipv6_hdr
*)l3_hdr
;
103 info
->l3_len
= sizeof(struct ipv6_hdr
);
104 l4_proto
= ipv6_hdr
->proto
;
105 ol_flags
|= PKT_TX_IPV6
;
107 return 0; /* packet type not supported, nothing to do */
109 if (l4_proto
== IPPROTO_UDP
) {
110 udp_hdr
= (struct udp_hdr
*)((char *)l3_hdr
+ info
->l3_len
);
111 ol_flags
|= PKT_TX_UDP_CKSUM
;
112 udp_hdr
->dgram_cksum
= get_psd_sum(l3_hdr
,
113 ethertype
, ol_flags
);
114 } else if (l4_proto
== IPPROTO_TCP
) {
115 tcp_hdr
= (struct tcp_hdr
*)((char *)l3_hdr
+ info
->l3_len
);
116 /* Put PKT_TX_TCP_SEG bit setting before get_psd_sum(), because
117 * it depends on PKT_TX_TCP_SEG to calculate pseudo-header
120 if (tso_segsz
!= 0) {
121 ol_flags
|= PKT_TX_TCP_SEG
;
122 info
->tso_segsz
= tso_segsz
;
123 info
->l4_len
= (tcp_hdr
->data_off
& 0xf0) >> 2;
125 ol_flags
|= PKT_TX_TCP_CKSUM
;
126 tcp_hdr
->cksum
= get_psd_sum(l3_hdr
, ethertype
, ol_flags
);
128 } else if (l4_proto
== IPPROTO_SCTP
) {
129 sctp_hdr
= (struct sctp_hdr
*)((char *)l3_hdr
+ info
->l3_len
);
131 ol_flags
|= PKT_TX_SCTP_CKSUM
;
138 decapsulation(struct rte_mbuf
*pkt
)
140 uint8_t l4_proto
= 0;
141 uint16_t outer_header_len
;
142 struct udp_hdr
*udp_hdr
;
143 union tunnel_offload_info info
= { .data
= 0 };
144 struct ether_hdr
*phdr
= rte_pktmbuf_mtod(pkt
, struct ether_hdr
*);
146 parse_ethernet(phdr
, &info
, &l4_proto
);
148 if (l4_proto
!= IPPROTO_UDP
)
151 udp_hdr
= (struct udp_hdr
*)((char *)phdr
+
152 info
.outer_l2_len
+ info
.outer_l3_len
);
154 /** check udp destination port, 4789 is the default vxlan port
155 * (rfc7348) or that the rx offload flag is set (i40e only
157 if (udp_hdr
->dst_port
!= rte_cpu_to_be_16(DEFAULT_VXLAN_PORT
) &&
158 (pkt
->packet_type
& RTE_PTYPE_TUNNEL_MASK
) == 0)
160 outer_header_len
= info
.outer_l2_len
+ info
.outer_l3_len
161 + sizeof(struct udp_hdr
) + sizeof(struct vxlan_hdr
);
163 rte_pktmbuf_adj(pkt
, outer_header_len
);
169 encapsulation(struct rte_mbuf
*m
, uint8_t queue_id
)
172 uint64_t ol_flags
= 0;
173 uint32_t old_len
= m
->pkt_len
, hash
;
174 union tunnel_offload_info tx_offload
= { .data
= 0 };
175 struct ether_hdr
*phdr
= rte_pktmbuf_mtod(m
, struct ether_hdr
*);
177 /*Allocate space for new ethernet, IPv4, UDP and VXLAN headers*/
178 struct ether_hdr
*pneth
= (struct ether_hdr
*) rte_pktmbuf_prepend(m
,
179 sizeof(struct ether_hdr
) + sizeof(struct ipv4_hdr
)
180 + sizeof(struct udp_hdr
) + sizeof(struct vxlan_hdr
));
182 struct ipv4_hdr
*ip
= (struct ipv4_hdr
*) &pneth
[1];
183 struct udp_hdr
*udp
= (struct udp_hdr
*) &ip
[1];
184 struct vxlan_hdr
*vxlan
= (struct vxlan_hdr
*) &udp
[1];
186 /* convert TX queue ID to vport ID */
187 vport_id
= queue_id
- 1;
189 /* replace original Ethernet header with ours */
190 pneth
= rte_memcpy(pneth
, &app_l2_hdr
[vport_id
],
191 sizeof(struct ether_hdr
));
193 /* copy in IP header */
194 ip
= rte_memcpy(ip
, &app_ip_hdr
[vport_id
],
195 sizeof(struct ipv4_hdr
));
196 ip
->total_length
= rte_cpu_to_be_16(m
->pkt_len
197 - sizeof(struct ether_hdr
));
199 /* outer IP checksum */
200 ol_flags
|= PKT_TX_OUTER_IP_CKSUM
;
201 ip
->hdr_checksum
= 0;
203 /* inner IP checksum offload */
205 ol_flags
|= process_inner_cksums(phdr
, &tx_offload
);
206 m
->l2_len
= tx_offload
.l2_len
;
207 m
->l3_len
= tx_offload
.l3_len
;
208 m
->l4_len
= tx_offload
.l4_len
;
209 m
->l2_len
+= ETHER_VXLAN_HLEN
;
212 m
->outer_l2_len
= sizeof(struct ether_hdr
);
213 m
->outer_l3_len
= sizeof(struct ipv4_hdr
);
215 ol_flags
|= PKT_TX_TUNNEL_VXLAN
;
217 m
->ol_flags
|= ol_flags
;
218 m
->tso_segsz
= tx_offload
.tso_segsz
;
221 vxlan
->vx_flags
= rte_cpu_to_be_32(VXLAN_HF_VNI
);
222 vxlan
->vx_vni
= rte_cpu_to_be_32(vxdev
.out_key
<< 8);
225 udp
->dgram_cksum
= 0;
226 udp
->dgram_len
= rte_cpu_to_be_16(old_len
227 + sizeof(struct udp_hdr
)
228 + sizeof(struct vxlan_hdr
));
230 udp
->dst_port
= rte_cpu_to_be_16(vxdev
.dst_port
);
231 hash
= rte_hash_crc(phdr
, 2 * ETHER_ADDR_LEN
, phdr
->ether_type
);
232 udp
->src_port
= rte_cpu_to_be_16((((uint64_t) hash
* PORT_RANGE
) >> 32)