1ab3f3d5eSRoi Dayan // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2ab3f3d5eSRoi Dayan // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3ab3f3d5eSRoi Dayan 
4ab3f3d5eSRoi Dayan #include <linux/if_macvlan.h>
5ab3f3d5eSRoi Dayan #include <linux/if_vlan.h>
6ab3f3d5eSRoi Dayan #include <net/bareudp.h>
7ab3f3d5eSRoi Dayan #include <net/bonding.h>
8ab3f3d5eSRoi Dayan #include "act.h"
9ab3f3d5eSRoi Dayan #include "vlan.h"
10ab3f3d5eSRoi Dayan #include "en/tc_tun_encap.h"
11ab3f3d5eSRoi Dayan #include "en/tc_priv.h"
12ab3f3d5eSRoi Dayan #include "en_rep.h"
13ab3f3d5eSRoi Dayan 
14ab3f3d5eSRoi Dayan static bool
15ab3f3d5eSRoi Dayan same_vf_reps(struct mlx5e_priv *priv, struct net_device *out_dev)
16ab3f3d5eSRoi Dayan {
17ab3f3d5eSRoi Dayan 	return mlx5e_eswitch_vf_rep(priv->netdev) &&
18ab3f3d5eSRoi Dayan 	       priv->netdev == out_dev;
19ab3f3d5eSRoi Dayan }
20ab3f3d5eSRoi Dayan 
21ab3f3d5eSRoi Dayan static int
22ab3f3d5eSRoi Dayan verify_uplink_forwarding(struct mlx5e_priv *priv,
23ab3f3d5eSRoi Dayan 			 struct mlx5_flow_attr *attr,
24ab3f3d5eSRoi Dayan 			 struct net_device *out_dev,
25ab3f3d5eSRoi Dayan 			 struct netlink_ext_ack *extack)
26ab3f3d5eSRoi Dayan {
27ab3f3d5eSRoi Dayan 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
28ab3f3d5eSRoi Dayan 	struct mlx5e_rep_priv *rep_priv;
29ab3f3d5eSRoi Dayan 
30ab3f3d5eSRoi Dayan 	/* Forwarding non encapsulated traffic between
31ab3f3d5eSRoi Dayan 	 * uplink ports is allowed only if
32ab3f3d5eSRoi Dayan 	 * termination_table_raw_traffic cap is set.
33ab3f3d5eSRoi Dayan 	 *
34ab3f3d5eSRoi Dayan 	 * Input vport was stored attr->in_rep.
35ab3f3d5eSRoi Dayan 	 * In LAG case, *priv* is the private data of
36ab3f3d5eSRoi Dayan 	 * uplink which may be not the input vport.
37ab3f3d5eSRoi Dayan 	 */
38ab3f3d5eSRoi Dayan 	rep_priv = mlx5e_rep_to_rep_priv(attr->esw_attr->in_rep);
39ab3f3d5eSRoi Dayan 
40ab3f3d5eSRoi Dayan 	if (!(mlx5e_eswitch_uplink_rep(rep_priv->netdev) &&
41ab3f3d5eSRoi Dayan 	      mlx5e_eswitch_uplink_rep(out_dev)))
42ab3f3d5eSRoi Dayan 		return 0;
43ab3f3d5eSRoi Dayan 
44ab3f3d5eSRoi Dayan 	if (!MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev,
45ab3f3d5eSRoi Dayan 					termination_table_raw_traffic)) {
46ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
47ab3f3d5eSRoi Dayan 				   "devices are both uplink, can't offload forwarding");
48ab3f3d5eSRoi Dayan 			return -EOPNOTSUPP;
49ab3f3d5eSRoi Dayan 	} else if (out_dev != rep_priv->netdev) {
50ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
51ab3f3d5eSRoi Dayan 				   "devices are not the same uplink, can't offload forwarding");
52ab3f3d5eSRoi Dayan 		return -EOPNOTSUPP;
53ab3f3d5eSRoi Dayan 	}
54ab3f3d5eSRoi Dayan 	return 0;
55ab3f3d5eSRoi Dayan }
56ab3f3d5eSRoi Dayan 
57ab3f3d5eSRoi Dayan static bool
58ab3f3d5eSRoi Dayan is_duplicated_output_device(struct net_device *dev,
59ab3f3d5eSRoi Dayan 			    struct net_device *out_dev,
60ab3f3d5eSRoi Dayan 			    int *ifindexes, int if_count,
61ab3f3d5eSRoi Dayan 			    struct netlink_ext_ack *extack)
62ab3f3d5eSRoi Dayan {
63ab3f3d5eSRoi Dayan 	int i;
64ab3f3d5eSRoi Dayan 
65ab3f3d5eSRoi Dayan 	for (i = 0; i < if_count; i++) {
66ab3f3d5eSRoi Dayan 		if (ifindexes[i] == out_dev->ifindex) {
67ab3f3d5eSRoi Dayan 			NL_SET_ERR_MSG_MOD(extack, "can't duplicate output to same device");
68ab3f3d5eSRoi Dayan 			netdev_err(dev, "can't duplicate output to same device: %s\n",
69ab3f3d5eSRoi Dayan 				   out_dev->name);
70ab3f3d5eSRoi Dayan 			return true;
71ab3f3d5eSRoi Dayan 		}
72ab3f3d5eSRoi Dayan 	}
73ab3f3d5eSRoi Dayan 
74ab3f3d5eSRoi Dayan 	return false;
75ab3f3d5eSRoi Dayan }
76ab3f3d5eSRoi Dayan 
77ab3f3d5eSRoi Dayan static struct net_device *
78ab3f3d5eSRoi Dayan get_fdb_out_dev(struct net_device *uplink_dev, struct net_device *out_dev)
79ab3f3d5eSRoi Dayan {
80ab3f3d5eSRoi Dayan 	struct net_device *fdb_out_dev = out_dev;
81ab3f3d5eSRoi Dayan 	struct net_device *uplink_upper;
82ab3f3d5eSRoi Dayan 
83ab3f3d5eSRoi Dayan 	rcu_read_lock();
84ab3f3d5eSRoi Dayan 	uplink_upper = netdev_master_upper_dev_get_rcu(uplink_dev);
85ab3f3d5eSRoi Dayan 	if (uplink_upper && netif_is_lag_master(uplink_upper) &&
86ab3f3d5eSRoi Dayan 	    uplink_upper == out_dev) {
87ab3f3d5eSRoi Dayan 		fdb_out_dev = uplink_dev;
88ab3f3d5eSRoi Dayan 	} else if (netif_is_lag_master(out_dev)) {
89ab3f3d5eSRoi Dayan 		fdb_out_dev = bond_option_active_slave_get_rcu(netdev_priv(out_dev));
90ab3f3d5eSRoi Dayan 		if (fdb_out_dev &&
91ab3f3d5eSRoi Dayan 		    (!mlx5e_eswitch_rep(fdb_out_dev) ||
92ab3f3d5eSRoi Dayan 		     !netdev_port_same_parent_id(fdb_out_dev, uplink_dev)))
93ab3f3d5eSRoi Dayan 			fdb_out_dev = NULL;
94ab3f3d5eSRoi Dayan 	}
95ab3f3d5eSRoi Dayan 	rcu_read_unlock();
96ab3f3d5eSRoi Dayan 	return fdb_out_dev;
97ab3f3d5eSRoi Dayan }
98ab3f3d5eSRoi Dayan 
99ab3f3d5eSRoi Dayan static bool
100ab3f3d5eSRoi Dayan tc_act_can_offload_mirred(struct mlx5e_tc_act_parse_state *parse_state,
101ab3f3d5eSRoi Dayan 			  const struct flow_action_entry *act,
102*8be9686dSRoi Dayan 			  int act_index,
103*8be9686dSRoi Dayan 			  struct mlx5_flow_attr *attr)
104ab3f3d5eSRoi Dayan {
105ab3f3d5eSRoi Dayan 	struct netlink_ext_ack *extack = parse_state->extack;
106ab3f3d5eSRoi Dayan 	struct mlx5e_tc_flow *flow = parse_state->flow;
107ab3f3d5eSRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
108ab3f3d5eSRoi Dayan 	struct net_device *out_dev = act->dev;
109ab3f3d5eSRoi Dayan 	struct mlx5e_priv *priv = flow->priv;
110ab3f3d5eSRoi Dayan 	struct mlx5_esw_flow_attr *esw_attr;
111ab3f3d5eSRoi Dayan 
112*8be9686dSRoi Dayan 	parse_attr = attr->parse_attr;
113*8be9686dSRoi Dayan 	esw_attr = attr->esw_attr;
114ab3f3d5eSRoi Dayan 
115ab3f3d5eSRoi Dayan 	if (!out_dev) {
116ab3f3d5eSRoi Dayan 		/* out_dev is NULL when filters with
117ab3f3d5eSRoi Dayan 		 * non-existing mirred device are replayed to
118ab3f3d5eSRoi Dayan 		 * the driver.
119ab3f3d5eSRoi Dayan 		 */
120ab3f3d5eSRoi Dayan 		return false;
121ab3f3d5eSRoi Dayan 	}
122ab3f3d5eSRoi Dayan 
123ab3f3d5eSRoi Dayan 	if (parse_state->mpls_push && !netif_is_bareudp(out_dev)) {
124ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack, "mpls is supported only through a bareudp device");
125ab3f3d5eSRoi Dayan 		return false;
126ab3f3d5eSRoi Dayan 	}
127ab3f3d5eSRoi Dayan 
128ab3f3d5eSRoi Dayan 	if (mlx5e_is_ft_flow(flow) && out_dev == priv->netdev) {
129ab3f3d5eSRoi Dayan 		/* Ignore forward to self rules generated
130ab3f3d5eSRoi Dayan 		 * by adding both mlx5 devs to the flow table
131ab3f3d5eSRoi Dayan 		 * block on a normal nft offload setup.
132ab3f3d5eSRoi Dayan 		 */
133ab3f3d5eSRoi Dayan 		return false;
134ab3f3d5eSRoi Dayan 	}
135ab3f3d5eSRoi Dayan 
136ab3f3d5eSRoi Dayan 	if (esw_attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) {
137ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
138ab3f3d5eSRoi Dayan 				   "can't support more output ports, can't offload forwarding");
139ab3f3d5eSRoi Dayan 		netdev_warn(priv->netdev,
140ab3f3d5eSRoi Dayan 			    "can't support more than %d output ports, can't offload forwarding\n",
141ab3f3d5eSRoi Dayan 			    esw_attr->out_count);
142ab3f3d5eSRoi Dayan 		return false;
143ab3f3d5eSRoi Dayan 	}
144ab3f3d5eSRoi Dayan 
145ab3f3d5eSRoi Dayan 	if (parse_state->encap ||
146ab3f3d5eSRoi Dayan 	    netdev_port_same_parent_id(priv->netdev, out_dev) ||
147ab3f3d5eSRoi Dayan 	    netif_is_ovs_master(out_dev))
148ab3f3d5eSRoi Dayan 		return true;
149ab3f3d5eSRoi Dayan 
150ab3f3d5eSRoi Dayan 	if (parse_attr->filter_dev != priv->netdev) {
151ab3f3d5eSRoi Dayan 		/* All mlx5 devices are called to configure
152ab3f3d5eSRoi Dayan 		 * high level device filters. Therefore, the
153ab3f3d5eSRoi Dayan 		 * *attempt* to  install a filter on invalid
154ab3f3d5eSRoi Dayan 		 * eswitch should not trigger an explicit error
155ab3f3d5eSRoi Dayan 		 */
156ab3f3d5eSRoi Dayan 		return false;
157ab3f3d5eSRoi Dayan 	}
158ab3f3d5eSRoi Dayan 
159ab3f3d5eSRoi Dayan 	NL_SET_ERR_MSG_MOD(extack, "devices are not on same switch HW, can't offload forwarding");
160ab3f3d5eSRoi Dayan 
161ab3f3d5eSRoi Dayan 	return false;
162ab3f3d5eSRoi Dayan }
163ab3f3d5eSRoi Dayan 
164ab3f3d5eSRoi Dayan static int
165ab3f3d5eSRoi Dayan parse_mirred_encap(struct mlx5e_tc_act_parse_state *parse_state,
166ab3f3d5eSRoi Dayan 		   const struct flow_action_entry *act,
167ab3f3d5eSRoi Dayan 		   struct mlx5_flow_attr *attr)
168ab3f3d5eSRoi Dayan {
169ab3f3d5eSRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
170ab3f3d5eSRoi Dayan 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
171ab3f3d5eSRoi Dayan 	struct net_device *out_dev = act->dev;
172ab3f3d5eSRoi Dayan 
173ab3f3d5eSRoi Dayan 	parse_attr->mirred_ifindex[esw_attr->out_count] = out_dev->ifindex;
174ab3f3d5eSRoi Dayan 	parse_attr->tun_info[esw_attr->out_count] =
175ab3f3d5eSRoi Dayan 		mlx5e_dup_tun_info(parse_state->tun_info);
176ab3f3d5eSRoi Dayan 
177ab3f3d5eSRoi Dayan 	if (!parse_attr->tun_info[esw_attr->out_count])
178ab3f3d5eSRoi Dayan 		return -ENOMEM;
179ab3f3d5eSRoi Dayan 
180ab3f3d5eSRoi Dayan 	parse_state->encap = false;
181ab3f3d5eSRoi Dayan 	esw_attr->dests[esw_attr->out_count].flags |= MLX5_ESW_DEST_ENCAP;
182ab3f3d5eSRoi Dayan 	esw_attr->out_count++;
183ab3f3d5eSRoi Dayan 	/* attr->dests[].rep is resolved when we handle encap */
184ab3f3d5eSRoi Dayan 
185ab3f3d5eSRoi Dayan 	return 0;
186ab3f3d5eSRoi Dayan }
187ab3f3d5eSRoi Dayan 
188ab3f3d5eSRoi Dayan static int
189ab3f3d5eSRoi Dayan parse_mirred(struct mlx5e_tc_act_parse_state *parse_state,
190ab3f3d5eSRoi Dayan 	     const struct flow_action_entry *act,
191ab3f3d5eSRoi Dayan 	     struct mlx5e_priv *priv,
192ab3f3d5eSRoi Dayan 	     struct mlx5_flow_attr *attr)
193ab3f3d5eSRoi Dayan {
194ab3f3d5eSRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
195ab3f3d5eSRoi Dayan 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
196ab3f3d5eSRoi Dayan 	struct netlink_ext_ack *extack = parse_state->extack;
197ab3f3d5eSRoi Dayan 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
198ab3f3d5eSRoi Dayan 	struct net_device *out_dev = act->dev;
199ab3f3d5eSRoi Dayan 	struct net_device *uplink_dev;
200ab3f3d5eSRoi Dayan 	struct mlx5e_priv *out_priv;
201ab3f3d5eSRoi Dayan 	struct mlx5_eswitch *esw;
202ab3f3d5eSRoi Dayan 	int *ifindexes;
203ab3f3d5eSRoi Dayan 	int if_count;
204ab3f3d5eSRoi Dayan 	int err;
205ab3f3d5eSRoi Dayan 
206ab3f3d5eSRoi Dayan 	esw = priv->mdev->priv.eswitch;
207ab3f3d5eSRoi Dayan 	uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
208ab3f3d5eSRoi Dayan 	ifindexes = parse_state->ifindexes;
209ab3f3d5eSRoi Dayan 	if_count = parse_state->if_count;
210ab3f3d5eSRoi Dayan 
211ab3f3d5eSRoi Dayan 	if (is_duplicated_output_device(priv->netdev, out_dev, ifindexes, if_count, extack))
212ab3f3d5eSRoi Dayan 		return -EOPNOTSUPP;
213ab3f3d5eSRoi Dayan 
214ab3f3d5eSRoi Dayan 	parse_state->ifindexes[if_count] = out_dev->ifindex;
215ab3f3d5eSRoi Dayan 	parse_state->if_count++;
216ab3f3d5eSRoi Dayan 
217ab3f3d5eSRoi Dayan 	out_dev = get_fdb_out_dev(uplink_dev, out_dev);
218ab3f3d5eSRoi Dayan 	if (!out_dev)
219ab3f3d5eSRoi Dayan 		return -ENODEV;
220ab3f3d5eSRoi Dayan 
221ab3f3d5eSRoi Dayan 	if (is_vlan_dev(out_dev)) {
222ab3f3d5eSRoi Dayan 		err = mlx5e_tc_act_vlan_add_push_action(priv, attr, &out_dev, extack);
223ab3f3d5eSRoi Dayan 		if (err)
224ab3f3d5eSRoi Dayan 			return err;
225ab3f3d5eSRoi Dayan 	}
226ab3f3d5eSRoi Dayan 
227ab3f3d5eSRoi Dayan 	if (is_vlan_dev(parse_attr->filter_dev)) {
228ab3f3d5eSRoi Dayan 		err = mlx5e_tc_act_vlan_add_pop_action(priv, attr, extack);
229ab3f3d5eSRoi Dayan 		if (err)
230ab3f3d5eSRoi Dayan 			return err;
231ab3f3d5eSRoi Dayan 	}
232ab3f3d5eSRoi Dayan 
233ab3f3d5eSRoi Dayan 	if (netif_is_macvlan(out_dev))
234ab3f3d5eSRoi Dayan 		out_dev = macvlan_dev_real_dev(out_dev);
235ab3f3d5eSRoi Dayan 
236ab3f3d5eSRoi Dayan 	err = verify_uplink_forwarding(priv, attr, out_dev, extack);
237ab3f3d5eSRoi Dayan 	if (err)
238ab3f3d5eSRoi Dayan 		return err;
239ab3f3d5eSRoi Dayan 
240ab3f3d5eSRoi Dayan 	if (!mlx5e_is_valid_eswitch_fwd_dev(priv, out_dev)) {
241ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
242ab3f3d5eSRoi Dayan 				   "devices are not on same switch HW, can't offload forwarding");
243ab3f3d5eSRoi Dayan 		return -EOPNOTSUPP;
244ab3f3d5eSRoi Dayan 	}
245ab3f3d5eSRoi Dayan 
246ab3f3d5eSRoi Dayan 	if (same_vf_reps(priv, out_dev)) {
247ab3f3d5eSRoi Dayan 		NL_SET_ERR_MSG_MOD(extack, "can't forward from a VF to itself");
248ab3f3d5eSRoi Dayan 		return -EOPNOTSUPP;
249ab3f3d5eSRoi Dayan 	}
250ab3f3d5eSRoi Dayan 
251ab3f3d5eSRoi Dayan 	out_priv = netdev_priv(out_dev);
252ab3f3d5eSRoi Dayan 	rpriv = out_priv->ppriv;
253ab3f3d5eSRoi Dayan 	esw_attr->dests[esw_attr->out_count].rep = rpriv->rep;
254ab3f3d5eSRoi Dayan 	esw_attr->dests[esw_attr->out_count].mdev = out_priv->mdev;
255ab3f3d5eSRoi Dayan 	esw_attr->out_count++;
256ab3f3d5eSRoi Dayan 
257ab3f3d5eSRoi Dayan 	return 0;
258ab3f3d5eSRoi Dayan }
259ab3f3d5eSRoi Dayan 
260ab3f3d5eSRoi Dayan static int
261ab3f3d5eSRoi Dayan parse_mirred_ovs_master(struct mlx5e_tc_act_parse_state *parse_state,
262ab3f3d5eSRoi Dayan 			const struct flow_action_entry *act,
263ab3f3d5eSRoi Dayan 			struct mlx5e_priv *priv,
264ab3f3d5eSRoi Dayan 			struct mlx5_flow_attr *attr)
265ab3f3d5eSRoi Dayan {
266ab3f3d5eSRoi Dayan 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
267ab3f3d5eSRoi Dayan 	struct net_device *out_dev = act->dev;
268ab3f3d5eSRoi Dayan 	int err;
269ab3f3d5eSRoi Dayan 
270ab3f3d5eSRoi Dayan 	err = mlx5e_set_fwd_to_int_port_actions(priv, attr, out_dev->ifindex,
271ab3f3d5eSRoi Dayan 						MLX5E_TC_INT_PORT_EGRESS,
272ab3f3d5eSRoi Dayan 						&attr->action, esw_attr->out_count);
273ab3f3d5eSRoi Dayan 	if (err)
274ab3f3d5eSRoi Dayan 		return err;
275ab3f3d5eSRoi Dayan 
276ab3f3d5eSRoi Dayan 	esw_attr->out_count++;
277ab3f3d5eSRoi Dayan 	return 0;
278ab3f3d5eSRoi Dayan }
279ab3f3d5eSRoi Dayan 
280ab3f3d5eSRoi Dayan static int
281ab3f3d5eSRoi Dayan tc_act_parse_mirred(struct mlx5e_tc_act_parse_state *parse_state,
282ab3f3d5eSRoi Dayan 		    const struct flow_action_entry *act,
283ab3f3d5eSRoi Dayan 		    struct mlx5e_priv *priv,
284ab3f3d5eSRoi Dayan 		    struct mlx5_flow_attr *attr)
285ab3f3d5eSRoi Dayan {
286ab3f3d5eSRoi Dayan 	struct net_device *out_dev = act->dev;
287ab3f3d5eSRoi Dayan 	int err = -EOPNOTSUPP;
288ab3f3d5eSRoi Dayan 
289ab3f3d5eSRoi Dayan 	if (parse_state->encap)
290ab3f3d5eSRoi Dayan 		err = parse_mirred_encap(parse_state, act, attr);
291ab3f3d5eSRoi Dayan 	else if (netdev_port_same_parent_id(priv->netdev, out_dev))
292ab3f3d5eSRoi Dayan 		err = parse_mirred(parse_state, act, priv, attr);
293ab3f3d5eSRoi Dayan 	else if (netif_is_ovs_master(out_dev))
294ab3f3d5eSRoi Dayan 		err = parse_mirred_ovs_master(parse_state, act, priv, attr);
295ab3f3d5eSRoi Dayan 
296ab3f3d5eSRoi Dayan 	if (err)
297ab3f3d5eSRoi Dayan 		return err;
298ab3f3d5eSRoi Dayan 
299ab3f3d5eSRoi Dayan 	attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
300ab3f3d5eSRoi Dayan 			MLX5_FLOW_CONTEXT_ACTION_COUNT;
301ab3f3d5eSRoi Dayan 
302ab3f3d5eSRoi Dayan 	return 0;
303ab3f3d5eSRoi Dayan }
304ab3f3d5eSRoi Dayan 
305ab3f3d5eSRoi Dayan struct mlx5e_tc_act mlx5e_tc_act_mirred = {
306ab3f3d5eSRoi Dayan 	.can_offload = tc_act_can_offload_mirred,
307ab3f3d5eSRoi Dayan 	.parse_action = tc_act_parse_mirred,
308ab3f3d5eSRoi Dayan };
309