Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <aviadye@mellanox.com>
Signed-off-by: Yossef Efraim <yossefe@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>

authored by

Aviad Yehezkel and committed by
Saeed Mahameed
cb010083 75ef3f55

+189 -12
+108 -10
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
··· 38 38 #include <linux/module.h> 39 39 40 40 #include "en.h" 41 - #include "accel/ipsec.h" 42 41 #include "en_accel/ipsec.h" 43 42 #include "en_accel/ipsec_rxtx.h" 44 43 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 - }; 53 44 54 45 static struct mlx5e_ipsec_sa_entry *to_ipsec_sa_entry(struct xfrm_state *x) 55 46 { ··· 112 121 ida_simple_remove(&ipsec->halloc, sa_entry->handle); 113 122 } 114 123 124 + static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry) 125 + { 126 + struct xfrm_replay_state_esn *replay_esn; 127 + u32 seq_bottom; 128 + u8 overlap; 129 + u32 *esn; 130 + 131 + if (!(sa_entry->x->props.flags & XFRM_STATE_ESN)) { 132 + sa_entry->esn_state.trigger = 0; 133 + return false; 134 + } 135 + 136 + replay_esn = sa_entry->x->replay_esn; 137 + seq_bottom = replay_esn->seq - replay_esn->replay_window + 1; 138 + overlap = sa_entry->esn_state.overlap; 139 + 140 + sa_entry->esn_state.esn = xfrm_replay_seqhi(sa_entry->x, 141 + htonl(seq_bottom)); 142 + esn = &sa_entry->esn_state.esn; 143 + 144 + sa_entry->esn_state.trigger = 1; 145 + if (unlikely(overlap && seq_bottom < MLX5E_IPSEC_ESN_SCOPE_MID)) { 146 + ++(*esn); 147 + sa_entry->esn_state.overlap = 0; 148 + return true; 149 + } else if (unlikely(!overlap && 150 + (seq_bottom >= MLX5E_IPSEC_ESN_SCOPE_MID))) { 151 + sa_entry->esn_state.overlap = 1; 152 + return true; 153 + } 154 + 155 + return false; 156 + } 157 + 115 158 static void 116 159 mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, 117 160 struct mlx5_accel_esp_xfrm_attrs *attrs) ··· 176 151 177 152 /* iv len */ 178 153 aes_gcm->icv_len = x->aead->alg_icv_len; 154 + 155 + /* esn */ 156 + if (sa_entry->esn_state.trigger) { 157 + attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED; 158 + attrs->esn = sa_entry->esn_state.esn; 159 + if (sa_entry->esn_state.overlap) 160 + attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP; 161 + } 179 162 180 163 /* rx handle */ 181 164 attrs->sa_handle = sa_entry->handle; ··· 220 187 netdev_info(netdev, "Cannot offload compressed xfrm states\n"); 221 188 return -EINVAL; 222 189 } 223 - if (x->props.flags & XFRM_STATE_ESN) { 190 + if (x->props.flags & XFRM_STATE_ESN && 191 + !(mlx5_accel_ipsec_device_caps(priv->mdev) & 192 + MLX5_ACCEL_IPSEC_CAP_ESN)) { 224 193 netdev_info(netdev, "Cannot offload ESN xfrm states\n"); 225 194 return -EINVAL; 226 195 } ··· 312 277 netdev_info(netdev, "Failed adding to SADB_RX: %d\n", err); 313 278 goto err_entry; 314 279 } 280 + } else { 281 + sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ? 282 + mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv; 315 283 } 284 + 285 + /* check esn */ 286 + mlx5e_ipsec_update_esn_state(sa_entry); 316 287 317 288 /* create xfrm */ 318 289 mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &attrs); ··· 385 344 return; 386 345 387 346 if (sa_entry->hw_context) { 347 + flush_workqueue(sa_entry->ipsec->wq); 388 348 mlx5_accel_esp_free_hw_context(sa_entry->hw_context); 389 349 mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm); 390 350 } ··· 416 374 ipsec->en_priv->ipsec = ipsec; 417 375 ipsec->no_trailer = !!(mlx5_accel_ipsec_device_caps(priv->mdev) & 418 376 MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER); 377 + ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0, 378 + priv->netdev->name); 379 + if (!ipsec->wq) { 380 + kfree(ipsec); 381 + return -ENOMEM; 382 + } 419 383 netdev_dbg(priv->netdev, "IPSec attached to netdevice\n"); 420 384 return 0; 421 385 } ··· 432 384 433 385 if (!ipsec) 434 386 return; 387 + 388 + drain_workqueue(ipsec->wq); 389 + destroy_workqueue(ipsec->wq); 435 390 436 391 ida_destroy(&ipsec->halloc); 437 392 kfree(ipsec); ··· 456 405 return true; 457 406 } 458 407 408 + struct mlx5e_ipsec_modify_state_work { 409 + struct work_struct work; 410 + struct mlx5_accel_esp_xfrm_attrs attrs; 411 + struct mlx5e_ipsec_sa_entry *sa_entry; 412 + }; 413 + 414 + static void _update_xfrm_state(struct work_struct *work) 415 + { 416 + int ret; 417 + struct mlx5e_ipsec_modify_state_work *modify_work = 418 + container_of(work, struct mlx5e_ipsec_modify_state_work, work); 419 + struct mlx5e_ipsec_sa_entry *sa_entry = modify_work->sa_entry; 420 + 421 + ret = mlx5_accel_esp_modify_xfrm(sa_entry->xfrm, 422 + &modify_work->attrs); 423 + if (ret) 424 + netdev_warn(sa_entry->ipsec->en_priv->netdev, 425 + "Not an IPSec offload device\n"); 426 + 427 + kfree(modify_work); 428 + } 429 + 430 + static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x) 431 + { 432 + struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x); 433 + struct mlx5e_ipsec_modify_state_work *modify_work; 434 + bool need_update; 435 + 436 + if (!sa_entry) 437 + return; 438 + 439 + need_update = mlx5e_ipsec_update_esn_state(sa_entry); 440 + if (!need_update) 441 + return; 442 + 443 + modify_work = kzalloc(sizeof(*modify_work), GFP_ATOMIC); 444 + if (!modify_work) 445 + return; 446 + 447 + mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &modify_work->attrs); 448 + modify_work->sa_entry = sa_entry; 449 + 450 + INIT_WORK(&modify_work->work, _update_xfrm_state); 451 + WARN_ON(!queue_work(sa_entry->ipsec->wq, &modify_work->work)); 452 + } 453 + 459 454 static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = { 460 455 .xdo_dev_state_add = mlx5e_xfrm_add_state, 461 456 .xdo_dev_state_delete = mlx5e_xfrm_del_state, 462 457 .xdo_dev_state_free = mlx5e_xfrm_free_state, 463 458 .xdo_dev_offload_ok = mlx5e_ipsec_offload_ok, 459 + .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state, 464 460 }; 465 461 466 462 void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
+23
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
··· 40 40 #include <net/xfrm.h> 41 41 #include <linux/idr.h> 42 42 43 + #include "accel/ipsec.h" 44 + 43 45 #define MLX5E_IPSEC_SADB_RX_BITS 10 46 + #define MLX5E_IPSEC_ESN_SCOPE_MID 0x80000000L 47 + 44 48 #define MLX5E_METADATA_ETHER_TYPE (0x8CE4) 45 49 #define MLX5E_METADATA_ETHER_LEN 8 46 50 ··· 86 82 struct ida halloc; 87 83 struct mlx5e_ipsec_sw_stats sw_stats; 88 84 struct mlx5e_ipsec_stats stats; 85 + struct workqueue_struct *wq; 86 + }; 87 + 88 + struct mlx5e_ipsec_esn_state { 89 + u32 esn; 90 + u8 trigger: 1; 91 + u8 overlap: 1; 92 + }; 93 + 94 + struct mlx5e_ipsec_sa_entry { 95 + struct hlist_node hlist; /* Item in SADB_RX hashtable */ 96 + struct mlx5e_ipsec_esn_state esn_state; 97 + unsigned int handle; /* Handle in SADB_RX */ 98 + struct xfrm_state *x; 99 + struct mlx5e_ipsec *ipsec; 100 + struct mlx5_accel_esp_xfrm *xfrm; 101 + void *hw_context; 102 + void (*set_iv_op)(struct sk_buff *skb, struct xfrm_state *x, 103 + struct xfrm_offload *xo); 89 104 }; 90 105 91 106 void mlx5e_ipsec_build_inverse_table(void);
+27 -2
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
··· 176 176 } 177 177 } 178 178 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) 180 203 { 181 204 int iv_offset; 182 205 __be64 seqno; ··· 251 228 struct mlx5e_priv *priv = netdev_priv(netdev); 252 229 struct xfrm_offload *xo = xfrm_offload(skb); 253 230 struct mlx5e_ipsec_metadata *mdata; 231 + struct mlx5e_ipsec_sa_entry *sa_entry; 254 232 struct xfrm_state *x; 255 233 256 234 if (!xo) ··· 286 262 goto drop; 287 263 } 288 264 mlx5e_ipsec_set_swp(skb, &wqe->eth, x->props.mode, xo); 289 - mlx5e_ipsec_set_iv(skb, xo); 265 + sa_entry = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle; 266 + sa_entry->set_iv_op(skb, x, xo); 290 267 mlx5e_ipsec_set_metadata(skb, mdata, xo); 291 268 292 269 return skb;
+5
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
··· 37 37 #ifdef CONFIG_MLX5_EN_IPSEC 38 38 39 39 #include <linux/skbuff.h> 40 + #include <net/xfrm.h> 40 41 #include "en.h" 41 42 42 43 struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev, ··· 47 46 void mlx5e_ipsec_inverse_table_init(void); 48 47 bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev, 49 48 netdev_features_t features); 49 + void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x, 50 + struct xfrm_offload *xo); 51 + void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x, 52 + struct xfrm_offload *xo); 50 53 struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev, 51 54 struct mlx5e_tx_wqe *wqe, 52 55 struct sk_buff *skb);
+22
drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
··· 347 347 if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, rx_no_trailer)) 348 348 ret |= MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER; 349 349 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 + 350 355 return ret; 351 356 } 352 357 ··· 474 469 sizeof(aes_gcm->seq_iv)); 475 470 memcpy(&hw_sa->ipsec_sa_v1.gcm.salt, &aes_gcm->salt, 476 471 sizeof(aes_gcm->salt)); 472 + 473 + /* esn */ 474 + if (xfrm_attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) { 475 + hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_ESN_EN; 476 + hw_sa->ipsec_sa_v1.flags |= 477 + (xfrm_attrs->flags & 478 + MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ? 479 + MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0; 480 + hw_sa->esn = htonl(xfrm_attrs->esn); 481 + } else { 482 + hw_sa->ipsec_sa_v1.flags &= ~MLX5_FPGA_IPSEC_SA_ESN_EN; 483 + hw_sa->ipsec_sa_v1.flags &= 484 + ~(xfrm_attrs->flags & 485 + MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ? 486 + MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0; 487 + hw_sa->esn = 0; 488 + } 477 489 478 490 /* rx handle */ 479 491 hw_sa->ipsec_sa_v1.sw_sa_handle = htonl(xfrm_attrs->sa_handle);
+2
include/linux/mlx5/accel.h
··· 110 110 MLX5_ACCEL_IPSEC_CAP_IPV6 = 1 << 3, 111 111 MLX5_ACCEL_IPSEC_CAP_LSO = 1 << 4, 112 112 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, 113 115 }; 114 116 115 117 #ifdef CONFIG_MLX5_ACCEL
+2
include/linux/mlx5/mlx5_ifc_fpga.h
··· 468 468 } __packed; 469 469 470 470 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), 471 473 MLX5_FPGA_IPSEC_SA_IPV6 = BIT(2), 472 474 MLX5_FPGA_IPSEC_SA_DIR_SX = BIT(3), 473 475 MLX5_FPGA_IPSEC_SA_SPI_EN = BIT(4),