18b474a9fSSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28b474a9fSSerhiy Boiko /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
38b474a9fSSerhiy Boiko
48b474a9fSSerhiy Boiko #include "prestera.h"
58b474a9fSSerhiy Boiko #include "prestera_acl.h"
647327e19SVolodymyr Mytnyk #include "prestera_flow.h"
78b474a9fSSerhiy Boiko #include "prestera_flower.h"
844af9571SMaksym Glubokiy #include "prestera_matchall.h"
98b474a9fSSerhiy Boiko
10604ba230SVolodymyr Mytnyk struct prestera_flower_template {
11604ba230SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
12fa5d824cSVolodymyr Mytnyk struct list_head list;
13fa5d824cSVolodymyr Mytnyk u32 chain_index;
14604ba230SVolodymyr Mytnyk };
15604ba230SVolodymyr Mytnyk
16b3ae2d35SVolodymyr Mytnyk static void
prestera_flower_template_free(struct prestera_flower_template * template)17b3ae2d35SVolodymyr Mytnyk prestera_flower_template_free(struct prestera_flower_template *template)
18604ba230SVolodymyr Mytnyk {
19fa5d824cSVolodymyr Mytnyk prestera_acl_ruleset_put(template->ruleset);
20fa5d824cSVolodymyr Mytnyk list_del(&template->list);
21fa5d824cSVolodymyr Mytnyk kfree(template);
22604ba230SVolodymyr Mytnyk }
23b3ae2d35SVolodymyr Mytnyk
prestera_flower_template_cleanup(struct prestera_flow_block * block)24b3ae2d35SVolodymyr Mytnyk void prestera_flower_template_cleanup(struct prestera_flow_block *block)
25b3ae2d35SVolodymyr Mytnyk {
26b3ae2d35SVolodymyr Mytnyk struct prestera_flower_template *template, *tmp;
27b3ae2d35SVolodymyr Mytnyk
28b3ae2d35SVolodymyr Mytnyk /* put the reference to all rulesets kept in tmpl create */
29b3ae2d35SVolodymyr Mytnyk list_for_each_entry_safe(template, tmp, &block->template_list, list)
30b3ae2d35SVolodymyr Mytnyk prestera_flower_template_free(template);
31604ba230SVolodymyr Mytnyk }
32604ba230SVolodymyr Mytnyk
33fa5d824cSVolodymyr Mytnyk static int
prestera_flower_parse_goto_action(struct prestera_flow_block * block,struct prestera_acl_rule * rule,u32 chain_index,const struct flow_action_entry * act)34fa5d824cSVolodymyr Mytnyk prestera_flower_parse_goto_action(struct prestera_flow_block *block,
35fa5d824cSVolodymyr Mytnyk struct prestera_acl_rule *rule,
36fa5d824cSVolodymyr Mytnyk u32 chain_index,
37fa5d824cSVolodymyr Mytnyk const struct flow_action_entry *act)
38fa5d824cSVolodymyr Mytnyk {
39fa5d824cSVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
40fa5d824cSVolodymyr Mytnyk
41fa5d824cSVolodymyr Mytnyk if (act->chain_index <= chain_index)
42fa5d824cSVolodymyr Mytnyk /* we can jump only forward */
43fa5d824cSVolodymyr Mytnyk return -EINVAL;
44fa5d824cSVolodymyr Mytnyk
45fa5d824cSVolodymyr Mytnyk if (rule->re_arg.jump.valid)
46fa5d824cSVolodymyr Mytnyk return -EEXIST;
47fa5d824cSVolodymyr Mytnyk
48fa5d824cSVolodymyr Mytnyk ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
49fa5d824cSVolodymyr Mytnyk act->chain_index);
50fa5d824cSVolodymyr Mytnyk if (IS_ERR(ruleset))
51fa5d824cSVolodymyr Mytnyk return PTR_ERR(ruleset);
52fa5d824cSVolodymyr Mytnyk
53fa5d824cSVolodymyr Mytnyk rule->re_arg.jump.valid = 1;
54fa5d824cSVolodymyr Mytnyk rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset);
55fa5d824cSVolodymyr Mytnyk
56fa5d824cSVolodymyr Mytnyk rule->jump_ruleset = ruleset;
57fa5d824cSVolodymyr Mytnyk
58fa5d824cSVolodymyr Mytnyk return 0;
59fa5d824cSVolodymyr Mytnyk }
60fa5d824cSVolodymyr Mytnyk
prestera_flower_parse_actions(struct prestera_flow_block * block,struct prestera_acl_rule * rule,struct flow_action * flow_action,u32 chain_index,struct netlink_ext_ack * extack)618b474a9fSSerhiy Boiko static int prestera_flower_parse_actions(struct prestera_flow_block *block,
628b474a9fSSerhiy Boiko struct prestera_acl_rule *rule,
638b474a9fSSerhiy Boiko struct flow_action *flow_action,
64fa5d824cSVolodymyr Mytnyk u32 chain_index,
658b474a9fSSerhiy Boiko struct netlink_ext_ack *extack)
668b474a9fSSerhiy Boiko {
678b474a9fSSerhiy Boiko const struct flow_action_entry *act;
68fa5d824cSVolodymyr Mytnyk int err, i;
698b474a9fSSerhiy Boiko
7047327e19SVolodymyr Mytnyk /* whole struct (rule->re_arg) must be initialized with 0 */
718b474a9fSSerhiy Boiko if (!flow_action_has_entries(flow_action))
728b474a9fSSerhiy Boiko return 0;
738b474a9fSSerhiy Boiko
74e8bd7025SVolodymyr Mytnyk if (!flow_action_mixed_hw_stats_check(flow_action, extack))
75e8bd7025SVolodymyr Mytnyk return -EOPNOTSUPP;
76e8bd7025SVolodymyr Mytnyk
77e8bd7025SVolodymyr Mytnyk act = flow_action_first_entry_get(flow_action);
78e8bd7025SVolodymyr Mytnyk if (act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED) {
79e8bd7025SVolodymyr Mytnyk /* Nothing to do */
80e8bd7025SVolodymyr Mytnyk } else if (act->hw_stats & FLOW_ACTION_HW_STATS_DELAYED) {
81e8bd7025SVolodymyr Mytnyk /* setup counter first */
82e8bd7025SVolodymyr Mytnyk rule->re_arg.count.valid = true;
83702e7014SMaksym Glubokiy err = prestera_acl_chain_to_client(chain_index, block->ingress,
84e8bd7025SVolodymyr Mytnyk &rule->re_arg.count.client);
85e8bd7025SVolodymyr Mytnyk if (err)
86e8bd7025SVolodymyr Mytnyk return err;
87e8bd7025SVolodymyr Mytnyk } else {
88e8bd7025SVolodymyr Mytnyk NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type");
89e8bd7025SVolodymyr Mytnyk return -EOPNOTSUPP;
90e8bd7025SVolodymyr Mytnyk }
91e8bd7025SVolodymyr Mytnyk
928b474a9fSSerhiy Boiko flow_action_for_each(i, act, flow_action) {
938b474a9fSSerhiy Boiko switch (act->id) {
948b474a9fSSerhiy Boiko case FLOW_ACTION_ACCEPT:
9547327e19SVolodymyr Mytnyk if (rule->re_arg.accept.valid)
9647327e19SVolodymyr Mytnyk return -EEXIST;
9747327e19SVolodymyr Mytnyk
9847327e19SVolodymyr Mytnyk rule->re_arg.accept.valid = 1;
998b474a9fSSerhiy Boiko break;
1008b474a9fSSerhiy Boiko case FLOW_ACTION_DROP:
10147327e19SVolodymyr Mytnyk if (rule->re_arg.drop.valid)
10247327e19SVolodymyr Mytnyk return -EEXIST;
10347327e19SVolodymyr Mytnyk
10447327e19SVolodymyr Mytnyk rule->re_arg.drop.valid = 1;
1058b474a9fSSerhiy Boiko break;
1068b474a9fSSerhiy Boiko case FLOW_ACTION_TRAP:
10747327e19SVolodymyr Mytnyk if (rule->re_arg.trap.valid)
10847327e19SVolodymyr Mytnyk return -EEXIST;
10947327e19SVolodymyr Mytnyk
11047327e19SVolodymyr Mytnyk rule->re_arg.trap.valid = 1;
1118b474a9fSSerhiy Boiko break;
112dde2daa0SVolodymyr Mytnyk case FLOW_ACTION_POLICE:
113dde2daa0SVolodymyr Mytnyk if (rule->re_arg.police.valid)
114dde2daa0SVolodymyr Mytnyk return -EEXIST;
115dde2daa0SVolodymyr Mytnyk
116dde2daa0SVolodymyr Mytnyk rule->re_arg.police.valid = 1;
117dde2daa0SVolodymyr Mytnyk rule->re_arg.police.rate =
118dde2daa0SVolodymyr Mytnyk act->police.rate_bytes_ps;
119dde2daa0SVolodymyr Mytnyk rule->re_arg.police.burst = act->police.burst;
1203c6aca33SMaksym Glubokiy rule->re_arg.police.ingress = block->ingress;
121dde2daa0SVolodymyr Mytnyk break;
122fa5d824cSVolodymyr Mytnyk case FLOW_ACTION_GOTO:
123fa5d824cSVolodymyr Mytnyk err = prestera_flower_parse_goto_action(block, rule,
124fa5d824cSVolodymyr Mytnyk chain_index,
125fa5d824cSVolodymyr Mytnyk act);
126fa5d824cSVolodymyr Mytnyk if (err)
127fa5d824cSVolodymyr Mytnyk return err;
128fa5d824cSVolodymyr Mytnyk break;
1298b474a9fSSerhiy Boiko default:
1308b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
1318b474a9fSSerhiy Boiko pr_err("Unsupported action\n");
1328b474a9fSSerhiy Boiko return -EOPNOTSUPP;
1338b474a9fSSerhiy Boiko }
1348b474a9fSSerhiy Boiko }
1358b474a9fSSerhiy Boiko
1368b474a9fSSerhiy Boiko return 0;
1378b474a9fSSerhiy Boiko }
1388b474a9fSSerhiy Boiko
prestera_flower_parse_meta(struct prestera_acl_rule * rule,struct flow_cls_offload * f,struct prestera_flow_block * block)1398b474a9fSSerhiy Boiko static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
1408b474a9fSSerhiy Boiko struct flow_cls_offload *f,
1418b474a9fSSerhiy Boiko struct prestera_flow_block *block)
14271c47aa9SMaksym Glubokiy {
14371c47aa9SMaksym Glubokiy struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
14447327e19SVolodymyr Mytnyk struct prestera_acl_match *r_match = &rule->re_key.match;
14547327e19SVolodymyr Mytnyk struct prestera_port *port;
1468b474a9fSSerhiy Boiko struct net_device *ingress_dev;
1478b474a9fSSerhiy Boiko struct flow_match_meta match;
14847327e19SVolodymyr Mytnyk __be16 key, mask;
1498b474a9fSSerhiy Boiko
1508b474a9fSSerhiy Boiko flow_rule_match_meta(f_rule, &match);
151f4356947SIdo Schimmel
152f4356947SIdo Schimmel if (match.mask->l2_miss) {
153f4356947SIdo Schimmel NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on \"l2_miss\"");
154f4356947SIdo Schimmel return -EOPNOTSUPP;
155f4356947SIdo Schimmel }
156f4356947SIdo Schimmel
1578b474a9fSSerhiy Boiko if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
1588b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD(f->common.extack,
1598b474a9fSSerhiy Boiko "Unsupported ingress ifindex mask");
1608b474a9fSSerhiy Boiko return -EINVAL;
1618b474a9fSSerhiy Boiko }
1628b474a9fSSerhiy Boiko
16347327e19SVolodymyr Mytnyk ingress_dev = __dev_get_by_index(block->net,
1648b474a9fSSerhiy Boiko match.key->ingress_ifindex);
1658b474a9fSSerhiy Boiko if (!ingress_dev) {
1668b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD(f->common.extack,
1678b474a9fSSerhiy Boiko "Can't find specified ingress port to match on");
1688b474a9fSSerhiy Boiko return -EINVAL;
1698b474a9fSSerhiy Boiko }
1708b474a9fSSerhiy Boiko
1718b474a9fSSerhiy Boiko if (!prestera_netdev_check(ingress_dev)) {
1728b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD(f->common.extack,
1738b474a9fSSerhiy Boiko "Can't match on switchdev ingress port");
1748b474a9fSSerhiy Boiko return -EINVAL;
1758b474a9fSSerhiy Boiko }
1768b474a9fSSerhiy Boiko port = netdev_priv(ingress_dev);
1778b474a9fSSerhiy Boiko
1781e20904eSMaksym Glubokiy mask = htons(0x1FFF << 3);
1791e20904eSMaksym Glubokiy key = htons(port->hw_id << 3);
18047327e19SVolodymyr Mytnyk rule_match_set(r_match->key, SYS_PORT, key);
18147327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, SYS_PORT, mask);
1828b474a9fSSerhiy Boiko
1831e20904eSMaksym Glubokiy mask = htons(0x3FF);
18447327e19SVolodymyr Mytnyk key = htons(port->dev_id);
18547327e19SVolodymyr Mytnyk rule_match_set(r_match->key, SYS_DEV, key);
18647327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, SYS_DEV, mask);
18747327e19SVolodymyr Mytnyk
18847327e19SVolodymyr Mytnyk return 0;
1898b474a9fSSerhiy Boiko }
1908b474a9fSSerhiy Boiko
prestera_flower_parse(struct prestera_flow_block * block,struct prestera_acl_rule * rule,struct flow_cls_offload * f)1918b474a9fSSerhiy Boiko static int prestera_flower_parse(struct prestera_flow_block *block,
1928b474a9fSSerhiy Boiko struct prestera_acl_rule *rule,
1938b474a9fSSerhiy Boiko struct flow_cls_offload *f)
19471c47aa9SMaksym Glubokiy {
19571c47aa9SMaksym Glubokiy struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
1968b474a9fSSerhiy Boiko struct flow_dissector *dissector = f_rule->match.dissector;
19747327e19SVolodymyr Mytnyk struct prestera_acl_match *r_match = &rule->re_key.match;
19847327e19SVolodymyr Mytnyk __be16 n_proto_mask = 0;
19947327e19SVolodymyr Mytnyk __be16 n_proto_key = 0;
2008b474a9fSSerhiy Boiko u16 addr_type = 0;
2018b474a9fSSerhiy Boiko u8 ip_proto = 0;
2028b474a9fSSerhiy Boiko int err;
2038b474a9fSSerhiy Boiko
2048b474a9fSSerhiy Boiko if (dissector->used_keys &
2052b3082c6SRatheesh Kannoth ~(BIT_ULL(FLOW_DISSECTOR_KEY_META) |
2062b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
2072b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
2082b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
2092b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
2102b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
2112b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_ICMP) |
2122b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
2132b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
2142b3082c6SRatheesh Kannoth BIT_ULL(FLOW_DISSECTOR_KEY_VLAN))) {
2158b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
2168b474a9fSSerhiy Boiko return -EOPNOTSUPP;
2178b474a9fSSerhiy Boiko }
2188b474a9fSSerhiy Boiko
2198b474a9fSSerhiy Boiko prestera_acl_rule_priority_set(rule, f->common.prio);
2208b474a9fSSerhiy Boiko
2218b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_META)) {
2228b474a9fSSerhiy Boiko err = prestera_flower_parse_meta(rule, f, block);
2238b474a9fSSerhiy Boiko if (err)
2248b474a9fSSerhiy Boiko return err;
2258b474a9fSSerhiy Boiko }
2268b474a9fSSerhiy Boiko
2278b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_CONTROL)) {
2288b474a9fSSerhiy Boiko struct flow_match_control match;
2298b474a9fSSerhiy Boiko
2308b474a9fSSerhiy Boiko flow_rule_match_control(f_rule, &match);
2318b474a9fSSerhiy Boiko addr_type = match.key->addr_type;
232*f8a5ea8cSAsbjørn Sloth Tønnesen
233*f8a5ea8cSAsbjørn Sloth Tønnesen if (flow_rule_has_control_flags(match.mask->flags,
234*f8a5ea8cSAsbjørn Sloth Tønnesen f->common.extack))
235*f8a5ea8cSAsbjørn Sloth Tønnesen return -EOPNOTSUPP;
2368b474a9fSSerhiy Boiko }
2378b474a9fSSerhiy Boiko
2388b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_BASIC)) {
2398b474a9fSSerhiy Boiko struct flow_match_basic match;
2408b474a9fSSerhiy Boiko
2418b474a9fSSerhiy Boiko flow_rule_match_basic(f_rule, &match);
24247327e19SVolodymyr Mytnyk n_proto_key = match.key->n_proto;
24347327e19SVolodymyr Mytnyk n_proto_mask = match.mask->n_proto;
2448b474a9fSSerhiy Boiko
24547327e19SVolodymyr Mytnyk if (ntohs(match.key->n_proto) == ETH_P_ALL) {
2468b474a9fSSerhiy Boiko n_proto_key = 0;
2478b474a9fSSerhiy Boiko n_proto_mask = 0;
2488b474a9fSSerhiy Boiko }
2498b474a9fSSerhiy Boiko
25047327e19SVolodymyr Mytnyk rule_match_set(r_match->key, ETH_TYPE, n_proto_key);
25147327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask);
2528b474a9fSSerhiy Boiko
25347327e19SVolodymyr Mytnyk rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto);
25447327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto);
2558b474a9fSSerhiy Boiko ip_proto = match.key->ip_proto;
2568b474a9fSSerhiy Boiko }
2578b474a9fSSerhiy Boiko
2588b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
2598b474a9fSSerhiy Boiko struct flow_match_eth_addrs match;
2608b474a9fSSerhiy Boiko
2618b474a9fSSerhiy Boiko flow_rule_match_eth_addrs(f_rule, &match);
2628b474a9fSSerhiy Boiko
26347327e19SVolodymyr Mytnyk /* DA key, mask */
26447327e19SVolodymyr Mytnyk rule_match_set_n(r_match->key,
26547327e19SVolodymyr Mytnyk ETH_DMAC_0, &match.key->dst[0], 4);
26647327e19SVolodymyr Mytnyk rule_match_set_n(r_match->key,
26747327e19SVolodymyr Mytnyk ETH_DMAC_1, &match.key->dst[4], 2);
2688b474a9fSSerhiy Boiko
26947327e19SVolodymyr Mytnyk rule_match_set_n(r_match->mask,
27047327e19SVolodymyr Mytnyk ETH_DMAC_0, &match.mask->dst[0], 4);
27147327e19SVolodymyr Mytnyk rule_match_set_n(r_match->mask,
27247327e19SVolodymyr Mytnyk ETH_DMAC_1, &match.mask->dst[4], 2);
27347327e19SVolodymyr Mytnyk
27447327e19SVolodymyr Mytnyk /* SA key, mask */
27547327e19SVolodymyr Mytnyk rule_match_set_n(r_match->key,
27647327e19SVolodymyr Mytnyk ETH_SMAC_0, &match.key->src[0], 4);
27747327e19SVolodymyr Mytnyk rule_match_set_n(r_match->key,
27847327e19SVolodymyr Mytnyk ETH_SMAC_1, &match.key->src[4], 2);
27947327e19SVolodymyr Mytnyk
28047327e19SVolodymyr Mytnyk rule_match_set_n(r_match->mask,
28147327e19SVolodymyr Mytnyk ETH_SMAC_0, &match.mask->src[0], 4);
28247327e19SVolodymyr Mytnyk rule_match_set_n(r_match->mask,
28347327e19SVolodymyr Mytnyk ETH_SMAC_1, &match.mask->src[4], 2);
2848b474a9fSSerhiy Boiko }
2858b474a9fSSerhiy Boiko
2868b474a9fSSerhiy Boiko if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2878b474a9fSSerhiy Boiko struct flow_match_ipv4_addrs match;
2888b474a9fSSerhiy Boiko
2898b474a9fSSerhiy Boiko flow_rule_match_ipv4_addrs(f_rule, &match);
2908b474a9fSSerhiy Boiko
29147327e19SVolodymyr Mytnyk rule_match_set(r_match->key, IP_SRC, match.key->src);
29247327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, IP_SRC, match.mask->src);
2938b474a9fSSerhiy Boiko
29447327e19SVolodymyr Mytnyk rule_match_set(r_match->key, IP_DST, match.key->dst);
29547327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, IP_DST, match.mask->dst);
2968b474a9fSSerhiy Boiko }
2978b474a9fSSerhiy Boiko
2988b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) {
2998b474a9fSSerhiy Boiko struct flow_match_ports match;
3008b474a9fSSerhiy Boiko
3018b474a9fSSerhiy Boiko if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
3028b474a9fSSerhiy Boiko NL_SET_ERR_MSG_MOD
3038b474a9fSSerhiy Boiko (f->common.extack,
3048b474a9fSSerhiy Boiko "Only UDP and TCP keys are supported");
3058b474a9fSSerhiy Boiko return -EINVAL;
3068b474a9fSSerhiy Boiko }
3078b474a9fSSerhiy Boiko
3088b474a9fSSerhiy Boiko flow_rule_match_ports(f_rule, &match);
3098b474a9fSSerhiy Boiko
31047327e19SVolodymyr Mytnyk rule_match_set(r_match->key, L4_PORT_SRC, match.key->src);
31147327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src);
3128b474a9fSSerhiy Boiko
31347327e19SVolodymyr Mytnyk rule_match_set(r_match->key, L4_PORT_DST, match.key->dst);
31447327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst);
3158b474a9fSSerhiy Boiko }
3168b474a9fSSerhiy Boiko
317551871bfSMaksym Glubokiy if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
318551871bfSMaksym Glubokiy struct flow_match_ports_range match;
319551871bfSMaksym Glubokiy __be32 tp_key, tp_mask;
320551871bfSMaksym Glubokiy
321551871bfSMaksym Glubokiy flow_rule_match_ports_range(f_rule, &match);
322551871bfSMaksym Glubokiy
323551871bfSMaksym Glubokiy /* src port range (min, max) */
324551871bfSMaksym Glubokiy tp_key = htonl(ntohs(match.key->tp_min.src) |
325551871bfSMaksym Glubokiy (ntohs(match.key->tp_max.src) << 16));
326551871bfSMaksym Glubokiy tp_mask = htonl(ntohs(match.mask->tp_min.src) |
327551871bfSMaksym Glubokiy (ntohs(match.mask->tp_max.src) << 16));
328551871bfSMaksym Glubokiy rule_match_set(r_match->key, L4_PORT_RANGE_SRC, tp_key);
329551871bfSMaksym Glubokiy rule_match_set(r_match->mask, L4_PORT_RANGE_SRC, tp_mask);
330551871bfSMaksym Glubokiy
331551871bfSMaksym Glubokiy /* dst port range (min, max) */
332551871bfSMaksym Glubokiy tp_key = htonl(ntohs(match.key->tp_min.dst) |
333551871bfSMaksym Glubokiy (ntohs(match.key->tp_max.dst) << 16));
334551871bfSMaksym Glubokiy tp_mask = htonl(ntohs(match.mask->tp_min.dst) |
335551871bfSMaksym Glubokiy (ntohs(match.mask->tp_max.dst) << 16));
336551871bfSMaksym Glubokiy rule_match_set(r_match->key, L4_PORT_RANGE_DST, tp_key);
337551871bfSMaksym Glubokiy rule_match_set(r_match->mask, L4_PORT_RANGE_DST, tp_mask);
338551871bfSMaksym Glubokiy }
339551871bfSMaksym Glubokiy
3408b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) {
3418b474a9fSSerhiy Boiko struct flow_match_vlan match;
3428b474a9fSSerhiy Boiko
3438b474a9fSSerhiy Boiko flow_rule_match_vlan(f_rule, &match);
3448b474a9fSSerhiy Boiko
3458b474a9fSSerhiy Boiko if (match.mask->vlan_id != 0) {
34647327e19SVolodymyr Mytnyk __be16 key = cpu_to_be16(match.key->vlan_id);
34747327e19SVolodymyr Mytnyk __be16 mask = cpu_to_be16(match.mask->vlan_id);
34847327e19SVolodymyr Mytnyk
34947327e19SVolodymyr Mytnyk rule_match_set(r_match->key, VLAN_ID, key);
35047327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, VLAN_ID, mask);
3518b474a9fSSerhiy Boiko }
3528b474a9fSSerhiy Boiko
35347327e19SVolodymyr Mytnyk rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid);
35447327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid);
3558b474a9fSSerhiy Boiko }
3568b474a9fSSerhiy Boiko
3578b474a9fSSerhiy Boiko if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) {
3588b474a9fSSerhiy Boiko struct flow_match_icmp match;
3598b474a9fSSerhiy Boiko
3608b474a9fSSerhiy Boiko flow_rule_match_icmp(f_rule, &match);
3618b474a9fSSerhiy Boiko
36247327e19SVolodymyr Mytnyk rule_match_set(r_match->key, ICMP_TYPE, match.key->type);
36347327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type);
3648b474a9fSSerhiy Boiko
36547327e19SVolodymyr Mytnyk rule_match_set(r_match->key, ICMP_CODE, match.key->code);
36647327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, ICMP_CODE, match.mask->code);
3678b474a9fSSerhiy Boiko }
3688b474a9fSSerhiy Boiko
36947327e19SVolodymyr Mytnyk return prestera_flower_parse_actions(block, rule, &f->rule->action,
370fa5d824cSVolodymyr Mytnyk f->common.chain_index,
3718b474a9fSSerhiy Boiko f->common.extack);
3728b474a9fSSerhiy Boiko }
3738b474a9fSSerhiy Boiko
prestera_flower_prio_check(struct prestera_flow_block * block,struct flow_cls_offload * f)37444af9571SMaksym Glubokiy static int prestera_flower_prio_check(struct prestera_flow_block *block,
37544af9571SMaksym Glubokiy struct flow_cls_offload *f)
37644af9571SMaksym Glubokiy {
37744af9571SMaksym Glubokiy u32 mall_prio_min;
37844af9571SMaksym Glubokiy u32 mall_prio_max;
37944af9571SMaksym Glubokiy int err;
38044af9571SMaksym Glubokiy
38144af9571SMaksym Glubokiy err = prestera_mall_prio_get(block, &mall_prio_min, &mall_prio_max);
38244af9571SMaksym Glubokiy if (err == -ENOENT)
38344af9571SMaksym Glubokiy /* No matchall filters installed on this chain. */
38444af9571SMaksym Glubokiy return 0;
38544af9571SMaksym Glubokiy
38644af9571SMaksym Glubokiy if (err) {
38744af9571SMaksym Glubokiy NL_SET_ERR_MSG(f->common.extack, "Failed to get matchall priorities");
38844af9571SMaksym Glubokiy return err;
38944af9571SMaksym Glubokiy }
39044af9571SMaksym Glubokiy
39144af9571SMaksym Glubokiy if (f->common.prio <= mall_prio_max && block->ingress) {
39244af9571SMaksym Glubokiy NL_SET_ERR_MSG(f->common.extack,
39344af9571SMaksym Glubokiy "Failed to add in front of existing matchall rules");
39444af9571SMaksym Glubokiy return -EOPNOTSUPP;
39544af9571SMaksym Glubokiy }
39644af9571SMaksym Glubokiy if (f->common.prio >= mall_prio_min && !block->ingress) {
39744af9571SMaksym Glubokiy NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing matchall rules");
39844af9571SMaksym Glubokiy return -EOPNOTSUPP;
39944af9571SMaksym Glubokiy }
40044af9571SMaksym Glubokiy
40144af9571SMaksym Glubokiy return 0;
40244af9571SMaksym Glubokiy }
40344af9571SMaksym Glubokiy
prestera_flower_prio_get(struct prestera_flow_block * block,u32 chain_index,u32 * prio_min,u32 * prio_max)40444af9571SMaksym Glubokiy int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index,
40544af9571SMaksym Glubokiy u32 *prio_min, u32 *prio_max)
40644af9571SMaksym Glubokiy {
40744af9571SMaksym Glubokiy struct prestera_acl_ruleset *ruleset;
40844af9571SMaksym Glubokiy
40944af9571SMaksym Glubokiy ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, chain_index);
41044af9571SMaksym Glubokiy if (IS_ERR(ruleset))
41144af9571SMaksym Glubokiy return PTR_ERR(ruleset);
41244af9571SMaksym Glubokiy
41344af9571SMaksym Glubokiy prestera_acl_ruleset_prio_get(ruleset, prio_min, prio_max);
41444af9571SMaksym Glubokiy return 0;
41544af9571SMaksym Glubokiy }
41644af9571SMaksym Glubokiy
prestera_flower_replace(struct prestera_flow_block * block,struct flow_cls_offload * f)4178b474a9fSSerhiy Boiko int prestera_flower_replace(struct prestera_flow_block *block,
4188b474a9fSSerhiy Boiko struct flow_cls_offload *f)
4198b474a9fSSerhiy Boiko {
42047327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
42147327e19SVolodymyr Mytnyk struct prestera_acl *acl = block->sw->acl;
4228b474a9fSSerhiy Boiko struct prestera_acl_rule *rule;
4238b474a9fSSerhiy Boiko int err;
4248b474a9fSSerhiy Boiko
42544af9571SMaksym Glubokiy err = prestera_flower_prio_check(block, f);
42644af9571SMaksym Glubokiy if (err)
42744af9571SMaksym Glubokiy return err;
42844af9571SMaksym Glubokiy
429fa5d824cSVolodymyr Mytnyk ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
43047327e19SVolodymyr Mytnyk if (IS_ERR(ruleset))
43147327e19SVolodymyr Mytnyk return PTR_ERR(ruleset);
43247327e19SVolodymyr Mytnyk
43347327e19SVolodymyr Mytnyk /* increments the ruleset reference */
434fa5d824cSVolodymyr Mytnyk rule = prestera_acl_rule_create(ruleset, f->cookie,
435fa5d824cSVolodymyr Mytnyk f->common.chain_index);
43647327e19SVolodymyr Mytnyk if (IS_ERR(rule)) {
43747327e19SVolodymyr Mytnyk err = PTR_ERR(rule);
43847327e19SVolodymyr Mytnyk goto err_rule_create;
43947327e19SVolodymyr Mytnyk }
4408b474a9fSSerhiy Boiko
4418b474a9fSSerhiy Boiko err = prestera_flower_parse(block, rule, f);
4428b474a9fSSerhiy Boiko if (err)
44347327e19SVolodymyr Mytnyk goto err_rule_add;
4448b474a9fSSerhiy Boiko
44547327e19SVolodymyr Mytnyk if (!prestera_acl_ruleset_is_offload(ruleset)) {
44647327e19SVolodymyr Mytnyk err = prestera_acl_ruleset_offload(ruleset);
44747327e19SVolodymyr Mytnyk if (err)
44847327e19SVolodymyr Mytnyk goto err_ruleset_offload;
44947327e19SVolodymyr Mytnyk }
45047327e19SVolodymyr Mytnyk
45147327e19SVolodymyr Mytnyk err = prestera_acl_rule_add(block->sw, rule);
4528b474a9fSSerhiy Boiko if (err)
4538b474a9fSSerhiy Boiko goto err_rule_add;
4548b474a9fSSerhiy Boiko
45547327e19SVolodymyr Mytnyk prestera_acl_ruleset_put(ruleset);
4568b474a9fSSerhiy Boiko return 0;
4578b474a9fSSerhiy Boiko
45847327e19SVolodymyr Mytnyk err_ruleset_offload:
4598b474a9fSSerhiy Boiko err_rule_add:
4608b474a9fSSerhiy Boiko prestera_acl_rule_destroy(rule);
46147327e19SVolodymyr Mytnyk err_rule_create:
46247327e19SVolodymyr Mytnyk prestera_acl_ruleset_put(ruleset);
4638b474a9fSSerhiy Boiko return err;
4648b474a9fSSerhiy Boiko }
4658b474a9fSSerhiy Boiko
prestera_flower_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)4668b474a9fSSerhiy Boiko void prestera_flower_destroy(struct prestera_flow_block *block,
4678b474a9fSSerhiy Boiko struct flow_cls_offload *f)
4688b474a9fSSerhiy Boiko {
46947327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
4708b474a9fSSerhiy Boiko struct prestera_acl_rule *rule;
4718b474a9fSSerhiy Boiko
472fa5d824cSVolodymyr Mytnyk ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
473fa5d824cSVolodymyr Mytnyk f->common.chain_index);
47447327e19SVolodymyr Mytnyk if (IS_ERR(ruleset))
47547327e19SVolodymyr Mytnyk return;
47647327e19SVolodymyr Mytnyk
47747327e19SVolodymyr Mytnyk rule = prestera_acl_rule_lookup(ruleset, f->cookie);
4788b474a9fSSerhiy Boiko if (rule) {
47947327e19SVolodymyr Mytnyk prestera_acl_rule_del(block->sw, rule);
4808b474a9fSSerhiy Boiko prestera_acl_rule_destroy(rule);
4818b474a9fSSerhiy Boiko }
48247327e19SVolodymyr Mytnyk prestera_acl_ruleset_put(ruleset);
4838b474a9fSSerhiy Boiko }
4848b474a9fSSerhiy Boiko
prestera_flower_tmplt_create(struct prestera_flow_block * block,struct flow_cls_offload * f)485604ba230SVolodymyr Mytnyk int prestera_flower_tmplt_create(struct prestera_flow_block *block,
486604ba230SVolodymyr Mytnyk struct flow_cls_offload *f)
487604ba230SVolodymyr Mytnyk {
488604ba230SVolodymyr Mytnyk struct prestera_flower_template *template;
489604ba230SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
490604ba230SVolodymyr Mytnyk struct prestera_acl_rule rule;
491604ba230SVolodymyr Mytnyk int err;
492604ba230SVolodymyr Mytnyk
493604ba230SVolodymyr Mytnyk memset(&rule, 0, sizeof(rule));
494604ba230SVolodymyr Mytnyk err = prestera_flower_parse(block, &rule, f);
495604ba230SVolodymyr Mytnyk if (err)
496604ba230SVolodymyr Mytnyk return err;
497604ba230SVolodymyr Mytnyk
498604ba230SVolodymyr Mytnyk template = kmalloc(sizeof(*template), GFP_KERNEL);
499604ba230SVolodymyr Mytnyk if (!template) {
500604ba230SVolodymyr Mytnyk err = -ENOMEM;
501604ba230SVolodymyr Mytnyk goto err_malloc;
502604ba230SVolodymyr Mytnyk }
503604ba230SVolodymyr Mytnyk
504604ba230SVolodymyr Mytnyk prestera_acl_rule_keymask_pcl_id_set(&rule, 0);
505fa5d824cSVolodymyr Mytnyk ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
506fa5d824cSVolodymyr Mytnyk f->common.chain_index);
507604ba230SVolodymyr Mytnyk if (IS_ERR_OR_NULL(ruleset)) {
508604ba230SVolodymyr Mytnyk err = -EINVAL;
509604ba230SVolodymyr Mytnyk goto err_ruleset_get;
510604ba230SVolodymyr Mytnyk }
511604ba230SVolodymyr Mytnyk
512604ba230SVolodymyr Mytnyk /* preserve keymask/template to this ruleset */
5139e6fd874SJiasheng Jiang err = prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask);
5149e6fd874SJiasheng Jiang if (err)
5159e6fd874SJiasheng Jiang goto err_ruleset_keymask_set;
516604ba230SVolodymyr Mytnyk
517604ba230SVolodymyr Mytnyk /* skip error, as it is not possible to reject template operation,
518604ba230SVolodymyr Mytnyk * so, keep the reference to the ruleset for rules to be added
519604ba230SVolodymyr Mytnyk * to that ruleset later. In case of offload fail, the ruleset
520604ba230SVolodymyr Mytnyk * will be offloaded again during adding a new rule. Also,
521604ba230SVolodymyr Mytnyk * unlikly possble that ruleset is already offloaded at this staage.
522604ba230SVolodymyr Mytnyk */
523604ba230SVolodymyr Mytnyk prestera_acl_ruleset_offload(ruleset);
524604ba230SVolodymyr Mytnyk
525604ba230SVolodymyr Mytnyk /* keep the reference to the ruleset */
526604ba230SVolodymyr Mytnyk template->ruleset = ruleset;
527fa5d824cSVolodymyr Mytnyk template->chain_index = f->common.chain_index;
528fa5d824cSVolodymyr Mytnyk list_add_rcu(&template->list, &block->template_list);
529604ba230SVolodymyr Mytnyk return 0;
530604ba230SVolodymyr Mytnyk
5319e6fd874SJiasheng Jiang err_ruleset_keymask_set:
5329e6fd874SJiasheng Jiang prestera_acl_ruleset_put(ruleset);
533604ba230SVolodymyr Mytnyk err_ruleset_get:
534604ba230SVolodymyr Mytnyk kfree(template);
535604ba230SVolodymyr Mytnyk err_malloc:
536604ba230SVolodymyr Mytnyk NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed");
537604ba230SVolodymyr Mytnyk return err;
538604ba230SVolodymyr Mytnyk }
539604ba230SVolodymyr Mytnyk
prestera_flower_tmplt_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)540604ba230SVolodymyr Mytnyk void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
541604ba230SVolodymyr Mytnyk struct flow_cls_offload *f)
542604ba230SVolodymyr Mytnyk {
543b3ae2d35SVolodymyr Mytnyk struct prestera_flower_template *template, *tmp;
544b3ae2d35SVolodymyr Mytnyk
545b3ae2d35SVolodymyr Mytnyk list_for_each_entry_safe(template, tmp, &block->template_list, list)
546b3ae2d35SVolodymyr Mytnyk if (template->chain_index == f->common.chain_index) {
547b3ae2d35SVolodymyr Mytnyk /* put the reference to the ruleset kept in create */
548b3ae2d35SVolodymyr Mytnyk prestera_flower_template_free(template);
549b3ae2d35SVolodymyr Mytnyk return;
550b3ae2d35SVolodymyr Mytnyk }
551604ba230SVolodymyr Mytnyk }
552604ba230SVolodymyr Mytnyk
prestera_flower_stats(struct prestera_flow_block * block,struct flow_cls_offload * f)5538b474a9fSSerhiy Boiko int prestera_flower_stats(struct prestera_flow_block *block,
5548b474a9fSSerhiy Boiko struct flow_cls_offload *f)
5558b474a9fSSerhiy Boiko {
55647327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
5578b474a9fSSerhiy Boiko struct prestera_acl_rule *rule;
5588b474a9fSSerhiy Boiko u64 packets;
5598b474a9fSSerhiy Boiko u64 lastuse;
5608b474a9fSSerhiy Boiko u64 bytes;
5618b474a9fSSerhiy Boiko int err;
5628b474a9fSSerhiy Boiko
563fa5d824cSVolodymyr Mytnyk ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
564fa5d824cSVolodymyr Mytnyk f->common.chain_index);
56547327e19SVolodymyr Mytnyk if (IS_ERR(ruleset))
56647327e19SVolodymyr Mytnyk return PTR_ERR(ruleset);
5678b474a9fSSerhiy Boiko
56847327e19SVolodymyr Mytnyk rule = prestera_acl_rule_lookup(ruleset, f->cookie);
56947327e19SVolodymyr Mytnyk if (!rule) {
57047327e19SVolodymyr Mytnyk err = -EINVAL;
57147327e19SVolodymyr Mytnyk goto err_rule_get_stats;
57247327e19SVolodymyr Mytnyk }
57347327e19SVolodymyr Mytnyk
57447327e19SVolodymyr Mytnyk err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets,
57547327e19SVolodymyr Mytnyk &bytes, &lastuse);
5768b474a9fSSerhiy Boiko if (err)
57747327e19SVolodymyr Mytnyk goto err_rule_get_stats;
5788b474a9fSSerhiy Boiko
5798b474a9fSSerhiy Boiko flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
58047327e19SVolodymyr Mytnyk FLOW_ACTION_HW_STATS_DELAYED);
58147327e19SVolodymyr Mytnyk
58247327e19SVolodymyr Mytnyk err_rule_get_stats:
58347327e19SVolodymyr Mytnyk prestera_acl_ruleset_put(ruleset);
58447327e19SVolodymyr Mytnyk return err;
5858b474a9fSSerhiy Boiko }
586