Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 802c2125 authored by Aviad Yehezkel's avatar Aviad Yehezkel Committed by Jason Gunthorpe
Browse files

IB/mlx5: Add IPsec support for egress and ingress



This commit introduces support for the esp_aes_gcm flow
specification for the Innova device. To that end we add
support for egress steering and some validations that an
IPsec rule is indeed valid.

Signed-off-by: default avatarMatan Barak <matanb@mellanox.com>
Signed-off-by: default avatarAviad Yehezkel <aviadye@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 363c5a57
Loading
Loading
Loading
Loading
+115 −12
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@
#include <linux/mlx5/port.h>
#include <linux/mlx5/port.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/fs.h>
#include <linux/mlx5/fs.h>
#include <linux/mlx5/fs_helpers.h>
#include <linux/list.h>
#include <linux/list.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_umem.h>
@@ -2321,8 +2322,28 @@ static void set_tos(void *outer_c, void *outer_v, u8 mask, u8 val)
		   offsetof(typeof(filter), field) -\
		   offsetof(typeof(filter), field) -\
		   sizeof(filter.field))
		   sizeof(filter.field))


static int parse_flow_flow_action(const union ib_flow_spec *ib_spec,
				  const struct ib_flow_attr *flow_attr,
				  struct mlx5_flow_act *action)
{
	struct mlx5_ib_flow_action *maction = to_mflow_act(ib_spec->action.act);

	switch (maction->ib_action.type) {
	case IB_FLOW_ACTION_ESP:
		/* Currently only AES_GCM keymat is supported by the driver */
		action->esp_id = (uintptr_t)maction->esp_aes_gcm.ctx;
		action->action |= flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS ?
			MLX5_FLOW_CONTEXT_ACTION_ENCRYPT :
			MLX5_FLOW_CONTEXT_ACTION_DECRYPT;
		return 0;
	default:
		return -EOPNOTSUPP;
	}
}

static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
			   u32 *match_v, const union ib_flow_spec *ib_spec,
			   u32 *match_v, const union ib_flow_spec *ib_spec,
			   const struct ib_flow_attr *flow_attr,
			   struct mlx5_flow_act *action)
			   struct mlx5_flow_act *action)
{
{
	void *misc_params_c = MLX5_ADDR_OF(fte_match_param, match_c,
	void *misc_params_c = MLX5_ADDR_OF(fte_match_param, match_c,
@@ -2332,6 +2353,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
	void *headers_c;
	void *headers_c;
	void *headers_v;
	void *headers_v;
	int match_ipv;
	int match_ipv;
	int ret;


	if (ib_spec->type & IB_FLOW_SPEC_INNER) {
	if (ib_spec->type & IB_FLOW_SPEC_INNER) {
		headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
		headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
@@ -2482,7 +2504,15 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
			       ntohl(ib_spec->ipv6.mask.flow_label),
			       ntohl(ib_spec->ipv6.mask.flow_label),
			       ntohl(ib_spec->ipv6.val.flow_label),
			       ntohl(ib_spec->ipv6.val.flow_label),
			       ib_spec->type & IB_FLOW_SPEC_INNER);
			       ib_spec->type & IB_FLOW_SPEC_INNER);
		break;
	case IB_FLOW_SPEC_ESP:
		if (ib_spec->esp.mask.seq)
			return -EOPNOTSUPP;


		MLX5_SET(fte_match_set_misc, misc_params_c, outer_esp_spi,
			 ntohl(ib_spec->esp.mask.spi));
		MLX5_SET(fte_match_set_misc, misc_params_v, outer_esp_spi,
			 ntohl(ib_spec->esp.val.spi));
		break;
		break;
	case IB_FLOW_SPEC_TCP:
	case IB_FLOW_SPEC_TCP:
		if (FIELDS_NOT_SUPPORTED(ib_spec->tcp_udp.mask,
		if (FIELDS_NOT_SUPPORTED(ib_spec->tcp_udp.mask,
@@ -2550,6 +2580,11 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
			return -EOPNOTSUPP;
			return -EOPNOTSUPP;
		action->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
		action->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
		break;
		break;
	case IB_FLOW_SPEC_ACTION_HANDLE:
		ret = parse_flow_flow_action(ib_spec, flow_attr, action);
		if (ret)
			return ret;
		break;
	default:
	default:
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -2591,6 +2626,46 @@ static bool flow_is_multicast_only(const struct ib_flow_attr *ib_attr)
	return false;
	return false;
}
}


enum valid_spec {
	VALID_SPEC_INVALID,
	VALID_SPEC_VALID,
	VALID_SPEC_NA,
};

static enum valid_spec
is_valid_esp_aes_gcm(struct mlx5_core_dev *mdev,
		     const struct mlx5_flow_spec *spec,
		     const struct mlx5_flow_act *flow_act,
		     bool egress)
{
	const u32 *match_c = spec->match_criteria;
	bool is_crypto =
		(flow_act->action & (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
				     MLX5_FLOW_CONTEXT_ACTION_DECRYPT));
	bool is_ipsec = mlx5_fs_is_ipsec_flow(match_c);
	bool is_drop = flow_act->action & MLX5_FLOW_CONTEXT_ACTION_DROP;

	/*
	 * Currently only crypto is supported in egress, when regular egress
	 * rules would be supported, always return VALID_SPEC_NA.
	 */
	if (!is_crypto)
		return egress ? VALID_SPEC_INVALID : VALID_SPEC_NA;

	return is_crypto && is_ipsec &&
		(!egress || (!is_drop && !flow_act->has_flow_tag)) ?
		VALID_SPEC_VALID : VALID_SPEC_INVALID;
}

static bool is_valid_spec(struct mlx5_core_dev *mdev,
			  const struct mlx5_flow_spec *spec,
			  const struct mlx5_flow_act *flow_act,
			  bool egress)
{
	/* We curretly only support ipsec egress flow */
	return is_valid_esp_aes_gcm(mdev, spec, flow_act, egress) != VALID_SPEC_INVALID;
}

static bool is_valid_ethertype(struct mlx5_core_dev *mdev,
static bool is_valid_ethertype(struct mlx5_core_dev *mdev,
			       const struct ib_flow_attr *flow_attr,
			       const struct ib_flow_attr *flow_attr,
			       bool check_inner)
			       bool check_inner)
@@ -2715,13 +2790,17 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
	max_table_size = BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev,
	max_table_size = BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev,
						       log_max_ft_size));
						       log_max_ft_size));
	if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
	if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
		if (flow_is_multicast_only(flow_attr) &&
		if (ft_type == MLX5_IB_FT_TX)
			priority = 0;
		else if (flow_is_multicast_only(flow_attr) &&
			 !dont_trap)
			 !dont_trap)
			priority = MLX5_IB_FLOW_MCAST_PRIO;
			priority = MLX5_IB_FLOW_MCAST_PRIO;
		else
		else
			priority = ib_prio_to_core_prio(flow_attr->priority,
			priority = ib_prio_to_core_prio(flow_attr->priority,
							dont_trap);
							dont_trap);
		ns = mlx5_get_flow_namespace(dev->mdev,
		ns = mlx5_get_flow_namespace(dev->mdev,
					     ft_type == MLX5_IB_FT_TX ?
					     MLX5_FLOW_NAMESPACE_EGRESS :
					     MLX5_FLOW_NAMESPACE_BYPASS);
					     MLX5_FLOW_NAMESPACE_BYPASS);
		num_entries = MLX5_FS_MAX_ENTRIES;
		num_entries = MLX5_FS_MAX_ENTRIES;
		num_groups = MLX5_FS_MAX_TYPES;
		num_groups = MLX5_FS_MAX_TYPES;
@@ -2808,6 +2887,7 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
	unsigned int spec_index;
	unsigned int spec_index;
	int err = 0;
	int err = 0;
	int dest_num = 1;
	int dest_num = 1;
	bool is_egress = flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS;


	if (!is_valid_attr(dev->mdev, flow_attr))
	if (!is_valid_attr(dev->mdev, flow_attr))
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);
@@ -2824,7 +2904,7 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
	for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
	for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
		err = parse_flow_attr(dev->mdev, spec->match_criteria,
		err = parse_flow_attr(dev->mdev, spec->match_criteria,
				      spec->match_value,
				      spec->match_value,
				      ib_flow, &flow_act);
				      ib_flow, flow_attr, &flow_act);
		if (err < 0)
		if (err < 0)
			goto free;
			goto free;


@@ -2847,11 +2927,22 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
	}
	}


	spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria);
	spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria);

	if (is_egress &&
	    !is_valid_spec(dev->mdev, spec, &flow_act, is_egress)) {
		err = -EINVAL;
		goto free;
	}

	if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
	if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
		rule_dst = NULL;
		rule_dst = NULL;
		dest_num = 0;
		dest_num = 0;
	} else {
	} else {
		flow_act.action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST :
		if (is_egress)
			flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW;
		else
			flow_act.action |=
				dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST :
					MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
					MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
	}
	}


@@ -3026,6 +3117,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
	struct mlx5_flow_destination *dst = NULL;
	struct mlx5_flow_destination *dst = NULL;
	struct mlx5_ib_flow_prio *ft_prio_tx = NULL;
	struct mlx5_ib_flow_prio *ft_prio_tx = NULL;
	struct mlx5_ib_flow_prio *ft_prio;
	struct mlx5_ib_flow_prio *ft_prio;
	bool is_egress = flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS;
	int err;
	int err;
	int underlay_qpn;
	int underlay_qpn;


@@ -3034,7 +3126,13 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,


	if (domain != IB_FLOW_DOMAIN_USER ||
	if (domain != IB_FLOW_DOMAIN_USER ||
	    flow_attr->port > dev->num_ports ||
	    flow_attr->port > dev->num_ports ||
	    (flow_attr->flags & ~IB_FLOW_ATTR_FLAGS_DONT_TRAP))
	    (flow_attr->flags & ~(IB_FLOW_ATTR_FLAGS_DONT_TRAP |
				  IB_FLOW_ATTR_FLAGS_EGRESS)))
		return ERR_PTR(-EINVAL);

	if (is_egress &&
	    (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
	     flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT))
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);


	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
@@ -3043,7 +3141,8 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,


	mutex_lock(&dev->flow_db->lock);
	mutex_lock(&dev->flow_db->lock);


	ft_prio = get_flow_table(dev, flow_attr, MLX5_IB_FT_RX);
	ft_prio = get_flow_table(dev, flow_attr,
				 is_egress ? MLX5_IB_FT_TX : MLX5_IB_FT_RX);
	if (IS_ERR(ft_prio)) {
	if (IS_ERR(ft_prio)) {
		err = PTR_ERR(ft_prio);
		err = PTR_ERR(ft_prio);
		goto unlock;
		goto unlock;
@@ -3057,11 +3156,15 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
		}
		}
	}
	}


	if (is_egress) {
		dst->type = MLX5_FLOW_DESTINATION_TYPE_PORT;
	} else {
		dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
		dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
		if (mqp->flags & MLX5_IB_QP_RSS)
		if (mqp->flags & MLX5_IB_QP_RSS)
			dst->tir_num = mqp->rss_qp.tirn;
			dst->tir_num = mqp->rss_qp.tirn;
		else
		else
			dst->tir_num = mqp->raw_packet_qp.rq.tirn;
			dst->tir_num = mqp->raw_packet_qp.rq.tirn;
	}


	if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
	if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
		if (flow_attr->flags & IB_FLOW_ATTR_FLAGS_DONT_TRAP)  {
		if (flow_attr->flags & IB_FLOW_ATTR_FLAGS_DONT_TRAP)  {
+2 −0
Original line number Original line Diff line number Diff line
@@ -152,6 +152,7 @@ struct mlx5_ib_pd {


#define MLX5_IB_NUM_FLOW_FT		(MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
#define MLX5_IB_NUM_FLOW_FT		(MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
#define MLX5_IB_NUM_SNIFFER_FTS		2
#define MLX5_IB_NUM_SNIFFER_FTS		2
#define MLX5_IB_NUM_EGRESS_FTS		1
struct mlx5_ib_flow_prio {
struct mlx5_ib_flow_prio {
	struct mlx5_flow_table		*flow_table;
	struct mlx5_flow_table		*flow_table;
	unsigned int			refcount;
	unsigned int			refcount;
@@ -167,6 +168,7 @@ struct mlx5_ib_flow_handler {
struct mlx5_ib_flow_db {
struct mlx5_ib_flow_db {
	struct mlx5_ib_flow_prio	prios[MLX5_IB_NUM_FLOW_FT];
	struct mlx5_ib_flow_prio	prios[MLX5_IB_NUM_FLOW_FT];
	struct mlx5_ib_flow_prio	sniffer[MLX5_IB_NUM_SNIFFER_FTS];
	struct mlx5_ib_flow_prio	sniffer[MLX5_IB_NUM_SNIFFER_FTS];
	struct mlx5_ib_flow_prio	egress[MLX5_IB_NUM_EGRESS_FTS];
	struct mlx5_flow_table		*lag_demux_ft;
	struct mlx5_flow_table		*lag_demux_ft;
	/* Protect flow steering bypass flow tables
	/* Protect flow steering bypass flow tables
	 * when add/del flow rules.
	 * when add/del flow rules.