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