xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2018 Mellanox Technologies. */
3 
4 #include <net/ip_tunnels.h>
5 #include <net/vxlan.h>
6 #include "lib/vxlan.h"
7 #include "en/tc_tun.h"
8 
mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv * priv)9 static bool mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv *priv)
10 {
11 	return !!MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap);
12 }
13 
mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry * e)14 static int mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry *e)
15 {
16 	return VXLAN_HLEN;
17 }
18 
mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv * priv,struct flow_cls_offload * f)19 static int mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv *priv,
20 					      struct flow_cls_offload *f)
21 {
22 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
23 	struct netlink_ext_ack *extack = f->common.extack;
24 	struct flow_match_ports enc_ports;
25 
26 	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS))
27 		return -EOPNOTSUPP;
28 
29 	flow_rule_match_enc_ports(rule, &enc_ports);
30 
31 	/* check the UDP destination port validity */
32 
33 	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan,
34 				    be16_to_cpu(enc_ports.key->dst))) {
35 		NL_SET_ERR_MSG_MOD(extack,
36 				   "Matched UDP dst port is not registered as a VXLAN port");
37 		netdev_warn(priv->netdev,
38 			    "UDP port %d is not registered as a VXLAN port\n",
39 			    be16_to_cpu(enc_ports.key->dst));
40 		return -EOPNOTSUPP;
41 	}
42 
43 	return 0;
44 }
45 
mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f,void * headers_c,void * headers_v)46 static int mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv *priv,
47 					      struct mlx5_flow_spec *spec,
48 					      struct flow_cls_offload *f,
49 					      void *headers_c,
50 					      void *headers_v)
51 {
52 	int err = 0;
53 
54 	err = mlx5e_tc_tun_parse_udp_ports(priv, spec, f, headers_c, headers_v);
55 	if (err)
56 		return err;
57 
58 	return mlx5e_tc_tun_check_udp_dport_vxlan(priv, f);
59 }
60 
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)61 static int mlx5e_tc_tun_init_encap_attr_vxlan(struct net_device *tunnel_dev,
62 					      struct mlx5e_priv *priv,
63 					      struct mlx5e_encap_entry *e,
64 					      struct netlink_ext_ack *extack)
65 {
66 	int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);
67 
68 	e->tunnel = &vxlan_tunnel;
69 
70 	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
71 		NL_SET_ERR_MSG_MOD(extack,
72 				   "vxlan udp dport was not registered with the HW");
73 		netdev_warn(priv->netdev,
74 			    "%d isn't an offloaded vxlan udp dport\n",
75 			    dst_port);
76 		return -EOPNOTSUPP;
77 	}
78 
79 	e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
80 	return 0;
81 }
82 
mlx5e_gen_ip_tunnel_header_vxlan(char buf[],__u8 * ip_proto,struct mlx5e_encap_entry * e)83 static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[],
84 					    __u8 *ip_proto,
85 					    struct mlx5e_encap_entry *e)
86 {
87 	const struct ip_tunnel_key *tun_key = &e->tun_info->key;
88 	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
89 	struct udphdr *udp = (struct udphdr *)(buf);
90 	const struct vxlan_metadata *md;
91 	struct vxlanhdr *vxh;
92 
93 	if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, tun_key->tun_flags) &&
94 	    e->tun_info->options_len != sizeof(*md))
95 		return -EOPNOTSUPP;
96 	vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
97 	*ip_proto = IPPROTO_UDP;
98 
99 	udp->dest = tun_key->tp_dst;
100 	vxh->vx_flags = VXLAN_HF_VNI;
101 	vxh->vx_vni = vxlan_vni_field(tun_id);
102 	if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, tun_key->tun_flags)) {
103 		md = ip_tunnel_info_opts(e->tun_info);
104 		vxlan_build_gbp_hdr(vxh, md);
105 	}
106 
107 	return 0;
108 }
109 
mlx5e_tc_tun_parse_vxlan_gbp_option(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f)110 static int mlx5e_tc_tun_parse_vxlan_gbp_option(struct mlx5e_priv *priv,
111 					       struct mlx5_flow_spec *spec,
112 					       struct flow_cls_offload *f)
113 {
114 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
115 	struct netlink_ext_ack *extack = f->common.extack;
116 	struct flow_match_enc_opts enc_opts;
117 	void *misc5_c, *misc5_v;
118 	u32 *gbp, *gbp_mask;
119 
120 	flow_rule_match_enc_opts(rule, &enc_opts);
121 
122 	if (memchr_inv(&enc_opts.mask->data, 0, sizeof(enc_opts.mask->data)) &&
123 	    !MLX5_CAP_ESW_FT_FIELD_SUPPORT_2(priv->mdev, tunnel_header_0_1)) {
124 		NL_SET_ERR_MSG_MOD(extack, "Matching on VxLAN GBP is not supported");
125 		return -EOPNOTSUPP;
126 	}
127 
128 	if (enc_opts.key->dst_opt_type != IP_TUNNEL_VXLAN_OPT_BIT) {
129 		NL_SET_ERR_MSG_MOD(extack, "Wrong VxLAN option type: not GBP");
130 		return -EOPNOTSUPP;
131 	}
132 
133 	if (enc_opts.key->len != sizeof(*gbp) ||
134 	    enc_opts.mask->len != sizeof(*gbp_mask)) {
135 		NL_SET_ERR_MSG_MOD(extack, "VxLAN GBP option/mask len is not 32 bits");
136 		return -EINVAL;
137 	}
138 
139 	gbp = (u32 *)&enc_opts.key->data[0];
140 	gbp_mask = (u32 *)&enc_opts.mask->data[0];
141 
142 	if (*gbp_mask & ~VXLAN_GBP_MASK) {
143 		NL_SET_ERR_MSG_FMT_MOD(extack, "Wrong VxLAN GBP mask(0x%08X)\n", *gbp_mask);
144 		return -EINVAL;
145 	}
146 
147 	misc5_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_5);
148 	misc5_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_5);
149 	MLX5_SET(fte_match_set_misc5, misc5_c, tunnel_header_0, *gbp_mask);
150 	MLX5_SET(fte_match_set_misc5, misc5_v, tunnel_header_0, *gbp);
151 
152 	spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_5;
153 
154 	return 0;
155 }
156 
mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f,void * headers_c,void * headers_v)157 static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
158 				    struct mlx5_flow_spec *spec,
159 				    struct flow_cls_offload *f,
160 				    void *headers_c,
161 				    void *headers_v)
162 {
163 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
164 	struct netlink_ext_ack *extack = f->common.extack;
165 	struct flow_match_enc_keyid enc_keyid;
166 	void *misc_c, *misc_v;
167 
168 	misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
169 	misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
170 
171 	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
172 		return 0;
173 
174 	flow_rule_match_enc_keyid(rule, &enc_keyid);
175 
176 	if (!enc_keyid.mask->keyid)
177 		return 0;
178 
179 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_OPTS)) {
180 		int err;
181 
182 		err = mlx5e_tc_tun_parse_vxlan_gbp_option(priv, spec, f);
183 		if (err)
184 			return err;
185 	}
186 
187 	/* match on VNI is required */
188 
189 	if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
190 					ft_field_support.outer_vxlan_vni)) {
191 		NL_SET_ERR_MSG_MOD(extack,
192 				   "Matching on VXLAN VNI is not supported");
193 		netdev_warn(priv->netdev,
194 			    "Matching on VXLAN VNI is not supported\n");
195 		return -EOPNOTSUPP;
196 	}
197 
198 	MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
199 		 be32_to_cpu(enc_keyid.mask->keyid));
200 	MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
201 		 be32_to_cpu(enc_keyid.key->keyid));
202 
203 	spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS;
204 
205 	return 0;
206 }
207 
mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key * a,struct mlx5e_encap_key * b)208 static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a,
209 						struct mlx5e_encap_key *b)
210 {
211 	return mlx5e_tc_tun_encap_info_equal_options(a, b,
212 						     IP_TUNNEL_VXLAN_OPT_BIT);
213 }
214 
mlx5e_tc_tun_get_remote_ifindex(struct net_device * mirred_dev)215 static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev)
216 {
217 	const struct vxlan_dev *vxlan = netdev_priv(mirred_dev);
218 	const struct vxlan_rdst *dst = &vxlan->default_dst;
219 
220 	return dst->remote_ifindex;
221 }
222 
223 struct mlx5e_tc_tunnel vxlan_tunnel = {
224 	.tunnel_type          = MLX5E_TC_TUNNEL_TYPE_VXLAN,
225 	.match_level          = MLX5_MATCH_L4,
226 	.can_offload          = mlx5e_tc_tun_can_offload_vxlan,
227 	.calc_hlen            = mlx5e_tc_tun_calc_hlen_vxlan,
228 	.init_encap_attr      = mlx5e_tc_tun_init_encap_attr_vxlan,
229 	.generate_ip_tun_hdr  = mlx5e_gen_ip_tunnel_header_vxlan,
230 	.parse_udp_ports      = mlx5e_tc_tun_parse_udp_ports_vxlan,
231 	.parse_tunnel         = mlx5e_tc_tun_parse_vxlan,
232 	.encap_info_equal     = mlx5e_tc_tun_encap_info_equal_vxlan,
233 	.get_remote_ifindex   = mlx5e_tc_tun_get_remote_ifindex,
234 };
235