196de2506SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
296de2506SJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
31a1e586fSPieter Jansen van Vuuren
41a1e586fSPieter Jansen van Vuuren #include <linux/bitfield.h>
5a6eb1817SJohn Hurley #include <linux/mpls.h>
61a1e586fSPieter Jansen van Vuuren #include <net/pkt_cls.h>
7ed8f2b52SPieter Jansen van Vuuren #include <net/tc_act/tc_csum.h>
81a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_gact.h>
91a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_mirred.h>
10a6eb1817SJohn Hurley #include <net/tc_act/tc_mpls.h>
11da83d8feSPieter Jansen van Vuuren #include <net/tc_act/tc_pedit.h>
121a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_vlan.h>
13b27d6a95SJohn Hurley #include <net/tc_act/tc_tunnel_key.h>
141a1e586fSPieter Jansen van Vuuren
151a1e586fSPieter Jansen van Vuuren #include "cmsg.h"
161a1e586fSPieter Jansen van Vuuren #include "main.h"
171a1e586fSPieter Jansen van Vuuren #include "../nfp_net_repr.h"
181a1e586fSPieter Jansen van Vuuren
199e7c32feSPieter Jansen van Vuuren /* The kernel versions of TUNNEL_* are not ABI and therefore vulnerable
209e7c32feSPieter Jansen van Vuuren * to change. Such changes will break our FW ABI.
219e7c32feSPieter Jansen van Vuuren */
229e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_CSUM cpu_to_be16(0x01)
239e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_KEY cpu_to_be16(0x04)
249e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_GENEVE_OPT cpu_to_be16(0x0800)
251922c9a4SJohn Hurley #define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS (IP_TUNNEL_INFO_TX | \
261922c9a4SJohn Hurley IP_TUNNEL_INFO_IPV6)
271922c9a4SJohn Hurley #define NFP_FL_SUPPORTED_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \
289e7c32feSPieter Jansen van Vuuren NFP_FL_TUNNEL_KEY | \
299e7c32feSPieter Jansen van Vuuren NFP_FL_TUNNEL_GENEVE_OPT)
3051a8cefcSJohn Hurley
31a6eb1817SJohn Hurley static int
nfp_fl_push_mpls(struct nfp_fl_push_mpls * push_mpls,const struct flow_action_entry * act,struct netlink_ext_ack * extack)32a6eb1817SJohn Hurley nfp_fl_push_mpls(struct nfp_fl_push_mpls *push_mpls,
33a6eb1817SJohn Hurley const struct flow_action_entry *act,
34a6eb1817SJohn Hurley struct netlink_ext_ack *extack)
35a6eb1817SJohn Hurley {
36a6eb1817SJohn Hurley size_t act_size = sizeof(struct nfp_fl_push_mpls);
37a6eb1817SJohn Hurley u32 mpls_lse = 0;
38a6eb1817SJohn Hurley
39a6eb1817SJohn Hurley push_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_MPLS;
40a6eb1817SJohn Hurley push_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
41a6eb1817SJohn Hurley
42a6eb1817SJohn Hurley /* BOS is optional in the TC action but required for offload. */
43a6eb1817SJohn Hurley if (act->mpls_push.bos != ACT_MPLS_BOS_NOT_SET) {
44a6eb1817SJohn Hurley mpls_lse |= act->mpls_push.bos << MPLS_LS_S_SHIFT;
45a6eb1817SJohn Hurley } else {
46a6eb1817SJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: BOS field must explicitly be set for MPLS push");
47a6eb1817SJohn Hurley return -EOPNOTSUPP;
48a6eb1817SJohn Hurley }
49a6eb1817SJohn Hurley
50a6eb1817SJohn Hurley /* Leave MPLS TC as a default value of 0 if not explicitly set. */
51a6eb1817SJohn Hurley if (act->mpls_push.tc != ACT_MPLS_TC_NOT_SET)
52a6eb1817SJohn Hurley mpls_lse |= act->mpls_push.tc << MPLS_LS_TC_SHIFT;
53a6eb1817SJohn Hurley
54a6eb1817SJohn Hurley /* Proto, label and TTL are enforced and verified for MPLS push. */
55a6eb1817SJohn Hurley mpls_lse |= act->mpls_push.label << MPLS_LS_LABEL_SHIFT;
56a6eb1817SJohn Hurley mpls_lse |= act->mpls_push.ttl << MPLS_LS_TTL_SHIFT;
57a6eb1817SJohn Hurley push_mpls->ethtype = act->mpls_push.proto;
58a6eb1817SJohn Hurley push_mpls->lse = cpu_to_be32(mpls_lse);
59a6eb1817SJohn Hurley
60a6eb1817SJohn Hurley return 0;
61a6eb1817SJohn Hurley }
62a6eb1817SJohn Hurley
6335b7c70cSJohn Hurley static void
nfp_fl_pop_mpls(struct nfp_fl_pop_mpls * pop_mpls,const struct flow_action_entry * act)6435b7c70cSJohn Hurley nfp_fl_pop_mpls(struct nfp_fl_pop_mpls *pop_mpls,
6535b7c70cSJohn Hurley const struct flow_action_entry *act)
6635b7c70cSJohn Hurley {
6735b7c70cSJohn Hurley size_t act_size = sizeof(struct nfp_fl_pop_mpls);
6835b7c70cSJohn Hurley
6935b7c70cSJohn Hurley pop_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_POP_MPLS;
7035b7c70cSJohn Hurley pop_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
7135b7c70cSJohn Hurley pop_mpls->ethtype = act->mpls_pop.proto;
7235b7c70cSJohn Hurley }
7335b7c70cSJohn Hurley
74e03e47a3SJohn Hurley static void
nfp_fl_set_mpls(struct nfp_fl_set_mpls * set_mpls,const struct flow_action_entry * act)75e03e47a3SJohn Hurley nfp_fl_set_mpls(struct nfp_fl_set_mpls *set_mpls,
76e03e47a3SJohn Hurley const struct flow_action_entry *act)
77e03e47a3SJohn Hurley {
78e03e47a3SJohn Hurley size_t act_size = sizeof(struct nfp_fl_set_mpls);
79e03e47a3SJohn Hurley u32 mpls_lse = 0, mpls_mask = 0;
80e03e47a3SJohn Hurley
81e03e47a3SJohn Hurley set_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_SET_MPLS;
82e03e47a3SJohn Hurley set_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
83e03e47a3SJohn Hurley
84e03e47a3SJohn Hurley if (act->mpls_mangle.label != ACT_MPLS_LABEL_NOT_SET) {
85e03e47a3SJohn Hurley mpls_lse |= act->mpls_mangle.label << MPLS_LS_LABEL_SHIFT;
86e03e47a3SJohn Hurley mpls_mask |= MPLS_LS_LABEL_MASK;
87e03e47a3SJohn Hurley }
88e03e47a3SJohn Hurley if (act->mpls_mangle.tc != ACT_MPLS_TC_NOT_SET) {
89e03e47a3SJohn Hurley mpls_lse |= act->mpls_mangle.tc << MPLS_LS_TC_SHIFT;
90e03e47a3SJohn Hurley mpls_mask |= MPLS_LS_TC_MASK;
91e03e47a3SJohn Hurley }
92e03e47a3SJohn Hurley if (act->mpls_mangle.bos != ACT_MPLS_BOS_NOT_SET) {
93e03e47a3SJohn Hurley mpls_lse |= act->mpls_mangle.bos << MPLS_LS_S_SHIFT;
94e03e47a3SJohn Hurley mpls_mask |= MPLS_LS_S_MASK;
95e03e47a3SJohn Hurley }
96e03e47a3SJohn Hurley if (act->mpls_mangle.ttl) {
97e03e47a3SJohn Hurley mpls_lse |= act->mpls_mangle.ttl << MPLS_LS_TTL_SHIFT;
98e03e47a3SJohn Hurley mpls_mask |= MPLS_LS_TTL_MASK;
99e03e47a3SJohn Hurley }
100e03e47a3SJohn Hurley
101e03e47a3SJohn Hurley set_mpls->lse = cpu_to_be32(mpls_lse);
102e03e47a3SJohn Hurley set_mpls->lse_mask = cpu_to_be32(mpls_mask);
103e03e47a3SJohn Hurley }
104e03e47a3SJohn Hurley
nfp_fl_pop_vlan(struct nfp_fl_pop_vlan * pop_vlan)1051a1e586fSPieter Jansen van Vuuren static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan)
1061a1e586fSPieter Jansen van Vuuren {
1071a1e586fSPieter Jansen van Vuuren size_t act_size = sizeof(struct nfp_fl_pop_vlan);
1081a1e586fSPieter Jansen van Vuuren
10962d3f60bSPieter Jansen van Vuuren pop_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_POP_VLAN;
11062d3f60bSPieter Jansen van Vuuren pop_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1111a1e586fSPieter Jansen van Vuuren pop_vlan->reserved = 0;
1121a1e586fSPieter Jansen van Vuuren }
1131a1e586fSPieter Jansen van Vuuren
1141a1e586fSPieter Jansen van Vuuren static void
nfp_fl_push_vlan(struct nfp_fl_push_vlan * push_vlan,const struct flow_action_entry * act)1151a1e586fSPieter Jansen van Vuuren nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
11673867881SPablo Neira Ayuso const struct flow_action_entry *act)
1171a1e586fSPieter Jansen van Vuuren {
1181a1e586fSPieter Jansen van Vuuren size_t act_size = sizeof(struct nfp_fl_push_vlan);
1191a1e586fSPieter Jansen van Vuuren u16 tmp_push_vlan_tci;
1201a1e586fSPieter Jansen van Vuuren
12162d3f60bSPieter Jansen van Vuuren push_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_VLAN;
12262d3f60bSPieter Jansen van Vuuren push_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1231a1e586fSPieter Jansen van Vuuren push_vlan->reserved = 0;
12473867881SPablo Neira Ayuso push_vlan->vlan_tpid = act->vlan.proto;
1251a1e586fSPieter Jansen van Vuuren
1261a1e586fSPieter Jansen van Vuuren tmp_push_vlan_tci =
12773867881SPablo Neira Ayuso FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, act->vlan.prio) |
12842cd5484SPieter Jansen van Vuuren FIELD_PREP(NFP_FL_PUSH_VLAN_VID, act->vlan.vid);
1291a1e586fSPieter Jansen van Vuuren push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci);
1301a1e586fSPieter Jansen van Vuuren }
1311a1e586fSPieter Jansen van Vuuren
1327e24a593SJohn Hurley static int
nfp_fl_pre_lag(struct nfp_app * app,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_flow,int act_len,struct netlink_ext_ack * extack)13373867881SPablo Neira Ayuso nfp_fl_pre_lag(struct nfp_app *app, const struct flow_action_entry *act,
134bef6e97dSPieter Jansen van Vuuren struct nfp_fl_payload *nfp_flow, int act_len,
135bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
1367e24a593SJohn Hurley {
1377e24a593SJohn Hurley size_t act_size = sizeof(struct nfp_fl_pre_lag);
1387e24a593SJohn Hurley struct nfp_fl_pre_lag *pre_lag;
1397e24a593SJohn Hurley struct net_device *out_dev;
1407e24a593SJohn Hurley int err;
1417e24a593SJohn Hurley
14273867881SPablo Neira Ayuso out_dev = act->dev;
1437e24a593SJohn Hurley if (!out_dev || !netif_is_lag_master(out_dev))
1447e24a593SJohn Hurley return 0;
1457e24a593SJohn Hurley
146bef6e97dSPieter Jansen van Vuuren if (act_len + act_size > NFP_FL_MAX_A_SIZ) {
147bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at LAG action");
1487e24a593SJohn Hurley return -EOPNOTSUPP;
149bef6e97dSPieter Jansen van Vuuren }
1507e24a593SJohn Hurley
1517e24a593SJohn Hurley /* Pre_lag action must be first on action list.
1529bacb93bSWalter Heymans * If other actions already exist they need to be pushed forward.
1537e24a593SJohn Hurley */
1547e24a593SJohn Hurley if (act_len)
1557e24a593SJohn Hurley memmove(nfp_flow->action_data + act_size,
1567e24a593SJohn Hurley nfp_flow->action_data, act_len);
1577e24a593SJohn Hurley
1587e24a593SJohn Hurley pre_lag = (struct nfp_fl_pre_lag *)nfp_flow->action_data;
159bef6e97dSPieter Jansen van Vuuren err = nfp_flower_lag_populate_pre_action(app, out_dev, pre_lag, extack);
1607e24a593SJohn Hurley if (err)
1617e24a593SJohn Hurley return err;
1627e24a593SJohn Hurley
1637e24a593SJohn Hurley pre_lag->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_LAG;
1647e24a593SJohn Hurley pre_lag->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1657e24a593SJohn Hurley
1667e24a593SJohn Hurley nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1677e24a593SJohn Hurley
1687e24a593SJohn Hurley return act_size;
1697e24a593SJohn Hurley }
1707e24a593SJohn Hurley
1711a1e586fSPieter Jansen van Vuuren static int
nfp_fl_output(struct nfp_app * app,struct nfp_fl_output * output,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_flow,bool last,struct net_device * in_dev,enum nfp_flower_tun_type tun_type,int * tun_out_cnt,bool pkt_host,struct netlink_ext_ack * extack)1727e24a593SJohn Hurley nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
17373867881SPablo Neira Ayuso const struct flow_action_entry *act,
17473867881SPablo Neira Ayuso struct nfp_fl_payload *nfp_flow,
1757e24a593SJohn Hurley bool last, struct net_device *in_dev,
176bef6e97dSPieter Jansen van Vuuren enum nfp_flower_tun_type tun_type, int *tun_out_cnt,
177f5c977eeSJohn Hurley bool pkt_host, struct netlink_ext_ack *extack)
1781a1e586fSPieter Jansen van Vuuren {
1791a1e586fSPieter Jansen van Vuuren size_t act_size = sizeof(struct nfp_fl_output);
1807e24a593SJohn Hurley struct nfp_flower_priv *priv = app->priv;
1811a1e586fSPieter Jansen van Vuuren struct net_device *out_dev;
18262d3f60bSPieter Jansen van Vuuren u16 tmp_flags;
1831a1e586fSPieter Jansen van Vuuren
18462d3f60bSPieter Jansen van Vuuren output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT;
18562d3f60bSPieter Jansen van Vuuren output->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1861a1e586fSPieter Jansen van Vuuren
18773867881SPablo Neira Ayuso out_dev = act->dev;
188bef6e97dSPieter Jansen van Vuuren if (!out_dev) {
189bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid egress interface for mirred action");
1901a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
191bef6e97dSPieter Jansen van Vuuren }
1921a1e586fSPieter Jansen van Vuuren
193b27d6a95SJohn Hurley tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0;
194b27d6a95SJohn Hurley
195b27d6a95SJohn Hurley if (tun_type) {
196b27d6a95SJohn Hurley /* Verify the egress netdev matches the tunnel type. */
197bef6e97dSPieter Jansen van Vuuren if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type)) {
198bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface does not match the required tunnel type");
199b27d6a95SJohn Hurley return -EOPNOTSUPP;
200bef6e97dSPieter Jansen van Vuuren }
201b27d6a95SJohn Hurley
202bef6e97dSPieter Jansen van Vuuren if (*tun_out_cnt) {
203bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot offload more than one tunnel mirred output per filter");
204b27d6a95SJohn Hurley return -EOPNOTSUPP;
205bef6e97dSPieter Jansen van Vuuren }
206b27d6a95SJohn Hurley (*tun_out_cnt)++;
207b27d6a95SJohn Hurley
208b27d6a95SJohn Hurley output->flags = cpu_to_be16(tmp_flags |
209b27d6a95SJohn Hurley NFP_FL_OUT_FLAGS_USE_TUN);
210b27d6a95SJohn Hurley output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
2117e24a593SJohn Hurley } else if (netif_is_lag_master(out_dev) &&
212e09303d3SLouis Peens priv->flower_en_feats & NFP_FL_ENABLE_LAG) {
2137e24a593SJohn Hurley int gid;
2147e24a593SJohn Hurley
2157e24a593SJohn Hurley output->flags = cpu_to_be16(tmp_flags);
2167e24a593SJohn Hurley gid = nfp_flower_lag_get_output_id(app, out_dev);
217bef6e97dSPieter Jansen van Vuuren if (gid < 0) {
218bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot find group id for LAG action");
2197e24a593SJohn Hurley return gid;
220bef6e97dSPieter Jansen van Vuuren }
2217e24a593SJohn Hurley output->port = cpu_to_be32(NFP_FL_LAG_OUT | gid);
222f5c977eeSJohn Hurley } else if (nfp_flower_internal_port_can_offload(app, out_dev)) {
223e30b2b68SLouis Peens if (!(priv->flower_ext_feats & NFP_FL_FEATS_PRE_TUN_RULES) &&
224e30b2b68SLouis Peens !(priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2)) {
225f5c977eeSJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules not supported in loaded firmware");
226f5c977eeSJohn Hurley return -EOPNOTSUPP;
227f5c977eeSJohn Hurley }
228f5c977eeSJohn Hurley
229f5c977eeSJohn Hurley if (nfp_flow->pre_tun_rule.dev || !pkt_host) {
230f5c977eeSJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules require single egress dev and ptype HOST action");
231f5c977eeSJohn Hurley return -EOPNOTSUPP;
232f5c977eeSJohn Hurley }
233f5c977eeSJohn Hurley
234f5c977eeSJohn Hurley nfp_flow->pre_tun_rule.dev = out_dev;
235f5c977eeSJohn Hurley
236f5c977eeSJohn Hurley return 0;
237b27d6a95SJohn Hurley } else {
238b27d6a95SJohn Hurley /* Set action output parameters. */
239b27d6a95SJohn Hurley output->flags = cpu_to_be16(tmp_flags);
240b27d6a95SJohn Hurley
2417885b4fcSJohn Hurley if (nfp_netdev_is_nfp_repr(in_dev)) {
2427885b4fcSJohn Hurley /* Confirm ingress and egress are on same device. */
243bef6e97dSPieter Jansen van Vuuren if (!netdev_port_same_parent_id(in_dev, out_dev)) {
244bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: ingress and egress interfaces are on different devices");
245bef6e97dSPieter Jansen van Vuuren return -EOPNOTSUPP;
246bef6e97dSPieter Jansen van Vuuren }
247bef6e97dSPieter Jansen van Vuuren }
248bef6e97dSPieter Jansen van Vuuren
249bef6e97dSPieter Jansen van Vuuren if (!nfp_netdev_is_nfp_repr(out_dev)) {
250bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface is not an nfp port");
2511a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
2527885b4fcSJohn Hurley }
2537885b4fcSJohn Hurley
2541a1e586fSPieter Jansen van Vuuren output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
255bef6e97dSPieter Jansen van Vuuren if (!output->port) {
256bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid port id for egress interface");
2571a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
258b27d6a95SJohn Hurley }
259bef6e97dSPieter Jansen van Vuuren }
2601a1e586fSPieter Jansen van Vuuren nfp_flow->meta.shortcut = output->port;
2611a1e586fSPieter Jansen van Vuuren
2621a1e586fSPieter Jansen van Vuuren return 0;
2631a1e586fSPieter Jansen van Vuuren }
2641a1e586fSPieter Jansen van Vuuren
265fccac580SPieter Jansen van Vuuren static bool
nfp_flower_tun_is_gre(struct flow_rule * rule,int start_idx)266e75dc265SLouis Peens nfp_flower_tun_is_gre(struct flow_rule *rule, int start_idx)
267fccac580SPieter Jansen van Vuuren {
268e75dc265SLouis Peens struct flow_action_entry *act = rule->action.entries;
269e75dc265SLouis Peens int num_act = rule->action.num_entries;
270fccac580SPieter Jansen van Vuuren int act_idx;
271fccac580SPieter Jansen van Vuuren
272fccac580SPieter Jansen van Vuuren /* Preparse action list for next mirred or redirect action */
273fccac580SPieter Jansen van Vuuren for (act_idx = start_idx + 1; act_idx < num_act; act_idx++)
274fccac580SPieter Jansen van Vuuren if (act[act_idx].id == FLOW_ACTION_REDIRECT ||
275fccac580SPieter Jansen van Vuuren act[act_idx].id == FLOW_ACTION_MIRRED)
276f7536ffbSYu Xiao return netif_is_gretap(act[act_idx].dev) ||
277f7536ffbSYu Xiao netif_is_ip6gretap(act[act_idx].dev);
278fccac580SPieter Jansen van Vuuren
279fccac580SPieter Jansen van Vuuren return false;
280fccac580SPieter Jansen van Vuuren }
281fccac580SPieter Jansen van Vuuren
2823ca3059dSJohn Hurley static enum nfp_flower_tun_type
nfp_fl_get_tun_from_act(struct nfp_app * app,struct flow_rule * rule,const struct flow_action_entry * act,int act_idx)283104dce5bSPieter Jansen van Vuuren nfp_fl_get_tun_from_act(struct nfp_app *app,
284e75dc265SLouis Peens struct flow_rule *rule,
285fccac580SPieter Jansen van Vuuren const struct flow_action_entry *act, int act_idx)
286b27d6a95SJohn Hurley {
28773867881SPablo Neira Ayuso const struct ip_tunnel_info *tun = act->tunnel;
2883ca3059dSJohn Hurley struct nfp_flower_priv *priv = app->priv;
289b27d6a95SJohn Hurley
290fccac580SPieter Jansen van Vuuren /* Determine the tunnel type based on the egress netdev
291fccac580SPieter Jansen van Vuuren * in the mirred action for tunnels without l4.
292fccac580SPieter Jansen van Vuuren */
293e75dc265SLouis Peens if (nfp_flower_tun_is_gre(rule, act_idx))
294fccac580SPieter Jansen van Vuuren return NFP_FL_TUNNEL_GRE;
295fccac580SPieter Jansen van Vuuren
2963ca3059dSJohn Hurley switch (tun->key.tp_dst) {
297bea96410SMoshe Shemesh case htons(IANA_VXLAN_UDP_PORT):
2983ca3059dSJohn Hurley return NFP_FL_TUNNEL_VXLAN;
299974eff2bSMoshe Shemesh case htons(GENEVE_UDP_PORT):
3003ca3059dSJohn Hurley if (priv->flower_ext_feats & NFP_FL_FEATS_GENEVE)
3013ca3059dSJohn Hurley return NFP_FL_TUNNEL_GENEVE;
302df561f66SGustavo A. R. Silva fallthrough;
3033ca3059dSJohn Hurley default:
3043ca3059dSJohn Hurley return NFP_FL_TUNNEL_NONE;
3053ca3059dSJohn Hurley }
306b27d6a95SJohn Hurley }
307b27d6a95SJohn Hurley
nfp_fl_pre_tunnel(char * act_data,int act_len)308b27d6a95SJohn Hurley static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
309b27d6a95SJohn Hurley {
310b27d6a95SJohn Hurley size_t act_size = sizeof(struct nfp_fl_pre_tunnel);
311b27d6a95SJohn Hurley struct nfp_fl_pre_tunnel *pre_tun_act;
312b27d6a95SJohn Hurley
313b27d6a95SJohn Hurley /* Pre_tunnel action must be first on action list.
3147e24a593SJohn Hurley * If other actions already exist they need to be pushed forward.
315b27d6a95SJohn Hurley */
316b27d6a95SJohn Hurley if (act_len)
317b27d6a95SJohn Hurley memmove(act_data + act_size, act_data, act_len);
318b27d6a95SJohn Hurley
319b27d6a95SJohn Hurley pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data;
320b27d6a95SJohn Hurley
321b27d6a95SJohn Hurley memset(pre_tun_act, 0, act_size);
322b27d6a95SJohn Hurley
32362d3f60bSPieter Jansen van Vuuren pre_tun_act->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_TUNNEL;
32462d3f60bSPieter Jansen van Vuuren pre_tun_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
325b27d6a95SJohn Hurley
326b27d6a95SJohn Hurley return pre_tun_act;
327b27d6a95SJohn Hurley }
328b27d6a95SJohn Hurley
329b27d6a95SJohn Hurley static int
nfp_fl_push_geneve_options(struct nfp_fl_payload * nfp_fl,int * list_len,const struct flow_action_entry * act,struct netlink_ext_ack * extack)3309e7c32feSPieter Jansen van Vuuren nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len,
331bef6e97dSPieter Jansen van Vuuren const struct flow_action_entry *act,
332bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
3339e7c32feSPieter Jansen van Vuuren {
33473867881SPablo Neira Ayuso struct ip_tunnel_info *ip_tun = (struct ip_tunnel_info *)act->tunnel;
3359e7c32feSPieter Jansen van Vuuren int opt_len, opt_cnt, act_start, tot_push_len;
3369e7c32feSPieter Jansen van Vuuren u8 *src = ip_tunnel_info_opts(ip_tun);
3379e7c32feSPieter Jansen van Vuuren
3389e7c32feSPieter Jansen van Vuuren /* We need to populate the options in reverse order for HW.
3399e7c32feSPieter Jansen van Vuuren * Therefore we go through the options, calculating the
3409e7c32feSPieter Jansen van Vuuren * number of options and the total size, then we populate
3419e7c32feSPieter Jansen van Vuuren * them in reverse order in the action list.
3429e7c32feSPieter Jansen van Vuuren */
3439e7c32feSPieter Jansen van Vuuren opt_cnt = 0;
3449e7c32feSPieter Jansen van Vuuren tot_push_len = 0;
3459e7c32feSPieter Jansen van Vuuren opt_len = ip_tun->options_len;
3469e7c32feSPieter Jansen van Vuuren while (opt_len > 0) {
3479e7c32feSPieter Jansen van Vuuren struct geneve_opt *opt = (struct geneve_opt *)src;
3489e7c32feSPieter Jansen van Vuuren
3499e7c32feSPieter Jansen van Vuuren opt_cnt++;
350bef6e97dSPieter Jansen van Vuuren if (opt_cnt > NFP_FL_MAX_GENEVE_OPT_CNT) {
351bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed number of geneve options exceeded");
3529e7c32feSPieter Jansen van Vuuren return -EOPNOTSUPP;
353bef6e97dSPieter Jansen van Vuuren }
3549e7c32feSPieter Jansen van Vuuren
3559e7c32feSPieter Jansen van Vuuren tot_push_len += sizeof(struct nfp_fl_push_geneve) +
3569e7c32feSPieter Jansen van Vuuren opt->length * 4;
357bef6e97dSPieter Jansen van Vuuren if (tot_push_len > NFP_FL_MAX_GENEVE_OPT_ACT) {
358bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options");
3599e7c32feSPieter Jansen van Vuuren return -EOPNOTSUPP;
360bef6e97dSPieter Jansen van Vuuren }
3619e7c32feSPieter Jansen van Vuuren
3629e7c32feSPieter Jansen van Vuuren opt_len -= sizeof(struct geneve_opt) + opt->length * 4;
3639e7c32feSPieter Jansen van Vuuren src += sizeof(struct geneve_opt) + opt->length * 4;
3649e7c32feSPieter Jansen van Vuuren }
3659e7c32feSPieter Jansen van Vuuren
366bef6e97dSPieter Jansen van Vuuren if (*list_len + tot_push_len > NFP_FL_MAX_A_SIZ) {
367bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options");
3689e7c32feSPieter Jansen van Vuuren return -EOPNOTSUPP;
369bef6e97dSPieter Jansen van Vuuren }
3709e7c32feSPieter Jansen van Vuuren
3719e7c32feSPieter Jansen van Vuuren act_start = *list_len;
3729e7c32feSPieter Jansen van Vuuren *list_len += tot_push_len;
3739e7c32feSPieter Jansen van Vuuren src = ip_tunnel_info_opts(ip_tun);
3749e7c32feSPieter Jansen van Vuuren while (opt_cnt) {
3759e7c32feSPieter Jansen van Vuuren struct geneve_opt *opt = (struct geneve_opt *)src;
3769e7c32feSPieter Jansen van Vuuren struct nfp_fl_push_geneve *push;
3779e7c32feSPieter Jansen van Vuuren size_t act_size, len;
3789e7c32feSPieter Jansen van Vuuren
3799e7c32feSPieter Jansen van Vuuren opt_cnt--;
3809e7c32feSPieter Jansen van Vuuren act_size = sizeof(struct nfp_fl_push_geneve) + opt->length * 4;
3819e7c32feSPieter Jansen van Vuuren tot_push_len -= act_size;
3829e7c32feSPieter Jansen van Vuuren len = act_start + tot_push_len;
3839e7c32feSPieter Jansen van Vuuren
3849e7c32feSPieter Jansen van Vuuren push = (struct nfp_fl_push_geneve *)&nfp_fl->action_data[len];
3859e7c32feSPieter Jansen van Vuuren push->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_GENEVE;
3869e7c32feSPieter Jansen van Vuuren push->head.len_lw = act_size >> NFP_FL_LW_SIZ;
3879e7c32feSPieter Jansen van Vuuren push->reserved = 0;
3889e7c32feSPieter Jansen van Vuuren push->class = opt->opt_class;
3899e7c32feSPieter Jansen van Vuuren push->type = opt->type;
3909e7c32feSPieter Jansen van Vuuren push->length = opt->length;
3919e7c32feSPieter Jansen van Vuuren memcpy(&push->opt_data, opt->opt_data, opt->length * 4);
3929e7c32feSPieter Jansen van Vuuren
3939e7c32feSPieter Jansen van Vuuren src += sizeof(struct geneve_opt) + opt->length * 4;
3949e7c32feSPieter Jansen van Vuuren }
3959e7c32feSPieter Jansen van Vuuren
3969e7c32feSPieter Jansen van Vuuren return 0;
3979e7c32feSPieter Jansen van Vuuren }
3989e7c32feSPieter Jansen van Vuuren
3999e7c32feSPieter Jansen van Vuuren static int
nfp_fl_set_tun(struct nfp_app * app,struct nfp_fl_set_tun * set_tun,const struct flow_action_entry * act,struct nfp_fl_pre_tunnel * pre_tun,enum nfp_flower_tun_type tun_type,struct net_device * netdev,struct netlink_ext_ack * extack)4001922c9a4SJohn Hurley nfp_fl_set_tun(struct nfp_app *app, struct nfp_fl_set_tun *set_tun,
40173867881SPablo Neira Ayuso const struct flow_action_entry *act,
4023ca3059dSJohn Hurley struct nfp_fl_pre_tunnel *pre_tun,
40350a5852aSJohn Hurley enum nfp_flower_tun_type tun_type,
404104dce5bSPieter Jansen van Vuuren struct net_device *netdev, struct netlink_ext_ack *extack)
405b27d6a95SJohn Hurley {
40673867881SPablo Neira Ayuso const struct ip_tunnel_info *ip_tun = act->tunnel;
4071922c9a4SJohn Hurley bool ipv6 = ip_tunnel_info_af(ip_tun) == AF_INET6;
4081922c9a4SJohn Hurley size_t act_size = sizeof(struct nfp_fl_set_tun);
4099e7c32feSPieter Jansen van Vuuren struct nfp_flower_priv *priv = app->priv;
4103ca3059dSJohn Hurley u32 tmp_set_ip_tun_type_index = 0;
411b27d6a95SJohn Hurley /* Currently support one pre-tunnel so index is always 0. */
412b27d6a95SJohn Hurley int pretun_idx = 0;
413b27d6a95SJohn Hurley
4141922c9a4SJohn Hurley if (!IS_ENABLED(CONFIG_IPV6) && ipv6)
4151922c9a4SJohn Hurley return -EOPNOTSUPP;
4161922c9a4SJohn Hurley
4171922c9a4SJohn Hurley if (ipv6 && !(priv->flower_ext_feats & NFP_FL_FEATS_IPV6_TUN))
4181922c9a4SJohn Hurley return -EOPNOTSUPP;
4191922c9a4SJohn Hurley
4209e7c32feSPieter Jansen van Vuuren BUILD_BUG_ON(NFP_FL_TUNNEL_CSUM != TUNNEL_CSUM ||
4219e7c32feSPieter Jansen van Vuuren NFP_FL_TUNNEL_KEY != TUNNEL_KEY ||
4229e7c32feSPieter Jansen van Vuuren NFP_FL_TUNNEL_GENEVE_OPT != TUNNEL_GENEVE_OPT);
4239e7c32feSPieter Jansen van Vuuren if (ip_tun->options_len &&
4249e7c32feSPieter Jansen van Vuuren (tun_type != NFP_FL_TUNNEL_GENEVE ||
425bef6e97dSPieter Jansen van Vuuren !(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE_OPT))) {
426bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support geneve options offload");
427b27d6a95SJohn Hurley return -EOPNOTSUPP;
428bef6e97dSPieter Jansen van Vuuren }
429b27d6a95SJohn Hurley
430*45490ce2SBaowen Zheng if (ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_UDP_TUN_FLAGS) {
431*45490ce2SBaowen Zheng NL_SET_ERR_MSG_MOD(extack,
432*45490ce2SBaowen Zheng "unsupported offload: loaded firmware does not support tunnel flag offload");
433*45490ce2SBaowen Zheng return -EOPNOTSUPP;
434*45490ce2SBaowen Zheng }
435*45490ce2SBaowen Zheng
4361922c9a4SJohn Hurley set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_TUNNEL;
4373ca3059dSJohn Hurley set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ;
438b27d6a95SJohn Hurley
439b27d6a95SJohn Hurley /* Set tunnel type and pre-tunnel index. */
4403ca3059dSJohn Hurley tmp_set_ip_tun_type_index |=
4411922c9a4SJohn Hurley FIELD_PREP(NFP_FL_TUNNEL_TYPE, tun_type) |
4421922c9a4SJohn Hurley FIELD_PREP(NFP_FL_PRE_TUN_INDEX, pretun_idx);
443b27d6a95SJohn Hurley
4443ca3059dSJohn Hurley set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index);
445*45490ce2SBaowen Zheng if (ip_tun->key.tun_flags & NFP_FL_TUNNEL_KEY)
4463ca3059dSJohn Hurley set_tun->tun_id = ip_tun->key.tun_id;
447ed21b637SJohn Hurley
4482a437471SJohn Hurley if (ip_tun->key.ttl) {
4492a437471SJohn Hurley set_tun->ttl = ip_tun->key.ttl;
4501922c9a4SJohn Hurley #ifdef CONFIG_IPV6
4511922c9a4SJohn Hurley } else if (ipv6) {
4521922c9a4SJohn Hurley struct net *net = dev_net(netdev);
4531922c9a4SJohn Hurley struct flowi6 flow = {};
4541922c9a4SJohn Hurley struct dst_entry *dst;
4551922c9a4SJohn Hurley
4561922c9a4SJohn Hurley flow.daddr = ip_tun->key.u.ipv6.dst;
4571922c9a4SJohn Hurley flow.flowi4_proto = IPPROTO_UDP;
4581922c9a4SJohn Hurley dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &flow, NULL);
4591922c9a4SJohn Hurley if (!IS_ERR(dst)) {
4601922c9a4SJohn Hurley set_tun->ttl = ip6_dst_hoplimit(dst);
4611922c9a4SJohn Hurley dst_release(dst);
4621922c9a4SJohn Hurley } else {
4631922c9a4SJohn Hurley set_tun->ttl = net->ipv6.devconf_all->hop_limit;
4641922c9a4SJohn Hurley }
4651922c9a4SJohn Hurley #endif
4662a437471SJohn Hurley } else {
4672a437471SJohn Hurley struct net *net = dev_net(netdev);
4682a437471SJohn Hurley struct flowi4 flow = {};
4692a437471SJohn Hurley struct rtable *rt;
4702a437471SJohn Hurley int err;
4712a437471SJohn Hurley
4722a437471SJohn Hurley /* Do a route lookup to determine ttl - if fails then use
4732a437471SJohn Hurley * default. Note that CONFIG_INET is a requirement of
4742a437471SJohn Hurley * CONFIG_NET_SWITCHDEV so must be defined here.
475ed21b637SJohn Hurley */
476ed21b637SJohn Hurley flow.daddr = ip_tun->key.u.ipv4.dst;
477ed21b637SJohn Hurley flow.flowi4_proto = IPPROTO_UDP;
478ed21b637SJohn Hurley rt = ip_route_output_key(net, &flow);
479ed21b637SJohn Hurley err = PTR_ERR_OR_ZERO(rt);
480ed21b637SJohn Hurley if (!err) {
481ed21b637SJohn Hurley set_tun->ttl = ip4_dst_hoplimit(&rt->dst);
482ed21b637SJohn Hurley ip_rt_put(rt);
483ed21b637SJohn Hurley } else {
4848281b7ecSKuniyuki Iwashima set_tun->ttl = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
485ed21b637SJohn Hurley }
4862a437471SJohn Hurley }
487b27d6a95SJohn Hurley
48851a8cefcSJohn Hurley set_tun->tos = ip_tun->key.tos;
48951a8cefcSJohn Hurley set_tun->tun_flags = ip_tun->key.tun_flags;
49051a8cefcSJohn Hurley
4919e7c32feSPieter Jansen van Vuuren if (tun_type == NFP_FL_TUNNEL_GENEVE) {
4929e7c32feSPieter Jansen van Vuuren set_tun->tun_proto = htons(ETH_P_TEB);
4939e7c32feSPieter Jansen van Vuuren set_tun->tun_len = ip_tun->options_len / 4;
4949e7c32feSPieter Jansen van Vuuren }
4959e7c32feSPieter Jansen van Vuuren
496b27d6a95SJohn Hurley /* Complete pre_tunnel action. */
4971922c9a4SJohn Hurley if (ipv6) {
4981922c9a4SJohn Hurley pre_tun->flags |= cpu_to_be16(NFP_FL_PRE_TUN_IPV6);
4991922c9a4SJohn Hurley pre_tun->ipv6_dst = ip_tun->key.u.ipv6.dst;
5001922c9a4SJohn Hurley } else {
5013ca3059dSJohn Hurley pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst;
5021922c9a4SJohn Hurley }
503b27d6a95SJohn Hurley
504b27d6a95SJohn Hurley return 0;
505b27d6a95SJohn Hurley }
506b27d6a95SJohn Hurley
nfp_fl_set_helper32(u32 value,u32 mask,u8 * p_exact,u8 * p_mask)507da83d8feSPieter Jansen van Vuuren static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask)
508da83d8feSPieter Jansen van Vuuren {
509da83d8feSPieter Jansen van Vuuren u32 oldvalue = get_unaligned((u32 *)p_exact);
510da83d8feSPieter Jansen van Vuuren u32 oldmask = get_unaligned((u32 *)p_mask);
511da83d8feSPieter Jansen van Vuuren
512da83d8feSPieter Jansen van Vuuren value &= mask;
513da83d8feSPieter Jansen van Vuuren value |= oldvalue & ~mask;
514da83d8feSPieter Jansen van Vuuren
515da83d8feSPieter Jansen van Vuuren put_unaligned(oldmask | mask, (u32 *)p_mask);
516da83d8feSPieter Jansen van Vuuren put_unaligned(value, (u32 *)p_exact);
517da83d8feSPieter Jansen van Vuuren }
518da83d8feSPieter Jansen van Vuuren
519da83d8feSPieter Jansen van Vuuren static int
nfp_fl_set_eth(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_eth * set_eth,struct netlink_ext_ack * extack)520c0bc5d8eSPablo Neira Ayuso nfp_fl_set_eth(const struct flow_action_entry *act, u32 off,
521bef6e97dSPieter Jansen van Vuuren struct nfp_fl_set_eth *set_eth, struct netlink_ext_ack *extack)
522da83d8feSPieter Jansen van Vuuren {
523da83d8feSPieter Jansen van Vuuren u32 exact, mask;
524da83d8feSPieter Jansen van Vuuren
525bef6e97dSPieter Jansen van Vuuren if (off + 4 > ETH_ALEN * 2) {
526bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action");
527da83d8feSPieter Jansen van Vuuren return -EOPNOTSUPP;
528bef6e97dSPieter Jansen van Vuuren }
529da83d8feSPieter Jansen van Vuuren
53073867881SPablo Neira Ayuso mask = ~act->mangle.mask;
53173867881SPablo Neira Ayuso exact = act->mangle.val;
532da83d8feSPieter Jansen van Vuuren
533bef6e97dSPieter Jansen van Vuuren if (exact & ~mask) {
534bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action");
535da83d8feSPieter Jansen van Vuuren return -EOPNOTSUPP;
536bef6e97dSPieter Jansen van Vuuren }
537da83d8feSPieter Jansen van Vuuren
538da83d8feSPieter Jansen van Vuuren nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off],
539da83d8feSPieter Jansen van Vuuren &set_eth->eth_addr_mask[off]);
540da83d8feSPieter Jansen van Vuuren
541da83d8feSPieter Jansen van Vuuren set_eth->reserved = cpu_to_be16(0);
54262d3f60bSPieter Jansen van Vuuren set_eth->head.jump_id = NFP_FL_ACTION_OPCODE_SET_ETHERNET;
54362d3f60bSPieter Jansen van Vuuren set_eth->head.len_lw = sizeof(*set_eth) >> NFP_FL_LW_SIZ;
544da83d8feSPieter Jansen van Vuuren
545da83d8feSPieter Jansen van Vuuren return 0;
546da83d8feSPieter Jansen van Vuuren }
547da83d8feSPieter Jansen van Vuuren
548a3c6b063SPieter Jansen van Vuuren struct ipv4_ttl_word {
549a3c6b063SPieter Jansen van Vuuren __u8 ttl;
550a3c6b063SPieter Jansen van Vuuren __u8 protocol;
551a3c6b063SPieter Jansen van Vuuren __sum16 check;
552a3c6b063SPieter Jansen van Vuuren };
553a3c6b063SPieter Jansen van Vuuren
554da83d8feSPieter Jansen van Vuuren static int
nfp_fl_set_ip4(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_ip4_addrs * set_ip_addr,struct nfp_fl_set_ip4_ttl_tos * set_ip_ttl_tos,struct netlink_ext_ack * extack)555c0bc5d8eSPablo Neira Ayuso nfp_fl_set_ip4(const struct flow_action_entry *act, u32 off,
556a3c6b063SPieter Jansen van Vuuren struct nfp_fl_set_ip4_addrs *set_ip_addr,
557bef6e97dSPieter Jansen van Vuuren struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos,
558bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
559c0b1bd9aSPieter Jansen van Vuuren {
560a3c6b063SPieter Jansen van Vuuren struct ipv4_ttl_word *ttl_word_mask;
561a3c6b063SPieter Jansen van Vuuren struct ipv4_ttl_word *ttl_word;
562a3c6b063SPieter Jansen van Vuuren struct iphdr *tos_word_mask;
563a3c6b063SPieter Jansen van Vuuren struct iphdr *tos_word;
564c0b1bd9aSPieter Jansen van Vuuren __be32 exact, mask;
565c0b1bd9aSPieter Jansen van Vuuren
566c0b1bd9aSPieter Jansen van Vuuren /* We are expecting tcf_pedit to return a big endian value */
56773867881SPablo Neira Ayuso mask = (__force __be32)~act->mangle.mask;
56873867881SPablo Neira Ayuso exact = (__force __be32)act->mangle.val;
569c0b1bd9aSPieter Jansen van Vuuren
570bef6e97dSPieter Jansen van Vuuren if (exact & ~mask) {
571bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 action");
572c0b1bd9aSPieter Jansen van Vuuren return -EOPNOTSUPP;
573bef6e97dSPieter Jansen van Vuuren }
574c0b1bd9aSPieter Jansen van Vuuren
575c0b1bd9aSPieter Jansen van Vuuren switch (off) {
576c0b1bd9aSPieter Jansen van Vuuren case offsetof(struct iphdr, daddr):
5778913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_dst_mask |= mask;
5788913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_dst &= ~mask;
5798913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_dst |= exact & mask;
580a3c6b063SPieter Jansen van Vuuren set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
581a3c6b063SPieter Jansen van Vuuren set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
582a3c6b063SPieter Jansen van Vuuren NFP_FL_LW_SIZ;
583c0b1bd9aSPieter Jansen van Vuuren break;
584c0b1bd9aSPieter Jansen van Vuuren case offsetof(struct iphdr, saddr):
5858913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_src_mask |= mask;
5868913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_src &= ~mask;
5878913806fSPieter Jansen van Vuuren set_ip_addr->ipv4_src |= exact & mask;
588a3c6b063SPieter Jansen van Vuuren set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
589a3c6b063SPieter Jansen van Vuuren set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
590a3c6b063SPieter Jansen van Vuuren NFP_FL_LW_SIZ;
591a3c6b063SPieter Jansen van Vuuren break;
592a3c6b063SPieter Jansen van Vuuren case offsetof(struct iphdr, ttl):
593a3c6b063SPieter Jansen van Vuuren ttl_word_mask = (struct ipv4_ttl_word *)&mask;
594a3c6b063SPieter Jansen van Vuuren ttl_word = (struct ipv4_ttl_word *)&exact;
595a3c6b063SPieter Jansen van Vuuren
596bef6e97dSPieter Jansen van Vuuren if (ttl_word_mask->protocol || ttl_word_mask->check) {
597bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 ttl action");
598a3c6b063SPieter Jansen van Vuuren return -EOPNOTSUPP;
599bef6e97dSPieter Jansen van Vuuren }
600a3c6b063SPieter Jansen van Vuuren
601a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_ttl_mask |= ttl_word_mask->ttl;
602a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_ttl &= ~ttl_word_mask->ttl;
603a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_ttl |= ttl_word->ttl & ttl_word_mask->ttl;
604a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->head.jump_id =
605a3c6b063SPieter Jansen van Vuuren NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
606a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
607a3c6b063SPieter Jansen van Vuuren NFP_FL_LW_SIZ;
608a3c6b063SPieter Jansen van Vuuren break;
609a3c6b063SPieter Jansen van Vuuren case round_down(offsetof(struct iphdr, tos), 4):
610a3c6b063SPieter Jansen van Vuuren tos_word_mask = (struct iphdr *)&mask;
611a3c6b063SPieter Jansen van Vuuren tos_word = (struct iphdr *)&exact;
612a3c6b063SPieter Jansen van Vuuren
613a3c6b063SPieter Jansen van Vuuren if (tos_word_mask->version || tos_word_mask->ihl ||
614bef6e97dSPieter Jansen van Vuuren tos_word_mask->tot_len) {
615bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 tos action");
616a3c6b063SPieter Jansen van Vuuren return -EOPNOTSUPP;
617bef6e97dSPieter Jansen van Vuuren }
618a3c6b063SPieter Jansen van Vuuren
619a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_tos_mask |= tos_word_mask->tos;
620a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_tos &= ~tos_word_mask->tos;
621a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->ipv4_tos |= tos_word->tos & tos_word_mask->tos;
622a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->head.jump_id =
623a3c6b063SPieter Jansen van Vuuren NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
624a3c6b063SPieter Jansen van Vuuren set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
625a3c6b063SPieter Jansen van Vuuren NFP_FL_LW_SIZ;
626c0b1bd9aSPieter Jansen van Vuuren break;
627c0b1bd9aSPieter Jansen van Vuuren default:
628bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv4 header");
629c0b1bd9aSPieter Jansen van Vuuren return -EOPNOTSUPP;
630c0b1bd9aSPieter Jansen van Vuuren }
631c0b1bd9aSPieter Jansen van Vuuren
632c0b1bd9aSPieter Jansen van Vuuren return 0;
633c0b1bd9aSPieter Jansen van Vuuren }
634c0b1bd9aSPieter Jansen van Vuuren
635354b82bbSPieter Jansen van Vuuren static void
nfp_fl_set_ip6_helper(int opcode_tag,u8 word,__be32 exact,__be32 mask,struct nfp_fl_set_ipv6_addr * ip6)636140b6abaSPieter Jansen van Vuuren nfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask,
637354b82bbSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_addr *ip6)
638354b82bbSPieter Jansen van Vuuren {
639140b6abaSPieter Jansen van Vuuren ip6->ipv6[word].mask |= mask;
640140b6abaSPieter Jansen van Vuuren ip6->ipv6[word].exact &= ~mask;
641140b6abaSPieter Jansen van Vuuren ip6->ipv6[word].exact |= exact & mask;
642354b82bbSPieter Jansen van Vuuren
643354b82bbSPieter Jansen van Vuuren ip6->reserved = cpu_to_be16(0);
64462d3f60bSPieter Jansen van Vuuren ip6->head.jump_id = opcode_tag;
64562d3f60bSPieter Jansen van Vuuren ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
646354b82bbSPieter Jansen van Vuuren }
647354b82bbSPieter Jansen van Vuuren
6484234d62cSPieter Jansen van Vuuren struct ipv6_hop_limit_word {
6494234d62cSPieter Jansen van Vuuren __be16 payload_len;
6504234d62cSPieter Jansen van Vuuren u8 nexthdr;
6514234d62cSPieter Jansen van Vuuren u8 hop_limit;
6524234d62cSPieter Jansen van Vuuren };
6534234d62cSPieter Jansen van Vuuren
6544234d62cSPieter Jansen van Vuuren static int
nfp_fl_set_ip6_hop_limit_flow_label(u32 off,__be32 exact,__be32 mask,struct nfp_fl_set_ipv6_tc_hl_fl * ip_hl_fl,struct netlink_ext_ack * extack)6554234d62cSPieter Jansen van Vuuren nfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask,
656bef6e97dSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl,
657bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
6584234d62cSPieter Jansen van Vuuren {
6594234d62cSPieter Jansen van Vuuren struct ipv6_hop_limit_word *fl_hl_mask;
6604234d62cSPieter Jansen van Vuuren struct ipv6_hop_limit_word *fl_hl;
6614234d62cSPieter Jansen van Vuuren
6624234d62cSPieter Jansen van Vuuren switch (off) {
6634234d62cSPieter Jansen van Vuuren case offsetof(struct ipv6hdr, payload_len):
6644234d62cSPieter Jansen van Vuuren fl_hl_mask = (struct ipv6_hop_limit_word *)&mask;
6654234d62cSPieter Jansen van Vuuren fl_hl = (struct ipv6_hop_limit_word *)&exact;
6664234d62cSPieter Jansen van Vuuren
667bef6e97dSPieter Jansen van Vuuren if (fl_hl_mask->nexthdr || fl_hl_mask->payload_len) {
668bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 hop limit action");
6694234d62cSPieter Jansen van Vuuren return -EOPNOTSUPP;
670bef6e97dSPieter Jansen van Vuuren }
6714234d62cSPieter Jansen van Vuuren
6724234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_hop_limit_mask |= fl_hl_mask->hop_limit;
6734234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_hop_limit &= ~fl_hl_mask->hop_limit;
6744234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_hop_limit |= fl_hl->hop_limit &
6754234d62cSPieter Jansen van Vuuren fl_hl_mask->hop_limit;
6764234d62cSPieter Jansen van Vuuren break;
6774234d62cSPieter Jansen van Vuuren case round_down(offsetof(struct ipv6hdr, flow_lbl), 4):
67827f2533bSYinjun Zhang if (mask & ~IPV6_FLOWINFO_MASK ||
67927f2533bSYinjun Zhang exact & ~IPV6_FLOWINFO_MASK) {
68027f2533bSYinjun Zhang NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 flow info action");
6814234d62cSPieter Jansen van Vuuren return -EOPNOTSUPP;
682bef6e97dSPieter Jansen van Vuuren }
6834234d62cSPieter Jansen van Vuuren
6844234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_label_mask |= mask;
6854234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_label &= ~mask;
6864234d62cSPieter Jansen van Vuuren ip_hl_fl->ipv6_label |= exact & mask;
6874234d62cSPieter Jansen van Vuuren break;
6884234d62cSPieter Jansen van Vuuren }
6894234d62cSPieter Jansen van Vuuren
6904234d62cSPieter Jansen van Vuuren ip_hl_fl->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL;
6914234d62cSPieter Jansen van Vuuren ip_hl_fl->head.len_lw = sizeof(*ip_hl_fl) >> NFP_FL_LW_SIZ;
6924234d62cSPieter Jansen van Vuuren
6934234d62cSPieter Jansen van Vuuren return 0;
6944234d62cSPieter Jansen van Vuuren }
6954234d62cSPieter Jansen van Vuuren
696354b82bbSPieter Jansen van Vuuren static int
nfp_fl_set_ip6(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_ipv6_addr * ip_dst,struct nfp_fl_set_ipv6_addr * ip_src,struct nfp_fl_set_ipv6_tc_hl_fl * ip_hl_fl,struct netlink_ext_ack * extack)697c0bc5d8eSPablo Neira Ayuso nfp_fl_set_ip6(const struct flow_action_entry *act, u32 off,
698354b82bbSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_addr *ip_dst,
6994234d62cSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_addr *ip_src,
700bef6e97dSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl,
701bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
702354b82bbSPieter Jansen van Vuuren {
703354b82bbSPieter Jansen van Vuuren __be32 exact, mask;
7044234d62cSPieter Jansen van Vuuren int err = 0;
705140b6abaSPieter Jansen van Vuuren u8 word;
706354b82bbSPieter Jansen van Vuuren
707354b82bbSPieter Jansen van Vuuren /* We are expecting tcf_pedit to return a big endian value */
70873867881SPablo Neira Ayuso mask = (__force __be32)~act->mangle.mask;
70973867881SPablo Neira Ayuso exact = (__force __be32)act->mangle.val;
710354b82bbSPieter Jansen van Vuuren
711bef6e97dSPieter Jansen van Vuuren if (exact & ~mask) {
712bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 action");
713354b82bbSPieter Jansen van Vuuren return -EOPNOTSUPP;
714bef6e97dSPieter Jansen van Vuuren }
715354b82bbSPieter Jansen van Vuuren
716140b6abaSPieter Jansen van Vuuren if (off < offsetof(struct ipv6hdr, saddr)) {
7174234d62cSPieter Jansen van Vuuren err = nfp_fl_set_ip6_hop_limit_flow_label(off, exact, mask,
718bef6e97dSPieter Jansen van Vuuren ip_hl_fl, extack);
719140b6abaSPieter Jansen van Vuuren } else if (off < offsetof(struct ipv6hdr, daddr)) {
720140b6abaSPieter Jansen van Vuuren word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact);
721140b6abaSPieter Jansen van Vuuren nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word,
722354b82bbSPieter Jansen van Vuuren exact, mask, ip_src);
723140b6abaSPieter Jansen van Vuuren } else if (off < offsetof(struct ipv6hdr, daddr) +
724140b6abaSPieter Jansen van Vuuren sizeof(struct in6_addr)) {
725140b6abaSPieter Jansen van Vuuren word = (off - offsetof(struct ipv6hdr, daddr)) / sizeof(exact);
726140b6abaSPieter Jansen van Vuuren nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, word,
727354b82bbSPieter Jansen van Vuuren exact, mask, ip_dst);
728140b6abaSPieter Jansen van Vuuren } else {
729bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv6 header");
730354b82bbSPieter Jansen van Vuuren return -EOPNOTSUPP;
731140b6abaSPieter Jansen van Vuuren }
732354b82bbSPieter Jansen van Vuuren
7334234d62cSPieter Jansen van Vuuren return err;
734354b82bbSPieter Jansen van Vuuren }
735354b82bbSPieter Jansen van Vuuren
736c0b1bd9aSPieter Jansen van Vuuren static int
nfp_fl_set_tport(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_tport * set_tport,int opcode,struct netlink_ext_ack * extack)737c0bc5d8eSPablo Neira Ayuso nfp_fl_set_tport(const struct flow_action_entry *act, u32 off,
738bef6e97dSPieter Jansen van Vuuren struct nfp_fl_set_tport *set_tport, int opcode,
739bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
740f8b7b0a6SPieter Jansen van Vuuren {
741f8b7b0a6SPieter Jansen van Vuuren u32 exact, mask;
742f8b7b0a6SPieter Jansen van Vuuren
743bef6e97dSPieter Jansen van Vuuren if (off) {
744bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of L4 header");
745f8b7b0a6SPieter Jansen van Vuuren return -EOPNOTSUPP;
746bef6e97dSPieter Jansen van Vuuren }
747f8b7b0a6SPieter Jansen van Vuuren
74873867881SPablo Neira Ayuso mask = ~act->mangle.mask;
74973867881SPablo Neira Ayuso exact = act->mangle.val;
750f8b7b0a6SPieter Jansen van Vuuren
751bef6e97dSPieter Jansen van Vuuren if (exact & ~mask) {
752bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit L4 action");
753f8b7b0a6SPieter Jansen van Vuuren return -EOPNOTSUPP;
754bef6e97dSPieter Jansen van Vuuren }
755f8b7b0a6SPieter Jansen van Vuuren
756f8b7b0a6SPieter Jansen van Vuuren nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val,
757f8b7b0a6SPieter Jansen van Vuuren set_tport->tp_port_mask);
758f8b7b0a6SPieter Jansen van Vuuren
759f8b7b0a6SPieter Jansen van Vuuren set_tport->reserved = cpu_to_be16(0);
76062d3f60bSPieter Jansen van Vuuren set_tport->head.jump_id = opcode;
76162d3f60bSPieter Jansen van Vuuren set_tport->head.len_lw = sizeof(*set_tport) >> NFP_FL_LW_SIZ;
762f8b7b0a6SPieter Jansen van Vuuren
763f8b7b0a6SPieter Jansen van Vuuren return 0;
764f8b7b0a6SPieter Jansen van Vuuren }
765f8b7b0a6SPieter Jansen van Vuuren
nfp_fl_csum_l4_to_flag(u8 ip_proto)766ed8f2b52SPieter Jansen van Vuuren static u32 nfp_fl_csum_l4_to_flag(u8 ip_proto)
767ed8f2b52SPieter Jansen van Vuuren {
768ed8f2b52SPieter Jansen van Vuuren switch (ip_proto) {
769ed8f2b52SPieter Jansen van Vuuren case 0:
770ed8f2b52SPieter Jansen van Vuuren /* Filter doesn't force proto match,
771ed8f2b52SPieter Jansen van Vuuren * both TCP and UDP will be updated if encountered
772ed8f2b52SPieter Jansen van Vuuren */
773ed8f2b52SPieter Jansen van Vuuren return TCA_CSUM_UPDATE_FLAG_TCP | TCA_CSUM_UPDATE_FLAG_UDP;
774ed8f2b52SPieter Jansen van Vuuren case IPPROTO_TCP:
775ed8f2b52SPieter Jansen van Vuuren return TCA_CSUM_UPDATE_FLAG_TCP;
776ed8f2b52SPieter Jansen van Vuuren case IPPROTO_UDP:
777ed8f2b52SPieter Jansen van Vuuren return TCA_CSUM_UPDATE_FLAG_UDP;
778ed8f2b52SPieter Jansen van Vuuren default:
779ed8f2b52SPieter Jansen van Vuuren /* All other protocols will be ignored by FW */
780ed8f2b52SPieter Jansen van Vuuren return 0;
781ed8f2b52SPieter Jansen van Vuuren }
782ed8f2b52SPieter Jansen van Vuuren }
783ed8f2b52SPieter Jansen van Vuuren
784eff07b42SPieter Jansen van Vuuren struct nfp_flower_pedit_acts {
785354b82bbSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
7864234d62cSPieter Jansen van Vuuren struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl;
787a3c6b063SPieter Jansen van Vuuren struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos;
788c0b1bd9aSPieter Jansen van Vuuren struct nfp_fl_set_ip4_addrs set_ip_addr;
789f8b7b0a6SPieter Jansen van Vuuren struct nfp_fl_set_tport set_tport;
790da83d8feSPieter Jansen van Vuuren struct nfp_fl_set_eth set_eth;
791eff07b42SPieter Jansen van Vuuren };
792eff07b42SPieter Jansen van Vuuren
793eff07b42SPieter Jansen van Vuuren static int
nfp_fl_commit_mangle(struct flow_rule * rule,char * nfp_action,int * a_len,struct nfp_flower_pedit_acts * set_act,u32 * csum_updated)794e75dc265SLouis Peens nfp_fl_commit_mangle(struct flow_rule *rule, char *nfp_action,
795eff07b42SPieter Jansen van Vuuren int *a_len, struct nfp_flower_pedit_acts *set_act,
796eff07b42SPieter Jansen van Vuuren u32 *csum_updated)
797eff07b42SPieter Jansen van Vuuren {
798d08c9e58SPieter Jansen van Vuuren size_t act_size = 0;
799ed8f2b52SPieter Jansen van Vuuren u8 ip_proto = 0;
800da83d8feSPieter Jansen van Vuuren
8018f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
8028f256622SPablo Neira Ayuso struct flow_match_basic match;
803ed8f2b52SPieter Jansen van Vuuren
8048f256622SPablo Neira Ayuso flow_rule_match_basic(rule, &match);
8058f256622SPablo Neira Ayuso ip_proto = match.key->ip_proto;
806ed8f2b52SPieter Jansen van Vuuren }
807ed8f2b52SPieter Jansen van Vuuren
808eff07b42SPieter Jansen van Vuuren if (set_act->set_eth.head.len_lw) {
809eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_eth);
810eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_eth, act_size);
811da83d8feSPieter Jansen van Vuuren *a_len += act_size;
812d08c9e58SPieter Jansen van Vuuren }
813eff07b42SPieter Jansen van Vuuren
814eff07b42SPieter Jansen van Vuuren if (set_act->set_ip_ttl_tos.head.len_lw) {
815a3c6b063SPieter Jansen van Vuuren nfp_action += act_size;
816eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip_ttl_tos);
817eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip_ttl_tos, act_size);
818a3c6b063SPieter Jansen van Vuuren *a_len += act_size;
819a3c6b063SPieter Jansen van Vuuren
820a3c6b063SPieter Jansen van Vuuren /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
821a3c6b063SPieter Jansen van Vuuren *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
822a3c6b063SPieter Jansen van Vuuren nfp_fl_csum_l4_to_flag(ip_proto);
823a3c6b063SPieter Jansen van Vuuren }
824eff07b42SPieter Jansen van Vuuren
825eff07b42SPieter Jansen van Vuuren if (set_act->set_ip_addr.head.len_lw) {
826d08c9e58SPieter Jansen van Vuuren nfp_action += act_size;
827eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip_addr);
828eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip_addr, act_size);
829c0b1bd9aSPieter Jansen van Vuuren *a_len += act_size;
830ed8f2b52SPieter Jansen van Vuuren
831ed8f2b52SPieter Jansen van Vuuren /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
832ed8f2b52SPieter Jansen van Vuuren *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
833ed8f2b52SPieter Jansen van Vuuren nfp_fl_csum_l4_to_flag(ip_proto);
834d08c9e58SPieter Jansen van Vuuren }
835eff07b42SPieter Jansen van Vuuren
836eff07b42SPieter Jansen van Vuuren if (set_act->set_ip6_tc_hl_fl.head.len_lw) {
8374234d62cSPieter Jansen van Vuuren nfp_action += act_size;
838eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip6_tc_hl_fl);
839eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip6_tc_hl_fl, act_size);
8404234d62cSPieter Jansen van Vuuren *a_len += act_size;
8414234d62cSPieter Jansen van Vuuren
8424234d62cSPieter Jansen van Vuuren /* Hardware will automatically fix TCP/UDP checksum. */
8434234d62cSPieter Jansen van Vuuren *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
8444234d62cSPieter Jansen van Vuuren }
845eff07b42SPieter Jansen van Vuuren
846eff07b42SPieter Jansen van Vuuren if (set_act->set_ip6_dst.head.len_lw &&
847eff07b42SPieter Jansen van Vuuren set_act->set_ip6_src.head.len_lw) {
848354b82bbSPieter Jansen van Vuuren /* TC compiles set src and dst IPv6 address as a single action,
849354b82bbSPieter Jansen van Vuuren * the hardware requires this to be 2 separate actions.
850354b82bbSPieter Jansen van Vuuren */
851d08c9e58SPieter Jansen van Vuuren nfp_action += act_size;
852eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip6_src);
853eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip6_src, act_size);
854354b82bbSPieter Jansen van Vuuren *a_len += act_size;
855354b82bbSPieter Jansen van Vuuren
856eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip6_dst);
857eff07b42SPieter Jansen van Vuuren memcpy(&nfp_action[sizeof(set_act->set_ip6_src)],
858eff07b42SPieter Jansen van Vuuren &set_act->set_ip6_dst, act_size);
859354b82bbSPieter Jansen van Vuuren *a_len += act_size;
860ed8f2b52SPieter Jansen van Vuuren
861ed8f2b52SPieter Jansen van Vuuren /* Hardware will automatically fix TCP/UDP checksum. */
862ed8f2b52SPieter Jansen van Vuuren *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
863eff07b42SPieter Jansen van Vuuren } else if (set_act->set_ip6_dst.head.len_lw) {
864d08c9e58SPieter Jansen van Vuuren nfp_action += act_size;
865eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip6_dst);
866eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip6_dst, act_size);
867354b82bbSPieter Jansen van Vuuren *a_len += act_size;
868ed8f2b52SPieter Jansen van Vuuren
869ed8f2b52SPieter Jansen van Vuuren /* Hardware will automatically fix TCP/UDP checksum. */
870ed8f2b52SPieter Jansen van Vuuren *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
871eff07b42SPieter Jansen van Vuuren } else if (set_act->set_ip6_src.head.len_lw) {
872d08c9e58SPieter Jansen van Vuuren nfp_action += act_size;
873eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_ip6_src);
874eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_ip6_src, act_size);
875354b82bbSPieter Jansen van Vuuren *a_len += act_size;
876ed8f2b52SPieter Jansen van Vuuren
877ed8f2b52SPieter Jansen van Vuuren /* Hardware will automatically fix TCP/UDP checksum. */
878ed8f2b52SPieter Jansen van Vuuren *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
879d08c9e58SPieter Jansen van Vuuren }
880eff07b42SPieter Jansen van Vuuren if (set_act->set_tport.head.len_lw) {
881d08c9e58SPieter Jansen van Vuuren nfp_action += act_size;
882eff07b42SPieter Jansen van Vuuren act_size = sizeof(set_act->set_tport);
883eff07b42SPieter Jansen van Vuuren memcpy(nfp_action, &set_act->set_tport, act_size);
884f8b7b0a6SPieter Jansen van Vuuren *a_len += act_size;
885ed8f2b52SPieter Jansen van Vuuren
886ed8f2b52SPieter Jansen van Vuuren /* Hardware will automatically fix TCP/UDP checksum. */
887ed8f2b52SPieter Jansen van Vuuren *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
888da83d8feSPieter Jansen van Vuuren }
889da83d8feSPieter Jansen van Vuuren
890da83d8feSPieter Jansen van Vuuren return 0;
891da83d8feSPieter Jansen van Vuuren }
892da83d8feSPieter Jansen van Vuuren
8931a1e586fSPieter Jansen van Vuuren static int
nfp_fl_pedit(const struct flow_action_entry * act,char * nfp_action,int * a_len,u32 * csum_updated,struct nfp_flower_pedit_acts * set_act,struct netlink_ext_ack * extack)894eff07b42SPieter Jansen van Vuuren nfp_fl_pedit(const struct flow_action_entry *act,
895e75dc265SLouis Peens char *nfp_action, int *a_len,
896bef6e97dSPieter Jansen van Vuuren u32 *csum_updated, struct nfp_flower_pedit_acts *set_act,
897bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
898eff07b42SPieter Jansen van Vuuren {
899eff07b42SPieter Jansen van Vuuren enum flow_action_mangle_base htype;
900eff07b42SPieter Jansen van Vuuren u32 offset;
901eff07b42SPieter Jansen van Vuuren
902eff07b42SPieter Jansen van Vuuren htype = act->mangle.htype;
903eff07b42SPieter Jansen van Vuuren offset = act->mangle.offset;
904eff07b42SPieter Jansen van Vuuren
905eff07b42SPieter Jansen van Vuuren switch (htype) {
906eff07b42SPieter Jansen van Vuuren case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
907bef6e97dSPieter Jansen van Vuuren return nfp_fl_set_eth(act, offset, &set_act->set_eth, extack);
908eff07b42SPieter Jansen van Vuuren case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
909eff07b42SPieter Jansen van Vuuren return nfp_fl_set_ip4(act, offset, &set_act->set_ip_addr,
910bef6e97dSPieter Jansen van Vuuren &set_act->set_ip_ttl_tos, extack);
911eff07b42SPieter Jansen van Vuuren case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
912eff07b42SPieter Jansen van Vuuren return nfp_fl_set_ip6(act, offset, &set_act->set_ip6_dst,
913eff07b42SPieter Jansen van Vuuren &set_act->set_ip6_src,
914bef6e97dSPieter Jansen van Vuuren &set_act->set_ip6_tc_hl_fl, extack);
915eff07b42SPieter Jansen van Vuuren case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
916eff07b42SPieter Jansen van Vuuren return nfp_fl_set_tport(act, offset, &set_act->set_tport,
917bef6e97dSPieter Jansen van Vuuren NFP_FL_ACTION_OPCODE_SET_TCP, extack);
918eff07b42SPieter Jansen van Vuuren case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
919eff07b42SPieter Jansen van Vuuren return nfp_fl_set_tport(act, offset, &set_act->set_tport,
920bef6e97dSPieter Jansen van Vuuren NFP_FL_ACTION_OPCODE_SET_UDP, extack);
921eff07b42SPieter Jansen van Vuuren default:
922bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported header");
923eff07b42SPieter Jansen van Vuuren return -EOPNOTSUPP;
924eff07b42SPieter Jansen van Vuuren }
925eff07b42SPieter Jansen van Vuuren }
926eff07b42SPieter Jansen van Vuuren
nfp_fl_meter(char * act_data)927147747ecSBaowen Zheng static struct nfp_fl_meter *nfp_fl_meter(char *act_data)
928147747ecSBaowen Zheng {
929147747ecSBaowen Zheng size_t act_size = sizeof(struct nfp_fl_meter);
930147747ecSBaowen Zheng struct nfp_fl_meter *meter_act;
931147747ecSBaowen Zheng
932147747ecSBaowen Zheng meter_act = (struct nfp_fl_meter *)act_data;
933147747ecSBaowen Zheng
934147747ecSBaowen Zheng memset(meter_act, 0, act_size);
935147747ecSBaowen Zheng
936147747ecSBaowen Zheng meter_act->head.jump_id = NFP_FL_ACTION_OPCODE_METER;
937147747ecSBaowen Zheng meter_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
938147747ecSBaowen Zheng
939147747ecSBaowen Zheng return meter_act;
940147747ecSBaowen Zheng }
941147747ecSBaowen Zheng
942147747ecSBaowen Zheng static int
nfp_flower_meter_action(struct nfp_app * app,const struct flow_action_entry * action,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,struct netlink_ext_ack * extack)943147747ecSBaowen Zheng nfp_flower_meter_action(struct nfp_app *app,
944147747ecSBaowen Zheng const struct flow_action_entry *action,
945147747ecSBaowen Zheng struct nfp_fl_payload *nfp_fl, int *a_len,
946147747ecSBaowen Zheng struct net_device *netdev,
947147747ecSBaowen Zheng struct netlink_ext_ack *extack)
948147747ecSBaowen Zheng {
949147747ecSBaowen Zheng struct nfp_fl_meter *fl_meter;
950147747ecSBaowen Zheng u32 meter_id;
951147747ecSBaowen Zheng
952147747ecSBaowen Zheng if (*a_len + sizeof(struct nfp_fl_meter) > NFP_FL_MAX_A_SIZ) {
953147747ecSBaowen Zheng NL_SET_ERR_MSG_MOD(extack,
954147747ecSBaowen Zheng "unsupported offload:meter action size beyond the allowed maximum");
955147747ecSBaowen Zheng return -EOPNOTSUPP;
956147747ecSBaowen Zheng }
957147747ecSBaowen Zheng
958147747ecSBaowen Zheng meter_id = action->hw_index;
959147747ecSBaowen Zheng if (!nfp_flower_search_meter_entry(app, meter_id)) {
960147747ecSBaowen Zheng NL_SET_ERR_MSG_MOD(extack,
961bdd6a89dSNiklas Söderlund "can not offload flow table with unsupported police action.");
962147747ecSBaowen Zheng return -EOPNOTSUPP;
963147747ecSBaowen Zheng }
964147747ecSBaowen Zheng
965147747ecSBaowen Zheng fl_meter = nfp_fl_meter(&nfp_fl->action_data[*a_len]);
966147747ecSBaowen Zheng *a_len += sizeof(struct nfp_fl_meter);
967147747ecSBaowen Zheng fl_meter->meter_id = cpu_to_be32(meter_id);
968147747ecSBaowen Zheng
969147747ecSBaowen Zheng return 0;
970147747ecSBaowen Zheng }
971147747ecSBaowen Zheng
972eff07b42SPieter Jansen van Vuuren static int
nfp_flower_output_action(struct nfp_app * app,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,bool last,enum nfp_flower_tun_type * tun_type,int * tun_out_cnt,int * out_cnt,u32 * csum_updated,bool pkt_host,struct netlink_ext_ack * extack)973eff07b42SPieter Jansen van Vuuren nfp_flower_output_action(struct nfp_app *app,
974eff07b42SPieter Jansen van Vuuren const struct flow_action_entry *act,
9757e24a593SJohn Hurley struct nfp_fl_payload *nfp_fl, int *a_len,
9767e24a593SJohn Hurley struct net_device *netdev, bool last,
9777e24a593SJohn Hurley enum nfp_flower_tun_type *tun_type, int *tun_out_cnt,
978f5c977eeSJohn Hurley int *out_cnt, u32 *csum_updated, bool pkt_host,
979bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
9807e24a593SJohn Hurley {
9817e24a593SJohn Hurley struct nfp_flower_priv *priv = app->priv;
9827e24a593SJohn Hurley struct nfp_fl_output *output;
9837e24a593SJohn Hurley int err, prelag_size;
9847e24a593SJohn Hurley
985ed8f2b52SPieter Jansen van Vuuren /* If csum_updated has not been reset by now, it means HW will
986ed8f2b52SPieter Jansen van Vuuren * incorrectly update csums when they are not requested.
987ed8f2b52SPieter Jansen van Vuuren */
988bef6e97dSPieter Jansen van Vuuren if (*csum_updated) {
989bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: set actions without updating checksums are not supported");
990ed8f2b52SPieter Jansen van Vuuren return -EOPNOTSUPP;
991bef6e97dSPieter Jansen van Vuuren }
992ed8f2b52SPieter Jansen van Vuuren
993bef6e97dSPieter Jansen van Vuuren if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) {
994bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: mirred output increases action list size beyond the allowed maximum");
9957e24a593SJohn Hurley return -EOPNOTSUPP;
996bef6e97dSPieter Jansen van Vuuren }
9977e24a593SJohn Hurley
9987e24a593SJohn Hurley output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
99973867881SPablo Neira Ayuso err = nfp_fl_output(app, output, act, nfp_fl, last, netdev, *tun_type,
1000f5c977eeSJohn Hurley tun_out_cnt, pkt_host, extack);
10017e24a593SJohn Hurley if (err)
10027e24a593SJohn Hurley return err;
10037e24a593SJohn Hurley
10047e24a593SJohn Hurley *a_len += sizeof(struct nfp_fl_output);
10057e24a593SJohn Hurley
1006e09303d3SLouis Peens if (priv->flower_en_feats & NFP_FL_ENABLE_LAG) {
10077e24a593SJohn Hurley /* nfp_fl_pre_lag returns -err or size of prelag action added.
10087e24a593SJohn Hurley * This will be 0 if it is not egressing to a lag dev.
10097e24a593SJohn Hurley */
1010bef6e97dSPieter Jansen van Vuuren prelag_size = nfp_fl_pre_lag(app, act, nfp_fl, *a_len, extack);
1011bef6e97dSPieter Jansen van Vuuren if (prelag_size < 0) {
10127e24a593SJohn Hurley return prelag_size;
1013bef6e97dSPieter Jansen van Vuuren } else if (prelag_size > 0 && (!last || *out_cnt)) {
1014bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: LAG action has to be last action in action list");
10157e24a593SJohn Hurley return -EOPNOTSUPP;
1016bef6e97dSPieter Jansen van Vuuren }
10177e24a593SJohn Hurley
10187e24a593SJohn Hurley *a_len += prelag_size;
10197e24a593SJohn Hurley }
10207e24a593SJohn Hurley (*out_cnt)++;
10217e24a593SJohn Hurley
10227e24a593SJohn Hurley return 0;
10237e24a593SJohn Hurley }
10247e24a593SJohn Hurley
10257e24a593SJohn Hurley static int
nfp_flower_loop_action(struct nfp_app * app,const struct flow_action_entry * act,struct flow_rule * rule,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,enum nfp_flower_tun_type * tun_type,int * tun_out_cnt,int * out_cnt,u32 * csum_updated,struct nfp_flower_pedit_acts * set_act,bool * pkt_host,struct netlink_ext_ack * extack,int act_idx)102673867881SPablo Neira Ayuso nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act,
1027e75dc265SLouis Peens struct flow_rule *rule,
10281a1e586fSPieter Jansen van Vuuren struct nfp_fl_payload *nfp_fl, int *a_len,
1029b27d6a95SJohn Hurley struct net_device *netdev,
10307e24a593SJohn Hurley enum nfp_flower_tun_type *tun_type, int *tun_out_cnt,
1031eff07b42SPieter Jansen van Vuuren int *out_cnt, u32 *csum_updated,
1032f5c977eeSJohn Hurley struct nfp_flower_pedit_acts *set_act, bool *pkt_host,
1033fccac580SPieter Jansen van Vuuren struct netlink_ext_ack *extack, int act_idx)
10341a1e586fSPieter Jansen van Vuuren {
1035147747ecSBaowen Zheng struct nfp_flower_priv *fl_priv = app->priv;
1036b27d6a95SJohn Hurley struct nfp_fl_pre_tunnel *pre_tun;
10371922c9a4SJohn Hurley struct nfp_fl_set_tun *set_tun;
10381a1e586fSPieter Jansen van Vuuren struct nfp_fl_push_vlan *psh_v;
1039a6eb1817SJohn Hurley struct nfp_fl_push_mpls *psh_m;
10401a1e586fSPieter Jansen van Vuuren struct nfp_fl_pop_vlan *pop_v;
104135b7c70cSJohn Hurley struct nfp_fl_pop_mpls *pop_m;
1042e03e47a3SJohn Hurley struct nfp_fl_set_mpls *set_m;
10431a1e586fSPieter Jansen van Vuuren int err;
10441a1e586fSPieter Jansen van Vuuren
104573867881SPablo Neira Ayuso switch (act->id) {
104673867881SPablo Neira Ayuso case FLOW_ACTION_DROP:
10471a1e586fSPieter Jansen van Vuuren nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP);
104873867881SPablo Neira Ayuso break;
1049f5c977eeSJohn Hurley case FLOW_ACTION_REDIRECT_INGRESS:
105073867881SPablo Neira Ayuso case FLOW_ACTION_REDIRECT:
105173867881SPablo Neira Ayuso err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
10527e24a593SJohn Hurley true, tun_type, tun_out_cnt,
1053f5c977eeSJohn Hurley out_cnt, csum_updated, *pkt_host,
1054f5c977eeSJohn Hurley extack);
10551a1e586fSPieter Jansen van Vuuren if (err)
10561a1e586fSPieter Jansen van Vuuren return err;
105773867881SPablo Neira Ayuso break;
1058f5c977eeSJohn Hurley case FLOW_ACTION_MIRRED_INGRESS:
105973867881SPablo Neira Ayuso case FLOW_ACTION_MIRRED:
106073867881SPablo Neira Ayuso err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
10617e24a593SJohn Hurley false, tun_type, tun_out_cnt,
1062f5c977eeSJohn Hurley out_cnt, csum_updated, *pkt_host,
1063f5c977eeSJohn Hurley extack);
10641a1e586fSPieter Jansen van Vuuren if (err)
10651a1e586fSPieter Jansen van Vuuren return err;
106673867881SPablo Neira Ayuso break;
106773867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_POP:
1068bef6e97dSPieter Jansen van Vuuren if (*a_len +
1069bef6e97dSPieter Jansen van Vuuren sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) {
1070bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop vlan");
10711a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
1072bef6e97dSPieter Jansen van Vuuren }
10731a1e586fSPieter Jansen van Vuuren
10741a1e586fSPieter Jansen van Vuuren pop_v = (struct nfp_fl_pop_vlan *)&nfp_fl->action_data[*a_len];
10751a1e586fSPieter Jansen van Vuuren nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_POPV);
10761a1e586fSPieter Jansen van Vuuren
10771a1e586fSPieter Jansen van Vuuren nfp_fl_pop_vlan(pop_v);
10781a1e586fSPieter Jansen van Vuuren *a_len += sizeof(struct nfp_fl_pop_vlan);
107973867881SPablo Neira Ayuso break;
108073867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_PUSH:
1081bef6e97dSPieter Jansen van Vuuren if (*a_len +
1082bef6e97dSPieter Jansen van Vuuren sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ) {
1083bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push vlan");
10841a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
1085bef6e97dSPieter Jansen van Vuuren }
10861a1e586fSPieter Jansen van Vuuren
10871a1e586fSPieter Jansen van Vuuren psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len];
10881a1e586fSPieter Jansen van Vuuren nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
10891a1e586fSPieter Jansen van Vuuren
109073867881SPablo Neira Ayuso nfp_fl_push_vlan(psh_v, act);
10911a1e586fSPieter Jansen van Vuuren *a_len += sizeof(struct nfp_fl_push_vlan);
109273867881SPablo Neira Ayuso break;
109373867881SPablo Neira Ayuso case FLOW_ACTION_TUNNEL_ENCAP: {
109473867881SPablo Neira Ayuso const struct ip_tunnel_info *ip_tun = act->tunnel;
1095224de549SLouis Peens
1096e75dc265SLouis Peens *tun_type = nfp_fl_get_tun_from_act(app, rule, act, act_idx);
1097bef6e97dSPieter Jansen van Vuuren if (*tun_type == NFP_FL_TUNNEL_NONE) {
1098bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel type in action list");
10993ca3059dSJohn Hurley return -EOPNOTSUPP;
1100bef6e97dSPieter Jansen van Vuuren }
11013ca3059dSJohn Hurley
1102bef6e97dSPieter Jansen van Vuuren if (ip_tun->mode & ~NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS) {
1103bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel flags in action list");
1104224de549SLouis Peens return -EOPNOTSUPP;
1105bef6e97dSPieter Jansen van Vuuren }
1106224de549SLouis Peens
1107b27d6a95SJohn Hurley /* Pre-tunnel action is required for tunnel encap.
1108b27d6a95SJohn Hurley * This checks for next hop entries on NFP.
1109b27d6a95SJohn Hurley * If none, the packet falls back before applying other actions.
1110b27d6a95SJohn Hurley */
1111b27d6a95SJohn Hurley if (*a_len + sizeof(struct nfp_fl_pre_tunnel) +
11121922c9a4SJohn Hurley sizeof(struct nfp_fl_set_tun) > NFP_FL_MAX_A_SIZ) {
1113bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at tunnel encap");
1114b27d6a95SJohn Hurley return -EOPNOTSUPP;
1115bef6e97dSPieter Jansen van Vuuren }
1116b27d6a95SJohn Hurley
1117b27d6a95SJohn Hurley pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len);
1118b27d6a95SJohn Hurley nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1119b27d6a95SJohn Hurley *a_len += sizeof(struct nfp_fl_pre_tunnel);
1120b27d6a95SJohn Hurley
1121bef6e97dSPieter Jansen van Vuuren err = nfp_fl_push_geneve_options(nfp_fl, a_len, act, extack);
11229e7c32feSPieter Jansen van Vuuren if (err)
11239e7c32feSPieter Jansen van Vuuren return err;
11249e7c32feSPieter Jansen van Vuuren
11253ca3059dSJohn Hurley set_tun = (void *)&nfp_fl->action_data[*a_len];
11261922c9a4SJohn Hurley err = nfp_fl_set_tun(app, set_tun, act, pre_tun, *tun_type,
11271922c9a4SJohn Hurley netdev, extack);
1128b27d6a95SJohn Hurley if (err)
1129b27d6a95SJohn Hurley return err;
11301922c9a4SJohn Hurley *a_len += sizeof(struct nfp_fl_set_tun);
113173867881SPablo Neira Ayuso }
113273867881SPablo Neira Ayuso break;
113373867881SPablo Neira Ayuso case FLOW_ACTION_TUNNEL_DECAP:
1134b27d6a95SJohn Hurley /* Tunnel decap is handled by default so accept action. */
1135b27d6a95SJohn Hurley return 0;
113673867881SPablo Neira Ayuso case FLOW_ACTION_MANGLE:
1137e75dc265SLouis Peens if (nfp_fl_pedit(act, &nfp_fl->action_data[*a_len],
1138bef6e97dSPieter Jansen van Vuuren a_len, csum_updated, set_act, extack))
1139da83d8feSPieter Jansen van Vuuren return -EOPNOTSUPP;
114073867881SPablo Neira Ayuso break;
114173867881SPablo Neira Ayuso case FLOW_ACTION_CSUM:
1142ed8f2b52SPieter Jansen van Vuuren /* csum action requests recalc of something we have not fixed */
1143bef6e97dSPieter Jansen van Vuuren if (act->csum_flags & ~*csum_updated) {
1144bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported csum update action in action list");
1145ed8f2b52SPieter Jansen van Vuuren return -EOPNOTSUPP;
1146bef6e97dSPieter Jansen van Vuuren }
1147ed8f2b52SPieter Jansen van Vuuren /* If we will correctly fix the csum we can remove it from the
1148ed8f2b52SPieter Jansen van Vuuren * csum update list. Which will later be used to check support.
1149ed8f2b52SPieter Jansen van Vuuren */
115073867881SPablo Neira Ayuso *csum_updated &= ~act->csum_flags;
115173867881SPablo Neira Ayuso break;
1152a6eb1817SJohn Hurley case FLOW_ACTION_MPLS_PUSH:
1153a6eb1817SJohn Hurley if (*a_len +
1154a6eb1817SJohn Hurley sizeof(struct nfp_fl_push_mpls) > NFP_FL_MAX_A_SIZ) {
1155a6eb1817SJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push MPLS");
1156a6eb1817SJohn Hurley return -EOPNOTSUPP;
1157a6eb1817SJohn Hurley }
1158a6eb1817SJohn Hurley
1159a6eb1817SJohn Hurley psh_m = (struct nfp_fl_push_mpls *)&nfp_fl->action_data[*a_len];
1160a6eb1817SJohn Hurley nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1161a6eb1817SJohn Hurley
1162a6eb1817SJohn Hurley err = nfp_fl_push_mpls(psh_m, act, extack);
1163a6eb1817SJohn Hurley if (err)
1164a6eb1817SJohn Hurley return err;
1165a6eb1817SJohn Hurley *a_len += sizeof(struct nfp_fl_push_mpls);
1166a6eb1817SJohn Hurley break;
116735b7c70cSJohn Hurley case FLOW_ACTION_MPLS_POP:
116835b7c70cSJohn Hurley if (*a_len +
116935b7c70cSJohn Hurley sizeof(struct nfp_fl_pop_mpls) > NFP_FL_MAX_A_SIZ) {
117035b7c70cSJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop MPLS");
117135b7c70cSJohn Hurley return -EOPNOTSUPP;
117235b7c70cSJohn Hurley }
117335b7c70cSJohn Hurley
117435b7c70cSJohn Hurley pop_m = (struct nfp_fl_pop_mpls *)&nfp_fl->action_data[*a_len];
117535b7c70cSJohn Hurley nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
117635b7c70cSJohn Hurley
117735b7c70cSJohn Hurley nfp_fl_pop_mpls(pop_m, act);
117835b7c70cSJohn Hurley *a_len += sizeof(struct nfp_fl_pop_mpls);
117935b7c70cSJohn Hurley break;
1180e03e47a3SJohn Hurley case FLOW_ACTION_MPLS_MANGLE:
1181e03e47a3SJohn Hurley if (*a_len +
1182e03e47a3SJohn Hurley sizeof(struct nfp_fl_set_mpls) > NFP_FL_MAX_A_SIZ) {
1183e03e47a3SJohn Hurley NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at set MPLS");
1184e03e47a3SJohn Hurley return -EOPNOTSUPP;
1185e03e47a3SJohn Hurley }
1186e03e47a3SJohn Hurley
1187e03e47a3SJohn Hurley set_m = (struct nfp_fl_set_mpls *)&nfp_fl->action_data[*a_len];
1188e03e47a3SJohn Hurley nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1189e03e47a3SJohn Hurley
1190e03e47a3SJohn Hurley nfp_fl_set_mpls(set_m, act);
1191e03e47a3SJohn Hurley *a_len += sizeof(struct nfp_fl_set_mpls);
1192e03e47a3SJohn Hurley break;
1193f5c977eeSJohn Hurley case FLOW_ACTION_PTYPE:
1194f5c977eeSJohn Hurley /* TC ptype skbedit sets PACKET_HOST for ingress redirect. */
1195f5c977eeSJohn Hurley if (act->ptype != PACKET_HOST)
1196f5c977eeSJohn Hurley return -EOPNOTSUPP;
1197f5c977eeSJohn Hurley
1198f5c977eeSJohn Hurley *pkt_host = true;
1199f5c977eeSJohn Hurley break;
1200147747ecSBaowen Zheng case FLOW_ACTION_POLICE:
1201147747ecSBaowen Zheng if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER)) {
1202147747ecSBaowen Zheng NL_SET_ERR_MSG_MOD(extack,
1203147747ecSBaowen Zheng "unsupported offload: unsupported police action in action list");
1204147747ecSBaowen Zheng return -EOPNOTSUPP;
1205147747ecSBaowen Zheng }
1206147747ecSBaowen Zheng
1207147747ecSBaowen Zheng err = nfp_flower_meter_action(app, act, nfp_fl, a_len, netdev,
1208147747ecSBaowen Zheng extack);
1209147747ecSBaowen Zheng if (err)
1210147747ecSBaowen Zheng return err;
1211147747ecSBaowen Zheng break;
121273867881SPablo Neira Ayuso default:
12131a1e586fSPieter Jansen van Vuuren /* Currently we do not handle any other actions. */
1214bef6e97dSPieter Jansen van Vuuren NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported action in action list");
12151a1e586fSPieter Jansen van Vuuren return -EOPNOTSUPP;
12161a1e586fSPieter Jansen van Vuuren }
12171a1e586fSPieter Jansen van Vuuren
12181a1e586fSPieter Jansen van Vuuren return 0;
12191a1e586fSPieter Jansen van Vuuren }
12201a1e586fSPieter Jansen van Vuuren
nfp_fl_check_mangle_start(struct flow_action * flow_act,int current_act_idx)1221eff07b42SPieter Jansen van Vuuren static bool nfp_fl_check_mangle_start(struct flow_action *flow_act,
1222eff07b42SPieter Jansen van Vuuren int current_act_idx)
1223eff07b42SPieter Jansen van Vuuren {
1224eff07b42SPieter Jansen van Vuuren struct flow_action_entry current_act;
1225eff07b42SPieter Jansen van Vuuren struct flow_action_entry prev_act;
1226eff07b42SPieter Jansen van Vuuren
1227eff07b42SPieter Jansen van Vuuren current_act = flow_act->entries[current_act_idx];
1228eff07b42SPieter Jansen van Vuuren if (current_act.id != FLOW_ACTION_MANGLE)
1229eff07b42SPieter Jansen van Vuuren return false;
1230eff07b42SPieter Jansen van Vuuren
1231eff07b42SPieter Jansen van Vuuren if (current_act_idx == 0)
1232eff07b42SPieter Jansen van Vuuren return true;
1233eff07b42SPieter Jansen van Vuuren
1234eff07b42SPieter Jansen van Vuuren prev_act = flow_act->entries[current_act_idx - 1];
1235eff07b42SPieter Jansen van Vuuren
1236eff07b42SPieter Jansen van Vuuren return prev_act.id != FLOW_ACTION_MANGLE;
1237eff07b42SPieter Jansen van Vuuren }
1238eff07b42SPieter Jansen van Vuuren
nfp_fl_check_mangle_end(struct flow_action * flow_act,int current_act_idx)1239eff07b42SPieter Jansen van Vuuren static bool nfp_fl_check_mangle_end(struct flow_action *flow_act,
1240eff07b42SPieter Jansen van Vuuren int current_act_idx)
1241eff07b42SPieter Jansen van Vuuren {
1242eff07b42SPieter Jansen van Vuuren struct flow_action_entry current_act;
1243eff07b42SPieter Jansen van Vuuren struct flow_action_entry next_act;
1244eff07b42SPieter Jansen van Vuuren
1245eff07b42SPieter Jansen van Vuuren current_act = flow_act->entries[current_act_idx];
1246eff07b42SPieter Jansen van Vuuren if (current_act.id != FLOW_ACTION_MANGLE)
1247eff07b42SPieter Jansen van Vuuren return false;
1248eff07b42SPieter Jansen van Vuuren
1249eff07b42SPieter Jansen van Vuuren if (current_act_idx == flow_act->num_entries)
1250eff07b42SPieter Jansen van Vuuren return true;
1251eff07b42SPieter Jansen van Vuuren
1252eff07b42SPieter Jansen van Vuuren next_act = flow_act->entries[current_act_idx + 1];
1253eff07b42SPieter Jansen van Vuuren
1254eff07b42SPieter Jansen van Vuuren return next_act.id != FLOW_ACTION_MANGLE;
1255eff07b42SPieter Jansen van Vuuren }
1256eff07b42SPieter Jansen van Vuuren
nfp_flower_compile_action(struct nfp_app * app,struct flow_rule * rule,struct net_device * netdev,struct nfp_fl_payload * nfp_flow,struct netlink_ext_ack * extack)12577e24a593SJohn Hurley int nfp_flower_compile_action(struct nfp_app *app,
1258e75dc265SLouis Peens struct flow_rule *rule,
12591a1e586fSPieter Jansen van Vuuren struct net_device *netdev,
1260bef6e97dSPieter Jansen van Vuuren struct nfp_fl_payload *nfp_flow,
1261bef6e97dSPieter Jansen van Vuuren struct netlink_ext_ack *extack)
12621a1e586fSPieter Jansen van Vuuren {
1263244cd96aSCong Wang int act_len, act_cnt, err, tun_out_cnt, out_cnt, i;
1264eff07b42SPieter Jansen van Vuuren struct nfp_flower_pedit_acts set_act;
1265b27d6a95SJohn Hurley enum nfp_flower_tun_type tun_type;
126673867881SPablo Neira Ayuso struct flow_action_entry *act;
1267f5c977eeSJohn Hurley bool pkt_host = false;
1268ed8f2b52SPieter Jansen van Vuuren u32 csum_updated = 0;
12691a1e586fSPieter Jansen van Vuuren
1270e75dc265SLouis Peens if (!flow_action_hw_stats_check(&rule->action, extack,
127136b4b92bSJakub Kicinski FLOW_ACTION_HW_STATS_DELAYED_BIT))
1272319a1d19SJiri Pirko return -EOPNOTSUPP;
1273319a1d19SJiri Pirko
12741a1e586fSPieter Jansen van Vuuren memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ);
12751a1e586fSPieter Jansen van Vuuren nfp_flow->meta.act_len = 0;
1276b27d6a95SJohn Hurley tun_type = NFP_FL_TUNNEL_NONE;
12771a1e586fSPieter Jansen van Vuuren act_len = 0;
12781a1e586fSPieter Jansen van Vuuren act_cnt = 0;
1279b27d6a95SJohn Hurley tun_out_cnt = 0;
12807e24a593SJohn Hurley out_cnt = 0;
12811a1e586fSPieter Jansen van Vuuren
1282e75dc265SLouis Peens flow_action_for_each(i, act, &rule->action) {
1283e75dc265SLouis Peens if (nfp_fl_check_mangle_start(&rule->action, i))
1284eff07b42SPieter Jansen van Vuuren memset(&set_act, 0, sizeof(set_act));
1285e75dc265SLouis Peens err = nfp_flower_loop_action(app, act, rule, nfp_flow, &act_len,
1286ed8f2b52SPieter Jansen van Vuuren netdev, &tun_type, &tun_out_cnt,
1287fccac580SPieter Jansen van Vuuren &out_cnt, &csum_updated,
1288f5c977eeSJohn Hurley &set_act, &pkt_host, extack, i);
12891a1e586fSPieter Jansen van Vuuren if (err)
12901a1e586fSPieter Jansen van Vuuren return err;
12911a1e586fSPieter Jansen van Vuuren act_cnt++;
1292e75dc265SLouis Peens if (nfp_fl_check_mangle_end(&rule->action, i))
1293e75dc265SLouis Peens nfp_fl_commit_mangle(rule,
1294eff07b42SPieter Jansen van Vuuren &nfp_flow->action_data[act_len],
1295eff07b42SPieter Jansen van Vuuren &act_len, &set_act, &csum_updated);
12961a1e586fSPieter Jansen van Vuuren }
12971a1e586fSPieter Jansen van Vuuren
12981a1e586fSPieter Jansen van Vuuren /* We optimise when the action list is small, this can unfortunately
12991a1e586fSPieter Jansen van Vuuren * not happen once we have more than one action in the action list.
13001a1e586fSPieter Jansen van Vuuren */
13011a1e586fSPieter Jansen van Vuuren if (act_cnt > 1)
13021a1e586fSPieter Jansen van Vuuren nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
13031a1e586fSPieter Jansen van Vuuren
13041a1e586fSPieter Jansen van Vuuren nfp_flow->meta.act_len = act_len;
13051a1e586fSPieter Jansen van Vuuren
13061a1e586fSPieter Jansen van Vuuren return 0;
13071a1e586fSPieter Jansen van Vuuren }
1308