18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/cls_api.c	Packet classifier API.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Changes:
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/err.h>
188c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/kmod.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/idr.h>
238c2ecf20Sopenharmony_ci#include <linux/jhash.h>
248c2ecf20Sopenharmony_ci#include <linux/rculist.h>
258c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
268c2ecf20Sopenharmony_ci#include <net/sock.h>
278c2ecf20Sopenharmony_ci#include <net/netlink.h>
288c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
298c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
308c2ecf20Sopenharmony_ci#include <net/tc_act/tc_pedit.h>
318c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mirred.h>
328c2ecf20Sopenharmony_ci#include <net/tc_act/tc_vlan.h>
338c2ecf20Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h>
348c2ecf20Sopenharmony_ci#include <net/tc_act/tc_csum.h>
358c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gact.h>
368c2ecf20Sopenharmony_ci#include <net/tc_act/tc_police.h>
378c2ecf20Sopenharmony_ci#include <net/tc_act/tc_sample.h>
388c2ecf20Sopenharmony_ci#include <net/tc_act/tc_skbedit.h>
398c2ecf20Sopenharmony_ci#include <net/tc_act/tc_ct.h>
408c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mpls.h>
418c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gate.h>
428c2ecf20Sopenharmony_ci#include <net/flow_offload.h>
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* The list of all installed classifier types */
458c2ecf20Sopenharmony_cistatic LIST_HEAD(tcf_proto_base);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Protects list of registered TC modules. It is pure SMP lock. */
488c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(cls_mod_lock);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic u32 destroy_obj_hashfn(const struct tcf_proto *tp)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return jhash_3words(tp->chain->index, tp->prio,
538c2ecf20Sopenharmony_ci			    (__force __u32)tp->protocol, 0);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void tcf_proto_signal_destroying(struct tcf_chain *chain,
578c2ecf20Sopenharmony_ci					struct tcf_proto *tp)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	mutex_lock(&block->proto_destroy_lock);
628c2ecf20Sopenharmony_ci	hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node,
638c2ecf20Sopenharmony_ci		     destroy_obj_hashfn(tp));
648c2ecf20Sopenharmony_ci	mutex_unlock(&block->proto_destroy_lock);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic bool tcf_proto_cmp(const struct tcf_proto *tp1,
688c2ecf20Sopenharmony_ci			  const struct tcf_proto *tp2)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	return tp1->chain->index == tp2->chain->index &&
718c2ecf20Sopenharmony_ci	       tp1->prio == tp2->prio &&
728c2ecf20Sopenharmony_ci	       tp1->protocol == tp2->protocol;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic bool tcf_proto_exists_destroying(struct tcf_chain *chain,
768c2ecf20Sopenharmony_ci					struct tcf_proto *tp)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	u32 hash = destroy_obj_hashfn(tp);
798c2ecf20Sopenharmony_ci	struct tcf_proto *iter;
808c2ecf20Sopenharmony_ci	bool found = false;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	rcu_read_lock();
838c2ecf20Sopenharmony_ci	hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter,
848c2ecf20Sopenharmony_ci				   destroy_ht_node, hash) {
858c2ecf20Sopenharmony_ci		if (tcf_proto_cmp(tp, iter)) {
868c2ecf20Sopenharmony_ci			found = true;
878c2ecf20Sopenharmony_ci			break;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	rcu_read_unlock();
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return found;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void
968c2ecf20Sopenharmony_citcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	mutex_lock(&block->proto_destroy_lock);
1018c2ecf20Sopenharmony_ci	if (hash_hashed(&tp->destroy_ht_node))
1028c2ecf20Sopenharmony_ci		hash_del_rcu(&tp->destroy_ht_node);
1038c2ecf20Sopenharmony_ci	mutex_unlock(&block->proto_destroy_lock);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Find classifier type by string name */
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *t, *res = NULL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (kind) {
1138c2ecf20Sopenharmony_ci		read_lock(&cls_mod_lock);
1148c2ecf20Sopenharmony_ci		list_for_each_entry(t, &tcf_proto_base, head) {
1158c2ecf20Sopenharmony_ci			if (strcmp(kind, t->kind) == 0) {
1168c2ecf20Sopenharmony_ci				if (try_module_get(t->owner))
1178c2ecf20Sopenharmony_ci					res = t;
1188c2ecf20Sopenharmony_ci				break;
1198c2ecf20Sopenharmony_ci			}
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci		read_unlock(&cls_mod_lock);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	return res;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic const struct tcf_proto_ops *
1278c2ecf20Sopenharmony_citcf_proto_lookup_ops(const char *kind, bool rtnl_held,
1288c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *ops;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ops = __tcf_proto_lookup_ops(kind);
1338c2ecf20Sopenharmony_ci	if (ops)
1348c2ecf20Sopenharmony_ci		return ops;
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
1368c2ecf20Sopenharmony_ci	if (rtnl_held)
1378c2ecf20Sopenharmony_ci		rtnl_unlock();
1388c2ecf20Sopenharmony_ci	request_module("cls_%s", kind);
1398c2ecf20Sopenharmony_ci	if (rtnl_held)
1408c2ecf20Sopenharmony_ci		rtnl_lock();
1418c2ecf20Sopenharmony_ci	ops = __tcf_proto_lookup_ops(kind);
1428c2ecf20Sopenharmony_ci	/* We dropped the RTNL semaphore in order to perform
1438c2ecf20Sopenharmony_ci	 * the module load. So, even if we succeeded in loading
1448c2ecf20Sopenharmony_ci	 * the module we have to replay the request. We indicate
1458c2ecf20Sopenharmony_ci	 * this using -EAGAIN.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	if (ops) {
1488c2ecf20Sopenharmony_ci		module_put(ops->owner);
1498c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci#endif
1528c2ecf20Sopenharmony_ci	NL_SET_ERR_MSG(extack, "TC classifier not found");
1538c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOENT);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Register(unregister) new classifier type */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciint register_tcf_proto_ops(struct tcf_proto_ops *ops)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct tcf_proto_ops *t;
1618c2ecf20Sopenharmony_ci	int rc = -EEXIST;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	write_lock(&cls_mod_lock);
1648c2ecf20Sopenharmony_ci	list_for_each_entry(t, &tcf_proto_base, head)
1658c2ecf20Sopenharmony_ci		if (!strcmp(ops->kind, t->kind))
1668c2ecf20Sopenharmony_ci			goto out;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	list_add_tail(&ops->head, &tcf_proto_base);
1698c2ecf20Sopenharmony_ci	rc = 0;
1708c2ecf20Sopenharmony_ciout:
1718c2ecf20Sopenharmony_ci	write_unlock(&cls_mod_lock);
1728c2ecf20Sopenharmony_ci	return rc;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_tcf_proto_ops);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic struct workqueue_struct *tc_filter_wq;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciint unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct tcf_proto_ops *t;
1818c2ecf20Sopenharmony_ci	int rc = -ENOENT;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Wait for outstanding call_rcu()s, if any, from a
1848c2ecf20Sopenharmony_ci	 * tcf_proto_ops's destroy() handler.
1858c2ecf20Sopenharmony_ci	 */
1868c2ecf20Sopenharmony_ci	rcu_barrier();
1878c2ecf20Sopenharmony_ci	flush_workqueue(tc_filter_wq);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	write_lock(&cls_mod_lock);
1908c2ecf20Sopenharmony_ci	list_for_each_entry(t, &tcf_proto_base, head) {
1918c2ecf20Sopenharmony_ci		if (t == ops) {
1928c2ecf20Sopenharmony_ci			list_del(&t->head);
1938c2ecf20Sopenharmony_ci			rc = 0;
1948c2ecf20Sopenharmony_ci			break;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci	write_unlock(&cls_mod_lock);
1988c2ecf20Sopenharmony_ci	return rc;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_tcf_proto_ops);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cibool tcf_queue_work(struct rcu_work *rwork, work_func_t func)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	INIT_RCU_WORK(rwork, func);
2058c2ecf20Sopenharmony_ci	return queue_rcu_work(tc_filter_wq, rwork);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_queue_work);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/* Select new prio value from the range, managed by kernel. */
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic inline u32 tcf_auto_prio(struct tcf_proto *tp)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u32 first = TC_H_MAKE(0xC0000000U, 0U);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (tp)
2168c2ecf20Sopenharmony_ci		first = tp->prio - 1;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return TC_H_MAJ(first);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic bool tcf_proto_check_kind(struct nlattr *kind, char *name)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	if (kind)
2248c2ecf20Sopenharmony_ci		return nla_strlcpy(name, kind, IFNAMSIZ) >= IFNAMSIZ;
2258c2ecf20Sopenharmony_ci	memset(name, 0, IFNAMSIZ);
2268c2ecf20Sopenharmony_ci	return false;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic bool tcf_proto_is_unlocked(const char *kind)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *ops;
2328c2ecf20Sopenharmony_ci	bool ret;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (strlen(kind) == 0)
2358c2ecf20Sopenharmony_ci		return false;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ops = tcf_proto_lookup_ops(kind, false, NULL);
2388c2ecf20Sopenharmony_ci	/* On error return false to take rtnl lock. Proto lookup/create
2398c2ecf20Sopenharmony_ci	 * functions will perform lookup again and properly handle errors.
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci	if (IS_ERR(ops))
2428c2ecf20Sopenharmony_ci		return false;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED);
2458c2ecf20Sopenharmony_ci	module_put(ops->owner);
2468c2ecf20Sopenharmony_ci	return ret;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
2508c2ecf20Sopenharmony_ci					  u32 prio, struct tcf_chain *chain,
2518c2ecf20Sopenharmony_ci					  bool rtnl_held,
2528c2ecf20Sopenharmony_ci					  struct netlink_ext_ack *extack)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct tcf_proto *tp;
2558c2ecf20Sopenharmony_ci	int err;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	tp = kzalloc(sizeof(*tp), GFP_KERNEL);
2588c2ecf20Sopenharmony_ci	if (!tp)
2598c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOBUFS);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack);
2628c2ecf20Sopenharmony_ci	if (IS_ERR(tp->ops)) {
2638c2ecf20Sopenharmony_ci		err = PTR_ERR(tp->ops);
2648c2ecf20Sopenharmony_ci		goto errout;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	tp->classify = tp->ops->classify;
2678c2ecf20Sopenharmony_ci	tp->protocol = protocol;
2688c2ecf20Sopenharmony_ci	tp->prio = prio;
2698c2ecf20Sopenharmony_ci	tp->chain = chain;
2708c2ecf20Sopenharmony_ci	spin_lock_init(&tp->lock);
2718c2ecf20Sopenharmony_ci	refcount_set(&tp->refcnt, 1);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	err = tp->ops->init(tp);
2748c2ecf20Sopenharmony_ci	if (err) {
2758c2ecf20Sopenharmony_ci		module_put(tp->ops->owner);
2768c2ecf20Sopenharmony_ci		goto errout;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	return tp;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cierrout:
2818c2ecf20Sopenharmony_ci	kfree(tp);
2828c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void tcf_proto_get(struct tcf_proto *tp)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	refcount_inc(&tp->refcnt);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held,
2938c2ecf20Sopenharmony_ci			      bool sig_destroy, struct netlink_ext_ack *extack)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	tp->ops->destroy(tp, rtnl_held, extack);
2968c2ecf20Sopenharmony_ci	if (sig_destroy)
2978c2ecf20Sopenharmony_ci		tcf_proto_signal_destroyed(tp->chain, tp);
2988c2ecf20Sopenharmony_ci	tcf_chain_put(tp->chain);
2998c2ecf20Sopenharmony_ci	module_put(tp->ops->owner);
3008c2ecf20Sopenharmony_ci	kfree_rcu(tp, rcu);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held,
3048c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&tp->refcnt))
3078c2ecf20Sopenharmony_ci		tcf_proto_destroy(tp, rtnl_held, true, extack);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic bool tcf_proto_check_delete(struct tcf_proto *tp)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	if (tp->ops->delete_empty)
3138c2ecf20Sopenharmony_ci		return tp->ops->delete_empty(tp);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	tp->deleting = true;
3168c2ecf20Sopenharmony_ci	return tp->deleting;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void tcf_proto_mark_delete(struct tcf_proto *tp)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	spin_lock(&tp->lock);
3228c2ecf20Sopenharmony_ci	tp->deleting = true;
3238c2ecf20Sopenharmony_ci	spin_unlock(&tp->lock);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic bool tcf_proto_is_deleting(struct tcf_proto *tp)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	bool deleting;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	spin_lock(&tp->lock);
3318c2ecf20Sopenharmony_ci	deleting = tp->deleting;
3328c2ecf20Sopenharmony_ci	spin_unlock(&tp->lock);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return deleting;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci#define ASSERT_BLOCK_LOCKED(block)					\
3388c2ecf20Sopenharmony_ci	lockdep_assert_held(&(block)->lock)
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistruct tcf_filter_chain_list_item {
3418c2ecf20Sopenharmony_ci	struct list_head list;
3428c2ecf20Sopenharmony_ci	tcf_chain_head_change_t *chain_head_change;
3438c2ecf20Sopenharmony_ci	void *chain_head_change_priv;
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic struct tcf_chain *tcf_chain_create(struct tcf_block *block,
3478c2ecf20Sopenharmony_ci					  u32 chain_index)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	chain = kzalloc(sizeof(*chain), GFP_KERNEL);
3548c2ecf20Sopenharmony_ci	if (!chain)
3558c2ecf20Sopenharmony_ci		return NULL;
3568c2ecf20Sopenharmony_ci	list_add_tail_rcu(&chain->list, &block->chain_list);
3578c2ecf20Sopenharmony_ci	mutex_init(&chain->filter_chain_lock);
3588c2ecf20Sopenharmony_ci	chain->block = block;
3598c2ecf20Sopenharmony_ci	chain->index = chain_index;
3608c2ecf20Sopenharmony_ci	chain->refcnt = 1;
3618c2ecf20Sopenharmony_ci	if (!chain->index)
3628c2ecf20Sopenharmony_ci		block->chain0.chain = chain;
3638c2ecf20Sopenharmony_ci	return chain;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item,
3678c2ecf20Sopenharmony_ci				       struct tcf_proto *tp_head)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	if (item->chain_head_change)
3708c2ecf20Sopenharmony_ci		item->chain_head_change(tp_head, item->chain_head_change_priv);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void tcf_chain0_head_change(struct tcf_chain *chain,
3748c2ecf20Sopenharmony_ci				   struct tcf_proto *tp_head)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
3778c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (chain->index)
3808c2ecf20Sopenharmony_ci		return;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
3838c2ecf20Sopenharmony_ci	list_for_each_entry(item, &block->chain0.filter_chain_list, list)
3848c2ecf20Sopenharmony_ci		tcf_chain_head_change_item(item, tp_head);
3858c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci/* Returns true if block can be safely freed. */
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic bool tcf_chain_detach(struct tcf_chain *chain)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	list_del_rcu(&chain->list);
3978c2ecf20Sopenharmony_ci	if (!chain->index)
3988c2ecf20Sopenharmony_ci		block->chain0.chain = NULL;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (list_empty(&block->chain_list) &&
4018c2ecf20Sopenharmony_ci	    refcount_read(&block->refcnt) == 0)
4028c2ecf20Sopenharmony_ci		return true;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return false;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void tcf_block_destroy(struct tcf_block *block)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	mutex_destroy(&block->lock);
4108c2ecf20Sopenharmony_ci	mutex_destroy(&block->proto_destroy_lock);
4118c2ecf20Sopenharmony_ci	kfree_rcu(block, rcu);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	mutex_destroy(&chain->filter_chain_lock);
4198c2ecf20Sopenharmony_ci	kfree_rcu(chain, rcu);
4208c2ecf20Sopenharmony_ci	if (free_block)
4218c2ecf20Sopenharmony_ci		tcf_block_destroy(block);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void tcf_chain_hold(struct tcf_chain *chain)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	ASSERT_BLOCK_LOCKED(chain->block);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	++chain->refcnt;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic bool tcf_chain_held_by_acts_only(struct tcf_chain *chain)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	ASSERT_BLOCK_LOCKED(chain->block);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* In case all the references are action references, this
4368c2ecf20Sopenharmony_ci	 * chain should not be shown to the user.
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	return chain->refcnt == chain->action_refcnt;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
4428c2ecf20Sopenharmony_ci					  u32 chain_index)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	ASSERT_BLOCK_LOCKED(block);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	list_for_each_entry(chain, &block->chain_list, list) {
4498c2ecf20Sopenharmony_ci		if (chain->index == chain_index)
4508c2ecf20Sopenharmony_ci			return chain;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	return NULL;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
4568c2ecf20Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block,
4578c2ecf20Sopenharmony_ci					      u32 chain_index)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(chain, &block->chain_list, list) {
4628c2ecf20Sopenharmony_ci		if (chain->index == chain_index)
4638c2ecf20Sopenharmony_ci			return chain;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	return NULL;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci#endif
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
4708c2ecf20Sopenharmony_ci			   u32 seq, u16 flags, int event, bool unicast);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
4738c2ecf20Sopenharmony_ci					 u32 chain_index, bool create,
4748c2ecf20Sopenharmony_ci					 bool by_act)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct tcf_chain *chain = NULL;
4778c2ecf20Sopenharmony_ci	bool is_first_reference;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
4808c2ecf20Sopenharmony_ci	chain = tcf_chain_lookup(block, chain_index);
4818c2ecf20Sopenharmony_ci	if (chain) {
4828c2ecf20Sopenharmony_ci		tcf_chain_hold(chain);
4838c2ecf20Sopenharmony_ci	} else {
4848c2ecf20Sopenharmony_ci		if (!create)
4858c2ecf20Sopenharmony_ci			goto errout;
4868c2ecf20Sopenharmony_ci		chain = tcf_chain_create(block, chain_index);
4878c2ecf20Sopenharmony_ci		if (!chain)
4888c2ecf20Sopenharmony_ci			goto errout;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (by_act)
4928c2ecf20Sopenharmony_ci		++chain->action_refcnt;
4938c2ecf20Sopenharmony_ci	is_first_reference = chain->refcnt - chain->action_refcnt == 1;
4948c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Send notification only in case we got the first
4978c2ecf20Sopenharmony_ci	 * non-action reference. Until then, the chain acts only as
4988c2ecf20Sopenharmony_ci	 * a placeholder for actions pointing to it and user ought
4998c2ecf20Sopenharmony_ci	 * not know about them.
5008c2ecf20Sopenharmony_ci	 */
5018c2ecf20Sopenharmony_ci	if (is_first_reference && !by_act)
5028c2ecf20Sopenharmony_ci		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
5038c2ecf20Sopenharmony_ci				RTM_NEWCHAIN, false);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return chain;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cierrout:
5088c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
5098c2ecf20Sopenharmony_ci	return chain;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
5138c2ecf20Sopenharmony_ci				       bool create)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	return __tcf_chain_get(block, chain_index, create, false);
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistruct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	return __tcf_chain_get(block, chain_index, true, true);
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_get_by_act);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
5258c2ecf20Sopenharmony_ci			       void *tmplt_priv);
5268c2ecf20Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
5278c2ecf20Sopenharmony_ci				  void *tmplt_priv, u32 chain_index,
5288c2ecf20Sopenharmony_ci				  struct tcf_block *block, struct sk_buff *oskb,
5298c2ecf20Sopenharmony_ci				  u32 seq, u16 flags, bool unicast);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
5328c2ecf20Sopenharmony_ci			    bool explicitly_created)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
5358c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *tmplt_ops;
5368c2ecf20Sopenharmony_ci	unsigned int refcnt, non_act_refcnt;
5378c2ecf20Sopenharmony_ci	bool free_block = false;
5388c2ecf20Sopenharmony_ci	void *tmplt_priv;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
5418c2ecf20Sopenharmony_ci	if (explicitly_created) {
5428c2ecf20Sopenharmony_ci		if (!chain->explicitly_created) {
5438c2ecf20Sopenharmony_ci			mutex_unlock(&block->lock);
5448c2ecf20Sopenharmony_ci			return;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci		chain->explicitly_created = false;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (by_act)
5508c2ecf20Sopenharmony_ci		chain->action_refcnt--;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* tc_chain_notify_delete can't be called while holding block lock.
5538c2ecf20Sopenharmony_ci	 * However, when block is unlocked chain can be changed concurrently, so
5548c2ecf20Sopenharmony_ci	 * save these to temporary variables.
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	refcnt = --chain->refcnt;
5578c2ecf20Sopenharmony_ci	non_act_refcnt = refcnt - chain->action_refcnt;
5588c2ecf20Sopenharmony_ci	tmplt_ops = chain->tmplt_ops;
5598c2ecf20Sopenharmony_ci	tmplt_priv = chain->tmplt_priv;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (non_act_refcnt == chain->explicitly_created && !by_act) {
5628c2ecf20Sopenharmony_ci		if (non_act_refcnt == 0)
5638c2ecf20Sopenharmony_ci			tc_chain_notify_delete(tmplt_ops, tmplt_priv,
5648c2ecf20Sopenharmony_ci					       chain->index, block, NULL, 0, 0,
5658c2ecf20Sopenharmony_ci					       false);
5668c2ecf20Sopenharmony_ci		/* Last reference to chain, no need to lock. */
5678c2ecf20Sopenharmony_ci		chain->flushing = false;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (refcnt == 0)
5718c2ecf20Sopenharmony_ci		free_block = tcf_chain_detach(chain);
5728c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (refcnt == 0) {
5758c2ecf20Sopenharmony_ci		tc_chain_tmplt_del(tmplt_ops, tmplt_priv);
5768c2ecf20Sopenharmony_ci		tcf_chain_destroy(chain, free_block);
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	__tcf_chain_put(chain, false, false);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_civoid tcf_chain_put_by_act(struct tcf_chain *chain)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	__tcf_chain_put(chain, true, false);
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_put_by_act);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	__tcf_chain_put(chain, false, true);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct tcf_proto *tp, *tp_next;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
6018c2ecf20Sopenharmony_ci	tp = tcf_chain_dereference(chain->filter_chain, chain);
6028c2ecf20Sopenharmony_ci	while (tp) {
6038c2ecf20Sopenharmony_ci		tp_next = rcu_dereference_protected(tp->next, 1);
6048c2ecf20Sopenharmony_ci		tcf_proto_signal_destroying(chain, tp);
6058c2ecf20Sopenharmony_ci		tp = tp_next;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	tp = tcf_chain_dereference(chain->filter_chain, chain);
6088c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(chain->filter_chain, NULL);
6098c2ecf20Sopenharmony_ci	tcf_chain0_head_change(chain, NULL);
6108c2ecf20Sopenharmony_ci	chain->flushing = true;
6118c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	while (tp) {
6148c2ecf20Sopenharmony_ci		tp_next = rcu_dereference_protected(tp->next, 1);
6158c2ecf20Sopenharmony_ci		tcf_proto_put(tp, rtnl_held, NULL);
6168c2ecf20Sopenharmony_ci		tp = tp_next;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block,
6218c2ecf20Sopenharmony_ci			   struct flow_block_offload *bo);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic void tcf_block_offload_init(struct flow_block_offload *bo,
6248c2ecf20Sopenharmony_ci				   struct net_device *dev, struct Qdisc *sch,
6258c2ecf20Sopenharmony_ci				   enum flow_block_command command,
6268c2ecf20Sopenharmony_ci				   enum flow_block_binder_type binder_type,
6278c2ecf20Sopenharmony_ci				   struct flow_block *flow_block,
6288c2ecf20Sopenharmony_ci				   bool shared, struct netlink_ext_ack *extack)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	bo->net = dev_net(dev);
6318c2ecf20Sopenharmony_ci	bo->command = command;
6328c2ecf20Sopenharmony_ci	bo->binder_type = binder_type;
6338c2ecf20Sopenharmony_ci	bo->block = flow_block;
6348c2ecf20Sopenharmony_ci	bo->block_shared = shared;
6358c2ecf20Sopenharmony_ci	bo->extack = extack;
6368c2ecf20Sopenharmony_ci	bo->sch = sch;
6378c2ecf20Sopenharmony_ci	bo->cb_list_head = &flow_block->cb_list;
6388c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bo->cb_list);
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block,
6428c2ecf20Sopenharmony_ci			     struct flow_block_offload *bo);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic void tc_block_indr_cleanup(struct flow_block_cb *block_cb)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct tcf_block *block = block_cb->indr.data;
6478c2ecf20Sopenharmony_ci	struct net_device *dev = block_cb->indr.dev;
6488c2ecf20Sopenharmony_ci	struct Qdisc *sch = block_cb->indr.sch;
6498c2ecf20Sopenharmony_ci	struct netlink_ext_ack extack = {};
6508c2ecf20Sopenharmony_ci	struct flow_block_offload bo = {};
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	tcf_block_offload_init(&bo, dev, sch, FLOW_BLOCK_UNBIND,
6538c2ecf20Sopenharmony_ci			       block_cb->indr.binder_type,
6548c2ecf20Sopenharmony_ci			       &block->flow_block, tcf_block_shared(block),
6558c2ecf20Sopenharmony_ci			       &extack);
6568c2ecf20Sopenharmony_ci	rtnl_lock();
6578c2ecf20Sopenharmony_ci	down_write(&block->cb_lock);
6588c2ecf20Sopenharmony_ci	list_del(&block_cb->driver_list);
6598c2ecf20Sopenharmony_ci	list_move(&block_cb->list, &bo.cb_list);
6608c2ecf20Sopenharmony_ci	tcf_block_unbind(block, &bo);
6618c2ecf20Sopenharmony_ci	up_write(&block->cb_lock);
6628c2ecf20Sopenharmony_ci	rtnl_unlock();
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic bool tcf_block_offload_in_use(struct tcf_block *block)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	return atomic_read(&block->offloadcnt);
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int tcf_block_offload_cmd(struct tcf_block *block,
6718c2ecf20Sopenharmony_ci				 struct net_device *dev, struct Qdisc *sch,
6728c2ecf20Sopenharmony_ci				 struct tcf_block_ext_info *ei,
6738c2ecf20Sopenharmony_ci				 enum flow_block_command command,
6748c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct flow_block_offload bo = {};
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	tcf_block_offload_init(&bo, dev, sch, command, ei->binder_type,
6798c2ecf20Sopenharmony_ci			       &block->flow_block, tcf_block_shared(block),
6808c2ecf20Sopenharmony_ci			       extack);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (dev->netdev_ops->ndo_setup_tc) {
6838c2ecf20Sopenharmony_ci		int err;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
6868c2ecf20Sopenharmony_ci		if (err < 0) {
6878c2ecf20Sopenharmony_ci			if (err != -EOPNOTSUPP)
6888c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed");
6898c2ecf20Sopenharmony_ci			return err;
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		return tcf_block_setup(block, &bo);
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	flow_indr_dev_setup_offload(dev, sch, TC_SETUP_BLOCK, block, &bo,
6968c2ecf20Sopenharmony_ci				    tc_block_indr_cleanup);
6978c2ecf20Sopenharmony_ci	tcf_block_setup(block, &bo);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
7038c2ecf20Sopenharmony_ci				  struct tcf_block_ext_info *ei,
7048c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	struct net_device *dev = q->dev_queue->dev;
7078c2ecf20Sopenharmony_ci	int err;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	down_write(&block->cb_lock);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* If tc offload feature is disabled and the block we try to bind
7128c2ecf20Sopenharmony_ci	 * to already has some offloaded filters, forbid to bind.
7138c2ecf20Sopenharmony_ci	 */
7148c2ecf20Sopenharmony_ci	if (dev->netdev_ops->ndo_setup_tc &&
7158c2ecf20Sopenharmony_ci	    !tc_can_offload(dev) &&
7168c2ecf20Sopenharmony_ci	    tcf_block_offload_in_use(block)) {
7178c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled");
7188c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
7198c2ecf20Sopenharmony_ci		goto err_unlock;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_BIND, extack);
7238c2ecf20Sopenharmony_ci	if (err == -EOPNOTSUPP)
7248c2ecf20Sopenharmony_ci		goto no_offload_dev_inc;
7258c2ecf20Sopenharmony_ci	if (err)
7268c2ecf20Sopenharmony_ci		goto err_unlock;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	up_write(&block->cb_lock);
7298c2ecf20Sopenharmony_ci	return 0;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cino_offload_dev_inc:
7328c2ecf20Sopenharmony_ci	if (tcf_block_offload_in_use(block))
7338c2ecf20Sopenharmony_ci		goto err_unlock;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	err = 0;
7368c2ecf20Sopenharmony_ci	block->nooffloaddevcnt++;
7378c2ecf20Sopenharmony_cierr_unlock:
7388c2ecf20Sopenharmony_ci	up_write(&block->cb_lock);
7398c2ecf20Sopenharmony_ci	return err;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
7438c2ecf20Sopenharmony_ci				     struct tcf_block_ext_info *ei)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct net_device *dev = q->dev_queue->dev;
7468c2ecf20Sopenharmony_ci	int err;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	down_write(&block->cb_lock);
7498c2ecf20Sopenharmony_ci	err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_UNBIND, NULL);
7508c2ecf20Sopenharmony_ci	if (err == -EOPNOTSUPP)
7518c2ecf20Sopenharmony_ci		goto no_offload_dev_dec;
7528c2ecf20Sopenharmony_ci	up_write(&block->cb_lock);
7538c2ecf20Sopenharmony_ci	return;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cino_offload_dev_dec:
7568c2ecf20Sopenharmony_ci	WARN_ON(block->nooffloaddevcnt-- == 0);
7578c2ecf20Sopenharmony_ci	up_write(&block->cb_lock);
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int
7618c2ecf20Sopenharmony_citcf_chain0_head_change_cb_add(struct tcf_block *block,
7628c2ecf20Sopenharmony_ci			      struct tcf_block_ext_info *ei,
7638c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
7668c2ecf20Sopenharmony_ci	struct tcf_chain *chain0;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	item = kmalloc(sizeof(*item), GFP_KERNEL);
7698c2ecf20Sopenharmony_ci	if (!item) {
7708c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed");
7718c2ecf20Sopenharmony_ci		return -ENOMEM;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci	item->chain_head_change = ei->chain_head_change;
7748c2ecf20Sopenharmony_ci	item->chain_head_change_priv = ei->chain_head_change_priv;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
7778c2ecf20Sopenharmony_ci	chain0 = block->chain0.chain;
7788c2ecf20Sopenharmony_ci	if (chain0)
7798c2ecf20Sopenharmony_ci		tcf_chain_hold(chain0);
7808c2ecf20Sopenharmony_ci	else
7818c2ecf20Sopenharmony_ci		list_add(&item->list, &block->chain0.filter_chain_list);
7828c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (chain0) {
7858c2ecf20Sopenharmony_ci		struct tcf_proto *tp_head;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		mutex_lock(&chain0->filter_chain_lock);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		tp_head = tcf_chain_dereference(chain0->filter_chain, chain0);
7908c2ecf20Sopenharmony_ci		if (tp_head)
7918c2ecf20Sopenharmony_ci			tcf_chain_head_change_item(item, tp_head);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		mutex_lock(&block->lock);
7948c2ecf20Sopenharmony_ci		list_add(&item->list, &block->chain0.filter_chain_list);
7958c2ecf20Sopenharmony_ci		mutex_unlock(&block->lock);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		mutex_unlock(&chain0->filter_chain_lock);
7988c2ecf20Sopenharmony_ci		tcf_chain_put(chain0);
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return 0;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void
8058c2ecf20Sopenharmony_citcf_chain0_head_change_cb_del(struct tcf_block *block,
8068c2ecf20Sopenharmony_ci			      struct tcf_block_ext_info *ei)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct tcf_filter_chain_list_item *item;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
8118c2ecf20Sopenharmony_ci	list_for_each_entry(item, &block->chain0.filter_chain_list, list) {
8128c2ecf20Sopenharmony_ci		if ((!ei->chain_head_change && !ei->chain_head_change_priv) ||
8138c2ecf20Sopenharmony_ci		    (item->chain_head_change == ei->chain_head_change &&
8148c2ecf20Sopenharmony_ci		     item->chain_head_change_priv == ei->chain_head_change_priv)) {
8158c2ecf20Sopenharmony_ci			if (block->chain0.chain)
8168c2ecf20Sopenharmony_ci				tcf_chain_head_change_item(item, NULL);
8178c2ecf20Sopenharmony_ci			list_del(&item->list);
8188c2ecf20Sopenharmony_ci			mutex_unlock(&block->lock);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci			kfree(item);
8218c2ecf20Sopenharmony_ci			return;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
8258c2ecf20Sopenharmony_ci	WARN_ON(1);
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistruct tcf_net {
8298c2ecf20Sopenharmony_ci	spinlock_t idr_lock; /* Protects idr */
8308c2ecf20Sopenharmony_ci	struct idr idr;
8318c2ecf20Sopenharmony_ci};
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic unsigned int tcf_net_id;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic int tcf_block_insert(struct tcf_block *block, struct net *net,
8368c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
8398c2ecf20Sopenharmony_ci	int err;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	idr_preload(GFP_KERNEL);
8428c2ecf20Sopenharmony_ci	spin_lock(&tn->idr_lock);
8438c2ecf20Sopenharmony_ci	err = idr_alloc_u32(&tn->idr, block, &block->index, block->index,
8448c2ecf20Sopenharmony_ci			    GFP_NOWAIT);
8458c2ecf20Sopenharmony_ci	spin_unlock(&tn->idr_lock);
8468c2ecf20Sopenharmony_ci	idr_preload_end();
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return err;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic void tcf_block_remove(struct tcf_block *block, struct net *net)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	spin_lock(&tn->idr_lock);
8568c2ecf20Sopenharmony_ci	idr_remove(&tn->idr, block->index);
8578c2ecf20Sopenharmony_ci	spin_unlock(&tn->idr_lock);
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
8618c2ecf20Sopenharmony_ci					  u32 block_index,
8628c2ecf20Sopenharmony_ci					  struct netlink_ext_ack *extack)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct tcf_block *block;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	block = kzalloc(sizeof(*block), GFP_KERNEL);
8678c2ecf20Sopenharmony_ci	if (!block) {
8688c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Memory allocation for block failed");
8698c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci	mutex_init(&block->lock);
8728c2ecf20Sopenharmony_ci	mutex_init(&block->proto_destroy_lock);
8738c2ecf20Sopenharmony_ci	init_rwsem(&block->cb_lock);
8748c2ecf20Sopenharmony_ci	flow_block_init(&block->flow_block);
8758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&block->chain_list);
8768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&block->owner_list);
8778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&block->chain0.filter_chain_list);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	refcount_set(&block->refcnt, 1);
8808c2ecf20Sopenharmony_ci	block->net = net;
8818c2ecf20Sopenharmony_ci	block->index = block_index;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Don't store q pointer for blocks which are shared */
8848c2ecf20Sopenharmony_ci	if (!tcf_block_shared(block))
8858c2ecf20Sopenharmony_ci		block->q = q;
8868c2ecf20Sopenharmony_ci	return block;
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	return idr_find(&tn->idr, block_index);
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct tcf_block *block;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	rcu_read_lock();
9018c2ecf20Sopenharmony_ci	block = tcf_block_lookup(net, block_index);
9028c2ecf20Sopenharmony_ci	if (block && !refcount_inc_not_zero(&block->refcnt))
9038c2ecf20Sopenharmony_ci		block = NULL;
9048c2ecf20Sopenharmony_ci	rcu_read_unlock();
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	return block;
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_cistatic struct tcf_chain *
9108c2ecf20Sopenharmony_ci__tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
9138c2ecf20Sopenharmony_ci	if (chain)
9148c2ecf20Sopenharmony_ci		chain = list_is_last(&chain->list, &block->chain_list) ?
9158c2ecf20Sopenharmony_ci			NULL : list_next_entry(chain, list);
9168c2ecf20Sopenharmony_ci	else
9178c2ecf20Sopenharmony_ci		chain = list_first_entry_or_null(&block->chain_list,
9188c2ecf20Sopenharmony_ci						 struct tcf_chain, list);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/* skip all action-only chains */
9218c2ecf20Sopenharmony_ci	while (chain && tcf_chain_held_by_acts_only(chain))
9228c2ecf20Sopenharmony_ci		chain = list_is_last(&chain->list, &block->chain_list) ?
9238c2ecf20Sopenharmony_ci			NULL : list_next_entry(chain, list);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (chain)
9268c2ecf20Sopenharmony_ci		tcf_chain_hold(chain);
9278c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	return chain;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/* Function to be used by all clients that want to iterate over all chains on
9338c2ecf20Sopenharmony_ci * block. It properly obtains block->lock and takes reference to chain before
9348c2ecf20Sopenharmony_ci * returning it. Users of this function must be tolerant to concurrent chain
9358c2ecf20Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is
9368c2ecf20Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide
9378c2ecf20Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with
9388c2ecf20Sopenharmony_ci * data and sent to user-space.
9398c2ecf20Sopenharmony_ci */
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistruct tcf_chain *
9428c2ecf20Sopenharmony_citcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (chain)
9478c2ecf20Sopenharmony_ci		tcf_chain_put(chain);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	return chain_next;
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_chain);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic struct tcf_proto *
9548c2ecf20Sopenharmony_ci__tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	u32 prio = 0;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	ASSERT_RTNL();
9598c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (!tp) {
9628c2ecf20Sopenharmony_ci		tp = tcf_chain_dereference(chain->filter_chain, chain);
9638c2ecf20Sopenharmony_ci	} else if (tcf_proto_is_deleting(tp)) {
9648c2ecf20Sopenharmony_ci		/* 'deleting' flag is set and chain->filter_chain_lock was
9658c2ecf20Sopenharmony_ci		 * unlocked, which means next pointer could be invalid. Restart
9668c2ecf20Sopenharmony_ci		 * search.
9678c2ecf20Sopenharmony_ci		 */
9688c2ecf20Sopenharmony_ci		prio = tp->prio + 1;
9698c2ecf20Sopenharmony_ci		tp = tcf_chain_dereference(chain->filter_chain, chain);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		for (; tp; tp = tcf_chain_dereference(tp->next, chain))
9728c2ecf20Sopenharmony_ci			if (!tp->deleting && tp->prio >= prio)
9738c2ecf20Sopenharmony_ci				break;
9748c2ecf20Sopenharmony_ci	} else {
9758c2ecf20Sopenharmony_ci		tp = tcf_chain_dereference(tp->next, chain);
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (tp)
9798c2ecf20Sopenharmony_ci		tcf_proto_get(tp);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return tp;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci/* Function to be used by all clients that want to iterate over all tp's on
9878c2ecf20Sopenharmony_ci * chain. Users of this function must be tolerant to concurrent tp
9888c2ecf20Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is
9898c2ecf20Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide
9908c2ecf20Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with
9918c2ecf20Sopenharmony_ci * data and sent to user-space.
9928c2ecf20Sopenharmony_ci */
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistruct tcf_proto *
9958c2ecf20Sopenharmony_citcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp,
9968c2ecf20Sopenharmony_ci		   bool rtnl_held)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if (tp)
10018c2ecf20Sopenharmony_ci		tcf_proto_put(tp, rtnl_held, NULL);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	return tp_next;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_proto);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	/* Last reference to block. At this point chains cannot be added or
10128c2ecf20Sopenharmony_ci	 * removed concurrently.
10138c2ecf20Sopenharmony_ci	 */
10148c2ecf20Sopenharmony_ci	for (chain = tcf_get_next_chain(block, NULL);
10158c2ecf20Sopenharmony_ci	     chain;
10168c2ecf20Sopenharmony_ci	     chain = tcf_get_next_chain(block, chain)) {
10178c2ecf20Sopenharmony_ci		tcf_chain_put_explicitly_created(chain);
10188c2ecf20Sopenharmony_ci		tcf_chain_flush(chain, rtnl_held);
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci/* Lookup Qdisc and increments its reference counter.
10238c2ecf20Sopenharmony_ci * Set parent, if necessary.
10248c2ecf20Sopenharmony_ci */
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
10278c2ecf20Sopenharmony_ci			    u32 *parent, int ifindex, bool rtnl_held,
10288c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	const struct Qdisc_class_ops *cops;
10318c2ecf20Sopenharmony_ci	struct net_device *dev;
10328c2ecf20Sopenharmony_ci	int err = 0;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
10358c2ecf20Sopenharmony_ci		return 0;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	rcu_read_lock();
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* Find link */
10408c2ecf20Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
10418c2ecf20Sopenharmony_ci	if (!dev) {
10428c2ecf20Sopenharmony_ci		rcu_read_unlock();
10438c2ecf20Sopenharmony_ci		return -ENODEV;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	/* Find qdisc */
10478c2ecf20Sopenharmony_ci	if (!*parent) {
10488c2ecf20Sopenharmony_ci		*q = rcu_dereference(dev->qdisc);
10498c2ecf20Sopenharmony_ci		*parent = (*q)->handle;
10508c2ecf20Sopenharmony_ci	} else {
10518c2ecf20Sopenharmony_ci		*q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
10528c2ecf20Sopenharmony_ci		if (!*q) {
10538c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
10548c2ecf20Sopenharmony_ci			err = -EINVAL;
10558c2ecf20Sopenharmony_ci			goto errout_rcu;
10568c2ecf20Sopenharmony_ci		}
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	*q = qdisc_refcount_inc_nz(*q);
10608c2ecf20Sopenharmony_ci	if (!*q) {
10618c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
10628c2ecf20Sopenharmony_ci		err = -EINVAL;
10638c2ecf20Sopenharmony_ci		goto errout_rcu;
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* Is it classful? */
10678c2ecf20Sopenharmony_ci	cops = (*q)->ops->cl_ops;
10688c2ecf20Sopenharmony_ci	if (!cops) {
10698c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Qdisc not classful");
10708c2ecf20Sopenharmony_ci		err = -EINVAL;
10718c2ecf20Sopenharmony_ci		goto errout_qdisc;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	if (!cops->tcf_block) {
10758c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
10768c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
10778c2ecf20Sopenharmony_ci		goto errout_qdisc;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_cierrout_rcu:
10818c2ecf20Sopenharmony_ci	/* At this point we know that qdisc is not noop_qdisc,
10828c2ecf20Sopenharmony_ci	 * which means that qdisc holds a reference to net_device
10838c2ecf20Sopenharmony_ci	 * and we hold a reference to qdisc, so it is safe to release
10848c2ecf20Sopenharmony_ci	 * rcu read lock.
10858c2ecf20Sopenharmony_ci	 */
10868c2ecf20Sopenharmony_ci	rcu_read_unlock();
10878c2ecf20Sopenharmony_ci	return err;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_cierrout_qdisc:
10908c2ecf20Sopenharmony_ci	rcu_read_unlock();
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	if (rtnl_held)
10938c2ecf20Sopenharmony_ci		qdisc_put(*q);
10948c2ecf20Sopenharmony_ci	else
10958c2ecf20Sopenharmony_ci		qdisc_put_unlocked(*q);
10968c2ecf20Sopenharmony_ci	*q = NULL;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	return err;
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl,
11028c2ecf20Sopenharmony_ci			       int ifindex, struct netlink_ext_ack *extack)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
11058c2ecf20Sopenharmony_ci		return 0;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* Do we search for filter, attached to class? */
11088c2ecf20Sopenharmony_ci	if (TC_H_MIN(parent)) {
11098c2ecf20Sopenharmony_ci		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		*cl = cops->find(q, parent);
11128c2ecf20Sopenharmony_ci		if (*cl == 0) {
11138c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Specified class doesn't exist");
11148c2ecf20Sopenharmony_ci			return -ENOENT;
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	return 0;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q,
11228c2ecf20Sopenharmony_ci					  unsigned long cl, int ifindex,
11238c2ecf20Sopenharmony_ci					  u32 block_index,
11248c2ecf20Sopenharmony_ci					  struct netlink_ext_ack *extack)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct tcf_block *block;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
11298c2ecf20Sopenharmony_ci		block = tcf_block_refcnt_get(net, block_index);
11308c2ecf20Sopenharmony_ci		if (!block) {
11318c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Block of given index was not found");
11328c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
11338c2ecf20Sopenharmony_ci		}
11348c2ecf20Sopenharmony_ci	} else {
11358c2ecf20Sopenharmony_ci		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		block = cops->tcf_block(q, cl, extack);
11388c2ecf20Sopenharmony_ci		if (!block)
11398c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci		if (tcf_block_shared(block)) {
11428c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters");
11438c2ecf20Sopenharmony_ci			return ERR_PTR(-EOPNOTSUPP);
11448c2ecf20Sopenharmony_ci		}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		/* Always take reference to block in order to support execution
11478c2ecf20Sopenharmony_ci		 * of rules update path of cls API without rtnl lock. Caller
11488c2ecf20Sopenharmony_ci		 * must release block when it is finished using it. 'if' block
11498c2ecf20Sopenharmony_ci		 * of this conditional obtain reference to block by calling
11508c2ecf20Sopenharmony_ci		 * tcf_block_refcnt_get().
11518c2ecf20Sopenharmony_ci		 */
11528c2ecf20Sopenharmony_ci		refcount_inc(&block->refcnt);
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return block;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
11598c2ecf20Sopenharmony_ci			    struct tcf_block_ext_info *ei, bool rtnl_held)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) {
11628c2ecf20Sopenharmony_ci		/* Flushing/putting all chains will cause the block to be
11638c2ecf20Sopenharmony_ci		 * deallocated when last chain is freed. However, if chain_list
11648c2ecf20Sopenharmony_ci		 * is empty, block has to be manually deallocated. After block
11658c2ecf20Sopenharmony_ci		 * reference counter reached 0, it is no longer possible to
11668c2ecf20Sopenharmony_ci		 * increment it or add new chains to block.
11678c2ecf20Sopenharmony_ci		 */
11688c2ecf20Sopenharmony_ci		bool free_block = list_empty(&block->chain_list);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		mutex_unlock(&block->lock);
11718c2ecf20Sopenharmony_ci		if (tcf_block_shared(block))
11728c2ecf20Sopenharmony_ci			tcf_block_remove(block, block->net);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		if (q)
11758c2ecf20Sopenharmony_ci			tcf_block_offload_unbind(block, q, ei);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		if (free_block)
11788c2ecf20Sopenharmony_ci			tcf_block_destroy(block);
11798c2ecf20Sopenharmony_ci		else
11808c2ecf20Sopenharmony_ci			tcf_block_flush_all_chains(block, rtnl_held);
11818c2ecf20Sopenharmony_ci	} else if (q) {
11828c2ecf20Sopenharmony_ci		tcf_block_offload_unbind(block, q, ei);
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	__tcf_block_put(block, NULL, NULL, rtnl_held);
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci/* Find tcf block.
11928c2ecf20Sopenharmony_ci * Set q, parent, cl when appropriate.
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
11968c2ecf20Sopenharmony_ci					u32 *parent, unsigned long *cl,
11978c2ecf20Sopenharmony_ci					int ifindex, u32 block_index,
11988c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct tcf_block *block;
12018c2ecf20Sopenharmony_ci	int err = 0;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	ASSERT_RTNL();
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack);
12068c2ecf20Sopenharmony_ci	if (err)
12078c2ecf20Sopenharmony_ci		goto errout;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack);
12108c2ecf20Sopenharmony_ci	if (err)
12118c2ecf20Sopenharmony_ci		goto errout_qdisc;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack);
12148c2ecf20Sopenharmony_ci	if (IS_ERR(block)) {
12158c2ecf20Sopenharmony_ci		err = PTR_ERR(block);
12168c2ecf20Sopenharmony_ci		goto errout_qdisc;
12178c2ecf20Sopenharmony_ci	}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	return block;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cierrout_qdisc:
12228c2ecf20Sopenharmony_ci	if (*q)
12238c2ecf20Sopenharmony_ci		qdisc_put(*q);
12248c2ecf20Sopenharmony_cierrout:
12258c2ecf20Sopenharmony_ci	*q = NULL;
12268c2ecf20Sopenharmony_ci	return ERR_PTR(err);
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
12308c2ecf20Sopenharmony_ci			      bool rtnl_held)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(block))
12338c2ecf20Sopenharmony_ci		tcf_block_refcnt_put(block, rtnl_held);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (q) {
12368c2ecf20Sopenharmony_ci		if (rtnl_held)
12378c2ecf20Sopenharmony_ci			qdisc_put(q);
12388c2ecf20Sopenharmony_ci		else
12398c2ecf20Sopenharmony_ci			qdisc_put_unlocked(q);
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cistruct tcf_block_owner_item {
12448c2ecf20Sopenharmony_ci	struct list_head list;
12458c2ecf20Sopenharmony_ci	struct Qdisc *q;
12468c2ecf20Sopenharmony_ci	enum flow_block_binder_type binder_type;
12478c2ecf20Sopenharmony_ci};
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_cistatic void
12508c2ecf20Sopenharmony_citcf_block_owner_netif_keep_dst(struct tcf_block *block,
12518c2ecf20Sopenharmony_ci			       struct Qdisc *q,
12528c2ecf20Sopenharmony_ci			       enum flow_block_binder_type binder_type)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	if (block->keep_dst &&
12558c2ecf20Sopenharmony_ci	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
12568c2ecf20Sopenharmony_ci	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
12578c2ecf20Sopenharmony_ci		netif_keep_dst(qdisc_dev(q));
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_civoid tcf_block_netif_keep_dst(struct tcf_block *block)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	struct tcf_block_owner_item *item;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	block->keep_dst = true;
12658c2ecf20Sopenharmony_ci	list_for_each_entry(item, &block->owner_list, list)
12668c2ecf20Sopenharmony_ci		tcf_block_owner_netif_keep_dst(block, item->q,
12678c2ecf20Sopenharmony_ci					       item->binder_type);
12688c2ecf20Sopenharmony_ci}
12698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_block_netif_keep_dst);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_cistatic int tcf_block_owner_add(struct tcf_block *block,
12728c2ecf20Sopenharmony_ci			       struct Qdisc *q,
12738c2ecf20Sopenharmony_ci			       enum flow_block_binder_type binder_type)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct tcf_block_owner_item *item;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	item = kmalloc(sizeof(*item), GFP_KERNEL);
12788c2ecf20Sopenharmony_ci	if (!item)
12798c2ecf20Sopenharmony_ci		return -ENOMEM;
12808c2ecf20Sopenharmony_ci	item->q = q;
12818c2ecf20Sopenharmony_ci	item->binder_type = binder_type;
12828c2ecf20Sopenharmony_ci	list_add(&item->list, &block->owner_list);
12838c2ecf20Sopenharmony_ci	return 0;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic void tcf_block_owner_del(struct tcf_block *block,
12878c2ecf20Sopenharmony_ci				struct Qdisc *q,
12888c2ecf20Sopenharmony_ci				enum flow_block_binder_type binder_type)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	struct tcf_block_owner_item *item;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	list_for_each_entry(item, &block->owner_list, list) {
12938c2ecf20Sopenharmony_ci		if (item->q == q && item->binder_type == binder_type) {
12948c2ecf20Sopenharmony_ci			list_del(&item->list);
12958c2ecf20Sopenharmony_ci			kfree(item);
12968c2ecf20Sopenharmony_ci			return;
12978c2ecf20Sopenharmony_ci		}
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci	WARN_ON(1);
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ciint tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
13038c2ecf20Sopenharmony_ci		      struct tcf_block_ext_info *ei,
13048c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	struct net *net = qdisc_net(q);
13078c2ecf20Sopenharmony_ci	struct tcf_block *block = NULL;
13088c2ecf20Sopenharmony_ci	int err;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	if (ei->block_index)
13118c2ecf20Sopenharmony_ci		/* block_index not 0 means the shared block is requested */
13128c2ecf20Sopenharmony_ci		block = tcf_block_refcnt_get(net, ei->block_index);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	if (!block) {
13158c2ecf20Sopenharmony_ci		block = tcf_block_create(net, q, ei->block_index, extack);
13168c2ecf20Sopenharmony_ci		if (IS_ERR(block))
13178c2ecf20Sopenharmony_ci			return PTR_ERR(block);
13188c2ecf20Sopenharmony_ci		if (tcf_block_shared(block)) {
13198c2ecf20Sopenharmony_ci			err = tcf_block_insert(block, net, extack);
13208c2ecf20Sopenharmony_ci			if (err)
13218c2ecf20Sopenharmony_ci				goto err_block_insert;
13228c2ecf20Sopenharmony_ci		}
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	err = tcf_block_owner_add(block, q, ei->binder_type);
13268c2ecf20Sopenharmony_ci	if (err)
13278c2ecf20Sopenharmony_ci		goto err_block_owner_add;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	tcf_block_owner_netif_keep_dst(block, q, ei->binder_type);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	err = tcf_chain0_head_change_cb_add(block, ei, extack);
13328c2ecf20Sopenharmony_ci	if (err)
13338c2ecf20Sopenharmony_ci		goto err_chain0_head_change_cb_add;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	err = tcf_block_offload_bind(block, q, ei, extack);
13368c2ecf20Sopenharmony_ci	if (err)
13378c2ecf20Sopenharmony_ci		goto err_block_offload_bind;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	*p_block = block;
13408c2ecf20Sopenharmony_ci	return 0;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cierr_block_offload_bind:
13438c2ecf20Sopenharmony_ci	tcf_chain0_head_change_cb_del(block, ei);
13448c2ecf20Sopenharmony_cierr_chain0_head_change_cb_add:
13458c2ecf20Sopenharmony_ci	tcf_block_owner_del(block, q, ei->binder_type);
13468c2ecf20Sopenharmony_cierr_block_owner_add:
13478c2ecf20Sopenharmony_cierr_block_insert:
13488c2ecf20Sopenharmony_ci	tcf_block_refcnt_put(block, true);
13498c2ecf20Sopenharmony_ci	return err;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get_ext);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_cistatic void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv)
13548c2ecf20Sopenharmony_ci{
13558c2ecf20Sopenharmony_ci	struct tcf_proto __rcu **p_filter_chain = priv;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	rcu_assign_pointer(*p_filter_chain, tp_head);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ciint tcf_block_get(struct tcf_block **p_block,
13618c2ecf20Sopenharmony_ci		  struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
13628c2ecf20Sopenharmony_ci		  struct netlink_ext_ack *extack)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	struct tcf_block_ext_info ei = {
13658c2ecf20Sopenharmony_ci		.chain_head_change = tcf_chain_head_change_dflt,
13668c2ecf20Sopenharmony_ci		.chain_head_change_priv = p_filter_chain,
13678c2ecf20Sopenharmony_ci	};
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	WARN_ON(!p_filter_chain);
13708c2ecf20Sopenharmony_ci	return tcf_block_get_ext(p_block, q, &ei, extack);
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci/* XXX: Standalone actions are not allowed to jump to any chain, and bound
13758c2ecf20Sopenharmony_ci * actions should be all removed after flushing.
13768c2ecf20Sopenharmony_ci */
13778c2ecf20Sopenharmony_civoid tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
13788c2ecf20Sopenharmony_ci		       struct tcf_block_ext_info *ei)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	if (!block)
13818c2ecf20Sopenharmony_ci		return;
13828c2ecf20Sopenharmony_ci	tcf_chain0_head_change_cb_del(block, ei);
13838c2ecf20Sopenharmony_ci	tcf_block_owner_del(block, q, ei->binder_type);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	__tcf_block_put(block, q, ei, true);
13868c2ecf20Sopenharmony_ci}
13878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put_ext);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_civoid tcf_block_put(struct tcf_block *block)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct tcf_block_ext_info ei = {0, };
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (!block)
13948c2ecf20Sopenharmony_ci		return;
13958c2ecf20Sopenharmony_ci	tcf_block_put_ext(block, block->q, &ei);
13968c2ecf20Sopenharmony_ci}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cistatic int
14018c2ecf20Sopenharmony_citcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb,
14028c2ecf20Sopenharmony_ci			    void *cb_priv, bool add, bool offload_in_use,
14038c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct tcf_chain *chain, *chain_prev;
14068c2ecf20Sopenharmony_ci	struct tcf_proto *tp, *tp_prev;
14078c2ecf20Sopenharmony_ci	int err;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	for (chain = __tcf_get_next_chain(block, NULL);
14128c2ecf20Sopenharmony_ci	     chain;
14138c2ecf20Sopenharmony_ci	     chain_prev = chain,
14148c2ecf20Sopenharmony_ci		     chain = __tcf_get_next_chain(block, chain),
14158c2ecf20Sopenharmony_ci		     tcf_chain_put(chain_prev)) {
14168c2ecf20Sopenharmony_ci		for (tp = __tcf_get_next_proto(chain, NULL); tp;
14178c2ecf20Sopenharmony_ci		     tp_prev = tp,
14188c2ecf20Sopenharmony_ci			     tp = __tcf_get_next_proto(chain, tp),
14198c2ecf20Sopenharmony_ci			     tcf_proto_put(tp_prev, true, NULL)) {
14208c2ecf20Sopenharmony_ci			if (tp->ops->reoffload) {
14218c2ecf20Sopenharmony_ci				err = tp->ops->reoffload(tp, add, cb, cb_priv,
14228c2ecf20Sopenharmony_ci							 extack);
14238c2ecf20Sopenharmony_ci				if (err && add)
14248c2ecf20Sopenharmony_ci					goto err_playback_remove;
14258c2ecf20Sopenharmony_ci			} else if (add && offload_in_use) {
14268c2ecf20Sopenharmony_ci				err = -EOPNOTSUPP;
14278c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support");
14288c2ecf20Sopenharmony_ci				goto err_playback_remove;
14298c2ecf20Sopenharmony_ci			}
14308c2ecf20Sopenharmony_ci		}
14318c2ecf20Sopenharmony_ci	}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	return 0;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_cierr_playback_remove:
14368c2ecf20Sopenharmony_ci	tcf_proto_put(tp, true, NULL);
14378c2ecf20Sopenharmony_ci	tcf_chain_put(chain);
14388c2ecf20Sopenharmony_ci	tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use,
14398c2ecf20Sopenharmony_ci				    extack);
14408c2ecf20Sopenharmony_ci	return err;
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic int tcf_block_bind(struct tcf_block *block,
14448c2ecf20Sopenharmony_ci			  struct flow_block_offload *bo)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	struct flow_block_cb *block_cb, *next;
14478c2ecf20Sopenharmony_ci	int err, i = 0;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	list_for_each_entry(block_cb, &bo->cb_list, list) {
14528c2ecf20Sopenharmony_ci		err = tcf_block_playback_offloads(block, block_cb->cb,
14538c2ecf20Sopenharmony_ci						  block_cb->cb_priv, true,
14548c2ecf20Sopenharmony_ci						  tcf_block_offload_in_use(block),
14558c2ecf20Sopenharmony_ci						  bo->extack);
14568c2ecf20Sopenharmony_ci		if (err)
14578c2ecf20Sopenharmony_ci			goto err_unroll;
14588c2ecf20Sopenharmony_ci		if (!bo->unlocked_driver_cb)
14598c2ecf20Sopenharmony_ci			block->lockeddevcnt++;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci		i++;
14628c2ecf20Sopenharmony_ci	}
14638c2ecf20Sopenharmony_ci	list_splice(&bo->cb_list, &block->flow_block.cb_list);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	return 0;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_cierr_unroll:
14688c2ecf20Sopenharmony_ci	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
14698c2ecf20Sopenharmony_ci		list_del(&block_cb->driver_list);
14708c2ecf20Sopenharmony_ci		if (i-- > 0) {
14718c2ecf20Sopenharmony_ci			list_del(&block_cb->list);
14728c2ecf20Sopenharmony_ci			tcf_block_playback_offloads(block, block_cb->cb,
14738c2ecf20Sopenharmony_ci						    block_cb->cb_priv, false,
14748c2ecf20Sopenharmony_ci						    tcf_block_offload_in_use(block),
14758c2ecf20Sopenharmony_ci						    NULL);
14768c2ecf20Sopenharmony_ci			if (!bo->unlocked_driver_cb)
14778c2ecf20Sopenharmony_ci				block->lockeddevcnt--;
14788c2ecf20Sopenharmony_ci		}
14798c2ecf20Sopenharmony_ci		flow_block_cb_free(block_cb);
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	return err;
14838c2ecf20Sopenharmony_ci}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block,
14868c2ecf20Sopenharmony_ci			     struct flow_block_offload *bo)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	struct flow_block_cb *block_cb, *next;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
14938c2ecf20Sopenharmony_ci		tcf_block_playback_offloads(block, block_cb->cb,
14948c2ecf20Sopenharmony_ci					    block_cb->cb_priv, false,
14958c2ecf20Sopenharmony_ci					    tcf_block_offload_in_use(block),
14968c2ecf20Sopenharmony_ci					    NULL);
14978c2ecf20Sopenharmony_ci		list_del(&block_cb->list);
14988c2ecf20Sopenharmony_ci		flow_block_cb_free(block_cb);
14998c2ecf20Sopenharmony_ci		if (!bo->unlocked_driver_cb)
15008c2ecf20Sopenharmony_ci			block->lockeddevcnt--;
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block,
15058c2ecf20Sopenharmony_ci			   struct flow_block_offload *bo)
15068c2ecf20Sopenharmony_ci{
15078c2ecf20Sopenharmony_ci	int err;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	switch (bo->command) {
15108c2ecf20Sopenharmony_ci	case FLOW_BLOCK_BIND:
15118c2ecf20Sopenharmony_ci		err = tcf_block_bind(block, bo);
15128c2ecf20Sopenharmony_ci		break;
15138c2ecf20Sopenharmony_ci	case FLOW_BLOCK_UNBIND:
15148c2ecf20Sopenharmony_ci		err = 0;
15158c2ecf20Sopenharmony_ci		tcf_block_unbind(block, bo);
15168c2ecf20Sopenharmony_ci		break;
15178c2ecf20Sopenharmony_ci	default:
15188c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
15198c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
15208c2ecf20Sopenharmony_ci	}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	return err;
15238c2ecf20Sopenharmony_ci}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci/* Main classifier routine: scans classifier chain attached
15268c2ecf20Sopenharmony_ci * to this qdisc, (optionally) tests for protocol and asks
15278c2ecf20Sopenharmony_ci * specific classifiers.
15288c2ecf20Sopenharmony_ci */
15298c2ecf20Sopenharmony_cistatic inline int __tcf_classify(struct sk_buff *skb,
15308c2ecf20Sopenharmony_ci				 const struct tcf_proto *tp,
15318c2ecf20Sopenharmony_ci				 const struct tcf_proto *orig_tp,
15328c2ecf20Sopenharmony_ci				 struct tcf_result *res,
15338c2ecf20Sopenharmony_ci				 bool compat_mode,
15348c2ecf20Sopenharmony_ci				 u32 *last_executed_chain)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
15378c2ecf20Sopenharmony_ci	const int max_reclassify_loop = 16;
15388c2ecf20Sopenharmony_ci	const struct tcf_proto *first_tp;
15398c2ecf20Sopenharmony_ci	int limit = 0;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_cireclassify:
15428c2ecf20Sopenharmony_ci#endif
15438c2ecf20Sopenharmony_ci	for (; tp; tp = rcu_dereference_bh(tp->next)) {
15448c2ecf20Sopenharmony_ci		__be16 protocol = skb_protocol(skb, false);
15458c2ecf20Sopenharmony_ci		int err;
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci		if (tp->protocol != protocol &&
15488c2ecf20Sopenharmony_ci		    tp->protocol != htons(ETH_P_ALL))
15498c2ecf20Sopenharmony_ci			continue;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci		err = tp->classify(skb, tp, res);
15528c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
15538c2ecf20Sopenharmony_ci		if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) {
15548c2ecf20Sopenharmony_ci			first_tp = orig_tp;
15558c2ecf20Sopenharmony_ci			*last_executed_chain = first_tp->chain->index;
15568c2ecf20Sopenharmony_ci			goto reset;
15578c2ecf20Sopenharmony_ci		} else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) {
15588c2ecf20Sopenharmony_ci			first_tp = res->goto_tp;
15598c2ecf20Sopenharmony_ci			*last_executed_chain = err & TC_ACT_EXT_VAL_MASK;
15608c2ecf20Sopenharmony_ci			goto reset;
15618c2ecf20Sopenharmony_ci		}
15628c2ecf20Sopenharmony_ci#endif
15638c2ecf20Sopenharmony_ci		if (err >= 0)
15648c2ecf20Sopenharmony_ci			return err;
15658c2ecf20Sopenharmony_ci	}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	return TC_ACT_UNSPEC; /* signal: continue lookup */
15688c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
15698c2ecf20Sopenharmony_cireset:
15708c2ecf20Sopenharmony_ci	if (unlikely(limit++ >= max_reclassify_loop)) {
15718c2ecf20Sopenharmony_ci		net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n",
15728c2ecf20Sopenharmony_ci				       tp->chain->block->index,
15738c2ecf20Sopenharmony_ci				       tp->prio & 0xffff,
15748c2ecf20Sopenharmony_ci				       ntohs(tp->protocol));
15758c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
15768c2ecf20Sopenharmony_ci	}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	tp = first_tp;
15798c2ecf20Sopenharmony_ci	goto reclassify;
15808c2ecf20Sopenharmony_ci#endif
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ciint tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
15848c2ecf20Sopenharmony_ci		 struct tcf_result *res, bool compat_mode)
15858c2ecf20Sopenharmony_ci{
15868c2ecf20Sopenharmony_ci	u32 last_executed_chain = 0;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	return __tcf_classify(skb, tp, tp, res, compat_mode,
15898c2ecf20Sopenharmony_ci			      &last_executed_chain);
15908c2ecf20Sopenharmony_ci}
15918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_classify);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ciint tcf_classify_ingress(struct sk_buff *skb,
15948c2ecf20Sopenharmony_ci			 const struct tcf_block *ingress_block,
15958c2ecf20Sopenharmony_ci			 const struct tcf_proto *tp,
15968c2ecf20Sopenharmony_ci			 struct tcf_result *res, bool compat_mode)
15978c2ecf20Sopenharmony_ci{
15988c2ecf20Sopenharmony_ci#if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
15998c2ecf20Sopenharmony_ci	u32 last_executed_chain = 0;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	return __tcf_classify(skb, tp, tp, res, compat_mode,
16028c2ecf20Sopenharmony_ci			      &last_executed_chain);
16038c2ecf20Sopenharmony_ci#else
16048c2ecf20Sopenharmony_ci	u32 last_executed_chain = tp ? tp->chain->index : 0;
16058c2ecf20Sopenharmony_ci	const struct tcf_proto *orig_tp = tp;
16068c2ecf20Sopenharmony_ci	struct tc_skb_ext *ext;
16078c2ecf20Sopenharmony_ci	int ret;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	ext = skb_ext_find(skb, TC_SKB_EXT);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	if (ext && ext->chain) {
16128c2ecf20Sopenharmony_ci		struct tcf_chain *fchain;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci		fchain = tcf_chain_lookup_rcu(ingress_block, ext->chain);
16158c2ecf20Sopenharmony_ci		if (!fchain)
16168c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		/* Consume, so cloned/redirect skbs won't inherit ext */
16198c2ecf20Sopenharmony_ci		skb_ext_del(skb, TC_SKB_EXT);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci		tp = rcu_dereference_bh(fchain->filter_chain);
16228c2ecf20Sopenharmony_ci		last_executed_chain = fchain->index;
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode,
16268c2ecf20Sopenharmony_ci			     &last_executed_chain);
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	/* If we missed on some chain */
16298c2ecf20Sopenharmony_ci	if (ret == TC_ACT_UNSPEC && last_executed_chain) {
16308c2ecf20Sopenharmony_ci		ext = tc_skb_ext_alloc(skb);
16318c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!ext))
16328c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
16338c2ecf20Sopenharmony_ci		ext->chain = last_executed_chain;
16348c2ecf20Sopenharmony_ci		ext->mru = qdisc_skb_cb(skb)->mru;
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	return ret;
16388c2ecf20Sopenharmony_ci#endif
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_classify_ingress);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_cistruct tcf_chain_info {
16438c2ecf20Sopenharmony_ci	struct tcf_proto __rcu **pprev;
16448c2ecf20Sopenharmony_ci	struct tcf_proto __rcu *next;
16458c2ecf20Sopenharmony_ci};
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain,
16488c2ecf20Sopenharmony_ci					   struct tcf_chain_info *chain_info)
16498c2ecf20Sopenharmony_ci{
16508c2ecf20Sopenharmony_ci	return tcf_chain_dereference(*chain_info->pprev, chain);
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic int tcf_chain_tp_insert(struct tcf_chain *chain,
16548c2ecf20Sopenharmony_ci			       struct tcf_chain_info *chain_info,
16558c2ecf20Sopenharmony_ci			       struct tcf_proto *tp)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	if (chain->flushing)
16588c2ecf20Sopenharmony_ci		return -EAGAIN;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info));
16618c2ecf20Sopenharmony_ci	if (*chain_info->pprev == chain->filter_chain)
16628c2ecf20Sopenharmony_ci		tcf_chain0_head_change(chain, tp);
16638c2ecf20Sopenharmony_ci	tcf_proto_get(tp);
16648c2ecf20Sopenharmony_ci	rcu_assign_pointer(*chain_info->pprev, tp);
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	return 0;
16678c2ecf20Sopenharmony_ci}
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_cistatic void tcf_chain_tp_remove(struct tcf_chain *chain,
16708c2ecf20Sopenharmony_ci				struct tcf_chain_info *chain_info,
16718c2ecf20Sopenharmony_ci				struct tcf_proto *tp)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain);
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	tcf_proto_mark_delete(tp);
16768c2ecf20Sopenharmony_ci	if (tp == chain->filter_chain)
16778c2ecf20Sopenharmony_ci		tcf_chain0_head_change(chain, next);
16788c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(*chain_info->pprev, next);
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
16828c2ecf20Sopenharmony_ci					   struct tcf_chain_info *chain_info,
16838c2ecf20Sopenharmony_ci					   u32 protocol, u32 prio,
16848c2ecf20Sopenharmony_ci					   bool prio_allocate);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci/* Try to insert new proto.
16878c2ecf20Sopenharmony_ci * If proto with specified priority already exists, free new proto
16888c2ecf20Sopenharmony_ci * and return existing one.
16898c2ecf20Sopenharmony_ci */
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain,
16928c2ecf20Sopenharmony_ci						    struct tcf_proto *tp_new,
16938c2ecf20Sopenharmony_ci						    u32 protocol, u32 prio,
16948c2ecf20Sopenharmony_ci						    bool rtnl_held)
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	struct tcf_chain_info chain_info;
16978c2ecf20Sopenharmony_ci	struct tcf_proto *tp;
16988c2ecf20Sopenharmony_ci	int err = 0;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	if (tcf_proto_exists_destroying(chain, tp_new)) {
17038c2ecf20Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
17048c2ecf20Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
17058c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
17068c2ecf20Sopenharmony_ci	}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info,
17098c2ecf20Sopenharmony_ci			       protocol, prio, false);
17108c2ecf20Sopenharmony_ci	if (!tp)
17118c2ecf20Sopenharmony_ci		err = tcf_chain_tp_insert(chain, &chain_info, tp_new);
17128c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	if (tp) {
17158c2ecf20Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
17168c2ecf20Sopenharmony_ci		tp_new = tp;
17178c2ecf20Sopenharmony_ci	} else if (err) {
17188c2ecf20Sopenharmony_ci		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
17198c2ecf20Sopenharmony_ci		tp_new = ERR_PTR(err);
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	return tp_new;
17238c2ecf20Sopenharmony_ci}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic void tcf_chain_tp_delete_empty(struct tcf_chain *chain,
17268c2ecf20Sopenharmony_ci				      struct tcf_proto *tp, bool rtnl_held,
17278c2ecf20Sopenharmony_ci				      struct netlink_ext_ack *extack)
17288c2ecf20Sopenharmony_ci{
17298c2ecf20Sopenharmony_ci	struct tcf_chain_info chain_info;
17308c2ecf20Sopenharmony_ci	struct tcf_proto *tp_iter;
17318c2ecf20Sopenharmony_ci	struct tcf_proto **pprev;
17328c2ecf20Sopenharmony_ci	struct tcf_proto *next;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	/* Atomically find and remove tp from chain. */
17378c2ecf20Sopenharmony_ci	for (pprev = &chain->filter_chain;
17388c2ecf20Sopenharmony_ci	     (tp_iter = tcf_chain_dereference(*pprev, chain));
17398c2ecf20Sopenharmony_ci	     pprev = &tp_iter->next) {
17408c2ecf20Sopenharmony_ci		if (tp_iter == tp) {
17418c2ecf20Sopenharmony_ci			chain_info.pprev = pprev;
17428c2ecf20Sopenharmony_ci			chain_info.next = tp_iter->next;
17438c2ecf20Sopenharmony_ci			WARN_ON(tp_iter->deleting);
17448c2ecf20Sopenharmony_ci			break;
17458c2ecf20Sopenharmony_ci		}
17468c2ecf20Sopenharmony_ci	}
17478c2ecf20Sopenharmony_ci	/* Verify that tp still exists and no new filters were inserted
17488c2ecf20Sopenharmony_ci	 * concurrently.
17498c2ecf20Sopenharmony_ci	 * Mark tp for deletion if it is empty.
17508c2ecf20Sopenharmony_ci	 */
17518c2ecf20Sopenharmony_ci	if (!tp_iter || !tcf_proto_check_delete(tp)) {
17528c2ecf20Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
17538c2ecf20Sopenharmony_ci		return;
17548c2ecf20Sopenharmony_ci	}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	tcf_proto_signal_destroying(chain, tp);
17578c2ecf20Sopenharmony_ci	next = tcf_chain_dereference(chain_info.next, chain);
17588c2ecf20Sopenharmony_ci	if (tp == chain->filter_chain)
17598c2ecf20Sopenharmony_ci		tcf_chain0_head_change(chain, next);
17608c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(*chain_info.pprev, next);
17618c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	tcf_proto_put(tp, rtnl_held, extack);
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
17678c2ecf20Sopenharmony_ci					   struct tcf_chain_info *chain_info,
17688c2ecf20Sopenharmony_ci					   u32 protocol, u32 prio,
17698c2ecf20Sopenharmony_ci					   bool prio_allocate)
17708c2ecf20Sopenharmony_ci{
17718c2ecf20Sopenharmony_ci	struct tcf_proto **pprev;
17728c2ecf20Sopenharmony_ci	struct tcf_proto *tp;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	/* Check the chain for existence of proto-tcf with this priority */
17758c2ecf20Sopenharmony_ci	for (pprev = &chain->filter_chain;
17768c2ecf20Sopenharmony_ci	     (tp = tcf_chain_dereference(*pprev, chain));
17778c2ecf20Sopenharmony_ci	     pprev = &tp->next) {
17788c2ecf20Sopenharmony_ci		if (tp->prio >= prio) {
17798c2ecf20Sopenharmony_ci			if (tp->prio == prio) {
17808c2ecf20Sopenharmony_ci				if (prio_allocate ||
17818c2ecf20Sopenharmony_ci				    (tp->protocol != protocol && protocol))
17828c2ecf20Sopenharmony_ci					return ERR_PTR(-EINVAL);
17838c2ecf20Sopenharmony_ci			} else {
17848c2ecf20Sopenharmony_ci				tp = NULL;
17858c2ecf20Sopenharmony_ci			}
17868c2ecf20Sopenharmony_ci			break;
17878c2ecf20Sopenharmony_ci		}
17888c2ecf20Sopenharmony_ci	}
17898c2ecf20Sopenharmony_ci	chain_info->pprev = pprev;
17908c2ecf20Sopenharmony_ci	if (tp) {
17918c2ecf20Sopenharmony_ci		chain_info->next = tp->next;
17928c2ecf20Sopenharmony_ci		tcf_proto_get(tp);
17938c2ecf20Sopenharmony_ci	} else {
17948c2ecf20Sopenharmony_ci		chain_info->next = NULL;
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci	return tp;
17978c2ecf20Sopenharmony_ci}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_cistatic int tcf_fill_node(struct net *net, struct sk_buff *skb,
18008c2ecf20Sopenharmony_ci			 struct tcf_proto *tp, struct tcf_block *block,
18018c2ecf20Sopenharmony_ci			 struct Qdisc *q, u32 parent, void *fh,
18028c2ecf20Sopenharmony_ci			 u32 portid, u32 seq, u16 flags, int event,
18038c2ecf20Sopenharmony_ci			 bool terse_dump, bool rtnl_held)
18048c2ecf20Sopenharmony_ci{
18058c2ecf20Sopenharmony_ci	struct tcmsg *tcm;
18068c2ecf20Sopenharmony_ci	struct nlmsghdr  *nlh;
18078c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
18108c2ecf20Sopenharmony_ci	if (!nlh)
18118c2ecf20Sopenharmony_ci		goto out_nlmsg_trim;
18128c2ecf20Sopenharmony_ci	tcm = nlmsg_data(nlh);
18138c2ecf20Sopenharmony_ci	tcm->tcm_family = AF_UNSPEC;
18148c2ecf20Sopenharmony_ci	tcm->tcm__pad1 = 0;
18158c2ecf20Sopenharmony_ci	tcm->tcm__pad2 = 0;
18168c2ecf20Sopenharmony_ci	if (q) {
18178c2ecf20Sopenharmony_ci		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
18188c2ecf20Sopenharmony_ci		tcm->tcm_parent = parent;
18198c2ecf20Sopenharmony_ci	} else {
18208c2ecf20Sopenharmony_ci		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
18218c2ecf20Sopenharmony_ci		tcm->tcm_block_index = block->index;
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
18248c2ecf20Sopenharmony_ci	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
18258c2ecf20Sopenharmony_ci		goto nla_put_failure;
18268c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index))
18278c2ecf20Sopenharmony_ci		goto nla_put_failure;
18288c2ecf20Sopenharmony_ci	if (!fh) {
18298c2ecf20Sopenharmony_ci		tcm->tcm_handle = 0;
18308c2ecf20Sopenharmony_ci	} else if (terse_dump) {
18318c2ecf20Sopenharmony_ci		if (tp->ops->terse_dump) {
18328c2ecf20Sopenharmony_ci			if (tp->ops->terse_dump(net, tp, fh, skb, tcm,
18338c2ecf20Sopenharmony_ci						rtnl_held) < 0)
18348c2ecf20Sopenharmony_ci				goto nla_put_failure;
18358c2ecf20Sopenharmony_ci		} else {
18368c2ecf20Sopenharmony_ci			goto cls_op_not_supp;
18378c2ecf20Sopenharmony_ci		}
18388c2ecf20Sopenharmony_ci	} else {
18398c2ecf20Sopenharmony_ci		if (tp->ops->dump &&
18408c2ecf20Sopenharmony_ci		    tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
18418c2ecf20Sopenharmony_ci			goto nla_put_failure;
18428c2ecf20Sopenharmony_ci	}
18438c2ecf20Sopenharmony_ci	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
18448c2ecf20Sopenharmony_ci	return skb->len;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ciout_nlmsg_trim:
18478c2ecf20Sopenharmony_cinla_put_failure:
18488c2ecf20Sopenharmony_cicls_op_not_supp:
18498c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
18508c2ecf20Sopenharmony_ci	return -1;
18518c2ecf20Sopenharmony_ci}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_cistatic int tfilter_notify(struct net *net, struct sk_buff *oskb,
18548c2ecf20Sopenharmony_ci			  struct nlmsghdr *n, struct tcf_proto *tp,
18558c2ecf20Sopenharmony_ci			  struct tcf_block *block, struct Qdisc *q,
18568c2ecf20Sopenharmony_ci			  u32 parent, void *fh, int event, bool unicast,
18578c2ecf20Sopenharmony_ci			  bool rtnl_held)
18588c2ecf20Sopenharmony_ci{
18598c2ecf20Sopenharmony_ci	struct sk_buff *skb;
18608c2ecf20Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
18618c2ecf20Sopenharmony_ci	int err = 0;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
18648c2ecf20Sopenharmony_ci	if (!skb)
18658c2ecf20Sopenharmony_ci		return -ENOBUFS;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
18688c2ecf20Sopenharmony_ci			  n->nlmsg_seq, n->nlmsg_flags, event,
18698c2ecf20Sopenharmony_ci			  false, rtnl_held) <= 0) {
18708c2ecf20Sopenharmony_ci		kfree_skb(skb);
18718c2ecf20Sopenharmony_ci		return -EINVAL;
18728c2ecf20Sopenharmony_ci	}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	if (unicast)
18758c2ecf20Sopenharmony_ci		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
18768c2ecf20Sopenharmony_ci	else
18778c2ecf20Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
18788c2ecf20Sopenharmony_ci				     n->nlmsg_flags & NLM_F_ECHO);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if (err > 0)
18818c2ecf20Sopenharmony_ci		err = 0;
18828c2ecf20Sopenharmony_ci	return err;
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_cistatic int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
18868c2ecf20Sopenharmony_ci			      struct nlmsghdr *n, struct tcf_proto *tp,
18878c2ecf20Sopenharmony_ci			      struct tcf_block *block, struct Qdisc *q,
18888c2ecf20Sopenharmony_ci			      u32 parent, void *fh, bool unicast, bool *last,
18898c2ecf20Sopenharmony_ci			      bool rtnl_held, struct netlink_ext_ack *extack)
18908c2ecf20Sopenharmony_ci{
18918c2ecf20Sopenharmony_ci	struct sk_buff *skb;
18928c2ecf20Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
18938c2ecf20Sopenharmony_ci	int err;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
18968c2ecf20Sopenharmony_ci	if (!skb)
18978c2ecf20Sopenharmony_ci		return -ENOBUFS;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
19008c2ecf20Sopenharmony_ci			  n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
19018c2ecf20Sopenharmony_ci			  false, rtnl_held) <= 0) {
19028c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to build del event notification");
19038c2ecf20Sopenharmony_ci		kfree_skb(skb);
19048c2ecf20Sopenharmony_ci		return -EINVAL;
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	err = tp->ops->delete(tp, fh, last, rtnl_held, extack);
19088c2ecf20Sopenharmony_ci	if (err) {
19098c2ecf20Sopenharmony_ci		kfree_skb(skb);
19108c2ecf20Sopenharmony_ci		return err;
19118c2ecf20Sopenharmony_ci	}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	if (unicast)
19148c2ecf20Sopenharmony_ci		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
19158c2ecf20Sopenharmony_ci	else
19168c2ecf20Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
19178c2ecf20Sopenharmony_ci				     n->nlmsg_flags & NLM_F_ECHO);
19188c2ecf20Sopenharmony_ci	if (err < 0)
19198c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to send filter delete notification");
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	if (err > 0)
19228c2ecf20Sopenharmony_ci		err = 0;
19238c2ecf20Sopenharmony_ci	return err;
19248c2ecf20Sopenharmony_ci}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_cistatic void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
19278c2ecf20Sopenharmony_ci				 struct tcf_block *block, struct Qdisc *q,
19288c2ecf20Sopenharmony_ci				 u32 parent, struct nlmsghdr *n,
19298c2ecf20Sopenharmony_ci				 struct tcf_chain *chain, int event,
19308c2ecf20Sopenharmony_ci				 bool rtnl_held)
19318c2ecf20Sopenharmony_ci{
19328c2ecf20Sopenharmony_ci	struct tcf_proto *tp;
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	for (tp = tcf_get_next_proto(chain, NULL, rtnl_held);
19358c2ecf20Sopenharmony_ci	     tp; tp = tcf_get_next_proto(chain, tp, rtnl_held))
19368c2ecf20Sopenharmony_ci		tfilter_notify(net, oskb, n, tp, block,
19378c2ecf20Sopenharmony_ci			       q, parent, NULL, event, false, rtnl_held);
19388c2ecf20Sopenharmony_ci}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_cistatic void tfilter_put(struct tcf_proto *tp, void *fh)
19418c2ecf20Sopenharmony_ci{
19428c2ecf20Sopenharmony_ci	if (tp->ops->put && fh)
19438c2ecf20Sopenharmony_ci		tp->ops->put(tp, fh);
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_cistatic int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
19478c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
19488c2ecf20Sopenharmony_ci{
19498c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
19508c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
19518c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
19528c2ecf20Sopenharmony_ci	struct tcmsg *t;
19538c2ecf20Sopenharmony_ci	u32 protocol;
19548c2ecf20Sopenharmony_ci	u32 prio;
19558c2ecf20Sopenharmony_ci	bool prio_allocate;
19568c2ecf20Sopenharmony_ci	u32 parent;
19578c2ecf20Sopenharmony_ci	u32 chain_index;
19588c2ecf20Sopenharmony_ci	struct Qdisc *q;
19598c2ecf20Sopenharmony_ci	struct tcf_chain_info chain_info;
19608c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
19618c2ecf20Sopenharmony_ci	struct tcf_block *block;
19628c2ecf20Sopenharmony_ci	struct tcf_proto *tp;
19638c2ecf20Sopenharmony_ci	unsigned long cl;
19648c2ecf20Sopenharmony_ci	void *fh;
19658c2ecf20Sopenharmony_ci	int err;
19668c2ecf20Sopenharmony_ci	int tp_created;
19678c2ecf20Sopenharmony_ci	bool rtnl_held = false;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
19708c2ecf20Sopenharmony_ci		return -EPERM;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_cireplay:
19738c2ecf20Sopenharmony_ci	tp_created = 0;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
19768c2ecf20Sopenharmony_ci				     rtm_tca_policy, extack);
19778c2ecf20Sopenharmony_ci	if (err < 0)
19788c2ecf20Sopenharmony_ci		return err;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	t = nlmsg_data(n);
19818c2ecf20Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
19828c2ecf20Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
19838c2ecf20Sopenharmony_ci	prio_allocate = false;
19848c2ecf20Sopenharmony_ci	parent = t->tcm_parent;
19858c2ecf20Sopenharmony_ci	tp = NULL;
19868c2ecf20Sopenharmony_ci	cl = 0;
19878c2ecf20Sopenharmony_ci	block = NULL;
19888c2ecf20Sopenharmony_ci	q = NULL;
19898c2ecf20Sopenharmony_ci	chain = NULL;
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	if (prio == 0) {
19928c2ecf20Sopenharmony_ci		/* If no priority is provided by the user,
19938c2ecf20Sopenharmony_ci		 * we allocate one.
19948c2ecf20Sopenharmony_ci		 */
19958c2ecf20Sopenharmony_ci		if (n->nlmsg_flags & NLM_F_CREATE) {
19968c2ecf20Sopenharmony_ci			prio = TC_H_MAKE(0x80000000U, 0U);
19978c2ecf20Sopenharmony_ci			prio_allocate = true;
19988c2ecf20Sopenharmony_ci		} else {
19998c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
20008c2ecf20Sopenharmony_ci			return -ENOENT;
20018c2ecf20Sopenharmony_ci		}
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	/* Find head of filter chain. */
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
20078c2ecf20Sopenharmony_ci	if (err)
20088c2ecf20Sopenharmony_ci		return err;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
20118c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
20128c2ecf20Sopenharmony_ci		err = -EINVAL;
20138c2ecf20Sopenharmony_ci		goto errout;
20148c2ecf20Sopenharmony_ci	}
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	/* Take rtnl mutex if rtnl_held was set to true on previous iteration,
20178c2ecf20Sopenharmony_ci	 * block is shared (no qdisc found), qdisc is not unlocked, classifier
20188c2ecf20Sopenharmony_ci	 * type is not specified, classifier is not unlocked.
20198c2ecf20Sopenharmony_ci	 */
20208c2ecf20Sopenharmony_ci	if (rtnl_held ||
20218c2ecf20Sopenharmony_ci	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
20228c2ecf20Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
20238c2ecf20Sopenharmony_ci		rtnl_held = true;
20248c2ecf20Sopenharmony_ci		rtnl_lock();
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
20288c2ecf20Sopenharmony_ci	if (err)
20298c2ecf20Sopenharmony_ci		goto errout;
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
20328c2ecf20Sopenharmony_ci				 extack);
20338c2ecf20Sopenharmony_ci	if (IS_ERR(block)) {
20348c2ecf20Sopenharmony_ci		err = PTR_ERR(block);
20358c2ecf20Sopenharmony_ci		goto errout;
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci	block->classid = parent;
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
20408c2ecf20Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
20418c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
20428c2ecf20Sopenharmony_ci		err = -EINVAL;
20438c2ecf20Sopenharmony_ci		goto errout;
20448c2ecf20Sopenharmony_ci	}
20458c2ecf20Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, true);
20468c2ecf20Sopenharmony_ci	if (!chain) {
20478c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot create specified filter chain");
20488c2ecf20Sopenharmony_ci		err = -ENOMEM;
20498c2ecf20Sopenharmony_ci		goto errout;
20508c2ecf20Sopenharmony_ci	}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
20538c2ecf20Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
20548c2ecf20Sopenharmony_ci			       prio, prio_allocate);
20558c2ecf20Sopenharmony_ci	if (IS_ERR(tp)) {
20568c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
20578c2ecf20Sopenharmony_ci		err = PTR_ERR(tp);
20588c2ecf20Sopenharmony_ci		goto errout_locked;
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	if (tp == NULL) {
20628c2ecf20Sopenharmony_ci		struct tcf_proto *tp_new = NULL;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci		if (chain->flushing) {
20658c2ecf20Sopenharmony_ci			err = -EAGAIN;
20668c2ecf20Sopenharmony_ci			goto errout_locked;
20678c2ecf20Sopenharmony_ci		}
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci		/* Proto-tcf does not exist, create new one */
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci		if (tca[TCA_KIND] == NULL || !protocol) {
20728c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified");
20738c2ecf20Sopenharmony_ci			err = -EINVAL;
20748c2ecf20Sopenharmony_ci			goto errout_locked;
20758c2ecf20Sopenharmony_ci		}
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
20788c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
20798c2ecf20Sopenharmony_ci			err = -ENOENT;
20808c2ecf20Sopenharmony_ci			goto errout_locked;
20818c2ecf20Sopenharmony_ci		}
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci		if (prio_allocate)
20848c2ecf20Sopenharmony_ci			prio = tcf_auto_prio(tcf_chain_tp_prev(chain,
20858c2ecf20Sopenharmony_ci							       &chain_info));
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
20888c2ecf20Sopenharmony_ci		tp_new = tcf_proto_create(name, protocol, prio, chain,
20898c2ecf20Sopenharmony_ci					  rtnl_held, extack);
20908c2ecf20Sopenharmony_ci		if (IS_ERR(tp_new)) {
20918c2ecf20Sopenharmony_ci			err = PTR_ERR(tp_new);
20928c2ecf20Sopenharmony_ci			goto errout_tp;
20938c2ecf20Sopenharmony_ci		}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci		tp_created = 1;
20968c2ecf20Sopenharmony_ci		tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio,
20978c2ecf20Sopenharmony_ci						rtnl_held);
20988c2ecf20Sopenharmony_ci		if (IS_ERR(tp)) {
20998c2ecf20Sopenharmony_ci			err = PTR_ERR(tp);
21008c2ecf20Sopenharmony_ci			goto errout_tp;
21018c2ecf20Sopenharmony_ci		}
21028c2ecf20Sopenharmony_ci	} else {
21038c2ecf20Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
21048c2ecf20Sopenharmony_ci	}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
21078c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
21088c2ecf20Sopenharmony_ci		err = -EINVAL;
21098c2ecf20Sopenharmony_ci		goto errout;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	if (!fh) {
21158c2ecf20Sopenharmony_ci		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
21168c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
21178c2ecf20Sopenharmony_ci			err = -ENOENT;
21188c2ecf20Sopenharmony_ci			goto errout;
21198c2ecf20Sopenharmony_ci		}
21208c2ecf20Sopenharmony_ci	} else if (n->nlmsg_flags & NLM_F_EXCL) {
21218c2ecf20Sopenharmony_ci		tfilter_put(tp, fh);
21228c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter already exists");
21238c2ecf20Sopenharmony_ci		err = -EEXIST;
21248c2ecf20Sopenharmony_ci		goto errout;
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) {
21288c2ecf20Sopenharmony_ci		tfilter_put(tp, fh);
21298c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind");
21308c2ecf20Sopenharmony_ci		err = -EINVAL;
21318c2ecf20Sopenharmony_ci		goto errout;
21328c2ecf20Sopenharmony_ci	}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
21358c2ecf20Sopenharmony_ci			      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE,
21368c2ecf20Sopenharmony_ci			      rtnl_held, extack);
21378c2ecf20Sopenharmony_ci	if (err == 0) {
21388c2ecf20Sopenharmony_ci		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
21398c2ecf20Sopenharmony_ci			       RTM_NEWTFILTER, false, rtnl_held);
21408c2ecf20Sopenharmony_ci		tfilter_put(tp, fh);
21418c2ecf20Sopenharmony_ci		/* q pointer is NULL for shared blocks */
21428c2ecf20Sopenharmony_ci		if (q)
21438c2ecf20Sopenharmony_ci			q->flags &= ~TCQ_F_CAN_BYPASS;
21448c2ecf20Sopenharmony_ci	}
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_cierrout:
21478c2ecf20Sopenharmony_ci	if (err && tp_created)
21488c2ecf20Sopenharmony_ci		tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
21498c2ecf20Sopenharmony_cierrout_tp:
21508c2ecf20Sopenharmony_ci	if (chain) {
21518c2ecf20Sopenharmony_ci		if (tp && !IS_ERR(tp))
21528c2ecf20Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
21538c2ecf20Sopenharmony_ci		if (!tp_created)
21548c2ecf20Sopenharmony_ci			tcf_chain_put(chain);
21558c2ecf20Sopenharmony_ci	}
21568c2ecf20Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	if (rtnl_held)
21598c2ecf20Sopenharmony_ci		rtnl_unlock();
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci	if (err == -EAGAIN) {
21628c2ecf20Sopenharmony_ci		/* Take rtnl lock in case EAGAIN is caused by concurrent flush
21638c2ecf20Sopenharmony_ci		 * of target chain.
21648c2ecf20Sopenharmony_ci		 */
21658c2ecf20Sopenharmony_ci		rtnl_held = true;
21668c2ecf20Sopenharmony_ci		/* Replay the request. */
21678c2ecf20Sopenharmony_ci		goto replay;
21688c2ecf20Sopenharmony_ci	}
21698c2ecf20Sopenharmony_ci	return err;
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_cierrout_locked:
21728c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
21738c2ecf20Sopenharmony_ci	goto errout;
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_cistatic int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
21778c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
21808c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
21818c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
21828c2ecf20Sopenharmony_ci	struct tcmsg *t;
21838c2ecf20Sopenharmony_ci	u32 protocol;
21848c2ecf20Sopenharmony_ci	u32 prio;
21858c2ecf20Sopenharmony_ci	u32 parent;
21868c2ecf20Sopenharmony_ci	u32 chain_index;
21878c2ecf20Sopenharmony_ci	struct Qdisc *q = NULL;
21888c2ecf20Sopenharmony_ci	struct tcf_chain_info chain_info;
21898c2ecf20Sopenharmony_ci	struct tcf_chain *chain = NULL;
21908c2ecf20Sopenharmony_ci	struct tcf_block *block = NULL;
21918c2ecf20Sopenharmony_ci	struct tcf_proto *tp = NULL;
21928c2ecf20Sopenharmony_ci	unsigned long cl = 0;
21938c2ecf20Sopenharmony_ci	void *fh = NULL;
21948c2ecf20Sopenharmony_ci	int err;
21958c2ecf20Sopenharmony_ci	bool rtnl_held = false;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
21988c2ecf20Sopenharmony_ci		return -EPERM;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
22018c2ecf20Sopenharmony_ci				     rtm_tca_policy, extack);
22028c2ecf20Sopenharmony_ci	if (err < 0)
22038c2ecf20Sopenharmony_ci		return err;
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	t = nlmsg_data(n);
22068c2ecf20Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
22078c2ecf20Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
22088c2ecf20Sopenharmony_ci	parent = t->tcm_parent;
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) {
22118c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set");
22128c2ecf20Sopenharmony_ci		return -ENOENT;
22138c2ecf20Sopenharmony_ci	}
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	/* Find head of filter chain. */
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
22188c2ecf20Sopenharmony_ci	if (err)
22198c2ecf20Sopenharmony_ci		return err;
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
22228c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
22238c2ecf20Sopenharmony_ci		err = -EINVAL;
22248c2ecf20Sopenharmony_ci		goto errout;
22258c2ecf20Sopenharmony_ci	}
22268c2ecf20Sopenharmony_ci	/* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
22278c2ecf20Sopenharmony_ci	 * found), qdisc is not unlocked, classifier type is not specified,
22288c2ecf20Sopenharmony_ci	 * classifier is not unlocked.
22298c2ecf20Sopenharmony_ci	 */
22308c2ecf20Sopenharmony_ci	if (!prio ||
22318c2ecf20Sopenharmony_ci	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
22328c2ecf20Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
22338c2ecf20Sopenharmony_ci		rtnl_held = true;
22348c2ecf20Sopenharmony_ci		rtnl_lock();
22358c2ecf20Sopenharmony_ci	}
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
22388c2ecf20Sopenharmony_ci	if (err)
22398c2ecf20Sopenharmony_ci		goto errout;
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
22428c2ecf20Sopenharmony_ci				 extack);
22438c2ecf20Sopenharmony_ci	if (IS_ERR(block)) {
22448c2ecf20Sopenharmony_ci		err = PTR_ERR(block);
22458c2ecf20Sopenharmony_ci		goto errout;
22468c2ecf20Sopenharmony_ci	}
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
22498c2ecf20Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
22508c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
22518c2ecf20Sopenharmony_ci		err = -EINVAL;
22528c2ecf20Sopenharmony_ci		goto errout;
22538c2ecf20Sopenharmony_ci	}
22548c2ecf20Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, false);
22558c2ecf20Sopenharmony_ci	if (!chain) {
22568c2ecf20Sopenharmony_ci		/* User requested flush on non-existent chain. Nothing to do,
22578c2ecf20Sopenharmony_ci		 * so just return success.
22588c2ecf20Sopenharmony_ci		 */
22598c2ecf20Sopenharmony_ci		if (prio == 0) {
22608c2ecf20Sopenharmony_ci			err = 0;
22618c2ecf20Sopenharmony_ci			goto errout;
22628c2ecf20Sopenharmony_ci		}
22638c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
22648c2ecf20Sopenharmony_ci		err = -ENOENT;
22658c2ecf20Sopenharmony_ci		goto errout;
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	if (prio == 0) {
22698c2ecf20Sopenharmony_ci		tfilter_notify_chain(net, skb, block, q, parent, n,
22708c2ecf20Sopenharmony_ci				     chain, RTM_DELTFILTER, rtnl_held);
22718c2ecf20Sopenharmony_ci		tcf_chain_flush(chain, rtnl_held);
22728c2ecf20Sopenharmony_ci		err = 0;
22738c2ecf20Sopenharmony_ci		goto errout;
22748c2ecf20Sopenharmony_ci	}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
22778c2ecf20Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
22788c2ecf20Sopenharmony_ci			       prio, false);
22798c2ecf20Sopenharmony_ci	if (!tp || IS_ERR(tp)) {
22808c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
22818c2ecf20Sopenharmony_ci		err = tp ? PTR_ERR(tp) : -ENOENT;
22828c2ecf20Sopenharmony_ci		goto errout_locked;
22838c2ecf20Sopenharmony_ci	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
22848c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
22858c2ecf20Sopenharmony_ci		err = -EINVAL;
22868c2ecf20Sopenharmony_ci		goto errout_locked;
22878c2ecf20Sopenharmony_ci	} else if (t->tcm_handle == 0) {
22888c2ecf20Sopenharmony_ci		tcf_proto_signal_destroying(chain, tp);
22898c2ecf20Sopenharmony_ci		tcf_chain_tp_remove(chain, &chain_info, tp);
22908c2ecf20Sopenharmony_ci		mutex_unlock(&chain->filter_chain_lock);
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci		tcf_proto_put(tp, rtnl_held, NULL);
22938c2ecf20Sopenharmony_ci		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
22948c2ecf20Sopenharmony_ci			       RTM_DELTFILTER, false, rtnl_held);
22958c2ecf20Sopenharmony_ci		err = 0;
22968c2ecf20Sopenharmony_ci		goto errout;
22978c2ecf20Sopenharmony_ci	}
22988c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	if (!fh) {
23038c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
23048c2ecf20Sopenharmony_ci		err = -ENOENT;
23058c2ecf20Sopenharmony_ci	} else {
23068c2ecf20Sopenharmony_ci		bool last;
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci		err = tfilter_del_notify(net, skb, n, tp, block,
23098c2ecf20Sopenharmony_ci					 q, parent, fh, false, &last,
23108c2ecf20Sopenharmony_ci					 rtnl_held, extack);
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci		if (err)
23138c2ecf20Sopenharmony_ci			goto errout;
23148c2ecf20Sopenharmony_ci		if (last)
23158c2ecf20Sopenharmony_ci			tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack);
23168c2ecf20Sopenharmony_ci	}
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_cierrout:
23198c2ecf20Sopenharmony_ci	if (chain) {
23208c2ecf20Sopenharmony_ci		if (tp && !IS_ERR(tp))
23218c2ecf20Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
23228c2ecf20Sopenharmony_ci		tcf_chain_put(chain);
23238c2ecf20Sopenharmony_ci	}
23248c2ecf20Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	if (rtnl_held)
23278c2ecf20Sopenharmony_ci		rtnl_unlock();
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	return err;
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_cierrout_locked:
23328c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
23338c2ecf20Sopenharmony_ci	goto errout;
23348c2ecf20Sopenharmony_ci}
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_cistatic int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
23378c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
23388c2ecf20Sopenharmony_ci{
23398c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
23408c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
23418c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
23428c2ecf20Sopenharmony_ci	struct tcmsg *t;
23438c2ecf20Sopenharmony_ci	u32 protocol;
23448c2ecf20Sopenharmony_ci	u32 prio;
23458c2ecf20Sopenharmony_ci	u32 parent;
23468c2ecf20Sopenharmony_ci	u32 chain_index;
23478c2ecf20Sopenharmony_ci	struct Qdisc *q = NULL;
23488c2ecf20Sopenharmony_ci	struct tcf_chain_info chain_info;
23498c2ecf20Sopenharmony_ci	struct tcf_chain *chain = NULL;
23508c2ecf20Sopenharmony_ci	struct tcf_block *block = NULL;
23518c2ecf20Sopenharmony_ci	struct tcf_proto *tp = NULL;
23528c2ecf20Sopenharmony_ci	unsigned long cl = 0;
23538c2ecf20Sopenharmony_ci	void *fh = NULL;
23548c2ecf20Sopenharmony_ci	int err;
23558c2ecf20Sopenharmony_ci	bool rtnl_held = false;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
23588c2ecf20Sopenharmony_ci				     rtm_tca_policy, extack);
23598c2ecf20Sopenharmony_ci	if (err < 0)
23608c2ecf20Sopenharmony_ci		return err;
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	t = nlmsg_data(n);
23638c2ecf20Sopenharmony_ci	protocol = TC_H_MIN(t->tcm_info);
23648c2ecf20Sopenharmony_ci	prio = TC_H_MAJ(t->tcm_info);
23658c2ecf20Sopenharmony_ci	parent = t->tcm_parent;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	if (prio == 0) {
23688c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
23698c2ecf20Sopenharmony_ci		return -ENOENT;
23708c2ecf20Sopenharmony_ci	}
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	/* Find head of filter chain. */
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
23758c2ecf20Sopenharmony_ci	if (err)
23768c2ecf20Sopenharmony_ci		return err;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
23798c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
23808c2ecf20Sopenharmony_ci		err = -EINVAL;
23818c2ecf20Sopenharmony_ci		goto errout;
23828c2ecf20Sopenharmony_ci	}
23838c2ecf20Sopenharmony_ci	/* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
23848c2ecf20Sopenharmony_ci	 * unlocked, classifier type is not specified, classifier is not
23858c2ecf20Sopenharmony_ci	 * unlocked.
23868c2ecf20Sopenharmony_ci	 */
23878c2ecf20Sopenharmony_ci	if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
23888c2ecf20Sopenharmony_ci	    !tcf_proto_is_unlocked(name)) {
23898c2ecf20Sopenharmony_ci		rtnl_held = true;
23908c2ecf20Sopenharmony_ci		rtnl_lock();
23918c2ecf20Sopenharmony_ci	}
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
23948c2ecf20Sopenharmony_ci	if (err)
23958c2ecf20Sopenharmony_ci		goto errout;
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
23988c2ecf20Sopenharmony_ci				 extack);
23998c2ecf20Sopenharmony_ci	if (IS_ERR(block)) {
24008c2ecf20Sopenharmony_ci		err = PTR_ERR(block);
24018c2ecf20Sopenharmony_ci		goto errout;
24028c2ecf20Sopenharmony_ci	}
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
24058c2ecf20Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
24068c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
24078c2ecf20Sopenharmony_ci		err = -EINVAL;
24088c2ecf20Sopenharmony_ci		goto errout;
24098c2ecf20Sopenharmony_ci	}
24108c2ecf20Sopenharmony_ci	chain = tcf_chain_get(block, chain_index, false);
24118c2ecf20Sopenharmony_ci	if (!chain) {
24128c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
24138c2ecf20Sopenharmony_ci		err = -EINVAL;
24148c2ecf20Sopenharmony_ci		goto errout;
24158c2ecf20Sopenharmony_ci	}
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	mutex_lock(&chain->filter_chain_lock);
24188c2ecf20Sopenharmony_ci	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
24198c2ecf20Sopenharmony_ci			       prio, false);
24208c2ecf20Sopenharmony_ci	mutex_unlock(&chain->filter_chain_lock);
24218c2ecf20Sopenharmony_ci	if (!tp || IS_ERR(tp)) {
24228c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
24238c2ecf20Sopenharmony_ci		err = tp ? PTR_ERR(tp) : -ENOENT;
24248c2ecf20Sopenharmony_ci		goto errout;
24258c2ecf20Sopenharmony_ci	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
24268c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
24278c2ecf20Sopenharmony_ci		err = -EINVAL;
24288c2ecf20Sopenharmony_ci		goto errout;
24298c2ecf20Sopenharmony_ci	}
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	fh = tp->ops->get(tp, t->tcm_handle);
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	if (!fh) {
24348c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
24358c2ecf20Sopenharmony_ci		err = -ENOENT;
24368c2ecf20Sopenharmony_ci	} else {
24378c2ecf20Sopenharmony_ci		err = tfilter_notify(net, skb, n, tp, block, q, parent,
24388c2ecf20Sopenharmony_ci				     fh, RTM_NEWTFILTER, true, rtnl_held);
24398c2ecf20Sopenharmony_ci		if (err < 0)
24408c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
24418c2ecf20Sopenharmony_ci	}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	tfilter_put(tp, fh);
24448c2ecf20Sopenharmony_cierrout:
24458c2ecf20Sopenharmony_ci	if (chain) {
24468c2ecf20Sopenharmony_ci		if (tp && !IS_ERR(tp))
24478c2ecf20Sopenharmony_ci			tcf_proto_put(tp, rtnl_held, NULL);
24488c2ecf20Sopenharmony_ci		tcf_chain_put(chain);
24498c2ecf20Sopenharmony_ci	}
24508c2ecf20Sopenharmony_ci	tcf_block_release(q, block, rtnl_held);
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	if (rtnl_held)
24538c2ecf20Sopenharmony_ci		rtnl_unlock();
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	return err;
24568c2ecf20Sopenharmony_ci}
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_cistruct tcf_dump_args {
24598c2ecf20Sopenharmony_ci	struct tcf_walker w;
24608c2ecf20Sopenharmony_ci	struct sk_buff *skb;
24618c2ecf20Sopenharmony_ci	struct netlink_callback *cb;
24628c2ecf20Sopenharmony_ci	struct tcf_block *block;
24638c2ecf20Sopenharmony_ci	struct Qdisc *q;
24648c2ecf20Sopenharmony_ci	u32 parent;
24658c2ecf20Sopenharmony_ci	bool terse_dump;
24668c2ecf20Sopenharmony_ci};
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_cistatic int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
24698c2ecf20Sopenharmony_ci{
24708c2ecf20Sopenharmony_ci	struct tcf_dump_args *a = (void *)arg;
24718c2ecf20Sopenharmony_ci	struct net *net = sock_net(a->skb->sk);
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ci	return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
24748c2ecf20Sopenharmony_ci			     n, NETLINK_CB(a->cb->skb).portid,
24758c2ecf20Sopenharmony_ci			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
24768c2ecf20Sopenharmony_ci			     RTM_NEWTFILTER, a->terse_dump, true);
24778c2ecf20Sopenharmony_ci}
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_cistatic bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
24808c2ecf20Sopenharmony_ci			   struct sk_buff *skb, struct netlink_callback *cb,
24818c2ecf20Sopenharmony_ci			   long index_start, long *p_index, bool terse)
24828c2ecf20Sopenharmony_ci{
24838c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
24848c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
24858c2ecf20Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
24868c2ecf20Sopenharmony_ci	struct tcf_proto *tp, *tp_prev;
24878c2ecf20Sopenharmony_ci	struct tcf_dump_args arg;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	for (tp = __tcf_get_next_proto(chain, NULL);
24908c2ecf20Sopenharmony_ci	     tp;
24918c2ecf20Sopenharmony_ci	     tp_prev = tp,
24928c2ecf20Sopenharmony_ci		     tp = __tcf_get_next_proto(chain, tp),
24938c2ecf20Sopenharmony_ci		     tcf_proto_put(tp_prev, true, NULL),
24948c2ecf20Sopenharmony_ci		     (*p_index)++) {
24958c2ecf20Sopenharmony_ci		if (*p_index < index_start)
24968c2ecf20Sopenharmony_ci			continue;
24978c2ecf20Sopenharmony_ci		if (TC_H_MAJ(tcm->tcm_info) &&
24988c2ecf20Sopenharmony_ci		    TC_H_MAJ(tcm->tcm_info) != tp->prio)
24998c2ecf20Sopenharmony_ci			continue;
25008c2ecf20Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_info) &&
25018c2ecf20Sopenharmony_ci		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
25028c2ecf20Sopenharmony_ci			continue;
25038c2ecf20Sopenharmony_ci		if (*p_index > index_start)
25048c2ecf20Sopenharmony_ci			memset(&cb->args[1], 0,
25058c2ecf20Sopenharmony_ci			       sizeof(cb->args) - sizeof(cb->args[0]));
25068c2ecf20Sopenharmony_ci		if (cb->args[1] == 0) {
25078c2ecf20Sopenharmony_ci			if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
25088c2ecf20Sopenharmony_ci					  NETLINK_CB(cb->skb).portid,
25098c2ecf20Sopenharmony_ci					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
25108c2ecf20Sopenharmony_ci					  RTM_NEWTFILTER, false, true) <= 0)
25118c2ecf20Sopenharmony_ci				goto errout;
25128c2ecf20Sopenharmony_ci			cb->args[1] = 1;
25138c2ecf20Sopenharmony_ci		}
25148c2ecf20Sopenharmony_ci		if (!tp->ops->walk)
25158c2ecf20Sopenharmony_ci			continue;
25168c2ecf20Sopenharmony_ci		arg.w.fn = tcf_node_dump;
25178c2ecf20Sopenharmony_ci		arg.skb = skb;
25188c2ecf20Sopenharmony_ci		arg.cb = cb;
25198c2ecf20Sopenharmony_ci		arg.block = block;
25208c2ecf20Sopenharmony_ci		arg.q = q;
25218c2ecf20Sopenharmony_ci		arg.parent = parent;
25228c2ecf20Sopenharmony_ci		arg.w.stop = 0;
25238c2ecf20Sopenharmony_ci		arg.w.skip = cb->args[1] - 1;
25248c2ecf20Sopenharmony_ci		arg.w.count = 0;
25258c2ecf20Sopenharmony_ci		arg.w.cookie = cb->args[2];
25268c2ecf20Sopenharmony_ci		arg.terse_dump = terse;
25278c2ecf20Sopenharmony_ci		tp->ops->walk(tp, &arg.w, true);
25288c2ecf20Sopenharmony_ci		cb->args[2] = arg.w.cookie;
25298c2ecf20Sopenharmony_ci		cb->args[1] = arg.w.count + 1;
25308c2ecf20Sopenharmony_ci		if (arg.w.stop)
25318c2ecf20Sopenharmony_ci			goto errout;
25328c2ecf20Sopenharmony_ci	}
25338c2ecf20Sopenharmony_ci	return true;
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_cierrout:
25368c2ecf20Sopenharmony_ci	tcf_proto_put(tp, true, NULL);
25378c2ecf20Sopenharmony_ci	return false;
25388c2ecf20Sopenharmony_ci}
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_cistatic const struct nla_policy tcf_tfilter_dump_policy[TCA_MAX + 1] = {
25418c2ecf20Sopenharmony_ci	[TCA_DUMP_FLAGS] = NLA_POLICY_BITFIELD32(TCA_DUMP_FLAGS_TERSE),
25428c2ecf20Sopenharmony_ci};
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci/* called with RTNL */
25458c2ecf20Sopenharmony_cistatic int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
25468c2ecf20Sopenharmony_ci{
25478c2ecf20Sopenharmony_ci	struct tcf_chain *chain, *chain_prev;
25488c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
25498c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
25508c2ecf20Sopenharmony_ci	struct Qdisc *q = NULL;
25518c2ecf20Sopenharmony_ci	struct tcf_block *block;
25528c2ecf20Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
25538c2ecf20Sopenharmony_ci	bool terse_dump = false;
25548c2ecf20Sopenharmony_ci	long index_start;
25558c2ecf20Sopenharmony_ci	long index;
25568c2ecf20Sopenharmony_ci	u32 parent;
25578c2ecf20Sopenharmony_ci	int err;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
25608c2ecf20Sopenharmony_ci		return skb->len;
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
25638c2ecf20Sopenharmony_ci				     tcf_tfilter_dump_policy, cb->extack);
25648c2ecf20Sopenharmony_ci	if (err)
25658c2ecf20Sopenharmony_ci		return err;
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	if (tca[TCA_DUMP_FLAGS]) {
25688c2ecf20Sopenharmony_ci		struct nla_bitfield32 flags =
25698c2ecf20Sopenharmony_ci			nla_get_bitfield32(tca[TCA_DUMP_FLAGS]);
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci		terse_dump = flags.value & TCA_DUMP_FLAGS_TERSE;
25728c2ecf20Sopenharmony_ci	}
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
25758c2ecf20Sopenharmony_ci		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
25768c2ecf20Sopenharmony_ci		if (!block)
25778c2ecf20Sopenharmony_ci			goto out;
25788c2ecf20Sopenharmony_ci		/* If we work with block index, q is NULL and parent value
25798c2ecf20Sopenharmony_ci		 * will never be used in the following code. The check
25808c2ecf20Sopenharmony_ci		 * in tcf_fill_node prevents it. However, compiler does not
25818c2ecf20Sopenharmony_ci		 * see that far, so set parent to zero to silence the warning
25828c2ecf20Sopenharmony_ci		 * about parent being uninitialized.
25838c2ecf20Sopenharmony_ci		 */
25848c2ecf20Sopenharmony_ci		parent = 0;
25858c2ecf20Sopenharmony_ci	} else {
25868c2ecf20Sopenharmony_ci		const struct Qdisc_class_ops *cops;
25878c2ecf20Sopenharmony_ci		struct net_device *dev;
25888c2ecf20Sopenharmony_ci		unsigned long cl = 0;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
25918c2ecf20Sopenharmony_ci		if (!dev)
25928c2ecf20Sopenharmony_ci			return skb->len;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci		parent = tcm->tcm_parent;
25958c2ecf20Sopenharmony_ci		if (!parent)
25968c2ecf20Sopenharmony_ci			q = rtnl_dereference(dev->qdisc);
25978c2ecf20Sopenharmony_ci		else
25988c2ecf20Sopenharmony_ci			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
25998c2ecf20Sopenharmony_ci		if (!q)
26008c2ecf20Sopenharmony_ci			goto out;
26018c2ecf20Sopenharmony_ci		cops = q->ops->cl_ops;
26028c2ecf20Sopenharmony_ci		if (!cops)
26038c2ecf20Sopenharmony_ci			goto out;
26048c2ecf20Sopenharmony_ci		if (!cops->tcf_block)
26058c2ecf20Sopenharmony_ci			goto out;
26068c2ecf20Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_parent)) {
26078c2ecf20Sopenharmony_ci			cl = cops->find(q, tcm->tcm_parent);
26088c2ecf20Sopenharmony_ci			if (cl == 0)
26098c2ecf20Sopenharmony_ci				goto out;
26108c2ecf20Sopenharmony_ci		}
26118c2ecf20Sopenharmony_ci		block = cops->tcf_block(q, cl, NULL);
26128c2ecf20Sopenharmony_ci		if (!block)
26138c2ecf20Sopenharmony_ci			goto out;
26148c2ecf20Sopenharmony_ci		parent = block->classid;
26158c2ecf20Sopenharmony_ci		if (tcf_block_shared(block))
26168c2ecf20Sopenharmony_ci			q = NULL;
26178c2ecf20Sopenharmony_ci	}
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	index_start = cb->args[0];
26208c2ecf20Sopenharmony_ci	index = 0;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	for (chain = __tcf_get_next_chain(block, NULL);
26238c2ecf20Sopenharmony_ci	     chain;
26248c2ecf20Sopenharmony_ci	     chain_prev = chain,
26258c2ecf20Sopenharmony_ci		     chain = __tcf_get_next_chain(block, chain),
26268c2ecf20Sopenharmony_ci		     tcf_chain_put(chain_prev)) {
26278c2ecf20Sopenharmony_ci		if (tca[TCA_CHAIN] &&
26288c2ecf20Sopenharmony_ci		    nla_get_u32(tca[TCA_CHAIN]) != chain->index)
26298c2ecf20Sopenharmony_ci			continue;
26308c2ecf20Sopenharmony_ci		if (!tcf_chain_dump(chain, q, parent, skb, cb,
26318c2ecf20Sopenharmony_ci				    index_start, &index, terse_dump)) {
26328c2ecf20Sopenharmony_ci			tcf_chain_put(chain);
26338c2ecf20Sopenharmony_ci			err = -EMSGSIZE;
26348c2ecf20Sopenharmony_ci			break;
26358c2ecf20Sopenharmony_ci		}
26368c2ecf20Sopenharmony_ci	}
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
26398c2ecf20Sopenharmony_ci		tcf_block_refcnt_put(block, true);
26408c2ecf20Sopenharmony_ci	cb->args[0] = index;
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ciout:
26438c2ecf20Sopenharmony_ci	/* If we did no progress, the error (EMSGSIZE) is real */
26448c2ecf20Sopenharmony_ci	if (skb->len == 0 && err)
26458c2ecf20Sopenharmony_ci		return err;
26468c2ecf20Sopenharmony_ci	return skb->len;
26478c2ecf20Sopenharmony_ci}
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_cistatic int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
26508c2ecf20Sopenharmony_ci			      void *tmplt_priv, u32 chain_index,
26518c2ecf20Sopenharmony_ci			      struct net *net, struct sk_buff *skb,
26528c2ecf20Sopenharmony_ci			      struct tcf_block *block,
26538c2ecf20Sopenharmony_ci			      u32 portid, u32 seq, u16 flags, int event)
26548c2ecf20Sopenharmony_ci{
26558c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
26568c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *ops;
26578c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
26588c2ecf20Sopenharmony_ci	struct tcmsg *tcm;
26598c2ecf20Sopenharmony_ci	void *priv;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	ops = tmplt_ops;
26628c2ecf20Sopenharmony_ci	priv = tmplt_priv;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
26658c2ecf20Sopenharmony_ci	if (!nlh)
26668c2ecf20Sopenharmony_ci		goto out_nlmsg_trim;
26678c2ecf20Sopenharmony_ci	tcm = nlmsg_data(nlh);
26688c2ecf20Sopenharmony_ci	tcm->tcm_family = AF_UNSPEC;
26698c2ecf20Sopenharmony_ci	tcm->tcm__pad1 = 0;
26708c2ecf20Sopenharmony_ci	tcm->tcm__pad2 = 0;
26718c2ecf20Sopenharmony_ci	tcm->tcm_handle = 0;
26728c2ecf20Sopenharmony_ci	if (block->q) {
26738c2ecf20Sopenharmony_ci		tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex;
26748c2ecf20Sopenharmony_ci		tcm->tcm_parent = block->q->handle;
26758c2ecf20Sopenharmony_ci	} else {
26768c2ecf20Sopenharmony_ci		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
26778c2ecf20Sopenharmony_ci		tcm->tcm_block_index = block->index;
26788c2ecf20Sopenharmony_ci	}
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, TCA_CHAIN, chain_index))
26818c2ecf20Sopenharmony_ci		goto nla_put_failure;
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci	if (ops) {
26848c2ecf20Sopenharmony_ci		if (nla_put_string(skb, TCA_KIND, ops->kind))
26858c2ecf20Sopenharmony_ci			goto nla_put_failure;
26868c2ecf20Sopenharmony_ci		if (ops->tmplt_dump(skb, net, priv) < 0)
26878c2ecf20Sopenharmony_ci			goto nla_put_failure;
26888c2ecf20Sopenharmony_ci	}
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
26918c2ecf20Sopenharmony_ci	return skb->len;
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ciout_nlmsg_trim:
26948c2ecf20Sopenharmony_cinla_put_failure:
26958c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
26968c2ecf20Sopenharmony_ci	return -EMSGSIZE;
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
27008c2ecf20Sopenharmony_ci			   u32 seq, u16 flags, int event, bool unicast)
27018c2ecf20Sopenharmony_ci{
27028c2ecf20Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
27038c2ecf20Sopenharmony_ci	struct tcf_block *block = chain->block;
27048c2ecf20Sopenharmony_ci	struct net *net = block->net;
27058c2ecf20Sopenharmony_ci	struct sk_buff *skb;
27068c2ecf20Sopenharmony_ci	int err = 0;
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
27098c2ecf20Sopenharmony_ci	if (!skb)
27108c2ecf20Sopenharmony_ci		return -ENOBUFS;
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci	if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
27138c2ecf20Sopenharmony_ci			       chain->index, net, skb, block, portid,
27148c2ecf20Sopenharmony_ci			       seq, flags, event) <= 0) {
27158c2ecf20Sopenharmony_ci		kfree_skb(skb);
27168c2ecf20Sopenharmony_ci		return -EINVAL;
27178c2ecf20Sopenharmony_ci	}
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	if (unicast)
27208c2ecf20Sopenharmony_ci		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
27218c2ecf20Sopenharmony_ci	else
27228c2ecf20Sopenharmony_ci		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
27238c2ecf20Sopenharmony_ci				     flags & NLM_F_ECHO);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci	if (err > 0)
27268c2ecf20Sopenharmony_ci		err = 0;
27278c2ecf20Sopenharmony_ci	return err;
27288c2ecf20Sopenharmony_ci}
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
27318c2ecf20Sopenharmony_ci				  void *tmplt_priv, u32 chain_index,
27328c2ecf20Sopenharmony_ci				  struct tcf_block *block, struct sk_buff *oskb,
27338c2ecf20Sopenharmony_ci				  u32 seq, u16 flags, bool unicast)
27348c2ecf20Sopenharmony_ci{
27358c2ecf20Sopenharmony_ci	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
27368c2ecf20Sopenharmony_ci	struct net *net = block->net;
27378c2ecf20Sopenharmony_ci	struct sk_buff *skb;
27388c2ecf20Sopenharmony_ci
27398c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
27408c2ecf20Sopenharmony_ci	if (!skb)
27418c2ecf20Sopenharmony_ci		return -ENOBUFS;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
27448c2ecf20Sopenharmony_ci			       block, portid, seq, flags, RTM_DELCHAIN) <= 0) {
27458c2ecf20Sopenharmony_ci		kfree_skb(skb);
27468c2ecf20Sopenharmony_ci		return -EINVAL;
27478c2ecf20Sopenharmony_ci	}
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	if (unicast)
27508c2ecf20Sopenharmony_ci		return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
27538c2ecf20Sopenharmony_ci}
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_cistatic int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
27568c2ecf20Sopenharmony_ci			      struct nlattr **tca,
27578c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
27588c2ecf20Sopenharmony_ci{
27598c2ecf20Sopenharmony_ci	const struct tcf_proto_ops *ops;
27608c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
27618c2ecf20Sopenharmony_ci	void *tmplt_priv;
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	/* If kind is not set, user did not specify template. */
27648c2ecf20Sopenharmony_ci	if (!tca[TCA_KIND])
27658c2ecf20Sopenharmony_ci		return 0;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
27688c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified TC chain template name too long");
27698c2ecf20Sopenharmony_ci		return -EINVAL;
27708c2ecf20Sopenharmony_ci	}
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_ci	ops = tcf_proto_lookup_ops(name, true, extack);
27738c2ecf20Sopenharmony_ci	if (IS_ERR(ops))
27748c2ecf20Sopenharmony_ci		return PTR_ERR(ops);
27758c2ecf20Sopenharmony_ci	if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) {
27768c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier");
27778c2ecf20Sopenharmony_ci		module_put(ops->owner);
27788c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
27798c2ecf20Sopenharmony_ci	}
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	tmplt_priv = ops->tmplt_create(net, chain, tca, extack);
27828c2ecf20Sopenharmony_ci	if (IS_ERR(tmplt_priv)) {
27838c2ecf20Sopenharmony_ci		module_put(ops->owner);
27848c2ecf20Sopenharmony_ci		return PTR_ERR(tmplt_priv);
27858c2ecf20Sopenharmony_ci	}
27868c2ecf20Sopenharmony_ci	chain->tmplt_ops = ops;
27878c2ecf20Sopenharmony_ci	chain->tmplt_priv = tmplt_priv;
27888c2ecf20Sopenharmony_ci	return 0;
27898c2ecf20Sopenharmony_ci}
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
27928c2ecf20Sopenharmony_ci			       void *tmplt_priv)
27938c2ecf20Sopenharmony_ci{
27948c2ecf20Sopenharmony_ci	/* If template ops are set, no work to do for us. */
27958c2ecf20Sopenharmony_ci	if (!tmplt_ops)
27968c2ecf20Sopenharmony_ci		return;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	tmplt_ops->tmplt_destroy(tmplt_priv);
27998c2ecf20Sopenharmony_ci	module_put(tmplt_ops->owner);
28008c2ecf20Sopenharmony_ci}
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci/* Add/delete/get a chain */
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_cistatic int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
28058c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
28068c2ecf20Sopenharmony_ci{
28078c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
28088c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
28098c2ecf20Sopenharmony_ci	struct tcmsg *t;
28108c2ecf20Sopenharmony_ci	u32 parent;
28118c2ecf20Sopenharmony_ci	u32 chain_index;
28128c2ecf20Sopenharmony_ci	struct Qdisc *q;
28138c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
28148c2ecf20Sopenharmony_ci	struct tcf_block *block;
28158c2ecf20Sopenharmony_ci	unsigned long cl;
28168c2ecf20Sopenharmony_ci	int err;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	if (n->nlmsg_type != RTM_GETCHAIN &&
28198c2ecf20Sopenharmony_ci	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
28208c2ecf20Sopenharmony_ci		return -EPERM;
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_cireplay:
28238c2ecf20Sopenharmony_ci	q = NULL;
28248c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
28258c2ecf20Sopenharmony_ci				     rtm_tca_policy, extack);
28268c2ecf20Sopenharmony_ci	if (err < 0)
28278c2ecf20Sopenharmony_ci		return err;
28288c2ecf20Sopenharmony_ci
28298c2ecf20Sopenharmony_ci	t = nlmsg_data(n);
28308c2ecf20Sopenharmony_ci	parent = t->tcm_parent;
28318c2ecf20Sopenharmony_ci	cl = 0;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	block = tcf_block_find(net, &q, &parent, &cl,
28348c2ecf20Sopenharmony_ci			       t->tcm_ifindex, t->tcm_block_index, extack);
28358c2ecf20Sopenharmony_ci	if (IS_ERR(block))
28368c2ecf20Sopenharmony_ci		return PTR_ERR(block);
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
28398c2ecf20Sopenharmony_ci	if (chain_index > TC_ACT_EXT_VAL_MASK) {
28408c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
28418c2ecf20Sopenharmony_ci		err = -EINVAL;
28428c2ecf20Sopenharmony_ci		goto errout_block;
28438c2ecf20Sopenharmony_ci	}
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
28468c2ecf20Sopenharmony_ci	chain = tcf_chain_lookup(block, chain_index);
28478c2ecf20Sopenharmony_ci	if (n->nlmsg_type == RTM_NEWCHAIN) {
28488c2ecf20Sopenharmony_ci		if (chain) {
28498c2ecf20Sopenharmony_ci			if (tcf_chain_held_by_acts_only(chain)) {
28508c2ecf20Sopenharmony_ci				/* The chain exists only because there is
28518c2ecf20Sopenharmony_ci				 * some action referencing it.
28528c2ecf20Sopenharmony_ci				 */
28538c2ecf20Sopenharmony_ci				tcf_chain_hold(chain);
28548c2ecf20Sopenharmony_ci			} else {
28558c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Filter chain already exists");
28568c2ecf20Sopenharmony_ci				err = -EEXIST;
28578c2ecf20Sopenharmony_ci				goto errout_block_locked;
28588c2ecf20Sopenharmony_ci			}
28598c2ecf20Sopenharmony_ci		} else {
28608c2ecf20Sopenharmony_ci			if (!(n->nlmsg_flags & NLM_F_CREATE)) {
28618c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
28628c2ecf20Sopenharmony_ci				err = -ENOENT;
28638c2ecf20Sopenharmony_ci				goto errout_block_locked;
28648c2ecf20Sopenharmony_ci			}
28658c2ecf20Sopenharmony_ci			chain = tcf_chain_create(block, chain_index);
28668c2ecf20Sopenharmony_ci			if (!chain) {
28678c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Failed to create filter chain");
28688c2ecf20Sopenharmony_ci				err = -ENOMEM;
28698c2ecf20Sopenharmony_ci				goto errout_block_locked;
28708c2ecf20Sopenharmony_ci			}
28718c2ecf20Sopenharmony_ci		}
28728c2ecf20Sopenharmony_ci	} else {
28738c2ecf20Sopenharmony_ci		if (!chain || tcf_chain_held_by_acts_only(chain)) {
28748c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
28758c2ecf20Sopenharmony_ci			err = -EINVAL;
28768c2ecf20Sopenharmony_ci			goto errout_block_locked;
28778c2ecf20Sopenharmony_ci		}
28788c2ecf20Sopenharmony_ci		tcf_chain_hold(chain);
28798c2ecf20Sopenharmony_ci	}
28808c2ecf20Sopenharmony_ci
28818c2ecf20Sopenharmony_ci	if (n->nlmsg_type == RTM_NEWCHAIN) {
28828c2ecf20Sopenharmony_ci		/* Modifying chain requires holding parent block lock. In case
28838c2ecf20Sopenharmony_ci		 * the chain was successfully added, take a reference to the
28848c2ecf20Sopenharmony_ci		 * chain. This ensures that an empty chain does not disappear at
28858c2ecf20Sopenharmony_ci		 * the end of this function.
28868c2ecf20Sopenharmony_ci		 */
28878c2ecf20Sopenharmony_ci		tcf_chain_hold(chain);
28888c2ecf20Sopenharmony_ci		chain->explicitly_created = true;
28898c2ecf20Sopenharmony_ci	}
28908c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci	switch (n->nlmsg_type) {
28938c2ecf20Sopenharmony_ci	case RTM_NEWCHAIN:
28948c2ecf20Sopenharmony_ci		err = tc_chain_tmplt_add(chain, net, tca, extack);
28958c2ecf20Sopenharmony_ci		if (err) {
28968c2ecf20Sopenharmony_ci			tcf_chain_put_explicitly_created(chain);
28978c2ecf20Sopenharmony_ci			goto errout;
28988c2ecf20Sopenharmony_ci		}
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
29018c2ecf20Sopenharmony_ci				RTM_NEWCHAIN, false);
29028c2ecf20Sopenharmony_ci		break;
29038c2ecf20Sopenharmony_ci	case RTM_DELCHAIN:
29048c2ecf20Sopenharmony_ci		tfilter_notify_chain(net, skb, block, q, parent, n,
29058c2ecf20Sopenharmony_ci				     chain, RTM_DELTFILTER, true);
29068c2ecf20Sopenharmony_ci		/* Flush the chain first as the user requested chain removal. */
29078c2ecf20Sopenharmony_ci		tcf_chain_flush(chain, true);
29088c2ecf20Sopenharmony_ci		/* In case the chain was successfully deleted, put a reference
29098c2ecf20Sopenharmony_ci		 * to the chain previously taken during addition.
29108c2ecf20Sopenharmony_ci		 */
29118c2ecf20Sopenharmony_ci		tcf_chain_put_explicitly_created(chain);
29128c2ecf20Sopenharmony_ci		break;
29138c2ecf20Sopenharmony_ci	case RTM_GETCHAIN:
29148c2ecf20Sopenharmony_ci		err = tc_chain_notify(chain, skb, n->nlmsg_seq,
29158c2ecf20Sopenharmony_ci				      n->nlmsg_flags, n->nlmsg_type, true);
29168c2ecf20Sopenharmony_ci		if (err < 0)
29178c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
29188c2ecf20Sopenharmony_ci		break;
29198c2ecf20Sopenharmony_ci	default:
29208c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
29218c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unsupported message type");
29228c2ecf20Sopenharmony_ci		goto errout;
29238c2ecf20Sopenharmony_ci	}
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_cierrout:
29268c2ecf20Sopenharmony_ci	tcf_chain_put(chain);
29278c2ecf20Sopenharmony_cierrout_block:
29288c2ecf20Sopenharmony_ci	tcf_block_release(q, block, true);
29298c2ecf20Sopenharmony_ci	if (err == -EAGAIN)
29308c2ecf20Sopenharmony_ci		/* Replay the request. */
29318c2ecf20Sopenharmony_ci		goto replay;
29328c2ecf20Sopenharmony_ci	return err;
29338c2ecf20Sopenharmony_ci
29348c2ecf20Sopenharmony_cierrout_block_locked:
29358c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
29368c2ecf20Sopenharmony_ci	goto errout_block;
29378c2ecf20Sopenharmony_ci}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci/* called with RTNL */
29408c2ecf20Sopenharmony_cistatic int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
29418c2ecf20Sopenharmony_ci{
29428c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
29438c2ecf20Sopenharmony_ci	struct nlattr *tca[TCA_MAX + 1];
29448c2ecf20Sopenharmony_ci	struct Qdisc *q = NULL;
29458c2ecf20Sopenharmony_ci	struct tcf_block *block;
29468c2ecf20Sopenharmony_ci	struct tcmsg *tcm = nlmsg_data(cb->nlh);
29478c2ecf20Sopenharmony_ci	struct tcf_chain *chain;
29488c2ecf20Sopenharmony_ci	long index_start;
29498c2ecf20Sopenharmony_ci	long index;
29508c2ecf20Sopenharmony_ci	u32 parent;
29518c2ecf20Sopenharmony_ci	int err;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
29548c2ecf20Sopenharmony_ci		return skb->len;
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
29578c2ecf20Sopenharmony_ci				     rtm_tca_policy, cb->extack);
29588c2ecf20Sopenharmony_ci	if (err)
29598c2ecf20Sopenharmony_ci		return err;
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
29628c2ecf20Sopenharmony_ci		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
29638c2ecf20Sopenharmony_ci		if (!block)
29648c2ecf20Sopenharmony_ci			goto out;
29658c2ecf20Sopenharmony_ci		/* If we work with block index, q is NULL and parent value
29668c2ecf20Sopenharmony_ci		 * will never be used in the following code. The check
29678c2ecf20Sopenharmony_ci		 * in tcf_fill_node prevents it. However, compiler does not
29688c2ecf20Sopenharmony_ci		 * see that far, so set parent to zero to silence the warning
29698c2ecf20Sopenharmony_ci		 * about parent being uninitialized.
29708c2ecf20Sopenharmony_ci		 */
29718c2ecf20Sopenharmony_ci		parent = 0;
29728c2ecf20Sopenharmony_ci	} else {
29738c2ecf20Sopenharmony_ci		const struct Qdisc_class_ops *cops;
29748c2ecf20Sopenharmony_ci		struct net_device *dev;
29758c2ecf20Sopenharmony_ci		unsigned long cl = 0;
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
29788c2ecf20Sopenharmony_ci		if (!dev)
29798c2ecf20Sopenharmony_ci			return skb->len;
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci		parent = tcm->tcm_parent;
29828c2ecf20Sopenharmony_ci		if (!parent) {
29838c2ecf20Sopenharmony_ci			q = rtnl_dereference(dev->qdisc);
29848c2ecf20Sopenharmony_ci			parent = q->handle;
29858c2ecf20Sopenharmony_ci		} else {
29868c2ecf20Sopenharmony_ci			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
29878c2ecf20Sopenharmony_ci		}
29888c2ecf20Sopenharmony_ci		if (!q)
29898c2ecf20Sopenharmony_ci			goto out;
29908c2ecf20Sopenharmony_ci		cops = q->ops->cl_ops;
29918c2ecf20Sopenharmony_ci		if (!cops)
29928c2ecf20Sopenharmony_ci			goto out;
29938c2ecf20Sopenharmony_ci		if (!cops->tcf_block)
29948c2ecf20Sopenharmony_ci			goto out;
29958c2ecf20Sopenharmony_ci		if (TC_H_MIN(tcm->tcm_parent)) {
29968c2ecf20Sopenharmony_ci			cl = cops->find(q, tcm->tcm_parent);
29978c2ecf20Sopenharmony_ci			if (cl == 0)
29988c2ecf20Sopenharmony_ci				goto out;
29998c2ecf20Sopenharmony_ci		}
30008c2ecf20Sopenharmony_ci		block = cops->tcf_block(q, cl, NULL);
30018c2ecf20Sopenharmony_ci		if (!block)
30028c2ecf20Sopenharmony_ci			goto out;
30038c2ecf20Sopenharmony_ci		if (tcf_block_shared(block))
30048c2ecf20Sopenharmony_ci			q = NULL;
30058c2ecf20Sopenharmony_ci	}
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_ci	index_start = cb->args[0];
30088c2ecf20Sopenharmony_ci	index = 0;
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	mutex_lock(&block->lock);
30118c2ecf20Sopenharmony_ci	list_for_each_entry(chain, &block->chain_list, list) {
30128c2ecf20Sopenharmony_ci		if ((tca[TCA_CHAIN] &&
30138c2ecf20Sopenharmony_ci		     nla_get_u32(tca[TCA_CHAIN]) != chain->index))
30148c2ecf20Sopenharmony_ci			continue;
30158c2ecf20Sopenharmony_ci		if (index < index_start) {
30168c2ecf20Sopenharmony_ci			index++;
30178c2ecf20Sopenharmony_ci			continue;
30188c2ecf20Sopenharmony_ci		}
30198c2ecf20Sopenharmony_ci		if (tcf_chain_held_by_acts_only(chain))
30208c2ecf20Sopenharmony_ci			continue;
30218c2ecf20Sopenharmony_ci		err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
30228c2ecf20Sopenharmony_ci					 chain->index, net, skb, block,
30238c2ecf20Sopenharmony_ci					 NETLINK_CB(cb->skb).portid,
30248c2ecf20Sopenharmony_ci					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
30258c2ecf20Sopenharmony_ci					 RTM_NEWCHAIN);
30268c2ecf20Sopenharmony_ci		if (err <= 0)
30278c2ecf20Sopenharmony_ci			break;
30288c2ecf20Sopenharmony_ci		index++;
30298c2ecf20Sopenharmony_ci	}
30308c2ecf20Sopenharmony_ci	mutex_unlock(&block->lock);
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_ci	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
30338c2ecf20Sopenharmony_ci		tcf_block_refcnt_put(block, true);
30348c2ecf20Sopenharmony_ci	cb->args[0] = index;
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ciout:
30378c2ecf20Sopenharmony_ci	/* If we did no progress, the error (EMSGSIZE) is real */
30388c2ecf20Sopenharmony_ci	if (skb->len == 0 && err)
30398c2ecf20Sopenharmony_ci		return err;
30408c2ecf20Sopenharmony_ci	return skb->len;
30418c2ecf20Sopenharmony_ci}
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_civoid tcf_exts_destroy(struct tcf_exts *exts)
30448c2ecf20Sopenharmony_ci{
30458c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
30468c2ecf20Sopenharmony_ci	if (exts->actions) {
30478c2ecf20Sopenharmony_ci		tcf_action_destroy(exts->actions, TCA_ACT_UNBIND);
30488c2ecf20Sopenharmony_ci		kfree(exts->actions);
30498c2ecf20Sopenharmony_ci	}
30508c2ecf20Sopenharmony_ci	exts->nr_actions = 0;
30518c2ecf20Sopenharmony_ci#endif
30528c2ecf20Sopenharmony_ci}
30538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_destroy);
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ciint tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
30568c2ecf20Sopenharmony_ci		      struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr,
30578c2ecf20Sopenharmony_ci		      bool rtnl_held, struct netlink_ext_ack *extack)
30588c2ecf20Sopenharmony_ci{
30598c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
30608c2ecf20Sopenharmony_ci	{
30618c2ecf20Sopenharmony_ci		int init_res[TCA_ACT_MAX_PRIO] = {};
30628c2ecf20Sopenharmony_ci		struct tc_action *act;
30638c2ecf20Sopenharmony_ci		size_t attr_size = 0;
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ci		if (exts->police && tb[exts->police]) {
30668c2ecf20Sopenharmony_ci			struct tc_action_ops *a_o;
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_ci			a_o = tc_action_load_ops("police", tb[exts->police], rtnl_held, extack);
30698c2ecf20Sopenharmony_ci			if (IS_ERR(a_o))
30708c2ecf20Sopenharmony_ci				return PTR_ERR(a_o);
30718c2ecf20Sopenharmony_ci			act = tcf_action_init_1(net, tp, tb[exts->police],
30728c2ecf20Sopenharmony_ci						rate_tlv, "police", ovr,
30738c2ecf20Sopenharmony_ci						TCA_ACT_BIND, a_o, init_res,
30748c2ecf20Sopenharmony_ci						rtnl_held, extack);
30758c2ecf20Sopenharmony_ci			module_put(a_o->owner);
30768c2ecf20Sopenharmony_ci			if (IS_ERR(act))
30778c2ecf20Sopenharmony_ci				return PTR_ERR(act);
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_ci			act->type = exts->type = TCA_OLD_COMPAT;
30808c2ecf20Sopenharmony_ci			exts->actions[0] = act;
30818c2ecf20Sopenharmony_ci			exts->nr_actions = 1;
30828c2ecf20Sopenharmony_ci			tcf_idr_insert_many(exts->actions);
30838c2ecf20Sopenharmony_ci		} else if (exts->action && tb[exts->action]) {
30848c2ecf20Sopenharmony_ci			int err;
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci			err = tcf_action_init(net, tp, tb[exts->action],
30878c2ecf20Sopenharmony_ci					      rate_tlv, NULL, ovr, TCA_ACT_BIND,
30888c2ecf20Sopenharmony_ci					      exts->actions, init_res,
30898c2ecf20Sopenharmony_ci					      &attr_size, rtnl_held, extack);
30908c2ecf20Sopenharmony_ci			if (err < 0)
30918c2ecf20Sopenharmony_ci				return err;
30928c2ecf20Sopenharmony_ci			exts->nr_actions = err;
30938c2ecf20Sopenharmony_ci		}
30948c2ecf20Sopenharmony_ci	}
30958c2ecf20Sopenharmony_ci#else
30968c2ecf20Sopenharmony_ci	if ((exts->action && tb[exts->action]) ||
30978c2ecf20Sopenharmony_ci	    (exts->police && tb[exts->police])) {
30988c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)");
30998c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
31008c2ecf20Sopenharmony_ci	}
31018c2ecf20Sopenharmony_ci#endif
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	return 0;
31048c2ecf20Sopenharmony_ci}
31058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_validate);
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_civoid tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src)
31088c2ecf20Sopenharmony_ci{
31098c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31108c2ecf20Sopenharmony_ci	struct tcf_exts old = *dst;
31118c2ecf20Sopenharmony_ci
31128c2ecf20Sopenharmony_ci	*dst = *src;
31138c2ecf20Sopenharmony_ci	tcf_exts_destroy(&old);
31148c2ecf20Sopenharmony_ci#endif
31158c2ecf20Sopenharmony_ci}
31168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_change);
31178c2ecf20Sopenharmony_ci
31188c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31198c2ecf20Sopenharmony_cistatic struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
31208c2ecf20Sopenharmony_ci{
31218c2ecf20Sopenharmony_ci	if (exts->nr_actions == 0)
31228c2ecf20Sopenharmony_ci		return NULL;
31238c2ecf20Sopenharmony_ci	else
31248c2ecf20Sopenharmony_ci		return exts->actions[0];
31258c2ecf20Sopenharmony_ci}
31268c2ecf20Sopenharmony_ci#endif
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ciint tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
31298c2ecf20Sopenharmony_ci{
31308c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31318c2ecf20Sopenharmony_ci	struct nlattr *nest;
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_ci	if (exts->action && tcf_exts_has_actions(exts)) {
31348c2ecf20Sopenharmony_ci		/*
31358c2ecf20Sopenharmony_ci		 * again for backward compatible mode - we want
31368c2ecf20Sopenharmony_ci		 * to work with both old and new modes of entering
31378c2ecf20Sopenharmony_ci		 * tc data even if iproute2  was newer - jhs
31388c2ecf20Sopenharmony_ci		 */
31398c2ecf20Sopenharmony_ci		if (exts->type != TCA_OLD_COMPAT) {
31408c2ecf20Sopenharmony_ci			nest = nla_nest_start_noflag(skb, exts->action);
31418c2ecf20Sopenharmony_ci			if (nest == NULL)
31428c2ecf20Sopenharmony_ci				goto nla_put_failure;
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci			if (tcf_action_dump(skb, exts->actions, 0, 0, false)
31458c2ecf20Sopenharmony_ci			    < 0)
31468c2ecf20Sopenharmony_ci				goto nla_put_failure;
31478c2ecf20Sopenharmony_ci			nla_nest_end(skb, nest);
31488c2ecf20Sopenharmony_ci		} else if (exts->police) {
31498c2ecf20Sopenharmony_ci			struct tc_action *act = tcf_exts_first_act(exts);
31508c2ecf20Sopenharmony_ci			nest = nla_nest_start_noflag(skb, exts->police);
31518c2ecf20Sopenharmony_ci			if (nest == NULL || !act)
31528c2ecf20Sopenharmony_ci				goto nla_put_failure;
31538c2ecf20Sopenharmony_ci			if (tcf_action_dump_old(skb, act, 0, 0) < 0)
31548c2ecf20Sopenharmony_ci				goto nla_put_failure;
31558c2ecf20Sopenharmony_ci			nla_nest_end(skb, nest);
31568c2ecf20Sopenharmony_ci		}
31578c2ecf20Sopenharmony_ci	}
31588c2ecf20Sopenharmony_ci	return 0;
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_cinla_put_failure:
31618c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
31628c2ecf20Sopenharmony_ci	return -1;
31638c2ecf20Sopenharmony_ci#else
31648c2ecf20Sopenharmony_ci	return 0;
31658c2ecf20Sopenharmony_ci#endif
31668c2ecf20Sopenharmony_ci}
31678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump);
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ciint tcf_exts_terse_dump(struct sk_buff *skb, struct tcf_exts *exts)
31708c2ecf20Sopenharmony_ci{
31718c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31728c2ecf20Sopenharmony_ci	struct nlattr *nest;
31738c2ecf20Sopenharmony_ci
31748c2ecf20Sopenharmony_ci	if (!exts->action || !tcf_exts_has_actions(exts))
31758c2ecf20Sopenharmony_ci		return 0;
31768c2ecf20Sopenharmony_ci
31778c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, exts->action);
31788c2ecf20Sopenharmony_ci	if (!nest)
31798c2ecf20Sopenharmony_ci		goto nla_put_failure;
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci	if (tcf_action_dump(skb, exts->actions, 0, 0, true) < 0)
31828c2ecf20Sopenharmony_ci		goto nla_put_failure;
31838c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
31848c2ecf20Sopenharmony_ci	return 0;
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_cinla_put_failure:
31878c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
31888c2ecf20Sopenharmony_ci	return -1;
31898c2ecf20Sopenharmony_ci#else
31908c2ecf20Sopenharmony_ci	return 0;
31918c2ecf20Sopenharmony_ci#endif
31928c2ecf20Sopenharmony_ci}
31938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_terse_dump);
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_ciint tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
31968c2ecf20Sopenharmony_ci{
31978c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31988c2ecf20Sopenharmony_ci	struct tc_action *a = tcf_exts_first_act(exts);
31998c2ecf20Sopenharmony_ci	if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0)
32008c2ecf20Sopenharmony_ci		return -1;
32018c2ecf20Sopenharmony_ci#endif
32028c2ecf20Sopenharmony_ci	return 0;
32038c2ecf20Sopenharmony_ci}
32048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump_stats);
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_cistatic void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
32078c2ecf20Sopenharmony_ci{
32088c2ecf20Sopenharmony_ci	if (*flags & TCA_CLS_FLAGS_IN_HW)
32098c2ecf20Sopenharmony_ci		return;
32108c2ecf20Sopenharmony_ci	*flags |= TCA_CLS_FLAGS_IN_HW;
32118c2ecf20Sopenharmony_ci	atomic_inc(&block->offloadcnt);
32128c2ecf20Sopenharmony_ci}
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_cistatic void tcf_block_offload_dec(struct tcf_block *block, u32 *flags)
32158c2ecf20Sopenharmony_ci{
32168c2ecf20Sopenharmony_ci	if (!(*flags & TCA_CLS_FLAGS_IN_HW))
32178c2ecf20Sopenharmony_ci		return;
32188c2ecf20Sopenharmony_ci	*flags &= ~TCA_CLS_FLAGS_IN_HW;
32198c2ecf20Sopenharmony_ci	atomic_dec(&block->offloadcnt);
32208c2ecf20Sopenharmony_ci}
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_cistatic void tc_cls_offload_cnt_update(struct tcf_block *block,
32238c2ecf20Sopenharmony_ci				      struct tcf_proto *tp, u32 *cnt,
32248c2ecf20Sopenharmony_ci				      u32 *flags, u32 diff, bool add)
32258c2ecf20Sopenharmony_ci{
32268c2ecf20Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci	spin_lock(&tp->lock);
32298c2ecf20Sopenharmony_ci	if (add) {
32308c2ecf20Sopenharmony_ci		if (!*cnt)
32318c2ecf20Sopenharmony_ci			tcf_block_offload_inc(block, flags);
32328c2ecf20Sopenharmony_ci		*cnt += diff;
32338c2ecf20Sopenharmony_ci	} else {
32348c2ecf20Sopenharmony_ci		*cnt -= diff;
32358c2ecf20Sopenharmony_ci		if (!*cnt)
32368c2ecf20Sopenharmony_ci			tcf_block_offload_dec(block, flags);
32378c2ecf20Sopenharmony_ci	}
32388c2ecf20Sopenharmony_ci	spin_unlock(&tp->lock);
32398c2ecf20Sopenharmony_ci}
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_cistatic void
32428c2ecf20Sopenharmony_citc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp,
32438c2ecf20Sopenharmony_ci			 u32 *cnt, u32 *flags)
32448c2ecf20Sopenharmony_ci{
32458c2ecf20Sopenharmony_ci	lockdep_assert_held(&block->cb_lock);
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci	spin_lock(&tp->lock);
32488c2ecf20Sopenharmony_ci	tcf_block_offload_dec(block, flags);
32498c2ecf20Sopenharmony_ci	*cnt = 0;
32508c2ecf20Sopenharmony_ci	spin_unlock(&tp->lock);
32518c2ecf20Sopenharmony_ci}
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_cistatic int
32548c2ecf20Sopenharmony_ci__tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
32558c2ecf20Sopenharmony_ci		   void *type_data, bool err_stop)
32568c2ecf20Sopenharmony_ci{
32578c2ecf20Sopenharmony_ci	struct flow_block_cb *block_cb;
32588c2ecf20Sopenharmony_ci	int ok_count = 0;
32598c2ecf20Sopenharmony_ci	int err;
32608c2ecf20Sopenharmony_ci
32618c2ecf20Sopenharmony_ci	list_for_each_entry(block_cb, &block->flow_block.cb_list, list) {
32628c2ecf20Sopenharmony_ci		err = block_cb->cb(type, type_data, block_cb->cb_priv);
32638c2ecf20Sopenharmony_ci		if (err) {
32648c2ecf20Sopenharmony_ci			if (err_stop)
32658c2ecf20Sopenharmony_ci				return err;
32668c2ecf20Sopenharmony_ci		} else {
32678c2ecf20Sopenharmony_ci			ok_count++;
32688c2ecf20Sopenharmony_ci		}
32698c2ecf20Sopenharmony_ci	}
32708c2ecf20Sopenharmony_ci	return ok_count;
32718c2ecf20Sopenharmony_ci}
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_ciint tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
32748c2ecf20Sopenharmony_ci		     void *type_data, bool err_stop, bool rtnl_held)
32758c2ecf20Sopenharmony_ci{
32768c2ecf20Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
32778c2ecf20Sopenharmony_ci	int ok_count;
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ciretry:
32808c2ecf20Sopenharmony_ci	if (take_rtnl)
32818c2ecf20Sopenharmony_ci		rtnl_lock();
32828c2ecf20Sopenharmony_ci	down_read(&block->cb_lock);
32838c2ecf20Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
32848c2ecf20Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
32858c2ecf20Sopenharmony_ci	 * obtain the locks in same order here.
32868c2ecf20Sopenharmony_ci	 */
32878c2ecf20Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
32888c2ecf20Sopenharmony_ci		up_read(&block->cb_lock);
32898c2ecf20Sopenharmony_ci		take_rtnl = true;
32908c2ecf20Sopenharmony_ci		goto retry;
32918c2ecf20Sopenharmony_ci	}
32928c2ecf20Sopenharmony_ci
32938c2ecf20Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
32948c2ecf20Sopenharmony_ci
32958c2ecf20Sopenharmony_ci	up_read(&block->cb_lock);
32968c2ecf20Sopenharmony_ci	if (take_rtnl)
32978c2ecf20Sopenharmony_ci		rtnl_unlock();
32988c2ecf20Sopenharmony_ci	return ok_count;
32998c2ecf20Sopenharmony_ci}
33008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_call);
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci/* Non-destructive filter add. If filter that wasn't already in hardware is
33038c2ecf20Sopenharmony_ci * successfully offloaded, increment block offloads counter. On failure,
33048c2ecf20Sopenharmony_ci * previously offloaded filter is considered to be intact and offloads counter
33058c2ecf20Sopenharmony_ci * is not decremented.
33068c2ecf20Sopenharmony_ci */
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ciint tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp,
33098c2ecf20Sopenharmony_ci		    enum tc_setup_type type, void *type_data, bool err_stop,
33108c2ecf20Sopenharmony_ci		    u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
33118c2ecf20Sopenharmony_ci{
33128c2ecf20Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
33138c2ecf20Sopenharmony_ci	int ok_count;
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ciretry:
33168c2ecf20Sopenharmony_ci	if (take_rtnl)
33178c2ecf20Sopenharmony_ci		rtnl_lock();
33188c2ecf20Sopenharmony_ci	down_read(&block->cb_lock);
33198c2ecf20Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
33208c2ecf20Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
33218c2ecf20Sopenharmony_ci	 * obtain the locks in same order here.
33228c2ecf20Sopenharmony_ci	 */
33238c2ecf20Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
33248c2ecf20Sopenharmony_ci		up_read(&block->cb_lock);
33258c2ecf20Sopenharmony_ci		take_rtnl = true;
33268c2ecf20Sopenharmony_ci		goto retry;
33278c2ecf20Sopenharmony_ci	}
33288c2ecf20Sopenharmony_ci
33298c2ecf20Sopenharmony_ci	/* Make sure all netdevs sharing this block are offload-capable. */
33308c2ecf20Sopenharmony_ci	if (block->nooffloaddevcnt && err_stop) {
33318c2ecf20Sopenharmony_ci		ok_count = -EOPNOTSUPP;
33328c2ecf20Sopenharmony_ci		goto err_unlock;
33338c2ecf20Sopenharmony_ci	}
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
33368c2ecf20Sopenharmony_ci	if (ok_count < 0)
33378c2ecf20Sopenharmony_ci		goto err_unlock;
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci	if (tp->ops->hw_add)
33408c2ecf20Sopenharmony_ci		tp->ops->hw_add(tp, type_data);
33418c2ecf20Sopenharmony_ci	if (ok_count > 0)
33428c2ecf20Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
33438c2ecf20Sopenharmony_ci					  ok_count, true);
33448c2ecf20Sopenharmony_cierr_unlock:
33458c2ecf20Sopenharmony_ci	up_read(&block->cb_lock);
33468c2ecf20Sopenharmony_ci	if (take_rtnl)
33478c2ecf20Sopenharmony_ci		rtnl_unlock();
33488c2ecf20Sopenharmony_ci	return ok_count < 0 ? ok_count : 0;
33498c2ecf20Sopenharmony_ci}
33508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_add);
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_ci/* Destructive filter replace. If filter that wasn't already in hardware is
33538c2ecf20Sopenharmony_ci * successfully offloaded, increment block offload counter. On failure,
33548c2ecf20Sopenharmony_ci * previously offloaded filter is considered to be destroyed and offload counter
33558c2ecf20Sopenharmony_ci * is decremented.
33568c2ecf20Sopenharmony_ci */
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_ciint tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp,
33598c2ecf20Sopenharmony_ci			enum tc_setup_type type, void *type_data, bool err_stop,
33608c2ecf20Sopenharmony_ci			u32 *old_flags, unsigned int *old_in_hw_count,
33618c2ecf20Sopenharmony_ci			u32 *new_flags, unsigned int *new_in_hw_count,
33628c2ecf20Sopenharmony_ci			bool rtnl_held)
33638c2ecf20Sopenharmony_ci{
33648c2ecf20Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
33658c2ecf20Sopenharmony_ci	int ok_count;
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_ciretry:
33688c2ecf20Sopenharmony_ci	if (take_rtnl)
33698c2ecf20Sopenharmony_ci		rtnl_lock();
33708c2ecf20Sopenharmony_ci	down_read(&block->cb_lock);
33718c2ecf20Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
33728c2ecf20Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
33738c2ecf20Sopenharmony_ci	 * obtain the locks in same order here.
33748c2ecf20Sopenharmony_ci	 */
33758c2ecf20Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
33768c2ecf20Sopenharmony_ci		up_read(&block->cb_lock);
33778c2ecf20Sopenharmony_ci		take_rtnl = true;
33788c2ecf20Sopenharmony_ci		goto retry;
33798c2ecf20Sopenharmony_ci	}
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_ci	/* Make sure all netdevs sharing this block are offload-capable. */
33828c2ecf20Sopenharmony_ci	if (block->nooffloaddevcnt && err_stop) {
33838c2ecf20Sopenharmony_ci		ok_count = -EOPNOTSUPP;
33848c2ecf20Sopenharmony_ci		goto err_unlock;
33858c2ecf20Sopenharmony_ci	}
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags);
33888c2ecf20Sopenharmony_ci	if (tp->ops->hw_del)
33898c2ecf20Sopenharmony_ci		tp->ops->hw_del(tp, type_data);
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
33928c2ecf20Sopenharmony_ci	if (ok_count < 0)
33938c2ecf20Sopenharmony_ci		goto err_unlock;
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci	if (tp->ops->hw_add)
33968c2ecf20Sopenharmony_ci		tp->ops->hw_add(tp, type_data);
33978c2ecf20Sopenharmony_ci	if (ok_count > 0)
33988c2ecf20Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, new_in_hw_count,
33998c2ecf20Sopenharmony_ci					  new_flags, ok_count, true);
34008c2ecf20Sopenharmony_cierr_unlock:
34018c2ecf20Sopenharmony_ci	up_read(&block->cb_lock);
34028c2ecf20Sopenharmony_ci	if (take_rtnl)
34038c2ecf20Sopenharmony_ci		rtnl_unlock();
34048c2ecf20Sopenharmony_ci	return ok_count < 0 ? ok_count : 0;
34058c2ecf20Sopenharmony_ci}
34068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_replace);
34078c2ecf20Sopenharmony_ci
34088c2ecf20Sopenharmony_ci/* Destroy filter and decrement block offload counter, if filter was previously
34098c2ecf20Sopenharmony_ci * offloaded.
34108c2ecf20Sopenharmony_ci */
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ciint tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp,
34138c2ecf20Sopenharmony_ci			enum tc_setup_type type, void *type_data, bool err_stop,
34148c2ecf20Sopenharmony_ci			u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
34158c2ecf20Sopenharmony_ci{
34168c2ecf20Sopenharmony_ci	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
34178c2ecf20Sopenharmony_ci	int ok_count;
34188c2ecf20Sopenharmony_ci
34198c2ecf20Sopenharmony_ciretry:
34208c2ecf20Sopenharmony_ci	if (take_rtnl)
34218c2ecf20Sopenharmony_ci		rtnl_lock();
34228c2ecf20Sopenharmony_ci	down_read(&block->cb_lock);
34238c2ecf20Sopenharmony_ci	/* Need to obtain rtnl lock if block is bound to devs that require it.
34248c2ecf20Sopenharmony_ci	 * In block bind code cb_lock is obtained while holding rtnl, so we must
34258c2ecf20Sopenharmony_ci	 * obtain the locks in same order here.
34268c2ecf20Sopenharmony_ci	 */
34278c2ecf20Sopenharmony_ci	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
34288c2ecf20Sopenharmony_ci		up_read(&block->cb_lock);
34298c2ecf20Sopenharmony_ci		take_rtnl = true;
34308c2ecf20Sopenharmony_ci		goto retry;
34318c2ecf20Sopenharmony_ci	}
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci	tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags);
34368c2ecf20Sopenharmony_ci	if (tp->ops->hw_del)
34378c2ecf20Sopenharmony_ci		tp->ops->hw_del(tp, type_data);
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci	up_read(&block->cb_lock);
34408c2ecf20Sopenharmony_ci	if (take_rtnl)
34418c2ecf20Sopenharmony_ci		rtnl_unlock();
34428c2ecf20Sopenharmony_ci	return ok_count < 0 ? ok_count : 0;
34438c2ecf20Sopenharmony_ci}
34448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_destroy);
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_ciint tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp,
34478c2ecf20Sopenharmony_ci			  bool add, flow_setup_cb_t *cb,
34488c2ecf20Sopenharmony_ci			  enum tc_setup_type type, void *type_data,
34498c2ecf20Sopenharmony_ci			  void *cb_priv, u32 *flags, unsigned int *in_hw_count)
34508c2ecf20Sopenharmony_ci{
34518c2ecf20Sopenharmony_ci	int err = cb(type, type_data, cb_priv);
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci	if (err) {
34548c2ecf20Sopenharmony_ci		if (add && tc_skip_sw(*flags))
34558c2ecf20Sopenharmony_ci			return err;
34568c2ecf20Sopenharmony_ci	} else {
34578c2ecf20Sopenharmony_ci		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1,
34588c2ecf20Sopenharmony_ci					  add);
34598c2ecf20Sopenharmony_ci	}
34608c2ecf20Sopenharmony_ci
34618c2ecf20Sopenharmony_ci	return 0;
34628c2ecf20Sopenharmony_ci}
34638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_reoffload);
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_cistatic int tcf_act_get_cookie(struct flow_action_entry *entry,
34668c2ecf20Sopenharmony_ci			      const struct tc_action *act)
34678c2ecf20Sopenharmony_ci{
34688c2ecf20Sopenharmony_ci	struct tc_cookie *cookie;
34698c2ecf20Sopenharmony_ci	int err = 0;
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci	rcu_read_lock();
34728c2ecf20Sopenharmony_ci	cookie = rcu_dereference(act->act_cookie);
34738c2ecf20Sopenharmony_ci	if (cookie) {
34748c2ecf20Sopenharmony_ci		entry->cookie = flow_action_cookie_create(cookie->data,
34758c2ecf20Sopenharmony_ci							  cookie->len,
34768c2ecf20Sopenharmony_ci							  GFP_ATOMIC);
34778c2ecf20Sopenharmony_ci		if (!entry->cookie)
34788c2ecf20Sopenharmony_ci			err = -ENOMEM;
34798c2ecf20Sopenharmony_ci	}
34808c2ecf20Sopenharmony_ci	rcu_read_unlock();
34818c2ecf20Sopenharmony_ci	return err;
34828c2ecf20Sopenharmony_ci}
34838c2ecf20Sopenharmony_ci
34848c2ecf20Sopenharmony_cistatic void tcf_act_put_cookie(struct flow_action_entry *entry)
34858c2ecf20Sopenharmony_ci{
34868c2ecf20Sopenharmony_ci	flow_action_cookie_destroy(entry->cookie);
34878c2ecf20Sopenharmony_ci}
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_civoid tc_cleanup_flow_action(struct flow_action *flow_action)
34908c2ecf20Sopenharmony_ci{
34918c2ecf20Sopenharmony_ci	struct flow_action_entry *entry;
34928c2ecf20Sopenharmony_ci	int i;
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci	flow_action_for_each(i, entry, flow_action) {
34958c2ecf20Sopenharmony_ci		tcf_act_put_cookie(entry);
34968c2ecf20Sopenharmony_ci		if (entry->destructor)
34978c2ecf20Sopenharmony_ci			entry->destructor(entry->destructor_priv);
34988c2ecf20Sopenharmony_ci	}
34998c2ecf20Sopenharmony_ci}
35008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_cleanup_flow_action);
35018c2ecf20Sopenharmony_ci
35028c2ecf20Sopenharmony_cistatic void tcf_mirred_get_dev(struct flow_action_entry *entry,
35038c2ecf20Sopenharmony_ci			       const struct tc_action *act)
35048c2ecf20Sopenharmony_ci{
35058c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
35068c2ecf20Sopenharmony_ci	entry->dev = act->ops->get_dev(act, &entry->destructor);
35078c2ecf20Sopenharmony_ci	if (!entry->dev)
35088c2ecf20Sopenharmony_ci		return;
35098c2ecf20Sopenharmony_ci	entry->destructor_priv = entry->dev;
35108c2ecf20Sopenharmony_ci#endif
35118c2ecf20Sopenharmony_ci}
35128c2ecf20Sopenharmony_ci
35138c2ecf20Sopenharmony_cistatic void tcf_tunnel_encap_put_tunnel(void *priv)
35148c2ecf20Sopenharmony_ci{
35158c2ecf20Sopenharmony_ci	struct ip_tunnel_info *tunnel = priv;
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_ci	kfree(tunnel);
35188c2ecf20Sopenharmony_ci}
35198c2ecf20Sopenharmony_ci
35208c2ecf20Sopenharmony_cistatic int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry,
35218c2ecf20Sopenharmony_ci				       const struct tc_action *act)
35228c2ecf20Sopenharmony_ci{
35238c2ecf20Sopenharmony_ci	entry->tunnel = tcf_tunnel_info_copy(act);
35248c2ecf20Sopenharmony_ci	if (!entry->tunnel)
35258c2ecf20Sopenharmony_ci		return -ENOMEM;
35268c2ecf20Sopenharmony_ci	entry->destructor = tcf_tunnel_encap_put_tunnel;
35278c2ecf20Sopenharmony_ci	entry->destructor_priv = entry->tunnel;
35288c2ecf20Sopenharmony_ci	return 0;
35298c2ecf20Sopenharmony_ci}
35308c2ecf20Sopenharmony_ci
35318c2ecf20Sopenharmony_cistatic void tcf_sample_get_group(struct flow_action_entry *entry,
35328c2ecf20Sopenharmony_ci				 const struct tc_action *act)
35338c2ecf20Sopenharmony_ci{
35348c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
35358c2ecf20Sopenharmony_ci	entry->sample.psample_group =
35368c2ecf20Sopenharmony_ci		act->ops->get_psample_group(act, &entry->destructor);
35378c2ecf20Sopenharmony_ci	entry->destructor_priv = entry->sample.psample_group;
35388c2ecf20Sopenharmony_ci#endif
35398c2ecf20Sopenharmony_ci}
35408c2ecf20Sopenharmony_ci
35418c2ecf20Sopenharmony_cistatic void tcf_gate_entry_destructor(void *priv)
35428c2ecf20Sopenharmony_ci{
35438c2ecf20Sopenharmony_ci	struct action_gate_entry *oe = priv;
35448c2ecf20Sopenharmony_ci
35458c2ecf20Sopenharmony_ci	kfree(oe);
35468c2ecf20Sopenharmony_ci}
35478c2ecf20Sopenharmony_ci
35488c2ecf20Sopenharmony_cistatic int tcf_gate_get_entries(struct flow_action_entry *entry,
35498c2ecf20Sopenharmony_ci				const struct tc_action *act)
35508c2ecf20Sopenharmony_ci{
35518c2ecf20Sopenharmony_ci	entry->gate.entries = tcf_gate_get_list(act);
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci	if (!entry->gate.entries)
35548c2ecf20Sopenharmony_ci		return -EINVAL;
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_ci	entry->destructor = tcf_gate_entry_destructor;
35578c2ecf20Sopenharmony_ci	entry->destructor_priv = entry->gate.entries;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	return 0;
35608c2ecf20Sopenharmony_ci}
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_cistatic enum flow_action_hw_stats tc_act_hw_stats(u8 hw_stats)
35638c2ecf20Sopenharmony_ci{
35648c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(hw_stats > TCA_ACT_HW_STATS_ANY))
35658c2ecf20Sopenharmony_ci		return FLOW_ACTION_HW_STATS_DONT_CARE;
35668c2ecf20Sopenharmony_ci	else if (!hw_stats)
35678c2ecf20Sopenharmony_ci		return FLOW_ACTION_HW_STATS_DISABLED;
35688c2ecf20Sopenharmony_ci
35698c2ecf20Sopenharmony_ci	return hw_stats;
35708c2ecf20Sopenharmony_ci}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ciint tc_setup_flow_action(struct flow_action *flow_action,
35738c2ecf20Sopenharmony_ci			 const struct tcf_exts *exts)
35748c2ecf20Sopenharmony_ci{
35758c2ecf20Sopenharmony_ci	struct tc_action *act;
35768c2ecf20Sopenharmony_ci	int i, j, k, err = 0;
35778c2ecf20Sopenharmony_ci
35788c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_ANY != FLOW_ACTION_HW_STATS_ANY);
35798c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE);
35808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED);
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_ci	if (!exts)
35838c2ecf20Sopenharmony_ci		return 0;
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	j = 0;
35868c2ecf20Sopenharmony_ci	tcf_exts_for_each_action(i, act, exts) {
35878c2ecf20Sopenharmony_ci		struct flow_action_entry *entry;
35888c2ecf20Sopenharmony_ci
35898c2ecf20Sopenharmony_ci		entry = &flow_action->entries[j];
35908c2ecf20Sopenharmony_ci		spin_lock_bh(&act->tcfa_lock);
35918c2ecf20Sopenharmony_ci		err = tcf_act_get_cookie(entry, act);
35928c2ecf20Sopenharmony_ci		if (err)
35938c2ecf20Sopenharmony_ci			goto err_out_locked;
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ci		entry->hw_stats = tc_act_hw_stats(act->hw_stats);
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci		if (is_tcf_gact_ok(act)) {
35988c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_ACCEPT;
35998c2ecf20Sopenharmony_ci		} else if (is_tcf_gact_shot(act)) {
36008c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_DROP;
36018c2ecf20Sopenharmony_ci		} else if (is_tcf_gact_trap(act)) {
36028c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_TRAP;
36038c2ecf20Sopenharmony_ci		} else if (is_tcf_gact_goto_chain(act)) {
36048c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_GOTO;
36058c2ecf20Sopenharmony_ci			entry->chain_index = tcf_gact_goto_chain_index(act);
36068c2ecf20Sopenharmony_ci		} else if (is_tcf_mirred_egress_redirect(act)) {
36078c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_REDIRECT;
36088c2ecf20Sopenharmony_ci			tcf_mirred_get_dev(entry, act);
36098c2ecf20Sopenharmony_ci		} else if (is_tcf_mirred_egress_mirror(act)) {
36108c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_MIRRED;
36118c2ecf20Sopenharmony_ci			tcf_mirred_get_dev(entry, act);
36128c2ecf20Sopenharmony_ci		} else if (is_tcf_mirred_ingress_redirect(act)) {
36138c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_REDIRECT_INGRESS;
36148c2ecf20Sopenharmony_ci			tcf_mirred_get_dev(entry, act);
36158c2ecf20Sopenharmony_ci		} else if (is_tcf_mirred_ingress_mirror(act)) {
36168c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_MIRRED_INGRESS;
36178c2ecf20Sopenharmony_ci			tcf_mirred_get_dev(entry, act);
36188c2ecf20Sopenharmony_ci		} else if (is_tcf_vlan(act)) {
36198c2ecf20Sopenharmony_ci			switch (tcf_vlan_action(act)) {
36208c2ecf20Sopenharmony_ci			case TCA_VLAN_ACT_PUSH:
36218c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_VLAN_PUSH;
36228c2ecf20Sopenharmony_ci				entry->vlan.vid = tcf_vlan_push_vid(act);
36238c2ecf20Sopenharmony_ci				entry->vlan.proto = tcf_vlan_push_proto(act);
36248c2ecf20Sopenharmony_ci				entry->vlan.prio = tcf_vlan_push_prio(act);
36258c2ecf20Sopenharmony_ci				break;
36268c2ecf20Sopenharmony_ci			case TCA_VLAN_ACT_POP:
36278c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_VLAN_POP;
36288c2ecf20Sopenharmony_ci				break;
36298c2ecf20Sopenharmony_ci			case TCA_VLAN_ACT_MODIFY:
36308c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_VLAN_MANGLE;
36318c2ecf20Sopenharmony_ci				entry->vlan.vid = tcf_vlan_push_vid(act);
36328c2ecf20Sopenharmony_ci				entry->vlan.proto = tcf_vlan_push_proto(act);
36338c2ecf20Sopenharmony_ci				entry->vlan.prio = tcf_vlan_push_prio(act);
36348c2ecf20Sopenharmony_ci				break;
36358c2ecf20Sopenharmony_ci			default:
36368c2ecf20Sopenharmony_ci				err = -EOPNOTSUPP;
36378c2ecf20Sopenharmony_ci				goto err_out_locked;
36388c2ecf20Sopenharmony_ci			}
36398c2ecf20Sopenharmony_ci		} else if (is_tcf_tunnel_set(act)) {
36408c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_TUNNEL_ENCAP;
36418c2ecf20Sopenharmony_ci			err = tcf_tunnel_encap_get_tunnel(entry, act);
36428c2ecf20Sopenharmony_ci			if (err)
36438c2ecf20Sopenharmony_ci				goto err_out_locked;
36448c2ecf20Sopenharmony_ci		} else if (is_tcf_tunnel_release(act)) {
36458c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_TUNNEL_DECAP;
36468c2ecf20Sopenharmony_ci		} else if (is_tcf_pedit(act)) {
36478c2ecf20Sopenharmony_ci			for (k = 0; k < tcf_pedit_nkeys(act); k++) {
36488c2ecf20Sopenharmony_ci				switch (tcf_pedit_cmd(act, k)) {
36498c2ecf20Sopenharmony_ci				case TCA_PEDIT_KEY_EX_CMD_SET:
36508c2ecf20Sopenharmony_ci					entry->id = FLOW_ACTION_MANGLE;
36518c2ecf20Sopenharmony_ci					break;
36528c2ecf20Sopenharmony_ci				case TCA_PEDIT_KEY_EX_CMD_ADD:
36538c2ecf20Sopenharmony_ci					entry->id = FLOW_ACTION_ADD;
36548c2ecf20Sopenharmony_ci					break;
36558c2ecf20Sopenharmony_ci				default:
36568c2ecf20Sopenharmony_ci					err = -EOPNOTSUPP;
36578c2ecf20Sopenharmony_ci					goto err_out_locked;
36588c2ecf20Sopenharmony_ci				}
36598c2ecf20Sopenharmony_ci				entry->mangle.htype = tcf_pedit_htype(act, k);
36608c2ecf20Sopenharmony_ci				entry->mangle.mask = tcf_pedit_mask(act, k);
36618c2ecf20Sopenharmony_ci				entry->mangle.val = tcf_pedit_val(act, k);
36628c2ecf20Sopenharmony_ci				entry->mangle.offset = tcf_pedit_offset(act, k);
36638c2ecf20Sopenharmony_ci				entry->hw_stats = tc_act_hw_stats(act->hw_stats);
36648c2ecf20Sopenharmony_ci				entry = &flow_action->entries[++j];
36658c2ecf20Sopenharmony_ci			}
36668c2ecf20Sopenharmony_ci		} else if (is_tcf_csum(act)) {
36678c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_CSUM;
36688c2ecf20Sopenharmony_ci			entry->csum_flags = tcf_csum_update_flags(act);
36698c2ecf20Sopenharmony_ci		} else if (is_tcf_skbedit_mark(act)) {
36708c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_MARK;
36718c2ecf20Sopenharmony_ci			entry->mark = tcf_skbedit_mark(act);
36728c2ecf20Sopenharmony_ci		} else if (is_tcf_sample(act)) {
36738c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_SAMPLE;
36748c2ecf20Sopenharmony_ci			entry->sample.trunc_size = tcf_sample_trunc_size(act);
36758c2ecf20Sopenharmony_ci			entry->sample.truncate = tcf_sample_truncate(act);
36768c2ecf20Sopenharmony_ci			entry->sample.rate = tcf_sample_rate(act);
36778c2ecf20Sopenharmony_ci			tcf_sample_get_group(entry, act);
36788c2ecf20Sopenharmony_ci		} else if (is_tcf_police(act)) {
36798c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_POLICE;
36808c2ecf20Sopenharmony_ci			entry->police.burst = tcf_police_burst(act);
36818c2ecf20Sopenharmony_ci			entry->police.rate_bytes_ps =
36828c2ecf20Sopenharmony_ci				tcf_police_rate_bytes_ps(act);
36838c2ecf20Sopenharmony_ci			entry->police.mtu = tcf_police_tcfp_mtu(act);
36848c2ecf20Sopenharmony_ci			entry->police.index = act->tcfa_index;
36858c2ecf20Sopenharmony_ci		} else if (is_tcf_ct(act)) {
36868c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_CT;
36878c2ecf20Sopenharmony_ci			entry->ct.action = tcf_ct_action(act);
36888c2ecf20Sopenharmony_ci			entry->ct.zone = tcf_ct_zone(act);
36898c2ecf20Sopenharmony_ci			entry->ct.flow_table = tcf_ct_ft(act);
36908c2ecf20Sopenharmony_ci		} else if (is_tcf_mpls(act)) {
36918c2ecf20Sopenharmony_ci			switch (tcf_mpls_action(act)) {
36928c2ecf20Sopenharmony_ci			case TCA_MPLS_ACT_PUSH:
36938c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_MPLS_PUSH;
36948c2ecf20Sopenharmony_ci				entry->mpls_push.proto = tcf_mpls_proto(act);
36958c2ecf20Sopenharmony_ci				entry->mpls_push.label = tcf_mpls_label(act);
36968c2ecf20Sopenharmony_ci				entry->mpls_push.tc = tcf_mpls_tc(act);
36978c2ecf20Sopenharmony_ci				entry->mpls_push.bos = tcf_mpls_bos(act);
36988c2ecf20Sopenharmony_ci				entry->mpls_push.ttl = tcf_mpls_ttl(act);
36998c2ecf20Sopenharmony_ci				break;
37008c2ecf20Sopenharmony_ci			case TCA_MPLS_ACT_POP:
37018c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_MPLS_POP;
37028c2ecf20Sopenharmony_ci				entry->mpls_pop.proto = tcf_mpls_proto(act);
37038c2ecf20Sopenharmony_ci				break;
37048c2ecf20Sopenharmony_ci			case TCA_MPLS_ACT_MODIFY:
37058c2ecf20Sopenharmony_ci				entry->id = FLOW_ACTION_MPLS_MANGLE;
37068c2ecf20Sopenharmony_ci				entry->mpls_mangle.label = tcf_mpls_label(act);
37078c2ecf20Sopenharmony_ci				entry->mpls_mangle.tc = tcf_mpls_tc(act);
37088c2ecf20Sopenharmony_ci				entry->mpls_mangle.bos = tcf_mpls_bos(act);
37098c2ecf20Sopenharmony_ci				entry->mpls_mangle.ttl = tcf_mpls_ttl(act);
37108c2ecf20Sopenharmony_ci				break;
37118c2ecf20Sopenharmony_ci			default:
37128c2ecf20Sopenharmony_ci				err = -EOPNOTSUPP;
37138c2ecf20Sopenharmony_ci				goto err_out_locked;
37148c2ecf20Sopenharmony_ci			}
37158c2ecf20Sopenharmony_ci		} else if (is_tcf_skbedit_ptype(act)) {
37168c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_PTYPE;
37178c2ecf20Sopenharmony_ci			entry->ptype = tcf_skbedit_ptype(act);
37188c2ecf20Sopenharmony_ci		} else if (is_tcf_skbedit_priority(act)) {
37198c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_PRIORITY;
37208c2ecf20Sopenharmony_ci			entry->priority = tcf_skbedit_priority(act);
37218c2ecf20Sopenharmony_ci		} else if (is_tcf_gate(act)) {
37228c2ecf20Sopenharmony_ci			entry->id = FLOW_ACTION_GATE;
37238c2ecf20Sopenharmony_ci			entry->gate.index = tcf_gate_index(act);
37248c2ecf20Sopenharmony_ci			entry->gate.prio = tcf_gate_prio(act);
37258c2ecf20Sopenharmony_ci			entry->gate.basetime = tcf_gate_basetime(act);
37268c2ecf20Sopenharmony_ci			entry->gate.cycletime = tcf_gate_cycletime(act);
37278c2ecf20Sopenharmony_ci			entry->gate.cycletimeext = tcf_gate_cycletimeext(act);
37288c2ecf20Sopenharmony_ci			entry->gate.num_entries = tcf_gate_num_entries(act);
37298c2ecf20Sopenharmony_ci			err = tcf_gate_get_entries(entry, act);
37308c2ecf20Sopenharmony_ci			if (err)
37318c2ecf20Sopenharmony_ci				goto err_out_locked;
37328c2ecf20Sopenharmony_ci		} else {
37338c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
37348c2ecf20Sopenharmony_ci			goto err_out_locked;
37358c2ecf20Sopenharmony_ci		}
37368c2ecf20Sopenharmony_ci		spin_unlock_bh(&act->tcfa_lock);
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci		if (!is_tcf_pedit(act))
37398c2ecf20Sopenharmony_ci			j++;
37408c2ecf20Sopenharmony_ci	}
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_cierr_out:
37438c2ecf20Sopenharmony_ci	if (err)
37448c2ecf20Sopenharmony_ci		tc_cleanup_flow_action(flow_action);
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_ci	return err;
37478c2ecf20Sopenharmony_cierr_out_locked:
37488c2ecf20Sopenharmony_ci	spin_unlock_bh(&act->tcfa_lock);
37498c2ecf20Sopenharmony_ci	goto err_out;
37508c2ecf20Sopenharmony_ci}
37518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tc_setup_flow_action);
37528c2ecf20Sopenharmony_ci
37538c2ecf20Sopenharmony_ciunsigned int tcf_exts_num_actions(struct tcf_exts *exts)
37548c2ecf20Sopenharmony_ci{
37558c2ecf20Sopenharmony_ci	unsigned int num_acts = 0;
37568c2ecf20Sopenharmony_ci	struct tc_action *act;
37578c2ecf20Sopenharmony_ci	int i;
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_ci	tcf_exts_for_each_action(i, act, exts) {
37608c2ecf20Sopenharmony_ci		if (is_tcf_pedit(act))
37618c2ecf20Sopenharmony_ci			num_acts += tcf_pedit_nkeys(act);
37628c2ecf20Sopenharmony_ci		else
37638c2ecf20Sopenharmony_ci			num_acts++;
37648c2ecf20Sopenharmony_ci	}
37658c2ecf20Sopenharmony_ci	return num_acts;
37668c2ecf20Sopenharmony_ci}
37678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_num_actions);
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
37708c2ecf20Sopenharmony_cistatic int tcf_qevent_parse_block_index(struct nlattr *block_index_attr,
37718c2ecf20Sopenharmony_ci					u32 *p_block_index,
37728c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
37738c2ecf20Sopenharmony_ci{
37748c2ecf20Sopenharmony_ci	*p_block_index = nla_get_u32(block_index_attr);
37758c2ecf20Sopenharmony_ci	if (!*p_block_index) {
37768c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Block number may not be zero");
37778c2ecf20Sopenharmony_ci		return -EINVAL;
37788c2ecf20Sopenharmony_ci	}
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_ci	return 0;
37818c2ecf20Sopenharmony_ci}
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ciint tcf_qevent_init(struct tcf_qevent *qe, struct Qdisc *sch,
37848c2ecf20Sopenharmony_ci		    enum flow_block_binder_type binder_type,
37858c2ecf20Sopenharmony_ci		    struct nlattr *block_index_attr,
37868c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack)
37878c2ecf20Sopenharmony_ci{
37888c2ecf20Sopenharmony_ci	u32 block_index;
37898c2ecf20Sopenharmony_ci	int err;
37908c2ecf20Sopenharmony_ci
37918c2ecf20Sopenharmony_ci	if (!block_index_attr)
37928c2ecf20Sopenharmony_ci		return 0;
37938c2ecf20Sopenharmony_ci
37948c2ecf20Sopenharmony_ci	err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack);
37958c2ecf20Sopenharmony_ci	if (err)
37968c2ecf20Sopenharmony_ci		return err;
37978c2ecf20Sopenharmony_ci
37988c2ecf20Sopenharmony_ci	if (!block_index)
37998c2ecf20Sopenharmony_ci		return 0;
38008c2ecf20Sopenharmony_ci
38018c2ecf20Sopenharmony_ci	qe->info.binder_type = binder_type;
38028c2ecf20Sopenharmony_ci	qe->info.chain_head_change = tcf_chain_head_change_dflt;
38038c2ecf20Sopenharmony_ci	qe->info.chain_head_change_priv = &qe->filter_chain;
38048c2ecf20Sopenharmony_ci	qe->info.block_index = block_index;
38058c2ecf20Sopenharmony_ci
38068c2ecf20Sopenharmony_ci	return tcf_block_get_ext(&qe->block, sch, &qe->info, extack);
38078c2ecf20Sopenharmony_ci}
38088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_init);
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_civoid tcf_qevent_destroy(struct tcf_qevent *qe, struct Qdisc *sch)
38118c2ecf20Sopenharmony_ci{
38128c2ecf20Sopenharmony_ci	if (qe->info.block_index)
38138c2ecf20Sopenharmony_ci		tcf_block_put_ext(qe->block, sch, &qe->info);
38148c2ecf20Sopenharmony_ci}
38158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_destroy);
38168c2ecf20Sopenharmony_ci
38178c2ecf20Sopenharmony_ciint tcf_qevent_validate_change(struct tcf_qevent *qe, struct nlattr *block_index_attr,
38188c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
38198c2ecf20Sopenharmony_ci{
38208c2ecf20Sopenharmony_ci	u32 block_index;
38218c2ecf20Sopenharmony_ci	int err;
38228c2ecf20Sopenharmony_ci
38238c2ecf20Sopenharmony_ci	if (!block_index_attr)
38248c2ecf20Sopenharmony_ci		return 0;
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci	err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack);
38278c2ecf20Sopenharmony_ci	if (err)
38288c2ecf20Sopenharmony_ci		return err;
38298c2ecf20Sopenharmony_ci
38308c2ecf20Sopenharmony_ci	/* Bounce newly-configured block or change in block. */
38318c2ecf20Sopenharmony_ci	if (block_index != qe->info.block_index) {
38328c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Change of blocks is not supported");
38338c2ecf20Sopenharmony_ci		return -EINVAL;
38348c2ecf20Sopenharmony_ci	}
38358c2ecf20Sopenharmony_ci
38368c2ecf20Sopenharmony_ci	return 0;
38378c2ecf20Sopenharmony_ci}
38388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_validate_change);
38398c2ecf20Sopenharmony_ci
38408c2ecf20Sopenharmony_cistruct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, struct sk_buff *skb,
38418c2ecf20Sopenharmony_ci				  struct sk_buff **to_free, int *ret)
38428c2ecf20Sopenharmony_ci{
38438c2ecf20Sopenharmony_ci	struct tcf_result cl_res;
38448c2ecf20Sopenharmony_ci	struct tcf_proto *fl;
38458c2ecf20Sopenharmony_ci
38468c2ecf20Sopenharmony_ci	if (!qe->info.block_index)
38478c2ecf20Sopenharmony_ci		return skb;
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	fl = rcu_dereference_bh(qe->filter_chain);
38508c2ecf20Sopenharmony_ci
38518c2ecf20Sopenharmony_ci	switch (tcf_classify(skb, fl, &cl_res, false)) {
38528c2ecf20Sopenharmony_ci	case TC_ACT_SHOT:
38538c2ecf20Sopenharmony_ci		qdisc_qstats_drop(sch);
38548c2ecf20Sopenharmony_ci		__qdisc_drop(skb, to_free);
38558c2ecf20Sopenharmony_ci		*ret = __NET_XMIT_BYPASS;
38568c2ecf20Sopenharmony_ci		return NULL;
38578c2ecf20Sopenharmony_ci	case TC_ACT_STOLEN:
38588c2ecf20Sopenharmony_ci	case TC_ACT_QUEUED:
38598c2ecf20Sopenharmony_ci	case TC_ACT_TRAP:
38608c2ecf20Sopenharmony_ci		__qdisc_drop(skb, to_free);
38618c2ecf20Sopenharmony_ci		*ret = __NET_XMIT_STOLEN;
38628c2ecf20Sopenharmony_ci		return NULL;
38638c2ecf20Sopenharmony_ci	case TC_ACT_REDIRECT:
38648c2ecf20Sopenharmony_ci		skb_do_redirect(skb);
38658c2ecf20Sopenharmony_ci		*ret = __NET_XMIT_STOLEN;
38668c2ecf20Sopenharmony_ci		return NULL;
38678c2ecf20Sopenharmony_ci	}
38688c2ecf20Sopenharmony_ci
38698c2ecf20Sopenharmony_ci	return skb;
38708c2ecf20Sopenharmony_ci}
38718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_handle);
38728c2ecf20Sopenharmony_ci
38738c2ecf20Sopenharmony_ciint tcf_qevent_dump(struct sk_buff *skb, int attr_name, struct tcf_qevent *qe)
38748c2ecf20Sopenharmony_ci{
38758c2ecf20Sopenharmony_ci	if (!qe->info.block_index)
38768c2ecf20Sopenharmony_ci		return 0;
38778c2ecf20Sopenharmony_ci	return nla_put_u32(skb, attr_name, qe->info.block_index);
38788c2ecf20Sopenharmony_ci}
38798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_dump);
38808c2ecf20Sopenharmony_ci#endif
38818c2ecf20Sopenharmony_ci
38828c2ecf20Sopenharmony_cistatic __net_init int tcf_net_init(struct net *net)
38838c2ecf20Sopenharmony_ci{
38848c2ecf20Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	spin_lock_init(&tn->idr_lock);
38878c2ecf20Sopenharmony_ci	idr_init(&tn->idr);
38888c2ecf20Sopenharmony_ci	return 0;
38898c2ecf20Sopenharmony_ci}
38908c2ecf20Sopenharmony_ci
38918c2ecf20Sopenharmony_cistatic void __net_exit tcf_net_exit(struct net *net)
38928c2ecf20Sopenharmony_ci{
38938c2ecf20Sopenharmony_ci	struct tcf_net *tn = net_generic(net, tcf_net_id);
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_ci	idr_destroy(&tn->idr);
38968c2ecf20Sopenharmony_ci}
38978c2ecf20Sopenharmony_ci
38988c2ecf20Sopenharmony_cistatic struct pernet_operations tcf_net_ops = {
38998c2ecf20Sopenharmony_ci	.init = tcf_net_init,
39008c2ecf20Sopenharmony_ci	.exit = tcf_net_exit,
39018c2ecf20Sopenharmony_ci	.id   = &tcf_net_id,
39028c2ecf20Sopenharmony_ci	.size = sizeof(struct tcf_net),
39038c2ecf20Sopenharmony_ci};
39048c2ecf20Sopenharmony_ci
39058c2ecf20Sopenharmony_cistatic int __init tc_filter_init(void)
39068c2ecf20Sopenharmony_ci{
39078c2ecf20Sopenharmony_ci	int err;
39088c2ecf20Sopenharmony_ci
39098c2ecf20Sopenharmony_ci	tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0);
39108c2ecf20Sopenharmony_ci	if (!tc_filter_wq)
39118c2ecf20Sopenharmony_ci		return -ENOMEM;
39128c2ecf20Sopenharmony_ci
39138c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&tcf_net_ops);
39148c2ecf20Sopenharmony_ci	if (err)
39158c2ecf20Sopenharmony_ci		goto err_register_pernet_subsys;
39168c2ecf20Sopenharmony_ci
39178c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL,
39188c2ecf20Sopenharmony_ci		      RTNL_FLAG_DOIT_UNLOCKED);
39198c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL,
39208c2ecf20Sopenharmony_ci		      RTNL_FLAG_DOIT_UNLOCKED);
39218c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
39228c2ecf20Sopenharmony_ci		      tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
39238c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
39248c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
39258c2ecf20Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
39268c2ecf20Sopenharmony_ci		      tc_dump_chain, 0);
39278c2ecf20Sopenharmony_ci
39288c2ecf20Sopenharmony_ci	return 0;
39298c2ecf20Sopenharmony_ci
39308c2ecf20Sopenharmony_cierr_register_pernet_subsys:
39318c2ecf20Sopenharmony_ci	destroy_workqueue(tc_filter_wq);
39328c2ecf20Sopenharmony_ci	return err;
39338c2ecf20Sopenharmony_ci}
39348c2ecf20Sopenharmony_ci
39358c2ecf20Sopenharmony_cisubsys_initcall(tc_filter_init);
3936