1101f4de9SOz Shlomo /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
2101f4de9SOz Shlomo /* Copyright (c) 2018 Mellanox Technologies. */
3101f4de9SOz Shlomo 
4101f4de9SOz Shlomo #include <net/vxlan.h>
5df2ef3bfSOz Shlomo #include <net/gre.h>
6101f4de9SOz Shlomo #include "lib/vxlan.h"
7101f4de9SOz Shlomo #include "en/tc_tun.h"
8101f4de9SOz Shlomo 
9101f4de9SOz Shlomo static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv,
10101f4de9SOz Shlomo 				   struct net_device *mirred_dev,
11101f4de9SOz Shlomo 				   struct net_device **out_dev,
12101f4de9SOz Shlomo 				   struct flowi4 *fl4,
13101f4de9SOz Shlomo 				   struct neighbour **out_n,
14101f4de9SOz Shlomo 				   u8 *out_ttl)
15101f4de9SOz Shlomo {
16101f4de9SOz Shlomo 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
17491c37e4SRabie Loulou 	struct net_device *uplink_dev, *uplink_upper;
18491c37e4SRabie Loulou 	bool dst_is_lag_dev;
19101f4de9SOz Shlomo 	struct rtable *rt;
20101f4de9SOz Shlomo 	struct neighbour *n = NULL;
21101f4de9SOz Shlomo 
22101f4de9SOz Shlomo #if IS_ENABLED(CONFIG_INET)
23101f4de9SOz Shlomo 	int ret;
24101f4de9SOz Shlomo 
25101f4de9SOz Shlomo 	rt = ip_route_output_key(dev_net(mirred_dev), fl4);
26101f4de9SOz Shlomo 	ret = PTR_ERR_OR_ZERO(rt);
27101f4de9SOz Shlomo 	if (ret)
28101f4de9SOz Shlomo 		return ret;
29101f4de9SOz Shlomo #else
30101f4de9SOz Shlomo 	return -EOPNOTSUPP;
31101f4de9SOz Shlomo #endif
32491c37e4SRabie Loulou 
33491c37e4SRabie Loulou 	uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
34491c37e4SRabie Loulou 	uplink_upper = netdev_master_upper_dev_get(uplink_dev);
35491c37e4SRabie Loulou 	dst_is_lag_dev = (uplink_upper &&
36491c37e4SRabie Loulou 			  netif_is_lag_master(uplink_upper) &&
37491c37e4SRabie Loulou 			  rt->dst.dev == uplink_upper &&
38491c37e4SRabie Loulou 			  mlx5_lag_is_active(priv->mdev));
39491c37e4SRabie Loulou 
40491c37e4SRabie Loulou 	/* if the egress device isn't on the same HW e-switch or
41491c37e4SRabie Loulou 	 * it's a LAG device, use the uplink
42491c37e4SRabie Loulou 	 */
43491c37e4SRabie Loulou 	if (!switchdev_port_same_parent_id(priv->netdev, rt->dst.dev) ||
44491c37e4SRabie Loulou 	    dst_is_lag_dev)
45491c37e4SRabie Loulou 		*out_dev = uplink_dev;
46101f4de9SOz Shlomo 	else
47101f4de9SOz Shlomo 		*out_dev = rt->dst.dev;
48101f4de9SOz Shlomo 
49101f4de9SOz Shlomo 	if (!(*out_ttl))
50101f4de9SOz Shlomo 		*out_ttl = ip4_dst_hoplimit(&rt->dst);
51101f4de9SOz Shlomo 	n = dst_neigh_lookup(&rt->dst, &fl4->daddr);
52101f4de9SOz Shlomo 	ip_rt_put(rt);
53101f4de9SOz Shlomo 	if (!n)
54101f4de9SOz Shlomo 		return -ENOMEM;
55101f4de9SOz Shlomo 
56101f4de9SOz Shlomo 	*out_n = n;
57101f4de9SOz Shlomo 	return 0;
58101f4de9SOz Shlomo }
59101f4de9SOz Shlomo 
60101f4de9SOz Shlomo static const char *mlx5e_netdev_kind(struct net_device *dev)
61101f4de9SOz Shlomo {
62101f4de9SOz Shlomo 	if (dev->rtnl_link_ops)
63101f4de9SOz Shlomo 		return dev->rtnl_link_ops->kind;
64101f4de9SOz Shlomo 	else
65101f4de9SOz Shlomo 		return "";
66101f4de9SOz Shlomo }
67101f4de9SOz Shlomo 
68101f4de9SOz Shlomo static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
69101f4de9SOz Shlomo 				   struct net_device *mirred_dev,
70101f4de9SOz Shlomo 				   struct net_device **out_dev,
71101f4de9SOz Shlomo 				   struct flowi6 *fl6,
72101f4de9SOz Shlomo 				   struct neighbour **out_n,
73101f4de9SOz Shlomo 				   u8 *out_ttl)
74101f4de9SOz Shlomo {
75101f4de9SOz Shlomo 	struct neighbour *n = NULL;
76101f4de9SOz Shlomo 	struct dst_entry *dst;
77101f4de9SOz Shlomo 
78101f4de9SOz Shlomo #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
79101f4de9SOz Shlomo 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
80491c37e4SRabie Loulou 	struct net_device *uplink_dev, *uplink_upper;
81491c37e4SRabie Loulou 	bool dst_is_lag_dev;
82101f4de9SOz Shlomo 	int ret;
83101f4de9SOz Shlomo 
84101f4de9SOz Shlomo 	ret = ipv6_stub->ipv6_dst_lookup(dev_net(mirred_dev), NULL, &dst,
85101f4de9SOz Shlomo 					 fl6);
86101f4de9SOz Shlomo 	if (ret < 0)
87101f4de9SOz Shlomo 		return ret;
88101f4de9SOz Shlomo 
89101f4de9SOz Shlomo 	if (!(*out_ttl))
90101f4de9SOz Shlomo 		*out_ttl = ip6_dst_hoplimit(dst);
91101f4de9SOz Shlomo 
92491c37e4SRabie Loulou 	uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
93491c37e4SRabie Loulou 	uplink_upper = netdev_master_upper_dev_get(uplink_dev);
94491c37e4SRabie Loulou 	dst_is_lag_dev = (uplink_upper &&
95491c37e4SRabie Loulou 			  netif_is_lag_master(uplink_upper) &&
96491c37e4SRabie Loulou 			  dst->dev == uplink_upper &&
97491c37e4SRabie Loulou 			  mlx5_lag_is_active(priv->mdev));
98491c37e4SRabie Loulou 
99491c37e4SRabie Loulou 	/* if the egress device isn't on the same HW e-switch or
100491c37e4SRabie Loulou 	 * it's a LAG device, use the uplink
101491c37e4SRabie Loulou 	 */
102491c37e4SRabie Loulou 	if (!switchdev_port_same_parent_id(priv->netdev, dst->dev) ||
103491c37e4SRabie Loulou 	    dst_is_lag_dev)
104491c37e4SRabie Loulou 		*out_dev = uplink_dev;
105101f4de9SOz Shlomo 	else
106101f4de9SOz Shlomo 		*out_dev = dst->dev;
107101f4de9SOz Shlomo #else
108101f4de9SOz Shlomo 	return -EOPNOTSUPP;
109101f4de9SOz Shlomo #endif
110101f4de9SOz Shlomo 
111101f4de9SOz Shlomo 	n = dst_neigh_lookup(dst, &fl6->daddr);
112101f4de9SOz Shlomo 	dst_release(dst);
113101f4de9SOz Shlomo 	if (!n)
114101f4de9SOz Shlomo 		return -ENOMEM;
115101f4de9SOz Shlomo 
116101f4de9SOz Shlomo 	*out_n = n;
117101f4de9SOz Shlomo 	return 0;
118101f4de9SOz Shlomo }
119101f4de9SOz Shlomo 
120101f4de9SOz Shlomo static int mlx5e_gen_vxlan_header(char buf[], struct ip_tunnel_key *tun_key)
121101f4de9SOz Shlomo {
122101f4de9SOz Shlomo 	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
123101f4de9SOz Shlomo 	struct udphdr *udp = (struct udphdr *)(buf);
124101f4de9SOz Shlomo 	struct vxlanhdr *vxh = (struct vxlanhdr *)
125101f4de9SOz Shlomo 			       ((char *)udp + sizeof(struct udphdr));
126101f4de9SOz Shlomo 
127101f4de9SOz Shlomo 	udp->dest = tun_key->tp_dst;
128101f4de9SOz Shlomo 	vxh->vx_flags = VXLAN_HF_VNI;
129101f4de9SOz Shlomo 	vxh->vx_vni = vxlan_vni_field(tun_id);
130101f4de9SOz Shlomo 
131101f4de9SOz Shlomo 	return 0;
132101f4de9SOz Shlomo }
133101f4de9SOz Shlomo 
134df2ef3bfSOz Shlomo static int mlx5e_gen_gre_header(char buf[], struct ip_tunnel_key *tun_key)
135df2ef3bfSOz Shlomo {
136df2ef3bfSOz Shlomo 	__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
137df2ef3bfSOz Shlomo 	int hdr_len;
138df2ef3bfSOz Shlomo 	struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);
139df2ef3bfSOz Shlomo 
140df2ef3bfSOz Shlomo 	/* the HW does not calculate GRE csum or sequences */
141df2ef3bfSOz Shlomo 	if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
142df2ef3bfSOz Shlomo 		return -EOPNOTSUPP;
143df2ef3bfSOz Shlomo 
144df2ef3bfSOz Shlomo 	greh->protocol = htons(ETH_P_TEB);
145df2ef3bfSOz Shlomo 
146df2ef3bfSOz Shlomo 	/* GRE key */
147df2ef3bfSOz Shlomo 	hdr_len = gre_calc_hlen(tun_key->tun_flags);
148df2ef3bfSOz Shlomo 	greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
149df2ef3bfSOz Shlomo 	if (tun_key->tun_flags & TUNNEL_KEY) {
150df2ef3bfSOz Shlomo 		__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
151df2ef3bfSOz Shlomo 
152df2ef3bfSOz Shlomo 		*ptr = tun_id;
153df2ef3bfSOz Shlomo 	}
154df2ef3bfSOz Shlomo 
155df2ef3bfSOz Shlomo 	return 0;
156df2ef3bfSOz Shlomo }
157df2ef3bfSOz Shlomo 
158101f4de9SOz Shlomo static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto,
159101f4de9SOz Shlomo 				      struct mlx5e_encap_entry *e)
160101f4de9SOz Shlomo {
161101f4de9SOz Shlomo 	int err = 0;
162101f4de9SOz Shlomo 	struct ip_tunnel_key *key = &e->tun_info.key;
163101f4de9SOz Shlomo 
164101f4de9SOz Shlomo 	if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
165101f4de9SOz Shlomo 		*ip_proto = IPPROTO_UDP;
166101f4de9SOz Shlomo 		err = mlx5e_gen_vxlan_header(buf, key);
167df2ef3bfSOz Shlomo 	} else if  (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
168df2ef3bfSOz Shlomo 		*ip_proto = IPPROTO_GRE;
169df2ef3bfSOz Shlomo 		err = mlx5e_gen_gre_header(buf, key);
170101f4de9SOz Shlomo 	} else {
171101f4de9SOz Shlomo 		pr_warn("mlx5: Cannot generate tunnel header for tunnel type (%d)\n"
172101f4de9SOz Shlomo 			, e->tunnel_type);
173101f4de9SOz Shlomo 		err = -EOPNOTSUPP;
174101f4de9SOz Shlomo 	}
175101f4de9SOz Shlomo 
176101f4de9SOz Shlomo 	return err;
177101f4de9SOz Shlomo }
178101f4de9SOz Shlomo 
179101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
180101f4de9SOz Shlomo 				    struct net_device *mirred_dev,
181101f4de9SOz Shlomo 				    struct mlx5e_encap_entry *e)
182101f4de9SOz Shlomo {
183101f4de9SOz Shlomo 	int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
184101f4de9SOz Shlomo 	int ipv4_encap_size = ETH_HLEN +
185101f4de9SOz Shlomo 			      sizeof(struct iphdr) +
186101f4de9SOz Shlomo 			      e->tunnel_hlen;
187101f4de9SOz Shlomo 	struct ip_tunnel_key *tun_key = &e->tun_info.key;
188101f4de9SOz Shlomo 	struct net_device *out_dev;
189101f4de9SOz Shlomo 	struct neighbour *n = NULL;
190101f4de9SOz Shlomo 	struct flowi4 fl4 = {};
191101f4de9SOz Shlomo 	char *encap_header;
192101f4de9SOz Shlomo 	struct ethhdr *eth;
193101f4de9SOz Shlomo 	u8 nud_state, ttl;
194101f4de9SOz Shlomo 	struct iphdr *ip;
195101f4de9SOz Shlomo 	int err;
196101f4de9SOz Shlomo 
197101f4de9SOz Shlomo 	if (max_encap_size < ipv4_encap_size) {
198101f4de9SOz Shlomo 		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
199101f4de9SOz Shlomo 			       ipv4_encap_size, max_encap_size);
200101f4de9SOz Shlomo 		return -EOPNOTSUPP;
201101f4de9SOz Shlomo 	}
202101f4de9SOz Shlomo 
203101f4de9SOz Shlomo 	encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
204101f4de9SOz Shlomo 	if (!encap_header)
205101f4de9SOz Shlomo 		return -ENOMEM;
206101f4de9SOz Shlomo 
207101f4de9SOz Shlomo 	/* add the IP fields */
208101f4de9SOz Shlomo 	fl4.flowi4_tos = tun_key->tos;
209101f4de9SOz Shlomo 	fl4.daddr = tun_key->u.ipv4.dst;
210101f4de9SOz Shlomo 	fl4.saddr = tun_key->u.ipv4.src;
211101f4de9SOz Shlomo 	ttl = tun_key->ttl;
212101f4de9SOz Shlomo 
213101f4de9SOz Shlomo 	err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev,
214101f4de9SOz Shlomo 				      &fl4, &n, &ttl);
215101f4de9SOz Shlomo 	if (err)
216101f4de9SOz Shlomo 		goto free_encap;
217101f4de9SOz Shlomo 
218101f4de9SOz Shlomo 	/* used by mlx5e_detach_encap to lookup a neigh hash table
219101f4de9SOz Shlomo 	 * entry in the neigh hash table when a user deletes a rule
220101f4de9SOz Shlomo 	 */
221101f4de9SOz Shlomo 	e->m_neigh.dev = n->dev;
222101f4de9SOz Shlomo 	e->m_neigh.family = n->ops->family;
223101f4de9SOz Shlomo 	memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
224101f4de9SOz Shlomo 	e->out_dev = out_dev;
225101f4de9SOz Shlomo 
226101f4de9SOz Shlomo 	/* It's important to add the neigh to the hash table before checking
227101f4de9SOz Shlomo 	 * the neigh validity state. So if we'll get a notification, in case the
228101f4de9SOz Shlomo 	 * neigh changes it's validity state, we would find the relevant neigh
229101f4de9SOz Shlomo 	 * in the hash.
230101f4de9SOz Shlomo 	 */
231101f4de9SOz Shlomo 	err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
232101f4de9SOz Shlomo 	if (err)
233101f4de9SOz Shlomo 		goto free_encap;
234101f4de9SOz Shlomo 
235101f4de9SOz Shlomo 	read_lock_bh(&n->lock);
236101f4de9SOz Shlomo 	nud_state = n->nud_state;
237101f4de9SOz Shlomo 	ether_addr_copy(e->h_dest, n->ha);
238101f4de9SOz Shlomo 	read_unlock_bh(&n->lock);
239101f4de9SOz Shlomo 
240101f4de9SOz Shlomo 	/* add ethernet header */
241101f4de9SOz Shlomo 	eth = (struct ethhdr *)encap_header;
242101f4de9SOz Shlomo 	ether_addr_copy(eth->h_dest, e->h_dest);
243101f4de9SOz Shlomo 	ether_addr_copy(eth->h_source, out_dev->dev_addr);
244101f4de9SOz Shlomo 	eth->h_proto = htons(ETH_P_IP);
245101f4de9SOz Shlomo 
246101f4de9SOz Shlomo 	/* add ip header */
247101f4de9SOz Shlomo 	ip = (struct iphdr *)((char *)eth + sizeof(struct ethhdr));
248101f4de9SOz Shlomo 	ip->tos = tun_key->tos;
249101f4de9SOz Shlomo 	ip->version = 0x4;
250101f4de9SOz Shlomo 	ip->ihl = 0x5;
251101f4de9SOz Shlomo 	ip->ttl = ttl;
252101f4de9SOz Shlomo 	ip->daddr = fl4.daddr;
253101f4de9SOz Shlomo 	ip->saddr = fl4.saddr;
254101f4de9SOz Shlomo 
255101f4de9SOz Shlomo 	/* add tunneling protocol header */
256101f4de9SOz Shlomo 	err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr),
257101f4de9SOz Shlomo 					 &ip->protocol, e);
258101f4de9SOz Shlomo 	if (err)
259101f4de9SOz Shlomo 		goto destroy_neigh_entry;
260101f4de9SOz Shlomo 
261101f4de9SOz Shlomo 	e->encap_size = ipv4_encap_size;
262101f4de9SOz Shlomo 	e->encap_header = encap_header;
263101f4de9SOz Shlomo 
264101f4de9SOz Shlomo 	if (!(nud_state & NUD_VALID)) {
265101f4de9SOz Shlomo 		neigh_event_send(n, NULL);
266101f4de9SOz Shlomo 		err = -EAGAIN;
267101f4de9SOz Shlomo 		goto out;
268101f4de9SOz Shlomo 	}
269101f4de9SOz Shlomo 
270101f4de9SOz Shlomo 	err = mlx5_packet_reformat_alloc(priv->mdev,
271101f4de9SOz Shlomo 					 e->reformat_type,
272101f4de9SOz Shlomo 					 ipv4_encap_size, encap_header,
273101f4de9SOz Shlomo 					 MLX5_FLOW_NAMESPACE_FDB,
274101f4de9SOz Shlomo 					 &e->encap_id);
275101f4de9SOz Shlomo 	if (err)
276101f4de9SOz Shlomo 		goto destroy_neigh_entry;
277101f4de9SOz Shlomo 
278101f4de9SOz Shlomo 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
279101f4de9SOz Shlomo 	mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
280101f4de9SOz Shlomo 	neigh_release(n);
281101f4de9SOz Shlomo 	return err;
282101f4de9SOz Shlomo 
283101f4de9SOz Shlomo destroy_neigh_entry:
284101f4de9SOz Shlomo 	mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
285101f4de9SOz Shlomo free_encap:
286101f4de9SOz Shlomo 	kfree(encap_header);
287101f4de9SOz Shlomo out:
288101f4de9SOz Shlomo 	if (n)
289101f4de9SOz Shlomo 		neigh_release(n);
290101f4de9SOz Shlomo 	return err;
291101f4de9SOz Shlomo }
292101f4de9SOz Shlomo 
293101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
294101f4de9SOz Shlomo 				    struct net_device *mirred_dev,
295101f4de9SOz Shlomo 				    struct mlx5e_encap_entry *e)
296101f4de9SOz Shlomo {
297101f4de9SOz Shlomo 	int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
298101f4de9SOz Shlomo 	int ipv6_encap_size = ETH_HLEN +
299101f4de9SOz Shlomo 			      sizeof(struct ipv6hdr) +
300101f4de9SOz Shlomo 			      e->tunnel_hlen;
301101f4de9SOz Shlomo 	struct ip_tunnel_key *tun_key = &e->tun_info.key;
302101f4de9SOz Shlomo 	struct net_device *out_dev;
303101f4de9SOz Shlomo 	struct neighbour *n = NULL;
304101f4de9SOz Shlomo 	struct flowi6 fl6 = {};
305101f4de9SOz Shlomo 	struct ipv6hdr *ip6h;
306101f4de9SOz Shlomo 	char *encap_header;
307101f4de9SOz Shlomo 	struct ethhdr *eth;
308101f4de9SOz Shlomo 	u8 nud_state, ttl;
309101f4de9SOz Shlomo 	int err;
310101f4de9SOz Shlomo 
311101f4de9SOz Shlomo 	if (max_encap_size < ipv6_encap_size) {
312101f4de9SOz Shlomo 		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
313101f4de9SOz Shlomo 			       ipv6_encap_size, max_encap_size);
314101f4de9SOz Shlomo 		return -EOPNOTSUPP;
315101f4de9SOz Shlomo 	}
316101f4de9SOz Shlomo 
317101f4de9SOz Shlomo 	encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
318101f4de9SOz Shlomo 	if (!encap_header)
319101f4de9SOz Shlomo 		return -ENOMEM;
320101f4de9SOz Shlomo 
321101f4de9SOz Shlomo 	ttl = tun_key->ttl;
322101f4de9SOz Shlomo 
323101f4de9SOz Shlomo 	fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
324101f4de9SOz Shlomo 	fl6.daddr = tun_key->u.ipv6.dst;
325101f4de9SOz Shlomo 	fl6.saddr = tun_key->u.ipv6.src;
326101f4de9SOz Shlomo 
327101f4de9SOz Shlomo 	err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev,
328101f4de9SOz Shlomo 				      &fl6, &n, &ttl);
329101f4de9SOz Shlomo 	if (err)
330101f4de9SOz Shlomo 		goto free_encap;
331101f4de9SOz Shlomo 
332101f4de9SOz Shlomo 	/* used by mlx5e_detach_encap to lookup a neigh hash table
333101f4de9SOz Shlomo 	 * entry in the neigh hash table when a user deletes a rule
334101f4de9SOz Shlomo 	 */
335101f4de9SOz Shlomo 	e->m_neigh.dev = n->dev;
336101f4de9SOz Shlomo 	e->m_neigh.family = n->ops->family;
337101f4de9SOz Shlomo 	memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
338101f4de9SOz Shlomo 	e->out_dev = out_dev;
339101f4de9SOz Shlomo 
340101f4de9SOz Shlomo 	/* It's importent to add the neigh to the hash table before checking
341101f4de9SOz Shlomo 	 * the neigh validity state. So if we'll get a notification, in case the
342101f4de9SOz Shlomo 	 * neigh changes it's validity state, we would find the relevant neigh
343101f4de9SOz Shlomo 	 * in the hash.
344101f4de9SOz Shlomo 	 */
345101f4de9SOz Shlomo 	err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
346101f4de9SOz Shlomo 	if (err)
347101f4de9SOz Shlomo 		goto free_encap;
348101f4de9SOz Shlomo 
349101f4de9SOz Shlomo 	read_lock_bh(&n->lock);
350101f4de9SOz Shlomo 	nud_state = n->nud_state;
351101f4de9SOz Shlomo 	ether_addr_copy(e->h_dest, n->ha);
352101f4de9SOz Shlomo 	read_unlock_bh(&n->lock);
353101f4de9SOz Shlomo 
354101f4de9SOz Shlomo 	/* add ethernet header */
355101f4de9SOz Shlomo 	eth = (struct ethhdr *)encap_header;
356101f4de9SOz Shlomo 	ether_addr_copy(eth->h_dest, e->h_dest);
357101f4de9SOz Shlomo 	ether_addr_copy(eth->h_source, out_dev->dev_addr);
358101f4de9SOz Shlomo 	eth->h_proto = htons(ETH_P_IPV6);
359101f4de9SOz Shlomo 
360101f4de9SOz Shlomo 	/* add ip header */
361101f4de9SOz Shlomo 	ip6h = (struct ipv6hdr *)((char *)eth + sizeof(struct ethhdr));
362101f4de9SOz Shlomo 	ip6_flow_hdr(ip6h, tun_key->tos, 0);
363101f4de9SOz Shlomo 	/* the HW fills up ipv6 payload len */
364101f4de9SOz Shlomo 	ip6h->hop_limit   = ttl;
365101f4de9SOz Shlomo 	ip6h->daddr	  = fl6.daddr;
366101f4de9SOz Shlomo 	ip6h->saddr	  = fl6.saddr;
367101f4de9SOz Shlomo 
368101f4de9SOz Shlomo 	/* add tunneling protocol header */
369101f4de9SOz Shlomo 	err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr),
370101f4de9SOz Shlomo 					 &ip6h->nexthdr, e);
371101f4de9SOz Shlomo 	if (err)
372101f4de9SOz Shlomo 		goto destroy_neigh_entry;
373101f4de9SOz Shlomo 
374101f4de9SOz Shlomo 	e->encap_size = ipv6_encap_size;
375101f4de9SOz Shlomo 	e->encap_header = encap_header;
376101f4de9SOz Shlomo 
377101f4de9SOz Shlomo 	if (!(nud_state & NUD_VALID)) {
378101f4de9SOz Shlomo 		neigh_event_send(n, NULL);
379101f4de9SOz Shlomo 		err = -EAGAIN;
380101f4de9SOz Shlomo 		goto out;
381101f4de9SOz Shlomo 	}
382101f4de9SOz Shlomo 
383101f4de9SOz Shlomo 	err = mlx5_packet_reformat_alloc(priv->mdev,
384101f4de9SOz Shlomo 					 e->reformat_type,
385101f4de9SOz Shlomo 					 ipv6_encap_size, encap_header,
386101f4de9SOz Shlomo 					 MLX5_FLOW_NAMESPACE_FDB,
387101f4de9SOz Shlomo 					 &e->encap_id);
388101f4de9SOz Shlomo 	if (err)
389101f4de9SOz Shlomo 		goto destroy_neigh_entry;
390101f4de9SOz Shlomo 
391101f4de9SOz Shlomo 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
392101f4de9SOz Shlomo 	mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
393101f4de9SOz Shlomo 	neigh_release(n);
394101f4de9SOz Shlomo 	return err;
395101f4de9SOz Shlomo 
396101f4de9SOz Shlomo destroy_neigh_entry:
397101f4de9SOz Shlomo 	mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
398101f4de9SOz Shlomo free_encap:
399101f4de9SOz Shlomo 	kfree(encap_header);
400101f4de9SOz Shlomo out:
401101f4de9SOz Shlomo 	if (n)
402101f4de9SOz Shlomo 		neigh_release(n);
403101f4de9SOz Shlomo 	return err;
404101f4de9SOz Shlomo }
405101f4de9SOz Shlomo 
406101f4de9SOz Shlomo int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev)
407101f4de9SOz Shlomo {
408101f4de9SOz Shlomo 	if (netif_is_vxlan(tunnel_dev))
409101f4de9SOz Shlomo 		return MLX5E_TC_TUNNEL_TYPE_VXLAN;
410df2ef3bfSOz Shlomo 	else if (netif_is_gretap(tunnel_dev) ||
411df2ef3bfSOz Shlomo 		 netif_is_ip6gretap(tunnel_dev))
412df2ef3bfSOz Shlomo 		return MLX5E_TC_TUNNEL_TYPE_GRETAP;
413101f4de9SOz Shlomo 	else
414101f4de9SOz Shlomo 		return MLX5E_TC_TUNNEL_TYPE_UNKNOWN;
415101f4de9SOz Shlomo }
416101f4de9SOz Shlomo 
417101f4de9SOz Shlomo bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
418101f4de9SOz Shlomo 				    struct net_device *netdev)
419101f4de9SOz Shlomo {
420101f4de9SOz Shlomo 	int tunnel_type = mlx5e_tc_tun_get_type(netdev);
421101f4de9SOz Shlomo 
422101f4de9SOz Shlomo 	if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN &&
423101f4de9SOz Shlomo 	    MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
424101f4de9SOz Shlomo 		return true;
425df2ef3bfSOz Shlomo 	else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP &&
426df2ef3bfSOz Shlomo 		 MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap))
427df2ef3bfSOz Shlomo 		return true;
428101f4de9SOz Shlomo 	else
429101f4de9SOz Shlomo 		return false;
430101f4de9SOz Shlomo }
431101f4de9SOz Shlomo 
432101f4de9SOz Shlomo int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
433101f4de9SOz Shlomo 				 struct mlx5e_priv *priv,
434101f4de9SOz Shlomo 				 struct mlx5e_encap_entry *e,
435101f4de9SOz Shlomo 				 struct netlink_ext_ack *extack)
436101f4de9SOz Shlomo {
437101f4de9SOz Shlomo 	e->tunnel_type = mlx5e_tc_tun_get_type(tunnel_dev);
438101f4de9SOz Shlomo 
439101f4de9SOz Shlomo 	if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
440101f4de9SOz Shlomo 		int dst_port =  be16_to_cpu(e->tun_info.key.tp_dst);
441101f4de9SOz Shlomo 
442101f4de9SOz Shlomo 		if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
443101f4de9SOz Shlomo 			NL_SET_ERR_MSG_MOD(extack,
444101f4de9SOz Shlomo 					   "vxlan udp dport was not registered with the HW");
445101f4de9SOz Shlomo 			netdev_warn(priv->netdev,
446101f4de9SOz Shlomo 				    "%d isn't an offloaded vxlan udp dport\n",
447101f4de9SOz Shlomo 				    dst_port);
448101f4de9SOz Shlomo 			return -EOPNOTSUPP;
449101f4de9SOz Shlomo 		}
450101f4de9SOz Shlomo 		e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
451101f4de9SOz Shlomo 		e->tunnel_hlen = VXLAN_HLEN;
452df2ef3bfSOz Shlomo 	} else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
453df2ef3bfSOz Shlomo 		e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
454df2ef3bfSOz Shlomo 		e->tunnel_hlen = gre_calc_hlen(e->tun_info.key.tun_flags);
455101f4de9SOz Shlomo 	} else {
456101f4de9SOz Shlomo 		e->reformat_type = -1;
457101f4de9SOz Shlomo 		e->tunnel_hlen = -1;
458101f4de9SOz Shlomo 		return -EOPNOTSUPP;
459101f4de9SOz Shlomo 	}
460101f4de9SOz Shlomo 	return 0;
461101f4de9SOz Shlomo }
462101f4de9SOz Shlomo 
463101f4de9SOz Shlomo static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
464101f4de9SOz Shlomo 				    struct mlx5_flow_spec *spec,
465101f4de9SOz Shlomo 				    struct tc_cls_flower_offload *f,
466101f4de9SOz Shlomo 				    void *headers_c,
467101f4de9SOz Shlomo 				    void *headers_v)
468101f4de9SOz Shlomo {
469101f4de9SOz Shlomo 	struct netlink_ext_ack *extack = f->common.extack;
470101f4de9SOz Shlomo 	struct flow_dissector_key_ports *key =
471101f4de9SOz Shlomo 		skb_flow_dissector_target(f->dissector,
472101f4de9SOz Shlomo 					  FLOW_DISSECTOR_KEY_ENC_PORTS,
473101f4de9SOz Shlomo 					  f->key);
474101f4de9SOz Shlomo 	struct flow_dissector_key_ports *mask =
475101f4de9SOz Shlomo 		skb_flow_dissector_target(f->dissector,
476101f4de9SOz Shlomo 					  FLOW_DISSECTOR_KEY_ENC_PORTS,
477101f4de9SOz Shlomo 					  f->mask);
478101f4de9SOz Shlomo 	void *misc_c = MLX5_ADDR_OF(fte_match_param,
479101f4de9SOz Shlomo 				    spec->match_criteria,
480101f4de9SOz Shlomo 				    misc_parameters);
481101f4de9SOz Shlomo 	void *misc_v = MLX5_ADDR_OF(fte_match_param,
482101f4de9SOz Shlomo 				    spec->match_value,
483101f4de9SOz Shlomo 				    misc_parameters);
484101f4de9SOz Shlomo 
485101f4de9SOz Shlomo 	/* Full udp dst port must be given */
486101f4de9SOz Shlomo 	if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS) ||
487101f4de9SOz Shlomo 	    memchr_inv(&mask->dst, 0xff, sizeof(mask->dst))) {
488101f4de9SOz Shlomo 		NL_SET_ERR_MSG_MOD(extack,
489101f4de9SOz Shlomo 				   "VXLAN decap filter must include enc_dst_port condition");
490101f4de9SOz Shlomo 		netdev_warn(priv->netdev,
491101f4de9SOz Shlomo 			    "VXLAN decap filter must include enc_dst_port condition\n");
492101f4de9SOz Shlomo 		return -EOPNOTSUPP;
493101f4de9SOz Shlomo 	}
494101f4de9SOz Shlomo 
495101f4de9SOz Shlomo 	/* udp dst port must be knonwn as a VXLAN port */
496101f4de9SOz Shlomo 	if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->dst))) {
497101f4de9SOz Shlomo 		NL_SET_ERR_MSG_MOD(extack,
498101f4de9SOz Shlomo 				   "Matched UDP port is not registered as a VXLAN port");
499101f4de9SOz Shlomo 		netdev_warn(priv->netdev,
500101f4de9SOz Shlomo 			    "UDP port %d is not registered as a VXLAN port\n",
501101f4de9SOz Shlomo 			    be16_to_cpu(key->dst));
502101f4de9SOz Shlomo 		return -EOPNOTSUPP;
503101f4de9SOz Shlomo 	}
504101f4de9SOz Shlomo 
505101f4de9SOz Shlomo 	/* dst UDP port is valid here */
506101f4de9SOz Shlomo 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
507101f4de9SOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
508101f4de9SOz Shlomo 
509101f4de9SOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport, ntohs(mask->dst));
510101f4de9SOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport, ntohs(key->dst));
511101f4de9SOz Shlomo 
512101f4de9SOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, ntohs(mask->src));
513101f4de9SOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport, ntohs(key->src));
514101f4de9SOz Shlomo 
515101f4de9SOz Shlomo 	/* match on VNI */
516101f4de9SOz Shlomo 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
517101f4de9SOz Shlomo 		struct flow_dissector_key_keyid *key =
518101f4de9SOz Shlomo 			skb_flow_dissector_target(f->dissector,
519101f4de9SOz Shlomo 						  FLOW_DISSECTOR_KEY_ENC_KEYID,
520101f4de9SOz Shlomo 						  f->key);
521101f4de9SOz Shlomo 		struct flow_dissector_key_keyid *mask =
522101f4de9SOz Shlomo 			skb_flow_dissector_target(f->dissector,
523101f4de9SOz Shlomo 						  FLOW_DISSECTOR_KEY_ENC_KEYID,
524101f4de9SOz Shlomo 						  f->mask);
525101f4de9SOz Shlomo 		MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
526101f4de9SOz Shlomo 			 be32_to_cpu(mask->keyid));
527101f4de9SOz Shlomo 		MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
528101f4de9SOz Shlomo 			 be32_to_cpu(key->keyid));
529101f4de9SOz Shlomo 	}
530101f4de9SOz Shlomo 	return 0;
531101f4de9SOz Shlomo }
532101f4de9SOz Shlomo 
533df2ef3bfSOz Shlomo static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
534df2ef3bfSOz Shlomo 				     struct mlx5_flow_spec *spec,
535df2ef3bfSOz Shlomo 				     struct tc_cls_flower_offload *f,
536df2ef3bfSOz Shlomo 				     void *outer_headers_c,
537df2ef3bfSOz Shlomo 				     void *outer_headers_v)
538df2ef3bfSOz Shlomo {
539df2ef3bfSOz Shlomo 	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
540df2ef3bfSOz Shlomo 				    misc_parameters);
541df2ef3bfSOz Shlomo 	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
542df2ef3bfSOz Shlomo 				    misc_parameters);
543df2ef3bfSOz Shlomo 
544df2ef3bfSOz Shlomo 	if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) {
545df2ef3bfSOz Shlomo 		NL_SET_ERR_MSG_MOD(f->common.extack,
546df2ef3bfSOz Shlomo 				   "GRE HW offloading is not supported");
547df2ef3bfSOz Shlomo 		netdev_warn(priv->netdev, "GRE HW offloading is not supported\n");
548df2ef3bfSOz Shlomo 		return -EOPNOTSUPP;
549df2ef3bfSOz Shlomo 	}
550df2ef3bfSOz Shlomo 
551df2ef3bfSOz Shlomo 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol);
552df2ef3bfSOz Shlomo 	MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
553df2ef3bfSOz Shlomo 		 ip_protocol, IPPROTO_GRE);
554df2ef3bfSOz Shlomo 
555df2ef3bfSOz Shlomo 	/* gre protocol*/
556df2ef3bfSOz Shlomo 	MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
557df2ef3bfSOz Shlomo 	MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);
558df2ef3bfSOz Shlomo 
559df2ef3bfSOz Shlomo 	/* gre key */
560df2ef3bfSOz Shlomo 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
561df2ef3bfSOz Shlomo 		struct flow_dissector_key_keyid *mask = NULL;
562df2ef3bfSOz Shlomo 		struct flow_dissector_key_keyid *key = NULL;
563df2ef3bfSOz Shlomo 
564df2ef3bfSOz Shlomo 		mask = skb_flow_dissector_target(f->dissector,
565df2ef3bfSOz Shlomo 						 FLOW_DISSECTOR_KEY_ENC_KEYID,
566df2ef3bfSOz Shlomo 						 f->mask);
567df2ef3bfSOz Shlomo 		MLX5_SET(fte_match_set_misc, misc_c,
568df2ef3bfSOz Shlomo 			 gre_key.key, be32_to_cpu(mask->keyid));
569df2ef3bfSOz Shlomo 
570df2ef3bfSOz Shlomo 		key = skb_flow_dissector_target(f->dissector,
571df2ef3bfSOz Shlomo 						FLOW_DISSECTOR_KEY_ENC_KEYID,
572df2ef3bfSOz Shlomo 						f->key);
573df2ef3bfSOz Shlomo 		MLX5_SET(fte_match_set_misc, misc_v,
574df2ef3bfSOz Shlomo 			 gre_key.key, be32_to_cpu(key->keyid));
575df2ef3bfSOz Shlomo 	}
576df2ef3bfSOz Shlomo 
577df2ef3bfSOz Shlomo 	return 0;
578df2ef3bfSOz Shlomo }
579df2ef3bfSOz Shlomo 
580101f4de9SOz Shlomo int mlx5e_tc_tun_parse(struct net_device *filter_dev,
581101f4de9SOz Shlomo 		       struct mlx5e_priv *priv,
582101f4de9SOz Shlomo 		       struct mlx5_flow_spec *spec,
583101f4de9SOz Shlomo 		       struct tc_cls_flower_offload *f,
584101f4de9SOz Shlomo 		       void *headers_c,
585101f4de9SOz Shlomo 		       void *headers_v)
586101f4de9SOz Shlomo {
587101f4de9SOz Shlomo 	int tunnel_type;
588101f4de9SOz Shlomo 	int err = 0;
589101f4de9SOz Shlomo 
590101f4de9SOz Shlomo 	tunnel_type = mlx5e_tc_tun_get_type(filter_dev);
591101f4de9SOz Shlomo 	if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
592101f4de9SOz Shlomo 		err = mlx5e_tc_tun_parse_vxlan(priv, spec, f,
593101f4de9SOz Shlomo 					       headers_c, headers_v);
594df2ef3bfSOz Shlomo 	} else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
595df2ef3bfSOz Shlomo 		err = mlx5e_tc_tun_parse_gretap(priv, spec, f,
596df2ef3bfSOz Shlomo 						headers_c, headers_v);
597101f4de9SOz Shlomo 	} else {
598101f4de9SOz Shlomo 		netdev_warn(priv->netdev,
599101f4de9SOz Shlomo 			    "decapsulation offload is not supported for %s net device (%d)\n",
600101f4de9SOz Shlomo 			    mlx5e_netdev_kind(filter_dev), tunnel_type);
601101f4de9SOz Shlomo 		return -EOPNOTSUPP;
602101f4de9SOz Shlomo 	}
603101f4de9SOz Shlomo 	return err;
604101f4de9SOz Shlomo }
605