net/mlx5e: Support IPsec NAT-T functionality
authorLeon Romanovsky <leonro@nvidia.com>
Wed, 19 Jul 2023 09:26:55 +0000 (12:26 +0300)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 25 Jul 2023 13:08:57 +0000 (15:08 +0200)
Extend mlx5 IPsec packet offload to support UDP encapsulation
of IPsec ESP packets.

Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c

index 891d39b..658b7d8 100644 (file)
@@ -354,6 +354,12 @@ void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
 
        mlx5e_ipsec_init_limits(sa_entry, attrs);
        mlx5e_ipsec_init_macs(sa_entry, attrs);
+
+       if (x->encap) {
+               attrs->encap = true;
+               attrs->sport = x->encap->encap_sport;
+               attrs->dport = x->encap->encap_dport;
+       }
 }
 
 static int mlx5e_xfrm_validate_state(struct mlx5_core_dev *mdev,
@@ -387,8 +393,25 @@ static int mlx5e_xfrm_validate_state(struct mlx5_core_dev *mdev,
                return -EINVAL;
        }
        if (x->encap) {
-               NL_SET_ERR_MSG_MOD(extack, "Encapsulated xfrm state may not be offloaded");
-               return -EINVAL;
+               if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_ESPINUDP)) {
+                       NL_SET_ERR_MSG_MOD(extack, "Encapsulation is not supported");
+                       return -EINVAL;
+               }
+
+               if (x->encap->encap_type != UDP_ENCAP_ESPINUDP) {
+                       NL_SET_ERR_MSG_MOD(extack, "Encapsulation other than UDP is not supported");
+                       return -EINVAL;
+               }
+
+               if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) {
+                       NL_SET_ERR_MSG_MOD(extack, "Encapsulation is supported in packet offload mode only");
+                       return -EINVAL;
+               }
+
+               if (x->props.mode != XFRM_MODE_TRANSPORT) {
+                       NL_SET_ERR_MSG_MOD(extack, "Encapsulation is supported in transport mode only");
+                       return -EINVAL;
+               }
        }
        if (!x->aead) {
                NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states without aead");
index b382b0c..7a70472 100644 (file)
@@ -94,13 +94,20 @@ struct mlx5_accel_esp_xfrm_attrs {
        u8 dir : 2;
        u8 type : 2;
        u8 drop : 1;
+       u8 encap : 1;
        u8 family;
        struct mlx5_replay_esn replay_esn;
        u32 authsize;
        u32 reqid;
        struct mlx5_ipsec_lft lft;
-       u8 smac[ETH_ALEN];
-       u8 dmac[ETH_ALEN];
+       union {
+               u8 smac[ETH_ALEN];
+               __be16 sport;
+       };
+       union {
+               u8 dmac[ETH_ALEN];
+               __be16 dport;
+       };
 };
 
 enum mlx5_ipsec_cap {
index dbe87bf..47baf98 100644 (file)
@@ -951,37 +951,70 @@ free_reformatbf:
        return -EINVAL;
 }
 
+static int get_reformat_type(struct mlx5_accel_esp_xfrm_attrs *attrs)
+{
+       switch (attrs->dir) {
+       case XFRM_DEV_OFFLOAD_IN:
+               if (attrs->encap)
+                       return MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT_OVER_UDP;
+               return MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT;
+       case XFRM_DEV_OFFLOAD_OUT:
+               if (attrs->family == AF_INET) {
+                       if (attrs->encap)
+                               return MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_UDPV4;
+                       return MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV4;
+               }
+
+               if (attrs->encap)
+                       return MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_UDPV6;
+               return MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV6;
+       default:
+               WARN_ON(true);
+       }
+
+       return -EINVAL;
+}
+
 static int
 setup_pkt_transport_reformat(struct mlx5_accel_esp_xfrm_attrs *attrs,
                             struct mlx5_pkt_reformat_params *reformat_params)
 {
-       u8 *reformatbf;
+       struct udphdr *udphdr;
+       char *reformatbf;
+       size_t bfflen;
        __be32 spi;
+       void *hdr;
+
+       reformat_params->type = get_reformat_type(attrs);
+       if (reformat_params->type < 0)
+               return reformat_params->type;
 
        switch (attrs->dir) {
        case XFRM_DEV_OFFLOAD_IN:
-               reformat_params->type = MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT;
                break;
        case XFRM_DEV_OFFLOAD_OUT:
-               if (attrs->family == AF_INET)
-                       reformat_params->type =
-                               MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV4;
-               else
-                       reformat_params->type =
-                               MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV6;
-
-               reformatbf = kzalloc(MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_SIZE,
-                                    GFP_KERNEL);
+               bfflen = MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_SIZE;
+               if (attrs->encap)
+                       bfflen += sizeof(*udphdr);
+
+               reformatbf = kzalloc(bfflen, GFP_KERNEL);
                if (!reformatbf)
                        return -ENOMEM;
 
+               hdr = reformatbf;
+               if (attrs->encap) {
+                       udphdr = (struct udphdr *)reformatbf;
+                       udphdr->source = attrs->sport;
+                       udphdr->dest = attrs->dport;
+                       hdr += sizeof(*udphdr);
+               }
+
                /* convert to network format */
                spi = htonl(attrs->spi);
-               memcpy(reformatbf, &spi, sizeof(spi));
+               memcpy(hdr, &spi, sizeof(spi));
 
                reformat_params->param_0 = attrs->authsize;
-               reformat_params->size =
-                       MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_SIZE;
+               reformat_params->size = bfflen;
                reformat_params->data = reformatbf;
                break;
        default: