prestera_flower.c (13514B)
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */ 3 4#include "prestera.h" 5#include "prestera_acl.h" 6#include "prestera_flow.h" 7#include "prestera_flower.h" 8 9struct prestera_flower_template { 10 struct prestera_acl_ruleset *ruleset; 11 struct list_head list; 12 u32 chain_index; 13}; 14 15static void 16prestera_flower_template_free(struct prestera_flower_template *template) 17{ 18 prestera_acl_ruleset_put(template->ruleset); 19 list_del(&template->list); 20 kfree(template); 21} 22 23void prestera_flower_template_cleanup(struct prestera_flow_block *block) 24{ 25 struct prestera_flower_template *template, *tmp; 26 27 /* put the reference to all rulesets kept in tmpl create */ 28 list_for_each_entry_safe(template, tmp, &block->template_list, list) 29 prestera_flower_template_free(template); 30} 31 32static int 33prestera_flower_parse_goto_action(struct prestera_flow_block *block, 34 struct prestera_acl_rule *rule, 35 u32 chain_index, 36 const struct flow_action_entry *act) 37{ 38 struct prestera_acl_ruleset *ruleset; 39 40 if (act->chain_index <= chain_index) 41 /* we can jump only forward */ 42 return -EINVAL; 43 44 if (rule->re_arg.jump.valid) 45 return -EEXIST; 46 47 ruleset = prestera_acl_ruleset_get(block->sw->acl, block, 48 act->chain_index); 49 if (IS_ERR(ruleset)) 50 return PTR_ERR(ruleset); 51 52 rule->re_arg.jump.valid = 1; 53 rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset); 54 55 rule->jump_ruleset = ruleset; 56 57 return 0; 58} 59 60static int prestera_flower_parse_actions(struct prestera_flow_block *block, 61 struct prestera_acl_rule *rule, 62 struct flow_action *flow_action, 63 u32 chain_index, 64 struct netlink_ext_ack *extack) 65{ 66 const struct flow_action_entry *act; 67 int err, i; 68 69 /* whole struct (rule->re_arg) must be initialized with 0 */ 70 if (!flow_action_has_entries(flow_action)) 71 return 0; 72 73 if (!flow_action_mixed_hw_stats_check(flow_action, extack)) 74 return -EOPNOTSUPP; 75 76 act = flow_action_first_entry_get(flow_action); 77 if (act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED) { 78 /* Nothing to do */ 79 } else if (act->hw_stats & FLOW_ACTION_HW_STATS_DELAYED) { 80 /* setup counter first */ 81 rule->re_arg.count.valid = true; 82 err = prestera_acl_chain_to_client(chain_index, 83 &rule->re_arg.count.client); 84 if (err) 85 return err; 86 } else { 87 NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type"); 88 return -EOPNOTSUPP; 89 } 90 91 flow_action_for_each(i, act, flow_action) { 92 switch (act->id) { 93 case FLOW_ACTION_ACCEPT: 94 if (rule->re_arg.accept.valid) 95 return -EEXIST; 96 97 rule->re_arg.accept.valid = 1; 98 break; 99 case FLOW_ACTION_DROP: 100 if (rule->re_arg.drop.valid) 101 return -EEXIST; 102 103 rule->re_arg.drop.valid = 1; 104 break; 105 case FLOW_ACTION_TRAP: 106 if (rule->re_arg.trap.valid) 107 return -EEXIST; 108 109 rule->re_arg.trap.valid = 1; 110 break; 111 case FLOW_ACTION_POLICE: 112 if (rule->re_arg.police.valid) 113 return -EEXIST; 114 115 rule->re_arg.police.valid = 1; 116 rule->re_arg.police.rate = 117 act->police.rate_bytes_ps; 118 rule->re_arg.police.burst = act->police.burst; 119 rule->re_arg.police.ingress = true; 120 break; 121 case FLOW_ACTION_GOTO: 122 err = prestera_flower_parse_goto_action(block, rule, 123 chain_index, 124 act); 125 if (err) 126 return err; 127 break; 128 default: 129 NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); 130 pr_err("Unsupported action\n"); 131 return -EOPNOTSUPP; 132 } 133 } 134 135 return 0; 136} 137 138static int prestera_flower_parse_meta(struct prestera_acl_rule *rule, 139 struct flow_cls_offload *f, 140 struct prestera_flow_block *block) 141{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); 142 struct prestera_acl_match *r_match = &rule->re_key.match; 143 struct prestera_port *port; 144 struct net_device *ingress_dev; 145 struct flow_match_meta match; 146 __be16 key, mask; 147 148 flow_rule_match_meta(f_rule, &match); 149 if (match.mask->ingress_ifindex != 0xFFFFFFFF) { 150 NL_SET_ERR_MSG_MOD(f->common.extack, 151 "Unsupported ingress ifindex mask"); 152 return -EINVAL; 153 } 154 155 ingress_dev = __dev_get_by_index(block->net, 156 match.key->ingress_ifindex); 157 if (!ingress_dev) { 158 NL_SET_ERR_MSG_MOD(f->common.extack, 159 "Can't find specified ingress port to match on"); 160 return -EINVAL; 161 } 162 163 if (!prestera_netdev_check(ingress_dev)) { 164 NL_SET_ERR_MSG_MOD(f->common.extack, 165 "Can't match on switchdev ingress port"); 166 return -EINVAL; 167 } 168 port = netdev_priv(ingress_dev); 169 170 mask = htons(0x1FFF); 171 key = htons(port->hw_id); 172 rule_match_set(r_match->key, SYS_PORT, key); 173 rule_match_set(r_match->mask, SYS_PORT, mask); 174 175 mask = htons(0x1FF); 176 key = htons(port->dev_id); 177 rule_match_set(r_match->key, SYS_DEV, key); 178 rule_match_set(r_match->mask, SYS_DEV, mask); 179 180 return 0; 181 182} 183 184static int prestera_flower_parse(struct prestera_flow_block *block, 185 struct prestera_acl_rule *rule, 186 struct flow_cls_offload *f) 187{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); 188 struct flow_dissector *dissector = f_rule->match.dissector; 189 struct prestera_acl_match *r_match = &rule->re_key.match; 190 __be16 n_proto_mask = 0; 191 __be16 n_proto_key = 0; 192 u16 addr_type = 0; 193 u8 ip_proto = 0; 194 int err; 195 196 if (dissector->used_keys & 197 ~(BIT(FLOW_DISSECTOR_KEY_META) | 198 BIT(FLOW_DISSECTOR_KEY_CONTROL) | 199 BIT(FLOW_DISSECTOR_KEY_BASIC) | 200 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | 201 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | 202 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | 203 BIT(FLOW_DISSECTOR_KEY_ICMP) | 204 BIT(FLOW_DISSECTOR_KEY_PORTS) | 205 BIT(FLOW_DISSECTOR_KEY_VLAN))) { 206 NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key"); 207 return -EOPNOTSUPP; 208 } 209 210 prestera_acl_rule_priority_set(rule, f->common.prio); 211 212 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_META)) { 213 err = prestera_flower_parse_meta(rule, f, block); 214 if (err) 215 return err; 216 } 217 218 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_CONTROL)) { 219 struct flow_match_control match; 220 221 flow_rule_match_control(f_rule, &match); 222 addr_type = match.key->addr_type; 223 } 224 225 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_BASIC)) { 226 struct flow_match_basic match; 227 228 flow_rule_match_basic(f_rule, &match); 229 n_proto_key = match.key->n_proto; 230 n_proto_mask = match.mask->n_proto; 231 232 if (ntohs(match.key->n_proto) == ETH_P_ALL) { 233 n_proto_key = 0; 234 n_proto_mask = 0; 235 } 236 237 rule_match_set(r_match->key, ETH_TYPE, n_proto_key); 238 rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask); 239 240 rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto); 241 rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto); 242 ip_proto = match.key->ip_proto; 243 } 244 245 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 246 struct flow_match_eth_addrs match; 247 248 flow_rule_match_eth_addrs(f_rule, &match); 249 250 /* DA key, mask */ 251 rule_match_set_n(r_match->key, 252 ETH_DMAC_0, &match.key->dst[0], 4); 253 rule_match_set_n(r_match->key, 254 ETH_DMAC_1, &match.key->dst[4], 2); 255 256 rule_match_set_n(r_match->mask, 257 ETH_DMAC_0, &match.mask->dst[0], 4); 258 rule_match_set_n(r_match->mask, 259 ETH_DMAC_1, &match.mask->dst[4], 2); 260 261 /* SA key, mask */ 262 rule_match_set_n(r_match->key, 263 ETH_SMAC_0, &match.key->src[0], 4); 264 rule_match_set_n(r_match->key, 265 ETH_SMAC_1, &match.key->src[4], 2); 266 267 rule_match_set_n(r_match->mask, 268 ETH_SMAC_0, &match.mask->src[0], 4); 269 rule_match_set_n(r_match->mask, 270 ETH_SMAC_1, &match.mask->src[4], 2); 271 } 272 273 if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 274 struct flow_match_ipv4_addrs match; 275 276 flow_rule_match_ipv4_addrs(f_rule, &match); 277 278 rule_match_set(r_match->key, IP_SRC, match.key->src); 279 rule_match_set(r_match->mask, IP_SRC, match.mask->src); 280 281 rule_match_set(r_match->key, IP_DST, match.key->dst); 282 rule_match_set(r_match->mask, IP_DST, match.mask->dst); 283 } 284 285 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) { 286 struct flow_match_ports match; 287 288 if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) { 289 NL_SET_ERR_MSG_MOD 290 (f->common.extack, 291 "Only UDP and TCP keys are supported"); 292 return -EINVAL; 293 } 294 295 flow_rule_match_ports(f_rule, &match); 296 297 rule_match_set(r_match->key, L4_PORT_SRC, match.key->src); 298 rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src); 299 300 rule_match_set(r_match->key, L4_PORT_DST, match.key->dst); 301 rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst); 302 } 303 304 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) { 305 struct flow_match_vlan match; 306 307 flow_rule_match_vlan(f_rule, &match); 308 309 if (match.mask->vlan_id != 0) { 310 __be16 key = cpu_to_be16(match.key->vlan_id); 311 __be16 mask = cpu_to_be16(match.mask->vlan_id); 312 313 rule_match_set(r_match->key, VLAN_ID, key); 314 rule_match_set(r_match->mask, VLAN_ID, mask); 315 } 316 317 rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid); 318 rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid); 319 } 320 321 if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) { 322 struct flow_match_icmp match; 323 324 flow_rule_match_icmp(f_rule, &match); 325 326 rule_match_set(r_match->key, ICMP_TYPE, match.key->type); 327 rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type); 328 329 rule_match_set(r_match->key, ICMP_CODE, match.key->code); 330 rule_match_set(r_match->mask, ICMP_CODE, match.mask->code); 331 } 332 333 return prestera_flower_parse_actions(block, rule, &f->rule->action, 334 f->common.chain_index, 335 f->common.extack); 336} 337 338int prestera_flower_replace(struct prestera_flow_block *block, 339 struct flow_cls_offload *f) 340{ 341 struct prestera_acl_ruleset *ruleset; 342 struct prestera_acl *acl = block->sw->acl; 343 struct prestera_acl_rule *rule; 344 int err; 345 346 ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index); 347 if (IS_ERR(ruleset)) 348 return PTR_ERR(ruleset); 349 350 /* increments the ruleset reference */ 351 rule = prestera_acl_rule_create(ruleset, f->cookie, 352 f->common.chain_index); 353 if (IS_ERR(rule)) { 354 err = PTR_ERR(rule); 355 goto err_rule_create; 356 } 357 358 err = prestera_flower_parse(block, rule, f); 359 if (err) 360 goto err_rule_add; 361 362 if (!prestera_acl_ruleset_is_offload(ruleset)) { 363 err = prestera_acl_ruleset_offload(ruleset); 364 if (err) 365 goto err_ruleset_offload; 366 } 367 368 err = prestera_acl_rule_add(block->sw, rule); 369 if (err) 370 goto err_rule_add; 371 372 prestera_acl_ruleset_put(ruleset); 373 return 0; 374 375err_ruleset_offload: 376err_rule_add: 377 prestera_acl_rule_destroy(rule); 378err_rule_create: 379 prestera_acl_ruleset_put(ruleset); 380 return err; 381} 382 383void prestera_flower_destroy(struct prestera_flow_block *block, 384 struct flow_cls_offload *f) 385{ 386 struct prestera_acl_ruleset *ruleset; 387 struct prestera_acl_rule *rule; 388 389 ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, 390 f->common.chain_index); 391 if (IS_ERR(ruleset)) 392 return; 393 394 rule = prestera_acl_rule_lookup(ruleset, f->cookie); 395 if (rule) { 396 prestera_acl_rule_del(block->sw, rule); 397 prestera_acl_rule_destroy(rule); 398 } 399 prestera_acl_ruleset_put(ruleset); 400 401} 402 403int prestera_flower_tmplt_create(struct prestera_flow_block *block, 404 struct flow_cls_offload *f) 405{ 406 struct prestera_flower_template *template; 407 struct prestera_acl_ruleset *ruleset; 408 struct prestera_acl_rule rule; 409 int err; 410 411 memset(&rule, 0, sizeof(rule)); 412 err = prestera_flower_parse(block, &rule, f); 413 if (err) 414 return err; 415 416 template = kmalloc(sizeof(*template), GFP_KERNEL); 417 if (!template) { 418 err = -ENOMEM; 419 goto err_malloc; 420 } 421 422 prestera_acl_rule_keymask_pcl_id_set(&rule, 0); 423 ruleset = prestera_acl_ruleset_get(block->sw->acl, block, 424 f->common.chain_index); 425 if (IS_ERR_OR_NULL(ruleset)) { 426 err = -EINVAL; 427 goto err_ruleset_get; 428 } 429 430 /* preserve keymask/template to this ruleset */ 431 prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask); 432 433 /* skip error, as it is not possible to reject template operation, 434 * so, keep the reference to the ruleset for rules to be added 435 * to that ruleset later. In case of offload fail, the ruleset 436 * will be offloaded again during adding a new rule. Also, 437 * unlikly possble that ruleset is already offloaded at this staage. 438 */ 439 prestera_acl_ruleset_offload(ruleset); 440 441 /* keep the reference to the ruleset */ 442 template->ruleset = ruleset; 443 template->chain_index = f->common.chain_index; 444 list_add_rcu(&template->list, &block->template_list); 445 return 0; 446 447err_ruleset_get: 448 kfree(template); 449err_malloc: 450 NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed"); 451 return err; 452} 453 454void prestera_flower_tmplt_destroy(struct prestera_flow_block *block, 455 struct flow_cls_offload *f) 456{ 457 struct prestera_flower_template *template, *tmp; 458 459 list_for_each_entry_safe(template, tmp, &block->template_list, list) 460 if (template->chain_index == f->common.chain_index) { 461 /* put the reference to the ruleset kept in create */ 462 prestera_flower_template_free(template); 463 return; 464 } 465} 466 467int prestera_flower_stats(struct prestera_flow_block *block, 468 struct flow_cls_offload *f) 469{ 470 struct prestera_acl_ruleset *ruleset; 471 struct prestera_acl_rule *rule; 472 u64 packets; 473 u64 lastuse; 474 u64 bytes; 475 int err; 476 477 ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, 478 f->common.chain_index); 479 if (IS_ERR(ruleset)) 480 return PTR_ERR(ruleset); 481 482 rule = prestera_acl_rule_lookup(ruleset, f->cookie); 483 if (!rule) { 484 err = -EINVAL; 485 goto err_rule_get_stats; 486 } 487 488 err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets, 489 &bytes, &lastuse); 490 if (err) 491 goto err_rule_get_stats; 492 493 flow_stats_update(&f->stats, bytes, packets, 0, lastuse, 494 FLOW_ACTION_HW_STATS_DELAYED); 495 496err_rule_get_stats: 497 prestera_acl_ruleset_put(ruleset); 498 return err; 499}