162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * net/sched/cls_api.c	Packet classifier API.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Changes:
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/skbuff.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/kmod.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/idr.h>
2362306a36Sopenharmony_ci#include <linux/jhash.h>
2462306a36Sopenharmony_ci#include <linux/rculist.h>
2562306a36Sopenharmony_ci#include <linux/rhashtable.h>
2662306a36Sopenharmony_ci#include <net/net_namespace.h>
2762306a36Sopenharmony_ci#include <net/sock.h>
2862306a36Sopenharmony_ci#include <net/netlink.h>
2962306a36Sopenharmony_ci#include <net/pkt_sched.h>
3062306a36Sopenharmony_ci#include <net/pkt_cls.h>
3162306a36Sopenharmony_ci#include <net/tc_act/tc_pedit.h>
3262306a36Sopenharmony_ci#include <net/tc_act/tc_mirred.h>
3362306a36Sopenharmony_ci#include <net/tc_act/tc_vlan.h>
3462306a36Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h>
3562306a36Sopenharmony_ci#include <net/tc_act/tc_csum.h>
3662306a36Sopenharmony_ci#include <net/tc_act/tc_gact.h>
3762306a36Sopenharmony_ci#include <net/tc_act/tc_police.h>
3862306a36Sopenharmony_ci#include <net/tc_act/tc_sample.h>
3962306a36Sopenharmony_ci#include <net/tc_act/tc_skbedit.h>
4062306a36Sopenharmony_ci#include <net/tc_act/tc_ct.h>
4162306a36Sopenharmony_ci#include <net/tc_act/tc_mpls.h>
4262306a36Sopenharmony_ci#include <net/tc_act/tc_gate.h>
4362306a36Sopenharmony_ci#include <net/flow_offload.h>
4462306a36Sopenharmony_ci#include <net/tc_wrapper.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* The list of all installed classifier types */
4762306a36Sopenharmony_cistatic LIST_HEAD(tcf_proto_base);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Protects list of registered TC modules. It is pure SMP lock. */
5062306a36Sopenharmony_cistatic DEFINE_RWLOCK(cls_mod_lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct xarray tcf_exts_miss_cookies_xa;
5362306a36Sopenharmony_cistruct tcf_exts_miss_cookie_node {
5462306a36Sopenharmony_ci	const struct tcf_chain *chain;
5562306a36Sopenharmony_ci	const struct tcf_proto *tp;
5662306a36Sopenharmony_ci	const struct tcf_exts *exts;
5762306a36Sopenharmony_ci	u32 chain_index;
5862306a36Sopenharmony_ci	u32 tp_prio;
5962306a36Sopenharmony_ci	u32 handle;
6062306a36Sopenharmony_ci	u32 miss_cookie_base;
6162306a36Sopenharmony_ci	struct rcu_head rcu;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Each tc action entry cookie will be comprised of 32bit miss_cookie_base +
6562306a36Sopenharmony_ci * action index in the exts tc actions array.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ciunion tcf_exts_miss_cookie {
6862306a36Sopenharmony_ci	struct {
6962306a36Sopenharmony_ci		u32 miss_cookie_base;
7062306a36Sopenharmony_ci		u32 act_index;
7162306a36Sopenharmony_ci	};
7262306a36Sopenharmony_ci	u64 miss_cookie;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
7662306a36Sopenharmony_cistatic int
7762306a36Sopenharmony_citcf_exts_miss_cookie_base_alloc(struct tcf_exts *exts, struct tcf_proto *tp,
7862306a36Sopenharmony_ci				u32 handle)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct tcf_exts_miss_cookie_node *n;
8162306a36Sopenharmony_ci	static u32 next;
8262306a36Sopenharmony_ci	int err;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (WARN_ON(!handle || !tp->ops->get_exts))
8562306a36Sopenharmony_ci		return -EINVAL;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	n = kzalloc(sizeof(*n), GFP_KERNEL);
8862306a36Sopenharmony_ci	if (!n)
8962306a36Sopenharmony_ci		return -ENOMEM;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	n->chain_index = tp->chain->index;
9262306a36Sopenharmony_ci	n->chain = tp->chain;
9362306a36Sopenharmony_ci	n->tp_prio = tp->prio;
9462306a36Sopenharmony_ci	n->tp = tp;
9562306a36Sopenharmony_ci	n->exts = exts;
9662306a36Sopenharmony_ci	n->handle = handle;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	err = xa_alloc_cyclic(&tcf_exts_miss_cookies_xa, &n->miss_cookie_base,
9962306a36Sopenharmony_ci			      n, xa_limit_32b, &next, GFP_KERNEL);
10062306a36Sopenharmony_ci	if (err)
10162306a36Sopenharmony_ci		goto err_xa_alloc;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	exts->miss_cookie_node = n;
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cierr_xa_alloc:
10762306a36Sopenharmony_ci	kfree(n);
10862306a36Sopenharmony_ci	return err;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void tcf_exts_miss_cookie_base_destroy(struct tcf_exts *exts)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct tcf_exts_miss_cookie_node *n;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (!exts->miss_cookie_node)
11662306a36Sopenharmony_ci		return;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	n = exts->miss_cookie_node;
11962306a36Sopenharmony_ci	xa_erase(&tcf_exts_miss_cookies_xa, n->miss_cookie_base);
12062306a36Sopenharmony_ci	kfree_rcu(n, rcu);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct tcf_exts_miss_cookie_node *
12462306a36Sopenharmony_citcf_exts_miss_cookie_lookup(u64 miss_cookie, int *act_index)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	union tcf_exts_miss_cookie mc = { .miss_cookie = miss_cookie, };
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	*act_index = mc.act_index;
12962306a36Sopenharmony_ci	return xa_load(&tcf_exts_miss_cookies_xa, mc.miss_cookie_base);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci#else /* IS_ENABLED(CONFIG_NET_TC_SKB_EXT) */
13262306a36Sopenharmony_cistatic int
13362306a36Sopenharmony_citcf_exts_miss_cookie_base_alloc(struct tcf_exts *exts, struct tcf_proto *tp,
13462306a36Sopenharmony_ci				u32 handle)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void tcf_exts_miss_cookie_base_destroy(struct tcf_exts *exts)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_NET_TC_SKB_EXT) */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic u64 tcf_exts_miss_cookie_get(u32 miss_cookie_base, int act_index)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	union tcf_exts_miss_cookie mc = { .act_index = act_index, };
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!miss_cookie_base)
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	mc.miss_cookie_base = miss_cookie_base;
15262306a36Sopenharmony_ci	return mc.miss_cookie;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
15662306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(tc_skb_ext_tc);
15762306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_civoid tc_skb_ext_tc_enable(void)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	static_branch_inc(&tc_skb_ext_tc);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc_enable);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_civoid tc_skb_ext_tc_disable(void)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	static_branch_dec(&tc_skb_ext_tc);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc_disable);
17062306a36Sopenharmony_ci#endif
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic u32 destroy_obj_hashfn(const struct tcf_proto *tp)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	return jhash_3words(tp->chain->index, tp->prio,
17562306a36Sopenharmony_ci			    (__force __u32)tp->protocol, 0);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void tcf_proto_signal_destroying(struct tcf_chain *chain,
17962306a36Sopenharmony_ci					struct tcf_proto *tp)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	mutex_lock(&block->proto_destroy_lock);
18462306a36Sopenharmony_ci	hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node,
18562306a36Sopenharmony_ci		     destroy_obj_hashfn(tp));
18662306a36Sopenharmony_ci	mutex_unlock(&block->proto_destroy_lock);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic bool tcf_proto_cmp(const struct tcf_proto *tp1,
19062306a36Sopenharmony_ci			  const struct tcf_proto *tp2)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	return tp1->chain->index == tp2->chain->index &&
19362306a36Sopenharmony_ci	       tp1->prio == tp2->prio &&
19462306a36Sopenharmony_ci	       tp1->protocol == tp2->protocol;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic bool tcf_proto_exists_destroying(struct tcf_chain *chain,
19862306a36Sopenharmony_ci					struct tcf_proto *tp)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u32 hash = destroy_obj_hashfn(tp);
20162306a36Sopenharmony_ci	struct tcf_proto *iter;
20262306a36Sopenharmony_ci	bool found = false;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	rcu_read_lock();
20562306a36Sopenharmony_ci	hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter,
20662306a36Sopenharmony_ci				   destroy_ht_node, hash) {
20762306a36Sopenharmony_ci		if (tcf_proto_cmp(tp, iter)) {
20862306a36Sopenharmony_ci			found = true;
20962306a36Sopenharmony_ci			break;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	rcu_read_unlock();
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return found;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void
21862306a36Sopenharmony_citcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	mutex_lock(&block->proto_destroy_lock);
22362306a36Sopenharmony_ci	if (hash_hashed(&tp->destroy_ht_node))
22462306a36Sopenharmony_ci		hash_del_rcu(&tp->destroy_ht_node);
22562306a36Sopenharmony_ci	mutex_unlock(&block->proto_destroy_lock);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/* Find classifier type by string name */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	const struct tcf_proto_ops *t, *res = NULL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (kind) {
23562306a36Sopenharmony_ci		read_lock(&cls_mod_lock);
23662306a36Sopenharmony_ci		list_for_each_entry(t, &tcf_proto_base, head) {
23762306a36Sopenharmony_ci			if (strcmp(kind, t->kind) == 0) {
23862306a36Sopenharmony_ci				if (try_module_get(t->owner))
23962306a36Sopenharmony_ci					res = t;
24062306a36Sopenharmony_ci				break;
24162306a36Sopenharmony_ci			}
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci		read_unlock(&cls_mod_lock);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	return res;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic const struct tcf_proto_ops *
24962306a36Sopenharmony_citcf_proto_lookup_ops(const char *kind, bool rtnl_held,
25062306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	const struct tcf_proto_ops *ops;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ops = __tcf_proto_lookup_ops(kind);
25562306a36Sopenharmony_ci	if (ops)
25662306a36Sopenharmony_ci		return ops;
25762306a36Sopenharmony_ci#ifdef CONFIG_MODULES
25862306a36Sopenharmony_ci	if (rtnl_held)
25962306a36Sopenharmony_ci		rtnl_unlock();
26062306a36Sopenharmony_ci	request_module("cls_%s", kind);
26162306a36Sopenharmony_ci	if (rtnl_held)
26262306a36Sopenharmony_ci		rtnl_lock();
26362306a36Sopenharmony_ci	ops = __tcf_proto_lookup_ops(kind);
26462306a36Sopenharmony_ci	/* We dropped the RTNL semaphore in order to perform
26562306a36Sopenharmony_ci	 * the module load. So, even if we succeeded in loading
26662306a36Sopenharmony_ci	 * the module we have to replay the request. We indicate
26762306a36Sopenharmony_ci	 * this using -EAGAIN.
26862306a36Sopenharmony_ci	 */
26962306a36Sopenharmony_ci	if (ops) {
27062306a36Sopenharmony_ci		module_put(ops->owner);
27162306a36Sopenharmony_ci		return ERR_PTR(-EAGAIN);
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci#endif
27462306a36Sopenharmony_ci	NL_SET_ERR_MSG(extack, "TC classifier not found");
27562306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/* Register(unregister) new classifier type */
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciint register_tcf_proto_ops(struct tcf_proto_ops *ops)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct tcf_proto_ops *t;
28362306a36Sopenharmony_ci	int rc = -EEXIST;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	write_lock(&cls_mod_lock);
28662306a36Sopenharmony_ci	list_for_each_entry(t, &tcf_proto_base, head)
28762306a36Sopenharmony_ci		if (!strcmp(ops->kind, t->kind))
28862306a36Sopenharmony_ci			goto out;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	list_add_tail(&ops->head, &tcf_proto_base);
29162306a36Sopenharmony_ci	rc = 0;
29262306a36Sopenharmony_ciout:
29362306a36Sopenharmony_ci	write_unlock(&cls_mod_lock);
29462306a36Sopenharmony_ci	return rc;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ciEXPORT_SYMBOL(register_tcf_proto_ops);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct workqueue_struct *tc_filter_wq;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_civoid unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct tcf_proto_ops *t;
30362306a36Sopenharmony_ci	int rc = -ENOENT;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Wait for outstanding call_rcu()s, if any, from a
30662306a36Sopenharmony_ci	 * tcf_proto_ops's destroy() handler.
30762306a36Sopenharmony_ci	 */
30862306a36Sopenharmony_ci	rcu_barrier();
30962306a36Sopenharmony_ci	flush_workqueue(tc_filter_wq);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	write_lock(&cls_mod_lock);
31262306a36Sopenharmony_ci	list_for_each_entry(t, &tcf_proto_base, head) {
31362306a36Sopenharmony_ci		if (t == ops) {
31462306a36Sopenharmony_ci			list_del(&t->head);
31562306a36Sopenharmony_ci			rc = 0;
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	write_unlock(&cls_mod_lock);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	WARN(rc, "unregister tc filter kind(%s) failed %d\n", ops->kind, rc);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_tcf_proto_ops);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cibool tcf_queue_work(struct rcu_work *rwork, work_func_t func)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	INIT_RCU_WORK(rwork, func);
32862306a36Sopenharmony_ci	return queue_rcu_work(tc_filter_wq, rwork);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_queue_work);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/* Select new prio value from the range, managed by kernel. */
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic inline u32 tcf_auto_prio(struct tcf_proto *tp)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	u32 first = TC_H_MAKE(0xC0000000U, 0U);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (tp)
33962306a36Sopenharmony_ci		first = tp->prio - 1;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return TC_H_MAJ(first);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic bool tcf_proto_check_kind(struct nlattr *kind, char *name)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	if (kind)
34762306a36Sopenharmony_ci		return nla_strscpy(name, kind, IFNAMSIZ) < 0;
34862306a36Sopenharmony_ci	memset(name, 0, IFNAMSIZ);
34962306a36Sopenharmony_ci	return false;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic bool tcf_proto_is_unlocked(const char *kind)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	const struct tcf_proto_ops *ops;
35562306a36Sopenharmony_ci	bool ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (strlen(kind) == 0)
35862306a36Sopenharmony_ci		return false;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ops = tcf_proto_lookup_ops(kind, false, NULL);
36162306a36Sopenharmony_ci	/* On error return false to take rtnl lock. Proto lookup/create
36262306a36Sopenharmony_ci	 * functions will perform lookup again and properly handle errors.
36362306a36Sopenharmony_ci	 */
36462306a36Sopenharmony_ci	if (IS_ERR(ops))
36562306a36Sopenharmony_ci		return false;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED);
36862306a36Sopenharmony_ci	module_put(ops->owner);
36962306a36Sopenharmony_ci	return ret;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
37362306a36Sopenharmony_ci					  u32 prio, struct tcf_chain *chain,
37462306a36Sopenharmony_ci					  bool rtnl_held,
37562306a36Sopenharmony_ci					  struct netlink_ext_ack *extack)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct tcf_proto *tp;
37862306a36Sopenharmony_ci	int err;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	tp = kzalloc(sizeof(*tp), GFP_KERNEL);
38162306a36Sopenharmony_ci	if (!tp)
38262306a36Sopenharmony_ci		return ERR_PTR(-ENOBUFS);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack);
38562306a36Sopenharmony_ci	if (IS_ERR(tp->ops)) {
38662306a36Sopenharmony_ci		err = PTR_ERR(tp->ops);
38762306a36Sopenharmony_ci		goto errout;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	tp->classify = tp->ops->classify;
39062306a36Sopenharmony_ci	tp->protocol = protocol;
39162306a36Sopenharmony_ci	tp->prio = prio;
39262306a36Sopenharmony_ci	tp->chain = chain;
39362306a36Sopenharmony_ci	spin_lock_init(&tp->lock);
39462306a36Sopenharmony_ci	refcount_set(&tp->refcnt, 1);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	err = tp->ops->init(tp);
39762306a36Sopenharmony_ci	if (err) {
39862306a36Sopenharmony_ci		module_put(tp->ops->owner);
39962306a36Sopenharmony_ci		goto errout;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	return tp;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cierrout:
40462306a36Sopenharmony_ci	kfree(tp);
40562306a36Sopenharmony_ci	return ERR_PTR(err);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void tcf_proto_get(struct tcf_proto *tp)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	refcount_inc(&tp->refcnt);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held,
41662306a36Sopenharmony_ci			      bool sig_destroy, struct netlink_ext_ack *extack)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	tp->ops->destroy(tp, rtnl_held, extack);
41962306a36Sopenharmony_ci	if (sig_destroy)
42062306a36Sopenharmony_ci		tcf_proto_signal_destroyed(tp->chain, tp);
42162306a36Sopenharmony_ci	tcf_chain_put(tp->chain);
42262306a36Sopenharmony_ci	module_put(tp->ops->owner);
42362306a36Sopenharmony_ci	kfree_rcu(tp, rcu);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held,
42762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	if (refcount_dec_and_test(&tp->refcnt))
43062306a36Sopenharmony_ci		tcf_proto_destroy(tp, rtnl_held, true, extack);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic bool tcf_proto_check_delete(struct tcf_proto *tp)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	if (tp->ops->delete_empty)
43662306a36Sopenharmony_ci		return tp->ops->delete_empty(tp);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	tp->deleting = true;
43962306a36Sopenharmony_ci	return tp->deleting;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void tcf_proto_mark_delete(struct tcf_proto *tp)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	spin_lock(&tp->lock);
44562306a36Sopenharmony_ci	tp->deleting = true;
44662306a36Sopenharmony_ci	spin_unlock(&tp->lock);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic bool tcf_proto_is_deleting(struct tcf_proto *tp)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	bool deleting;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	spin_lock(&tp->lock);
45462306a36Sopenharmony_ci	deleting = tp->deleting;
45562306a36Sopenharmony_ci	spin_unlock(&tp->lock);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return deleting;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci#define ASSERT_BLOCK_LOCKED(block)					\
46162306a36Sopenharmony_ci	lockdep_assert_held(&(block)->lock)
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistruct tcf_filter_chain_list_item {
46462306a36Sopenharmony_ci	struct list_head list;
46562306a36Sopenharmony_ci	tcf_chain_head_change_t *chain_head_change;
46662306a36Sopenharmony_ci	void *chain_head_change_priv;
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_create(struct tcf_block *block,
47062306a36Sopenharmony_ci					  u32 chain_index)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct tcf_chain *chain;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	chain = kzalloc(sizeof(*chain), GFP_KERNEL);
47762306a36Sopenharmony_ci	if (!chain)
47862306a36Sopenharmony_ci		return NULL;
47962306a36Sopenharmony_ci	list_add_tail_rcu(&chain->list, &block->chain_list);
48062306a36Sopenharmony_ci	mutex_init(&chain->filter_chain_lock);
48162306a36Sopenharmony_ci	chain->block = block;
48262306a36Sopenharmony_ci	chain->index = chain_index;
48362306a36Sopenharmony_ci	chain->refcnt = 1;
48462306a36Sopenharmony_ci	if (!chain->index)
48562306a36Sopenharmony_ci		block->chain0.chain = chain;
48662306a36Sopenharmony_ci	return chain;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item,
49062306a36Sopenharmony_ci				       struct tcf_proto *tp_head)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	if (item->chain_head_change)
49362306a36Sopenharmony_ci		item->chain_head_change(tp_head, item->chain_head_change_priv);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic void tcf_chain0_head_change(struct tcf_chain *chain,
49762306a36Sopenharmony_ci				   struct tcf_proto *tp_head)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
50062306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (chain->index)
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	mutex_lock(&block->lock);
50662306a36Sopenharmony_ci	list_for_each_entry(item, &block->chain0.filter_chain_list, list)
50762306a36Sopenharmony_ci		tcf_chain_head_change_item(item, tp_head);
50862306a36Sopenharmony_ci	mutex_unlock(&block->lock);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/* Returns true if block can be safely freed. */
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic bool tcf_chain_detach(struct tcf_chain *chain)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	list_del_rcu(&chain->list);
52062306a36Sopenharmony_ci	if (!chain->index)
52162306a36Sopenharmony_ci		block->chain0.chain = NULL;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (list_empty(&block->chain_list) &&
52462306a36Sopenharmony_ci	    refcount_read(&block->refcnt) == 0)
52562306a36Sopenharmony_ci		return true;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return false;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void tcf_block_destroy(struct tcf_block *block)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	mutex_destroy(&block->lock);
53362306a36Sopenharmony_ci	mutex_destroy(&block->proto_destroy_lock);
53462306a36Sopenharmony_ci	kfree_rcu(block, rcu);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	mutex_destroy(&chain->filter_chain_lock);
54262306a36Sopenharmony_ci	kfree_rcu(chain, rcu);
54362306a36Sopenharmony_ci	if (free_block)
54462306a36Sopenharmony_ci		tcf_block_destroy(block);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void tcf_chain_hold(struct tcf_chain *chain)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	ASSERT_BLOCK_LOCKED(chain->block);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	++chain->refcnt;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic bool tcf_chain_held_by_acts_only(struct tcf_chain *chain)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	ASSERT_BLOCK_LOCKED(chain->block);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* In case all the references are action references, this
55962306a36Sopenharmony_ci	 * chain should not be shown to the user.
56062306a36Sopenharmony_ci	 */
56162306a36Sopenharmony_ci	return chain->refcnt == chain->action_refcnt;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
56562306a36Sopenharmony_ci					  u32 chain_index)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct tcf_chain *chain;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	list_for_each_entry(chain, &block->chain_list, list) {
57262306a36Sopenharmony_ci		if (chain->index == chain_index)
57362306a36Sopenharmony_ci			return chain;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	return NULL;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
57962306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block,
58062306a36Sopenharmony_ci					      u32 chain_index)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct tcf_chain *chain;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	list_for_each_entry_rcu(chain, &block->chain_list, list) {
58562306a36Sopenharmony_ci		if (chain->index == chain_index)
58662306a36Sopenharmony_ci			return chain;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	return NULL;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci#endif
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
59362306a36Sopenharmony_ci			   u32 seq, u16 flags, int event, bool unicast,
59462306a36Sopenharmony_ci			   struct netlink_ext_ack *extack);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
59762306a36Sopenharmony_ci					 u32 chain_index, bool create,
59862306a36Sopenharmony_ci					 bool by_act)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct tcf_chain *chain = NULL;
60162306a36Sopenharmony_ci	bool is_first_reference;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	mutex_lock(&block->lock);
60462306a36Sopenharmony_ci	chain = tcf_chain_lookup(block, chain_index);
60562306a36Sopenharmony_ci	if (chain) {
60662306a36Sopenharmony_ci		tcf_chain_hold(chain);
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		if (!create)
60962306a36Sopenharmony_ci			goto errout;
61062306a36Sopenharmony_ci		chain = tcf_chain_create(block, chain_index);
61162306a36Sopenharmony_ci		if (!chain)
61262306a36Sopenharmony_ci			goto errout;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (by_act)
61662306a36Sopenharmony_ci		++chain->action_refcnt;
61762306a36Sopenharmony_ci	is_first_reference = chain->refcnt - chain->action_refcnt == 1;
61862306a36Sopenharmony_ci	mutex_unlock(&block->lock);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Send notification only in case we got the first
62162306a36Sopenharmony_ci	 * non-action reference. Until then, the chain acts only as
62262306a36Sopenharmony_ci	 * a placeholder for actions pointing to it and user ought
62362306a36Sopenharmony_ci	 * not know about them.
62462306a36Sopenharmony_ci	 */
62562306a36Sopenharmony_ci	if (is_first_reference && !by_act)
62662306a36Sopenharmony_ci		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
62762306a36Sopenharmony_ci				RTM_NEWCHAIN, false, NULL);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return chain;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cierrout:
63262306a36Sopenharmony_ci	mutex_unlock(&block->lock);
63362306a36Sopenharmony_ci	return chain;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
63762306a36Sopenharmony_ci				       bool create)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	return __tcf_chain_get(block, chain_index, create, false);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistruct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	return __tcf_chain_get(block, chain_index, true, true);
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_get_by_act);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
64962306a36Sopenharmony_ci			       void *tmplt_priv);
65062306a36Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
65162306a36Sopenharmony_ci				  void *tmplt_priv, u32 chain_index,
65262306a36Sopenharmony_ci				  struct tcf_block *block, struct sk_buff *oskb,
65362306a36Sopenharmony_ci				  u32 seq, u16 flags, bool unicast);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
65662306a36Sopenharmony_ci			    bool explicitly_created)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
65962306a36Sopenharmony_ci	const struct tcf_proto_ops *tmplt_ops;
66062306a36Sopenharmony_ci	unsigned int refcnt, non_act_refcnt;
66162306a36Sopenharmony_ci	bool free_block = false;
66262306a36Sopenharmony_ci	void *tmplt_priv;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	mutex_lock(&block->lock);
66562306a36Sopenharmony_ci	if (explicitly_created) {
66662306a36Sopenharmony_ci		if (!chain->explicitly_created) {
66762306a36Sopenharmony_ci			mutex_unlock(&block->lock);
66862306a36Sopenharmony_ci			return;
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci		chain->explicitly_created = false;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (by_act)
67462306a36Sopenharmony_ci		chain->action_refcnt--;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* tc_chain_notify_delete can't be called while holding block lock.
67762306a36Sopenharmony_ci	 * However, when block is unlocked chain can be changed concurrently, so
67862306a36Sopenharmony_ci	 * save these to temporary variables.
67962306a36Sopenharmony_ci	 */
68062306a36Sopenharmony_ci	refcnt = --chain->refcnt;
68162306a36Sopenharmony_ci	non_act_refcnt = refcnt - chain->action_refcnt;
68262306a36Sopenharmony_ci	tmplt_ops = chain->tmplt_ops;
68362306a36Sopenharmony_ci	tmplt_priv = chain->tmplt_priv;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (non_act_refcnt == chain->explicitly_created && !by_act) {
68662306a36Sopenharmony_ci		if (non_act_refcnt == 0)
68762306a36Sopenharmony_ci			tc_chain_notify_delete(tmplt_ops, tmplt_priv,
68862306a36Sopenharmony_ci					       chain->index, block, NULL, 0, 0,
68962306a36Sopenharmony_ci					       false);
69062306a36Sopenharmony_ci		/* Last reference to chain, no need to lock. */
69162306a36Sopenharmony_ci		chain->flushing = false;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (refcnt == 0)
69562306a36Sopenharmony_ci		free_block = tcf_chain_detach(chain);
69662306a36Sopenharmony_ci	mutex_unlock(&block->lock);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (refcnt == 0) {
69962306a36Sopenharmony_ci		tc_chain_tmplt_del(tmplt_ops, tmplt_priv);
70062306a36Sopenharmony_ci		tcf_chain_destroy(chain, free_block);
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	__tcf_chain_put(chain, false, false);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_civoid tcf_chain_put_by_act(struct tcf_chain *chain)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	__tcf_chain_put(chain, true, false);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_put_by_act);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	__tcf_chain_put(chain, false, true);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct tcf_proto *tp, *tp_next;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
72562306a36Sopenharmony_ci	tp = tcf_chain_dereference(chain->filter_chain, chain);
72662306a36Sopenharmony_ci	while (tp) {
72762306a36Sopenharmony_ci		tp_next = rcu_dereference_protected(tp->next, 1);
72862306a36Sopenharmony_ci		tcf_proto_signal_destroying(chain, tp);
72962306a36Sopenharmony_ci		tp = tp_next;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci	tp = tcf_chain_dereference(chain->filter_chain, chain);
73262306a36Sopenharmony_ci	RCU_INIT_POINTER(chain->filter_chain, NULL);
73362306a36Sopenharmony_ci	tcf_chain0_head_change(chain, NULL);
73462306a36Sopenharmony_ci	chain->flushing = true;
73562306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	while (tp) {
73862306a36Sopenharmony_ci		tp_next = rcu_dereference_protected(tp->next, 1);
73962306a36Sopenharmony_ci		tcf_proto_put(tp, rtnl_held, NULL);
74062306a36Sopenharmony_ci		tp = tp_next;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block,
74562306a36Sopenharmony_ci			   struct flow_block_offload *bo);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void tcf_block_offload_init(struct flow_block_offload *bo,
74862306a36Sopenharmony_ci				   struct net_device *dev, struct Qdisc *sch,
74962306a36Sopenharmony_ci				   enum flow_block_command command,
75062306a36Sopenharmony_ci				   enum flow_block_binder_type binder_type,
75162306a36Sopenharmony_ci				   struct flow_block *flow_block,
75262306a36Sopenharmony_ci				   bool shared, struct netlink_ext_ack *extack)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	bo->net = dev_net(dev);
75562306a36Sopenharmony_ci	bo->command = command;
75662306a36Sopenharmony_ci	bo->binder_type = binder_type;
75762306a36Sopenharmony_ci	bo->block = flow_block;
75862306a36Sopenharmony_ci	bo->block_shared = shared;
75962306a36Sopenharmony_ci	bo->extack = extack;
76062306a36Sopenharmony_ci	bo->sch = sch;
76162306a36Sopenharmony_ci	bo->cb_list_head = &flow_block->cb_list;
76262306a36Sopenharmony_ci	INIT_LIST_HEAD(&bo->cb_list);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block,
76662306a36Sopenharmony_ci			     struct flow_block_offload *bo);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void tc_block_indr_cleanup(struct flow_block_cb *block_cb)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct tcf_block *block = block_cb->indr.data;
77162306a36Sopenharmony_ci	struct net_device *dev = block_cb->indr.dev;
77262306a36Sopenharmony_ci	struct Qdisc *sch = block_cb->indr.sch;
77362306a36Sopenharmony_ci	struct netlink_ext_ack extack = {};
77462306a36Sopenharmony_ci	struct flow_block_offload bo = {};
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	tcf_block_offload_init(&bo, dev, sch, FLOW_BLOCK_UNBIND,
77762306a36Sopenharmony_ci			       block_cb->indr.binder_type,
77862306a36Sopenharmony_ci			       &block->flow_block, tcf_block_shared(block),
77962306a36Sopenharmony_ci			       &extack);
78062306a36Sopenharmony_ci	rtnl_lock();
78162306a36Sopenharmony_ci	down_write(&block->cb_lock);
78262306a36Sopenharmony_ci	list_del(&block_cb->driver_list);
78362306a36Sopenharmony_ci	list_move(&block_cb->list, &bo.cb_list);
78462306a36Sopenharmony_ci	tcf_block_unbind(block, &bo);
78562306a36Sopenharmony_ci	up_write(&block->cb_lock);
78662306a36Sopenharmony_ci	rtnl_unlock();
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic bool tcf_block_offload_in_use(struct tcf_block *block)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	return atomic_read(&block->offloadcnt);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int tcf_block_offload_cmd(struct tcf_block *block,
79562306a36Sopenharmony_ci				 struct net_device *dev, struct Qdisc *sch,
79662306a36Sopenharmony_ci				 struct tcf_block_ext_info *ei,
79762306a36Sopenharmony_ci				 enum flow_block_command command,
79862306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct flow_block_offload bo = {};
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	tcf_block_offload_init(&bo, dev, sch, command, ei->binder_type,
80362306a36Sopenharmony_ci			       &block->flow_block, tcf_block_shared(block),
80462306a36Sopenharmony_ci			       extack);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_setup_tc) {
80762306a36Sopenharmony_ci		int err;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
81062306a36Sopenharmony_ci		if (err < 0) {
81162306a36Sopenharmony_ci			if (err != -EOPNOTSUPP)
81262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed");
81362306a36Sopenharmony_ci			return err;
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		return tcf_block_setup(block, &bo);
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	flow_indr_dev_setup_offload(dev, sch, TC_SETUP_BLOCK, block, &bo,
82062306a36Sopenharmony_ci				    tc_block_indr_cleanup);
82162306a36Sopenharmony_ci	tcf_block_setup(block, &bo);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return -EOPNOTSUPP;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
82762306a36Sopenharmony_ci				  struct tcf_block_ext_info *ei,
82862306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct net_device *dev = q->dev_queue->dev;
83162306a36Sopenharmony_ci	int err;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	down_write(&block->cb_lock);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* If tc offload feature is disabled and the block we try to bind
83662306a36Sopenharmony_ci	 * to already has some offloaded filters, forbid to bind.
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_setup_tc &&
83962306a36Sopenharmony_ci	    !tc_can_offload(dev) &&
84062306a36Sopenharmony_ci	    tcf_block_offload_in_use(block)) {
84162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled");
84262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
84362306a36Sopenharmony_ci		goto err_unlock;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_BIND, extack);
84762306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
84862306a36Sopenharmony_ci		goto no_offload_dev_inc;
84962306a36Sopenharmony_ci	if (err)
85062306a36Sopenharmony_ci		goto err_unlock;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	up_write(&block->cb_lock);
85362306a36Sopenharmony_ci	return 0;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cino_offload_dev_inc:
85662306a36Sopenharmony_ci	if (tcf_block_offload_in_use(block))
85762306a36Sopenharmony_ci		goto err_unlock;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	err = 0;
86062306a36Sopenharmony_ci	block->nooffloaddevcnt++;
86162306a36Sopenharmony_cierr_unlock:
86262306a36Sopenharmony_ci	up_write(&block->cb_lock);
86362306a36Sopenharmony_ci	return err;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
86762306a36Sopenharmony_ci				     struct tcf_block_ext_info *ei)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct net_device *dev = q->dev_queue->dev;
87062306a36Sopenharmony_ci	int err;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	down_write(&block->cb_lock);
87362306a36Sopenharmony_ci	err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_UNBIND, NULL);
87462306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
87562306a36Sopenharmony_ci		goto no_offload_dev_dec;
87662306a36Sopenharmony_ci	up_write(&block->cb_lock);
87762306a36Sopenharmony_ci	return;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cino_offload_dev_dec:
88062306a36Sopenharmony_ci	WARN_ON(block->nooffloaddevcnt-- == 0);
88162306a36Sopenharmony_ci	up_write(&block->cb_lock);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic int
88562306a36Sopenharmony_citcf_chain0_head_change_cb_add(struct tcf_block *block,
88662306a36Sopenharmony_ci			      struct tcf_block_ext_info *ei,
88762306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
89062306a36Sopenharmony_ci	struct tcf_chain *chain0;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	item = kmalloc(sizeof(*item), GFP_KERNEL);
89362306a36Sopenharmony_ci	if (!item) {
89462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed");
89562306a36Sopenharmony_ci		return -ENOMEM;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci	item->chain_head_change = ei->chain_head_change;
89862306a36Sopenharmony_ci	item->chain_head_change_priv = ei->chain_head_change_priv;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	mutex_lock(&block->lock);
90162306a36Sopenharmony_ci	chain0 = block->chain0.chain;
90262306a36Sopenharmony_ci	if (chain0)
90362306a36Sopenharmony_ci		tcf_chain_hold(chain0);
90462306a36Sopenharmony_ci	else
90562306a36Sopenharmony_ci		list_add(&item->list, &block->chain0.filter_chain_list);
90662306a36Sopenharmony_ci	mutex_unlock(&block->lock);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (chain0) {
90962306a36Sopenharmony_ci		struct tcf_proto *tp_head;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		mutex_lock(&chain0->filter_chain_lock);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		tp_head = tcf_chain_dereference(chain0->filter_chain, chain0);
91462306a36Sopenharmony_ci		if (tp_head)
91562306a36Sopenharmony_ci			tcf_chain_head_change_item(item, tp_head);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		mutex_lock(&block->lock);
91862306a36Sopenharmony_ci		list_add(&item->list, &block->chain0.filter_chain_list);
91962306a36Sopenharmony_ci		mutex_unlock(&block->lock);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		mutex_unlock(&chain0->filter_chain_lock);
92262306a36Sopenharmony_ci		tcf_chain_put(chain0);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return 0;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic void
92962306a36Sopenharmony_citcf_chain0_head_change_cb_del(struct tcf_block *block,
93062306a36Sopenharmony_ci			      struct tcf_block_ext_info *ei)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	mutex_lock(&block->lock);
93562306a36Sopenharmony_ci	list_for_each_entry(item, &block->chain0.filter_chain_list, list) {
93662306a36Sopenharmony_ci		if ((!ei->chain_head_change && !ei->chain_head_change_priv) ||
93762306a36Sopenharmony_ci		    (item->chain_head_change == ei->chain_head_change &&
93862306a36Sopenharmony_ci		     item->chain_head_change_priv == ei->chain_head_change_priv)) {
93962306a36Sopenharmony_ci			if (block->chain0.chain)
94062306a36Sopenharmony_ci				tcf_chain_head_change_item(item, NULL);
94162306a36Sopenharmony_ci			list_del(&item->list);
94262306a36Sopenharmony_ci			mutex_unlock(&block->lock);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci			kfree(item);
94562306a36Sopenharmony_ci			return;
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci	mutex_unlock(&block->lock);
94962306a36Sopenharmony_ci	WARN_ON(1);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistruct tcf_net {
95362306a36Sopenharmony_ci	spinlock_t idr_lock; /* Protects idr */
95462306a36Sopenharmony_ci	struct idr idr;
95562306a36Sopenharmony_ci};
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic unsigned int tcf_net_id;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int tcf_block_insert(struct tcf_block *block, struct net *net,
96062306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
96362306a36Sopenharmony_ci	int err;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
96662306a36Sopenharmony_ci	spin_lock(&tn->idr_lock);
96762306a36Sopenharmony_ci	err = idr_alloc_u32(&tn->idr, block, &block->index, block->index,
96862306a36Sopenharmony_ci			    GFP_NOWAIT);
96962306a36Sopenharmony_ci	spin_unlock(&tn->idr_lock);
97062306a36Sopenharmony_ci	idr_preload_end();
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return err;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic void tcf_block_remove(struct tcf_block *block, struct net *net)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	spin_lock(&tn->idr_lock);
98062306a36Sopenharmony_ci	idr_remove(&tn->idr, block->index);
98162306a36Sopenharmony_ci	spin_unlock(&tn->idr_lock);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
98562306a36Sopenharmony_ci					  u32 block_index,
98662306a36Sopenharmony_ci					  struct netlink_ext_ack *extack)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct tcf_block *block;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	block = kzalloc(sizeof(*block), GFP_KERNEL);
99162306a36Sopenharmony_ci	if (!block) {
99262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Memory allocation for block failed");
99362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci	mutex_init(&block->lock);
99662306a36Sopenharmony_ci	mutex_init(&block->proto_destroy_lock);
99762306a36Sopenharmony_ci	init_rwsem(&block->cb_lock);
99862306a36Sopenharmony_ci	flow_block_init(&block->flow_block);
99962306a36Sopenharmony_ci	INIT_LIST_HEAD(&block->chain_list);
100062306a36Sopenharmony_ci	INIT_LIST_HEAD(&block->owner_list);
100162306a36Sopenharmony_ci	INIT_LIST_HEAD(&block->chain0.filter_chain_list);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	refcount_set(&block->refcnt, 1);
100462306a36Sopenharmony_ci	block->net = net;
100562306a36Sopenharmony_ci	block->index = block_index;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/* Don't store q pointer for blocks which are shared */
100862306a36Sopenharmony_ci	if (!tcf_block_shared(block))
100962306a36Sopenharmony_ci		block->q = q;
101062306a36Sopenharmony_ci	return block;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	return idr_find(&tn->idr, block_index);
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct tcf_block *block;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	rcu_read_lock();
102562306a36Sopenharmony_ci	block = tcf_block_lookup(net, block_index);
102662306a36Sopenharmony_ci	if (block && !refcount_inc_not_zero(&block->refcnt))
102762306a36Sopenharmony_ci		block = NULL;
102862306a36Sopenharmony_ci	rcu_read_unlock();
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return block;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic struct tcf_chain *
103462306a36Sopenharmony_ci__tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	mutex_lock(&block->lock);
103762306a36Sopenharmony_ci	if (chain)
103862306a36Sopenharmony_ci		chain = list_is_last(&chain->list, &block->chain_list) ?
103962306a36Sopenharmony_ci			NULL : list_next_entry(chain, list);
104062306a36Sopenharmony_ci	else
104162306a36Sopenharmony_ci		chain = list_first_entry_or_null(&block->chain_list,
104262306a36Sopenharmony_ci						 struct tcf_chain, list);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* skip all action-only chains */
104562306a36Sopenharmony_ci	while (chain && tcf_chain_held_by_acts_only(chain))
104662306a36Sopenharmony_ci		chain = list_is_last(&chain->list, &block->chain_list) ?
104762306a36Sopenharmony_ci			NULL : list_next_entry(chain, list);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (chain)
105062306a36Sopenharmony_ci		tcf_chain_hold(chain);
105162306a36Sopenharmony_ci	mutex_unlock(&block->lock);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return chain;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci/* Function to be used by all clients that want to iterate over all chains on
105762306a36Sopenharmony_ci * block. It properly obtains block->lock and takes reference to chain before
105862306a36Sopenharmony_ci * returning it. Users of this function must be tolerant to concurrent chain
105962306a36Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is
106062306a36Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide
106162306a36Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with
106262306a36Sopenharmony_ci * data and sent to user-space.
106362306a36Sopenharmony_ci */
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistruct tcf_chain *
106662306a36Sopenharmony_citcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (chain)
107162306a36Sopenharmony_ci		tcf_chain_put(chain);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	return chain_next;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_chain);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic struct tcf_proto *
107862306a36Sopenharmony_ci__tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	u32 prio = 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	ASSERT_RTNL();
108362306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (!tp) {
108662306a36Sopenharmony_ci		tp = tcf_chain_dereference(chain->filter_chain, chain);
108762306a36Sopenharmony_ci	} else if (tcf_proto_is_deleting(tp)) {
108862306a36Sopenharmony_ci		/* 'deleting' flag is set and chain->filter_chain_lock was
108962306a36Sopenharmony_ci		 * unlocked, which means next pointer could be invalid. Restart
109062306a36Sopenharmony_ci		 * search.
109162306a36Sopenharmony_ci		 */
109262306a36Sopenharmony_ci		prio = tp->prio + 1;
109362306a36Sopenharmony_ci		tp = tcf_chain_dereference(chain->filter_chain, chain);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		for (; tp; tp = tcf_chain_dereference(tp->next, chain))
109662306a36Sopenharmony_ci			if (!tp->deleting && tp->prio >= prio)
109762306a36Sopenharmony_ci				break;
109862306a36Sopenharmony_ci	} else {
109962306a36Sopenharmony_ci		tp = tcf_chain_dereference(tp->next, chain);
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (tp)
110362306a36Sopenharmony_ci		tcf_proto_get(tp);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	return tp;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci/* Function to be used by all clients that want to iterate over all tp's on
111162306a36Sopenharmony_ci * chain. Users of this function must be tolerant to concurrent tp
111262306a36Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is
111362306a36Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide
111462306a36Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with
111562306a36Sopenharmony_ci * data and sent to user-space.
111662306a36Sopenharmony_ci */
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistruct tcf_proto *
111962306a36Sopenharmony_citcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (tp)
112462306a36Sopenharmony_ci		tcf_proto_put(tp, true, NULL);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	return tp_next;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_proto);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct tcf_chain *chain;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	/* Last reference to block. At this point chains cannot be added or
113562306a36Sopenharmony_ci	 * removed concurrently.
113662306a36Sopenharmony_ci	 */
113762306a36Sopenharmony_ci	for (chain = tcf_get_next_chain(block, NULL);
113862306a36Sopenharmony_ci	     chain;
113962306a36Sopenharmony_ci	     chain = tcf_get_next_chain(block, chain)) {
114062306a36Sopenharmony_ci		tcf_chain_put_explicitly_created(chain);
114162306a36Sopenharmony_ci		tcf_chain_flush(chain, rtnl_held);
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/* Lookup Qdisc and increments its reference counter.
114662306a36Sopenharmony_ci * Set parent, if necessary.
114762306a36Sopenharmony_ci */
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
115062306a36Sopenharmony_ci			    u32 *parent, int ifindex, bool rtnl_held,
115162306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	const struct Qdisc_class_ops *cops;
115462306a36Sopenharmony_ci	struct net_device *dev;
115562306a36Sopenharmony_ci	int err = 0;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
115862306a36Sopenharmony_ci		return 0;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	rcu_read_lock();
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	/* Find link */
116362306a36Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
116462306a36Sopenharmony_ci	if (!dev) {
116562306a36Sopenharmony_ci		rcu_read_unlock();
116662306a36Sopenharmony_ci		return -ENODEV;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* Find qdisc */
117062306a36Sopenharmony_ci	if (!*parent) {
117162306a36Sopenharmony_ci		*q = rcu_dereference(dev->qdisc);
117262306a36Sopenharmony_ci		*parent = (*q)->handle;
117362306a36Sopenharmony_ci	} else {
117462306a36Sopenharmony_ci		*q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
117562306a36Sopenharmony_ci		if (!*q) {
117662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
117762306a36Sopenharmony_ci			err = -EINVAL;
117862306a36Sopenharmony_ci			goto errout_rcu;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	*q = qdisc_refcount_inc_nz(*q);
118362306a36Sopenharmony_ci	if (!*q) {
118462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
118562306a36Sopenharmony_ci		err = -EINVAL;
118662306a36Sopenharmony_ci		goto errout_rcu;
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	/* Is it classful? */
119062306a36Sopenharmony_ci	cops = (*q)->ops->cl_ops;
119162306a36Sopenharmony_ci	if (!cops) {
119262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Qdisc not classful");
119362306a36Sopenharmony_ci		err = -EINVAL;
119462306a36Sopenharmony_ci		goto errout_qdisc;
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (!cops->tcf_block) {
119862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
119962306a36Sopenharmony_ci		err = -EOPNOTSUPP;
120062306a36Sopenharmony_ci		goto errout_qdisc;
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cierrout_rcu:
120462306a36Sopenharmony_ci	/* At this point we know that qdisc is not noop_qdisc,
120562306a36Sopenharmony_ci	 * which means that qdisc holds a reference to net_device
120662306a36Sopenharmony_ci	 * and we hold a reference to qdisc, so it is safe to release
120762306a36Sopenharmony_ci	 * rcu read lock.
120862306a36Sopenharmony_ci	 */
120962306a36Sopenharmony_ci	rcu_read_unlock();
121062306a36Sopenharmony_ci	return err;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cierrout_qdisc:
121362306a36Sopenharmony_ci	rcu_read_unlock();
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (rtnl_held)
121662306a36Sopenharmony_ci		qdisc_put(*q);
121762306a36Sopenharmony_ci	else
121862306a36Sopenharmony_ci		qdisc_put_unlocked(*q);
121962306a36Sopenharmony_ci	*q = NULL;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	return err;
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl,
122562306a36Sopenharmony_ci			       int ifindex, struct netlink_ext_ack *extack)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
122862306a36Sopenharmony_ci		return 0;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* Do we search for filter, attached to class? */
123162306a36Sopenharmony_ci	if (TC_H_MIN(parent)) {
123262306a36Sopenharmony_ci		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci		*cl = cops->find(q, parent);
123562306a36Sopenharmony_ci		if (*cl == 0) {
123662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Specified class doesn't exist");
123762306a36Sopenharmony_ci			return -ENOENT;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return 0;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q,
124562306a36Sopenharmony_ci					  unsigned long cl, int ifindex,
124662306a36Sopenharmony_ci					  u32 block_index,
124762306a36Sopenharmony_ci					  struct netlink_ext_ack *extack)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct tcf_block *block;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
125262306a36Sopenharmony_ci		block = tcf_block_refcnt_get(net, block_index);
125362306a36Sopenharmony_ci		if (!block) {
125462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Block of given index was not found");
125562306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
125662306a36Sopenharmony_ci		}
125762306a36Sopenharmony_ci	} else {
125862306a36Sopenharmony_ci		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		block = cops->tcf_block(q, cl, extack);
126162306a36Sopenharmony_ci		if (!block)
126262306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		if (tcf_block_shared(block)) {
126562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters");
126662306a36Sopenharmony_ci			return ERR_PTR(-EOPNOTSUPP);
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		/* Always take reference to block in order to support execution
127062306a36Sopenharmony_ci		 * of rules update path of cls API without rtnl lock. Caller
127162306a36Sopenharmony_ci		 * must release block when it is finished using it. 'if' block
127262306a36Sopenharmony_ci		 * of this conditional obtain reference to block by calling
127362306a36Sopenharmony_ci		 * tcf_block_refcnt_get().
127462306a36Sopenharmony_ci		 */
127562306a36Sopenharmony_ci		refcount_inc(&block->refcnt);
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	return block;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
128262306a36Sopenharmony_ci			    struct tcf_block_ext_info *ei, bool rtnl_held)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) {
128562306a36Sopenharmony_ci		/* Flushing/putting all chains will cause the block to be
128662306a36Sopenharmony_ci		 * deallocated when last chain is freed. However, if chain_list
128762306a36Sopenharmony_ci		 * is empty, block has to be manually deallocated. After block
128862306a36Sopenharmony_ci		 * reference counter reached 0, it is no longer possible to
128962306a36Sopenharmony_ci		 * increment it or add new chains to block.
129062306a36Sopenharmony_ci		 */
129162306a36Sopenharmony_ci		bool free_block = list_empty(&block->chain_list);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		mutex_unlock(&block->lock);
129462306a36Sopenharmony_ci		if (tcf_block_shared(block))
129562306a36Sopenharmony_ci			tcf_block_remove(block, block->net);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		if (q)
129862306a36Sopenharmony_ci			tcf_block_offload_unbind(block, q, ei);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		if (free_block)
130162306a36Sopenharmony_ci			tcf_block_destroy(block);
130262306a36Sopenharmony_ci		else
130362306a36Sopenharmony_ci			tcf_block_flush_all_chains(block, rtnl_held);
130462306a36Sopenharmony_ci	} else if (q) {
130562306a36Sopenharmony_ci		tcf_block_offload_unbind(block, q, ei);
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	__tcf_block_put(block, NULL, NULL, rtnl_held);
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci/* Find tcf block.
131562306a36Sopenharmony_ci * Set q, parent, cl when appropriate.
131662306a36Sopenharmony_ci */
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
131962306a36Sopenharmony_ci					u32 *parent, unsigned long *cl,
132062306a36Sopenharmony_ci					int ifindex, u32 block_index,
132162306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct tcf_block *block;
132462306a36Sopenharmony_ci	int err = 0;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	ASSERT_RTNL();
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack);
132962306a36Sopenharmony_ci	if (err)
133062306a36Sopenharmony_ci		goto errout;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack);
133362306a36Sopenharmony_ci	if (err)
133462306a36Sopenharmony_ci		goto errout_qdisc;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack);
133762306a36Sopenharmony_ci	if (IS_ERR(block)) {
133862306a36Sopenharmony_ci		err = PTR_ERR(block);
133962306a36Sopenharmony_ci		goto errout_qdisc;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	return block;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cierrout_qdisc:
134562306a36Sopenharmony_ci	if (*q)
134662306a36Sopenharmony_ci		qdisc_put(*q);
134762306a36Sopenharmony_cierrout:
134862306a36Sopenharmony_ci	*q = NULL;
134962306a36Sopenharmony_ci	return ERR_PTR(err);
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_cistatic void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
135362306a36Sopenharmony_ci			      bool rtnl_held)
135462306a36Sopenharmony_ci{
135562306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(block))
135662306a36Sopenharmony_ci		tcf_block_refcnt_put(block, rtnl_held);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	if (q) {
135962306a36Sopenharmony_ci		if (rtnl_held)
136062306a36Sopenharmony_ci			qdisc_put(q);
136162306a36Sopenharmony_ci		else
136262306a36Sopenharmony_ci			qdisc_put_unlocked(q);
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistruct tcf_block_owner_item {
136762306a36Sopenharmony_ci	struct list_head list;
136862306a36Sopenharmony_ci	struct Qdisc *q;
136962306a36Sopenharmony_ci	enum flow_block_binder_type binder_type;
137062306a36Sopenharmony_ci};
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic void
137362306a36Sopenharmony_citcf_block_owner_netif_keep_dst(struct tcf_block *block,
137462306a36Sopenharmony_ci			       struct Qdisc *q,
137562306a36Sopenharmony_ci			       enum flow_block_binder_type binder_type)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	if (block->keep_dst &&
137862306a36Sopenharmony_ci	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
137962306a36Sopenharmony_ci	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
138062306a36Sopenharmony_ci		netif_keep_dst(qdisc_dev(q));
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_civoid tcf_block_netif_keep_dst(struct tcf_block *block)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct tcf_block_owner_item *item;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	block->keep_dst = true;
138862306a36Sopenharmony_ci	list_for_each_entry(item, &block->owner_list, list)
138962306a36Sopenharmony_ci		tcf_block_owner_netif_keep_dst(block, item->q,
139062306a36Sopenharmony_ci					       item->binder_type);
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_netif_keep_dst);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic int tcf_block_owner_add(struct tcf_block *block,
139562306a36Sopenharmony_ci			       struct Qdisc *q,
139662306a36Sopenharmony_ci			       enum flow_block_binder_type binder_type)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct tcf_block_owner_item *item;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	item = kmalloc(sizeof(*item), GFP_KERNEL);
140162306a36Sopenharmony_ci	if (!item)
140262306a36Sopenharmony_ci		return -ENOMEM;
140362306a36Sopenharmony_ci	item->q = q;
140462306a36Sopenharmony_ci	item->binder_type = binder_type;
140562306a36Sopenharmony_ci	list_add(&item->list, &block->owner_list);
140662306a36Sopenharmony_ci	return 0;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cistatic void tcf_block_owner_del(struct tcf_block *block,
141062306a36Sopenharmony_ci				struct Qdisc *q,
141162306a36Sopenharmony_ci				enum flow_block_binder_type binder_type)
141262306a36Sopenharmony_ci{
141362306a36Sopenharmony_ci	struct tcf_block_owner_item *item;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	list_for_each_entry(item, &block->owner_list, list) {
141662306a36Sopenharmony_ci		if (item->q == q && item->binder_type == binder_type) {
141762306a36Sopenharmony_ci			list_del(&item->list);
141862306a36Sopenharmony_ci			kfree(item);
141962306a36Sopenharmony_ci			return;
142062306a36Sopenharmony_ci		}
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci	WARN_ON(1);
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ciint tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
142662306a36Sopenharmony_ci		      struct tcf_block_ext_info *ei,
142762306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct net *net = qdisc_net(q);
143062306a36Sopenharmony_ci	struct tcf_block *block = NULL;
143162306a36Sopenharmony_ci	int err;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (ei->block_index)
143462306a36Sopenharmony_ci		/* block_index not 0 means the shared block is requested */
143562306a36Sopenharmony_ci		block = tcf_block_refcnt_get(net, ei->block_index);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	if (!block) {
143862306a36Sopenharmony_ci		block = tcf_block_create(net, q, ei->block_index, extack);
143962306a36Sopenharmony_ci		if (IS_ERR(block))
144062306a36Sopenharmony_ci			return PTR_ERR(block);
144162306a36Sopenharmony_ci		if (tcf_block_shared(block)) {
144262306a36Sopenharmony_ci			err = tcf_block_insert(block, net, extack);
144362306a36Sopenharmony_ci			if (err)
144462306a36Sopenharmony_ci				goto err_block_insert;
144562306a36Sopenharmony_ci		}
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	err = tcf_block_owner_add(block, q, ei->binder_type);
144962306a36Sopenharmony_ci	if (err)
145062306a36Sopenharmony_ci		goto err_block_owner_add;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	tcf_block_owner_netif_keep_dst(block, q, ei->binder_type);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	err = tcf_chain0_head_change_cb_add(block, ei, extack);
145562306a36Sopenharmony_ci	if (err)
145662306a36Sopenharmony_ci		goto err_chain0_head_change_cb_add;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	err = tcf_block_offload_bind(block, q, ei, extack);
145962306a36Sopenharmony_ci	if (err)
146062306a36Sopenharmony_ci		goto err_block_offload_bind;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	*p_block = block;
146362306a36Sopenharmony_ci	return 0;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_cierr_block_offload_bind:
146662306a36Sopenharmony_ci	tcf_chain0_head_change_cb_del(block, ei);
146762306a36Sopenharmony_cierr_chain0_head_change_cb_add:
146862306a36Sopenharmony_ci	tcf_block_owner_del(block, q, ei->binder_type);
146962306a36Sopenharmony_cierr_block_owner_add:
147062306a36Sopenharmony_cierr_block_insert:
147162306a36Sopenharmony_ci	tcf_block_refcnt_put(block, true);
147262306a36Sopenharmony_ci	return err;
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get_ext);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_cistatic void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv)
147762306a36Sopenharmony_ci{
147862306a36Sopenharmony_ci	struct tcf_proto __rcu **p_filter_chain = priv;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	rcu_assign_pointer(*p_filter_chain, tp_head);
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ciint tcf_block_get(struct tcf_block **p_block,
148462306a36Sopenharmony_ci		  struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
148562306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct tcf_block_ext_info ei = {
148862306a36Sopenharmony_ci		.chain_head_change = tcf_chain_head_change_dflt,
148962306a36Sopenharmony_ci		.chain_head_change_priv = p_filter_chain,
149062306a36Sopenharmony_ci	};
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	WARN_ON(!p_filter_chain);
149362306a36Sopenharmony_ci	return tcf_block_get_ext(p_block, q, &ei, extack);
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci/* XXX: Standalone actions are not allowed to jump to any chain, and bound
149862306a36Sopenharmony_ci * actions should be all removed after flushing.
149962306a36Sopenharmony_ci */
150062306a36Sopenharmony_civoid tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
150162306a36Sopenharmony_ci		       struct tcf_block_ext_info *ei)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	if (!block)
150462306a36Sopenharmony_ci		return;
150562306a36Sopenharmony_ci	tcf_chain0_head_change_cb_del(block, ei);
150662306a36Sopenharmony_ci	tcf_block_owner_del(block, q, ei->binder_type);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	__tcf_block_put(block, q, ei, true);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put_ext);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_civoid tcf_block_put(struct tcf_block *block)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct tcf_block_ext_info ei = {0, };
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (!block)
151762306a36Sopenharmony_ci		return;
151862306a36Sopenharmony_ci	tcf_block_put_ext(block, block->q, &ei);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic int
152462306a36Sopenharmony_citcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb,
152562306a36Sopenharmony_ci			    void *cb_priv, bool add, bool offload_in_use,
152662306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct tcf_chain *chain, *chain_prev;
152962306a36Sopenharmony_ci	struct tcf_proto *tp, *tp_prev;
153062306a36Sopenharmony_ci	int err;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	for (chain = __tcf_get_next_chain(block, NULL);
153562306a36Sopenharmony_ci	     chain;
153662306a36Sopenharmony_ci	     chain_prev = chain,
153762306a36Sopenharmony_ci		     chain = __tcf_get_next_chain(block, chain),
153862306a36Sopenharmony_ci		     tcf_chain_put(chain_prev)) {
153962306a36Sopenharmony_ci		if (chain->tmplt_ops && add)
154062306a36Sopenharmony_ci			chain->tmplt_ops->tmplt_reoffload(chain, true, cb,
154162306a36Sopenharmony_ci							  cb_priv);
154262306a36Sopenharmony_ci		for (tp = __tcf_get_next_proto(chain, NULL); tp;
154362306a36Sopenharmony_ci		     tp_prev = tp,
154462306a36Sopenharmony_ci			     tp = __tcf_get_next_proto(chain, tp),
154562306a36Sopenharmony_ci			     tcf_proto_put(tp_prev, true, NULL)) {
154662306a36Sopenharmony_ci			if (tp->ops->reoffload) {
154762306a36Sopenharmony_ci				err = tp->ops->reoffload(tp, add, cb, cb_priv,
154862306a36Sopenharmony_ci							 extack);
154962306a36Sopenharmony_ci				if (err && add)
155062306a36Sopenharmony_ci					goto err_playback_remove;
155162306a36Sopenharmony_ci			} else if (add && offload_in_use) {
155262306a36Sopenharmony_ci				err = -EOPNOTSUPP;
155362306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support");
155462306a36Sopenharmony_ci				goto err_playback_remove;
155562306a36Sopenharmony_ci			}
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci		if (chain->tmplt_ops && !add)
155862306a36Sopenharmony_ci			chain->tmplt_ops->tmplt_reoffload(chain, false, cb,
155962306a36Sopenharmony_ci							  cb_priv);
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return 0;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cierr_playback_remove:
156562306a36Sopenharmony_ci	tcf_proto_put(tp, true, NULL);
156662306a36Sopenharmony_ci	tcf_chain_put(chain);
156762306a36Sopenharmony_ci	tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use,
156862306a36Sopenharmony_ci				    extack);
156962306a36Sopenharmony_ci	return err;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cistatic int tcf_block_bind(struct tcf_block *block,
157362306a36Sopenharmony_ci			  struct flow_block_offload *bo)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct flow_block_cb *block_cb, *next;
157662306a36Sopenharmony_ci	int err, i = 0;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	list_for_each_entry(block_cb, &bo->cb_list, list) {
158162306a36Sopenharmony_ci		err = tcf_block_playback_offloads(block, block_cb->cb,
158262306a36Sopenharmony_ci						  block_cb->cb_priv, true,
158362306a36Sopenharmony_ci						  tcf_block_offload_in_use(block),
158462306a36Sopenharmony_ci						  bo->extack);
158562306a36Sopenharmony_ci		if (err)
158662306a36Sopenharmony_ci			goto err_unroll;
158762306a36Sopenharmony_ci		if (!bo->unlocked_driver_cb)
158862306a36Sopenharmony_ci			block->lockeddevcnt++;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci		i++;
159162306a36Sopenharmony_ci	}
159262306a36Sopenharmony_ci	list_splice(&bo->cb_list, &block->flow_block.cb_list);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	return 0;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_cierr_unroll:
159762306a36Sopenharmony_ci	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
159862306a36Sopenharmony_ci		list_del(&block_cb->driver_list);
159962306a36Sopenharmony_ci		if (i-- > 0) {
160062306a36Sopenharmony_ci			list_del(&block_cb->list);
160162306a36Sopenharmony_ci			tcf_block_playback_offloads(block, block_cb->cb,
160262306a36Sopenharmony_ci						    block_cb->cb_priv, false,
160362306a36Sopenharmony_ci						    tcf_block_offload_in_use(block),
160462306a36Sopenharmony_ci						    NULL);
160562306a36Sopenharmony_ci			if (!bo->unlocked_driver_cb)
160662306a36Sopenharmony_ci				block->lockeddevcnt--;
160762306a36Sopenharmony_ci		}
160862306a36Sopenharmony_ci		flow_block_cb_free(block_cb);
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	return err;
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block,
161562306a36Sopenharmony_ci			     struct flow_block_offload *bo)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	struct flow_block_cb *block_cb, *next;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
162262306a36Sopenharmony_ci		tcf_block_playback_offloads(block, block_cb->cb,
162362306a36Sopenharmony_ci					    block_cb->cb_priv, false,
162462306a36Sopenharmony_ci					    tcf_block_offload_in_use(block),
162562306a36Sopenharmony_ci					    NULL);
162662306a36Sopenharmony_ci		list_del(&block_cb->list);
162762306a36Sopenharmony_ci		flow_block_cb_free(block_cb);
162862306a36Sopenharmony_ci		if (!bo->unlocked_driver_cb)
162962306a36Sopenharmony_ci			block->lockeddevcnt--;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block,
163462306a36Sopenharmony_ci			   struct flow_block_offload *bo)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	int err;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	switch (bo->command) {
163962306a36Sopenharmony_ci	case FLOW_BLOCK_BIND:
164062306a36Sopenharmony_ci		err = tcf_block_bind(block, bo);
164162306a36Sopenharmony_ci		break;
164262306a36Sopenharmony_ci	case FLOW_BLOCK_UNBIND:
164362306a36Sopenharmony_ci		err = 0;
164462306a36Sopenharmony_ci		tcf_block_unbind(block, bo);
164562306a36Sopenharmony_ci		break;
164662306a36Sopenharmony_ci	default:
164762306a36Sopenharmony_ci		WARN_ON_ONCE(1);
164862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	return err;
165262306a36Sopenharmony_ci}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci/* Main classifier routine: scans classifier chain attached
165562306a36Sopenharmony_ci * to this qdisc, (optionally) tests for protocol and asks
165662306a36Sopenharmony_ci * specific classifiers.
165762306a36Sopenharmony_ci */
165862306a36Sopenharmony_cistatic inline int __tcf_classify(struct sk_buff *skb,
165962306a36Sopenharmony_ci				 const struct tcf_proto *tp,
166062306a36Sopenharmony_ci				 const struct tcf_proto *orig_tp,
166162306a36Sopenharmony_ci				 struct tcf_result *res,
166262306a36Sopenharmony_ci				 bool compat_mode,
166362306a36Sopenharmony_ci				 struct tcf_exts_miss_cookie_node *n,
166462306a36Sopenharmony_ci				 int act_index,
166562306a36Sopenharmony_ci				 u32 *last_executed_chain)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
166862306a36Sopenharmony_ci	const int max_reclassify_loop = 16;
166962306a36Sopenharmony_ci	const struct tcf_proto *first_tp;
167062306a36Sopenharmony_ci	int limit = 0;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_cireclassify:
167362306a36Sopenharmony_ci#endif
167462306a36Sopenharmony_ci	for (; tp; tp = rcu_dereference_bh(tp->next)) {
167562306a36Sopenharmony_ci		__be16 protocol = skb_protocol(skb, false);
167662306a36Sopenharmony_ci		int err = 0;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci		if (n) {
167962306a36Sopenharmony_ci			struct tcf_exts *exts;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci			if (n->tp_prio != tp->prio)
168262306a36Sopenharmony_ci				continue;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci			/* We re-lookup the tp and chain based on index instead
168562306a36Sopenharmony_ci			 * of having hard refs and locks to them, so do a sanity
168662306a36Sopenharmony_ci			 * check if any of tp,chain,exts was replaced by the
168762306a36Sopenharmony_ci			 * time we got here with a cookie from hardware.
168862306a36Sopenharmony_ci			 */
168962306a36Sopenharmony_ci			if (unlikely(n->tp != tp || n->tp->chain != n->chain ||
169062306a36Sopenharmony_ci				     !tp->ops->get_exts))
169162306a36Sopenharmony_ci				return TC_ACT_SHOT;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci			exts = tp->ops->get_exts(tp, n->handle);
169462306a36Sopenharmony_ci			if (unlikely(!exts || n->exts != exts))
169562306a36Sopenharmony_ci				return TC_ACT_SHOT;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci			n = NULL;
169862306a36Sopenharmony_ci			err = tcf_exts_exec_ex(skb, exts, act_index, res);
169962306a36Sopenharmony_ci		} else {
170062306a36Sopenharmony_ci			if (tp->protocol != protocol &&
170162306a36Sopenharmony_ci			    tp->protocol != htons(ETH_P_ALL))
170262306a36Sopenharmony_ci				continue;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci			err = tc_classify(skb, tp, res);
170562306a36Sopenharmony_ci		}
170662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
170762306a36Sopenharmony_ci		if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) {
170862306a36Sopenharmony_ci			first_tp = orig_tp;
170962306a36Sopenharmony_ci			*last_executed_chain = first_tp->chain->index;
171062306a36Sopenharmony_ci			goto reset;
171162306a36Sopenharmony_ci		} else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) {
171262306a36Sopenharmony_ci			first_tp = res->goto_tp;
171362306a36Sopenharmony_ci			*last_executed_chain = err & TC_ACT_EXT_VAL_MASK;
171462306a36Sopenharmony_ci			goto reset;
171562306a36Sopenharmony_ci		}
171662306a36Sopenharmony_ci#endif
171762306a36Sopenharmony_ci		if (err >= 0)
171862306a36Sopenharmony_ci			return err;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (unlikely(n))
172262306a36Sopenharmony_ci		return TC_ACT_SHOT;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return TC_ACT_UNSPEC; /* signal: continue lookup */
172562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
172662306a36Sopenharmony_cireset:
172762306a36Sopenharmony_ci	if (unlikely(limit++ >= max_reclassify_loop)) {
172862306a36Sopenharmony_ci		net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n",
172962306a36Sopenharmony_ci				       tp->chain->block->index,
173062306a36Sopenharmony_ci				       tp->prio & 0xffff,
173162306a36Sopenharmony_ci				       ntohs(tp->protocol));
173262306a36Sopenharmony_ci		return TC_ACT_SHOT;
173362306a36Sopenharmony_ci	}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	tp = first_tp;
173662306a36Sopenharmony_ci	goto reclassify;
173762306a36Sopenharmony_ci#endif
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ciint tcf_classify(struct sk_buff *skb,
174162306a36Sopenharmony_ci		 const struct tcf_block *block,
174262306a36Sopenharmony_ci		 const struct tcf_proto *tp,
174362306a36Sopenharmony_ci		 struct tcf_result *res, bool compat_mode)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci#if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
174662306a36Sopenharmony_ci	u32 last_executed_chain = 0;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	return __tcf_classify(skb, tp, tp, res, compat_mode, NULL, 0,
174962306a36Sopenharmony_ci			      &last_executed_chain);
175062306a36Sopenharmony_ci#else
175162306a36Sopenharmony_ci	u32 last_executed_chain = tp ? tp->chain->index : 0;
175262306a36Sopenharmony_ci	struct tcf_exts_miss_cookie_node *n = NULL;
175362306a36Sopenharmony_ci	const struct tcf_proto *orig_tp = tp;
175462306a36Sopenharmony_ci	struct tc_skb_ext *ext;
175562306a36Sopenharmony_ci	int act_index = 0;
175662306a36Sopenharmony_ci	int ret;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (block) {
175962306a36Sopenharmony_ci		ext = skb_ext_find(skb, TC_SKB_EXT);
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci		if (ext && (ext->chain || ext->act_miss)) {
176262306a36Sopenharmony_ci			struct tcf_chain *fchain;
176362306a36Sopenharmony_ci			u32 chain;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci			if (ext->act_miss) {
176662306a36Sopenharmony_ci				n = tcf_exts_miss_cookie_lookup(ext->act_miss_cookie,
176762306a36Sopenharmony_ci								&act_index);
176862306a36Sopenharmony_ci				if (!n)
176962306a36Sopenharmony_ci					return TC_ACT_SHOT;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci				chain = n->chain_index;
177262306a36Sopenharmony_ci			} else {
177362306a36Sopenharmony_ci				chain = ext->chain;
177462306a36Sopenharmony_ci			}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci			fchain = tcf_chain_lookup_rcu(block, chain);
177762306a36Sopenharmony_ci			if (!fchain)
177862306a36Sopenharmony_ci				return TC_ACT_SHOT;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci			/* Consume, so cloned/redirect skbs won't inherit ext */
178162306a36Sopenharmony_ci			skb_ext_del(skb, TC_SKB_EXT);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci			tp = rcu_dereference_bh(fchain->filter_chain);
178462306a36Sopenharmony_ci			last_executed_chain = fchain->index;
178562306a36Sopenharmony_ci		}
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode, n, act_index,
178962306a36Sopenharmony_ci			     &last_executed_chain);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (tc_skb_ext_tc_enabled()) {
179262306a36Sopenharmony_ci		/* If we missed on some chain */
179362306a36Sopenharmony_ci		if (ret == TC_ACT_UNSPEC && last_executed_chain) {
179462306a36Sopenharmony_ci			struct tc_skb_cb *cb = tc_skb_cb(skb);
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci			ext = tc_skb_ext_alloc(skb);
179762306a36Sopenharmony_ci			if (WARN_ON_ONCE(!ext))
179862306a36Sopenharmony_ci				return TC_ACT_SHOT;
179962306a36Sopenharmony_ci			ext->chain = last_executed_chain;
180062306a36Sopenharmony_ci			ext->mru = cb->mru;
180162306a36Sopenharmony_ci			ext->post_ct = cb->post_ct;
180262306a36Sopenharmony_ci			ext->post_ct_snat = cb->post_ct_snat;
180362306a36Sopenharmony_ci			ext->post_ct_dnat = cb->post_ct_dnat;
180462306a36Sopenharmony_ci			ext->zone = cb->zone;
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	return ret;
180962306a36Sopenharmony_ci#endif
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_classify);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_cistruct tcf_chain_info {
181462306a36Sopenharmony_ci	struct tcf_proto __rcu **pprev;
181562306a36Sopenharmony_ci	struct tcf_proto __rcu *next;
181662306a36Sopenharmony_ci};
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain,
181962306a36Sopenharmony_ci					   struct tcf_chain_info *chain_info)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	return tcf_chain_dereference(*chain_info->pprev, chain);
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic int tcf_chain_tp_insert(struct tcf_chain *chain,
182562306a36Sopenharmony_ci			       struct tcf_chain_info *chain_info,
182662306a36Sopenharmony_ci			       struct tcf_proto *tp)
182762306a36Sopenharmony_ci{
182862306a36Sopenharmony_ci	if (chain->flushing)
182962306a36Sopenharmony_ci		return -EAGAIN;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info));
183262306a36Sopenharmony_ci	if (*chain_info->pprev == chain->filter_chain)
183362306a36Sopenharmony_ci		tcf_chain0_head_change(chain, tp);
183462306a36Sopenharmony_ci	tcf_proto_get(tp);
183562306a36Sopenharmony_ci	rcu_assign_pointer(*chain_info->pprev, tp);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return 0;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void tcf_chain_tp_remove(struct tcf_chain *chain,
184162306a36Sopenharmony_ci				struct tcf_chain_info *chain_info,
184262306a36Sopenharmony_ci				struct tcf_proto *tp)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	tcf_proto_mark_delete(tp);
184762306a36Sopenharmony_ci	if (tp == chain->filter_chain)
184862306a36Sopenharmony_ci		tcf_chain0_head_change(chain, next);
184962306a36Sopenharmony_ci	RCU_INIT_POINTER(*chain_info->pprev, next);
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
185362306a36Sopenharmony_ci					   struct tcf_chain_info *chain_info,
185462306a36Sopenharmony_ci					   u32 protocol, u32 prio,
185562306a36Sopenharmony_ci					   bool prio_allocate);
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci/* Try to insert new proto.
185862306a36Sopenharmony_ci * If proto with specified priority already exists, free new proto
185962306a36Sopenharmony_ci * and return existing one.
186062306a36Sopenharmony_ci */
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain,
186362306a36Sopenharmony_ci						    struct tcf_proto *tp_new,
186462306a36Sopenharmony_ci						    u32 protocol, u32 prio,
186562306a36Sopenharmony_ci						    bool rtnl_held)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	struct tcf_chain_info chain_info;
186862306a36Sopenharmony_ci	struct tcf_proto *tp;
186962306a36Sopenharmony_ci	int err = 0;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (tcf_proto_exists_destroying(chain, tp_new)) {
187462306a36Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
187562306a36Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
187662306a36Sopenharmony_ci		return ERR_PTR(-EAGAIN);
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info,
188062306a36Sopenharmony_ci			       protocol, prio, false);
188162306a36Sopenharmony_ci	if (!tp)
188262306a36Sopenharmony_ci		err = tcf_chain_tp_insert(chain, &chain_info, tp_new);
188362306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	if (tp) {
188662306a36Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
188762306a36Sopenharmony_ci		tp_new = tp;
188862306a36Sopenharmony_ci	} else if (err) {
188962306a36Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
189062306a36Sopenharmony_ci		tp_new = ERR_PTR(err);
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	return tp_new;
189462306a36Sopenharmony_ci}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic void tcf_chain_tp_delete_empty(struct tcf_chain *chain,
189762306a36Sopenharmony_ci				      struct tcf_proto *tp, bool rtnl_held,
189862306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	struct tcf_chain_info chain_info;
190162306a36Sopenharmony_ci	struct tcf_proto *tp_iter;
190262306a36Sopenharmony_ci	struct tcf_proto **pprev;
190362306a36Sopenharmony_ci	struct tcf_proto *next;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	/* Atomically find and remove tp from chain. */
190862306a36Sopenharmony_ci	for (pprev = &chain->filter_chain;
190962306a36Sopenharmony_ci	     (tp_iter = tcf_chain_dereference(*pprev, chain));
191062306a36Sopenharmony_ci	     pprev = &tp_iter->next) {
191162306a36Sopenharmony_ci		if (tp_iter == tp) {
191262306a36Sopenharmony_ci			chain_info.pprev = pprev;
191362306a36Sopenharmony_ci			chain_info.next = tp_iter->next;
191462306a36Sopenharmony_ci			WARN_ON(tp_iter->deleting);
191562306a36Sopenharmony_ci			break;
191662306a36Sopenharmony_ci		}
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci	/* Verify that tp still exists and no new filters were inserted
191962306a36Sopenharmony_ci	 * concurrently.
192062306a36Sopenharmony_ci	 * Mark tp for deletion if it is empty.
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	if (!tp_iter || !tcf_proto_check_delete(tp)) {
192362306a36Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
192462306a36Sopenharmony_ci		return;
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	tcf_proto_signal_destroying(chain, tp);
192862306a36Sopenharmony_ci	next = tcf_chain_dereference(chain_info.next, chain);
192962306a36Sopenharmony_ci	if (tp == chain->filter_chain)
193062306a36Sopenharmony_ci		tcf_chain0_head_change(chain, next);
193162306a36Sopenharmony_ci	RCU_INIT_POINTER(*chain_info.pprev, next);
193262306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	tcf_proto_put(tp, rtnl_held, extack);
193562306a36Sopenharmony_ci}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
193862306a36Sopenharmony_ci					   struct tcf_chain_info *chain_info,
193962306a36Sopenharmony_ci					   u32 protocol, u32 prio,
194062306a36Sopenharmony_ci					   bool prio_allocate)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	struct tcf_proto **pprev;
194362306a36Sopenharmony_ci	struct tcf_proto *tp;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* Check the chain for existence of proto-tcf with this priority */
194662306a36Sopenharmony_ci	for (pprev = &chain->filter_chain;
194762306a36Sopenharmony_ci	     (tp = tcf_chain_dereference(*pprev, chain));
194862306a36Sopenharmony_ci	     pprev = &tp->next) {
194962306a36Sopenharmony_ci		if (tp->prio >= prio) {
195062306a36Sopenharmony_ci			if (tp->prio == prio) {
195162306a36Sopenharmony_ci				if (prio_allocate ||
195262306a36Sopenharmony_ci				    (tp->protocol != protocol && protocol))
195362306a36Sopenharmony_ci					return ERR_PTR(-EINVAL);
195462306a36Sopenharmony_ci			} else {
195562306a36Sopenharmony_ci				tp = NULL;
195662306a36Sopenharmony_ci			}
195762306a36Sopenharmony_ci			break;
195862306a36Sopenharmony_ci		}
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci	chain_info->pprev = pprev;
196162306a36Sopenharmony_ci	if (tp) {
196262306a36Sopenharmony_ci		chain_info->next = tp->next;
196362306a36Sopenharmony_ci		tcf_proto_get(tp);
196462306a36Sopenharmony_ci	} else {
196562306a36Sopenharmony_ci		chain_info->next = NULL;
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci	return tp;
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_cistatic int tcf_fill_node(struct net *net, struct sk_buff *skb,
197162306a36Sopenharmony_ci			 struct tcf_proto *tp, struct tcf_block *block,
197262306a36Sopenharmony_ci			 struct Qdisc *q, u32 parent, void *fh,
197362306a36Sopenharmony_ci			 u32 portid, u32 seq, u16 flags, int event,
197462306a36Sopenharmony_ci			 bool terse_dump, bool rtnl_held,
197562306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
197662306a36Sopenharmony_ci{
197762306a36Sopenharmony_ci	struct tcmsg *tcm;
197862306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
197962306a36Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
198262306a36Sopenharmony_ci	if (!nlh)
198362306a36Sopenharmony_ci		goto out_nlmsg_trim;
198462306a36Sopenharmony_ci	tcm = nlmsg_data(nlh);
198562306a36Sopenharmony_ci	tcm->tcm_family = AF_UNSPEC;
198662306a36Sopenharmony_ci	tcm->tcm__pad1 = 0;
198762306a36Sopenharmony_ci	tcm->tcm__pad2 = 0;
198862306a36Sopenharmony_ci	if (q) {
198962306a36Sopenharmony_ci		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
199062306a36Sopenharmony_ci		tcm->tcm_parent = parent;
199162306a36Sopenharmony_ci	} else {
199262306a36Sopenharmony_ci		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
199362306a36Sopenharmony_ci		tcm->tcm_block_index = block->index;
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
199662306a36Sopenharmony_ci	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
199762306a36Sopenharmony_ci		goto nla_put_failure;
199862306a36Sopenharmony_ci	if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index))
199962306a36Sopenharmony_ci		goto nla_put_failure;
200062306a36Sopenharmony_ci	if (!fh) {
200162306a36Sopenharmony_ci		tcm->tcm_handle = 0;
200262306a36Sopenharmony_ci	} else if (terse_dump) {
200362306a36Sopenharmony_ci		if (tp->ops->terse_dump) {
200462306a36Sopenharmony_ci			if (tp->ops->terse_dump(net, tp, fh, skb, tcm,
200562306a36Sopenharmony_ci						rtnl_held) < 0)
200662306a36Sopenharmony_ci				goto nla_put_failure;
200762306a36Sopenharmony_ci		} else {
200862306a36Sopenharmony_ci			goto cls_op_not_supp;
200962306a36Sopenharmony_ci		}
201062306a36Sopenharmony_ci	} else {
201162306a36Sopenharmony_ci		if (tp->ops->dump &&
201262306a36Sopenharmony_ci		    tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
201362306a36Sopenharmony_ci			goto nla_put_failure;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	if (extack && extack->_msg &&
201762306a36Sopenharmony_ci	    nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
201862306a36Sopenharmony_ci		goto nla_put_failure;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	return skb->len;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ciout_nlmsg_trim:
202562306a36Sopenharmony_cinla_put_failure:
202662306a36Sopenharmony_cicls_op_not_supp:
202762306a36Sopenharmony_ci	nlmsg_trim(skb, b);
202862306a36Sopenharmony_ci	return -1;
202962306a36Sopenharmony_ci}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_cistatic int tfilter_notify(struct net *net, struct sk_buff *oskb,
203262306a36Sopenharmony_ci			  struct nlmsghdr *n, struct tcf_proto *tp,
203362306a36Sopenharmony_ci			  struct tcf_block *block, struct Qdisc *q,
203462306a36Sopenharmony_ci			  u32 parent, void *fh, int event, bool unicast,
203562306a36Sopenharmony_ci			  bool rtnl_held, struct netlink_ext_ack *extack)
203662306a36Sopenharmony_ci{
203762306a36Sopenharmony_ci	struct sk_buff *skb;
203862306a36Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
203962306a36Sopenharmony_ci	int err = 0;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
204262306a36Sopenharmony_ci	if (!skb)
204362306a36Sopenharmony_ci		return -ENOBUFS;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
204662306a36Sopenharmony_ci			  n->nlmsg_seq, n->nlmsg_flags, event,
204762306a36Sopenharmony_ci			  false, rtnl_held, extack) <= 0) {
204862306a36Sopenharmony_ci		kfree_skb(skb);
204962306a36Sopenharmony_ci		return -EINVAL;
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	if (unicast)
205362306a36Sopenharmony_ci		err = rtnl_unicast(skb, net, portid);
205462306a36Sopenharmony_ci	else
205562306a36Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
205662306a36Sopenharmony_ci				     n->nlmsg_flags & NLM_F_ECHO);
205762306a36Sopenharmony_ci	return err;
205862306a36Sopenharmony_ci}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_cistatic int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
206162306a36Sopenharmony_ci			      struct nlmsghdr *n, struct tcf_proto *tp,
206262306a36Sopenharmony_ci			      struct tcf_block *block, struct Qdisc *q,
206362306a36Sopenharmony_ci			      u32 parent, void *fh, bool unicast, bool *last,
206462306a36Sopenharmony_ci			      bool rtnl_held, struct netlink_ext_ack *extack)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	struct sk_buff *skb;
206762306a36Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
206862306a36Sopenharmony_ci	int err;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
207162306a36Sopenharmony_ci	if (!skb)
207262306a36Sopenharmony_ci		return -ENOBUFS;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
207562306a36Sopenharmony_ci			  n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
207662306a36Sopenharmony_ci			  false, rtnl_held, extack) <= 0) {
207762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to build del event notification");
207862306a36Sopenharmony_ci		kfree_skb(skb);
207962306a36Sopenharmony_ci		return -EINVAL;
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	err = tp->ops->delete(tp, fh, last, rtnl_held, extack);
208362306a36Sopenharmony_ci	if (err) {
208462306a36Sopenharmony_ci		kfree_skb(skb);
208562306a36Sopenharmony_ci		return err;
208662306a36Sopenharmony_ci	}
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	if (unicast)
208962306a36Sopenharmony_ci		err = rtnl_unicast(skb, net, portid);
209062306a36Sopenharmony_ci	else
209162306a36Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
209262306a36Sopenharmony_ci				     n->nlmsg_flags & NLM_F_ECHO);
209362306a36Sopenharmony_ci	if (err < 0)
209462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to send filter delete notification");
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	return err;
209762306a36Sopenharmony_ci}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_cistatic void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
210062306a36Sopenharmony_ci				 struct tcf_block *block, struct Qdisc *q,
210162306a36Sopenharmony_ci				 u32 parent, struct nlmsghdr *n,
210262306a36Sopenharmony_ci				 struct tcf_chain *chain, int event,
210362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
210462306a36Sopenharmony_ci{
210562306a36Sopenharmony_ci	struct tcf_proto *tp;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	for (tp = tcf_get_next_proto(chain, NULL);
210862306a36Sopenharmony_ci	     tp; tp = tcf_get_next_proto(chain, tp))
210962306a36Sopenharmony_ci		tfilter_notify(net, oskb, n, tp, block, q, parent, NULL,
211062306a36Sopenharmony_ci			       event, false, true, extack);
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic void tfilter_put(struct tcf_proto *tp, void *fh)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	if (tp->ops->put && fh)
211662306a36Sopenharmony_ci		tp->ops->put(tp, fh);
211762306a36Sopenharmony_ci}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_cistatic bool is_qdisc_ingress(__u32 classid)
212062306a36Sopenharmony_ci{
212162306a36Sopenharmony_ci	return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS));
212262306a36Sopenharmony_ci}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_cistatic int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
212562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
212662306a36Sopenharmony_ci{
212762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
212862306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
212962306a36Sopenharmony_ci	char name[IFNAMSIZ];
213062306a36Sopenharmony_ci	struct tcmsg *t;
213162306a36Sopenharmony_ci	u32 protocol;
213262306a36Sopenharmony_ci	u32 prio;
213362306a36Sopenharmony_ci	bool prio_allocate;
213462306a36Sopenharmony_ci	u32 parent;
213562306a36Sopenharmony_ci	u32 chain_index;
213662306a36Sopenharmony_ci	struct Qdisc *q;
213762306a36Sopenharmony_ci	struct tcf_chain_info chain_info;
213862306a36Sopenharmony_ci	struct tcf_chain *chain;
213962306a36Sopenharmony_ci	struct tcf_block *block;
214062306a36Sopenharmony_ci	struct tcf_proto *tp;
214162306a36Sopenharmony_ci	unsigned long cl;
214262306a36Sopenharmony_ci	void *fh;
214362306a36Sopenharmony_ci	int err;
214462306a36Sopenharmony_ci	int tp_created;
214562306a36Sopenharmony_ci	bool rtnl_held = false;
214662306a36Sopenharmony_ci	u32 flags;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cireplay:
214962306a36Sopenharmony_ci	tp_created = 0;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
215262306a36Sopenharmony_ci				     rtm_tca_policy, extack);
215362306a36Sopenharmony_ci	if (err < 0)
215462306a36Sopenharmony_ci		return err;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	t = nlmsg_data(n);
215762306a36Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
215862306a36Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
215962306a36Sopenharmony_ci	prio_allocate = false;
216062306a36Sopenharmony_ci	parent = t->tcm_parent;
216162306a36Sopenharmony_ci	tp = NULL;
216262306a36Sopenharmony_ci	cl = 0;
216362306a36Sopenharmony_ci	block = NULL;
216462306a36Sopenharmony_ci	q = NULL;
216562306a36Sopenharmony_ci	chain = NULL;
216662306a36Sopenharmony_ci	flags = 0;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	if (prio == 0) {
216962306a36Sopenharmony_ci		/* If no priority is provided by the user,
217062306a36Sopenharmony_ci		 * we allocate one.
217162306a36Sopenharmony_ci		 */
217262306a36Sopenharmony_ci		if (n->nlmsg_flags & NLM_F_CREATE) {
217362306a36Sopenharmony_ci			prio = TC_H_MAKE(0x80000000U, 0U);
217462306a36Sopenharmony_ci			prio_allocate = true;
217562306a36Sopenharmony_ci		} else {
217662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
217762306a36Sopenharmony_ci			return -ENOENT;
217862306a36Sopenharmony_ci		}
217962306a36Sopenharmony_ci	}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	/* Find head of filter chain. */
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
218462306a36Sopenharmony_ci	if (err)
218562306a36Sopenharmony_ci		return err;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
218862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
218962306a36Sopenharmony_ci		err = -EINVAL;
219062306a36Sopenharmony_ci		goto errout;
219162306a36Sopenharmony_ci	}
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	/* Take rtnl mutex if rtnl_held was set to true on previous iteration,
219462306a36Sopenharmony_ci	 * block is shared (no qdisc found), qdisc is not unlocked, classifier
219562306a36Sopenharmony_ci	 * type is not specified, classifier is not unlocked.
219662306a36Sopenharmony_ci	 */
219762306a36Sopenharmony_ci	if (rtnl_held ||
219862306a36Sopenharmony_ci	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
219962306a36Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
220062306a36Sopenharmony_ci		rtnl_held = true;
220162306a36Sopenharmony_ci		rtnl_lock();
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
220562306a36Sopenharmony_ci	if (err)
220662306a36Sopenharmony_ci		goto errout;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
220962306a36Sopenharmony_ci				 extack);
221062306a36Sopenharmony_ci	if (IS_ERR(block)) {
221162306a36Sopenharmony_ci		err = PTR_ERR(block);
221262306a36Sopenharmony_ci		goto errout;
221362306a36Sopenharmony_ci	}
221462306a36Sopenharmony_ci	block->classid = parent;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
221762306a36Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
221862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
221962306a36Sopenharmony_ci		err = -EINVAL;
222062306a36Sopenharmony_ci		goto errout;
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, true);
222362306a36Sopenharmony_ci	if (!chain) {
222462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot create specified filter chain");
222562306a36Sopenharmony_ci		err = -ENOMEM;
222662306a36Sopenharmony_ci		goto errout;
222762306a36Sopenharmony_ci	}
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
223062306a36Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
223162306a36Sopenharmony_ci			       prio, prio_allocate);
223262306a36Sopenharmony_ci	if (IS_ERR(tp)) {
223362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
223462306a36Sopenharmony_ci		err = PTR_ERR(tp);
223562306a36Sopenharmony_ci		goto errout_locked;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	if (tp == NULL) {
223962306a36Sopenharmony_ci		struct tcf_proto *tp_new = NULL;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci		if (chain->flushing) {
224262306a36Sopenharmony_ci			err = -EAGAIN;
224362306a36Sopenharmony_ci			goto errout_locked;
224462306a36Sopenharmony_ci		}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci		/* Proto-tcf does not exist, create new one */
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci		if (tca[TCA_KIND] == NULL || !protocol) {
224962306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified");
225062306a36Sopenharmony_ci			err = -EINVAL;
225162306a36Sopenharmony_ci			goto errout_locked;
225262306a36Sopenharmony_ci		}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
225562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
225662306a36Sopenharmony_ci			err = -ENOENT;
225762306a36Sopenharmony_ci			goto errout_locked;
225862306a36Sopenharmony_ci		}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci		if (prio_allocate)
226162306a36Sopenharmony_ci			prio = tcf_auto_prio(tcf_chain_tp_prev(chain,
226262306a36Sopenharmony_ci							       &chain_info));
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
226562306a36Sopenharmony_ci		tp_new = tcf_proto_create(name, protocol, prio, chain,
226662306a36Sopenharmony_ci					  rtnl_held, extack);
226762306a36Sopenharmony_ci		if (IS_ERR(tp_new)) {
226862306a36Sopenharmony_ci			err = PTR_ERR(tp_new);
226962306a36Sopenharmony_ci			goto errout_tp;
227062306a36Sopenharmony_ci		}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci		tp_created = 1;
227362306a36Sopenharmony_ci		tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio,
227462306a36Sopenharmony_ci						rtnl_held);
227562306a36Sopenharmony_ci		if (IS_ERR(tp)) {
227662306a36Sopenharmony_ci			err = PTR_ERR(tp);
227762306a36Sopenharmony_ci			goto errout_tp;
227862306a36Sopenharmony_ci		}
227962306a36Sopenharmony_ci	} else {
228062306a36Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
228162306a36Sopenharmony_ci	}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
228462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
228562306a36Sopenharmony_ci		err = -EINVAL;
228662306a36Sopenharmony_ci		goto errout;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	if (!fh) {
229262306a36Sopenharmony_ci		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
229362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
229462306a36Sopenharmony_ci			err = -ENOENT;
229562306a36Sopenharmony_ci			goto errout;
229662306a36Sopenharmony_ci		}
229762306a36Sopenharmony_ci	} else if (n->nlmsg_flags & NLM_F_EXCL) {
229862306a36Sopenharmony_ci		tfilter_put(tp, fh);
229962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter already exists");
230062306a36Sopenharmony_ci		err = -EEXIST;
230162306a36Sopenharmony_ci		goto errout;
230262306a36Sopenharmony_ci	}
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) {
230562306a36Sopenharmony_ci		tfilter_put(tp, fh);
230662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind");
230762306a36Sopenharmony_ci		err = -EINVAL;
230862306a36Sopenharmony_ci		goto errout;
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	if (!(n->nlmsg_flags & NLM_F_CREATE))
231262306a36Sopenharmony_ci		flags |= TCA_ACT_FLAGS_REPLACE;
231362306a36Sopenharmony_ci	if (!rtnl_held)
231462306a36Sopenharmony_ci		flags |= TCA_ACT_FLAGS_NO_RTNL;
231562306a36Sopenharmony_ci	if (is_qdisc_ingress(parent))
231662306a36Sopenharmony_ci		flags |= TCA_ACT_FLAGS_AT_INGRESS;
231762306a36Sopenharmony_ci	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
231862306a36Sopenharmony_ci			      flags, extack);
231962306a36Sopenharmony_ci	if (err == 0) {
232062306a36Sopenharmony_ci		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
232162306a36Sopenharmony_ci			       RTM_NEWTFILTER, false, rtnl_held, extack);
232262306a36Sopenharmony_ci		tfilter_put(tp, fh);
232362306a36Sopenharmony_ci		/* q pointer is NULL for shared blocks */
232462306a36Sopenharmony_ci		if (q)
232562306a36Sopenharmony_ci			q->flags &= ~TCQ_F_CAN_BYPASS;
232662306a36Sopenharmony_ci	}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_cierrout:
232962306a36Sopenharmony_ci	if (err && tp_created)
233062306a36Sopenharmony_ci		tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
233162306a36Sopenharmony_cierrout_tp:
233262306a36Sopenharmony_ci	if (chain) {
233362306a36Sopenharmony_ci		if (tp && !IS_ERR(tp))
233462306a36Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
233562306a36Sopenharmony_ci		if (!tp_created)
233662306a36Sopenharmony_ci			tcf_chain_put(chain);
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	if (rtnl_held)
234162306a36Sopenharmony_ci		rtnl_unlock();
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	if (err == -EAGAIN) {
234462306a36Sopenharmony_ci		/* Take rtnl lock in case EAGAIN is caused by concurrent flush
234562306a36Sopenharmony_ci		 * of target chain.
234662306a36Sopenharmony_ci		 */
234762306a36Sopenharmony_ci		rtnl_held = true;
234862306a36Sopenharmony_ci		/* Replay the request. */
234962306a36Sopenharmony_ci		goto replay;
235062306a36Sopenharmony_ci	}
235162306a36Sopenharmony_ci	return err;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_cierrout_locked:
235462306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
235562306a36Sopenharmony_ci	goto errout;
235662306a36Sopenharmony_ci}
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_cistatic int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
235962306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
236062306a36Sopenharmony_ci{
236162306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
236262306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
236362306a36Sopenharmony_ci	char name[IFNAMSIZ];
236462306a36Sopenharmony_ci	struct tcmsg *t;
236562306a36Sopenharmony_ci	u32 protocol;
236662306a36Sopenharmony_ci	u32 prio;
236762306a36Sopenharmony_ci	u32 parent;
236862306a36Sopenharmony_ci	u32 chain_index;
236962306a36Sopenharmony_ci	struct Qdisc *q = NULL;
237062306a36Sopenharmony_ci	struct tcf_chain_info chain_info;
237162306a36Sopenharmony_ci	struct tcf_chain *chain = NULL;
237262306a36Sopenharmony_ci	struct tcf_block *block = NULL;
237362306a36Sopenharmony_ci	struct tcf_proto *tp = NULL;
237462306a36Sopenharmony_ci	unsigned long cl = 0;
237562306a36Sopenharmony_ci	void *fh = NULL;
237662306a36Sopenharmony_ci	int err;
237762306a36Sopenharmony_ci	bool rtnl_held = false;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
238062306a36Sopenharmony_ci				     rtm_tca_policy, extack);
238162306a36Sopenharmony_ci	if (err < 0)
238262306a36Sopenharmony_ci		return err;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	t = nlmsg_data(n);
238562306a36Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
238662306a36Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
238762306a36Sopenharmony_ci	parent = t->tcm_parent;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) {
239062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set");
239162306a36Sopenharmony_ci		return -ENOENT;
239262306a36Sopenharmony_ci	}
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	/* Find head of filter chain. */
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
239762306a36Sopenharmony_ci	if (err)
239862306a36Sopenharmony_ci		return err;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
240162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
240262306a36Sopenharmony_ci		err = -EINVAL;
240362306a36Sopenharmony_ci		goto errout;
240462306a36Sopenharmony_ci	}
240562306a36Sopenharmony_ci	/* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
240662306a36Sopenharmony_ci	 * found), qdisc is not unlocked, classifier type is not specified,
240762306a36Sopenharmony_ci	 * classifier is not unlocked.
240862306a36Sopenharmony_ci	 */
240962306a36Sopenharmony_ci	if (!prio ||
241062306a36Sopenharmony_ci	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
241162306a36Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
241262306a36Sopenharmony_ci		rtnl_held = true;
241362306a36Sopenharmony_ci		rtnl_lock();
241462306a36Sopenharmony_ci	}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
241762306a36Sopenharmony_ci	if (err)
241862306a36Sopenharmony_ci		goto errout;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
242162306a36Sopenharmony_ci				 extack);
242262306a36Sopenharmony_ci	if (IS_ERR(block)) {
242362306a36Sopenharmony_ci		err = PTR_ERR(block);
242462306a36Sopenharmony_ci		goto errout;
242562306a36Sopenharmony_ci	}
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
242862306a36Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
242962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
243062306a36Sopenharmony_ci		err = -EINVAL;
243162306a36Sopenharmony_ci		goto errout;
243262306a36Sopenharmony_ci	}
243362306a36Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, false);
243462306a36Sopenharmony_ci	if (!chain) {
243562306a36Sopenharmony_ci		/* User requested flush on non-existent chain. Nothing to do,
243662306a36Sopenharmony_ci		 * so just return success.
243762306a36Sopenharmony_ci		 */
243862306a36Sopenharmony_ci		if (prio == 0) {
243962306a36Sopenharmony_ci			err = 0;
244062306a36Sopenharmony_ci			goto errout;
244162306a36Sopenharmony_ci		}
244262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
244362306a36Sopenharmony_ci		err = -ENOENT;
244462306a36Sopenharmony_ci		goto errout;
244562306a36Sopenharmony_ci	}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	if (prio == 0) {
244862306a36Sopenharmony_ci		tfilter_notify_chain(net, skb, block, q, parent, n,
244962306a36Sopenharmony_ci				     chain, RTM_DELTFILTER, extack);
245062306a36Sopenharmony_ci		tcf_chain_flush(chain, rtnl_held);
245162306a36Sopenharmony_ci		err = 0;
245262306a36Sopenharmony_ci		goto errout;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
245662306a36Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
245762306a36Sopenharmony_ci			       prio, false);
245862306a36Sopenharmony_ci	if (!tp || IS_ERR(tp)) {
245962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
246062306a36Sopenharmony_ci		err = tp ? PTR_ERR(tp) : -ENOENT;
246162306a36Sopenharmony_ci		goto errout_locked;
246262306a36Sopenharmony_ci	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
246362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
246462306a36Sopenharmony_ci		err = -EINVAL;
246562306a36Sopenharmony_ci		goto errout_locked;
246662306a36Sopenharmony_ci	} else if (t->tcm_handle == 0) {
246762306a36Sopenharmony_ci		tcf_proto_signal_destroying(chain, tp);
246862306a36Sopenharmony_ci		tcf_chain_tp_remove(chain, &chain_info, tp);
246962306a36Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci		tcf_proto_put(tp, rtnl_held, NULL);
247262306a36Sopenharmony_ci		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
247362306a36Sopenharmony_ci			       RTM_DELTFILTER, false, rtnl_held, extack);
247462306a36Sopenharmony_ci		err = 0;
247562306a36Sopenharmony_ci		goto errout;
247662306a36Sopenharmony_ci	}
247762306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	if (!fh) {
248262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
248362306a36Sopenharmony_ci		err = -ENOENT;
248462306a36Sopenharmony_ci	} else {
248562306a36Sopenharmony_ci		bool last;
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci		err = tfilter_del_notify(net, skb, n, tp, block,
248862306a36Sopenharmony_ci					 q, parent, fh, false, &last,
248962306a36Sopenharmony_ci					 rtnl_held, extack);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci		if (err)
249262306a36Sopenharmony_ci			goto errout;
249362306a36Sopenharmony_ci		if (last)
249462306a36Sopenharmony_ci			tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack);
249562306a36Sopenharmony_ci	}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_cierrout:
249862306a36Sopenharmony_ci	if (chain) {
249962306a36Sopenharmony_ci		if (tp && !IS_ERR(tp))
250062306a36Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
250162306a36Sopenharmony_ci		tcf_chain_put(chain);
250262306a36Sopenharmony_ci	}
250362306a36Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	if (rtnl_held)
250662306a36Sopenharmony_ci		rtnl_unlock();
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	return err;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_cierrout_locked:
251162306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
251262306a36Sopenharmony_ci	goto errout;
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
251662306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
251762306a36Sopenharmony_ci{
251862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
251962306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
252062306a36Sopenharmony_ci	char name[IFNAMSIZ];
252162306a36Sopenharmony_ci	struct tcmsg *t;
252262306a36Sopenharmony_ci	u32 protocol;
252362306a36Sopenharmony_ci	u32 prio;
252462306a36Sopenharmony_ci	u32 parent;
252562306a36Sopenharmony_ci	u32 chain_index;
252662306a36Sopenharmony_ci	struct Qdisc *q = NULL;
252762306a36Sopenharmony_ci	struct tcf_chain_info chain_info;
252862306a36Sopenharmony_ci	struct tcf_chain *chain = NULL;
252962306a36Sopenharmony_ci	struct tcf_block *block = NULL;
253062306a36Sopenharmony_ci	struct tcf_proto *tp = NULL;
253162306a36Sopenharmony_ci	unsigned long cl = 0;
253262306a36Sopenharmony_ci	void *fh = NULL;
253362306a36Sopenharmony_ci	int err;
253462306a36Sopenharmony_ci	bool rtnl_held = false;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
253762306a36Sopenharmony_ci				     rtm_tca_policy, extack);
253862306a36Sopenharmony_ci	if (err < 0)
253962306a36Sopenharmony_ci		return err;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	t = nlmsg_data(n);
254262306a36Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
254362306a36Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
254462306a36Sopenharmony_ci	parent = t->tcm_parent;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	if (prio == 0) {
254762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
254862306a36Sopenharmony_ci		return -ENOENT;
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	/* Find head of filter chain. */
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
255462306a36Sopenharmony_ci	if (err)
255562306a36Sopenharmony_ci		return err;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
255862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
255962306a36Sopenharmony_ci		err = -EINVAL;
256062306a36Sopenharmony_ci		goto errout;
256162306a36Sopenharmony_ci	}
256262306a36Sopenharmony_ci	/* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
256362306a36Sopenharmony_ci	 * unlocked, classifier type is not specified, classifier is not
256462306a36Sopenharmony_ci	 * unlocked.
256562306a36Sopenharmony_ci	 */
256662306a36Sopenharmony_ci	if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
256762306a36Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
256862306a36Sopenharmony_ci		rtnl_held = true;
256962306a36Sopenharmony_ci		rtnl_lock();
257062306a36Sopenharmony_ci	}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
257362306a36Sopenharmony_ci	if (err)
257462306a36Sopenharmony_ci		goto errout;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
257762306a36Sopenharmony_ci				 extack);
257862306a36Sopenharmony_ci	if (IS_ERR(block)) {
257962306a36Sopenharmony_ci		err = PTR_ERR(block);
258062306a36Sopenharmony_ci		goto errout;
258162306a36Sopenharmony_ci	}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
258462306a36Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
258562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
258662306a36Sopenharmony_ci		err = -EINVAL;
258762306a36Sopenharmony_ci		goto errout;
258862306a36Sopenharmony_ci	}
258962306a36Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, false);
259062306a36Sopenharmony_ci	if (!chain) {
259162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
259262306a36Sopenharmony_ci		err = -EINVAL;
259362306a36Sopenharmony_ci		goto errout;
259462306a36Sopenharmony_ci	}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
259762306a36Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
259862306a36Sopenharmony_ci			       prio, false);
259962306a36Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
260062306a36Sopenharmony_ci	if (!tp || IS_ERR(tp)) {
260162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
260262306a36Sopenharmony_ci		err = tp ? PTR_ERR(tp) : -ENOENT;
260362306a36Sopenharmony_ci		goto errout;
260462306a36Sopenharmony_ci	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
260562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
260662306a36Sopenharmony_ci		err = -EINVAL;
260762306a36Sopenharmony_ci		goto errout;
260862306a36Sopenharmony_ci	}
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	if (!fh) {
261362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
261462306a36Sopenharmony_ci		err = -ENOENT;
261562306a36Sopenharmony_ci	} else {
261662306a36Sopenharmony_ci		err = tfilter_notify(net, skb, n, tp, block, q, parent,
261762306a36Sopenharmony_ci				     fh, RTM_NEWTFILTER, true, rtnl_held, NULL);
261862306a36Sopenharmony_ci		if (err < 0)
261962306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
262062306a36Sopenharmony_ci	}
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	tfilter_put(tp, fh);
262362306a36Sopenharmony_cierrout:
262462306a36Sopenharmony_ci	if (chain) {
262562306a36Sopenharmony_ci		if (tp && !IS_ERR(tp))
262662306a36Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
262762306a36Sopenharmony_ci		tcf_chain_put(chain);
262862306a36Sopenharmony_ci	}
262962306a36Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	if (rtnl_held)
263262306a36Sopenharmony_ci		rtnl_unlock();
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	return err;
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistruct tcf_dump_args {
263862306a36Sopenharmony_ci	struct tcf_walker w;
263962306a36Sopenharmony_ci	struct sk_buff *skb;
264062306a36Sopenharmony_ci	struct netlink_callback *cb;
264162306a36Sopenharmony_ci	struct tcf_block *block;
264262306a36Sopenharmony_ci	struct Qdisc *q;
264362306a36Sopenharmony_ci	u32 parent;
264462306a36Sopenharmony_ci	bool terse_dump;
264562306a36Sopenharmony_ci};
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_cistatic int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
264862306a36Sopenharmony_ci{
264962306a36Sopenharmony_ci	struct tcf_dump_args *a = (void *)arg;
265062306a36Sopenharmony_ci	struct net *net = sock_net(a->skb->sk);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
265362306a36Sopenharmony_ci			     n, NETLINK_CB(a->cb->skb).portid,
265462306a36Sopenharmony_ci			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
265562306a36Sopenharmony_ci			     RTM_NEWTFILTER, a->terse_dump, true, NULL);
265662306a36Sopenharmony_ci}
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_cistatic bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
265962306a36Sopenharmony_ci			   struct sk_buff *skb, struct netlink_callback *cb,
266062306a36Sopenharmony_ci			   long index_start, long *p_index, bool terse)
266162306a36Sopenharmony_ci{
266262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
266362306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
266462306a36Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
266562306a36Sopenharmony_ci	struct tcf_proto *tp, *tp_prev;
266662306a36Sopenharmony_ci	struct tcf_dump_args arg;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	for (tp = __tcf_get_next_proto(chain, NULL);
266962306a36Sopenharmony_ci	     tp;
267062306a36Sopenharmony_ci	     tp_prev = tp,
267162306a36Sopenharmony_ci		     tp = __tcf_get_next_proto(chain, tp),
267262306a36Sopenharmony_ci		     tcf_proto_put(tp_prev, true, NULL),
267362306a36Sopenharmony_ci		     (*p_index)++) {
267462306a36Sopenharmony_ci		if (*p_index < index_start)
267562306a36Sopenharmony_ci			continue;
267662306a36Sopenharmony_ci		if (TC_H_MAJ(tcm->tcm_info) &&
267762306a36Sopenharmony_ci		    TC_H_MAJ(tcm->tcm_info) != tp->prio)
267862306a36Sopenharmony_ci			continue;
267962306a36Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_info) &&
268062306a36Sopenharmony_ci		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
268162306a36Sopenharmony_ci			continue;
268262306a36Sopenharmony_ci		if (*p_index > index_start)
268362306a36Sopenharmony_ci			memset(&cb->args[1], 0,
268462306a36Sopenharmony_ci			       sizeof(cb->args) - sizeof(cb->args[0]));
268562306a36Sopenharmony_ci		if (cb->args[1] == 0) {
268662306a36Sopenharmony_ci			if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
268762306a36Sopenharmony_ci					  NETLINK_CB(cb->skb).portid,
268862306a36Sopenharmony_ci					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
268962306a36Sopenharmony_ci					  RTM_NEWTFILTER, false, true, NULL) <= 0)
269062306a36Sopenharmony_ci				goto errout;
269162306a36Sopenharmony_ci			cb->args[1] = 1;
269262306a36Sopenharmony_ci		}
269362306a36Sopenharmony_ci		if (!tp->ops->walk)
269462306a36Sopenharmony_ci			continue;
269562306a36Sopenharmony_ci		arg.w.fn = tcf_node_dump;
269662306a36Sopenharmony_ci		arg.skb = skb;
269762306a36Sopenharmony_ci		arg.cb = cb;
269862306a36Sopenharmony_ci		arg.block = block;
269962306a36Sopenharmony_ci		arg.q = q;
270062306a36Sopenharmony_ci		arg.parent = parent;
270162306a36Sopenharmony_ci		arg.w.stop = 0;
270262306a36Sopenharmony_ci		arg.w.skip = cb->args[1] - 1;
270362306a36Sopenharmony_ci		arg.w.count = 0;
270462306a36Sopenharmony_ci		arg.w.cookie = cb->args[2];
270562306a36Sopenharmony_ci		arg.terse_dump = terse;
270662306a36Sopenharmony_ci		tp->ops->walk(tp, &arg.w, true);
270762306a36Sopenharmony_ci		cb->args[2] = arg.w.cookie;
270862306a36Sopenharmony_ci		cb->args[1] = arg.w.count + 1;
270962306a36Sopenharmony_ci		if (arg.w.stop)
271062306a36Sopenharmony_ci			goto errout;
271162306a36Sopenharmony_ci	}
271262306a36Sopenharmony_ci	return true;
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cierrout:
271562306a36Sopenharmony_ci	tcf_proto_put(tp, true, NULL);
271662306a36Sopenharmony_ci	return false;
271762306a36Sopenharmony_ci}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_cistatic const struct nla_policy tcf_tfilter_dump_policy[TCA_MAX + 1] = {
272062306a36Sopenharmony_ci	[TCA_DUMP_FLAGS] = NLA_POLICY_BITFIELD32(TCA_DUMP_FLAGS_TERSE),
272162306a36Sopenharmony_ci};
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci/* called with RTNL */
272462306a36Sopenharmony_cistatic int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
272562306a36Sopenharmony_ci{
272662306a36Sopenharmony_ci	struct tcf_chain *chain, *chain_prev;
272762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
272862306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
272962306a36Sopenharmony_ci	struct Qdisc *q = NULL;
273062306a36Sopenharmony_ci	struct tcf_block *block;
273162306a36Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
273262306a36Sopenharmony_ci	bool terse_dump = false;
273362306a36Sopenharmony_ci	long index_start;
273462306a36Sopenharmony_ci	long index;
273562306a36Sopenharmony_ci	u32 parent;
273662306a36Sopenharmony_ci	int err;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
273962306a36Sopenharmony_ci		return skb->len;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
274262306a36Sopenharmony_ci				     tcf_tfilter_dump_policy, cb->extack);
274362306a36Sopenharmony_ci	if (err)
274462306a36Sopenharmony_ci		return err;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (tca[TCA_DUMP_FLAGS]) {
274762306a36Sopenharmony_ci		struct nla_bitfield32 flags =
274862306a36Sopenharmony_ci			nla_get_bitfield32(tca[TCA_DUMP_FLAGS]);
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci		terse_dump = flags.value & TCA_DUMP_FLAGS_TERSE;
275162306a36Sopenharmony_ci	}
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
275462306a36Sopenharmony_ci		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
275562306a36Sopenharmony_ci		if (!block)
275662306a36Sopenharmony_ci			goto out;
275762306a36Sopenharmony_ci		/* If we work with block index, q is NULL and parent value
275862306a36Sopenharmony_ci		 * will never be used in the following code. The check
275962306a36Sopenharmony_ci		 * in tcf_fill_node prevents it. However, compiler does not
276062306a36Sopenharmony_ci		 * see that far, so set parent to zero to silence the warning
276162306a36Sopenharmony_ci		 * about parent being uninitialized.
276262306a36Sopenharmony_ci		 */
276362306a36Sopenharmony_ci		parent = 0;
276462306a36Sopenharmony_ci	} else {
276562306a36Sopenharmony_ci		const struct Qdisc_class_ops *cops;
276662306a36Sopenharmony_ci		struct net_device *dev;
276762306a36Sopenharmony_ci		unsigned long cl = 0;
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
277062306a36Sopenharmony_ci		if (!dev)
277162306a36Sopenharmony_ci			return skb->len;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci		parent = tcm->tcm_parent;
277462306a36Sopenharmony_ci		if (!parent)
277562306a36Sopenharmony_ci			q = rtnl_dereference(dev->qdisc);
277662306a36Sopenharmony_ci		else
277762306a36Sopenharmony_ci			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
277862306a36Sopenharmony_ci		if (!q)
277962306a36Sopenharmony_ci			goto out;
278062306a36Sopenharmony_ci		cops = q->ops->cl_ops;
278162306a36Sopenharmony_ci		if (!cops)
278262306a36Sopenharmony_ci			goto out;
278362306a36Sopenharmony_ci		if (!cops->tcf_block)
278462306a36Sopenharmony_ci			goto out;
278562306a36Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_parent)) {
278662306a36Sopenharmony_ci			cl = cops->find(q, tcm->tcm_parent);
278762306a36Sopenharmony_ci			if (cl == 0)
278862306a36Sopenharmony_ci				goto out;
278962306a36Sopenharmony_ci		}
279062306a36Sopenharmony_ci		block = cops->tcf_block(q, cl, NULL);
279162306a36Sopenharmony_ci		if (!block)
279262306a36Sopenharmony_ci			goto out;
279362306a36Sopenharmony_ci		parent = block->classid;
279462306a36Sopenharmony_ci		if (tcf_block_shared(block))
279562306a36Sopenharmony_ci			q = NULL;
279662306a36Sopenharmony_ci	}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	index_start = cb->args[0];
279962306a36Sopenharmony_ci	index = 0;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	for (chain = __tcf_get_next_chain(block, NULL);
280262306a36Sopenharmony_ci	     chain;
280362306a36Sopenharmony_ci	     chain_prev = chain,
280462306a36Sopenharmony_ci		     chain = __tcf_get_next_chain(block, chain),
280562306a36Sopenharmony_ci		     tcf_chain_put(chain_prev)) {
280662306a36Sopenharmony_ci		if (tca[TCA_CHAIN] &&
280762306a36Sopenharmony_ci		    nla_get_u32(tca[TCA_CHAIN]) != chain->index)
280862306a36Sopenharmony_ci			continue;
280962306a36Sopenharmony_ci		if (!tcf_chain_dump(chain, q, parent, skb, cb,
281062306a36Sopenharmony_ci				    index_start, &index, terse_dump)) {
281162306a36Sopenharmony_ci			tcf_chain_put(chain);
281262306a36Sopenharmony_ci			err = -EMSGSIZE;
281362306a36Sopenharmony_ci			break;
281462306a36Sopenharmony_ci		}
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
281862306a36Sopenharmony_ci		tcf_block_refcnt_put(block, true);
281962306a36Sopenharmony_ci	cb->args[0] = index;
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ciout:
282262306a36Sopenharmony_ci	/* If we did no progress, the error (EMSGSIZE) is real */
282362306a36Sopenharmony_ci	if (skb->len == 0 && err)
282462306a36Sopenharmony_ci		return err;
282562306a36Sopenharmony_ci	return skb->len;
282662306a36Sopenharmony_ci}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_cistatic int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
282962306a36Sopenharmony_ci			      void *tmplt_priv, u32 chain_index,
283062306a36Sopenharmony_ci			      struct net *net, struct sk_buff *skb,
283162306a36Sopenharmony_ci			      struct tcf_block *block,
283262306a36Sopenharmony_ci			      u32 portid, u32 seq, u16 flags, int event,
283362306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
283462306a36Sopenharmony_ci{
283562306a36Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
283662306a36Sopenharmony_ci	const struct tcf_proto_ops *ops;
283762306a36Sopenharmony_ci	struct nlmsghdr *nlh;
283862306a36Sopenharmony_ci	struct tcmsg *tcm;
283962306a36Sopenharmony_ci	void *priv;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	ops = tmplt_ops;
284262306a36Sopenharmony_ci	priv = tmplt_priv;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
284562306a36Sopenharmony_ci	if (!nlh)
284662306a36Sopenharmony_ci		goto out_nlmsg_trim;
284762306a36Sopenharmony_ci	tcm = nlmsg_data(nlh);
284862306a36Sopenharmony_ci	tcm->tcm_family = AF_UNSPEC;
284962306a36Sopenharmony_ci	tcm->tcm__pad1 = 0;
285062306a36Sopenharmony_ci	tcm->tcm__pad2 = 0;
285162306a36Sopenharmony_ci	tcm->tcm_handle = 0;
285262306a36Sopenharmony_ci	if (block->q) {
285362306a36Sopenharmony_ci		tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex;
285462306a36Sopenharmony_ci		tcm->tcm_parent = block->q->handle;
285562306a36Sopenharmony_ci	} else {
285662306a36Sopenharmony_ci		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
285762306a36Sopenharmony_ci		tcm->tcm_block_index = block->index;
285862306a36Sopenharmony_ci	}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	if (nla_put_u32(skb, TCA_CHAIN, chain_index))
286162306a36Sopenharmony_ci		goto nla_put_failure;
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	if (ops) {
286462306a36Sopenharmony_ci		if (nla_put_string(skb, TCA_KIND, ops->kind))
286562306a36Sopenharmony_ci			goto nla_put_failure;
286662306a36Sopenharmony_ci		if (ops->tmplt_dump(skb, net, priv) < 0)
286762306a36Sopenharmony_ci			goto nla_put_failure;
286862306a36Sopenharmony_ci	}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	if (extack && extack->_msg &&
287162306a36Sopenharmony_ci	    nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
287262306a36Sopenharmony_ci		goto out_nlmsg_trim;
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	return skb->len;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ciout_nlmsg_trim:
287962306a36Sopenharmony_cinla_put_failure:
288062306a36Sopenharmony_ci	nlmsg_trim(skb, b);
288162306a36Sopenharmony_ci	return -EMSGSIZE;
288262306a36Sopenharmony_ci}
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
288562306a36Sopenharmony_ci			   u32 seq, u16 flags, int event, bool unicast,
288662306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
288962306a36Sopenharmony_ci	struct tcf_block *block = chain->block;
289062306a36Sopenharmony_ci	struct net *net = block->net;
289162306a36Sopenharmony_ci	struct sk_buff *skb;
289262306a36Sopenharmony_ci	int err = 0;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
289562306a36Sopenharmony_ci	if (!skb)
289662306a36Sopenharmony_ci		return -ENOBUFS;
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
289962306a36Sopenharmony_ci			       chain->index, net, skb, block, portid,
290062306a36Sopenharmony_ci			       seq, flags, event, extack) <= 0) {
290162306a36Sopenharmony_ci		kfree_skb(skb);
290262306a36Sopenharmony_ci		return -EINVAL;
290362306a36Sopenharmony_ci	}
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci	if (unicast)
290662306a36Sopenharmony_ci		err = rtnl_unicast(skb, net, portid);
290762306a36Sopenharmony_ci	else
290862306a36Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
290962306a36Sopenharmony_ci				     flags & NLM_F_ECHO);
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	return err;
291262306a36Sopenharmony_ci}
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
291562306a36Sopenharmony_ci				  void *tmplt_priv, u32 chain_index,
291662306a36Sopenharmony_ci				  struct tcf_block *block, struct sk_buff *oskb,
291762306a36Sopenharmony_ci				  u32 seq, u16 flags, bool unicast)
291862306a36Sopenharmony_ci{
291962306a36Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
292062306a36Sopenharmony_ci	struct net *net = block->net;
292162306a36Sopenharmony_ci	struct sk_buff *skb;
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
292462306a36Sopenharmony_ci	if (!skb)
292562306a36Sopenharmony_ci		return -ENOBUFS;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
292862306a36Sopenharmony_ci			       block, portid, seq, flags, RTM_DELCHAIN, NULL) <= 0) {
292962306a36Sopenharmony_ci		kfree_skb(skb);
293062306a36Sopenharmony_ci		return -EINVAL;
293162306a36Sopenharmony_ci	}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	if (unicast)
293462306a36Sopenharmony_ci		return rtnl_unicast(skb, net, portid);
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
293762306a36Sopenharmony_ci}
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_cistatic int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
294062306a36Sopenharmony_ci			      struct nlattr **tca,
294162306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
294262306a36Sopenharmony_ci{
294362306a36Sopenharmony_ci	const struct tcf_proto_ops *ops;
294462306a36Sopenharmony_ci	char name[IFNAMSIZ];
294562306a36Sopenharmony_ci	void *tmplt_priv;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	/* If kind is not set, user did not specify template. */
294862306a36Sopenharmony_ci	if (!tca[TCA_KIND])
294962306a36Sopenharmony_ci		return 0;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
295262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC chain template name too long");
295362306a36Sopenharmony_ci		return -EINVAL;
295462306a36Sopenharmony_ci	}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	ops = tcf_proto_lookup_ops(name, true, extack);
295762306a36Sopenharmony_ci	if (IS_ERR(ops))
295862306a36Sopenharmony_ci		return PTR_ERR(ops);
295962306a36Sopenharmony_ci	if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump ||
296062306a36Sopenharmony_ci	    !ops->tmplt_reoffload) {
296162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier");
296262306a36Sopenharmony_ci		module_put(ops->owner);
296362306a36Sopenharmony_ci		return -EOPNOTSUPP;
296462306a36Sopenharmony_ci	}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	tmplt_priv = ops->tmplt_create(net, chain, tca, extack);
296762306a36Sopenharmony_ci	if (IS_ERR(tmplt_priv)) {
296862306a36Sopenharmony_ci		module_put(ops->owner);
296962306a36Sopenharmony_ci		return PTR_ERR(tmplt_priv);
297062306a36Sopenharmony_ci	}
297162306a36Sopenharmony_ci	chain->tmplt_ops = ops;
297262306a36Sopenharmony_ci	chain->tmplt_priv = tmplt_priv;
297362306a36Sopenharmony_ci	return 0;
297462306a36Sopenharmony_ci}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
297762306a36Sopenharmony_ci			       void *tmplt_priv)
297862306a36Sopenharmony_ci{
297962306a36Sopenharmony_ci	/* If template ops are set, no work to do for us. */
298062306a36Sopenharmony_ci	if (!tmplt_ops)
298162306a36Sopenharmony_ci		return;
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	tmplt_ops->tmplt_destroy(tmplt_priv);
298462306a36Sopenharmony_ci	module_put(tmplt_ops->owner);
298562306a36Sopenharmony_ci}
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci/* Add/delete/get a chain */
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_cistatic int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
299062306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
299162306a36Sopenharmony_ci{
299262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
299362306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
299462306a36Sopenharmony_ci	struct tcmsg *t;
299562306a36Sopenharmony_ci	u32 parent;
299662306a36Sopenharmony_ci	u32 chain_index;
299762306a36Sopenharmony_ci	struct Qdisc *q;
299862306a36Sopenharmony_ci	struct tcf_chain *chain;
299962306a36Sopenharmony_ci	struct tcf_block *block;
300062306a36Sopenharmony_ci	unsigned long cl;
300162306a36Sopenharmony_ci	int err;
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_cireplay:
300462306a36Sopenharmony_ci	q = NULL;
300562306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
300662306a36Sopenharmony_ci				     rtm_tca_policy, extack);
300762306a36Sopenharmony_ci	if (err < 0)
300862306a36Sopenharmony_ci		return err;
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	t = nlmsg_data(n);
301162306a36Sopenharmony_ci	parent = t->tcm_parent;
301262306a36Sopenharmony_ci	cl = 0;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	block = tcf_block_find(net, &q, &parent, &cl,
301562306a36Sopenharmony_ci			       t->tcm_ifindex, t->tcm_block_index, extack);
301662306a36Sopenharmony_ci	if (IS_ERR(block))
301762306a36Sopenharmony_ci		return PTR_ERR(block);
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
302062306a36Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
302162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
302262306a36Sopenharmony_ci		err = -EINVAL;
302362306a36Sopenharmony_ci		goto errout_block;
302462306a36Sopenharmony_ci	}
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci	mutex_lock(&block->lock);
302762306a36Sopenharmony_ci	chain = tcf_chain_lookup(block, chain_index);
302862306a36Sopenharmony_ci	if (n->nlmsg_type == RTM_NEWCHAIN) {
302962306a36Sopenharmony_ci		if (chain) {
303062306a36Sopenharmony_ci			if (tcf_chain_held_by_acts_only(chain)) {
303162306a36Sopenharmony_ci				/* The chain exists only because there is
303262306a36Sopenharmony_ci				 * some action referencing it.
303362306a36Sopenharmony_ci				 */
303462306a36Sopenharmony_ci				tcf_chain_hold(chain);
303562306a36Sopenharmony_ci			} else {
303662306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Filter chain already exists");
303762306a36Sopenharmony_ci				err = -EEXIST;
303862306a36Sopenharmony_ci				goto errout_block_locked;
303962306a36Sopenharmony_ci			}
304062306a36Sopenharmony_ci		} else {
304162306a36Sopenharmony_ci			if (!(n->nlmsg_flags & NLM_F_CREATE)) {
304262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
304362306a36Sopenharmony_ci				err = -ENOENT;
304462306a36Sopenharmony_ci				goto errout_block_locked;
304562306a36Sopenharmony_ci			}
304662306a36Sopenharmony_ci			chain = tcf_chain_create(block, chain_index);
304762306a36Sopenharmony_ci			if (!chain) {
304862306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Failed to create filter chain");
304962306a36Sopenharmony_ci				err = -ENOMEM;
305062306a36Sopenharmony_ci				goto errout_block_locked;
305162306a36Sopenharmony_ci			}
305262306a36Sopenharmony_ci		}
305362306a36Sopenharmony_ci	} else {
305462306a36Sopenharmony_ci		if (!chain || tcf_chain_held_by_acts_only(chain)) {
305562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
305662306a36Sopenharmony_ci			err = -EINVAL;
305762306a36Sopenharmony_ci			goto errout_block_locked;
305862306a36Sopenharmony_ci		}
305962306a36Sopenharmony_ci		tcf_chain_hold(chain);
306062306a36Sopenharmony_ci	}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	if (n->nlmsg_type == RTM_NEWCHAIN) {
306362306a36Sopenharmony_ci		/* Modifying chain requires holding parent block lock. In case
306462306a36Sopenharmony_ci		 * the chain was successfully added, take a reference to the
306562306a36Sopenharmony_ci		 * chain. This ensures that an empty chain does not disappear at
306662306a36Sopenharmony_ci		 * the end of this function.
306762306a36Sopenharmony_ci		 */
306862306a36Sopenharmony_ci		tcf_chain_hold(chain);
306962306a36Sopenharmony_ci		chain->explicitly_created = true;
307062306a36Sopenharmony_ci	}
307162306a36Sopenharmony_ci	mutex_unlock(&block->lock);
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	switch (n->nlmsg_type) {
307462306a36Sopenharmony_ci	case RTM_NEWCHAIN:
307562306a36Sopenharmony_ci		err = tc_chain_tmplt_add(chain, net, tca, extack);
307662306a36Sopenharmony_ci		if (err) {
307762306a36Sopenharmony_ci			tcf_chain_put_explicitly_created(chain);
307862306a36Sopenharmony_ci			goto errout;
307962306a36Sopenharmony_ci		}
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
308262306a36Sopenharmony_ci				RTM_NEWCHAIN, false, extack);
308362306a36Sopenharmony_ci		break;
308462306a36Sopenharmony_ci	case RTM_DELCHAIN:
308562306a36Sopenharmony_ci		tfilter_notify_chain(net, skb, block, q, parent, n,
308662306a36Sopenharmony_ci				     chain, RTM_DELTFILTER, extack);
308762306a36Sopenharmony_ci		/* Flush the chain first as the user requested chain removal. */
308862306a36Sopenharmony_ci		tcf_chain_flush(chain, true);
308962306a36Sopenharmony_ci		/* In case the chain was successfully deleted, put a reference
309062306a36Sopenharmony_ci		 * to the chain previously taken during addition.
309162306a36Sopenharmony_ci		 */
309262306a36Sopenharmony_ci		tcf_chain_put_explicitly_created(chain);
309362306a36Sopenharmony_ci		break;
309462306a36Sopenharmony_ci	case RTM_GETCHAIN:
309562306a36Sopenharmony_ci		err = tc_chain_notify(chain, skb, n->nlmsg_seq,
309662306a36Sopenharmony_ci				      n->nlmsg_flags, n->nlmsg_type, true, extack);
309762306a36Sopenharmony_ci		if (err < 0)
309862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
309962306a36Sopenharmony_ci		break;
310062306a36Sopenharmony_ci	default:
310162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
310262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unsupported message type");
310362306a36Sopenharmony_ci		goto errout;
310462306a36Sopenharmony_ci	}
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_cierrout:
310762306a36Sopenharmony_ci	tcf_chain_put(chain);
310862306a36Sopenharmony_cierrout_block:
310962306a36Sopenharmony_ci	tcf_block_release(q, block, true);
311062306a36Sopenharmony_ci	if (err == -EAGAIN)
311162306a36Sopenharmony_ci		/* Replay the request. */
311262306a36Sopenharmony_ci		goto replay;
311362306a36Sopenharmony_ci	return err;
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_cierrout_block_locked:
311662306a36Sopenharmony_ci	mutex_unlock(&block->lock);
311762306a36Sopenharmony_ci	goto errout_block;
311862306a36Sopenharmony_ci}
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci/* called with RTNL */
312162306a36Sopenharmony_cistatic int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
312262306a36Sopenharmony_ci{
312362306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
312462306a36Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
312562306a36Sopenharmony_ci	struct Qdisc *q = NULL;
312662306a36Sopenharmony_ci	struct tcf_block *block;
312762306a36Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
312862306a36Sopenharmony_ci	struct tcf_chain *chain;
312962306a36Sopenharmony_ci	long index_start;
313062306a36Sopenharmony_ci	long index;
313162306a36Sopenharmony_ci	int err;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
313462306a36Sopenharmony_ci		return skb->len;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
313762306a36Sopenharmony_ci				     rtm_tca_policy, cb->extack);
313862306a36Sopenharmony_ci	if (err)
313962306a36Sopenharmony_ci		return err;
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
314262306a36Sopenharmony_ci		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
314362306a36Sopenharmony_ci		if (!block)
314462306a36Sopenharmony_ci			goto out;
314562306a36Sopenharmony_ci	} else {
314662306a36Sopenharmony_ci		const struct Qdisc_class_ops *cops;
314762306a36Sopenharmony_ci		struct net_device *dev;
314862306a36Sopenharmony_ci		unsigned long cl = 0;
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
315162306a36Sopenharmony_ci		if (!dev)
315262306a36Sopenharmony_ci			return skb->len;
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci		if (!tcm->tcm_parent)
315562306a36Sopenharmony_ci			q = rtnl_dereference(dev->qdisc);
315662306a36Sopenharmony_ci		else
315762306a36Sopenharmony_ci			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci		if (!q)
316062306a36Sopenharmony_ci			goto out;
316162306a36Sopenharmony_ci		cops = q->ops->cl_ops;
316262306a36Sopenharmony_ci		if (!cops)
316362306a36Sopenharmony_ci			goto out;
316462306a36Sopenharmony_ci		if (!cops->tcf_block)
316562306a36Sopenharmony_ci			goto out;
316662306a36Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_parent)) {
316762306a36Sopenharmony_ci			cl = cops->find(q, tcm->tcm_parent);
316862306a36Sopenharmony_ci			if (cl == 0)
316962306a36Sopenharmony_ci				goto out;
317062306a36Sopenharmony_ci		}
317162306a36Sopenharmony_ci		block = cops->tcf_block(q, cl, NULL);
317262306a36Sopenharmony_ci		if (!block)
317362306a36Sopenharmony_ci			goto out;
317462306a36Sopenharmony_ci		if (tcf_block_shared(block))
317562306a36Sopenharmony_ci			q = NULL;
317662306a36Sopenharmony_ci	}
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	index_start = cb->args[0];
317962306a36Sopenharmony_ci	index = 0;
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	mutex_lock(&block->lock);
318262306a36Sopenharmony_ci	list_for_each_entry(chain, &block->chain_list, list) {
318362306a36Sopenharmony_ci		if ((tca[TCA_CHAIN] &&
318462306a36Sopenharmony_ci		     nla_get_u32(tca[TCA_CHAIN]) != chain->index))
318562306a36Sopenharmony_ci			continue;
318662306a36Sopenharmony_ci		if (index < index_start) {
318762306a36Sopenharmony_ci			index++;
318862306a36Sopenharmony_ci			continue;
318962306a36Sopenharmony_ci		}
319062306a36Sopenharmony_ci		if (tcf_chain_held_by_acts_only(chain))
319162306a36Sopenharmony_ci			continue;
319262306a36Sopenharmony_ci		err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
319362306a36Sopenharmony_ci					 chain->index, net, skb, block,
319462306a36Sopenharmony_ci					 NETLINK_CB(cb->skb).portid,
319562306a36Sopenharmony_ci					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
319662306a36Sopenharmony_ci					 RTM_NEWCHAIN, NULL);
319762306a36Sopenharmony_ci		if (err <= 0)
319862306a36Sopenharmony_ci			break;
319962306a36Sopenharmony_ci		index++;
320062306a36Sopenharmony_ci	}
320162306a36Sopenharmony_ci	mutex_unlock(&block->lock);
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
320462306a36Sopenharmony_ci		tcf_block_refcnt_put(block, true);
320562306a36Sopenharmony_ci	cb->args[0] = index;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ciout:
320862306a36Sopenharmony_ci	/* If we did no progress, the error (EMSGSIZE) is real */
320962306a36Sopenharmony_ci	if (skb->len == 0 && err)
321062306a36Sopenharmony_ci		return err;
321162306a36Sopenharmony_ci	return skb->len;
321262306a36Sopenharmony_ci}
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ciint tcf_exts_init_ex(struct tcf_exts *exts, struct net *net, int action,
321562306a36Sopenharmony_ci		     int police, struct tcf_proto *tp, u32 handle,
321662306a36Sopenharmony_ci		     bool use_action_miss)
321762306a36Sopenharmony_ci{
321862306a36Sopenharmony_ci	int err = 0;
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
322162306a36Sopenharmony_ci	exts->type = 0;
322262306a36Sopenharmony_ci	exts->nr_actions = 0;
322362306a36Sopenharmony_ci	exts->miss_cookie_node = NULL;
322462306a36Sopenharmony_ci	/* Note: we do not own yet a reference on net.
322562306a36Sopenharmony_ci	 * This reference might be taken later from tcf_exts_get_net().
322662306a36Sopenharmony_ci	 */
322762306a36Sopenharmony_ci	exts->net = net;
322862306a36Sopenharmony_ci	exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
322962306a36Sopenharmony_ci				GFP_KERNEL);
323062306a36Sopenharmony_ci	if (!exts->actions)
323162306a36Sopenharmony_ci		return -ENOMEM;
323262306a36Sopenharmony_ci#endif
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	exts->action = action;
323562306a36Sopenharmony_ci	exts->police = police;
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	if (!use_action_miss)
323862306a36Sopenharmony_ci		return 0;
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	err = tcf_exts_miss_cookie_base_alloc(exts, tp, handle);
324162306a36Sopenharmony_ci	if (err)
324262306a36Sopenharmony_ci		goto err_miss_alloc;
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci	return 0;
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_cierr_miss_alloc:
324762306a36Sopenharmony_ci	tcf_exts_destroy(exts);
324862306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
324962306a36Sopenharmony_ci	exts->actions = NULL;
325062306a36Sopenharmony_ci#endif
325162306a36Sopenharmony_ci	return err;
325262306a36Sopenharmony_ci}
325362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_init_ex);
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_civoid tcf_exts_destroy(struct tcf_exts *exts)
325662306a36Sopenharmony_ci{
325762306a36Sopenharmony_ci	tcf_exts_miss_cookie_base_destroy(exts);
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
326062306a36Sopenharmony_ci	if (exts->actions) {
326162306a36Sopenharmony_ci		tcf_action_destroy(exts->actions, TCA_ACT_UNBIND);
326262306a36Sopenharmony_ci		kfree(exts->actions);
326362306a36Sopenharmony_ci	}
326462306a36Sopenharmony_ci	exts->nr_actions = 0;
326562306a36Sopenharmony_ci#endif
326662306a36Sopenharmony_ci}
326762306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_destroy);
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ciint tcf_exts_validate_ex(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
327062306a36Sopenharmony_ci			 struct nlattr *rate_tlv, struct tcf_exts *exts,
327162306a36Sopenharmony_ci			 u32 flags, u32 fl_flags, struct netlink_ext_ack *extack)
327262306a36Sopenharmony_ci{
327362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
327462306a36Sopenharmony_ci	{
327562306a36Sopenharmony_ci		int init_res[TCA_ACT_MAX_PRIO] = {};
327662306a36Sopenharmony_ci		struct tc_action *act;
327762306a36Sopenharmony_ci		size_t attr_size = 0;
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci		if (exts->police && tb[exts->police]) {
328062306a36Sopenharmony_ci			struct tc_action_ops *a_o;
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci			a_o = tc_action_load_ops(tb[exts->police], true,
328362306a36Sopenharmony_ci						 !(flags & TCA_ACT_FLAGS_NO_RTNL),
328462306a36Sopenharmony_ci						 extack);
328562306a36Sopenharmony_ci			if (IS_ERR(a_o))
328662306a36Sopenharmony_ci				return PTR_ERR(a_o);
328762306a36Sopenharmony_ci			flags |= TCA_ACT_FLAGS_POLICE | TCA_ACT_FLAGS_BIND;
328862306a36Sopenharmony_ci			act = tcf_action_init_1(net, tp, tb[exts->police],
328962306a36Sopenharmony_ci						rate_tlv, a_o, init_res, flags,
329062306a36Sopenharmony_ci						extack);
329162306a36Sopenharmony_ci			module_put(a_o->owner);
329262306a36Sopenharmony_ci			if (IS_ERR(act))
329362306a36Sopenharmony_ci				return PTR_ERR(act);
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci			act->type = exts->type = TCA_OLD_COMPAT;
329662306a36Sopenharmony_ci			exts->actions[0] = act;
329762306a36Sopenharmony_ci			exts->nr_actions = 1;
329862306a36Sopenharmony_ci			tcf_idr_insert_many(exts->actions);
329962306a36Sopenharmony_ci		} else if (exts->action && tb[exts->action]) {
330062306a36Sopenharmony_ci			int err;
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci			flags |= TCA_ACT_FLAGS_BIND;
330362306a36Sopenharmony_ci			err = tcf_action_init(net, tp, tb[exts->action],
330462306a36Sopenharmony_ci					      rate_tlv, exts->actions, init_res,
330562306a36Sopenharmony_ci					      &attr_size, flags, fl_flags,
330662306a36Sopenharmony_ci					      extack);
330762306a36Sopenharmony_ci			if (err < 0)
330862306a36Sopenharmony_ci				return err;
330962306a36Sopenharmony_ci			exts->nr_actions = err;
331062306a36Sopenharmony_ci		}
331162306a36Sopenharmony_ci	}
331262306a36Sopenharmony_ci#else
331362306a36Sopenharmony_ci	if ((exts->action && tb[exts->action]) ||
331462306a36Sopenharmony_ci	    (exts->police && tb[exts->police])) {
331562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)");
331662306a36Sopenharmony_ci		return -EOPNOTSUPP;
331762306a36Sopenharmony_ci	}
331862306a36Sopenharmony_ci#endif
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci	return 0;
332162306a36Sopenharmony_ci}
332262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_validate_ex);
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ciint tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
332562306a36Sopenharmony_ci		      struct nlattr *rate_tlv, struct tcf_exts *exts,
332662306a36Sopenharmony_ci		      u32 flags, struct netlink_ext_ack *extack)
332762306a36Sopenharmony_ci{
332862306a36Sopenharmony_ci	return tcf_exts_validate_ex(net, tp, tb, rate_tlv, exts,
332962306a36Sopenharmony_ci				    flags, 0, extack);
333062306a36Sopenharmony_ci}
333162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_validate);
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_civoid tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src)
333462306a36Sopenharmony_ci{
333562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
333662306a36Sopenharmony_ci	struct tcf_exts old = *dst;
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	*dst = *src;
333962306a36Sopenharmony_ci	tcf_exts_destroy(&old);
334062306a36Sopenharmony_ci#endif
334162306a36Sopenharmony_ci}
334262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_change);
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
334562306a36Sopenharmony_cistatic struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
334662306a36Sopenharmony_ci{
334762306a36Sopenharmony_ci	if (exts->nr_actions == 0)
334862306a36Sopenharmony_ci		return NULL;
334962306a36Sopenharmony_ci	else
335062306a36Sopenharmony_ci		return exts->actions[0];
335162306a36Sopenharmony_ci}
335262306a36Sopenharmony_ci#endif
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ciint tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
335562306a36Sopenharmony_ci{
335662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
335762306a36Sopenharmony_ci	struct nlattr *nest;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	if (exts->action && tcf_exts_has_actions(exts)) {
336062306a36Sopenharmony_ci		/*
336162306a36Sopenharmony_ci		 * again for backward compatible mode - we want
336262306a36Sopenharmony_ci		 * to work with both old and new modes of entering
336362306a36Sopenharmony_ci		 * tc data even if iproute2  was newer - jhs
336462306a36Sopenharmony_ci		 */
336562306a36Sopenharmony_ci		if (exts->type != TCA_OLD_COMPAT) {
336662306a36Sopenharmony_ci			nest = nla_nest_start_noflag(skb, exts->action);
336762306a36Sopenharmony_ci			if (nest == NULL)
336862306a36Sopenharmony_ci				goto nla_put_failure;
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci			if (tcf_action_dump(skb, exts->actions, 0, 0, false)
337162306a36Sopenharmony_ci			    < 0)
337262306a36Sopenharmony_ci				goto nla_put_failure;
337362306a36Sopenharmony_ci			nla_nest_end(skb, nest);
337462306a36Sopenharmony_ci		} else if (exts->police) {
337562306a36Sopenharmony_ci			struct tc_action *act = tcf_exts_first_act(exts);
337662306a36Sopenharmony_ci			nest = nla_nest_start_noflag(skb, exts->police);
337762306a36Sopenharmony_ci			if (nest == NULL || !act)
337862306a36Sopenharmony_ci				goto nla_put_failure;
337962306a36Sopenharmony_ci			if (tcf_action_dump_old(skb, act, 0, 0) < 0)
338062306a36Sopenharmony_ci				goto nla_put_failure;
338162306a36Sopenharmony_ci			nla_nest_end(skb, nest);
338262306a36Sopenharmony_ci		}
338362306a36Sopenharmony_ci	}
338462306a36Sopenharmony_ci	return 0;
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_cinla_put_failure:
338762306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
338862306a36Sopenharmony_ci	return -1;
338962306a36Sopenharmony_ci#else
339062306a36Sopenharmony_ci	return 0;
339162306a36Sopenharmony_ci#endif
339262306a36Sopenharmony_ci}
339362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ciint tcf_exts_terse_dump(struct sk_buff *skb, struct tcf_exts *exts)
339662306a36Sopenharmony_ci{
339762306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
339862306a36Sopenharmony_ci	struct nlattr *nest;
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	if (!exts->action || !tcf_exts_has_actions(exts))
340162306a36Sopenharmony_ci		return 0;
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, exts->action);
340462306a36Sopenharmony_ci	if (!nest)
340562306a36Sopenharmony_ci		goto nla_put_failure;
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci	if (tcf_action_dump(skb, exts->actions, 0, 0, true) < 0)
340862306a36Sopenharmony_ci		goto nla_put_failure;
340962306a36Sopenharmony_ci	nla_nest_end(skb, nest);
341062306a36Sopenharmony_ci	return 0;
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_cinla_put_failure:
341362306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
341462306a36Sopenharmony_ci	return -1;
341562306a36Sopenharmony_ci#else
341662306a36Sopenharmony_ci	return 0;
341762306a36Sopenharmony_ci#endif
341862306a36Sopenharmony_ci}
341962306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_terse_dump);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ciint tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
342262306a36Sopenharmony_ci{
342362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
342462306a36Sopenharmony_ci	struct tc_action *a = tcf_exts_first_act(exts);
342562306a36Sopenharmony_ci	if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0)
342662306a36Sopenharmony_ci		return -1;
342762306a36Sopenharmony_ci#endif
342862306a36Sopenharmony_ci	return 0;
342962306a36Sopenharmony_ci}
343062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump_stats);
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_cistatic void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
343362306a36Sopenharmony_ci{
343462306a36Sopenharmony_ci	if (*flags & TCA_CLS_FLAGS_IN_HW)
343562306a36Sopenharmony_ci		return;
343662306a36Sopenharmony_ci	*flags |= TCA_CLS_FLAGS_IN_HW;
343762306a36Sopenharmony_ci	atomic_inc(&block->offloadcnt);
343862306a36Sopenharmony_ci}
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_cistatic void tcf_block_offload_dec(struct tcf_block *block, u32 *flags)
344162306a36Sopenharmony_ci{
344262306a36Sopenharmony_ci	if (!(*flags & TCA_CLS_FLAGS_IN_HW))
344362306a36Sopenharmony_ci		return;
344462306a36Sopenharmony_ci	*flags &= ~TCA_CLS_FLAGS_IN_HW;
344562306a36Sopenharmony_ci	atomic_dec(&block->offloadcnt);
344662306a36Sopenharmony_ci}
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_cistatic void tc_cls_offload_cnt_update(struct tcf_block *block,
344962306a36Sopenharmony_ci				      struct tcf_proto *tp, u32 *cnt,
345062306a36Sopenharmony_ci				      u32 *flags, u32 diff, bool add)
345162306a36Sopenharmony_ci{
345262306a36Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	spin_lock(&tp->lock);
345562306a36Sopenharmony_ci	if (add) {
345662306a36Sopenharmony_ci		if (!*cnt)
345762306a36Sopenharmony_ci			tcf_block_offload_inc(block, flags);
345862306a36Sopenharmony_ci		*cnt += diff;
345962306a36Sopenharmony_ci	} else {
346062306a36Sopenharmony_ci		*cnt -= diff;
346162306a36Sopenharmony_ci		if (!*cnt)
346262306a36Sopenharmony_ci			tcf_block_offload_dec(block, flags);
346362306a36Sopenharmony_ci	}
346462306a36Sopenharmony_ci	spin_unlock(&tp->lock);
346562306a36Sopenharmony_ci}
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_cistatic void
346862306a36Sopenharmony_citc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp,
346962306a36Sopenharmony_ci			 u32 *cnt, u32 *flags)
347062306a36Sopenharmony_ci{
347162306a36Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	spin_lock(&tp->lock);
347462306a36Sopenharmony_ci	tcf_block_offload_dec(block, flags);
347562306a36Sopenharmony_ci	*cnt = 0;
347662306a36Sopenharmony_ci	spin_unlock(&tp->lock);
347762306a36Sopenharmony_ci}
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_cistatic int
348062306a36Sopenharmony_ci__tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
348162306a36Sopenharmony_ci		   void *type_data, bool err_stop)
348262306a36Sopenharmony_ci{
348362306a36Sopenharmony_ci	struct flow_block_cb *block_cb;
348462306a36Sopenharmony_ci	int ok_count = 0;
348562306a36Sopenharmony_ci	int err;
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	list_for_each_entry(block_cb, &block->flow_block.cb_list, list) {
348862306a36Sopenharmony_ci		err = block_cb->cb(type, type_data, block_cb->cb_priv);
348962306a36Sopenharmony_ci		if (err) {
349062306a36Sopenharmony_ci			if (err_stop)
349162306a36Sopenharmony_ci				return err;
349262306a36Sopenharmony_ci		} else {
349362306a36Sopenharmony_ci			ok_count++;
349462306a36Sopenharmony_ci		}
349562306a36Sopenharmony_ci	}
349662306a36Sopenharmony_ci	return ok_count;
349762306a36Sopenharmony_ci}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ciint tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
350062306a36Sopenharmony_ci		     void *type_data, bool err_stop, bool rtnl_held)
350162306a36Sopenharmony_ci{
350262306a36Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
350362306a36Sopenharmony_ci	int ok_count;
350462306a36Sopenharmony_ci
350562306a36Sopenharmony_ciretry:
350662306a36Sopenharmony_ci	if (take_rtnl)
350762306a36Sopenharmony_ci		rtnl_lock();
350862306a36Sopenharmony_ci	down_read(&block->cb_lock);
350962306a36Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
351062306a36Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
351162306a36Sopenharmony_ci	 * obtain the locks in same order here.
351262306a36Sopenharmony_ci	 */
351362306a36Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
351462306a36Sopenharmony_ci		up_read(&block->cb_lock);
351562306a36Sopenharmony_ci		take_rtnl = true;
351662306a36Sopenharmony_ci		goto retry;
351762306a36Sopenharmony_ci	}
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci	up_read(&block->cb_lock);
352262306a36Sopenharmony_ci	if (take_rtnl)
352362306a36Sopenharmony_ci		rtnl_unlock();
352462306a36Sopenharmony_ci	return ok_count;
352562306a36Sopenharmony_ci}
352662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_call);
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci/* Non-destructive filter add. If filter that wasn't already in hardware is
352962306a36Sopenharmony_ci * successfully offloaded, increment block offloads counter. On failure,
353062306a36Sopenharmony_ci * previously offloaded filter is considered to be intact and offloads counter
353162306a36Sopenharmony_ci * is not decremented.
353262306a36Sopenharmony_ci */
353362306a36Sopenharmony_ci
353462306a36Sopenharmony_ciint tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp,
353562306a36Sopenharmony_ci		    enum tc_setup_type type, void *type_data, bool err_stop,
353662306a36Sopenharmony_ci		    u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
353762306a36Sopenharmony_ci{
353862306a36Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
353962306a36Sopenharmony_ci	int ok_count;
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ciretry:
354262306a36Sopenharmony_ci	if (take_rtnl)
354362306a36Sopenharmony_ci		rtnl_lock();
354462306a36Sopenharmony_ci	down_read(&block->cb_lock);
354562306a36Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
354662306a36Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
354762306a36Sopenharmony_ci	 * obtain the locks in same order here.
354862306a36Sopenharmony_ci	 */
354962306a36Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
355062306a36Sopenharmony_ci		up_read(&block->cb_lock);
355162306a36Sopenharmony_ci		take_rtnl = true;
355262306a36Sopenharmony_ci		goto retry;
355362306a36Sopenharmony_ci	}
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	/* Make sure all netdevs sharing this block are offload-capable. */
355662306a36Sopenharmony_ci	if (block->nooffloaddevcnt && err_stop) {
355762306a36Sopenharmony_ci		ok_count = -EOPNOTSUPP;
355862306a36Sopenharmony_ci		goto err_unlock;
355962306a36Sopenharmony_ci	}
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
356262306a36Sopenharmony_ci	if (ok_count < 0)
356362306a36Sopenharmony_ci		goto err_unlock;
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	if (tp->ops->hw_add)
356662306a36Sopenharmony_ci		tp->ops->hw_add(tp, type_data);
356762306a36Sopenharmony_ci	if (ok_count > 0)
356862306a36Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
356962306a36Sopenharmony_ci					  ok_count, true);
357062306a36Sopenharmony_cierr_unlock:
357162306a36Sopenharmony_ci	up_read(&block->cb_lock);
357262306a36Sopenharmony_ci	if (take_rtnl)
357362306a36Sopenharmony_ci		rtnl_unlock();
357462306a36Sopenharmony_ci	return min(ok_count, 0);
357562306a36Sopenharmony_ci}
357662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_add);
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci/* Destructive filter replace. If filter that wasn't already in hardware is
357962306a36Sopenharmony_ci * successfully offloaded, increment block offload counter. On failure,
358062306a36Sopenharmony_ci * previously offloaded filter is considered to be destroyed and offload counter
358162306a36Sopenharmony_ci * is decremented.
358262306a36Sopenharmony_ci */
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ciint tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp,
358562306a36Sopenharmony_ci			enum tc_setup_type type, void *type_data, bool err_stop,
358662306a36Sopenharmony_ci			u32 *old_flags, unsigned int *old_in_hw_count,
358762306a36Sopenharmony_ci			u32 *new_flags, unsigned int *new_in_hw_count,
358862306a36Sopenharmony_ci			bool rtnl_held)
358962306a36Sopenharmony_ci{
359062306a36Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
359162306a36Sopenharmony_ci	int ok_count;
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ciretry:
359462306a36Sopenharmony_ci	if (take_rtnl)
359562306a36Sopenharmony_ci		rtnl_lock();
359662306a36Sopenharmony_ci	down_read(&block->cb_lock);
359762306a36Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
359862306a36Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
359962306a36Sopenharmony_ci	 * obtain the locks in same order here.
360062306a36Sopenharmony_ci	 */
360162306a36Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
360262306a36Sopenharmony_ci		up_read(&block->cb_lock);
360362306a36Sopenharmony_ci		take_rtnl = true;
360462306a36Sopenharmony_ci		goto retry;
360562306a36Sopenharmony_ci	}
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	/* Make sure all netdevs sharing this block are offload-capable. */
360862306a36Sopenharmony_ci	if (block->nooffloaddevcnt && err_stop) {
360962306a36Sopenharmony_ci		ok_count = -EOPNOTSUPP;
361062306a36Sopenharmony_ci		goto err_unlock;
361162306a36Sopenharmony_ci	}
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci	tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags);
361462306a36Sopenharmony_ci	if (tp->ops->hw_del)
361562306a36Sopenharmony_ci		tp->ops->hw_del(tp, type_data);
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
361862306a36Sopenharmony_ci	if (ok_count < 0)
361962306a36Sopenharmony_ci		goto err_unlock;
362062306a36Sopenharmony_ci
362162306a36Sopenharmony_ci	if (tp->ops->hw_add)
362262306a36Sopenharmony_ci		tp->ops->hw_add(tp, type_data);
362362306a36Sopenharmony_ci	if (ok_count > 0)
362462306a36Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, new_in_hw_count,
362562306a36Sopenharmony_ci					  new_flags, ok_count, true);
362662306a36Sopenharmony_cierr_unlock:
362762306a36Sopenharmony_ci	up_read(&block->cb_lock);
362862306a36Sopenharmony_ci	if (take_rtnl)
362962306a36Sopenharmony_ci		rtnl_unlock();
363062306a36Sopenharmony_ci	return min(ok_count, 0);
363162306a36Sopenharmony_ci}
363262306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_replace);
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci/* Destroy filter and decrement block offload counter, if filter was previously
363562306a36Sopenharmony_ci * offloaded.
363662306a36Sopenharmony_ci */
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ciint tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp,
363962306a36Sopenharmony_ci			enum tc_setup_type type, void *type_data, bool err_stop,
364062306a36Sopenharmony_ci			u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
364162306a36Sopenharmony_ci{
364262306a36Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
364362306a36Sopenharmony_ci	int ok_count;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ciretry:
364662306a36Sopenharmony_ci	if (take_rtnl)
364762306a36Sopenharmony_ci		rtnl_lock();
364862306a36Sopenharmony_ci	down_read(&block->cb_lock);
364962306a36Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
365062306a36Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
365162306a36Sopenharmony_ci	 * obtain the locks in same order here.
365262306a36Sopenharmony_ci	 */
365362306a36Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
365462306a36Sopenharmony_ci		up_read(&block->cb_lock);
365562306a36Sopenharmony_ci		take_rtnl = true;
365662306a36Sopenharmony_ci		goto retry;
365762306a36Sopenharmony_ci	}
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags);
366262306a36Sopenharmony_ci	if (tp->ops->hw_del)
366362306a36Sopenharmony_ci		tp->ops->hw_del(tp, type_data);
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci	up_read(&block->cb_lock);
366662306a36Sopenharmony_ci	if (take_rtnl)
366762306a36Sopenharmony_ci		rtnl_unlock();
366862306a36Sopenharmony_ci	return min(ok_count, 0);
366962306a36Sopenharmony_ci}
367062306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_destroy);
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ciint tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp,
367362306a36Sopenharmony_ci			  bool add, flow_setup_cb_t *cb,
367462306a36Sopenharmony_ci			  enum tc_setup_type type, void *type_data,
367562306a36Sopenharmony_ci			  void *cb_priv, u32 *flags, unsigned int *in_hw_count)
367662306a36Sopenharmony_ci{
367762306a36Sopenharmony_ci	int err = cb(type, type_data, cb_priv);
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci	if (err) {
368062306a36Sopenharmony_ci		if (add && tc_skip_sw(*flags))
368162306a36Sopenharmony_ci			return err;
368262306a36Sopenharmony_ci	} else {
368362306a36Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1,
368462306a36Sopenharmony_ci					  add);
368562306a36Sopenharmony_ci	}
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	return 0;
368862306a36Sopenharmony_ci}
368962306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_reoffload);
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_cistatic int tcf_act_get_user_cookie(struct flow_action_entry *entry,
369262306a36Sopenharmony_ci				   const struct tc_action *act)
369362306a36Sopenharmony_ci{
369462306a36Sopenharmony_ci	struct tc_cookie *user_cookie;
369562306a36Sopenharmony_ci	int err = 0;
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci	rcu_read_lock();
369862306a36Sopenharmony_ci	user_cookie = rcu_dereference(act->user_cookie);
369962306a36Sopenharmony_ci	if (user_cookie) {
370062306a36Sopenharmony_ci		entry->user_cookie = flow_action_cookie_create(user_cookie->data,
370162306a36Sopenharmony_ci							       user_cookie->len,
370262306a36Sopenharmony_ci							       GFP_ATOMIC);
370362306a36Sopenharmony_ci		if (!entry->user_cookie)
370462306a36Sopenharmony_ci			err = -ENOMEM;
370562306a36Sopenharmony_ci	}
370662306a36Sopenharmony_ci	rcu_read_unlock();
370762306a36Sopenharmony_ci	return err;
370862306a36Sopenharmony_ci}
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_cistatic void tcf_act_put_user_cookie(struct flow_action_entry *entry)
371162306a36Sopenharmony_ci{
371262306a36Sopenharmony_ci	flow_action_cookie_destroy(entry->user_cookie);
371362306a36Sopenharmony_ci}
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_civoid tc_cleanup_offload_action(struct flow_action *flow_action)
371662306a36Sopenharmony_ci{
371762306a36Sopenharmony_ci	struct flow_action_entry *entry;
371862306a36Sopenharmony_ci	int i;
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci	flow_action_for_each(i, entry, flow_action) {
372162306a36Sopenharmony_ci		tcf_act_put_user_cookie(entry);
372262306a36Sopenharmony_ci		if (entry->destructor)
372362306a36Sopenharmony_ci			entry->destructor(entry->destructor_priv);
372462306a36Sopenharmony_ci	}
372562306a36Sopenharmony_ci}
372662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_cleanup_offload_action);
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_cistatic int tc_setup_offload_act(struct tc_action *act,
372962306a36Sopenharmony_ci				struct flow_action_entry *entry,
373062306a36Sopenharmony_ci				u32 *index_inc,
373162306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
373262306a36Sopenharmony_ci{
373362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
373462306a36Sopenharmony_ci	if (act->ops->offload_act_setup) {
373562306a36Sopenharmony_ci		return act->ops->offload_act_setup(act, entry, index_inc, true,
373662306a36Sopenharmony_ci						   extack);
373762306a36Sopenharmony_ci	} else {
373862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Action does not support offload");
373962306a36Sopenharmony_ci		return -EOPNOTSUPP;
374062306a36Sopenharmony_ci	}
374162306a36Sopenharmony_ci#else
374262306a36Sopenharmony_ci	return 0;
374362306a36Sopenharmony_ci#endif
374462306a36Sopenharmony_ci}
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ciint tc_setup_action(struct flow_action *flow_action,
374762306a36Sopenharmony_ci		    struct tc_action *actions[],
374862306a36Sopenharmony_ci		    u32 miss_cookie_base,
374962306a36Sopenharmony_ci		    struct netlink_ext_ack *extack)
375062306a36Sopenharmony_ci{
375162306a36Sopenharmony_ci	int i, j, k, index, err = 0;
375262306a36Sopenharmony_ci	struct tc_action *act;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_ANY != FLOW_ACTION_HW_STATS_ANY);
375562306a36Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE);
375662306a36Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED);
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_ci	if (!actions)
375962306a36Sopenharmony_ci		return 0;
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	j = 0;
376262306a36Sopenharmony_ci	tcf_act_for_each_action(i, act, actions) {
376362306a36Sopenharmony_ci		struct flow_action_entry *entry;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci		entry = &flow_action->entries[j];
376662306a36Sopenharmony_ci		spin_lock_bh(&act->tcfa_lock);
376762306a36Sopenharmony_ci		err = tcf_act_get_user_cookie(entry, act);
376862306a36Sopenharmony_ci		if (err)
376962306a36Sopenharmony_ci			goto err_out_locked;
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_ci		index = 0;
377262306a36Sopenharmony_ci		err = tc_setup_offload_act(act, entry, &index, extack);
377362306a36Sopenharmony_ci		if (err)
377462306a36Sopenharmony_ci			goto err_out_locked;
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci		for (k = 0; k < index ; k++) {
377762306a36Sopenharmony_ci			entry[k].hw_stats = tc_act_hw_stats(act->hw_stats);
377862306a36Sopenharmony_ci			entry[k].hw_index = act->tcfa_index;
377962306a36Sopenharmony_ci			entry[k].cookie = (unsigned long)act;
378062306a36Sopenharmony_ci			entry[k].miss_cookie =
378162306a36Sopenharmony_ci				tcf_exts_miss_cookie_get(miss_cookie_base, i);
378262306a36Sopenharmony_ci		}
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci		j += index;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci		spin_unlock_bh(&act->tcfa_lock);
378762306a36Sopenharmony_ci	}
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_cierr_out:
379062306a36Sopenharmony_ci	if (err)
379162306a36Sopenharmony_ci		tc_cleanup_offload_action(flow_action);
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci	return err;
379462306a36Sopenharmony_cierr_out_locked:
379562306a36Sopenharmony_ci	spin_unlock_bh(&act->tcfa_lock);
379662306a36Sopenharmony_ci	goto err_out;
379762306a36Sopenharmony_ci}
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ciint tc_setup_offload_action(struct flow_action *flow_action,
380062306a36Sopenharmony_ci			    const struct tcf_exts *exts,
380162306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
380262306a36Sopenharmony_ci{
380362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
380462306a36Sopenharmony_ci	u32 miss_cookie_base;
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	if (!exts)
380762306a36Sopenharmony_ci		return 0;
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	miss_cookie_base = exts->miss_cookie_node ?
381062306a36Sopenharmony_ci			   exts->miss_cookie_node->miss_cookie_base : 0;
381162306a36Sopenharmony_ci	return tc_setup_action(flow_action, exts->actions, miss_cookie_base,
381262306a36Sopenharmony_ci			       extack);
381362306a36Sopenharmony_ci#else
381462306a36Sopenharmony_ci	return 0;
381562306a36Sopenharmony_ci#endif
381662306a36Sopenharmony_ci}
381762306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_offload_action);
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ciunsigned int tcf_exts_num_actions(struct tcf_exts *exts)
382062306a36Sopenharmony_ci{
382162306a36Sopenharmony_ci	unsigned int num_acts = 0;
382262306a36Sopenharmony_ci	struct tc_action *act;
382362306a36Sopenharmony_ci	int i;
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	tcf_exts_for_each_action(i, act, exts) {
382662306a36Sopenharmony_ci		if (is_tcf_pedit(act))
382762306a36Sopenharmony_ci			num_acts += tcf_pedit_nkeys(act);
382862306a36Sopenharmony_ci		else
382962306a36Sopenharmony_ci			num_acts++;
383062306a36Sopenharmony_ci	}
383162306a36Sopenharmony_ci	return num_acts;
383262306a36Sopenharmony_ci}
383362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_num_actions);
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
383662306a36Sopenharmony_cistatic int tcf_qevent_parse_block_index(struct nlattr *block_index_attr,
383762306a36Sopenharmony_ci					u32 *p_block_index,
383862306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
383962306a36Sopenharmony_ci{
384062306a36Sopenharmony_ci	*p_block_index = nla_get_u32(block_index_attr);
384162306a36Sopenharmony_ci	if (!*p_block_index) {
384262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Block number may not be zero");
384362306a36Sopenharmony_ci		return -EINVAL;
384462306a36Sopenharmony_ci	}
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci	return 0;
384762306a36Sopenharmony_ci}
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ciint tcf_qevent_init(struct tcf_qevent *qe, struct Qdisc *sch,
385062306a36Sopenharmony_ci		    enum flow_block_binder_type binder_type,
385162306a36Sopenharmony_ci		    struct nlattr *block_index_attr,
385262306a36Sopenharmony_ci		    struct netlink_ext_ack *extack)
385362306a36Sopenharmony_ci{
385462306a36Sopenharmony_ci	u32 block_index;
385562306a36Sopenharmony_ci	int err;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	if (!block_index_attr)
385862306a36Sopenharmony_ci		return 0;
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack);
386162306a36Sopenharmony_ci	if (err)
386262306a36Sopenharmony_ci		return err;
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	qe->info.binder_type = binder_type;
386562306a36Sopenharmony_ci	qe->info.chain_head_change = tcf_chain_head_change_dflt;
386662306a36Sopenharmony_ci	qe->info.chain_head_change_priv = &qe->filter_chain;
386762306a36Sopenharmony_ci	qe->info.block_index = block_index;
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	return tcf_block_get_ext(&qe->block, sch, &qe->info, extack);
387062306a36Sopenharmony_ci}
387162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_init);
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_civoid tcf_qevent_destroy(struct tcf_qevent *qe, struct Qdisc *sch)
387462306a36Sopenharmony_ci{
387562306a36Sopenharmony_ci	if (qe->info.block_index)
387662306a36Sopenharmony_ci		tcf_block_put_ext(qe->block, sch, &qe->info);
387762306a36Sopenharmony_ci}
387862306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_destroy);
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ciint tcf_qevent_validate_change(struct tcf_qevent *qe, struct nlattr *block_index_attr,
388162306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
388262306a36Sopenharmony_ci{
388362306a36Sopenharmony_ci	u32 block_index;
388462306a36Sopenharmony_ci	int err;
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	if (!block_index_attr)
388762306a36Sopenharmony_ci		return 0;
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack);
389062306a36Sopenharmony_ci	if (err)
389162306a36Sopenharmony_ci		return err;
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci	/* Bounce newly-configured block or change in block. */
389462306a36Sopenharmony_ci	if (block_index != qe->info.block_index) {
389562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Change of blocks is not supported");
389662306a36Sopenharmony_ci		return -EINVAL;
389762306a36Sopenharmony_ci	}
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	return 0;
390062306a36Sopenharmony_ci}
390162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_validate_change);
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_cistruct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, struct sk_buff *skb,
390462306a36Sopenharmony_ci				  struct sk_buff **to_free, int *ret)
390562306a36Sopenharmony_ci{
390662306a36Sopenharmony_ci	struct tcf_result cl_res;
390762306a36Sopenharmony_ci	struct tcf_proto *fl;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	if (!qe->info.block_index)
391062306a36Sopenharmony_ci		return skb;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	fl = rcu_dereference_bh(qe->filter_chain);
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci	switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
391562306a36Sopenharmony_ci	case TC_ACT_SHOT:
391662306a36Sopenharmony_ci		qdisc_qstats_drop(sch);
391762306a36Sopenharmony_ci		__qdisc_drop(skb, to_free);
391862306a36Sopenharmony_ci		*ret = __NET_XMIT_BYPASS;
391962306a36Sopenharmony_ci		return NULL;
392062306a36Sopenharmony_ci	case TC_ACT_STOLEN:
392162306a36Sopenharmony_ci	case TC_ACT_QUEUED:
392262306a36Sopenharmony_ci	case TC_ACT_TRAP:
392362306a36Sopenharmony_ci		__qdisc_drop(skb, to_free);
392462306a36Sopenharmony_ci		*ret = __NET_XMIT_STOLEN;
392562306a36Sopenharmony_ci		return NULL;
392662306a36Sopenharmony_ci	case TC_ACT_REDIRECT:
392762306a36Sopenharmony_ci		skb_do_redirect(skb);
392862306a36Sopenharmony_ci		*ret = __NET_XMIT_STOLEN;
392962306a36Sopenharmony_ci		return NULL;
393062306a36Sopenharmony_ci	}
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci	return skb;
393362306a36Sopenharmony_ci}
393462306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_handle);
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ciint tcf_qevent_dump(struct sk_buff *skb, int attr_name, struct tcf_qevent *qe)
393762306a36Sopenharmony_ci{
393862306a36Sopenharmony_ci	if (!qe->info.block_index)
393962306a36Sopenharmony_ci		return 0;
394062306a36Sopenharmony_ci	return nla_put_u32(skb, attr_name, qe->info.block_index);
394162306a36Sopenharmony_ci}
394262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_dump);
394362306a36Sopenharmony_ci#endif
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_cistatic __net_init int tcf_net_init(struct net *net)
394662306a36Sopenharmony_ci{
394762306a36Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	spin_lock_init(&tn->idr_lock);
395062306a36Sopenharmony_ci	idr_init(&tn->idr);
395162306a36Sopenharmony_ci	return 0;
395262306a36Sopenharmony_ci}
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_cistatic void __net_exit tcf_net_exit(struct net *net)
395562306a36Sopenharmony_ci{
395662306a36Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_ci	idr_destroy(&tn->idr);
395962306a36Sopenharmony_ci}
396062306a36Sopenharmony_ci
396162306a36Sopenharmony_cistatic struct pernet_operations tcf_net_ops = {
396262306a36Sopenharmony_ci	.init = tcf_net_init,
396362306a36Sopenharmony_ci	.exit = tcf_net_exit,
396462306a36Sopenharmony_ci	.id   = &tcf_net_id,
396562306a36Sopenharmony_ci	.size = sizeof(struct tcf_net),
396662306a36Sopenharmony_ci};
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_cistatic int __init tc_filter_init(void)
396962306a36Sopenharmony_ci{
397062306a36Sopenharmony_ci	int err;
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0);
397362306a36Sopenharmony_ci	if (!tc_filter_wq)
397462306a36Sopenharmony_ci		return -ENOMEM;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	err = register_pernet_subsys(&tcf_net_ops);
397762306a36Sopenharmony_ci	if (err)
397862306a36Sopenharmony_ci		goto err_register_pernet_subsys;
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	xa_init_flags(&tcf_exts_miss_cookies_xa, XA_FLAGS_ALLOC1);
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL,
398362306a36Sopenharmony_ci		      RTNL_FLAG_DOIT_UNLOCKED);
398462306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL,
398562306a36Sopenharmony_ci		      RTNL_FLAG_DOIT_UNLOCKED);
398662306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
398762306a36Sopenharmony_ci		      tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
398862306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
398962306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
399062306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
399162306a36Sopenharmony_ci		      tc_dump_chain, 0);
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	return 0;
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_cierr_register_pernet_subsys:
399662306a36Sopenharmony_ci	destroy_workqueue(tc_filter_wq);
399762306a36Sopenharmony_ci	return err;
399862306a36Sopenharmony_ci}
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_cisubsys_initcall(tc_filter_init);
4001