brintos

brintos / linux-shallow public Read only

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