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

Commit d386939a authored by Yevgeny Kliteynik's avatar Yevgeny Kliteynik Committed by Saeed Mahameed
Browse files

net/mlx5e: Rearrange tc tunnel code in a modular way



Rearrange tc tunnel code so that it would be easy to add future tunnels:
 - Define tc tunnel object with the fields and callbacks that any
   tunnel must implement.
 - Define tc UDP tunnel object for UDP tunnels, such as VXLAN
 - Move each tunnel code (GRE, VXLAN) to its own separate file
 - Rewrite tc tunnel implementation in a general way - using only
   the objects and their callbacks.

Reviewed-by: default avatarOz Shlomo <ozsh@mellanox.com>
Signed-off-by: default avatarYevgeny Kliteynik <kliteyn@mellanox.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
parent 1f6da306
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o
mlx5_core-$(CONFIG_MLX5_EN_RXNFC)    += en_fs_ethtool.o
mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \
					lib/geneve.o
					lib/geneve.o en/tc_tun_vxlan.o en/tc_tun_gre.o

#
# Core extra
+83 −198
Original line number Diff line number Diff line
@@ -3,9 +3,19 @@

#include <net/vxlan.h>
#include <net/gre.h>
#include "lib/vxlan.h"
#include "en/tc_tun.h"

struct mlx5e_tc_tunnel *mlx5e_get_tc_tun(struct net_device *tunnel_dev)
{
	if (netif_is_vxlan(tunnel_dev))
		return &vxlan_tunnel;
	else if (netif_is_gretap(tunnel_dev) ||
		 netif_is_ip6gretap(tunnel_dev))
		return &gre_tunnel;
	else
		return NULL;
}

static int get_route_and_out_devs(struct mlx5e_priv *priv,
				  struct net_device *dev,
				  struct net_device **route_dev,
@@ -141,64 +151,15 @@ static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
	return 0;
}

static int mlx5e_gen_vxlan_header(char buf[],
				  const struct ip_tunnel_key *tun_key)
{
	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
	struct udphdr *udp = (struct udphdr *)(buf);
	struct vxlanhdr *vxh = (struct vxlanhdr *)
			       ((char *)udp + sizeof(struct udphdr));

	udp->dest = tun_key->tp_dst;
	vxh->vx_flags = VXLAN_HF_VNI;
	vxh->vx_vni = vxlan_vni_field(tun_id);

	return 0;
}

static int mlx5e_gen_gre_header(char buf[], const struct ip_tunnel_key *tun_key)
{
	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
	int hdr_len;
	struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);

	/* the HW does not calculate GRE csum or sequences */
	if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
		return -EOPNOTSUPP;

	greh->protocol = htons(ETH_P_TEB);

	/* GRE key */
	hdr_len = gre_calc_hlen(tun_key->tun_flags);
	greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
	if (tun_key->tun_flags & TUNNEL_KEY) {
		__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);

		*ptr = tun_id;
	}

	return 0;
}

static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto,
				      struct mlx5e_encap_entry *e)
{
	int err = 0;
	const struct ip_tunnel_key *key = &e->tun_info->key;

	if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
		*ip_proto = IPPROTO_UDP;
		err = mlx5e_gen_vxlan_header(buf, key);
	} else if  (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
		*ip_proto = IPPROTO_GRE;
		err = mlx5e_gen_gre_header(buf, key);
	} else {
		pr_warn("mlx5: Cannot generate tunnel header for tunnel type (%d)\n"
			, e->tunnel_type);
		err = -EOPNOTSUPP;
	if (!e->tunnel) {
		pr_warn("mlx5: Cannot generate tunnel header for this tunnel\n");
		return -EOPNOTSUPP;
	}

	return err;
	return e->tunnel->generate_ip_tun_hdr(buf, ip_proto, e);
}

static char *gen_eth_tnl_hdr(char *buf, struct net_device *dev,
@@ -254,7 +215,7 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
	ipv4_encap_size =
		(is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
		sizeof(struct iphdr) +
		e->tunnel_hlen;
		e->tunnel->calc_hlen(e);

	if (max_encap_size < ipv4_encap_size) {
		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
@@ -370,7 +331,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
	ipv6_encap_size =
		(is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
		sizeof(struct ipv6hdr) +
		e->tunnel_hlen;
		e->tunnel->calc_hlen(e);

	if (max_encap_size < ipv6_encap_size) {
		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
@@ -456,27 +417,12 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
	return err;
}

int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev)
{
	if (netif_is_vxlan(tunnel_dev))
		return MLX5E_TC_TUNNEL_TYPE_VXLAN;
	else if (netif_is_gretap(tunnel_dev) ||
		 netif_is_ip6gretap(tunnel_dev))
		return MLX5E_TC_TUNNEL_TYPE_GRETAP;
	else
		return MLX5E_TC_TUNNEL_TYPE_UNKNOWN;
}

bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
				    struct net_device *netdev)
{
	int tunnel_type = mlx5e_tc_tun_get_type(netdev);
	struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(netdev);

	if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN &&
	    MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
		return true;
	else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP &&
		 MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap))
	if (tunnel && tunnel->can_offload(priv))
		return true;
	else
		return false;
@@ -487,33 +433,55 @@ int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
				 struct mlx5e_encap_entry *e,
				 struct netlink_ext_ack *extack)
{
	e->tunnel_type = mlx5e_tc_tun_get_type(tunnel_dev);
	struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(tunnel_dev);

	if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
		int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);
	if (!tunnel) {
		e->reformat_type = -1;
		return -EOPNOTSUPP;
	}

		if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
			NL_SET_ERR_MSG_MOD(extack,
					   "vxlan udp dport was not registered with the HW");
	return tunnel->init_encap_attr(tunnel_dev, priv, e, extack);
}

int mlx5e_tc_tun_parse(struct net_device *filter_dev,
		       struct mlx5e_priv *priv,
		       struct mlx5_flow_spec *spec,
		       struct tc_cls_flower_offload *f,
		       void *headers_c,
		       void *headers_v, u8 *match_level)
{
	struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(filter_dev);
	int err = 0;

	if (!tunnel) {
		netdev_warn(priv->netdev,
				    "%d isn't an offloaded vxlan udp dport\n",
				    dst_port);
			return -EOPNOTSUPP;
			    "decapsulation offload is not supported for %s net device\n",
			    mlx5e_netdev_kind(filter_dev));
		err = -EOPNOTSUPP;
		goto out;
	}
		e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
		e->tunnel_hlen = VXLAN_HLEN;
	} else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
		e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
		e->tunnel_hlen = gre_calc_hlen(e->tun_info->key.tun_flags);
	} else {
		e->reformat_type = -1;
		e->tunnel_hlen = -1;
		return -EOPNOTSUPP;

	*match_level = tunnel->match_level;

	if (tunnel->parse_udp_ports) {
		err = tunnel->parse_udp_ports(priv, spec, f,
					      headers_c, headers_v);
		if (err)
			goto out;
	}
	return 0;

	if (tunnel->parse_tunnel) {
		err = tunnel->parse_tunnel(priv, spec, f,
					   headers_c, headers_v);
		if (err)
			goto out;
	}

static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
out:
	return err;
}

int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv,
				 struct mlx5_flow_spec *spec,
				 struct tc_cls_flower_offload *f,
				 void *headers_c,
@@ -521,37 +489,31 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
{
	struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
	struct netlink_ext_ack *extack = f->common.extack;
	void *misc_c = MLX5_ADDR_OF(fte_match_param,
				    spec->match_criteria,
				    misc_parameters);
	void *misc_v = MLX5_ADDR_OF(fte_match_param,
				    spec->match_value,
				    misc_parameters);
	struct flow_match_ports enc_ports;

	flow_rule_match_enc_ports(rule, &enc_ports);

	/* Full udp dst port must be given */
	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS) ||
	    memchr_inv(&enc_ports.mask->dst, 0xff, sizeof(enc_ports.mask->dst))) {

	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "VXLAN decap filter must include enc_dst_port condition");
				   "UDP tunnel decap filter must include enc_dst_port condition");
		netdev_warn(priv->netdev,
			    "VXLAN decap filter must include enc_dst_port condition\n");
			    "UDP tunnel decap filter must include enc_dst_port condition\n");
		return -EOPNOTSUPP;
	}

	/* udp dst port must be knonwn as a VXLAN port */
	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(enc_ports.key->dst))) {
	flow_rule_match_enc_ports(rule, &enc_ports);

	if (memchr_inv(&enc_ports.mask->dst, 0xff,
		       sizeof(enc_ports.mask->dst))) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Matched UDP port is not registered as a VXLAN port");
				   "UDP tunnel decap filter must match enc_dst_port fully");
		netdev_warn(priv->netdev,
			    "UDP port %d is not registered as a VXLAN port\n",
			    be16_to_cpu(enc_ports.key->dst));
			    "UDP tunnel decap filter must match enc_dst_port fully\n");
		return -EOPNOTSUPP;
	}

	/* dst UDP port is valid here */
	/* match on UDP protocol and dst port number */

	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
	MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);

@@ -560,92 +522,15 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
	MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport,
		 ntohs(enc_ports.key->dst));

	/* UDP src port on outer header is generated by HW,
	 * so it is probably a bad idea to request matching it.
	 * Nonetheless, it is allowed.
	 */

	MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport,
		 ntohs(enc_ports.mask->src));
	MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport,
		 ntohs(enc_ports.key->src));

	/* match on VNI */
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
		struct flow_match_enc_keyid enc_keyid;

		flow_rule_match_enc_keyid(rule, &enc_keyid);

		MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
			 be32_to_cpu(enc_keyid.mask->keyid));
		MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
			 be32_to_cpu(enc_keyid.key->keyid));
	}
	return 0;
}

static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
				     struct mlx5_flow_spec *spec,
				     struct tc_cls_flower_offload *f,
				     void *outer_headers_c,
				     void *outer_headers_v)
{
	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
				    misc_parameters);
	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
				    misc_parameters);
	struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);

	if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) {
		NL_SET_ERR_MSG_MOD(f->common.extack,
				   "GRE HW offloading is not supported");
		netdev_warn(priv->netdev, "GRE HW offloading is not supported\n");
		return -EOPNOTSUPP;
	}

	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol);
	MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
		 ip_protocol, IPPROTO_GRE);

	/* gre protocol*/
	MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
	MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);

	/* gre key */
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
		struct flow_match_enc_keyid enc_keyid;

		flow_rule_match_enc_keyid(rule, &enc_keyid);
		MLX5_SET(fte_match_set_misc, misc_c,
			 gre_key.key, be32_to_cpu(enc_keyid.mask->keyid));
		MLX5_SET(fte_match_set_misc, misc_v,
			 gre_key.key, be32_to_cpu(enc_keyid.key->keyid));
	}

	return 0;
}

int mlx5e_tc_tun_parse(struct net_device *filter_dev,
		       struct mlx5e_priv *priv,
		       struct mlx5_flow_spec *spec,
		       struct tc_cls_flower_offload *f,
		       void *headers_c,
		       void *headers_v, u8 *match_level)
{
	int tunnel_type;
	int err = 0;

	tunnel_type = mlx5e_tc_tun_get_type(filter_dev);
	if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
		*match_level = MLX5_MATCH_L4;
		err = mlx5e_tc_tun_parse_vxlan(priv, spec, f,
					       headers_c, headers_v);
	} else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
		*match_level = MLX5_MATCH_L3;
		err = mlx5e_tc_tun_parse_gretap(priv, spec, f,
						headers_c, headers_v);
	} else {
		netdev_warn(priv->netdev,
			    "decapsulation offload is not supported for %s (kind: \"%s\")\n",
			    netdev_name(filter_dev),
			    mlx5e_netdev_kind(filter_dev));

		return -EOPNOTSUPP;
	}
	return err;
}
+37 −2
Original line number Diff line number Diff line
@@ -14,9 +14,39 @@
enum {
	MLX5E_TC_TUNNEL_TYPE_UNKNOWN,
	MLX5E_TC_TUNNEL_TYPE_VXLAN,
	MLX5E_TC_TUNNEL_TYPE_GRETAP
	MLX5E_TC_TUNNEL_TYPE_GRETAP,
};

struct mlx5e_tc_tunnel {
	int tunnel_type;
	enum mlx5_flow_match_level match_level;

	bool (*can_offload)(struct mlx5e_priv *priv);
	int (*calc_hlen)(struct mlx5e_encap_entry *e);
	int (*init_encap_attr)(struct net_device *tunnel_dev,
			       struct mlx5e_priv *priv,
			       struct mlx5e_encap_entry *e,
			       struct netlink_ext_ack *extack);
	int (*generate_ip_tun_hdr)(char buf[],
				   __u8 *ip_proto,
				   struct mlx5e_encap_entry *e);
	int (*parse_udp_ports)(struct mlx5e_priv *priv,
			       struct mlx5_flow_spec *spec,
			       struct tc_cls_flower_offload *f,
			       void *headers_c,
			       void *headers_v);
	int (*parse_tunnel)(struct mlx5e_priv *priv,
			    struct mlx5_flow_spec *spec,
			    struct tc_cls_flower_offload *f,
			    void *headers_c,
			    void *headers_v);
};

extern struct mlx5e_tc_tunnel vxlan_tunnel;
extern struct mlx5e_tc_tunnel gre_tunnel;

struct mlx5e_tc_tunnel *mlx5e_get_tc_tun(struct net_device *tunnel_dev);

int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
				 struct mlx5e_priv *priv,
				 struct mlx5e_encap_entry *e,
@@ -30,7 +60,6 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
				    struct net_device *mirred_dev,
				    struct mlx5e_encap_entry *e);

int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev);
bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
				    struct net_device *netdev);

@@ -41,4 +70,10 @@ int mlx5e_tc_tun_parse(struct net_device *filter_dev,
		       void *headers_c,
		       void *headers_v, u8 *match_level);

int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv,
				 struct mlx5_flow_spec *spec,
				 struct tc_cls_flower_offload *f,
				 void *headers_c,
				 void *headers_v);

#endif //__MLX5_EN_TC_TUNNEL_H__
+95 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2018 Mellanox Technologies. */

#include <net/gre.h>
#include "en/tc_tun.h"

static bool mlx5e_tc_tun_can_offload_gretap(struct mlx5e_priv *priv)
{
	return !!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap);
}

static int mlx5e_tc_tun_calc_hlen_gretap(struct mlx5e_encap_entry *e)
{
	return gre_calc_hlen(e->tun_info->key.tun_flags);
}

static int mlx5e_tc_tun_init_encap_attr_gretap(struct net_device *tunnel_dev,
					       struct mlx5e_priv *priv,
					       struct mlx5e_encap_entry *e,
					       struct netlink_ext_ack *extack)
{
	e->tunnel = &gre_tunnel;
	e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
	return 0;
}

static int mlx5e_gen_ip_tunnel_header_gretap(char buf[],
					     __u8 *ip_proto,
					     struct mlx5e_encap_entry *e)
{
	const struct ip_tunnel_key *tun_key  = &e->tun_info->key;
	struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);
	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
	int hdr_len;

	*ip_proto = IPPROTO_GRE;

	/* the HW does not calculate GRE csum or sequences */
	if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
		return -EOPNOTSUPP;

	greh->protocol = htons(ETH_P_TEB);

	/* GRE key */
	hdr_len	= mlx5e_tc_tun_calc_hlen_gretap(e);
	greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
	if (tun_key->tun_flags & TUNNEL_KEY) {
		__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
		*ptr = tun_id;
	}

	return 0;
}

static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
				     struct mlx5_flow_spec *spec,
				     struct tc_cls_flower_offload *f,
				     void *headers_c,
				     void *headers_v)
{
	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
	struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);

	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
	MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_GRE);

	/* gre protocol */
	MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
	MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);

	/* gre key */
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
		struct flow_match_enc_keyid enc_keyid;

		flow_rule_match_enc_keyid(rule, &enc_keyid);
		MLX5_SET(fte_match_set_misc, misc_c,
			 gre_key.key, be32_to_cpu(enc_keyid.mask->keyid));
		MLX5_SET(fte_match_set_misc, misc_v,
			 gre_key.key, be32_to_cpu(enc_keyid.key->keyid));
	}

	return 0;
}

struct mlx5e_tc_tunnel gre_tunnel = {
	.tunnel_type          = MLX5E_TC_TUNNEL_TYPE_GRETAP,
	.match_level          = MLX5_MATCH_L3,
	.can_offload          = mlx5e_tc_tun_can_offload_gretap,
	.calc_hlen            = mlx5e_tc_tun_calc_hlen_gretap,
	.init_encap_attr      = mlx5e_tc_tun_init_encap_attr_gretap,
	.generate_ip_tun_hdr  = mlx5e_gen_ip_tunnel_header_gretap,
	.parse_udp_ports      = NULL,
	.parse_tunnel         = mlx5e_tc_tun_parse_gretap,
};
+151 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2018 Mellanox Technologies. */

#include <net/vxlan.h>
#include "lib/vxlan.h"
#include "en/tc_tun.h"

static bool mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv *priv)
{
	return !!MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap);
}

static int mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry *e)
{
	return VXLAN_HLEN;
}

static int mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv *priv,
					      struct tc_cls_flower_offload *f)
{
	struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
	struct netlink_ext_ack *extack = f->common.extack;
	struct flow_match_ports enc_ports;

	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS))
		return -EOPNOTSUPP;

	flow_rule_match_enc_ports(rule, &enc_ports);

	/* check the UDP destination port validity */

	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan,
				    be16_to_cpu(enc_ports.key->dst))) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Matched UDP dst port is not registered as a VXLAN port");
		netdev_warn(priv->netdev,
			    "UDP port %d is not registered as a VXLAN port\n",
			    be16_to_cpu(enc_ports.key->dst));
		return -EOPNOTSUPP;
	}

	return 0;
}

static int mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv *priv,
					      struct mlx5_flow_spec *spec,
					      struct tc_cls_flower_offload *f,
					      void *headers_c,
					      void *headers_v)
{
	int err = 0;

	err = mlx5e_tc_tun_parse_udp_ports(priv, spec, f, headers_c, headers_v);
	if (err)
		return err;

	return mlx5e_tc_tun_check_udp_dport_vxlan(priv, f);
}

static int mlx5e_tc_tun_init_encap_attr_vxlan(struct net_device *tunnel_dev,
					      struct mlx5e_priv *priv,
					      struct mlx5e_encap_entry *e,
					      struct netlink_ext_ack *extack)
{
	int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);

	e->tunnel = &vxlan_tunnel;

	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "vxlan udp dport was not registered with the HW");
		netdev_warn(priv->netdev,
			    "%d isn't an offloaded vxlan udp dport\n",
			    dst_port);
		return -EOPNOTSUPP;
	}

	e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
	return 0;
}

static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[],
					    __u8 *ip_proto,
					    struct mlx5e_encap_entry *e)
{
	const struct ip_tunnel_key *tun_key = &e->tun_info->key;
	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
	struct udphdr *udp = (struct udphdr *)(buf);
	struct vxlanhdr *vxh;

	vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
	*ip_proto = IPPROTO_UDP;

	udp->dest = tun_key->tp_dst;
	vxh->vx_flags = VXLAN_HF_VNI;
	vxh->vx_vni = vxlan_vni_field(tun_id);

	return 0;
}

static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
				    struct mlx5_flow_spec *spec,
				    struct tc_cls_flower_offload *f,
				    void *headers_c,
				    void *headers_v)
{
	struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
	struct netlink_ext_ack *extack = f->common.extack;
	struct flow_match_enc_keyid enc_keyid;
	void *misc_c, *misc_v;

	misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
	misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);

	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
		return 0;

	flow_rule_match_enc_keyid(rule, &enc_keyid);

	if (!enc_keyid.mask->keyid)
		return 0;

	/* match on VNI is required */

	if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
					ft_field_support.outer_vxlan_vni)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Matching on VXLAN VNI is not supported");
		netdev_warn(priv->netdev,
			    "Matching on VXLAN VNI is not supported\n");
		return -EOPNOTSUPP;
	}

	MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
		 be32_to_cpu(enc_keyid.mask->keyid));
	MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
		 be32_to_cpu(enc_keyid.key->keyid));

	return 0;
}

struct mlx5e_tc_tunnel vxlan_tunnel = {
	.tunnel_type          = MLX5E_TC_TUNNEL_TYPE_VXLAN,
	.match_level          = MLX5_MATCH_L4,
	.can_offload          = mlx5e_tc_tun_can_offload_vxlan,
	.calc_hlen            = mlx5e_tc_tun_calc_hlen_vxlan,
	.init_encap_attr      = mlx5e_tc_tun_init_encap_attr_vxlan,
	.generate_ip_tun_hdr  = mlx5e_gen_ip_tunnel_header_vxlan,
	.parse_udp_ports      = mlx5e_tc_tun_parse_udp_ports_vxlan,
	.parse_tunnel         = mlx5e_tc_tun_parse_vxlan,
};
Loading