Skip to content

Commit cb01008

Browse files
aviadyeSaeed Mahameed
authored andcommitted
net/mlx5: IPSec, Add support for ESN
Currently ESN is not supported with IPSec device offload. This patch adds ESN support to IPsec device offload. Implementing new xfrm device operation to synchronize offloading device ESN with xfrm received SN. New QP command to update SA state at the following: ESN 1 ESN 2 ESN 3 |-----------*-----------|-----------*-----------|-----------* ^ ^ ^ ^ ^ ^ ^ - marks where QP command invoked to update the SA ESN state machine. | - marks the start of the ESN scope (0-2^32-1). At this point move SA ESN overlap bit to zero and increment ESN. * - marks the middle of the ESN scope (2^31). At this point move SA ESN overlap bit to one. Signed-off-by: Aviad Yehezkel <[email protected]> Signed-off-by: Yossef Efraim <[email protected]> Signed-off-by: Saeed Mahameed <[email protected]>
1 parent 75ef3f5 commit cb01008

File tree

7 files changed

+189
-12
lines changed

7 files changed

+189
-12
lines changed

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

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,9 @@
3838
#include <linux/module.h>
3939

4040
#include "en.h"
41-
#include "accel/ipsec.h"
4241
#include "en_accel/ipsec.h"
4342
#include "en_accel/ipsec_rxtx.h"
4443

45-
struct mlx5e_ipsec_sa_entry {
46-
struct hlist_node hlist; /* Item in SADB_RX hashtable */
47-
unsigned int handle; /* Handle in SADB_RX */
48-
struct xfrm_state *x;
49-
struct mlx5e_ipsec *ipsec;
50-
struct mlx5_accel_esp_xfrm *xfrm;
51-
void *hw_context;
52-
};
5344

5445
static struct mlx5e_ipsec_sa_entry *to_ipsec_sa_entry(struct xfrm_state *x)
5546
{
@@ -121,6 +112,40 @@ static void mlx5e_ipsec_sadb_rx_free(struct mlx5e_ipsec_sa_entry *sa_entry)
121112
ida_simple_remove(&ipsec->halloc, sa_entry->handle);
122113
}
123114

115+
static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry)
116+
{
117+
struct xfrm_replay_state_esn *replay_esn;
118+
u32 seq_bottom;
119+
u8 overlap;
120+
u32 *esn;
121+
122+
if (!(sa_entry->x->props.flags & XFRM_STATE_ESN)) {
123+
sa_entry->esn_state.trigger = 0;
124+
return false;
125+
}
126+
127+
replay_esn = sa_entry->x->replay_esn;
128+
seq_bottom = replay_esn->seq - replay_esn->replay_window + 1;
129+
overlap = sa_entry->esn_state.overlap;
130+
131+
sa_entry->esn_state.esn = xfrm_replay_seqhi(sa_entry->x,
132+
htonl(seq_bottom));
133+
esn = &sa_entry->esn_state.esn;
134+
135+
sa_entry->esn_state.trigger = 1;
136+
if (unlikely(overlap && seq_bottom < MLX5E_IPSEC_ESN_SCOPE_MID)) {
137+
++(*esn);
138+
sa_entry->esn_state.overlap = 0;
139+
return true;
140+
} else if (unlikely(!overlap &&
141+
(seq_bottom >= MLX5E_IPSEC_ESN_SCOPE_MID))) {
142+
sa_entry->esn_state.overlap = 1;
143+
return true;
144+
}
145+
146+
return false;
147+
}
148+
124149
static void
125150
mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
126151
struct mlx5_accel_esp_xfrm_attrs *attrs)
@@ -152,6 +177,14 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
152177
/* iv len */
153178
aes_gcm->icv_len = x->aead->alg_icv_len;
154179

180+
/* esn */
181+
if (sa_entry->esn_state.trigger) {
182+
attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED;
183+
attrs->esn = sa_entry->esn_state.esn;
184+
if (sa_entry->esn_state.overlap)
185+
attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP;
186+
}
187+
155188
/* rx handle */
156189
attrs->sa_handle = sa_entry->handle;
157190

@@ -187,7 +220,9 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x)
187220
netdev_info(netdev, "Cannot offload compressed xfrm states\n");
188221
return -EINVAL;
189222
}
190-
if (x->props.flags & XFRM_STATE_ESN) {
223+
if (x->props.flags & XFRM_STATE_ESN &&
224+
!(mlx5_accel_ipsec_device_caps(priv->mdev) &
225+
MLX5_ACCEL_IPSEC_CAP_ESN)) {
191226
netdev_info(netdev, "Cannot offload ESN xfrm states\n");
192227
return -EINVAL;
193228
}
@@ -277,8 +312,14 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
277312
netdev_info(netdev, "Failed adding to SADB_RX: %d\n", err);
278313
goto err_entry;
279314
}
315+
} else {
316+
sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ?
317+
mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv;
280318
}
281319

320+
/* check esn */
321+
mlx5e_ipsec_update_esn_state(sa_entry);
322+
282323
/* create xfrm */
283324
mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &attrs);
284325
sa_entry->xfrm =
@@ -344,6 +385,7 @@ static void mlx5e_xfrm_free_state(struct xfrm_state *x)
344385
return;
345386

346387
if (sa_entry->hw_context) {
388+
flush_workqueue(sa_entry->ipsec->wq);
347389
mlx5_accel_esp_free_hw_context(sa_entry->hw_context);
348390
mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm);
349391
}
@@ -374,6 +416,12 @@ int mlx5e_ipsec_init(struct mlx5e_priv *priv)
374416
ipsec->en_priv->ipsec = ipsec;
375417
ipsec->no_trailer = !!(mlx5_accel_ipsec_device_caps(priv->mdev) &
376418
MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER);
419+
ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0,
420+
priv->netdev->name);
421+
if (!ipsec->wq) {
422+
kfree(ipsec);
423+
return -ENOMEM;
424+
}
377425
netdev_dbg(priv->netdev, "IPSec attached to netdevice\n");
378426
return 0;
379427
}
@@ -385,6 +433,9 @@ void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
385433
if (!ipsec)
386434
return;
387435

436+
drain_workqueue(ipsec->wq);
437+
destroy_workqueue(ipsec->wq);
438+
388439
ida_destroy(&ipsec->halloc);
389440
kfree(ipsec);
390441
priv->ipsec = NULL;
@@ -405,11 +456,58 @@ static bool mlx5e_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
405456
return true;
406457
}
407458

459+
struct mlx5e_ipsec_modify_state_work {
460+
struct work_struct work;
461+
struct mlx5_accel_esp_xfrm_attrs attrs;
462+
struct mlx5e_ipsec_sa_entry *sa_entry;
463+
};
464+
465+
static void _update_xfrm_state(struct work_struct *work)
466+
{
467+
int ret;
468+
struct mlx5e_ipsec_modify_state_work *modify_work =
469+
container_of(work, struct mlx5e_ipsec_modify_state_work, work);
470+
struct mlx5e_ipsec_sa_entry *sa_entry = modify_work->sa_entry;
471+
472+
ret = mlx5_accel_esp_modify_xfrm(sa_entry->xfrm,
473+
&modify_work->attrs);
474+
if (ret)
475+
netdev_warn(sa_entry->ipsec->en_priv->netdev,
476+
"Not an IPSec offload device\n");
477+
478+
kfree(modify_work);
479+
}
480+
481+
static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x)
482+
{
483+
struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x);
484+
struct mlx5e_ipsec_modify_state_work *modify_work;
485+
bool need_update;
486+
487+
if (!sa_entry)
488+
return;
489+
490+
need_update = mlx5e_ipsec_update_esn_state(sa_entry);
491+
if (!need_update)
492+
return;
493+
494+
modify_work = kzalloc(sizeof(*modify_work), GFP_ATOMIC);
495+
if (!modify_work)
496+
return;
497+
498+
mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &modify_work->attrs);
499+
modify_work->sa_entry = sa_entry;
500+
501+
INIT_WORK(&modify_work->work, _update_xfrm_state);
502+
WARN_ON(!queue_work(sa_entry->ipsec->wq, &modify_work->work));
503+
}
504+
408505
static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = {
409506
.xdo_dev_state_add = mlx5e_xfrm_add_state,
410507
.xdo_dev_state_delete = mlx5e_xfrm_del_state,
411508
.xdo_dev_state_free = mlx5e_xfrm_free_state,
412509
.xdo_dev_offload_ok = mlx5e_ipsec_offload_ok,
510+
.xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state,
413511
};
414512

415513
void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@
4040
#include <net/xfrm.h>
4141
#include <linux/idr.h>
4242

43+
#include "accel/ipsec.h"
44+
4345
#define MLX5E_IPSEC_SADB_RX_BITS 10
46+
#define MLX5E_IPSEC_ESN_SCOPE_MID 0x80000000L
47+
4448
#define MLX5E_METADATA_ETHER_TYPE (0x8CE4)
4549
#define MLX5E_METADATA_ETHER_LEN 8
4650

@@ -82,6 +86,25 @@ struct mlx5e_ipsec {
8286
struct ida halloc;
8387
struct mlx5e_ipsec_sw_stats sw_stats;
8488
struct mlx5e_ipsec_stats stats;
89+
struct workqueue_struct *wq;
90+
};
91+
92+
struct mlx5e_ipsec_esn_state {
93+
u32 esn;
94+
u8 trigger: 1;
95+
u8 overlap: 1;
96+
};
97+
98+
struct mlx5e_ipsec_sa_entry {
99+
struct hlist_node hlist; /* Item in SADB_RX hashtable */
100+
struct mlx5e_ipsec_esn_state esn_state;
101+
unsigned int handle; /* Handle in SADB_RX */
102+
struct xfrm_state *x;
103+
struct mlx5e_ipsec *ipsec;
104+
struct mlx5_accel_esp_xfrm *xfrm;
105+
void *hw_context;
106+
void (*set_iv_op)(struct sk_buff *skb, struct xfrm_state *x,
107+
struct xfrm_offload *xo);
85108
};
86109

87110
void mlx5e_ipsec_build_inverse_table(void);

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,30 @@ static void mlx5e_ipsec_set_swp(struct sk_buff *skb,
176176
}
177177
}
178178

179-
static void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_offload *xo)
179+
void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x,
180+
struct xfrm_offload *xo)
181+
{
182+
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
183+
__u32 oseq = replay_esn->oseq;
184+
int iv_offset;
185+
__be64 seqno;
186+
u32 seq_hi;
187+
188+
if (unlikely(skb_is_gso(skb) && oseq < MLX5E_IPSEC_ESN_SCOPE_MID &&
189+
MLX5E_IPSEC_ESN_SCOPE_MID < (oseq - skb_shinfo(skb)->gso_segs))) {
190+
seq_hi = xo->seq.hi - 1;
191+
} else {
192+
seq_hi = xo->seq.hi;
193+
}
194+
195+
/* Place the SN in the IV field */
196+
seqno = cpu_to_be64(xo->seq.low + ((u64)seq_hi << 32));
197+
iv_offset = skb_transport_offset(skb) + sizeof(struct ip_esp_hdr);
198+
skb_store_bits(skb, iv_offset, &seqno, 8);
199+
}
200+
201+
void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x,
202+
struct xfrm_offload *xo)
180203
{
181204
int iv_offset;
182205
__be64 seqno;
@@ -228,6 +251,7 @@ struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
228251
struct mlx5e_priv *priv = netdev_priv(netdev);
229252
struct xfrm_offload *xo = xfrm_offload(skb);
230253
struct mlx5e_ipsec_metadata *mdata;
254+
struct mlx5e_ipsec_sa_entry *sa_entry;
231255
struct xfrm_state *x;
232256

233257
if (!xo)
@@ -262,7 +286,8 @@ struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
262286
goto drop;
263287
}
264288
mlx5e_ipsec_set_swp(skb, &wqe->eth, x->props.mode, xo);
265-
mlx5e_ipsec_set_iv(skb, xo);
289+
sa_entry = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle;
290+
sa_entry->set_iv_op(skb, x, xo);
266291
mlx5e_ipsec_set_metadata(skb, mdata, xo);
267292

268293
return skb;

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#ifdef CONFIG_MLX5_EN_IPSEC
3838

3939
#include <linux/skbuff.h>
40+
#include <net/xfrm.h>
4041
#include "en.h"
4142

4243
struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
@@ -46,6 +47,10 @@ void mlx5e_ipsec_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
4647
void mlx5e_ipsec_inverse_table_init(void);
4748
bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
4849
netdev_features_t features);
50+
void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x,
51+
struct xfrm_offload *xo);
52+
void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x,
53+
struct xfrm_offload *xo);
4954
struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
5055
struct mlx5e_tx_wqe *wqe,
5156
struct sk_buff *skb);

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,11 @@ u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev)
347347
if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, rx_no_trailer))
348348
ret |= MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER;
349349

350+
if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, esn)) {
351+
ret |= MLX5_ACCEL_IPSEC_CAP_ESN;
352+
ret |= MLX5_ACCEL_IPSEC_CAP_TX_IV_IS_ESN;
353+
}
354+
350355
return ret;
351356
}
352357

@@ -470,6 +475,23 @@ mlx5_fpga_ipsec_build_hw_xfrm(struct mlx5_core_dev *mdev,
470475
memcpy(&hw_sa->ipsec_sa_v1.gcm.salt, &aes_gcm->salt,
471476
sizeof(aes_gcm->salt));
472477

478+
/* esn */
479+
if (xfrm_attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) {
480+
hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_ESN_EN;
481+
hw_sa->ipsec_sa_v1.flags |=
482+
(xfrm_attrs->flags &
483+
MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ?
484+
MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0;
485+
hw_sa->esn = htonl(xfrm_attrs->esn);
486+
} else {
487+
hw_sa->ipsec_sa_v1.flags &= ~MLX5_FPGA_IPSEC_SA_ESN_EN;
488+
hw_sa->ipsec_sa_v1.flags &=
489+
~(xfrm_attrs->flags &
490+
MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ?
491+
MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0;
492+
hw_sa->esn = 0;
493+
}
494+
473495
/* rx handle */
474496
hw_sa->ipsec_sa_v1.sw_sa_handle = htonl(xfrm_attrs->sa_handle);
475497

include/linux/mlx5/accel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ enum mlx5_accel_ipsec_cap {
110110
MLX5_ACCEL_IPSEC_CAP_IPV6 = 1 << 3,
111111
MLX5_ACCEL_IPSEC_CAP_LSO = 1 << 4,
112112
MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER = 1 << 5,
113+
MLX5_ACCEL_IPSEC_CAP_ESN = 1 << 6,
114+
MLX5_ACCEL_IPSEC_CAP_TX_IV_IS_ESN = 1 << 7,
113115
};
114116

115117
#ifdef CONFIG_MLX5_ACCEL

include/linux/mlx5/mlx5_ifc_fpga.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ struct mlx5_ifc_fpga_ipsec_cmd_cap {
468468
} __packed;
469469

470470
enum mlx5_ifc_fpga_ipsec_sa_flags {
471+
MLX5_FPGA_IPSEC_SA_ESN_EN = BIT(0),
472+
MLX5_FPGA_IPSEC_SA_ESN_OVERLAP = BIT(1),
471473
MLX5_FPGA_IPSEC_SA_IPV6 = BIT(2),
472474
MLX5_FPGA_IPSEC_SA_DIR_SX = BIT(3),
473475
MLX5_FPGA_IPSEC_SA_SPI_EN = BIT(4),

0 commit comments

Comments
 (0)