aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c')
-rw-r--r--sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c2270
1 files changed, 2270 insertions, 0 deletions
diff --git a/sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c b/sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c
new file mode 100644
index 000000000000..fb9ca94278db
--- /dev/null
+++ b/sys/dev/mlx5/mlx5_accel/mlx5_ipsec_fs.c
@@ -0,0 +1,2270 @@
+/*-
+ * Copyright (c) 2023 NVIDIA corporation & affiliates.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_ipsec.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <net/pfkeyv2.h>
+#include <netipsec/key_var.h>
+#include <netipsec/keydb.h>
+#include <netipsec/ipsec.h>
+#include <netipsec/xform.h>
+#include <netipsec/ipsec_offload.h>
+#include <dev/mlx5/fs.h>
+#include <dev/mlx5/mlx5_en/en.h>
+#include <dev/mlx5/qp.h>
+#include <dev/mlx5/mlx5_accel/ipsec.h>
+#include <dev/mlx5/mlx5_core/fs_core.h>
+#include <dev/mlx5/mlx5_core/fs_chains.h>
+
+/*
+ * TX tables are organized differently for Ethernet and for RoCE:
+ *
+ * +=========+
+ * Ethernet Tx | SA KSPI | match
+ * --------------------->|Flowtable|----->+ +
+ * | |\ | / \
+ * +=========+ | | / \ +=========+ +=========+
+ * miss | | / \ | Status | | |
+ * DROP<--------+ |---->|Encrypt|------>|Flowtable|---->| TX NS |
+ * | \ / | | | |
+ * | \ / +=========+ +=========+
+ * +=========+ +=========+ | \ / |
+ * RoCE | Policy | match|SA ReqId |match| + |
+ * Tx |Flowtable|----->|Flowtable|---->+ |
+ * ---->|IP header| |ReqId+IP | |
+ * | | | header |--------------------------------+
+ * +=========+ +=========+ miss |
+ * | |
+ * | miss |
+ * +-------------------------------------------------------
+ *
+ * +=========+
+ * | RDMA |
+ * |Flowtable|
+ * | |
+ * Rx Tables and rules: +=========+
+ * + /
+ * +=========+ +=========+ / \ +=========+ +=========+ /match
+ * | Policy | | SA | / \ | Status | | RoCE |/
+ * ---->|Flowtable| match|Flowtable| match / \ |Flowtable|----->|Flowtable|
+ * |IP header|----->|IP header|----->|Decrypt|----->| | | Roce V2 |
+ * | | |+ESP+SPI | \ / | | | UDP port|\
+ * +=========+ +=========+ \ / +=========+ +=========+ \miss
+ * | | \ / \
+ * | | + +=========+
+ * | miss | miss | Ethernet|
+ * +--------------->---------------------------------------------------->| RX NS |
+ * | |
+ * +=========+
+ *
+ */
+
+#define NUM_IPSEC_FTE BIT(15)
+#define IPSEC_TUNNEL_DEFAULT_TTL 0x40
+
+struct mlx5e_ipsec_fc {
+ struct mlx5_fc *cnt;
+ struct mlx5_fc *drop;
+};
+
+struct mlx5e_ipsec_ft {
+ struct mutex mutex; /* Protect changes to this struct */
+ struct mlx5_flow_table *pol;
+ struct mlx5_flow_table *sa_kspi;
+ struct mlx5_flow_table *sa;
+ struct mlx5_flow_table *status;
+ u32 refcnt;
+};
+
+struct mlx5e_ipsec_tx_roce {
+ struct mlx5_flow_group *g;
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_namespace *ns;
+};
+
+struct mlx5e_ipsec_miss {
+ struct mlx5_flow_group *group;
+ struct mlx5_flow_handle *rule;
+};
+
+struct mlx5e_ipsec_tx {
+ struct mlx5e_ipsec_ft ft;
+ struct mlx5e_ipsec_miss pol;
+ struct mlx5e_ipsec_miss kspi_miss;
+ struct mlx5e_ipsec_rule status;
+ struct mlx5e_ipsec_rule kspi_bypass_rule; /*rule for IPSEC bypass*/
+ struct mlx5_flow_namespace *ns;
+ struct mlx5e_ipsec_fc *fc;
+ struct mlx5_fs_chains *chains;
+ struct mlx5e_ipsec_tx_roce roce;
+};
+
+struct mlx5e_ipsec_rx_roce {
+ struct mlx5_flow_group *g;
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_handle *rule;
+ struct mlx5e_ipsec_miss roce_miss;
+
+ struct mlx5_flow_table *ft_rdma;
+ struct mlx5_flow_namespace *ns_rdma;
+};
+
+struct mlx5e_ipsec_rx_ip_type {
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_namespace *ns;
+ struct mlx5_flow_handle *ipv4_rule;
+ struct mlx5_flow_handle *ipv6_rule;
+ struct mlx5e_ipsec_miss miss;
+};
+
+struct mlx5e_ipsec_rx {
+ struct mlx5e_ipsec_ft ft;
+ struct mlx5e_ipsec_miss pol;
+ struct mlx5e_ipsec_miss sa;
+ struct mlx5e_ipsec_rule status;
+ struct mlx5_flow_namespace *ns;
+ struct mlx5e_ipsec_fc *fc;
+ struct mlx5_fs_chains *chains;
+ struct mlx5e_ipsec_rx_roce roce;
+};
+
+static void setup_fte_reg_a_with_tag(struct mlx5_flow_spec *spec,
+ u16 kspi);
+static void setup_fte_reg_a_no_tag(struct mlx5_flow_spec *spec);
+
+static void setup_fte_no_frags(struct mlx5_flow_spec *spec)
+{
+ /* Non fragmented */
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.frag);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.frag, 0);
+}
+
+static void setup_fte_esp(struct mlx5_flow_spec *spec)
+{
+ /* ESP header */
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_protocol, IPPROTO_ESP);
+}
+
+static void setup_fte_spi(struct mlx5_flow_spec *spec, u32 spi, bool encap)
+{
+ /* SPI number */
+ spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS;
+
+ if (encap) {
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters.inner_esp_spi);
+ MLX5_SET(fte_match_param, spec->match_value, misc_parameters.inner_esp_spi, spi);
+ } else {
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters.outer_esp_spi);
+ MLX5_SET(fte_match_param, spec->match_value, misc_parameters.outer_esp_spi, spi);
+ }
+}
+
+static void
+setup_fte_vid(struct mlx5_flow_spec *spec, u16 vid)
+{
+ /* virtual lan tag */
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ outer_headers.cvlan_tag);
+ MLX5_SET(fte_match_param, spec->match_value,
+ outer_headers.cvlan_tag, 1);
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ outer_headers.first_vid);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.first_vid,
+ vid);
+}
+
+static void
+clear_fte_vid(struct mlx5_flow_spec *spec)
+{
+ MLX5_SET(fte_match_param, spec->match_criteria,
+ outer_headers.cvlan_tag, 0);
+ MLX5_SET(fte_match_param, spec->match_value,
+ outer_headers.cvlan_tag, 0);
+ MLX5_SET(fte_match_param, spec->match_criteria,
+ outer_headers.first_vid, 0);
+ MLX5_SET(fte_match_param, spec->match_value,
+ outer_headers.first_vid, 0);
+}
+
+static void
+setup_fte_no_vid(struct mlx5_flow_spec *spec)
+{
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ outer_headers.cvlan_tag);
+ MLX5_SET(fte_match_param, spec->match_value,
+ outer_headers.cvlan_tag, 0);
+}
+
+static struct mlx5_fs_chains *
+ipsec_chains_create(struct mlx5_core_dev *mdev, struct mlx5_flow_table *miss_ft,
+ enum mlx5_flow_namespace_type ns, int base_prio,
+ int base_level, struct mlx5_flow_table **root_ft)
+{
+ struct mlx5_chains_attr attr = {};
+ struct mlx5_fs_chains *chains;
+ struct mlx5_flow_table *ft;
+ int err;
+
+ attr.flags = MLX5_CHAINS_AND_PRIOS_SUPPORTED |
+ MLX5_CHAINS_IGNORE_FLOW_LEVEL_SUPPORTED;
+ attr.max_grp_num = 2;
+ attr.default_ft = miss_ft;
+ attr.ns = ns;
+ attr.fs_base_prio = base_prio;
+ attr.fs_base_level = base_level;
+ chains = mlx5_chains_create(mdev, &attr);
+ if (IS_ERR(chains))
+ return chains;
+
+ /* Create chain 0, prio 1, level 0 to connect chains to prev in fs_core */
+ ft = mlx5_chains_get_table(chains, 0, 1, 0);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_chains_get;
+ }
+
+ *root_ft = ft;
+ return chains;
+
+err_chains_get:
+ mlx5_chains_destroy(chains);
+ return ERR_PTR(err);
+}
+
+static void ipsec_chains_destroy(struct mlx5_fs_chains *chains)
+{
+ mlx5_chains_put_table(chains, 0, 1, 0);
+ mlx5_chains_destroy(chains);
+}
+
+static struct mlx5_flow_table *
+ipsec_chains_get_table(struct mlx5_fs_chains *chains, u32 prio)
+{
+ return mlx5_chains_get_table(chains, 0, prio + 1, 0);
+}
+
+static void ipsec_chains_put_table(struct mlx5_fs_chains *chains, u32 prio)
+{
+ mlx5_chains_put_table(chains, 0, prio + 1, 0);
+}
+
+static struct mlx5_flow_table *ipsec_rx_ft_create(struct mlx5_flow_namespace *ns,
+ int level, int prio,
+ int max_num_groups)
+{
+ struct mlx5_flow_table_attr ft_attr = {};
+
+ ft_attr.max_fte = NUM_IPSEC_FTE;
+ ft_attr.level = level;
+ ft_attr.prio = prio;
+ ft_attr.autogroup.max_num_groups = max_num_groups;
+ ft_attr.autogroup.num_reserved_entries = 1;
+
+ return mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
+}
+
+static int ipsec_miss_create(struct mlx5_core_dev *mdev,
+ struct mlx5_flow_table *ft,
+ struct mlx5e_ipsec_miss *miss,
+ struct mlx5_flow_destination *dest)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_spec *spec;
+ u32 *flow_group_in;
+ int err = 0;
+
+ flow_group_in = kvzalloc(inlen, GFP_KERNEL);
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!flow_group_in || !spec) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Create miss_group */
+ MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ft->max_fte - 1);
+ MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ft->max_fte - 1);
+ miss->group = mlx5_create_flow_group(ft, flow_group_in);
+ if (IS_ERR(miss->group)) {
+ err = PTR_ERR(miss->group);
+ mlx5_core_err(mdev, "fail to create IPsec miss_group err=%d\n",
+ err);
+ goto out;
+ }
+
+ if (dest)
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ else
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+ /* Create miss rule */
+ miss->rule = mlx5_add_flow_rules(ft, NULL, &flow_act, dest, 1);
+ if (IS_ERR(miss->rule)) {
+ mlx5_destroy_flow_group(miss->group);
+ err = PTR_ERR(miss->rule);
+ mlx5_core_err(mdev, "fail to create IPsec miss_rule err=%d\n",
+ err);
+ goto out;
+ }
+out:
+ kvfree(flow_group_in);
+ kvfree(spec);
+ return err;
+}
+
+static int setup_modify_header(struct mlx5_core_dev *mdev, u32 val, u8 dir,
+ struct mlx5_flow_act *flow_act)
+{
+ u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
+ enum mlx5_flow_namespace_type ns_type;
+ struct mlx5_modify_hdr *modify_hdr;
+
+ MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
+ switch (dir) {
+ case IPSEC_DIR_INBOUND:
+ MLX5_SET(set_action_in, action, field,
+ MLX5_ACTION_IN_FIELD_METADATA_REG_B);
+ ns_type = MLX5_FLOW_NAMESPACE_KERNEL;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ MLX5_SET(set_action_in, action, field,
+ MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);
+ ns_type = MLX5_FLOW_NAMESPACE_EGRESS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ MLX5_SET(set_action_in, action, data, val);
+ MLX5_SET(set_action_in, action, offset, 0);
+ MLX5_SET(set_action_in, action, length, 32);
+
+ modify_hdr = mlx5_modify_header_alloc(mdev, ns_type, 1, action);
+ if (IS_ERR(modify_hdr)) {
+ mlx5_core_err(mdev, "Failed to allocate modify_header %ld\n",
+ PTR_ERR(modify_hdr));
+ return PTR_ERR(modify_hdr);
+ }
+
+ flow_act->modify_hdr = modify_hdr;
+ flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
+ return 0;
+}
+
+static int
+setup_pkt_transport_reformat(struct mlx5_accel_esp_xfrm_attrs *attrs,
+ struct mlx5_pkt_reformat_params *reformat_params)
+{
+ struct udphdr *udphdr;
+ size_t bfflen = 16;
+ char *reformatbf;
+ __be32 spi;
+ void *hdr;
+
+ if (attrs->family == AF_INET) {
+ if (attrs->encap)
+ reformat_params->type = MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_UDPV4;
+ else
+ reformat_params->type = MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV4;
+ } else {
+ if (attrs->encap)
+ reformat_params->type =
+ MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_UDPV6;
+ else
+ reformat_params->type =
+ MLX5_REFORMAT_TYPE_ADD_ESP_TRANSPORT_OVER_IPV6;
+ }
+
+ 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->uh_sport = attrs->sport;
+ udphdr->uh_dport = attrs->dport;
+ hdr += sizeof(*udphdr);
+ }
+
+ /* convert to network format */
+ spi = htonl(attrs->spi);
+ memcpy(hdr, &spi, 4);
+
+ reformat_params->param_0 = attrs->authsize;
+ reformat_params->size = bfflen;
+ reformat_params->data = reformatbf;
+
+ return 0;
+}
+
+static int setup_pkt_reformat(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm_attrs *attrs,
+ struct mlx5_flow_act *flow_act)
+{
+ enum mlx5_flow_namespace_type ns_type = MLX5_FLOW_NAMESPACE_EGRESS;
+ struct mlx5_pkt_reformat_params reformat_params = {};
+ struct mlx5_pkt_reformat *pkt_reformat;
+ int ret;
+
+ if (attrs->dir == IPSEC_DIR_INBOUND) {
+ if (attrs->encap)
+ reformat_params.type = MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT_OVER_UDP;
+ else
+ reformat_params.type = MLX5_REFORMAT_TYPE_DEL_ESP_TRANSPORT;
+ ns_type = MLX5_FLOW_NAMESPACE_KERNEL;
+ goto cmd;
+ }
+
+ ret = setup_pkt_transport_reformat(attrs, &reformat_params);
+ if (ret)
+ return ret;
+cmd:
+ pkt_reformat =
+ mlx5_packet_reformat_alloc(mdev, &reformat_params, ns_type);
+ if (reformat_params.data)
+ kfree(reformat_params.data);
+ if (IS_ERR(pkt_reformat))
+ return PTR_ERR(pkt_reformat);
+
+ flow_act->pkt_reformat = pkt_reformat;
+ flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
+ return 0;
+}
+
+static void setup_fte_addr4(struct mlx5_flow_spec *spec, __be32 *saddr,
+ __be32 *daddr)
+{
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, 4);
+
+ memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4), saddr, 4);
+ memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4), daddr, 4);
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ outer_headers.src_ipv4_src_ipv6.ipv4_layout.ipv4);
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ outer_headers.dst_ipv4_dst_ipv6.ipv4_layout.ipv4);
+}
+
+static void setup_fte_addr6(struct mlx5_flow_spec *spec, __be32 *saddr,
+ __be32 *daddr)
+{
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, 6);
+
+ memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), saddr, 16);
+ memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), daddr, 16);
+ memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+ outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), 0xff, 16);
+ memset(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+ outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), 0xff, 16);
+}
+
+static void
+setup_fte_ip_version(struct mlx5_flow_spec *spec, u8 family)
+{
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version,
+ family == AF_INET ? 4 : 6);
+}
+
+static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
+ struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
+ struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
+ struct mlx5_flow_destination dest[2] = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ struct mlx5e_ipsec_rx *rx;
+ struct mlx5_fc *counter;
+ int err;
+
+ rx = (attrs->family == AF_INET) ? ipsec->rx_ipv4 : ipsec->rx_ipv6;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ if (!attrs->drop) {
+ err = setup_modify_header(mdev, sa_entry->kspi | BIT(31), IPSEC_DIR_INBOUND,
+ &flow_act);
+ if (err)
+ goto err_mod_header;
+ }
+
+ err = setup_pkt_reformat(mdev, attrs, &flow_act);
+ if (err)
+ goto err_pkt_reformat;
+
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_add_cnt;
+ }
+
+ flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC;
+ flow_act.crypto.op = MLX5_FLOW_ACT_CRYPTO_OP_DECRYPT;
+ flow_act.crypto.obj_id = sa_entry->ipsec_obj_id;
+ flow_act.flags |= FLOW_ACT_NO_APPEND;
+
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+
+ if (attrs->drop)
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+ else
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest[0].ft = rx->ft.status;
+ dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[1].counter_id = mlx5_fc_id(counter);
+
+ if (attrs->family == AF_INET)
+ setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4);
+ else
+ setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6);
+
+ if (!attrs->encap)
+ setup_fte_esp(spec);
+
+ setup_fte_spi(spec, attrs->spi, attrs->encap);
+ setup_fte_no_frags(spec);
+
+ if (sa_entry->vid != VLAN_NONE)
+ setup_fte_vid(spec, sa_entry->vid);
+ else
+ setup_fte_no_vid(spec);
+
+ rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, dest, 2);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add RX ipsec rule err=%d\n", err);
+ goto err_add_flow;
+ }
+ ipsec_rule->rule = rule;
+
+ /* Add another rule for zero vid */
+ if (sa_entry->vid == VLAN_NONE) {
+ clear_fte_vid(spec);
+ setup_fte_vid(spec, 0);
+ rule = mlx5_add_flow_rules(rx->ft.sa, spec, &flow_act, dest, 2);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev,
+ "fail to add RX ipsec zero vid rule err=%d\n",
+ err);
+ goto err_add_flow;
+ }
+ ipsec_rule->vid_zero_rule = rule;
+ }
+
+ kvfree(spec);
+ ipsec_rule->fc = counter;
+ ipsec_rule->modify_hdr = flow_act.modify_hdr;
+ ipsec_rule->pkt_reformat = flow_act.pkt_reformat;
+ return 0;
+
+err_add_flow:
+ mlx5_fc_destroy(mdev, counter);
+ if (ipsec_rule->rule != NULL)
+ mlx5_del_flow_rules(&ipsec_rule->rule);
+err_add_cnt:
+ mlx5_packet_reformat_dealloc(mdev, flow_act.pkt_reformat);
+err_pkt_reformat:
+ if (flow_act.modify_hdr != NULL)
+ mlx5_modify_header_dealloc(mdev, flow_act.modify_hdr);
+err_mod_header:
+ kvfree(spec);
+
+ return err;
+}
+
+static struct mlx5_flow_table *ipsec_tx_ft_create(struct mlx5_flow_namespace *ns,
+ int level, int prio,
+ int max_num_groups)
+{
+ struct mlx5_flow_table_attr ft_attr = {};
+
+ ft_attr.autogroup.num_reserved_entries = 1;
+ ft_attr.autogroup.max_num_groups = max_num_groups;
+ ft_attr.max_fte = NUM_IPSEC_FTE;
+ ft_attr.level = level;
+ ft_attr.prio = prio;
+
+ return mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
+}
+
+static int ipsec_counter_rule_tx(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx)
+{
+ struct mlx5_flow_destination dest = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *fte;
+ int err;
+
+ /* create fte */
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_COUNT |
+ MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest.counter_id = mlx5_fc_id(tx->fc->cnt);
+ fte = mlx5_add_flow_rules(tx->ft.status, NULL, &flow_act, &dest, 1);
+ if (IS_ERR_OR_NULL(fte)) {
+ err = PTR_ERR(fte);
+ mlx5_core_err(mdev, "Fail to add ipsec tx counter rule err=%d\n", err);
+ goto err_rule;
+ }
+
+ tx->status.rule = fte;
+ return 0;
+
+err_rule:
+ return err;
+}
+
+static void tx_destroy_roce(struct mlx5e_ipsec_tx *tx) {
+ if (!tx->roce.ft)
+ return;
+
+ mlx5_del_flow_rules(&tx->roce.rule);
+ mlx5_destroy_flow_group(tx->roce.g);
+ mlx5_destroy_flow_table(tx->roce.ft);
+ tx->roce.ft = NULL;
+}
+
+/* IPsec TX flow steering */
+static void tx_destroy(struct mlx5e_ipsec_tx *tx)
+{
+ tx_destroy_roce(tx);
+ if (tx->chains) {
+ ipsec_chains_destroy(tx->chains);
+ } else {
+ mlx5_del_flow_rules(&tx->pol.rule);
+ mlx5_destroy_flow_group(tx->pol.group);
+ mlx5_destroy_flow_table(tx->ft.pol);
+ }
+ mlx5_destroy_flow_table(tx->ft.sa);
+ mlx5_del_flow_rules(&tx->kspi_miss.rule);
+ mlx5_destroy_flow_group(tx->kspi_miss.group);
+ mlx5_del_flow_rules(&tx->kspi_bypass_rule.rule);
+ mlx5_del_flow_rules(&tx->kspi_bypass_rule.kspi_rule);
+ mlx5_destroy_flow_table(tx->ft.sa_kspi);
+ mlx5_del_flow_rules(&tx->status.rule);
+ mlx5_destroy_flow_table(tx->ft.status);
+}
+
+static int ipsec_tx_roce_rule_setup(struct mlx5_core_dev *mdev,
+ struct mlx5e_ipsec_tx *tx)
+{
+ struct mlx5_flow_destination dst = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ int err = 0;
+
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ dst.type = MLX5_FLOW_DESTINATION_TYPE_TABLE_TYPE;
+ dst.ft = tx->ft.pol;
+ rule = mlx5_add_flow_rules(tx->roce.ft, NULL, &flow_act, &dst, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Fail to add TX roce ipsec rule err=%d\n",
+ err);
+ goto out;
+ }
+ tx->roce.rule = rule;
+
+out:
+ return err;
+}
+
+static int ipsec_tx_create_roce(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx)
+{
+ struct mlx5_flow_table_attr ft_attr = {};
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *g;
+ int ix = 0;
+ int err;
+ u32 *in;
+
+ if (!tx->roce.ns)
+ return -EOPNOTSUPP;
+
+ in = kvzalloc(MLX5_ST_SZ_BYTES(create_flow_group_in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ ft_attr.max_fte = 1;
+ ft = mlx5_create_flow_table(tx->roce.ns, &ft_attr);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ mlx5_core_err(mdev, "Fail to create ipsec tx roce ft err=%d\n",
+ err);
+ goto fail_table;
+ }
+ tx->roce.ft = ft;
+
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += 1;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ g = mlx5_create_flow_group(ft, in);
+ if (IS_ERR(g)) {
+ err = PTR_ERR(g);
+ mlx5_core_err(mdev, "Fail to create ipsec tx roce group err=%d\n",
+ err);
+ goto fail_group;
+ }
+ tx->roce.g = g;
+
+ err = ipsec_tx_roce_rule_setup(mdev, tx);
+ if (err) {
+ mlx5_core_err(mdev, "Fail to create RoCE IPsec tx rules err=%d\n", err);
+ goto fail_rule;
+ }
+
+ kvfree(in);
+ return 0;
+
+fail_rule:
+ mlx5_destroy_flow_group(tx->roce.g);
+fail_group:
+ mlx5_destroy_flow_table(tx->roce.ft);
+ tx->roce.ft = NULL;
+fail_table:
+ kvfree(in);
+ return err;
+}
+
+/*
+ * Setting a rule in KSPI table for values that should bypass IPSEC.
+ *
+ * mdev - mlx5 core device
+ * tx - IPSEC TX
+ * return - 0 for success errno for failure
+ */
+static int tx_create_kspi_bypass_rules(struct mlx5_core_dev *mdev,
+ struct mlx5e_ipsec_tx *tx)
+{
+ struct mlx5_flow_destination dest = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_act flow_act_kspi = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ dest.ft = tx->ft.status;
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ flow_act_kspi.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+
+ setup_fte_reg_a_with_tag(spec, IPSEC_ACCEL_DRV_SPI_BYPASS);
+ rule = mlx5_add_flow_rules(tx->ft.sa_kspi, spec, &flow_act_kspi,
+ &dest, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Fail to add ipsec kspi bypass rule err=%d\n",
+ err);
+ goto err_add_kspi_rule;
+ }
+ tx->kspi_bypass_rule.kspi_rule = rule;
+
+ /* set the rule for packets withoiut ipsec tag. */
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ memset(spec, 0, sizeof(*spec));
+ setup_fte_reg_a_no_tag(spec);
+ rule = mlx5_add_flow_rules(tx->ft.sa_kspi, spec, &flow_act, &dest, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Fail to add ipsec kspi bypass rule err=%d\n", err);
+ goto err_add_rule;
+ }
+ tx->kspi_bypass_rule.rule = rule;
+
+ kvfree(spec);
+ return 0;
+err_add_rule:
+ mlx5_del_flow_rules(&tx->kspi_bypass_rule.kspi_rule);
+err_add_kspi_rule:
+ kvfree(spec);
+ return err;
+}
+
+
+static int tx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_tx *tx)
+{
+ struct mlx5_flow_destination dest = {};
+ struct mlx5_flow_table *ft;
+ int err;
+
+ /*
+ * Tx flow is different for ethernet traffic then for RoCE packets
+ * For Ethernet packets we start in SA KSPI table that matches KSPI of SA rule
+ * to the KSPI in the packet metadata
+ * For RoCE traffic we start in Policy table, then move to SA table
+ * which matches either reqid of the SA rule to reqid reported by policy table
+ * or ip header fields of SA to the packet IP header fields.
+ * Tables are ordered by their level so we set kspi
+ * with level 0 to have it first one for ethernet traffic.
+ * For RoCE the RoCE TX table direct the packets to policy table explicitly
+ */
+ ft = ipsec_tx_ft_create(tx->ns, 0, 0, 4);
+ if (IS_ERR(ft))
+ return PTR_ERR(ft);
+ tx->ft.sa_kspi = ft;
+
+ ft = ipsec_tx_ft_create(tx->ns, 2, 0, 4);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_reqid_ft;
+ }
+ tx->ft.sa = ft;
+
+ if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PRIO) {
+ tx->chains = ipsec_chains_create(
+ mdev, tx->ft.sa, MLX5_FLOW_NAMESPACE_EGRESS_IPSEC, 0, 1,
+ &tx->ft.pol);
+ if (IS_ERR(tx->chains)) {
+ err = PTR_ERR(tx->chains);
+ goto err_pol_ft;
+ }
+ } else {
+ ft = ipsec_tx_ft_create(tx->ns, 1, 0, 2);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_pol_ft;
+ }
+ tx->ft.pol = ft;
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = tx->ft.sa;
+ err = ipsec_miss_create(mdev, tx->ft.pol, &tx->pol, &dest);
+ if (err)
+ goto err_pol_miss;
+ }
+
+ ft = ipsec_tx_ft_create(tx->ns, 2, 0, 1);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_status_ft;
+ }
+ tx->ft.status = ft;
+
+ /* set miss rule for kspi table with drop action*/
+ err = ipsec_miss_create(mdev, tx->ft.sa_kspi, &tx->kspi_miss, NULL);
+ if (err)
+ goto err_kspi_miss;
+
+ err = tx_create_kspi_bypass_rules(mdev, tx);
+ if (err)
+ goto err_kspi_rule;
+
+ err = ipsec_counter_rule_tx(mdev, tx);
+ if (err)
+ goto err_status_rule;
+
+ err = ipsec_tx_create_roce(mdev, tx);
+ if (err)
+ goto err_counter_rule;
+
+ return 0;
+
+err_counter_rule:
+ mlx5_del_flow_rules(&tx->status.rule);
+err_status_rule:
+ mlx5_del_flow_rules(&tx->kspi_bypass_rule.rule);
+ mlx5_del_flow_rules(&tx->kspi_bypass_rule.kspi_rule);
+err_kspi_rule:
+ mlx5_destroy_flow_table(tx->ft.status);
+err_status_ft:
+ if (tx->chains) {
+ ipsec_chains_destroy(tx->chains);
+ } else {
+ mlx5_del_flow_rules(&tx->pol.rule);
+ mlx5_destroy_flow_group(tx->pol.group);
+ }
+err_pol_miss:
+ if (!tx->chains)
+ mlx5_destroy_flow_table(tx->ft.pol);
+err_pol_ft:
+ mlx5_del_flow_rules(&tx->kspi_miss.rule);
+ mlx5_destroy_flow_group(tx->kspi_miss.group);
+err_kspi_miss:
+ mlx5_destroy_flow_table(tx->ft.sa);
+err_reqid_ft:
+ mlx5_destroy_flow_table(tx->ft.sa_kspi);
+ return err;
+}
+
+static int tx_get(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec,
+ struct mlx5e_ipsec_tx *tx)
+{
+ int err;
+
+ if (tx->ft.refcnt)
+ goto skip;
+
+ err = tx_create(mdev, tx);
+ if (err)
+ return err;
+
+skip:
+ tx->ft.refcnt++;
+ return 0;
+}
+
+static void tx_put(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_tx *tx)
+{
+ if (--tx->ft.refcnt)
+ return;
+
+ tx_destroy(tx);
+}
+
+static struct mlx5e_ipsec_tx *tx_ft_get(struct mlx5_core_dev *mdev,
+ struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+ int err;
+
+ mutex_lock(&tx->ft.mutex);
+ err = tx_get(mdev, ipsec, tx);
+ mutex_unlock(&tx->ft.mutex);
+ if (err)
+ return ERR_PTR(err);
+
+ return tx;
+}
+
+static struct mlx5_flow_table *tx_ft_get_policy(struct mlx5_core_dev *mdev,
+ struct mlx5e_ipsec *ipsec,
+ u32 prio)
+{
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+ struct mlx5_flow_table *ft;
+ int err;
+
+ mutex_lock(&tx->ft.mutex);
+ err = tx_get(mdev, ipsec, tx);
+ if (err)
+ goto err_get;
+
+ ft = tx->chains ? ipsec_chains_get_table(tx->chains, prio) : tx->ft.pol;
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_get_ft;
+ }
+
+ mutex_unlock(&tx->ft.mutex);
+ return ft;
+
+err_get_ft:
+ tx_put(ipsec, tx);
+err_get:
+ mutex_unlock(&tx->ft.mutex);
+ return ERR_PTR(err);
+}
+
+static void tx_ft_put_policy(struct mlx5e_ipsec *ipsec, u32 prio)
+{
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+
+ mutex_lock(&tx->ft.mutex);
+ if (tx->chains)
+ ipsec_chains_put_table(tx->chains, prio);
+
+ tx_put(ipsec, tx);
+ mutex_unlock(&tx->ft.mutex);
+}
+
+static void tx_ft_put(struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+
+ mutex_lock(&tx->ft.mutex);
+ tx_put(ipsec, tx);
+ mutex_unlock(&tx->ft.mutex);
+}
+
+static void setup_fte_reg_a_with_tag(struct mlx5_flow_spec *spec,
+ u16 kspi)
+{
+ /* Add IPsec indicator in metadata_reg_a. */
+ spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ misc_parameters_2.metadata_reg_a);
+ MLX5_SET(fte_match_param, spec->match_value,
+ misc_parameters_2.metadata_reg_a,
+ MLX5_ETH_WQE_FT_META_IPSEC << 23 | kspi);
+}
+
+static void setup_fte_reg_a_no_tag(struct mlx5_flow_spec *spec)
+{
+ /* Add IPsec indicator in metadata_reg_a. */
+ spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;
+
+ MLX5_SET(fte_match_param, spec->match_criteria,
+ misc_parameters_2.metadata_reg_a,
+ MLX5_ETH_WQE_FT_META_IPSEC << 23);
+ MLX5_SET(fte_match_param, spec->match_value,
+ misc_parameters_2.metadata_reg_a,
+ 0);
+}
+
+static void setup_fte_reg_c0(struct mlx5_flow_spec *spec, u32 reqid)
+{
+ /* Pass policy check before choosing this SA */
+ spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;
+
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
+ misc_parameters_2.metadata_reg_c_0);
+ MLX5_SET(fte_match_param, spec->match_value,
+ misc_parameters_2.metadata_reg_c_0, reqid);
+}
+
+static void setup_fte_upper_proto_match(struct mlx5_flow_spec *spec, struct upspec *upspec)
+{
+ switch (upspec->proto) {
+ case IPPROTO_UDP:
+ if (upspec->dport) {
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4,
+ spec->match_criteria, udp_dport);
+ MLX5_SET(fte_match_set_lyr_2_4, spec->match_value,
+ udp_dport, upspec->dport);
+ }
+
+ if (upspec->sport) {
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4,
+ spec->match_criteria, udp_sport);
+ MLX5_SET(fte_match_set_lyr_2_4, spec->match_value,
+ udp_dport, upspec->sport);
+ }
+ break;
+ case IPPROTO_TCP:
+ if (upspec->dport) {
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4,
+ spec->match_criteria, tcp_dport);
+ MLX5_SET(fte_match_set_lyr_2_4, spec->match_value,
+ tcp_dport, upspec->dport);
+ }
+
+ if (upspec->sport) {
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4,
+ spec->match_criteria, tcp_sport);
+ MLX5_SET(fte_match_set_lyr_2_4, spec->match_value,
+ tcp_dport, upspec->sport);
+ }
+ break;
+ default:
+ return;
+ }
+
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, spec->match_criteria, ip_protocol);
+ MLX5_SET(fte_match_set_lyr_2_4, spec->match_value, ip_protocol, upspec->proto);
+}
+
+static int tx_add_kspi_rule(struct mlx5e_ipsec_sa_entry *sa_entry,
+ struct mlx5e_ipsec_tx *tx,
+ struct mlx5_flow_act *flow_act,
+ struct mlx5_flow_destination *dest,
+ int num_dest)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ setup_fte_no_frags(spec);
+ setup_fte_reg_a_with_tag(spec, sa_entry->kspi);
+
+ rule = mlx5_add_flow_rules(tx->ft.sa_kspi, spec, flow_act, dest, num_dest);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add TX ipsec kspi rule err=%d\n", err);
+ goto err_add_kspi_flow;
+ }
+ ipsec_rule->kspi_rule = rule;
+ kvfree(spec);
+ return 0;
+
+err_add_kspi_flow:
+ kvfree(spec);
+ return err;
+}
+
+static int tx_add_reqid_ip_rules(struct mlx5e_ipsec_sa_entry *sa_entry,
+ struct mlx5e_ipsec_tx *tx,
+ struct mlx5_flow_act *flow_act,
+ struct mlx5_flow_destination *dest,
+ int num_dest)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
+ struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ flow_act->flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
+
+ if(attrs->reqid) {
+ setup_fte_no_frags(spec);
+ setup_fte_reg_c0(spec, attrs->reqid);
+ rule = mlx5_add_flow_rules(tx->ft.sa, spec, flow_act, dest, num_dest);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add TX ipsec reqid rule err=%d\n", err);
+ goto err_add_reqid_rule;
+ }
+ ipsec_rule->reqid_rule = rule;
+ memset(spec, 0, sizeof(*spec));
+ }
+
+ if (attrs->family == AF_INET)
+ setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4);
+ else
+ setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6);
+ setup_fte_no_frags(spec);
+
+ rule = mlx5_add_flow_rules(tx->ft.sa, spec, flow_act, dest, num_dest);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add TX ipsec ip rule err=%d\n", err);
+ goto err_add_ip_rule;
+ }
+ ipsec_rule->rule = rule;
+ kvfree(spec);
+ return 0;
+
+err_add_ip_rule:
+ mlx5_del_flow_rules(&ipsec_rule->reqid_rule);
+err_add_reqid_rule:
+ kvfree(spec);
+ return err;
+}
+
+static int tx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
+ struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
+ struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
+ struct mlx5_flow_destination dest[2] = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5e_ipsec_tx *tx;
+ struct mlx5_fc *counter;
+ int err;
+
+ tx = tx_ft_get(mdev, ipsec);
+ if (IS_ERR(tx))
+ return PTR_ERR(tx);
+
+ err = setup_pkt_reformat(mdev, attrs, &flow_act);
+ if (err)
+ goto err_pkt_reformat;
+
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_add_cnt;
+ }
+
+ flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC;
+ flow_act.crypto.obj_id = sa_entry->ipsec_obj_id;
+ flow_act.flags |= FLOW_ACT_NO_APPEND;
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+
+ if (attrs->drop)
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+ else
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+
+ dest[0].ft = tx->ft.status;
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[1].counter_id = mlx5_fc_id(counter);
+
+ err = tx_add_kspi_rule(sa_entry, tx, &flow_act, dest, 2);
+ if (err) {
+ goto err_add_kspi_rule;
+ }
+
+ err = tx_add_reqid_ip_rules(sa_entry, tx, &flow_act, dest, 2);
+ if (err) {
+ goto err_add_reqid_ip_rule;
+ }
+
+ ipsec_rule->fc = counter;
+ ipsec_rule->pkt_reformat = flow_act.pkt_reformat;
+ return 0;
+
+err_add_reqid_ip_rule:
+ mlx5_del_flow_rules(&ipsec_rule->kspi_rule);
+err_add_kspi_rule:
+ mlx5_fc_destroy(mdev, counter);
+err_add_cnt:
+ if (flow_act.pkt_reformat)
+ mlx5_packet_reformat_dealloc(mdev, flow_act.pkt_reformat);
+err_pkt_reformat:
+ tx_ft_put(ipsec);
+ return err;
+}
+
+static int tx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry)
+{
+ struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry);
+ struct mlx5e_ipsec_tx *tx = pol_entry->ipsec->tx;
+ struct mlx5_flow_destination dest[2] = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ struct mlx5_flow_table *ft;
+ int err, dstn = 0;
+
+ ft = tx_ft_get_policy(mdev, pol_entry->ipsec, attrs->prio);
+ if (IS_ERR(ft))
+ return PTR_ERR(ft);
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec) {
+ err = -ENOMEM;
+ goto err_alloc;
+ }
+
+ if (attrs->family == AF_INET)
+ setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4);
+ else
+ setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6);
+
+ setup_fte_no_frags(spec);
+ setup_fte_upper_proto_match(spec, &attrs->upspec);
+
+ switch (attrs->action) {
+ case IPSEC_POLICY_IPSEC:
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ err = setup_modify_header(mdev, attrs->reqid,
+ IPSEC_DIR_OUTBOUND, &flow_act);
+ if (err)
+ goto err_mod_header;
+ break;
+ case IPSEC_POLICY_DISCARD:
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[dstn].counter_id = mlx5_fc_id(tx->fc->drop);
+ dstn++;
+ break;
+ default:
+ err = -EINVAL;
+ goto err_mod_header;
+ }
+
+ flow_act.flags |= FLOW_ACT_NO_APPEND;
+ dest[dstn].ft = tx->ft.sa;
+ dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dstn++;
+ rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, dstn);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add TX ipsec rule err=%d\n", err);
+ goto err_action;
+ }
+
+ kvfree(spec);
+ pol_entry->ipsec_rule.rule = rule;
+ pol_entry->ipsec_rule.modify_hdr = flow_act.modify_hdr;
+ return 0;
+
+err_action:
+ if (flow_act.modify_hdr)
+ mlx5_modify_header_dealloc(mdev, flow_act.modify_hdr);
+err_mod_header:
+ kvfree(spec);
+err_alloc:
+ tx_ft_put_policy(pol_entry->ipsec, attrs->prio);
+ return err;
+}
+
+static int rx_add_policy(struct mlx5e_ipsec_pol_entry *pol_entry)
+{
+ struct mlx5_accel_pol_xfrm_attrs *attrs = &pol_entry->attrs;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry);
+ struct mlx5e_ipsec *ipsec = pol_entry->ipsec;
+ struct mlx5_flow_destination dest[2];
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ struct mlx5_flow_table *ft;
+ struct mlx5e_ipsec_rx *rx;
+ int err, dstn = 0;
+
+ rx = (attrs->family == AF_INET) ? ipsec->rx_ipv4 : ipsec->rx_ipv6;
+ ft = rx->chains ? ipsec_chains_get_table(rx->chains, attrs->prio) : rx->ft.pol;
+ if (IS_ERR(ft))
+ return PTR_ERR(ft);
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec) {
+ err = -ENOMEM;
+ goto err_alloc;
+ }
+
+ switch (attrs->action) {
+ case IPSEC_POLICY_IPSEC:
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ break;
+ case IPSEC_POLICY_DISCARD:
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[dstn].counter_id = mlx5_fc_id(rx->fc->drop);
+ dstn++;
+ break;
+ default:
+ err = -EINVAL;
+ goto err_action;
+ }
+
+ flow_act.flags |= FLOW_ACT_NO_APPEND;
+ dest[dstn].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest[dstn].ft = rx->ft.sa;
+ dstn++;
+
+ if (attrs->family == AF_INET)
+ setup_fte_addr4(spec, &attrs->saddr.a4, &attrs->daddr.a4);
+ else
+ setup_fte_addr6(spec, attrs->saddr.a6, attrs->daddr.a6);
+
+ setup_fte_no_frags(spec);
+ setup_fte_upper_proto_match(spec, &attrs->upspec);
+ if (attrs->vid != VLAN_NONE)
+ setup_fte_vid(spec, attrs->vid);
+ else
+ setup_fte_no_vid(spec);
+
+ rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, dstn);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev,
+ "Failed to add RX IPsec policy rule err=%d\n", err);
+ goto err_action;
+ }
+ pol_entry->ipsec_rule.rule = rule;
+
+ /* Add also rule for zero vid */
+ if (attrs->vid == VLAN_NONE) {
+ clear_fte_vid(spec);
+ setup_fte_vid(spec, 0);
+ rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, dstn);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev,
+ "Failed to add RX IPsec policy rule err=%d\n",
+ err);
+ goto err_action;
+ }
+ pol_entry->ipsec_rule.vid_zero_rule = rule;
+ }
+
+ kvfree(spec);
+ return 0;
+
+err_action:
+ if (pol_entry->ipsec_rule.rule != NULL)
+ mlx5_del_flow_rules(&pol_entry->ipsec_rule.rule);
+ kvfree(spec);
+err_alloc:
+ if (rx->chains != NULL)
+ ipsec_chains_put_table(rx->chains, attrs->prio);
+ return err;
+}
+
+static void ipsec_fs_destroy_counters(struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5e_ipsec_rx *rx_ipv4 = ipsec->rx_ipv4;
+ struct mlx5_core_dev *mdev = ipsec->mdev;
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+
+ mlx5_fc_destroy(mdev, rx_ipv4->fc->drop);
+ mlx5_fc_destroy(mdev, rx_ipv4->fc->cnt);
+ kfree(rx_ipv4->fc);
+ mlx5_fc_destroy(mdev, tx->fc->drop);
+ mlx5_fc_destroy(mdev, tx->fc->cnt);
+ kfree(tx->fc);
+}
+
+static int ipsec_fs_init_counters(struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5e_ipsec_rx *rx_ipv4 = ipsec->rx_ipv4;
+ struct mlx5e_ipsec_rx *rx_ipv6 = ipsec->rx_ipv6;
+ struct mlx5_core_dev *mdev = ipsec->mdev;
+ struct mlx5e_ipsec_tx *tx = ipsec->tx;
+ struct mlx5e_ipsec_fc *fc;
+ struct mlx5_fc *counter;
+ int err;
+
+ fc = kzalloc(sizeof(*tx->fc), GFP_KERNEL);
+ if (!fc)
+ return -ENOMEM;
+
+ tx->fc = fc;
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_tx_fc_alloc;
+ }
+
+ fc->cnt = counter;
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_tx_fc_cnt;
+ }
+
+ fc->drop = counter;
+
+ fc = kzalloc(sizeof(*tx->fc), GFP_KERNEL);
+ if (!fc) {
+ err = -ENOMEM;
+ goto err_tx_fc_drop;
+ }
+
+ /* Both IPv4 and IPv6 point to same flow counters struct. */
+ rx_ipv4->fc = fc;
+ rx_ipv6->fc = fc;
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_rx_fc_alloc;
+ }
+
+ fc->cnt = counter;
+ counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(counter)) {
+ err = PTR_ERR(counter);
+ goto err_rx_fc_cnt;
+ }
+
+ fc->drop = counter;
+ return 0;
+
+err_rx_fc_cnt:
+ mlx5_fc_destroy(mdev, rx_ipv4->fc->cnt);
+err_rx_fc_alloc:
+ kfree(rx_ipv4->fc);
+err_tx_fc_drop:
+ mlx5_fc_destroy(mdev, tx->fc->drop);
+err_tx_fc_cnt:
+ mlx5_fc_destroy(mdev, tx->fc->cnt);
+err_tx_fc_alloc:
+ kfree(tx->fc);
+ return err;
+}
+
+static int ipsec_status_rule(struct mlx5_core_dev *mdev,
+ struct mlx5e_ipsec_rx *rx,
+ struct mlx5_flow_destination *dest)
+{
+ u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_modify_hdr *modify_hdr;
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ /* Action to copy 7 bit ipsec_syndrome to regB[24:30] */
+ MLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY);
+ MLX5_SET(copy_action_in, action, src_field, MLX5_ACTION_IN_FIELD_IPSEC_SYNDROME);
+ MLX5_SET(copy_action_in, action, src_offset, 0);
+ MLX5_SET(copy_action_in, action, length, 7);
+ MLX5_SET(copy_action_in, action, dst_field, MLX5_ACTION_IN_FIELD_METADATA_REG_B);
+ MLX5_SET(copy_action_in, action, dst_offset, 24);
+
+ modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL,
+ 1, action);
+
+ if (IS_ERR(modify_hdr)) {
+ err = PTR_ERR(modify_hdr);
+ mlx5_core_err(mdev,
+ "fail to alloc ipsec copy modify_header_id err=%d\n", err);
+ goto out_spec;
+ }
+
+ /* create fte */
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ flow_act.modify_hdr = modify_hdr;
+
+ rule = mlx5_add_flow_rules(rx->ft.status, spec, &flow_act, dest, 2);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "fail to add ipsec rx err copy rule err=%d\n", err);
+ goto out;
+ }
+
+ kvfree(spec);
+ rx->status.rule = rule;
+ rx->status.modify_hdr = modify_hdr;
+ return 0;
+
+out:
+ mlx5_modify_header_dealloc(mdev, modify_hdr);
+out_spec:
+ kvfree(spec);
+ return err;
+}
+
+static void ipsec_fs_rx_roce_rules_destroy(struct mlx5e_ipsec_rx_roce *rx_roce)
+{
+ if (!rx_roce->ns_rdma)
+ return;
+
+ mlx5_del_flow_rules(&rx_roce->roce_miss.rule);
+ mlx5_del_flow_rules(&rx_roce->rule);
+ mlx5_destroy_flow_group(rx_roce->roce_miss.group);
+ mlx5_destroy_flow_group(rx_roce->g);
+}
+
+static void ipsec_fs_rx_catchall_rules_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx)
+{
+ mutex_lock(&rx->ft.mutex);
+ mlx5_del_flow_rules(&rx->sa.rule);
+ mlx5_destroy_flow_group(rx->sa.group);
+ if (rx->chains == NULL) {
+ mlx5_del_flow_rules(&rx->pol.rule);
+ mlx5_destroy_flow_group(rx->pol.group);
+ }
+ mlx5_del_flow_rules(&rx->status.rule);
+ mlx5_modify_header_dealloc(mdev, rx->status.modify_hdr);
+ ipsec_fs_rx_roce_rules_destroy(&rx->roce);
+ mutex_unlock(&rx->ft.mutex);
+}
+
+static void ipsec_fs_rx_roce_table_destroy(struct mlx5e_ipsec_rx_roce *rx_roce)
+{
+ if (!rx_roce->ns_rdma)
+ return;
+
+ mlx5_destroy_flow_table(rx_roce->ft_rdma);
+ mlx5_destroy_flow_table(rx_roce->ft);
+}
+
+static void
+ipsec_fs_rx_ip_type_catchall_rule_destroy(struct mlx5e_ipsec_rx_ip_type* rx_ip_type)
+{
+ mlx5_del_flow_rules(&rx_ip_type->ipv4_rule);
+ mlx5_del_flow_rules(&rx_ip_type->ipv6_rule);
+ mlx5_del_flow_rules(&rx_ip_type->miss.rule);
+ mlx5_destroy_flow_group(rx_ip_type->miss.group);
+ rx_ip_type->miss.group = NULL;
+}
+
+static void ipsec_fs_rx_table_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx)
+{
+ if (rx->chains) {
+ ipsec_chains_destroy(rx->chains);
+ } else {
+ mlx5_del_flow_rules(&rx->pol.rule);
+ mlx5_destroy_flow_table(rx->ft.pol);
+ }
+ mlx5_destroy_flow_table(rx->ft.sa);
+ mlx5_destroy_flow_table(rx->ft.status);
+ ipsec_fs_rx_roce_table_destroy(&rx->roce);
+}
+
+static void ipsec_roce_setup_udp_dport(struct mlx5_flow_spec *spec, u16 dport)
+{
+ spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_protocol, IPPROTO_UDP);
+ MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.udp_dport);
+ MLX5_SET(fte_match_param, spec->match_value, outer_headers.udp_dport, dport);
+}
+
+static int ipsec_roce_rx_rule_setup(struct mlx5_flow_destination *default_dst,
+ struct mlx5e_ipsec_rx_roce *roce, struct mlx5_core_dev *mdev)
+{
+ struct mlx5_flow_destination dst = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err = 0;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ ipsec_roce_setup_udp_dport(spec, ROCE_V2_UDP_DPORT);
+
+ //flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;//not needed it is added in command
+ dst.type = MLX5_FLOW_DESTINATION_TYPE_TABLE_TYPE;
+ dst.ft = roce->ft_rdma;
+
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ rule = mlx5_add_flow_rules(roce->ft, spec, &flow_act, &dst, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Fail to add RX roce ipsec rule err=%d\n",
+ err);
+ goto fail_add_rule;
+ }
+
+ roce->rule = rule;
+
+ rule = mlx5_add_flow_rules(roce->ft, NULL, &flow_act, default_dst, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Fail to add RX roce ipsec miss rule err=%d\n",
+ err);
+ goto fail_add_default_rule;
+ }
+
+ roce->roce_miss.rule = rule;
+
+ kvfree(spec);
+ return 0;
+
+fail_add_default_rule:
+ mlx5_del_flow_rules(&roce->rule);
+fail_add_rule:
+ kvfree(spec);
+ return err;
+}
+
+static int ipsec_roce_rx_rules(struct mlx5e_ipsec_rx *rx, struct mlx5_flow_destination *defdst,
+ struct mlx5_core_dev *mdev)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct mlx5_flow_group *g;
+ void *outer_headers_c;
+ u32 *in;
+ int err = 0;
+ int ix = 0;
+ u8 *mc;
+
+ if (!rx->roce.ns_rdma)
+ return 0;
+
+ in = kvzalloc(inlen, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
+ outer_headers_c = MLX5_ADDR_OF(fte_match_param, mc, outer_headers);
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol);
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, udp_dport);
+
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += 1;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ g = mlx5_create_flow_group(rx->roce.ft, in);
+ if (IS_ERR(g)) {
+ err = PTR_ERR(g);
+ mlx5_core_err(mdev, "Fail to create ipsec rx roce group at nic err=%d\n", err);
+ goto fail_group;
+ }
+ rx->roce.g = g;
+
+ memset(in, 0, MLX5_ST_SZ_BYTES(create_flow_group_in));
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += 1;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ g = mlx5_create_flow_group(rx->roce.ft, in);
+ if (IS_ERR(g)) {
+ err = PTR_ERR(g);
+ mlx5_core_err(mdev, "Fail to create ipsec rx roce miss group at nic err=%d\n",
+ err);
+ goto fail_mgroup;
+ }
+ rx->roce.roce_miss.group = g;
+
+ err = ipsec_roce_rx_rule_setup(defdst, &rx->roce, mdev);
+ if (err)
+ goto fail_setup_rule;
+
+ kvfree(in);
+ return 0;
+
+fail_setup_rule:
+ mlx5_destroy_flow_group(rx->roce.roce_miss.group);
+fail_mgroup:
+ mlx5_destroy_flow_group(rx->roce.g);
+fail_group:
+ kvfree(in);
+ return err;
+}
+
+static int ipsec_fs_rx_catchall_rules(struct mlx5e_priv *priv,
+ struct mlx5e_ipsec_rx *rx,
+ struct mlx5_flow_destination *defdst)
+{
+ struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5_flow_destination dest[2] = {};
+ int err = 0;
+
+ mutex_lock(&rx->ft.mutex);
+ /* IPsec RoCE RX rules */
+ err = ipsec_roce_rx_rules(rx, defdst, mdev);
+ if (err)
+ goto out;
+
+ /* IPsec Rx IP Status table rule */
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ if (rx->roce.ft)
+ dest[0].ft = rx->roce.ft;
+ else
+ dest[0].ft = priv->fts.vlan.t;
+
+ dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[1].counter_id = mlx5_fc_id(rx->fc->cnt);
+ err = ipsec_status_rule(mdev, rx, dest);
+ if (err)
+ goto err_roce_rules_destroy;
+
+ if (!rx->chains) {
+ /* IPsec Rx IP policy default miss rule */
+ err = ipsec_miss_create(mdev, rx->ft.pol, &rx->pol, defdst);
+ if (err)
+ goto err_status_rule_destroy;
+ }
+
+ /* FIXME: This is workaround to current design
+ * which installs SA on firt packet. So we need to forward this
+ * packet to the stack. It doesn't work with RoCE and eswitch traffic,
+ */
+ err = ipsec_miss_create(mdev, rx->ft.sa, &rx->sa, defdst);
+ if (err)
+ goto err_status_sa_rule_destroy;
+
+ mutex_unlock(&rx->ft.mutex);
+ return 0;
+
+err_status_sa_rule_destroy:
+ if (!rx->chains) {
+ mlx5_del_flow_rules(&rx->pol.rule);
+ mlx5_destroy_flow_group(rx->pol.group);
+ }
+err_status_rule_destroy:
+ mlx5_del_flow_rules(&rx->status.rule);
+ mlx5_modify_header_dealloc(mdev, rx->status.modify_hdr);
+err_roce_rules_destroy:
+ ipsec_fs_rx_roce_rules_destroy(&rx->roce);
+out:
+ mutex_unlock(&rx->ft.mutex);
+ return err;
+}
+
+static int ipsec_fs_rx_roce_tables_create(struct mlx5e_ipsec_rx *rx,
+ int rx_init_level, int rdma_init_level)
+{
+ struct mlx5_flow_table_attr ft_attr = {};
+ struct mlx5_flow_table *ft;
+ int err = 0;
+
+ if (!rx->roce.ns_rdma)
+ return 0;
+
+ ft_attr.max_fte = 2;
+ ft_attr.level = rx_init_level;
+ ft = mlx5_create_flow_table(rx->ns, &ft_attr);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ return err;
+ }
+ rx->roce.ft = ft;
+
+ ft_attr.max_fte = 0;
+ ft_attr.level = rdma_init_level;
+ ft = mlx5_create_flow_table(rx->roce.ns_rdma, &ft_attr);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto out;
+ }
+ rx->roce.ft_rdma = ft;
+
+ return 0;
+out:
+ mlx5_destroy_flow_table(rx->roce.ft);
+ rx->roce.ft = NULL;
+ return err;
+}
+
+static int
+ipsec_fs_rx_ip_type_catchall_rules_create(struct mlx5e_priv *priv,
+ struct mlx5_flow_destination *defdst)
+{
+ struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_ipsec *ipsec = priv->ipsec;
+ struct mlx5_flow_destination dst = {};
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int err = 0;
+
+ spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec) {
+ return -ENOMEM;
+ }
+ dst.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+
+ /* Set rule for ipv4 packets */
+ dst.ft = ipsec->rx_ipv4->ft.pol;
+ setup_fte_ip_version(spec, AF_INET);
+ rule = mlx5_add_flow_rules(ipsec->rx_ip_type->ft, spec, &flow_act, &dst, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Failed to add ipv4 rule to ip_type table err=%d\n",
+ err);
+ goto out;
+ }
+ ipsec->rx_ip_type->ipv4_rule = rule;
+
+ /* Set rule for ipv6 packets */
+ dst.ft = ipsec->rx_ipv6->ft.pol;
+ setup_fte_ip_version(spec, AF_INET6);
+ rule = mlx5_add_flow_rules(ipsec->rx_ip_type->ft, spec, &flow_act, &dst, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ mlx5_core_err(mdev, "Failed to add ipv6 rule to ip_type table err=%d\n",
+ err);
+ goto fail_add_ipv6_rule;
+ }
+ ipsec->rx_ip_type->ipv6_rule = rule;
+
+ /* set miss rule */
+ err = ipsec_miss_create(mdev, ipsec->rx_ip_type->ft, &ipsec->rx_ip_type->miss, defdst);
+ if (err) {
+ mlx5_core_err(mdev, "Failed to add miss rule to ip_type table err=%d\n",
+ err);
+ goto fail_miss_rule;
+ }
+
+ goto out;
+
+fail_miss_rule:
+ mlx5_del_flow_rules(&ipsec->rx_ip_type->ipv6_rule);
+fail_add_ipv6_rule:
+ mlx5_del_flow_rules(&ipsec->rx_ip_type->ipv4_rule);
+out:
+ kvfree(spec);
+ return err;
+}
+
+static int
+ipsec_fs_rx_ip_type_table_create(struct mlx5e_priv *priv,
+ int level)
+{
+ struct mlx5e_ipsec *ipsec = priv->ipsec;
+ struct mlx5_flow_table *ft;
+ int err = 0;
+
+ /* Create rx ip type table */
+ ft = ipsec_rx_ft_create(ipsec->rx_ip_type->ns, level, 0, 1);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto out;
+ }
+ ipsec->rx_ip_type->ft = ft;
+
+ priv->fts.ipsec_ft = priv->ipsec->rx_ip_type->ft;
+
+out:
+ return err;
+}
+
+static int ipsec_fs_rx_table_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec_rx *rx,
+ int rx_init_level, int rdma_init_level)
+{
+ struct mlx5_flow_namespace *ns = rx->ns;
+ struct mlx5_flow_table *ft;
+ int err = 0;
+
+ mutex_lock(&rx->ft.mutex);
+
+ /* IPsec Rx IP SA table create */
+ ft = ipsec_rx_ft_create(ns, rx_init_level + 1, 0, 1);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto out;
+ }
+ rx->ft.sa = ft;
+
+ /* IPsec Rx IP Status table create */
+ ft = ipsec_rx_ft_create(ns, rx_init_level + 2, 0, 1);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_sa_table_destroy;
+ }
+ rx->ft.status = ft;
+
+ if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PRIO) {
+ rx->chains = ipsec_chains_create(mdev, rx->ft.sa,
+ MLX5_FLOW_NAMESPACE_KERNEL, 0,
+ rx_init_level, &rx->ft.pol);
+ if (IS_ERR(rx->chains)) {
+ err = PTR_ERR(rx->chains);
+ goto err_status_table_destroy;
+ }
+ } else {
+ ft = ipsec_rx_ft_create(ns, rx_init_level, 0, 1);
+ if (IS_ERR(ft)) {
+ err = PTR_ERR(ft);
+ goto err_status_table_destroy;
+ }
+ rx->ft.pol = ft;
+ }
+
+ /* IPsec RoCE RX tables create*/
+ err = ipsec_fs_rx_roce_tables_create(rx, rx_init_level + 3,
+ rdma_init_level);
+ if (err)
+ goto err_pol_table_destroy;
+
+ goto out;
+
+err_pol_table_destroy:
+ mlx5_destroy_flow_table(rx->ft.pol);
+err_status_table_destroy:
+ mlx5_destroy_flow_table(rx->ft.status);
+err_sa_table_destroy:
+ mlx5_destroy_flow_table(rx->ft.sa);
+out:
+ mutex_unlock(&rx->ft.mutex);
+ return err;
+}
+
+#define NIC_RDMA_BOTH_DIRS_CAPS (MLX5_FT_NIC_RX_2_NIC_RX_RDMA | MLX5_FT_NIC_TX_RDMA_2_NIC_TX)
+
+static void mlx5e_accel_ipsec_fs_init_roce(struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5_core_dev *mdev = ipsec->mdev;
+ struct mlx5_flow_namespace *ns;
+
+ if ((MLX5_CAP_GEN_2(ipsec->mdev, flow_table_type_2_type) &
+ NIC_RDMA_BOTH_DIRS_CAPS) != NIC_RDMA_BOTH_DIRS_CAPS) {
+ mlx5_core_dbg(mdev, "Failed to init roce ns, capabilities not supported\n");
+ return;
+ }
+
+ ns = mlx5_get_flow_namespace(ipsec->mdev, MLX5_FLOW_NAMESPACE_RDMA_RX_IPSEC);
+ if (!ns) {
+ mlx5_core_err(mdev, "Failed to init roce rx ns\n");
+ return;
+ }
+
+ ipsec->rx_ipv4->roce.ns_rdma = ns;
+ ipsec->rx_ipv6->roce.ns_rdma = ns;
+
+ ns = mlx5_get_flow_namespace(ipsec->mdev, MLX5_FLOW_NAMESPACE_RDMA_TX_IPSEC);
+ if (!ns) {
+ ipsec->rx_ipv4->roce.ns_rdma = NULL;
+ ipsec->rx_ipv6->roce.ns_rdma = NULL;
+ mlx5_core_err(mdev, "Failed to init roce tx ns\n");
+ return;
+ }
+
+ ipsec->tx->roce.ns = ns;
+}
+
+int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
+{
+ if (sa_entry->attrs.dir == IPSEC_DIR_OUTBOUND)
+ return tx_add_rule(sa_entry);
+
+ return rx_add_rule(sa_entry);
+}
+
+void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &sa_entry->ipsec_rule;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry);
+
+ mlx5_del_flow_rules(&ipsec_rule->rule);
+ mlx5_del_flow_rules(&ipsec_rule->kspi_rule);
+ if (ipsec_rule->vid_zero_rule != NULL)
+ mlx5_del_flow_rules(&ipsec_rule->vid_zero_rule);
+ if (ipsec_rule->reqid_rule != NULL)
+ mlx5_del_flow_rules(&ipsec_rule->reqid_rule);
+ mlx5_fc_destroy(mdev, ipsec_rule->fc);
+ mlx5_packet_reformat_dealloc(mdev, ipsec_rule->pkt_reformat);
+ if (sa_entry->attrs.dir == IPSEC_DIR_OUTBOUND) {
+ tx_ft_put(sa_entry->ipsec);
+ return;
+ }
+
+ if (ipsec_rule->modify_hdr != NULL)
+ mlx5_modify_header_dealloc(mdev, ipsec_rule->modify_hdr);
+}
+
+int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry)
+{
+ if (pol_entry->attrs.dir == IPSEC_DIR_OUTBOUND)
+ return tx_add_policy(pol_entry);
+
+ return rx_add_policy(pol_entry);
+}
+
+void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry)
+{
+ struct mlx5e_ipsec_rule *ipsec_rule = &pol_entry->ipsec_rule;
+ struct mlx5_core_dev *mdev = mlx5e_ipsec_pol2dev(pol_entry);
+
+ mlx5_del_flow_rules(&ipsec_rule->rule);
+ if (ipsec_rule->vid_zero_rule != NULL)
+ mlx5_del_flow_rules(&ipsec_rule->vid_zero_rule);
+
+ if (pol_entry->attrs.dir == IPSEC_DIR_INBOUND) {
+ struct mlx5e_ipsec_rx *rx;
+
+ rx = (pol_entry->attrs.family == AF_INET)
+ ? pol_entry->ipsec->rx_ipv4
+ : pol_entry->ipsec->rx_ipv6;
+ if (rx->chains)
+ ipsec_chains_put_table(rx->chains,
+ pol_entry->attrs.prio);
+ return;
+ }
+
+ if (ipsec_rule->modify_hdr)
+ mlx5_modify_header_dealloc(mdev, ipsec_rule->modify_hdr);
+
+ tx_ft_put_policy(pol_entry->ipsec, pol_entry->attrs.prio);
+}
+
+void mlx5e_accel_ipsec_fs_rx_catchall_rules_destroy(struct mlx5e_priv *priv)
+{
+ /* Check if IPsec supported */
+ if (!priv->ipsec)
+ return;
+
+ ipsec_fs_rx_ip_type_catchall_rule_destroy(priv->ipsec->rx_ip_type);
+ ipsec_fs_rx_catchall_rules_destroy(priv->mdev, priv->ipsec->rx_ipv4);
+ ipsec_fs_rx_catchall_rules_destroy(priv->mdev, priv->ipsec->rx_ipv6);
+}
+
+int mlx5e_accel_ipsec_fs_rx_catchall_rules(struct mlx5e_priv *priv)
+{
+ struct mlx5e_ipsec *ipsec = priv->ipsec;
+ struct mlx5_flow_destination dest = {};
+ int err = 0;
+
+ /* Check if IPsec supported */
+ if (!ipsec)
+ return 0;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = priv->fts.vlan.t;
+ err = ipsec_fs_rx_catchall_rules(priv, ipsec->rx_ipv6, &dest);
+ if (err)
+ goto out;
+
+ err = ipsec_fs_rx_catchall_rules(priv, ipsec->rx_ipv4, &dest);
+ if (err)
+ ipsec_fs_rx_catchall_rules_destroy(priv->mdev, priv->ipsec->rx_ipv6);
+
+ err = ipsec_fs_rx_ip_type_catchall_rules_create(priv, &dest);
+ if (err) {
+ ipsec_fs_rx_catchall_rules_destroy(priv->mdev, priv->ipsec->rx_ipv6);
+ ipsec_fs_rx_catchall_rules_destroy(priv->mdev, priv->ipsec->rx_ipv4);
+ }
+
+out:
+ return err;
+}
+
+void mlx5e_accel_ipsec_fs_rx_tables_destroy(struct mlx5e_priv *priv)
+{
+ struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_ipsec *ipsec = priv->ipsec;
+
+ /* Check if IPsec supported */
+ if (!ipsec)
+ return;
+
+ mlx5_destroy_flow_table(ipsec->rx_ip_type->ft);
+ ipsec_fs_rx_table_destroy(mdev, ipsec->rx_ipv6);
+ ipsec_fs_rx_table_destroy(mdev, ipsec->rx_ipv4);
+}
+
+int mlx5e_accel_ipsec_fs_rx_tables_create(struct mlx5e_priv *priv)
+{
+ struct mlx5e_ipsec *ipsec = priv->ipsec;
+ int err = 0;
+
+ /* Check if IPsec supported */
+ if (!ipsec)
+ return 0;
+
+ err = ipsec_fs_rx_ip_type_table_create(priv, 0);
+ if (err)
+ return err;
+
+ err = ipsec_fs_rx_table_create(ipsec->mdev, ipsec->rx_ipv4, 1, 0);
+ if (err)
+ goto err_ipv4_table;
+
+ err = ipsec_fs_rx_table_create(ipsec->mdev, ipsec->rx_ipv6, 5, 1);
+ if (err)
+ goto err_ipv6_table;
+
+ return 0;
+
+err_ipv6_table:
+ ipsec_fs_rx_table_destroy(priv->mdev, ipsec->rx_ipv4);
+err_ipv4_table:
+ mlx5_destroy_flow_table(ipsec->rx_ip_type->ft);
+ return err;
+}
+
+void mlx5e_accel_ipsec_fs_cleanup(struct mlx5e_ipsec *ipsec)
+{
+ WARN_ON(ipsec->tx->ft.refcnt);
+ mutex_destroy(&ipsec->rx_ipv6->ft.mutex);
+ mutex_destroy(&ipsec->rx_ipv4->ft.mutex);
+ mutex_destroy(&ipsec->tx->ft.mutex);
+ ipsec_fs_destroy_counters(ipsec);
+ kfree(ipsec->rx_ip_type);
+ kfree(ipsec->rx_ipv6);
+ kfree(ipsec->rx_ipv4);
+ kfree(ipsec->tx);
+}
+
+int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec)
+{
+ struct mlx5_flow_namespace *tns, *rns;
+ int err = -ENOMEM;
+
+ tns = mlx5_get_flow_namespace(ipsec->mdev, MLX5_FLOW_NAMESPACE_EGRESS_IPSEC);
+ if (!tns)
+ return -EOPNOTSUPP;
+
+ rns = mlx5_get_flow_namespace(ipsec->mdev, MLX5_FLOW_NAMESPACE_KERNEL);
+ if (!rns)
+ return -EOPNOTSUPP;
+
+ ipsec->tx = kzalloc(sizeof(*ipsec->tx), GFP_KERNEL);
+ if (!ipsec->tx)
+ return -ENOMEM;
+
+ ipsec->rx_ip_type = kzalloc(sizeof(*ipsec->rx_ip_type), GFP_KERNEL);
+ if (!ipsec->rx_ip_type)
+ goto err_tx;
+
+ ipsec->rx_ipv4 = kzalloc(sizeof(*ipsec->rx_ipv4), GFP_KERNEL);
+ if (!ipsec->rx_ipv4)
+ goto err_ip_type;
+
+ ipsec->rx_ipv6 = kzalloc(sizeof(*ipsec->rx_ipv6), GFP_KERNEL);
+ if (!ipsec->rx_ipv6)
+ goto err_rx_ipv4;
+
+ err = ipsec_fs_init_counters(ipsec);
+ if (err)
+ goto err_rx_ipv6;
+
+ ipsec->tx->ns = tns;
+ mutex_init(&ipsec->tx->ft.mutex);
+ ipsec->rx_ip_type->ns = rns;
+ ipsec->rx_ipv4->ns = rns;
+ ipsec->rx_ipv6->ns = rns;
+ mutex_init(&ipsec->rx_ipv4->ft.mutex);
+ mutex_init(&ipsec->rx_ipv6->ft.mutex);
+
+ mlx5e_accel_ipsec_fs_init_roce(ipsec);
+
+ return 0;
+
+err_rx_ipv6:
+ kfree(ipsec->rx_ipv6);
+err_rx_ipv4:
+ kfree(ipsec->rx_ipv4);
+err_ip_type:
+ kfree(ipsec->rx_ip_type);
+err_tx:
+ kfree(ipsec->tx);
+ return err;
+}
+
+void mlx5e_accel_ipsec_fs_modify(struct mlx5e_ipsec_sa_entry *sa_entry)
+{
+ struct mlx5e_ipsec_sa_entry sa_entry_shadow = {};
+ int err;
+
+ memcpy(&sa_entry_shadow, sa_entry, sizeof(*sa_entry));
+ memset(&sa_entry_shadow.ipsec_rule, 0x00, sizeof(sa_entry->ipsec_rule));
+
+ err = mlx5e_accel_ipsec_fs_add_rule(&sa_entry_shadow);
+ if (err)
+ return;
+ mlx5e_accel_ipsec_fs_del_rule(sa_entry);
+ memcpy(sa_entry, &sa_entry_shadow, sizeof(*sa_entry));
+}