nfp: support IPsec offloading for NFP3800
authorHuanhuan Wang <huanhuan.wang@corigine.com>
Wed, 8 Feb 2023 09:10:00 +0000 (10:10 +0100)
committerJakub Kicinski <kuba@kernel.org>
Fri, 10 Feb 2023 06:22:19 +0000 (22:22 -0800)
Add IPsec offloading support for NFP3800. Include data
plane and control plane.

Data plane: add IPsec packet process flow in NFP3800
datapath (NFDk).

Control plane: add an algorithm support distinction flow
in xfrm hook function xdo_dev_state_add(), as NFP3800 has
a different set of IPsec algorithm support.

This matches existing support for the NFP6000/NFP4000 and
their NFD3 datapath.

In addition, fixup the md_bytes calculation for NFD3 datapath
to make sure the two datapahts are keept in sync.

Signed-off-by: Huanhuan Wang <huanhuan.wang@corigine.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Link: https://lore.kernel.org/r/20230208091000.4139974-1-simon.horman@corigine.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/netronome/nfp/Makefile
drivers/net/ethernet/netronome/nfp/crypto/ipsec.c
drivers/net/ethernet/netronome/nfp/nfd3/dp.c
drivers/net/ethernet/netronome/nfp/nfdk/dp.c
drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/nfdk/nfdk.h

index c90d35f5ebca2614de3379e585820f28e165f74a..808599b8066e9fe6522b7237ba9c1bdd87af9ded 100644 (file)
@@ -80,7 +80,7 @@ nfp-objs += \
            abm/main.o
 endif
 
-nfp-$(CONFIG_NFP_NET_IPSEC) += crypto/ipsec.o nfd3/ipsec.o
+nfp-$(CONFIG_NFP_NET_IPSEC) += crypto/ipsec.o nfd3/ipsec.o nfdk/ipsec.o
 
 nfp-$(CONFIG_NFP_DEBUG) += nfp_net_debugfs.o
 
index b442631779813adaca0915316a72fe8a694ca988..b8bc89fc2540485a0b1973cd664fb30848e42cd8 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/ktime.h>
 #include <net/xfrm.h>
 
+#include "../nfpcore/nfp_dev.h"
 #include "../nfp_net_ctrl.h"
 #include "../nfp_net.h"
 #include "crypto.h"
@@ -330,6 +331,10 @@ static int nfp_net_xfrm_add_state(struct xfrm_state *x,
                trunc_len = -1;
                break;
        case SADB_AALG_MD5HMAC:
+               if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
+                       NL_SET_ERR_MSG_MOD(extack, "Unsupported authentication algorithm");
+                       return -EINVAL;
+               }
                set_md5hmac(cfg, &trunc_len);
                break;
        case SADB_AALG_SHA1HMAC:
@@ -373,6 +378,10 @@ static int nfp_net_xfrm_add_state(struct xfrm_state *x,
                cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_NULL;
                break;
        case SADB_EALG_3DESCBC:
+               if (nn->pdev->device == PCI_DEVICE_ID_NFP3800) {
+                       NL_SET_ERR_MSG_MOD(extack, "Unsupported encryption algorithm for offload");
+                       return -EINVAL;
+               }
                cfg->ctrl_word.cimode = NFP_IPSEC_CIMODE_CBC;
                cfg->ctrl_word.cipher = NFP_IPSEC_CIPHER_3DES;
                break;
index 861082c5dbffbcfa68e1c44905190eb453c90940..59fb0583cc080530af616167dd56375b4c8f351b 100644 (file)
@@ -192,10 +192,10 @@ static int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb,
                return 0;
 
        md_bytes = sizeof(meta_id) +
-                  !!md_dst * NFP_NET_META_PORTID_SIZE +
-                  !!tls_handle * NFP_NET_META_CONN_HANDLE_SIZE +
-                  vlan_insert * NFP_NET_META_VLAN_SIZE +
-                  *ipsec * NFP_NET_META_IPSEC_FIELD_SIZE; /* IPsec has 12 bytes of metadata */
+                  (!!md_dst ? NFP_NET_META_PORTID_SIZE : 0) +
+                  (!!tls_handle ? NFP_NET_META_CONN_HANDLE_SIZE : 0) +
+                  (vlan_insert ? NFP_NET_META_VLAN_SIZE : 0) +
+                  (*ipsec ? NFP_NET_META_IPSEC_FIELD_SIZE : 0);
 
        if (unlikely(skb_cow_head(skb, md_bytes)))
                return -ENOMEM;
@@ -226,9 +226,6 @@ static int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb,
                meta_id |= NFP_NET_META_VLAN;
        }
        if (*ipsec) {
-               /* IPsec has three consecutive 4-bit IPsec metadata types,
-                * so in total IPsec has three 4 bytes of metadata.
-                */
                data -= NFP_NET_META_IPSEC_SIZE;
                put_unaligned_be32(offload_info.seq_hi, data);
                data -= NFP_NET_META_IPSEC_SIZE;
index ccacb6ab6c39f8742c310e1081b3051970618217..d60c0e991a91cf3503c44d41f3cfbdf4ed419ac6 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/overflow.h>
 #include <linux/sizes.h>
 #include <linux/bitfield.h>
+#include <net/xfrm.h>
 
 #include "../nfp_app.h"
 #include "../nfp_net.h"
@@ -172,25 +173,32 @@ close_block:
 
 static int
 nfp_nfdk_prep_tx_meta(struct nfp_net_dp *dp, struct nfp_app *app,
-                     struct sk_buff *skb)
+                     struct sk_buff *skb, bool *ipsec)
 {
        struct metadata_dst *md_dst = skb_metadata_dst(skb);
+       struct nfp_ipsec_offload offload_info;
        unsigned char *data;
        bool vlan_insert;
        u32 meta_id = 0;
        int md_bytes;
 
+#ifdef CONFIG_NFP_NET_IPSEC
+       if (xfrm_offload(skb))
+               *ipsec = nfp_net_ipsec_tx_prep(dp, skb, &offload_info);
+#endif
+
        if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX))
                md_dst = NULL;
 
        vlan_insert = skb_vlan_tag_present(skb) && (dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN_V2);
 
-       if (!(md_dst || vlan_insert))
+       if (!(md_dst || vlan_insert || *ipsec))
                return 0;
 
        md_bytes = sizeof(meta_id) +
-                  !!md_dst * NFP_NET_META_PORTID_SIZE +
-                  vlan_insert * NFP_NET_META_VLAN_SIZE;
+                  (!!md_dst ? NFP_NET_META_PORTID_SIZE : 0) +
+                  (vlan_insert ? NFP_NET_META_VLAN_SIZE : 0) +
+                  (*ipsec ? NFP_NET_META_IPSEC_FIELD_SIZE : 0);
 
        if (unlikely(skb_cow_head(skb, md_bytes)))
                return -ENOMEM;
@@ -212,6 +220,17 @@ nfp_nfdk_prep_tx_meta(struct nfp_net_dp *dp, struct nfp_app *app,
                meta_id |= NFP_NET_META_VLAN;
        }
 
+       if (*ipsec) {
+               data -= NFP_NET_META_IPSEC_SIZE;
+               put_unaligned_be32(offload_info.seq_hi, data);
+               data -= NFP_NET_META_IPSEC_SIZE;
+               put_unaligned_be32(offload_info.seq_low, data);
+               data -= NFP_NET_META_IPSEC_SIZE;
+               put_unaligned_be32(offload_info.handle - 1, data);
+               meta_id <<= NFP_NET_META_IPSEC_FIELD_SIZE;
+               meta_id |= NFP_NET_META_IPSEC << 8 | NFP_NET_META_IPSEC << 4 | NFP_NET_META_IPSEC;
+       }
+
        meta_id = FIELD_PREP(NFDK_META_LEN, md_bytes) |
                  FIELD_PREP(NFDK_META_FIELDS, meta_id);
 
@@ -243,6 +262,7 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev)
        struct nfp_net_dp *dp;
        int nr_frags, wr_idx;
        dma_addr_t dma_addr;
+       bool ipsec = false;
        u64 metadata;
 
        dp = &nn->dp;
@@ -263,7 +283,7 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev)
                return NETDEV_TX_BUSY;
        }
 
-       metadata = nfp_nfdk_prep_tx_meta(dp, nn->app, skb);
+       metadata = nfp_nfdk_prep_tx_meta(dp, nn->app, skb, &ipsec);
        if (unlikely((int)metadata < 0))
                goto err_flush;
 
@@ -361,6 +381,9 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev)
 
        (txd - 1)->dma_len_type = cpu_to_le16(dlen_type | NFDK_DESC_TX_EOP);
 
+       if (ipsec)
+               metadata = nfp_nfdk_ipsec_tx(metadata, skb);
+
        if (!skb_is_gso(skb)) {
                real_len = skb->len;
                /* Metadata desc */
@@ -760,6 +783,15 @@ nfp_nfdk_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
                                return false;
                        data += sizeof(struct nfp_net_tls_resync_req);
                        break;
+#ifdef CONFIG_NFP_NET_IPSEC
+               case NFP_NET_META_IPSEC:
+                       /* Note: IPsec packet could have zero saidx, so need add 1
+                        * to indicate packet is IPsec packet within driver.
+                        */
+                       meta->ipsec_saidx = get_unaligned_be32(data) + 1;
+                       data += 4;
+                       break;
+#endif
                default:
                        return true;
                }
@@ -1186,6 +1218,13 @@ static int nfp_nfdk_rx(struct nfp_net_rx_ring *rx_ring, int budget)
                        continue;
                }
 
+#ifdef CONFIG_NFP_NET_IPSEC
+               if (meta.ipsec_saidx != 0 && unlikely(nfp_net_ipsec_rx(&meta, skb))) {
+                       nfp_nfdk_rx_drop(dp, r_vec, rx_ring, NULL, skb);
+                       continue;
+               }
+#endif
+
                if (meta_len_xdp)
                        skb_metadata_set(skb, meta_len_xdp);
 
diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c b/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c
new file mode 100644 (file)
index 0000000..58d8f59
--- /dev/null
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2023 Corigine, Inc */
+
+#include <net/xfrm.h>
+
+#include "../nfp_net.h"
+#include "nfdk.h"
+
+u64 nfp_nfdk_ipsec_tx(u64 flags, struct sk_buff *skb)
+{
+       struct xfrm_state *x = xfrm_input_state(skb);
+
+       if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM))
+               flags |= NFDK_DESC_TX_L3_CSUM | NFDK_DESC_TX_L4_CSUM;
+
+       return flags;
+}
index 0ea51d9f2325d884426890b53b397d3d1c688b44..fe55980348e9697d9b6ae9d6707f950d6c7a05fb 100644 (file)
@@ -125,4 +125,12 @@ nfp_nfdk_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
 void nfp_nfdk_ctrl_poll(struct tasklet_struct *t);
 void nfp_nfdk_rx_ring_fill_freelist(struct nfp_net_dp *dp,
                                    struct nfp_net_rx_ring *rx_ring);
+#ifndef CONFIG_NFP_NET_IPSEC
+static inline u64 nfp_nfdk_ipsec_tx(u64 flags, struct sk_buff *skb)
+{
+       return flags;
+}
+#else
+u64 nfp_nfdk_ipsec_tx(u64 flags, struct sk_buff *skb);
+#endif
 #endif