Skip to content

Commit 2ac9cfe

Browse files
ilantayariSaeed Mahameed
authored andcommitted
net/mlx5e: IPSec, Add Innova IPSec offload TX data path
In the TX data path, prepend a special metadata ethertype which instructs the hardware to perform cryptography. In addition, fill Software-Parser segment in TX descriptor so that the hardware may parse the ESP protocol, and perform TX checksum offload on the inner payload. Support GSO, by providing the inverse of gso_size in the metadata. This allows the FPGA to update the ESP header (seqno and seqiv) on the resulting packets, by calculating the packet number within the GSO back from the TCP sequence number. Note that for GSO SKBs, the stack does not include an ESP trailer, unlike the non-GSO case. Signed-off-by: Ilan Tayari <[email protected]> Signed-off-by: Yossi Kuperman <[email protected]> Signed-off-by: Yevgeny Kliteynik <[email protected]> Signed-off-by: Boris Pismenny <[email protected]> Signed-off-by: Saeed Mahameed <[email protected]>
1 parent 899a59d commit 2ac9cfe

File tree

7 files changed

+319
-9
lines changed

7 files changed

+319
-9
lines changed

drivers/net/ethernet/mellanox/mlx5/core/en.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ struct mlx5e_sq_dma {
328328

329329
enum {
330330
MLX5E_SQ_STATE_ENABLED,
331+
MLX5E_SQ_STATE_IPSEC,
331332
};
332333

333334
struct mlx5e_sq_wqe_info {

drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,26 @@ void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
399399
priv->ipsec = NULL;
400400
}
401401

402+
static bool mlx5e_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
403+
{
404+
if (x->props.family == AF_INET) {
405+
/* Offload with IPv4 options is not supported yet */
406+
if (ip_hdr(skb)->ihl > 5)
407+
return false;
408+
} else {
409+
/* Offload with IPv6 extension headers is not support yet */
410+
if (ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr))
411+
return false;
412+
}
413+
414+
return true;
415+
}
416+
402417
static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = {
403418
.xdo_dev_state_add = mlx5e_xfrm_add_state,
404419
.xdo_dev_state_delete = mlx5e_xfrm_del_state,
405420
.xdo_dev_state_free = mlx5e_xfrm_free_state,
421+
.xdo_dev_offload_ok = mlx5e_ipsec_offload_ok,
406422
};
407423

408424
void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
@@ -431,4 +447,15 @@ void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
431447

432448
netdev->features |= NETIF_F_HW_ESP_TX_CSUM;
433449
netdev->hw_enc_features |= NETIF_F_HW_ESP_TX_CSUM;
450+
451+
if (!(mlx5_accel_ipsec_device_caps(mdev) & MLX5_ACCEL_IPSEC_LSO) ||
452+
!MLX5_CAP_ETH(mdev, swp_lso)) {
453+
mlx5_core_dbg(mdev, "mlx5e: ESP LSO not supported\n");
454+
return;
455+
}
456+
457+
mlx5_core_dbg(mdev, "mlx5e: ESP GSO capability turned on\n");
458+
netdev->features |= NETIF_F_GSO_ESP;
459+
netdev->hw_features |= NETIF_F_GSO_ESP;
460+
netdev->hw_enc_features |= NETIF_F_GSO_ESP;
434461
}

drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ struct mlx5e_ipsec_sw_stats {
5050
atomic64_t ipsec_rx_drop_sp_alloc;
5151
atomic64_t ipsec_rx_drop_sadb_miss;
5252
atomic64_t ipsec_rx_drop_syndrome;
53+
atomic64_t ipsec_tx_drop_bundle;
54+
atomic64_t ipsec_tx_drop_no_state;
55+
atomic64_t ipsec_tx_drop_not_ip;
56+
atomic64_t ipsec_tx_drop_trailer;
57+
atomic64_t ipsec_tx_drop_metadata;
5358
};
5459

5560
struct mlx5e_ipsec {
@@ -60,6 +65,7 @@ struct mlx5e_ipsec {
6065
struct mlx5e_ipsec_sw_stats sw_stats;
6166
};
6267

68+
void mlx5e_ipsec_build_inverse_table(void);
6369
int mlx5e_ipsec_init(struct mlx5e_priv *priv);
6470
void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv);
6571
void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv);
@@ -69,6 +75,10 @@ struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *dev,
6975

7076
#else
7177

78+
static inline void mlx5e_ipsec_build_inverse_table(void)
79+
{
80+
}
81+
7282
static inline int mlx5e_ipsec_init(struct mlx5e_priv *priv)
7383
{
7484
return 0;

drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#include <crypto/aead.h>
3535
#include <net/xfrm.h>
36+
#include <net/esp.h>
3637

3738
#include "en_accel/ipsec_rxtx.h"
3839
#include "en_accel/ipsec.h"
@@ -48,17 +49,228 @@ struct mlx5e_ipsec_rx_metadata {
4849
__be32 sa_handle;
4950
} __packed;
5051

52+
enum {
53+
MLX5E_IPSEC_TX_SYNDROME_OFFLOAD = 0x8,
54+
MLX5E_IPSEC_TX_SYNDROME_OFFLOAD_WITH_LSO_TCP = 0x9,
55+
};
56+
57+
struct mlx5e_ipsec_tx_metadata {
58+
__be16 mss_inv; /* 1/MSS in 16bit fixed point, only for LSO */
59+
__be16 seq; /* LSBs of the first TCP seq, only for LSO */
60+
u8 esp_next_proto; /* Next protocol of ESP */
61+
} __packed;
62+
5163
struct mlx5e_ipsec_metadata {
5264
unsigned char syndrome;
5365
union {
5466
unsigned char raw[5];
5567
/* from FPGA to host, on successful decrypt */
5668
struct mlx5e_ipsec_rx_metadata rx;
69+
/* from host to FPGA */
70+
struct mlx5e_ipsec_tx_metadata tx;
5771
} __packed content;
5872
/* packet type ID field */
5973
__be16 ethertype;
6074
} __packed;
6175

76+
#define MAX_LSO_MSS 2048
77+
78+
/* Pre-calculated (Q0.16) fixed-point inverse 1/x function */
79+
static __be16 mlx5e_ipsec_inverse_table[MAX_LSO_MSS];
80+
81+
static inline __be16 mlx5e_ipsec_mss_inv(struct sk_buff *skb)
82+
{
83+
return mlx5e_ipsec_inverse_table[skb_shinfo(skb)->gso_size];
84+
}
85+
86+
static struct mlx5e_ipsec_metadata *mlx5e_ipsec_add_metadata(struct sk_buff *skb)
87+
{
88+
struct mlx5e_ipsec_metadata *mdata;
89+
struct ethhdr *eth;
90+
91+
if (unlikely(skb_cow_head(skb, sizeof(*mdata))))
92+
return ERR_PTR(-ENOMEM);
93+
94+
eth = (struct ethhdr *)skb_push(skb, sizeof(*mdata));
95+
skb->mac_header -= sizeof(*mdata);
96+
mdata = (struct mlx5e_ipsec_metadata *)(eth + 1);
97+
98+
memmove(skb->data, skb->data + sizeof(*mdata),
99+
2 * ETH_ALEN);
100+
101+
eth->h_proto = cpu_to_be16(MLX5E_METADATA_ETHER_TYPE);
102+
103+
memset(mdata->content.raw, 0, sizeof(mdata->content.raw));
104+
return mdata;
105+
}
106+
107+
static int mlx5e_ipsec_remove_trailer(struct sk_buff *skb, struct xfrm_state *x)
108+
{
109+
unsigned int alen = crypto_aead_authsize(x->data);
110+
struct ipv6hdr *ipv6hdr = ipv6_hdr(skb);
111+
struct iphdr *ipv4hdr = ip_hdr(skb);
112+
unsigned int trailer_len;
113+
u8 plen;
114+
int ret;
115+
116+
ret = skb_copy_bits(skb, skb->len - alen - 2, &plen, 1);
117+
if (unlikely(ret))
118+
return ret;
119+
120+
trailer_len = alen + plen + 2;
121+
122+
pskb_trim(skb, skb->len - trailer_len);
123+
if (skb->protocol == htons(ETH_P_IP)) {
124+
ipv4hdr->tot_len = htons(ntohs(ipv4hdr->tot_len) - trailer_len);
125+
ip_send_check(ipv4hdr);
126+
} else {
127+
ipv6hdr->payload_len = htons(ntohs(ipv6hdr->payload_len) -
128+
trailer_len);
129+
}
130+
return 0;
131+
}
132+
133+
static void mlx5e_ipsec_set_swp(struct sk_buff *skb,
134+
struct mlx5_wqe_eth_seg *eseg, u8 mode,
135+
struct xfrm_offload *xo)
136+
{
137+
u8 proto;
138+
139+
/* Tunnel Mode:
140+
* SWP: OutL3 InL3 InL4
141+
* Pkt: MAC IP ESP IP L4
142+
*
143+
* Transport Mode:
144+
* SWP: OutL3 InL4
145+
* InL3
146+
* Pkt: MAC IP ESP L4
147+
*
148+
* Offsets are in 2-byte words, counting from start of frame
149+
*/
150+
eseg->swp_outer_l3_offset = skb_network_offset(skb) / 2;
151+
if (skb->protocol == htons(ETH_P_IPV6))
152+
eseg->swp_flags |= MLX5_ETH_WQE_SWP_OUTER_L3_IPV6;
153+
154+
if (mode == XFRM_MODE_TUNNEL) {
155+
eseg->swp_inner_l3_offset = skb_inner_network_offset(skb) / 2;
156+
if (xo->proto == IPPROTO_IPV6) {
157+
eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6;
158+
proto = inner_ipv6_hdr(skb)->nexthdr;
159+
} else {
160+
proto = inner_ip_hdr(skb)->protocol;
161+
}
162+
} else {
163+
eseg->swp_inner_l3_offset = skb_network_offset(skb) / 2;
164+
if (skb->protocol == htons(ETH_P_IPV6))
165+
eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6;
166+
proto = xo->proto;
167+
}
168+
switch (proto) {
169+
case IPPROTO_UDP:
170+
eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L4_UDP;
171+
/* Fall through */
172+
case IPPROTO_TCP:
173+
eseg->swp_inner_l4_offset = skb_inner_transport_offset(skb) / 2;
174+
break;
175+
}
176+
}
177+
178+
static void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_offload *xo)
179+
{
180+
int iv_offset;
181+
__be64 seqno;
182+
183+
/* Place the SN in the IV field */
184+
seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32));
185+
iv_offset = skb_transport_offset(skb) + sizeof(struct ip_esp_hdr);
186+
skb_store_bits(skb, iv_offset, &seqno, 8);
187+
}
188+
189+
static void mlx5e_ipsec_set_metadata(struct sk_buff *skb,
190+
struct mlx5e_ipsec_metadata *mdata,
191+
struct xfrm_offload *xo)
192+
{
193+
struct ip_esp_hdr *esph;
194+
struct tcphdr *tcph;
195+
196+
if (skb_is_gso(skb)) {
197+
/* Add LSO metadata indication */
198+
esph = ip_esp_hdr(skb);
199+
tcph = inner_tcp_hdr(skb);
200+
netdev_dbg(skb->dev, " Offloading GSO packet outer L3 %u; L4 %u; Inner L3 %u; L4 %u\n",
201+
skb->network_header,
202+
skb->transport_header,
203+
skb->inner_network_header,
204+
skb->inner_transport_header);
205+
netdev_dbg(skb->dev, " Offloading GSO packet of len %u; mss %u; TCP sp %u dp %u seq 0x%x ESP seq 0x%x\n",
206+
skb->len, skb_shinfo(skb)->gso_size,
207+
ntohs(tcph->source), ntohs(tcph->dest),
208+
ntohl(tcph->seq), ntohl(esph->seq_no));
209+
mdata->syndrome = MLX5E_IPSEC_TX_SYNDROME_OFFLOAD_WITH_LSO_TCP;
210+
mdata->content.tx.mss_inv = mlx5e_ipsec_mss_inv(skb);
211+
mdata->content.tx.seq = htons(ntohl(tcph->seq) & 0xFFFF);
212+
} else {
213+
mdata->syndrome = MLX5E_IPSEC_TX_SYNDROME_OFFLOAD;
214+
}
215+
mdata->content.tx.esp_next_proto = xo->proto;
216+
217+
netdev_dbg(skb->dev, " TX metadata syndrome %u proto %u mss_inv %04x seq %04x\n",
218+
mdata->syndrome, mdata->content.tx.esp_next_proto,
219+
ntohs(mdata->content.tx.mss_inv),
220+
ntohs(mdata->content.tx.seq));
221+
}
222+
223+
struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
224+
struct mlx5e_tx_wqe *wqe,
225+
struct sk_buff *skb)
226+
{
227+
struct mlx5e_priv *priv = netdev_priv(netdev);
228+
struct xfrm_offload *xo = xfrm_offload(skb);
229+
struct mlx5e_ipsec_metadata *mdata;
230+
struct xfrm_state *x;
231+
232+
if (!xo)
233+
return skb;
234+
235+
if (unlikely(skb->sp->len != 1)) {
236+
atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_bundle);
237+
goto drop;
238+
}
239+
240+
x = xfrm_input_state(skb);
241+
if (unlikely(!x)) {
242+
atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_no_state);
243+
goto drop;
244+
}
245+
246+
if (unlikely(!x->xso.offload_handle ||
247+
(skb->protocol != htons(ETH_P_IP) &&
248+
skb->protocol != htons(ETH_P_IPV6)))) {
249+
atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_not_ip);
250+
goto drop;
251+
}
252+
253+
if (!skb_is_gso(skb))
254+
if (unlikely(mlx5e_ipsec_remove_trailer(skb, x))) {
255+
atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_trailer);
256+
goto drop;
257+
}
258+
mdata = mlx5e_ipsec_add_metadata(skb);
259+
if (unlikely(IS_ERR(mdata))) {
260+
atomic64_inc(&priv->ipsec->sw_stats.ipsec_tx_drop_metadata);
261+
goto drop;
262+
}
263+
mlx5e_ipsec_set_swp(skb, &wqe->eth, x->props.mode, xo);
264+
mlx5e_ipsec_set_iv(skb, xo);
265+
mlx5e_ipsec_set_metadata(skb, mdata, xo);
266+
267+
return skb;
268+
269+
drop:
270+
kfree_skb(skb);
271+
return NULL;
272+
}
273+
62274
static inline struct xfrm_state *
63275
mlx5e_ipsec_build_sp(struct net_device *netdev, struct sk_buff *skb,
64276
struct mlx5e_ipsec_metadata *mdata)
@@ -133,3 +345,34 @@ struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
133345

134346
return skb;
135347
}
348+
349+
bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
350+
netdev_features_t features)
351+
{
352+
struct xfrm_state *x;
353+
354+
if (skb->sp && skb->sp->len) {
355+
x = skb->sp->xvec[0];
356+
if (x && x->xso.offload_handle)
357+
return true;
358+
}
359+
return false;
360+
}
361+
362+
void mlx5e_ipsec_build_inverse_table(void)
363+
{
364+
u16 mss_inv;
365+
u32 mss;
366+
367+
/* Calculate 1/x inverse table for use in GSO data path.
368+
* Using this table, we provide the IPSec accelerator with the value of
369+
* 1/gso_size so that it can infer the position of each segment inside
370+
* the GSO, and increment the ESP sequence number, and generate the IV.
371+
* The HW needs this value in Q0.16 fixed-point number format
372+
*/
373+
mlx5e_ipsec_inverse_table[1] = htons(0xFFFF);
374+
for (mss = 2; mss < MAX_LSO_MSS; mss++) {
375+
mss_inv = ((1ULL << 32) / mss) >> 16;
376+
mlx5e_ipsec_inverse_table[mss] = htons(mss_inv);
377+
}
378+
}

drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,22 @@
3434
#ifndef __MLX5E_IPSEC_RXTX_H__
3535
#define __MLX5E_IPSEC_RXTX_H__
3636

37+
#ifdef CONFIG_MLX5_EN_IPSEC
38+
3739
#include <linux/skbuff.h>
3840
#include "en.h"
3941

4042
struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
4143
struct sk_buff *skb);
4244
void mlx5e_ipsec_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
4345

44-
#endif /* __MLX5E_IPSEC_RXTX_H__ */
46+
void mlx5e_ipsec_inverse_table_init(void);
47+
bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
48+
netdev_features_t features);
49+
struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
50+
struct mlx5e_tx_wqe *wqe,
51+
struct sk_buff *skb);
52+
53+
#endif /* CONFIG_MLX5_EN_IPSEC */
54+
55+
#endif /* __MLX5E_IPSEC_RXTX_H__ */

0 commit comments

Comments
 (0)