12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * net/sched/cls_api.c Packet classifier API. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Changes: 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/types.h> 141da177e4SLinus Torvalds #include <linux/kernel.h> 151da177e4SLinus Torvalds #include <linux/string.h> 161da177e4SLinus Torvalds #include <linux/errno.h> 1733a48927SJiri Pirko #include <linux/err.h> 181da177e4SLinus Torvalds #include <linux/skbuff.h> 191da177e4SLinus Torvalds #include <linux/init.h> 201da177e4SLinus Torvalds #include <linux/kmod.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 2248617387SJiri Pirko #include <linux/idr.h> 237f76fa36SJohn Hurley #include <linux/rhashtable.h> 2459eb87cbSJohn Hurley #include <linux/jhash.h> 2543719298SPaul Blakey #include <linux/rculist.h> 26b854272bSDenis V. Lunev #include <net/net_namespace.h> 27b854272bSDenis V. Lunev #include <net/sock.h> 28dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 291da177e4SLinus Torvalds #include <net/pkt_sched.h> 301da177e4SLinus Torvalds #include <net/pkt_cls.h> 31e3ab786bSPablo Neira Ayuso #include <net/tc_act/tc_pedit.h> 323a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_mirred.h> 333a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_vlan.h> 343a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_tunnel_key.h> 353a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_csum.h> 363a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_gact.h> 378c8cfc6eSPieter Jansen van Vuuren #include <net/tc_act/tc_police.h> 38a7a7be60SPieter Jansen van Vuuren #include <net/tc_act/tc_sample.h> 393a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_skbedit.h> 40b57dc7c1SPaul Blakey #include <net/tc_act/tc_ct.h> 416749d590SJohn Hurley #include <net/tc_act/tc_mpls.h> 42d29bdd69SPo Liu #include <net/tc_act/tc_gate.h> 434e481908Swenxu #include <net/flow_offload.h> 441da177e4SLinus Torvalds 45e331473fSDavide Caratti extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; 46e331473fSDavide Caratti 471da177e4SLinus Torvalds /* The list of all installed classifier types */ 4836272874SWANG Cong static LIST_HEAD(tcf_proto_base); 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */ 511da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock); 521da177e4SLinus Torvalds 5359eb87cbSJohn Hurley static u32 destroy_obj_hashfn(const struct tcf_proto *tp) 5459eb87cbSJohn Hurley { 5559eb87cbSJohn Hurley return jhash_3words(tp->chain->index, tp->prio, 5659eb87cbSJohn Hurley (__force __u32)tp->protocol, 0); 5759eb87cbSJohn Hurley } 5859eb87cbSJohn Hurley 5959eb87cbSJohn Hurley static void tcf_proto_signal_destroying(struct tcf_chain *chain, 6059eb87cbSJohn Hurley struct tcf_proto *tp) 6159eb87cbSJohn Hurley { 6259eb87cbSJohn Hurley struct tcf_block *block = chain->block; 6359eb87cbSJohn Hurley 6459eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 6559eb87cbSJohn Hurley hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node, 6659eb87cbSJohn Hurley destroy_obj_hashfn(tp)); 6759eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 6859eb87cbSJohn Hurley } 6959eb87cbSJohn Hurley 7059eb87cbSJohn Hurley static bool tcf_proto_cmp(const struct tcf_proto *tp1, 7159eb87cbSJohn Hurley const struct tcf_proto *tp2) 7259eb87cbSJohn Hurley { 7359eb87cbSJohn Hurley return tp1->chain->index == tp2->chain->index && 7459eb87cbSJohn Hurley tp1->prio == tp2->prio && 7559eb87cbSJohn Hurley tp1->protocol == tp2->protocol; 7659eb87cbSJohn Hurley } 7759eb87cbSJohn Hurley 7859eb87cbSJohn Hurley static bool tcf_proto_exists_destroying(struct tcf_chain *chain, 7959eb87cbSJohn Hurley struct tcf_proto *tp) 8059eb87cbSJohn Hurley { 8159eb87cbSJohn Hurley u32 hash = destroy_obj_hashfn(tp); 8259eb87cbSJohn Hurley struct tcf_proto *iter; 8359eb87cbSJohn Hurley bool found = false; 8459eb87cbSJohn Hurley 8559eb87cbSJohn Hurley rcu_read_lock(); 8659eb87cbSJohn Hurley hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter, 8759eb87cbSJohn Hurley destroy_ht_node, hash) { 8859eb87cbSJohn Hurley if (tcf_proto_cmp(tp, iter)) { 8959eb87cbSJohn Hurley found = true; 9059eb87cbSJohn Hurley break; 9159eb87cbSJohn Hurley } 9259eb87cbSJohn Hurley } 9359eb87cbSJohn Hurley rcu_read_unlock(); 9459eb87cbSJohn Hurley 9559eb87cbSJohn Hurley return found; 9659eb87cbSJohn Hurley } 9759eb87cbSJohn Hurley 9859eb87cbSJohn Hurley static void 9959eb87cbSJohn Hurley tcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp) 10059eb87cbSJohn Hurley { 10159eb87cbSJohn Hurley struct tcf_block *block = chain->block; 10259eb87cbSJohn Hurley 10359eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 10459eb87cbSJohn Hurley if (hash_hashed(&tp->destroy_ht_node)) 10559eb87cbSJohn Hurley hash_del_rcu(&tp->destroy_ht_node); 10659eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 10759eb87cbSJohn Hurley } 10859eb87cbSJohn Hurley 1091da177e4SLinus Torvalds /* Find classifier type by string name */ 1101da177e4SLinus Torvalds 111f34e8bffSJiri Pirko static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind) 1121da177e4SLinus Torvalds { 113dcd76081SEric Dumazet const struct tcf_proto_ops *t, *res = NULL; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if (kind) { 1161da177e4SLinus Torvalds read_lock(&cls_mod_lock); 11736272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) { 11833a48927SJiri Pirko if (strcmp(kind, t->kind) == 0) { 119dcd76081SEric Dumazet if (try_module_get(t->owner)) 120dcd76081SEric Dumazet res = t; 1211da177e4SLinus Torvalds break; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds read_unlock(&cls_mod_lock); 1251da177e4SLinus Torvalds } 126dcd76081SEric Dumazet return res; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 129f34e8bffSJiri Pirko static const struct tcf_proto_ops * 13012db03b6SVlad Buslov tcf_proto_lookup_ops(const char *kind, bool rtnl_held, 13112db03b6SVlad Buslov struct netlink_ext_ack *extack) 132f34e8bffSJiri Pirko { 133f34e8bffSJiri Pirko const struct tcf_proto_ops *ops; 134f34e8bffSJiri Pirko 135f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 136f34e8bffSJiri Pirko if (ops) 137f34e8bffSJiri Pirko return ops; 138f34e8bffSJiri Pirko #ifdef CONFIG_MODULES 13912db03b6SVlad Buslov if (rtnl_held) 140f34e8bffSJiri Pirko rtnl_unlock(); 141f34e8bffSJiri Pirko request_module("cls_%s", kind); 14212db03b6SVlad Buslov if (rtnl_held) 143f34e8bffSJiri Pirko rtnl_lock(); 144f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 145f34e8bffSJiri Pirko /* We dropped the RTNL semaphore in order to perform 146f34e8bffSJiri Pirko * the module load. So, even if we succeeded in loading 147f34e8bffSJiri Pirko * the module we have to replay the request. We indicate 148f34e8bffSJiri Pirko * this using -EAGAIN. 149f34e8bffSJiri Pirko */ 150f34e8bffSJiri Pirko if (ops) { 151f34e8bffSJiri Pirko module_put(ops->owner); 152f34e8bffSJiri Pirko return ERR_PTR(-EAGAIN); 153f34e8bffSJiri Pirko } 154f34e8bffSJiri Pirko #endif 155f34e8bffSJiri Pirko NL_SET_ERR_MSG(extack, "TC classifier not found"); 156f34e8bffSJiri Pirko return ERR_PTR(-ENOENT); 157f34e8bffSJiri Pirko } 158f34e8bffSJiri Pirko 1591da177e4SLinus Torvalds /* Register(unregister) new classifier type */ 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops) 1621da177e4SLinus Torvalds { 16336272874SWANG Cong struct tcf_proto_ops *t; 1641da177e4SLinus Torvalds int rc = -EEXIST; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds write_lock(&cls_mod_lock); 16736272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) 1681da177e4SLinus Torvalds if (!strcmp(ops->kind, t->kind)) 1691da177e4SLinus Torvalds goto out; 1701da177e4SLinus Torvalds 17136272874SWANG Cong list_add_tail(&ops->head, &tcf_proto_base); 1721da177e4SLinus Torvalds rc = 0; 1731da177e4SLinus Torvalds out: 1741da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1751da177e4SLinus Torvalds return rc; 1761da177e4SLinus Torvalds } 177aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops); 1781da177e4SLinus Torvalds 1797aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq; 1807aa0045dSCong Wang 1811da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 1821da177e4SLinus Torvalds { 18336272874SWANG Cong struct tcf_proto_ops *t; 1841da177e4SLinus Torvalds int rc = -ENOENT; 1851da177e4SLinus Torvalds 186c78e1746SDaniel Borkmann /* Wait for outstanding call_rcu()s, if any, from a 187c78e1746SDaniel Borkmann * tcf_proto_ops's destroy() handler. 188c78e1746SDaniel Borkmann */ 189c78e1746SDaniel Borkmann rcu_barrier(); 1907aa0045dSCong Wang flush_workqueue(tc_filter_wq); 191c78e1746SDaniel Borkmann 1921da177e4SLinus Torvalds write_lock(&cls_mod_lock); 193dcd76081SEric Dumazet list_for_each_entry(t, &tcf_proto_base, head) { 194dcd76081SEric Dumazet if (t == ops) { 19536272874SWANG Cong list_del(&t->head); 1961da177e4SLinus Torvalds rc = 0; 197dcd76081SEric Dumazet break; 198dcd76081SEric Dumazet } 199dcd76081SEric Dumazet } 2001da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 2011da177e4SLinus Torvalds return rc; 2021da177e4SLinus Torvalds } 203aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops); 2041da177e4SLinus Torvalds 205aaa908ffSCong Wang bool tcf_queue_work(struct rcu_work *rwork, work_func_t func) 2067aa0045dSCong Wang { 207aaa908ffSCong Wang INIT_RCU_WORK(rwork, func); 208aaa908ffSCong Wang return queue_rcu_work(tc_filter_wq, rwork); 2097aa0045dSCong Wang } 2107aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work); 2117aa0045dSCong Wang 2121da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */ 2131da177e4SLinus Torvalds 214aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp) 2151da177e4SLinus Torvalds { 2161da177e4SLinus Torvalds u32 first = TC_H_MAKE(0xC0000000U, 0U); 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds if (tp) 2191da177e4SLinus Torvalds first = tp->prio - 1; 2201da177e4SLinus Torvalds 2217961973aSJiri Pirko return TC_H_MAJ(first); 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2246f96c3c6SCong Wang static bool tcf_proto_check_kind(struct nlattr *kind, char *name) 2256f96c3c6SCong Wang { 2266f96c3c6SCong Wang if (kind) 2276f96c3c6SCong Wang return nla_strlcpy(name, kind, IFNAMSIZ) >= IFNAMSIZ; 2286f96c3c6SCong Wang memset(name, 0, IFNAMSIZ); 2296f96c3c6SCong Wang return false; 2306f96c3c6SCong Wang } 2316f96c3c6SCong Wang 232470502deSVlad Buslov static bool tcf_proto_is_unlocked(const char *kind) 233470502deSVlad Buslov { 234470502deSVlad Buslov const struct tcf_proto_ops *ops; 235470502deSVlad Buslov bool ret; 236470502deSVlad Buslov 2376f96c3c6SCong Wang if (strlen(kind) == 0) 2386f96c3c6SCong Wang return false; 2396f96c3c6SCong Wang 240470502deSVlad Buslov ops = tcf_proto_lookup_ops(kind, false, NULL); 241470502deSVlad Buslov /* On error return false to take rtnl lock. Proto lookup/create 242470502deSVlad Buslov * functions will perform lookup again and properly handle errors. 243470502deSVlad Buslov */ 244470502deSVlad Buslov if (IS_ERR(ops)) 245470502deSVlad Buslov return false; 246470502deSVlad Buslov 247470502deSVlad Buslov ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); 248470502deSVlad Buslov module_put(ops->owner); 249470502deSVlad Buslov return ret; 250470502deSVlad Buslov } 251470502deSVlad Buslov 25233a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 253c35a4accSAlexander Aring u32 prio, struct tcf_chain *chain, 25412db03b6SVlad Buslov bool rtnl_held, 255c35a4accSAlexander Aring struct netlink_ext_ack *extack) 25633a48927SJiri Pirko { 25733a48927SJiri Pirko struct tcf_proto *tp; 25833a48927SJiri Pirko int err; 25933a48927SJiri Pirko 26033a48927SJiri Pirko tp = kzalloc(sizeof(*tp), GFP_KERNEL); 26133a48927SJiri Pirko if (!tp) 26233a48927SJiri Pirko return ERR_PTR(-ENOBUFS); 26333a48927SJiri Pirko 26412db03b6SVlad Buslov tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack); 265f34e8bffSJiri Pirko if (IS_ERR(tp->ops)) { 266f34e8bffSJiri Pirko err = PTR_ERR(tp->ops); 267d68d75fdSJiri Pirko goto errout; 26833a48927SJiri Pirko } 26933a48927SJiri Pirko tp->classify = tp->ops->classify; 27033a48927SJiri Pirko tp->protocol = protocol; 27133a48927SJiri Pirko tp->prio = prio; 2725bc17018SJiri Pirko tp->chain = chain; 2738b64678eSVlad Buslov spin_lock_init(&tp->lock); 2744dbfa766SVlad Buslov refcount_set(&tp->refcnt, 1); 27533a48927SJiri Pirko 27633a48927SJiri Pirko err = tp->ops->init(tp); 27733a48927SJiri Pirko if (err) { 27833a48927SJiri Pirko module_put(tp->ops->owner); 27933a48927SJiri Pirko goto errout; 28033a48927SJiri Pirko } 28133a48927SJiri Pirko return tp; 28233a48927SJiri Pirko 28333a48927SJiri Pirko errout: 28433a48927SJiri Pirko kfree(tp); 28533a48927SJiri Pirko return ERR_PTR(err); 28633a48927SJiri Pirko } 28733a48927SJiri Pirko 2884dbfa766SVlad Buslov static void tcf_proto_get(struct tcf_proto *tp) 2894dbfa766SVlad Buslov { 2904dbfa766SVlad Buslov refcount_inc(&tp->refcnt); 2914dbfa766SVlad Buslov } 2924dbfa766SVlad Buslov 2934dbfa766SVlad Buslov static void tcf_chain_put(struct tcf_chain *chain); 2944dbfa766SVlad Buslov 29512db03b6SVlad Buslov static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, 29659eb87cbSJohn Hurley bool sig_destroy, struct netlink_ext_ack *extack) 297cf1facdaSJiri Pirko { 29812db03b6SVlad Buslov tp->ops->destroy(tp, rtnl_held, extack); 29959eb87cbSJohn Hurley if (sig_destroy) 30059eb87cbSJohn Hurley tcf_proto_signal_destroyed(tp->chain, tp); 3014dbfa766SVlad Buslov tcf_chain_put(tp->chain); 302cf1facdaSJiri Pirko module_put(tp->ops->owner); 303cf1facdaSJiri Pirko kfree_rcu(tp, rcu); 304cf1facdaSJiri Pirko } 305cf1facdaSJiri Pirko 30612db03b6SVlad Buslov static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held, 3074dbfa766SVlad Buslov struct netlink_ext_ack *extack) 3084dbfa766SVlad Buslov { 3094dbfa766SVlad Buslov if (refcount_dec_and_test(&tp->refcnt)) 31059eb87cbSJohn Hurley tcf_proto_destroy(tp, rtnl_held, true, extack); 3114dbfa766SVlad Buslov } 3124dbfa766SVlad Buslov 313a5b72a08SDavide Caratti static bool tcf_proto_check_delete(struct tcf_proto *tp) 3148b64678eSVlad Buslov { 315a5b72a08SDavide Caratti if (tp->ops->delete_empty) 316a5b72a08SDavide Caratti return tp->ops->delete_empty(tp); 3178b64678eSVlad Buslov 3188b64678eSVlad Buslov tp->deleting = true; 3198b64678eSVlad Buslov return tp->deleting; 3208b64678eSVlad Buslov } 3218b64678eSVlad Buslov 3228b64678eSVlad Buslov static void tcf_proto_mark_delete(struct tcf_proto *tp) 3238b64678eSVlad Buslov { 3248b64678eSVlad Buslov spin_lock(&tp->lock); 3258b64678eSVlad Buslov tp->deleting = true; 3268b64678eSVlad Buslov spin_unlock(&tp->lock); 3278b64678eSVlad Buslov } 3288b64678eSVlad Buslov 3298b64678eSVlad Buslov static bool tcf_proto_is_deleting(struct tcf_proto *tp) 3308b64678eSVlad Buslov { 3318b64678eSVlad Buslov bool deleting; 3328b64678eSVlad Buslov 3338b64678eSVlad Buslov spin_lock(&tp->lock); 3348b64678eSVlad Buslov deleting = tp->deleting; 3358b64678eSVlad Buslov spin_unlock(&tp->lock); 3368b64678eSVlad Buslov 3378b64678eSVlad Buslov return deleting; 3388b64678eSVlad Buslov } 3398b64678eSVlad Buslov 340c266f64dSVlad Buslov #define ASSERT_BLOCK_LOCKED(block) \ 341c266f64dSVlad Buslov lockdep_assert_held(&(block)->lock) 342c266f64dSVlad Buslov 343a9b19443SJiri Pirko struct tcf_filter_chain_list_item { 344a9b19443SJiri Pirko struct list_head list; 345a9b19443SJiri Pirko tcf_chain_head_change_t *chain_head_change; 346a9b19443SJiri Pirko void *chain_head_change_priv; 347a9b19443SJiri Pirko }; 348a9b19443SJiri Pirko 3495bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block, 3505bc17018SJiri Pirko u32 chain_index) 3512190d1d0SJiri Pirko { 3525bc17018SJiri Pirko struct tcf_chain *chain; 3535bc17018SJiri Pirko 354c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 355c266f64dSVlad Buslov 3565bc17018SJiri Pirko chain = kzalloc(sizeof(*chain), GFP_KERNEL); 3575bc17018SJiri Pirko if (!chain) 3585bc17018SJiri Pirko return NULL; 35943719298SPaul Blakey list_add_tail_rcu(&chain->list, &block->chain_list); 360ed76f5edSVlad Buslov mutex_init(&chain->filter_chain_lock); 3615bc17018SJiri Pirko chain->block = block; 3625bc17018SJiri Pirko chain->index = chain_index; 363e2ef7544SCong Wang chain->refcnt = 1; 364f71e0ca4SJiri Pirko if (!chain->index) 365f71e0ca4SJiri Pirko block->chain0.chain = chain; 3665bc17018SJiri Pirko return chain; 3672190d1d0SJiri Pirko } 3682190d1d0SJiri Pirko 369a9b19443SJiri Pirko static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, 370a9b19443SJiri Pirko struct tcf_proto *tp_head) 371a9b19443SJiri Pirko { 372a9b19443SJiri Pirko if (item->chain_head_change) 373a9b19443SJiri Pirko item->chain_head_change(tp_head, item->chain_head_change_priv); 374a9b19443SJiri Pirko } 375f71e0ca4SJiri Pirko 376f71e0ca4SJiri Pirko static void tcf_chain0_head_change(struct tcf_chain *chain, 377c7eb7d72SJiri Pirko struct tcf_proto *tp_head) 378c7eb7d72SJiri Pirko { 379a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 380f71e0ca4SJiri Pirko struct tcf_block *block = chain->block; 381a9b19443SJiri Pirko 382f71e0ca4SJiri Pirko if (chain->index) 383f71e0ca4SJiri Pirko return; 384165f0135SVlad Buslov 385165f0135SVlad Buslov mutex_lock(&block->lock); 386f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) 387a9b19443SJiri Pirko tcf_chain_head_change_item(item, tp_head); 388165f0135SVlad Buslov mutex_unlock(&block->lock); 389c7eb7d72SJiri Pirko } 390c7eb7d72SJiri Pirko 391c266f64dSVlad Buslov /* Returns true if block can be safely freed. */ 392c266f64dSVlad Buslov 393c266f64dSVlad Buslov static bool tcf_chain_detach(struct tcf_chain *chain) 394f93e1cdcSJiri Pirko { 395efbf7897SCong Wang struct tcf_block *block = chain->block; 396efbf7897SCong Wang 397c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 398c266f64dSVlad Buslov 39943719298SPaul Blakey list_del_rcu(&chain->list); 400f71e0ca4SJiri Pirko if (!chain->index) 401f71e0ca4SJiri Pirko block->chain0.chain = NULL; 402c266f64dSVlad Buslov 403c266f64dSVlad Buslov if (list_empty(&block->chain_list) && 404c266f64dSVlad Buslov refcount_read(&block->refcnt) == 0) 405c266f64dSVlad Buslov return true; 406c266f64dSVlad Buslov 407c266f64dSVlad Buslov return false; 408c266f64dSVlad Buslov } 409c266f64dSVlad Buslov 410c266f64dSVlad Buslov static void tcf_block_destroy(struct tcf_block *block) 411c266f64dSVlad Buslov { 412c266f64dSVlad Buslov mutex_destroy(&block->lock); 41359eb87cbSJohn Hurley mutex_destroy(&block->proto_destroy_lock); 4140607e439SVlad Buslov kfree_rcu(block, rcu); 4152190d1d0SJiri Pirko } 4162190d1d0SJiri Pirko 417c266f64dSVlad Buslov static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block) 418c266f64dSVlad Buslov { 419c266f64dSVlad Buslov struct tcf_block *block = chain->block; 420c266f64dSVlad Buslov 421ed76f5edSVlad Buslov mutex_destroy(&chain->filter_chain_lock); 422ee3bbfe8SDavide Caratti kfree_rcu(chain, rcu); 423c266f64dSVlad Buslov if (free_block) 424c266f64dSVlad Buslov tcf_block_destroy(block); 425c266f64dSVlad Buslov } 426c266f64dSVlad Buslov 427e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain) 428e2ef7544SCong Wang { 429c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 430c266f64dSVlad Buslov 431e2ef7544SCong Wang ++chain->refcnt; 432e2ef7544SCong Wang } 433e2ef7544SCong Wang 4343d32f4c5SJiri Pirko static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain) 4351f3ed383SJiri Pirko { 436c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 437c266f64dSVlad Buslov 4381f3ed383SJiri Pirko /* In case all the references are action references, this 4393d32f4c5SJiri Pirko * chain should not be shown to the user. 4401f3ed383SJiri Pirko */ 4411f3ed383SJiri Pirko return chain->refcnt == chain->action_refcnt; 4421f3ed383SJiri Pirko } 4431f3ed383SJiri Pirko 44432a4f5ecSJiri Pirko static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, 44532a4f5ecSJiri Pirko u32 chain_index) 4465bc17018SJiri Pirko { 4475bc17018SJiri Pirko struct tcf_chain *chain; 4485bc17018SJiri Pirko 449c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 450c266f64dSVlad Buslov 4515bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 45232a4f5ecSJiri Pirko if (chain->index == chain_index) 45332a4f5ecSJiri Pirko return chain; 45432a4f5ecSJiri Pirko } 45532a4f5ecSJiri Pirko return NULL; 45632a4f5ecSJiri Pirko } 45732a4f5ecSJiri Pirko 458af699626SPaul Blakey #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 459af699626SPaul Blakey static struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block, 460af699626SPaul Blakey u32 chain_index) 461af699626SPaul Blakey { 462af699626SPaul Blakey struct tcf_chain *chain; 463af699626SPaul Blakey 464af699626SPaul Blakey list_for_each_entry_rcu(chain, &block->chain_list, list) { 465af699626SPaul Blakey if (chain->index == chain_index) 466af699626SPaul Blakey return chain; 467af699626SPaul Blakey } 468af699626SPaul Blakey return NULL; 469af699626SPaul Blakey } 470af699626SPaul Blakey #endif 471af699626SPaul Blakey 47232a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 47332a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast); 47432a4f5ecSJiri Pirko 47553681407SJiri Pirko static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, 47653681407SJiri Pirko u32 chain_index, bool create, 47753681407SJiri Pirko bool by_act) 47832a4f5ecSJiri Pirko { 479c266f64dSVlad Buslov struct tcf_chain *chain = NULL; 480c266f64dSVlad Buslov bool is_first_reference; 48132a4f5ecSJiri Pirko 482c266f64dSVlad Buslov mutex_lock(&block->lock); 483c266f64dSVlad Buslov chain = tcf_chain_lookup(block, chain_index); 48432a4f5ecSJiri Pirko if (chain) { 485e2ef7544SCong Wang tcf_chain_hold(chain); 48653681407SJiri Pirko } else { 48732a4f5ecSJiri Pirko if (!create) 488c266f64dSVlad Buslov goto errout; 48932a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 49032a4f5ecSJiri Pirko if (!chain) 491c266f64dSVlad Buslov goto errout; 49253681407SJiri Pirko } 49353681407SJiri Pirko 49453681407SJiri Pirko if (by_act) 49553681407SJiri Pirko ++chain->action_refcnt; 496c266f64dSVlad Buslov is_first_reference = chain->refcnt - chain->action_refcnt == 1; 497c266f64dSVlad Buslov mutex_unlock(&block->lock); 49853681407SJiri Pirko 49953681407SJiri Pirko /* Send notification only in case we got the first 50053681407SJiri Pirko * non-action reference. Until then, the chain acts only as 50153681407SJiri Pirko * a placeholder for actions pointing to it and user ought 50253681407SJiri Pirko * not know about them. 50353681407SJiri Pirko */ 504c266f64dSVlad Buslov if (is_first_reference && !by_act) 50532a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 50632a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 50753681407SJiri Pirko 50832a4f5ecSJiri Pirko return chain; 509c266f64dSVlad Buslov 510c266f64dSVlad Buslov errout: 511c266f64dSVlad Buslov mutex_unlock(&block->lock); 512c266f64dSVlad Buslov return chain; 513e2ef7544SCong Wang } 51453681407SJiri Pirko 515290b1c8bSJiri Pirko static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 51653681407SJiri Pirko bool create) 51753681407SJiri Pirko { 51853681407SJiri Pirko return __tcf_chain_get(block, chain_index, create, false); 51953681407SJiri Pirko } 5205bc17018SJiri Pirko 5211f3ed383SJiri Pirko struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index) 5221f3ed383SJiri Pirko { 52353681407SJiri Pirko return __tcf_chain_get(block, chain_index, true, true); 5241f3ed383SJiri Pirko } 5251f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_get_by_act); 5261f3ed383SJiri Pirko 527a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 528a5654820SVlad Buslov void *tmplt_priv); 529a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 530a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 531a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 532a5654820SVlad Buslov u32 seq, u16 flags, bool unicast); 5339f407f17SJiri Pirko 53491052fa1SVlad Buslov static void __tcf_chain_put(struct tcf_chain *chain, bool by_act, 53591052fa1SVlad Buslov bool explicitly_created) 5365bc17018SJiri Pirko { 537c266f64dSVlad Buslov struct tcf_block *block = chain->block; 538a5654820SVlad Buslov const struct tcf_proto_ops *tmplt_ops; 539b62989fcSVlad Buslov bool free_block = false; 540c266f64dSVlad Buslov unsigned int refcnt; 541a5654820SVlad Buslov void *tmplt_priv; 542c266f64dSVlad Buslov 543c266f64dSVlad Buslov mutex_lock(&block->lock); 54491052fa1SVlad Buslov if (explicitly_created) { 54591052fa1SVlad Buslov if (!chain->explicitly_created) { 54691052fa1SVlad Buslov mutex_unlock(&block->lock); 54791052fa1SVlad Buslov return; 54891052fa1SVlad Buslov } 54991052fa1SVlad Buslov chain->explicitly_created = false; 55091052fa1SVlad Buslov } 55191052fa1SVlad Buslov 55253681407SJiri Pirko if (by_act) 55353681407SJiri Pirko chain->action_refcnt--; 554c266f64dSVlad Buslov 555c266f64dSVlad Buslov /* tc_chain_notify_delete can't be called while holding block lock. 556c266f64dSVlad Buslov * However, when block is unlocked chain can be changed concurrently, so 557c266f64dSVlad Buslov * save these to temporary variables. 558c266f64dSVlad Buslov */ 559c266f64dSVlad Buslov refcnt = --chain->refcnt; 560a5654820SVlad Buslov tmplt_ops = chain->tmplt_ops; 561a5654820SVlad Buslov tmplt_priv = chain->tmplt_priv; 56253681407SJiri Pirko 56353681407SJiri Pirko /* The last dropped non-action reference will trigger notification. */ 564b62989fcSVlad Buslov if (refcnt - chain->action_refcnt == 0 && !by_act) { 565b62989fcSVlad Buslov tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index, 566a5654820SVlad Buslov block, NULL, 0, 0, false); 567726d0612SVlad Buslov /* Last reference to chain, no need to lock. */ 568726d0612SVlad Buslov chain->flushing = false; 569726d0612SVlad Buslov } 57053681407SJiri Pirko 571b62989fcSVlad Buslov if (refcnt == 0) 572b62989fcSVlad Buslov free_block = tcf_chain_detach(chain); 573b62989fcSVlad Buslov mutex_unlock(&block->lock); 574b62989fcSVlad Buslov 575c266f64dSVlad Buslov if (refcnt == 0) { 576a5654820SVlad Buslov tc_chain_tmplt_del(tmplt_ops, tmplt_priv); 577c266f64dSVlad Buslov tcf_chain_destroy(chain, free_block); 5785bc17018SJiri Pirko } 57932a4f5ecSJiri Pirko } 58053681407SJiri Pirko 581290b1c8bSJiri Pirko static void tcf_chain_put(struct tcf_chain *chain) 58253681407SJiri Pirko { 58391052fa1SVlad Buslov __tcf_chain_put(chain, false, false); 58453681407SJiri Pirko } 5855bc17018SJiri Pirko 5861f3ed383SJiri Pirko void tcf_chain_put_by_act(struct tcf_chain *chain) 5871f3ed383SJiri Pirko { 58891052fa1SVlad Buslov __tcf_chain_put(chain, true, false); 5891f3ed383SJiri Pirko } 5901f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_put_by_act); 5911f3ed383SJiri Pirko 59232a4f5ecSJiri Pirko static void tcf_chain_put_explicitly_created(struct tcf_chain *chain) 59332a4f5ecSJiri Pirko { 59491052fa1SVlad Buslov __tcf_chain_put(chain, false, true); 59532a4f5ecSJiri Pirko } 59632a4f5ecSJiri Pirko 59712db03b6SVlad Buslov static void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held) 598290b1c8bSJiri Pirko { 5994dbfa766SVlad Buslov struct tcf_proto *tp, *tp_next; 600290b1c8bSJiri Pirko 601ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 602ed76f5edSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 60359eb87cbSJohn Hurley while (tp) { 60459eb87cbSJohn Hurley tp_next = rcu_dereference_protected(tp->next, 1); 60559eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 60659eb87cbSJohn Hurley tp = tp_next; 60759eb87cbSJohn Hurley } 60859eb87cbSJohn Hurley tp = tcf_chain_dereference(chain->filter_chain, chain); 6094dbfa766SVlad Buslov RCU_INIT_POINTER(chain->filter_chain, NULL); 610290b1c8bSJiri Pirko tcf_chain0_head_change(chain, NULL); 611726d0612SVlad Buslov chain->flushing = true; 612ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 613ed76f5edSVlad Buslov 614290b1c8bSJiri Pirko while (tp) { 6154dbfa766SVlad Buslov tp_next = rcu_dereference_protected(tp->next, 1); 61612db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 6174dbfa766SVlad Buslov tp = tp_next; 618290b1c8bSJiri Pirko } 619290b1c8bSJiri Pirko } 620290b1c8bSJiri Pirko 6214e481908Swenxu static int tcf_block_setup(struct tcf_block *block, 6224e481908Swenxu struct flow_block_offload *bo); 6234e481908Swenxu 62425a443f7SJohn Hurley static void tc_indr_block_cmd(struct net_device *dev, struct tcf_block *block, 62525a443f7SJohn Hurley flow_indr_block_bind_cb_t *cb, void *cb_priv, 62625a443f7SJohn Hurley enum flow_block_command command, bool ingress) 6274e481908Swenxu { 6284e481908Swenxu struct flow_block_offload bo = { 6294e481908Swenxu .command = command, 63025a443f7SJohn Hurley .binder_type = ingress ? 63125a443f7SJohn Hurley FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS : 63225a443f7SJohn Hurley FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS, 6334e481908Swenxu .net = dev_net(dev), 6344e481908Swenxu .block_shared = tcf_block_non_null_shared(block), 6354e481908Swenxu }; 6364e481908Swenxu INIT_LIST_HEAD(&bo.cb_list); 6374e481908Swenxu 6384e481908Swenxu if (!block) 6394e481908Swenxu return; 6404e481908Swenxu 6414e481908Swenxu bo.block = &block->flow_block; 6424e481908Swenxu 6434f8116c8SVlad Buslov down_write(&block->cb_lock); 6444e481908Swenxu cb(dev, cb_priv, TC_SETUP_BLOCK, &bo); 6454e481908Swenxu 6464e481908Swenxu tcf_block_setup(block, &bo); 6474f8116c8SVlad Buslov up_write(&block->cb_lock); 6484e481908Swenxu } 6494e481908Swenxu 65025a443f7SJohn Hurley static struct tcf_block *tc_dev_block(struct net_device *dev, bool ingress) 6517f76fa36SJohn Hurley { 6527f76fa36SJohn Hurley const struct Qdisc_class_ops *cops; 65325a443f7SJohn Hurley const struct Qdisc_ops *ops; 6547f76fa36SJohn Hurley struct Qdisc *qdisc; 6557f76fa36SJohn Hurley 6567f76fa36SJohn Hurley if (!dev_ingress_queue(dev)) 6577f76fa36SJohn Hurley return NULL; 6587f76fa36SJohn Hurley 6597f76fa36SJohn Hurley qdisc = dev_ingress_queue(dev)->qdisc_sleeping; 6607f76fa36SJohn Hurley if (!qdisc) 6617f76fa36SJohn Hurley return NULL; 6627f76fa36SJohn Hurley 66325a443f7SJohn Hurley ops = qdisc->ops; 66425a443f7SJohn Hurley if (!ops) 66525a443f7SJohn Hurley return NULL; 66625a443f7SJohn Hurley 66725a443f7SJohn Hurley if (!ingress && !strcmp("ingress", ops->id)) 66825a443f7SJohn Hurley return NULL; 66925a443f7SJohn Hurley 67025a443f7SJohn Hurley cops = ops->cl_ops; 6717f76fa36SJohn Hurley if (!cops) 6727f76fa36SJohn Hurley return NULL; 6737f76fa36SJohn Hurley 6747f76fa36SJohn Hurley if (!cops->tcf_block) 6757f76fa36SJohn Hurley return NULL; 6767f76fa36SJohn Hurley 67725a443f7SJohn Hurley return cops->tcf_block(qdisc, 67825a443f7SJohn Hurley ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS, 67925a443f7SJohn Hurley NULL); 6807f76fa36SJohn Hurley } 6817f76fa36SJohn Hurley 68225a443f7SJohn Hurley static void tc_indr_block_get_and_cmd(struct net_device *dev, 6834e481908Swenxu flow_indr_block_bind_cb_t *cb, 684242453c2Swenxu void *cb_priv, 6859c0e189eSPablo Neira Ayuso enum flow_block_command command) 6867f76fa36SJohn Hurley { 68725a443f7SJohn Hurley struct tcf_block *block; 6887f76fa36SJohn Hurley 68925a443f7SJohn Hurley block = tc_dev_block(dev, true); 69025a443f7SJohn Hurley tc_indr_block_cmd(dev, block, cb, cb_priv, command, true); 69125a443f7SJohn Hurley 69225a443f7SJohn Hurley block = tc_dev_block(dev, false); 69325a443f7SJohn Hurley tc_indr_block_cmd(dev, block, cb, cb_priv, command, false); 6947f76fa36SJohn Hurley } 6957f76fa36SJohn Hurley 6964e481908Swenxu static void tc_indr_block_call(struct tcf_block *block, 6974e481908Swenxu struct net_device *dev, 6987f76fa36SJohn Hurley struct tcf_block_ext_info *ei, 6999c0e189eSPablo Neira Ayuso enum flow_block_command command, 7007f76fa36SJohn Hurley struct netlink_ext_ack *extack) 7017f76fa36SJohn Hurley { 702955bcb6eSPablo Neira Ayuso struct flow_block_offload bo = { 7037f76fa36SJohn Hurley .command = command, 7047f76fa36SJohn Hurley .binder_type = ei->binder_type, 705da3eeb90SPablo Neira Ayuso .net = dev_net(dev), 70614bfb13fSPablo Neira Ayuso .block = &block->flow_block, 707955bcb6eSPablo Neira Ayuso .block_shared = tcf_block_shared(block), 7087f76fa36SJohn Hurley .extack = extack, 7097f76fa36SJohn Hurley }; 71059094b1eSPablo Neira Ayuso INIT_LIST_HEAD(&bo.cb_list); 7117f76fa36SJohn Hurley 712133a2fe5Swenxu flow_indr_block_call(dev, &bo, command, TC_SETUP_BLOCK); 71359094b1eSPablo Neira Ayuso tcf_block_setup(block, &bo); 7147f76fa36SJohn Hurley } 7157f76fa36SJohn Hurley 716caa72601SJiri Pirko static bool tcf_block_offload_in_use(struct tcf_block *block) 717caa72601SJiri Pirko { 71897394befSVlad Buslov return atomic_read(&block->offloadcnt); 719caa72601SJiri Pirko } 720caa72601SJiri Pirko 721caa72601SJiri Pirko static int tcf_block_offload_cmd(struct tcf_block *block, 722caa72601SJiri Pirko struct net_device *dev, 7238c4083b3SJiri Pirko struct tcf_block_ext_info *ei, 7249c0e189eSPablo Neira Ayuso enum flow_block_command command, 72560513bd8SJohn Hurley struct netlink_ext_ack *extack) 7268c4083b3SJiri Pirko { 727955bcb6eSPablo Neira Ayuso struct flow_block_offload bo = {}; 72859094b1eSPablo Neira Ayuso int err; 7298c4083b3SJiri Pirko 730da3eeb90SPablo Neira Ayuso bo.net = dev_net(dev); 7318c4083b3SJiri Pirko bo.command = command; 7328c4083b3SJiri Pirko bo.binder_type = ei->binder_type; 73314bfb13fSPablo Neira Ayuso bo.block = &block->flow_block; 734955bcb6eSPablo Neira Ayuso bo.block_shared = tcf_block_shared(block); 73560513bd8SJohn Hurley bo.extack = extack; 73659094b1eSPablo Neira Ayuso INIT_LIST_HEAD(&bo.cb_list); 73759094b1eSPablo Neira Ayuso 73859094b1eSPablo Neira Ayuso err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 739b70ba69eSJesper Dangaard Brouer if (err < 0) { 740b70ba69eSJesper Dangaard Brouer if (err != -EOPNOTSUPP) 741b70ba69eSJesper Dangaard Brouer NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed"); 74259094b1eSPablo Neira Ayuso return err; 743b70ba69eSJesper Dangaard Brouer } 74459094b1eSPablo Neira Ayuso 74559094b1eSPablo Neira Ayuso return tcf_block_setup(block, &bo); 7468c4083b3SJiri Pirko } 7478c4083b3SJiri Pirko 748caa72601SJiri Pirko static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 74960513bd8SJohn Hurley struct tcf_block_ext_info *ei, 75060513bd8SJohn Hurley struct netlink_ext_ack *extack) 7518c4083b3SJiri Pirko { 752caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 753caa72601SJiri Pirko int err; 754caa72601SJiri Pirko 7554f8116c8SVlad Buslov down_write(&block->cb_lock); 756caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 757caa72601SJiri Pirko goto no_offload_dev_inc; 758caa72601SJiri Pirko 759caa72601SJiri Pirko /* If tc offload feature is disabled and the block we try to bind 760caa72601SJiri Pirko * to already has some offloaded filters, forbid to bind. 761caa72601SJiri Pirko */ 76260513bd8SJohn Hurley if (!tc_can_offload(dev) && tcf_block_offload_in_use(block)) { 76360513bd8SJohn Hurley NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled"); 7644f8116c8SVlad Buslov err = -EOPNOTSUPP; 7654f8116c8SVlad Buslov goto err_unlock; 76660513bd8SJohn Hurley } 767caa72601SJiri Pirko 7689c0e189eSPablo Neira Ayuso err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_BIND, extack); 769caa72601SJiri Pirko if (err == -EOPNOTSUPP) 770caa72601SJiri Pirko goto no_offload_dev_inc; 7717f76fa36SJohn Hurley if (err) 7724f8116c8SVlad Buslov goto err_unlock; 773caa72601SJiri Pirko 7749c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack); 7754f8116c8SVlad Buslov up_write(&block->cb_lock); 7767f76fa36SJohn Hurley return 0; 7777f76fa36SJohn Hurley 778caa72601SJiri Pirko no_offload_dev_inc: 7794f8116c8SVlad Buslov if (tcf_block_offload_in_use(block)) { 7804f8116c8SVlad Buslov err = -EOPNOTSUPP; 7814f8116c8SVlad Buslov goto err_unlock; 7824f8116c8SVlad Buslov } 7834f8116c8SVlad Buslov err = 0; 784caa72601SJiri Pirko block->nooffloaddevcnt++; 7859c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack); 7864f8116c8SVlad Buslov err_unlock: 7874f8116c8SVlad Buslov up_write(&block->cb_lock); 7884f8116c8SVlad Buslov return err; 7898c4083b3SJiri Pirko } 7908c4083b3SJiri Pirko 7918c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 7928c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 7938c4083b3SJiri Pirko { 794caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 795caa72601SJiri Pirko int err; 796caa72601SJiri Pirko 7974f8116c8SVlad Buslov down_write(&block->cb_lock); 7989c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_UNBIND, NULL); 7997f76fa36SJohn Hurley 800caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 801caa72601SJiri Pirko goto no_offload_dev_dec; 8029c0e189eSPablo Neira Ayuso err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_UNBIND, NULL); 803caa72601SJiri Pirko if (err == -EOPNOTSUPP) 804caa72601SJiri Pirko goto no_offload_dev_dec; 8054f8116c8SVlad Buslov up_write(&block->cb_lock); 806caa72601SJiri Pirko return; 807caa72601SJiri Pirko 808caa72601SJiri Pirko no_offload_dev_dec: 809caa72601SJiri Pirko WARN_ON(block->nooffloaddevcnt-- == 0); 8104f8116c8SVlad Buslov up_write(&block->cb_lock); 8118c4083b3SJiri Pirko } 8128c4083b3SJiri Pirko 813a9b19443SJiri Pirko static int 814f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_add(struct tcf_block *block, 815a9b19443SJiri Pirko struct tcf_block_ext_info *ei, 816a9b19443SJiri Pirko struct netlink_ext_ack *extack) 817a9b19443SJiri Pirko { 818a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 819165f0135SVlad Buslov struct tcf_chain *chain0; 820a9b19443SJiri Pirko 821a9b19443SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 822a9b19443SJiri Pirko if (!item) { 823a9b19443SJiri Pirko NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed"); 824a9b19443SJiri Pirko return -ENOMEM; 825a9b19443SJiri Pirko } 826a9b19443SJiri Pirko item->chain_head_change = ei->chain_head_change; 827a9b19443SJiri Pirko item->chain_head_change_priv = ei->chain_head_change_priv; 828165f0135SVlad Buslov 829165f0135SVlad Buslov mutex_lock(&block->lock); 830165f0135SVlad Buslov chain0 = block->chain0.chain; 831ed76f5edSVlad Buslov if (chain0) 832ed76f5edSVlad Buslov tcf_chain_hold(chain0); 833ed76f5edSVlad Buslov else 834f71e0ca4SJiri Pirko list_add(&item->list, &block->chain0.filter_chain_list); 835165f0135SVlad Buslov mutex_unlock(&block->lock); 836165f0135SVlad Buslov 837ed76f5edSVlad Buslov if (chain0) { 838ed76f5edSVlad Buslov struct tcf_proto *tp_head; 839ed76f5edSVlad Buslov 840ed76f5edSVlad Buslov mutex_lock(&chain0->filter_chain_lock); 841ed76f5edSVlad Buslov 842ed76f5edSVlad Buslov tp_head = tcf_chain_dereference(chain0->filter_chain, chain0); 843ed76f5edSVlad Buslov if (tp_head) 844ed76f5edSVlad Buslov tcf_chain_head_change_item(item, tp_head); 845ed76f5edSVlad Buslov 846ed76f5edSVlad Buslov mutex_lock(&block->lock); 847ed76f5edSVlad Buslov list_add(&item->list, &block->chain0.filter_chain_list); 848ed76f5edSVlad Buslov mutex_unlock(&block->lock); 849ed76f5edSVlad Buslov 850ed76f5edSVlad Buslov mutex_unlock(&chain0->filter_chain_lock); 851ed76f5edSVlad Buslov tcf_chain_put(chain0); 852ed76f5edSVlad Buslov } 853ed76f5edSVlad Buslov 854a9b19443SJiri Pirko return 0; 855a9b19443SJiri Pirko } 856a9b19443SJiri Pirko 857a9b19443SJiri Pirko static void 858f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(struct tcf_block *block, 859a9b19443SJiri Pirko struct tcf_block_ext_info *ei) 860a9b19443SJiri Pirko { 861a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 862a9b19443SJiri Pirko 863165f0135SVlad Buslov mutex_lock(&block->lock); 864f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) { 865a9b19443SJiri Pirko if ((!ei->chain_head_change && !ei->chain_head_change_priv) || 866a9b19443SJiri Pirko (item->chain_head_change == ei->chain_head_change && 867a9b19443SJiri Pirko item->chain_head_change_priv == ei->chain_head_change_priv)) { 868165f0135SVlad Buslov if (block->chain0.chain) 869a9b19443SJiri Pirko tcf_chain_head_change_item(item, NULL); 870a9b19443SJiri Pirko list_del(&item->list); 871165f0135SVlad Buslov mutex_unlock(&block->lock); 872165f0135SVlad Buslov 873a9b19443SJiri Pirko kfree(item); 874a9b19443SJiri Pirko return; 875a9b19443SJiri Pirko } 876a9b19443SJiri Pirko } 877165f0135SVlad Buslov mutex_unlock(&block->lock); 878a9b19443SJiri Pirko WARN_ON(1); 879a9b19443SJiri Pirko } 880a9b19443SJiri Pirko 88148617387SJiri Pirko struct tcf_net { 882ab281629SVlad Buslov spinlock_t idr_lock; /* Protects idr */ 88348617387SJiri Pirko struct idr idr; 88448617387SJiri Pirko }; 88548617387SJiri Pirko 88648617387SJiri Pirko static unsigned int tcf_net_id; 88748617387SJiri Pirko 88848617387SJiri Pirko static int tcf_block_insert(struct tcf_block *block, struct net *net, 889bb047dddSJiri Pirko struct netlink_ext_ack *extack) 890a9b19443SJiri Pirko { 89148617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 892ab281629SVlad Buslov int err; 89348617387SJiri Pirko 894ab281629SVlad Buslov idr_preload(GFP_KERNEL); 895ab281629SVlad Buslov spin_lock(&tn->idr_lock); 896ab281629SVlad Buslov err = idr_alloc_u32(&tn->idr, block, &block->index, block->index, 897ab281629SVlad Buslov GFP_NOWAIT); 898ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 899ab281629SVlad Buslov idr_preload_end(); 900ab281629SVlad Buslov 901ab281629SVlad Buslov return err; 902a9b19443SJiri Pirko } 903a9b19443SJiri Pirko 90448617387SJiri Pirko static void tcf_block_remove(struct tcf_block *block, struct net *net) 90548617387SJiri Pirko { 90648617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 90748617387SJiri Pirko 908ab281629SVlad Buslov spin_lock(&tn->idr_lock); 9099c160941SMatthew Wilcox idr_remove(&tn->idr, block->index); 910ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 91148617387SJiri Pirko } 91248617387SJiri Pirko 91348617387SJiri Pirko static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, 914bb047dddSJiri Pirko u32 block_index, 9158d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 9166529eabaSJiri Pirko { 91748617387SJiri Pirko struct tcf_block *block; 9186529eabaSJiri Pirko 91948617387SJiri Pirko block = kzalloc(sizeof(*block), GFP_KERNEL); 9208d1a77f9SAlexander Aring if (!block) { 9218d1a77f9SAlexander Aring NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); 92248617387SJiri Pirko return ERR_PTR(-ENOMEM); 9238d1a77f9SAlexander Aring } 924c266f64dSVlad Buslov mutex_init(&block->lock); 92559eb87cbSJohn Hurley mutex_init(&block->proto_destroy_lock); 9264f8116c8SVlad Buslov init_rwsem(&block->cb_lock); 92714bfb13fSPablo Neira Ayuso flow_block_init(&block->flow_block); 9285bc17018SJiri Pirko INIT_LIST_HEAD(&block->chain_list); 929f36fe1c4SJiri Pirko INIT_LIST_HEAD(&block->owner_list); 930f71e0ca4SJiri Pirko INIT_LIST_HEAD(&block->chain0.filter_chain_list); 931acb67442SJiri Pirko 932cfebd7e2SVlad Buslov refcount_set(&block->refcnt, 1); 93348617387SJiri Pirko block->net = net; 934bb047dddSJiri Pirko block->index = block_index; 935bb047dddSJiri Pirko 936bb047dddSJiri Pirko /* Don't store q pointer for blocks which are shared */ 937bb047dddSJiri Pirko if (!tcf_block_shared(block)) 93848617387SJiri Pirko block->q = q; 93948617387SJiri Pirko return block; 94048617387SJiri Pirko } 94148617387SJiri Pirko 94248617387SJiri Pirko static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) 94348617387SJiri Pirko { 94448617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 94548617387SJiri Pirko 946322d884bSMatthew Wilcox return idr_find(&tn->idr, block_index); 94748617387SJiri Pirko } 94848617387SJiri Pirko 9490607e439SVlad Buslov static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index) 9500607e439SVlad Buslov { 9510607e439SVlad Buslov struct tcf_block *block; 9520607e439SVlad Buslov 9530607e439SVlad Buslov rcu_read_lock(); 9540607e439SVlad Buslov block = tcf_block_lookup(net, block_index); 9550607e439SVlad Buslov if (block && !refcount_inc_not_zero(&block->refcnt)) 9560607e439SVlad Buslov block = NULL; 9570607e439SVlad Buslov rcu_read_unlock(); 9580607e439SVlad Buslov 9590607e439SVlad Buslov return block; 9600607e439SVlad Buslov } 9610607e439SVlad Buslov 962bbf73830SVlad Buslov static struct tcf_chain * 963bbf73830SVlad Buslov __tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 964bbf73830SVlad Buslov { 965bbf73830SVlad Buslov mutex_lock(&block->lock); 966bbf73830SVlad Buslov if (chain) 967bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 968bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 969bbf73830SVlad Buslov else 970bbf73830SVlad Buslov chain = list_first_entry_or_null(&block->chain_list, 971bbf73830SVlad Buslov struct tcf_chain, list); 972bbf73830SVlad Buslov 973bbf73830SVlad Buslov /* skip all action-only chains */ 974bbf73830SVlad Buslov while (chain && tcf_chain_held_by_acts_only(chain)) 975bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 976bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 977bbf73830SVlad Buslov 978bbf73830SVlad Buslov if (chain) 979bbf73830SVlad Buslov tcf_chain_hold(chain); 980bbf73830SVlad Buslov mutex_unlock(&block->lock); 981bbf73830SVlad Buslov 982bbf73830SVlad Buslov return chain; 983bbf73830SVlad Buslov } 984bbf73830SVlad Buslov 985bbf73830SVlad Buslov /* Function to be used by all clients that want to iterate over all chains on 986bbf73830SVlad Buslov * block. It properly obtains block->lock and takes reference to chain before 987bbf73830SVlad Buslov * returning it. Users of this function must be tolerant to concurrent chain 988bbf73830SVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 989bbf73830SVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 990bbf73830SVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 991bbf73830SVlad Buslov * data and sent to user-space. 992bbf73830SVlad Buslov */ 993bbf73830SVlad Buslov 994bbf73830SVlad Buslov struct tcf_chain * 995bbf73830SVlad Buslov tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 996bbf73830SVlad Buslov { 997bbf73830SVlad Buslov struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain); 998bbf73830SVlad Buslov 999bbf73830SVlad Buslov if (chain) 1000bbf73830SVlad Buslov tcf_chain_put(chain); 1001bbf73830SVlad Buslov 1002bbf73830SVlad Buslov return chain_next; 1003bbf73830SVlad Buslov } 1004bbf73830SVlad Buslov EXPORT_SYMBOL(tcf_get_next_chain); 1005bbf73830SVlad Buslov 1006fe2923afSVlad Buslov static struct tcf_proto * 1007fe2923afSVlad Buslov __tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 1008fe2923afSVlad Buslov { 10098b64678eSVlad Buslov u32 prio = 0; 10108b64678eSVlad Buslov 1011fe2923afSVlad Buslov ASSERT_RTNL(); 1012fe2923afSVlad Buslov mutex_lock(&chain->filter_chain_lock); 1013fe2923afSVlad Buslov 10148b64678eSVlad Buslov if (!tp) { 1015fe2923afSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 10168b64678eSVlad Buslov } else if (tcf_proto_is_deleting(tp)) { 10178b64678eSVlad Buslov /* 'deleting' flag is set and chain->filter_chain_lock was 10188b64678eSVlad Buslov * unlocked, which means next pointer could be invalid. Restart 10198b64678eSVlad Buslov * search. 10208b64678eSVlad Buslov */ 10218b64678eSVlad Buslov prio = tp->prio + 1; 10228b64678eSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 10238b64678eSVlad Buslov 10248b64678eSVlad Buslov for (; tp; tp = tcf_chain_dereference(tp->next, chain)) 10258b64678eSVlad Buslov if (!tp->deleting && tp->prio >= prio) 10268b64678eSVlad Buslov break; 10278b64678eSVlad Buslov } else { 1028fe2923afSVlad Buslov tp = tcf_chain_dereference(tp->next, chain); 10298b64678eSVlad Buslov } 1030fe2923afSVlad Buslov 1031fe2923afSVlad Buslov if (tp) 1032fe2923afSVlad Buslov tcf_proto_get(tp); 1033fe2923afSVlad Buslov 1034fe2923afSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 1035fe2923afSVlad Buslov 1036fe2923afSVlad Buslov return tp; 1037fe2923afSVlad Buslov } 1038fe2923afSVlad Buslov 1039fe2923afSVlad Buslov /* Function to be used by all clients that want to iterate over all tp's on 1040fe2923afSVlad Buslov * chain. Users of this function must be tolerant to concurrent tp 1041fe2923afSVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 1042fe2923afSVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 1043fe2923afSVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 1044fe2923afSVlad Buslov * data and sent to user-space. 1045fe2923afSVlad Buslov */ 1046fe2923afSVlad Buslov 1047fe2923afSVlad Buslov struct tcf_proto * 104812db03b6SVlad Buslov tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp, 104912db03b6SVlad Buslov bool rtnl_held) 1050fe2923afSVlad Buslov { 1051fe2923afSVlad Buslov struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp); 1052fe2923afSVlad Buslov 1053fe2923afSVlad Buslov if (tp) 105412db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 1055fe2923afSVlad Buslov 1056fe2923afSVlad Buslov return tp_next; 1057fe2923afSVlad Buslov } 1058fe2923afSVlad Buslov EXPORT_SYMBOL(tcf_get_next_proto); 1059fe2923afSVlad Buslov 106012db03b6SVlad Buslov static void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held) 1061f0023436SVlad Buslov { 1062f0023436SVlad Buslov struct tcf_chain *chain; 1063f0023436SVlad Buslov 1064bbf73830SVlad Buslov /* Last reference to block. At this point chains cannot be added or 1065bbf73830SVlad Buslov * removed concurrently. 1066f0023436SVlad Buslov */ 1067bbf73830SVlad Buslov for (chain = tcf_get_next_chain(block, NULL); 1068bbf73830SVlad Buslov chain; 1069bbf73830SVlad Buslov chain = tcf_get_next_chain(block, chain)) { 1070f0023436SVlad Buslov tcf_chain_put_explicitly_created(chain); 107112db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 1072f0023436SVlad Buslov } 1073f0023436SVlad Buslov } 1074f0023436SVlad Buslov 107518d3eefbSVlad Buslov /* Lookup Qdisc and increments its reference counter. 107618d3eefbSVlad Buslov * Set parent, if necessary. 107718d3eefbSVlad Buslov */ 107818d3eefbSVlad Buslov 107918d3eefbSVlad Buslov static int __tcf_qdisc_find(struct net *net, struct Qdisc **q, 108018d3eefbSVlad Buslov u32 *parent, int ifindex, bool rtnl_held, 108118d3eefbSVlad Buslov struct netlink_ext_ack *extack) 108218d3eefbSVlad Buslov { 108318d3eefbSVlad Buslov const struct Qdisc_class_ops *cops; 108418d3eefbSVlad Buslov struct net_device *dev; 108518d3eefbSVlad Buslov int err = 0; 108618d3eefbSVlad Buslov 108718d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 108818d3eefbSVlad Buslov return 0; 108918d3eefbSVlad Buslov 109018d3eefbSVlad Buslov rcu_read_lock(); 109118d3eefbSVlad Buslov 109218d3eefbSVlad Buslov /* Find link */ 109318d3eefbSVlad Buslov dev = dev_get_by_index_rcu(net, ifindex); 109418d3eefbSVlad Buslov if (!dev) { 109518d3eefbSVlad Buslov rcu_read_unlock(); 109618d3eefbSVlad Buslov return -ENODEV; 109718d3eefbSVlad Buslov } 109818d3eefbSVlad Buslov 109918d3eefbSVlad Buslov /* Find qdisc */ 110018d3eefbSVlad Buslov if (!*parent) { 110118d3eefbSVlad Buslov *q = dev->qdisc; 110218d3eefbSVlad Buslov *parent = (*q)->handle; 110318d3eefbSVlad Buslov } else { 110418d3eefbSVlad Buslov *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent)); 110518d3eefbSVlad Buslov if (!*q) { 110618d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 110718d3eefbSVlad Buslov err = -EINVAL; 110818d3eefbSVlad Buslov goto errout_rcu; 110918d3eefbSVlad Buslov } 111018d3eefbSVlad Buslov } 111118d3eefbSVlad Buslov 111218d3eefbSVlad Buslov *q = qdisc_refcount_inc_nz(*q); 111318d3eefbSVlad Buslov if (!*q) { 111418d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 111518d3eefbSVlad Buslov err = -EINVAL; 111618d3eefbSVlad Buslov goto errout_rcu; 111718d3eefbSVlad Buslov } 111818d3eefbSVlad Buslov 111918d3eefbSVlad Buslov /* Is it classful? */ 112018d3eefbSVlad Buslov cops = (*q)->ops->cl_ops; 112118d3eefbSVlad Buslov if (!cops) { 112218d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Qdisc not classful"); 112318d3eefbSVlad Buslov err = -EINVAL; 112418d3eefbSVlad Buslov goto errout_qdisc; 112518d3eefbSVlad Buslov } 112618d3eefbSVlad Buslov 112718d3eefbSVlad Buslov if (!cops->tcf_block) { 112818d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Class doesn't support blocks"); 112918d3eefbSVlad Buslov err = -EOPNOTSUPP; 113018d3eefbSVlad Buslov goto errout_qdisc; 113118d3eefbSVlad Buslov } 113218d3eefbSVlad Buslov 113318d3eefbSVlad Buslov errout_rcu: 113418d3eefbSVlad Buslov /* At this point we know that qdisc is not noop_qdisc, 113518d3eefbSVlad Buslov * which means that qdisc holds a reference to net_device 113618d3eefbSVlad Buslov * and we hold a reference to qdisc, so it is safe to release 113718d3eefbSVlad Buslov * rcu read lock. 113818d3eefbSVlad Buslov */ 113918d3eefbSVlad Buslov rcu_read_unlock(); 114018d3eefbSVlad Buslov return err; 114118d3eefbSVlad Buslov 114218d3eefbSVlad Buslov errout_qdisc: 114318d3eefbSVlad Buslov rcu_read_unlock(); 114418d3eefbSVlad Buslov 114518d3eefbSVlad Buslov if (rtnl_held) 114618d3eefbSVlad Buslov qdisc_put(*q); 114718d3eefbSVlad Buslov else 114818d3eefbSVlad Buslov qdisc_put_unlocked(*q); 114918d3eefbSVlad Buslov *q = NULL; 115018d3eefbSVlad Buslov 115118d3eefbSVlad Buslov return err; 115218d3eefbSVlad Buslov } 115318d3eefbSVlad Buslov 115418d3eefbSVlad Buslov static int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl, 115518d3eefbSVlad Buslov int ifindex, struct netlink_ext_ack *extack) 115618d3eefbSVlad Buslov { 115718d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 115818d3eefbSVlad Buslov return 0; 115918d3eefbSVlad Buslov 116018d3eefbSVlad Buslov /* Do we search for filter, attached to class? */ 116118d3eefbSVlad Buslov if (TC_H_MIN(parent)) { 116218d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 116318d3eefbSVlad Buslov 116418d3eefbSVlad Buslov *cl = cops->find(q, parent); 116518d3eefbSVlad Buslov if (*cl == 0) { 116618d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Specified class doesn't exist"); 116718d3eefbSVlad Buslov return -ENOENT; 116818d3eefbSVlad Buslov } 116918d3eefbSVlad Buslov } 117018d3eefbSVlad Buslov 117118d3eefbSVlad Buslov return 0; 117218d3eefbSVlad Buslov } 117318d3eefbSVlad Buslov 117418d3eefbSVlad Buslov static struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q, 117518d3eefbSVlad Buslov unsigned long cl, int ifindex, 117618d3eefbSVlad Buslov u32 block_index, 117718d3eefbSVlad Buslov struct netlink_ext_ack *extack) 117818d3eefbSVlad Buslov { 117918d3eefbSVlad Buslov struct tcf_block *block; 118018d3eefbSVlad Buslov 118118d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 118218d3eefbSVlad Buslov block = tcf_block_refcnt_get(net, block_index); 118318d3eefbSVlad Buslov if (!block) { 118418d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Block of given index was not found"); 118518d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 118618d3eefbSVlad Buslov } 118718d3eefbSVlad Buslov } else { 118818d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 118918d3eefbSVlad Buslov 119018d3eefbSVlad Buslov block = cops->tcf_block(q, cl, extack); 119118d3eefbSVlad Buslov if (!block) 119218d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 119318d3eefbSVlad Buslov 119418d3eefbSVlad Buslov if (tcf_block_shared(block)) { 119518d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters"); 119618d3eefbSVlad Buslov return ERR_PTR(-EOPNOTSUPP); 119718d3eefbSVlad Buslov } 119818d3eefbSVlad Buslov 119918d3eefbSVlad Buslov /* Always take reference to block in order to support execution 120018d3eefbSVlad Buslov * of rules update path of cls API without rtnl lock. Caller 120118d3eefbSVlad Buslov * must release block when it is finished using it. 'if' block 120218d3eefbSVlad Buslov * of this conditional obtain reference to block by calling 120318d3eefbSVlad Buslov * tcf_block_refcnt_get(). 120418d3eefbSVlad Buslov */ 120518d3eefbSVlad Buslov refcount_inc(&block->refcnt); 120618d3eefbSVlad Buslov } 120718d3eefbSVlad Buslov 120818d3eefbSVlad Buslov return block; 120918d3eefbSVlad Buslov } 121018d3eefbSVlad Buslov 12110607e439SVlad Buslov static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q, 121212db03b6SVlad Buslov struct tcf_block_ext_info *ei, bool rtnl_held) 12130607e439SVlad Buslov { 1214c266f64dSVlad Buslov if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) { 12150607e439SVlad Buslov /* Flushing/putting all chains will cause the block to be 12160607e439SVlad Buslov * deallocated when last chain is freed. However, if chain_list 12170607e439SVlad Buslov * is empty, block has to be manually deallocated. After block 12180607e439SVlad Buslov * reference counter reached 0, it is no longer possible to 12190607e439SVlad Buslov * increment it or add new chains to block. 12200607e439SVlad Buslov */ 12210607e439SVlad Buslov bool free_block = list_empty(&block->chain_list); 12220607e439SVlad Buslov 1223c266f64dSVlad Buslov mutex_unlock(&block->lock); 12240607e439SVlad Buslov if (tcf_block_shared(block)) 12250607e439SVlad Buslov tcf_block_remove(block, block->net); 12260607e439SVlad Buslov 12270607e439SVlad Buslov if (q) 12280607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 12290607e439SVlad Buslov 12300607e439SVlad Buslov if (free_block) 1231c266f64dSVlad Buslov tcf_block_destroy(block); 12320607e439SVlad Buslov else 123312db03b6SVlad Buslov tcf_block_flush_all_chains(block, rtnl_held); 12340607e439SVlad Buslov } else if (q) { 12350607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 12360607e439SVlad Buslov } 12370607e439SVlad Buslov } 12380607e439SVlad Buslov 123912db03b6SVlad Buslov static void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held) 12400607e439SVlad Buslov { 124112db03b6SVlad Buslov __tcf_block_put(block, NULL, NULL, rtnl_held); 12420607e439SVlad Buslov } 12430607e439SVlad Buslov 1244c431f89bSVlad Buslov /* Find tcf block. 1245c431f89bSVlad Buslov * Set q, parent, cl when appropriate. 1246c431f89bSVlad Buslov */ 1247c431f89bSVlad Buslov 1248c431f89bSVlad Buslov static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q, 1249c431f89bSVlad Buslov u32 *parent, unsigned long *cl, 1250c431f89bSVlad Buslov int ifindex, u32 block_index, 1251c431f89bSVlad Buslov struct netlink_ext_ack *extack) 1252c431f89bSVlad Buslov { 1253c431f89bSVlad Buslov struct tcf_block *block; 1254e368fdb6SVlad Buslov int err = 0; 1255c431f89bSVlad Buslov 125618d3eefbSVlad Buslov ASSERT_RTNL(); 1257c431f89bSVlad Buslov 125818d3eefbSVlad Buslov err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack); 125918d3eefbSVlad Buslov if (err) 126018d3eefbSVlad Buslov goto errout; 1261e368fdb6SVlad Buslov 126218d3eefbSVlad Buslov err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack); 126318d3eefbSVlad Buslov if (err) 1264e368fdb6SVlad Buslov goto errout_qdisc; 1265c431f89bSVlad Buslov 126618d3eefbSVlad Buslov block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack); 1267af736bf0SDan Carpenter if (IS_ERR(block)) { 1268af736bf0SDan Carpenter err = PTR_ERR(block); 1269e368fdb6SVlad Buslov goto errout_qdisc; 1270af736bf0SDan Carpenter } 1271c431f89bSVlad Buslov 1272c431f89bSVlad Buslov return block; 1273e368fdb6SVlad Buslov 1274e368fdb6SVlad Buslov errout_qdisc: 127518d3eefbSVlad Buslov if (*q) 1276e368fdb6SVlad Buslov qdisc_put(*q); 127718d3eefbSVlad Buslov errout: 1278460b3601SCong Wang *q = NULL; 1279e368fdb6SVlad Buslov return ERR_PTR(err); 1280e368fdb6SVlad Buslov } 1281e368fdb6SVlad Buslov 128212db03b6SVlad Buslov static void tcf_block_release(struct Qdisc *q, struct tcf_block *block, 128312db03b6SVlad Buslov bool rtnl_held) 1284e368fdb6SVlad Buslov { 1285787ce6d0SVlad Buslov if (!IS_ERR_OR_NULL(block)) 128612db03b6SVlad Buslov tcf_block_refcnt_put(block, rtnl_held); 1287787ce6d0SVlad Buslov 1288470502deSVlad Buslov if (q) { 1289470502deSVlad Buslov if (rtnl_held) 1290e368fdb6SVlad Buslov qdisc_put(q); 1291470502deSVlad Buslov else 1292470502deSVlad Buslov qdisc_put_unlocked(q); 1293470502deSVlad Buslov } 1294c431f89bSVlad Buslov } 1295c431f89bSVlad Buslov 1296f36fe1c4SJiri Pirko struct tcf_block_owner_item { 1297f36fe1c4SJiri Pirko struct list_head list; 1298f36fe1c4SJiri Pirko struct Qdisc *q; 129932f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type; 1300f36fe1c4SJiri Pirko }; 1301f36fe1c4SJiri Pirko 1302f36fe1c4SJiri Pirko static void 1303f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(struct tcf_block *block, 1304f36fe1c4SJiri Pirko struct Qdisc *q, 130532f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1306f36fe1c4SJiri Pirko { 1307f36fe1c4SJiri Pirko if (block->keep_dst && 130832f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && 130932f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 1310f36fe1c4SJiri Pirko netif_keep_dst(qdisc_dev(q)); 1311f36fe1c4SJiri Pirko } 1312f36fe1c4SJiri Pirko 1313f36fe1c4SJiri Pirko void tcf_block_netif_keep_dst(struct tcf_block *block) 1314f36fe1c4SJiri Pirko { 1315f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1316f36fe1c4SJiri Pirko 1317f36fe1c4SJiri Pirko block->keep_dst = true; 1318f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) 1319f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, item->q, 1320f36fe1c4SJiri Pirko item->binder_type); 1321f36fe1c4SJiri Pirko } 1322f36fe1c4SJiri Pirko EXPORT_SYMBOL(tcf_block_netif_keep_dst); 1323f36fe1c4SJiri Pirko 1324f36fe1c4SJiri Pirko static int tcf_block_owner_add(struct tcf_block *block, 1325f36fe1c4SJiri Pirko struct Qdisc *q, 132632f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1327f36fe1c4SJiri Pirko { 1328f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1329f36fe1c4SJiri Pirko 1330f36fe1c4SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 1331f36fe1c4SJiri Pirko if (!item) 1332f36fe1c4SJiri Pirko return -ENOMEM; 1333f36fe1c4SJiri Pirko item->q = q; 1334f36fe1c4SJiri Pirko item->binder_type = binder_type; 1335f36fe1c4SJiri Pirko list_add(&item->list, &block->owner_list); 1336f36fe1c4SJiri Pirko return 0; 1337f36fe1c4SJiri Pirko } 1338f36fe1c4SJiri Pirko 1339f36fe1c4SJiri Pirko static void tcf_block_owner_del(struct tcf_block *block, 1340f36fe1c4SJiri Pirko struct Qdisc *q, 134132f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1342f36fe1c4SJiri Pirko { 1343f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1344f36fe1c4SJiri Pirko 1345f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) { 1346f36fe1c4SJiri Pirko if (item->q == q && item->binder_type == binder_type) { 1347f36fe1c4SJiri Pirko list_del(&item->list); 1348f36fe1c4SJiri Pirko kfree(item); 1349f36fe1c4SJiri Pirko return; 1350f36fe1c4SJiri Pirko } 1351f36fe1c4SJiri Pirko } 1352f36fe1c4SJiri Pirko WARN_ON(1); 1353f36fe1c4SJiri Pirko } 1354f36fe1c4SJiri Pirko 135548617387SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 135648617387SJiri Pirko struct tcf_block_ext_info *ei, 135748617387SJiri Pirko struct netlink_ext_ack *extack) 135848617387SJiri Pirko { 135948617387SJiri Pirko struct net *net = qdisc_net(q); 136048617387SJiri Pirko struct tcf_block *block = NULL; 136148617387SJiri Pirko int err; 136248617387SJiri Pirko 1363787ce6d0SVlad Buslov if (ei->block_index) 136448617387SJiri Pirko /* block_index not 0 means the shared block is requested */ 1365787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, ei->block_index); 136648617387SJiri Pirko 136748617387SJiri Pirko if (!block) { 1368bb047dddSJiri Pirko block = tcf_block_create(net, q, ei->block_index, extack); 136948617387SJiri Pirko if (IS_ERR(block)) 137048617387SJiri Pirko return PTR_ERR(block); 1371bb047dddSJiri Pirko if (tcf_block_shared(block)) { 1372bb047dddSJiri Pirko err = tcf_block_insert(block, net, extack); 137348617387SJiri Pirko if (err) 137448617387SJiri Pirko goto err_block_insert; 137548617387SJiri Pirko } 137648617387SJiri Pirko } 137748617387SJiri Pirko 1378f36fe1c4SJiri Pirko err = tcf_block_owner_add(block, q, ei->binder_type); 1379f36fe1c4SJiri Pirko if (err) 1380f36fe1c4SJiri Pirko goto err_block_owner_add; 1381f36fe1c4SJiri Pirko 1382f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, q, ei->binder_type); 1383f36fe1c4SJiri Pirko 1384f71e0ca4SJiri Pirko err = tcf_chain0_head_change_cb_add(block, ei, extack); 1385a9b19443SJiri Pirko if (err) 1386f71e0ca4SJiri Pirko goto err_chain0_head_change_cb_add; 1387caa72601SJiri Pirko 138860513bd8SJohn Hurley err = tcf_block_offload_bind(block, q, ei, extack); 1389caa72601SJiri Pirko if (err) 1390caa72601SJiri Pirko goto err_block_offload_bind; 1391caa72601SJiri Pirko 13926529eabaSJiri Pirko *p_block = block; 13936529eabaSJiri Pirko return 0; 13942190d1d0SJiri Pirko 1395caa72601SJiri Pirko err_block_offload_bind: 1396f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1397f71e0ca4SJiri Pirko err_chain0_head_change_cb_add: 1398f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 1399f36fe1c4SJiri Pirko err_block_owner_add: 140048617387SJiri Pirko err_block_insert: 140112db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 14022190d1d0SJiri Pirko return err; 14036529eabaSJiri Pirko } 14048c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext); 14058c4083b3SJiri Pirko 1406c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 1407c7eb7d72SJiri Pirko { 1408c7eb7d72SJiri Pirko struct tcf_proto __rcu **p_filter_chain = priv; 1409c7eb7d72SJiri Pirko 1410c7eb7d72SJiri Pirko rcu_assign_pointer(*p_filter_chain, tp_head); 1411c7eb7d72SJiri Pirko } 1412c7eb7d72SJiri Pirko 14138c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block, 14148d1a77f9SAlexander Aring struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, 14158d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 14168c4083b3SJiri Pirko { 1417c7eb7d72SJiri Pirko struct tcf_block_ext_info ei = { 1418c7eb7d72SJiri Pirko .chain_head_change = tcf_chain_head_change_dflt, 1419c7eb7d72SJiri Pirko .chain_head_change_priv = p_filter_chain, 1420c7eb7d72SJiri Pirko }; 14218c4083b3SJiri Pirko 1422c7eb7d72SJiri Pirko WARN_ON(!p_filter_chain); 14238d1a77f9SAlexander Aring return tcf_block_get_ext(p_block, q, &ei, extack); 14248c4083b3SJiri Pirko } 14256529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get); 14266529eabaSJiri Pirko 14277aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound 1428a60b3f51SRoman Kapl * actions should be all removed after flushing. 1429e2ef7544SCong Wang */ 1430c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 1431e1ea2f98SDavid S. Miller struct tcf_block_ext_info *ei) 14327aa0045dSCong Wang { 1433c30abd5eSDavid S. Miller if (!block) 1434c30abd5eSDavid S. Miller return; 1435f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1436f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 143748617387SJiri Pirko 143812db03b6SVlad Buslov __tcf_block_put(block, q, ei, true); 143948617387SJiri Pirko } 14408c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext); 14418c4083b3SJiri Pirko 14428c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block) 14438c4083b3SJiri Pirko { 14448c4083b3SJiri Pirko struct tcf_block_ext_info ei = {0, }; 14458c4083b3SJiri Pirko 14464853f128SJiri Pirko if (!block) 14474853f128SJiri Pirko return; 1448c7eb7d72SJiri Pirko tcf_block_put_ext(block, block->q, &ei); 14498c4083b3SJiri Pirko } 1450e1ea2f98SDavid S. Miller 14516529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put); 1452cf1facdaSJiri Pirko 145332636742SJohn Hurley static int 1454a7323311SPablo Neira Ayuso tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, 145532636742SJohn Hurley void *cb_priv, bool add, bool offload_in_use, 145632636742SJohn Hurley struct netlink_ext_ack *extack) 145732636742SJohn Hurley { 1458bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 1459fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 146032636742SJohn Hurley int err; 146132636742SJohn Hurley 14624f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14634f8116c8SVlad Buslov 1464bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 1465bbf73830SVlad Buslov chain; 1466bbf73830SVlad Buslov chain_prev = chain, 1467bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 1468bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 1469fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); tp; 1470fe2923afSVlad Buslov tp_prev = tp, 1471fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 147212db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL)) { 147332636742SJohn Hurley if (tp->ops->reoffload) { 147432636742SJohn Hurley err = tp->ops->reoffload(tp, add, cb, cb_priv, 147532636742SJohn Hurley extack); 147632636742SJohn Hurley if (err && add) 147732636742SJohn Hurley goto err_playback_remove; 147832636742SJohn Hurley } else if (add && offload_in_use) { 147932636742SJohn Hurley err = -EOPNOTSUPP; 148032636742SJohn Hurley NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support"); 148132636742SJohn Hurley goto err_playback_remove; 148232636742SJohn Hurley } 148332636742SJohn Hurley } 148432636742SJohn Hurley } 148532636742SJohn Hurley 148632636742SJohn Hurley return 0; 148732636742SJohn Hurley 148832636742SJohn Hurley err_playback_remove: 148912db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 1490bbf73830SVlad Buslov tcf_chain_put(chain); 149132636742SJohn Hurley tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use, 149232636742SJohn Hurley extack); 149332636742SJohn Hurley return err; 149432636742SJohn Hurley } 149532636742SJohn Hurley 149659094b1eSPablo Neira Ayuso static int tcf_block_bind(struct tcf_block *block, 149759094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 149859094b1eSPablo Neira Ayuso { 149959094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 150059094b1eSPablo Neira Ayuso int err, i = 0; 150159094b1eSPablo Neira Ayuso 15024f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 15034f8116c8SVlad Buslov 150459094b1eSPablo Neira Ayuso list_for_each_entry(block_cb, &bo->cb_list, list) { 150559094b1eSPablo Neira Ayuso err = tcf_block_playback_offloads(block, block_cb->cb, 150659094b1eSPablo Neira Ayuso block_cb->cb_priv, true, 150759094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 150859094b1eSPablo Neira Ayuso bo->extack); 150959094b1eSPablo Neira Ayuso if (err) 151059094b1eSPablo Neira Ayuso goto err_unroll; 1511c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1512c9f14470SVlad Buslov block->lockeddevcnt++; 151359094b1eSPablo Neira Ayuso 151459094b1eSPablo Neira Ayuso i++; 151559094b1eSPablo Neira Ayuso } 151614bfb13fSPablo Neira Ayuso list_splice(&bo->cb_list, &block->flow_block.cb_list); 151759094b1eSPablo Neira Ayuso 151859094b1eSPablo Neira Ayuso return 0; 151959094b1eSPablo Neira Ayuso 152059094b1eSPablo Neira Ayuso err_unroll: 152159094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 152259094b1eSPablo Neira Ayuso if (i-- > 0) { 152359094b1eSPablo Neira Ayuso list_del(&block_cb->list); 152459094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 152559094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 152659094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 152759094b1eSPablo Neira Ayuso NULL); 1528c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1529c9f14470SVlad Buslov block->lockeddevcnt--; 153059094b1eSPablo Neira Ayuso } 153159094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 153259094b1eSPablo Neira Ayuso } 153359094b1eSPablo Neira Ayuso 153459094b1eSPablo Neira Ayuso return err; 153559094b1eSPablo Neira Ayuso } 153659094b1eSPablo Neira Ayuso 153759094b1eSPablo Neira Ayuso static void tcf_block_unbind(struct tcf_block *block, 153859094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 153959094b1eSPablo Neira Ayuso { 154059094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 154159094b1eSPablo Neira Ayuso 15424f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 15434f8116c8SVlad Buslov 154459094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 154559094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 154659094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 154759094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 154859094b1eSPablo Neira Ayuso NULL); 154959094b1eSPablo Neira Ayuso list_del(&block_cb->list); 155059094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 1551c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1552c9f14470SVlad Buslov block->lockeddevcnt--; 155359094b1eSPablo Neira Ayuso } 155459094b1eSPablo Neira Ayuso } 155559094b1eSPablo Neira Ayuso 155659094b1eSPablo Neira Ayuso static int tcf_block_setup(struct tcf_block *block, 155759094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 155859094b1eSPablo Neira Ayuso { 155959094b1eSPablo Neira Ayuso int err; 156059094b1eSPablo Neira Ayuso 156159094b1eSPablo Neira Ayuso switch (bo->command) { 156259094b1eSPablo Neira Ayuso case FLOW_BLOCK_BIND: 156359094b1eSPablo Neira Ayuso err = tcf_block_bind(block, bo); 156459094b1eSPablo Neira Ayuso break; 156559094b1eSPablo Neira Ayuso case FLOW_BLOCK_UNBIND: 156659094b1eSPablo Neira Ayuso err = 0; 156759094b1eSPablo Neira Ayuso tcf_block_unbind(block, bo); 156859094b1eSPablo Neira Ayuso break; 156959094b1eSPablo Neira Ayuso default: 157059094b1eSPablo Neira Ayuso WARN_ON_ONCE(1); 157159094b1eSPablo Neira Ayuso err = -EOPNOTSUPP; 157259094b1eSPablo Neira Ayuso } 157359094b1eSPablo Neira Ayuso 157459094b1eSPablo Neira Ayuso return err; 157559094b1eSPablo Neira Ayuso } 157659094b1eSPablo Neira Ayuso 157787d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached 157887d83093SJiri Pirko * to this qdisc, (optionally) tests for protocol and asks 157987d83093SJiri Pirko * specific classifiers. 158087d83093SJiri Pirko */ 15819410c940SPaul Blakey static inline int __tcf_classify(struct sk_buff *skb, 15829410c940SPaul Blakey const struct tcf_proto *tp, 1583af699626SPaul Blakey const struct tcf_proto *orig_tp, 15849410c940SPaul Blakey struct tcf_result *res, 15859410c940SPaul Blakey bool compat_mode, 15869410c940SPaul Blakey u32 *last_executed_chain) 158787d83093SJiri Pirko { 158887d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 158987d83093SJiri Pirko const int max_reclassify_loop = 4; 1590ee538dceSJiri Pirko const struct tcf_proto *first_tp; 159187d83093SJiri Pirko int limit = 0; 159287d83093SJiri Pirko 159387d83093SJiri Pirko reclassify: 159487d83093SJiri Pirko #endif 159587d83093SJiri Pirko for (; tp; tp = rcu_dereference_bh(tp->next)) { 1596cd0c4e70SCong Wang __be16 protocol = tc_skb_protocol(skb); 159787d83093SJiri Pirko int err; 159887d83093SJiri Pirko 159987d83093SJiri Pirko if (tp->protocol != protocol && 160087d83093SJiri Pirko tp->protocol != htons(ETH_P_ALL)) 160187d83093SJiri Pirko continue; 160287d83093SJiri Pirko 160387d83093SJiri Pirko err = tp->classify(skb, tp, res); 160487d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 1605db50514fSJiri Pirko if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 1606ee538dceSJiri Pirko first_tp = orig_tp; 16079410c940SPaul Blakey *last_executed_chain = first_tp->chain->index; 160887d83093SJiri Pirko goto reset; 1609db50514fSJiri Pirko } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 1610ee538dceSJiri Pirko first_tp = res->goto_tp; 16119410c940SPaul Blakey *last_executed_chain = err & TC_ACT_EXT_VAL_MASK; 1612db50514fSJiri Pirko goto reset; 1613db50514fSJiri Pirko } 161487d83093SJiri Pirko #endif 161587d83093SJiri Pirko if (err >= 0) 161687d83093SJiri Pirko return err; 161787d83093SJiri Pirko } 161887d83093SJiri Pirko 161987d83093SJiri Pirko return TC_ACT_UNSPEC; /* signal: continue lookup */ 162087d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 162187d83093SJiri Pirko reset: 162287d83093SJiri Pirko if (unlikely(limit++ >= max_reclassify_loop)) { 16239d3aaff3SJiri Pirko net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n", 16249d3aaff3SJiri Pirko tp->chain->block->index, 16259d3aaff3SJiri Pirko tp->prio & 0xffff, 162687d83093SJiri Pirko ntohs(tp->protocol)); 162787d83093SJiri Pirko return TC_ACT_SHOT; 162887d83093SJiri Pirko } 162987d83093SJiri Pirko 1630ee538dceSJiri Pirko tp = first_tp; 163187d83093SJiri Pirko goto reclassify; 163287d83093SJiri Pirko #endif 163387d83093SJiri Pirko } 16349410c940SPaul Blakey 16359410c940SPaul Blakey int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, 16369410c940SPaul Blakey struct tcf_result *res, bool compat_mode) 16379410c940SPaul Blakey { 16389410c940SPaul Blakey u32 last_executed_chain = 0; 16399410c940SPaul Blakey 1640af699626SPaul Blakey return __tcf_classify(skb, tp, tp, res, compat_mode, 16419410c940SPaul Blakey &last_executed_chain); 16429410c940SPaul Blakey } 164387d83093SJiri Pirko EXPORT_SYMBOL(tcf_classify); 164487d83093SJiri Pirko 16457d17c544SPaul Blakey int tcf_classify_ingress(struct sk_buff *skb, 16467d17c544SPaul Blakey const struct tcf_block *ingress_block, 16477d17c544SPaul Blakey const struct tcf_proto *tp, 16489410c940SPaul Blakey struct tcf_result *res, bool compat_mode) 16499410c940SPaul Blakey { 16509410c940SPaul Blakey #if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 16519410c940SPaul Blakey u32 last_executed_chain = 0; 16529410c940SPaul Blakey 1653af699626SPaul Blakey return __tcf_classify(skb, tp, tp, res, compat_mode, 16549410c940SPaul Blakey &last_executed_chain); 16559410c940SPaul Blakey #else 16569410c940SPaul Blakey u32 last_executed_chain = tp ? tp->chain->index : 0; 1657af699626SPaul Blakey const struct tcf_proto *orig_tp = tp; 16589410c940SPaul Blakey struct tc_skb_ext *ext; 16599410c940SPaul Blakey int ret; 16609410c940SPaul Blakey 1661af699626SPaul Blakey ext = skb_ext_find(skb, TC_SKB_EXT); 1662af699626SPaul Blakey 1663af699626SPaul Blakey if (ext && ext->chain) { 1664af699626SPaul Blakey struct tcf_chain *fchain; 1665af699626SPaul Blakey 1666af699626SPaul Blakey fchain = tcf_chain_lookup_rcu(ingress_block, ext->chain); 1667af699626SPaul Blakey if (!fchain) 1668af699626SPaul Blakey return TC_ACT_SHOT; 1669af699626SPaul Blakey 1670af699626SPaul Blakey /* Consume, so cloned/redirect skbs won't inherit ext */ 1671af699626SPaul Blakey skb_ext_del(skb, TC_SKB_EXT); 1672af699626SPaul Blakey 1673af699626SPaul Blakey tp = rcu_dereference_bh(fchain->filter_chain); 1674a080da6aSPaul Blakey last_executed_chain = fchain->index; 1675af699626SPaul Blakey } 1676af699626SPaul Blakey 1677af699626SPaul Blakey ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode, 1678af699626SPaul Blakey &last_executed_chain); 16799410c940SPaul Blakey 16809410c940SPaul Blakey /* If we missed on some chain */ 16819410c940SPaul Blakey if (ret == TC_ACT_UNSPEC && last_executed_chain) { 16829410c940SPaul Blakey ext = skb_ext_add(skb, TC_SKB_EXT); 16839410c940SPaul Blakey if (WARN_ON_ONCE(!ext)) 16849410c940SPaul Blakey return TC_ACT_SHOT; 16859410c940SPaul Blakey ext->chain = last_executed_chain; 16869410c940SPaul Blakey } 16879410c940SPaul Blakey 16889410c940SPaul Blakey return ret; 16899410c940SPaul Blakey #endif 16909410c940SPaul Blakey } 16919410c940SPaul Blakey EXPORT_SYMBOL(tcf_classify_ingress); 16929410c940SPaul Blakey 16932190d1d0SJiri Pirko struct tcf_chain_info { 16942190d1d0SJiri Pirko struct tcf_proto __rcu **pprev; 16952190d1d0SJiri Pirko struct tcf_proto __rcu *next; 16962190d1d0SJiri Pirko }; 16972190d1d0SJiri Pirko 1698ed76f5edSVlad Buslov static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain, 1699ed76f5edSVlad Buslov struct tcf_chain_info *chain_info) 17002190d1d0SJiri Pirko { 1701ed76f5edSVlad Buslov return tcf_chain_dereference(*chain_info->pprev, chain); 17022190d1d0SJiri Pirko } 17032190d1d0SJiri Pirko 1704726d0612SVlad Buslov static int tcf_chain_tp_insert(struct tcf_chain *chain, 17052190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 17062190d1d0SJiri Pirko struct tcf_proto *tp) 17072190d1d0SJiri Pirko { 1708726d0612SVlad Buslov if (chain->flushing) 1709726d0612SVlad Buslov return -EAGAIN; 1710726d0612SVlad Buslov 1711c7eb7d72SJiri Pirko if (*chain_info->pprev == chain->filter_chain) 1712f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, tp); 17134dbfa766SVlad Buslov tcf_proto_get(tp); 1714ed76f5edSVlad Buslov RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info)); 17152190d1d0SJiri Pirko rcu_assign_pointer(*chain_info->pprev, tp); 1716726d0612SVlad Buslov 1717726d0612SVlad Buslov return 0; 17182190d1d0SJiri Pirko } 17192190d1d0SJiri Pirko 17202190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain, 17212190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 17222190d1d0SJiri Pirko struct tcf_proto *tp) 17232190d1d0SJiri Pirko { 1724ed76f5edSVlad Buslov struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain); 17252190d1d0SJiri Pirko 17268b64678eSVlad Buslov tcf_proto_mark_delete(tp); 1727c7eb7d72SJiri Pirko if (tp == chain->filter_chain) 1728f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, next); 17292190d1d0SJiri Pirko RCU_INIT_POINTER(*chain_info->pprev, next); 17302190d1d0SJiri Pirko } 17312190d1d0SJiri Pirko 17322190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 17332190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 17342190d1d0SJiri Pirko u32 protocol, u32 prio, 17358b64678eSVlad Buslov bool prio_allocate); 17368b64678eSVlad Buslov 17378b64678eSVlad Buslov /* Try to insert new proto. 17388b64678eSVlad Buslov * If proto with specified priority already exists, free new proto 17398b64678eSVlad Buslov * and return existing one. 17408b64678eSVlad Buslov */ 17418b64678eSVlad Buslov 17428b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain, 17438b64678eSVlad Buslov struct tcf_proto *tp_new, 174412db03b6SVlad Buslov u32 protocol, u32 prio, 174512db03b6SVlad Buslov bool rtnl_held) 17468b64678eSVlad Buslov { 17478b64678eSVlad Buslov struct tcf_chain_info chain_info; 17488b64678eSVlad Buslov struct tcf_proto *tp; 1749726d0612SVlad Buslov int err = 0; 17508b64678eSVlad Buslov 17518b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 17528b64678eSVlad Buslov 175359eb87cbSJohn Hurley if (tcf_proto_exists_destroying(chain, tp_new)) { 175459eb87cbSJohn Hurley mutex_unlock(&chain->filter_chain_lock); 175559eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 175659eb87cbSJohn Hurley return ERR_PTR(-EAGAIN); 175759eb87cbSJohn Hurley } 175859eb87cbSJohn Hurley 17598b64678eSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, 17608b64678eSVlad Buslov protocol, prio, false); 17618b64678eSVlad Buslov if (!tp) 1762726d0612SVlad Buslov err = tcf_chain_tp_insert(chain, &chain_info, tp_new); 17638b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17648b64678eSVlad Buslov 17658b64678eSVlad Buslov if (tp) { 176659eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 17678b64678eSVlad Buslov tp_new = tp; 1768726d0612SVlad Buslov } else if (err) { 176959eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 1770726d0612SVlad Buslov tp_new = ERR_PTR(err); 17718b64678eSVlad Buslov } 17728b64678eSVlad Buslov 17738b64678eSVlad Buslov return tp_new; 17748b64678eSVlad Buslov } 17758b64678eSVlad Buslov 17768b64678eSVlad Buslov static void tcf_chain_tp_delete_empty(struct tcf_chain *chain, 177712db03b6SVlad Buslov struct tcf_proto *tp, bool rtnl_held, 17788b64678eSVlad Buslov struct netlink_ext_ack *extack) 17798b64678eSVlad Buslov { 17808b64678eSVlad Buslov struct tcf_chain_info chain_info; 17818b64678eSVlad Buslov struct tcf_proto *tp_iter; 17828b64678eSVlad Buslov struct tcf_proto **pprev; 17838b64678eSVlad Buslov struct tcf_proto *next; 17848b64678eSVlad Buslov 17858b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 17868b64678eSVlad Buslov 17878b64678eSVlad Buslov /* Atomically find and remove tp from chain. */ 17888b64678eSVlad Buslov for (pprev = &chain->filter_chain; 17898b64678eSVlad Buslov (tp_iter = tcf_chain_dereference(*pprev, chain)); 17908b64678eSVlad Buslov pprev = &tp_iter->next) { 17918b64678eSVlad Buslov if (tp_iter == tp) { 17928b64678eSVlad Buslov chain_info.pprev = pprev; 17938b64678eSVlad Buslov chain_info.next = tp_iter->next; 17948b64678eSVlad Buslov WARN_ON(tp_iter->deleting); 17958b64678eSVlad Buslov break; 17968b64678eSVlad Buslov } 17978b64678eSVlad Buslov } 17988b64678eSVlad Buslov /* Verify that tp still exists and no new filters were inserted 17998b64678eSVlad Buslov * concurrently. 18008b64678eSVlad Buslov * Mark tp for deletion if it is empty. 18018b64678eSVlad Buslov */ 1802a5b72a08SDavide Caratti if (!tp_iter || !tcf_proto_check_delete(tp)) { 18038b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 18048b64678eSVlad Buslov return; 18058b64678eSVlad Buslov } 18068b64678eSVlad Buslov 180759eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 18088b64678eSVlad Buslov next = tcf_chain_dereference(chain_info.next, chain); 18098b64678eSVlad Buslov if (tp == chain->filter_chain) 18108b64678eSVlad Buslov tcf_chain0_head_change(chain, next); 18118b64678eSVlad Buslov RCU_INIT_POINTER(*chain_info.pprev, next); 18128b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 18138b64678eSVlad Buslov 181412db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, extack); 18158b64678eSVlad Buslov } 18168b64678eSVlad Buslov 18178b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 18188b64678eSVlad Buslov struct tcf_chain_info *chain_info, 18198b64678eSVlad Buslov u32 protocol, u32 prio, 18202190d1d0SJiri Pirko bool prio_allocate) 18212190d1d0SJiri Pirko { 18222190d1d0SJiri Pirko struct tcf_proto **pprev; 18232190d1d0SJiri Pirko struct tcf_proto *tp; 18242190d1d0SJiri Pirko 18252190d1d0SJiri Pirko /* Check the chain for existence of proto-tcf with this priority */ 18262190d1d0SJiri Pirko for (pprev = &chain->filter_chain; 1827ed76f5edSVlad Buslov (tp = tcf_chain_dereference(*pprev, chain)); 1828ed76f5edSVlad Buslov pprev = &tp->next) { 18292190d1d0SJiri Pirko if (tp->prio >= prio) { 18302190d1d0SJiri Pirko if (tp->prio == prio) { 18312190d1d0SJiri Pirko if (prio_allocate || 18322190d1d0SJiri Pirko (tp->protocol != protocol && protocol)) 18332190d1d0SJiri Pirko return ERR_PTR(-EINVAL); 18342190d1d0SJiri Pirko } else { 18352190d1d0SJiri Pirko tp = NULL; 18362190d1d0SJiri Pirko } 18372190d1d0SJiri Pirko break; 18382190d1d0SJiri Pirko } 18392190d1d0SJiri Pirko } 18402190d1d0SJiri Pirko chain_info->pprev = pprev; 18414dbfa766SVlad Buslov if (tp) { 18424dbfa766SVlad Buslov chain_info->next = tp->next; 18434dbfa766SVlad Buslov tcf_proto_get(tp); 18444dbfa766SVlad Buslov } else { 18454dbfa766SVlad Buslov chain_info->next = NULL; 18464dbfa766SVlad Buslov } 18472190d1d0SJiri Pirko return tp; 18482190d1d0SJiri Pirko } 18492190d1d0SJiri Pirko 18507120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb, 18517960d1daSJiri Pirko struct tcf_proto *tp, struct tcf_block *block, 18527960d1daSJiri Pirko struct Qdisc *q, u32 parent, void *fh, 185312db03b6SVlad Buslov u32 portid, u32 seq, u16 flags, int event, 1854*f8ab1807SVlad Buslov bool terse_dump, bool rtnl_held) 18557120371cSWANG Cong { 18567120371cSWANG Cong struct tcmsg *tcm; 18577120371cSWANG Cong struct nlmsghdr *nlh; 18587120371cSWANG Cong unsigned char *b = skb_tail_pointer(skb); 18597120371cSWANG Cong 18607120371cSWANG Cong nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 18617120371cSWANG Cong if (!nlh) 18627120371cSWANG Cong goto out_nlmsg_trim; 18637120371cSWANG Cong tcm = nlmsg_data(nlh); 18647120371cSWANG Cong tcm->tcm_family = AF_UNSPEC; 18657120371cSWANG Cong tcm->tcm__pad1 = 0; 18667120371cSWANG Cong tcm->tcm__pad2 = 0; 18677960d1daSJiri Pirko if (q) { 1868a10fa201SJiri Pirko tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 1869a10fa201SJiri Pirko tcm->tcm_parent = parent; 18707960d1daSJiri Pirko } else { 18717960d1daSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 18727960d1daSJiri Pirko tcm->tcm_block_index = block->index; 18737960d1daSJiri Pirko } 18747120371cSWANG Cong tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 18757120371cSWANG Cong if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 18767120371cSWANG Cong goto nla_put_failure; 18777120371cSWANG Cong if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 18787120371cSWANG Cong goto nla_put_failure; 18797120371cSWANG Cong if (!fh) { 18807120371cSWANG Cong tcm->tcm_handle = 0; 1881*f8ab1807SVlad Buslov } else if (terse_dump) { 1882*f8ab1807SVlad Buslov if (tp->ops->terse_dump) { 1883*f8ab1807SVlad Buslov if (tp->ops->terse_dump(net, tp, fh, skb, tcm, 1884*f8ab1807SVlad Buslov rtnl_held) < 0) 1885*f8ab1807SVlad Buslov goto nla_put_failure; 1886*f8ab1807SVlad Buslov } else { 1887*f8ab1807SVlad Buslov goto cls_op_not_supp; 1888*f8ab1807SVlad Buslov } 18897120371cSWANG Cong } else { 189012db03b6SVlad Buslov if (tp->ops->dump && 189112db03b6SVlad Buslov tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) 18927120371cSWANG Cong goto nla_put_failure; 18937120371cSWANG Cong } 18947120371cSWANG Cong nlh->nlmsg_len = skb_tail_pointer(skb) - b; 18957120371cSWANG Cong return skb->len; 18967120371cSWANG Cong 18977120371cSWANG Cong out_nlmsg_trim: 18987120371cSWANG Cong nla_put_failure: 1899*f8ab1807SVlad Buslov cls_op_not_supp: 19007120371cSWANG Cong nlmsg_trim(skb, b); 19017120371cSWANG Cong return -1; 19027120371cSWANG Cong } 19037120371cSWANG Cong 19047120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb, 19057120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 19067960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 190712db03b6SVlad Buslov u32 parent, void *fh, int event, bool unicast, 190812db03b6SVlad Buslov bool rtnl_held) 19097120371cSWANG Cong { 19107120371cSWANG Cong struct sk_buff *skb; 19117120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 19125b5f99b1SZhike Wang int err = 0; 19137120371cSWANG Cong 19147120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 19157120371cSWANG Cong if (!skb) 19167120371cSWANG Cong return -ENOBUFS; 19177120371cSWANG Cong 19187960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 191912db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, event, 1920*f8ab1807SVlad Buslov false, rtnl_held) <= 0) { 19217120371cSWANG Cong kfree_skb(skb); 19227120371cSWANG Cong return -EINVAL; 19237120371cSWANG Cong } 19247120371cSWANG Cong 19257120371cSWANG Cong if (unicast) 19265b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 19275b5f99b1SZhike Wang else 19285b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 19297120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 19305b5f99b1SZhike Wang 19315b5f99b1SZhike Wang if (err > 0) 19325b5f99b1SZhike Wang err = 0; 19335b5f99b1SZhike Wang return err; 19347120371cSWANG Cong } 19357120371cSWANG Cong 19367120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 19377120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 19387960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 1939c35a4accSAlexander Aring u32 parent, void *fh, bool unicast, bool *last, 194012db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 19417120371cSWANG Cong { 19427120371cSWANG Cong struct sk_buff *skb; 19437120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 19447120371cSWANG Cong int err; 19457120371cSWANG Cong 19467120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 19477120371cSWANG Cong if (!skb) 19487120371cSWANG Cong return -ENOBUFS; 19497120371cSWANG Cong 19507960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 195112db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, 1952*f8ab1807SVlad Buslov false, rtnl_held) <= 0) { 1953c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to build del event notification"); 19547120371cSWANG Cong kfree_skb(skb); 19557120371cSWANG Cong return -EINVAL; 19567120371cSWANG Cong } 19577120371cSWANG Cong 195812db03b6SVlad Buslov err = tp->ops->delete(tp, fh, last, rtnl_held, extack); 19597120371cSWANG Cong if (err) { 19607120371cSWANG Cong kfree_skb(skb); 19617120371cSWANG Cong return err; 19627120371cSWANG Cong } 19637120371cSWANG Cong 19647120371cSWANG Cong if (unicast) 19655b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 19665b5f99b1SZhike Wang else 1967c35a4accSAlexander Aring err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 19687120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 1969c35a4accSAlexander Aring if (err < 0) 1970c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to send filter delete notification"); 19715b5f99b1SZhike Wang 19725b5f99b1SZhike Wang if (err > 0) 19735b5f99b1SZhike Wang err = 0; 1974c35a4accSAlexander Aring return err; 19757120371cSWANG Cong } 19767120371cSWANG Cong 19777120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 19787960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 19797960d1daSJiri Pirko u32 parent, struct nlmsghdr *n, 198012db03b6SVlad Buslov struct tcf_chain *chain, int event, 198112db03b6SVlad Buslov bool rtnl_held) 19827120371cSWANG Cong { 19837120371cSWANG Cong struct tcf_proto *tp; 19847120371cSWANG Cong 198512db03b6SVlad Buslov for (tp = tcf_get_next_proto(chain, NULL, rtnl_held); 198612db03b6SVlad Buslov tp; tp = tcf_get_next_proto(chain, tp, rtnl_held)) 19877960d1daSJiri Pirko tfilter_notify(net, oskb, n, tp, block, 198812db03b6SVlad Buslov q, parent, NULL, event, false, rtnl_held); 19897120371cSWANG Cong } 19907120371cSWANG Cong 19917d5509faSVlad Buslov static void tfilter_put(struct tcf_proto *tp, void *fh) 19927d5509faSVlad Buslov { 19937d5509faSVlad Buslov if (tp->ops->put && fh) 19947d5509faSVlad Buslov tp->ops->put(tp, fh); 19957d5509faSVlad Buslov } 19967d5509faSVlad Buslov 1997c431f89bSVlad Buslov static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1998c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19991da177e4SLinus Torvalds { 20003b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2001add93b61SPatrick McHardy struct nlattr *tca[TCA_MAX + 1]; 20026f96c3c6SCong Wang char name[IFNAMSIZ]; 20031da177e4SLinus Torvalds struct tcmsg *t; 20041da177e4SLinus Torvalds u32 protocol; 20051da177e4SLinus Torvalds u32 prio; 20069d36d9e5SJiri Pirko bool prio_allocate; 20071da177e4SLinus Torvalds u32 parent; 20085bc17018SJiri Pirko u32 chain_index; 20097960d1daSJiri Pirko struct Qdisc *q = NULL; 20102190d1d0SJiri Pirko struct tcf_chain_info chain_info; 20115bc17018SJiri Pirko struct tcf_chain *chain = NULL; 20126529eabaSJiri Pirko struct tcf_block *block; 20131da177e4SLinus Torvalds struct tcf_proto *tp; 20141da177e4SLinus Torvalds unsigned long cl; 20158113c095SWANG Cong void *fh; 20161da177e4SLinus Torvalds int err; 2017628185cfSDaniel Borkmann int tp_created; 2018470502deSVlad Buslov bool rtnl_held = false; 20191da177e4SLinus Torvalds 2020c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 2021dfc47ef8SEric W. Biederman return -EPERM; 2022de179c8cSHong zhi guo 20231da177e4SLinus Torvalds replay: 2024628185cfSDaniel Borkmann tp_created = 0; 2025628185cfSDaniel Borkmann 20268cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 20278cb08174SJohannes Berg rtm_tca_policy, extack); 2028de179c8cSHong zhi guo if (err < 0) 2029de179c8cSHong zhi guo return err; 2030de179c8cSHong zhi guo 2031942b8165SDavid S. Miller t = nlmsg_data(n); 20321da177e4SLinus Torvalds protocol = TC_H_MIN(t->tcm_info); 20331da177e4SLinus Torvalds prio = TC_H_MAJ(t->tcm_info); 20349d36d9e5SJiri Pirko prio_allocate = false; 20351da177e4SLinus Torvalds parent = t->tcm_parent; 20364dbfa766SVlad Buslov tp = NULL; 20371da177e4SLinus Torvalds cl = 0; 2038470502deSVlad Buslov block = NULL; 20391da177e4SLinus Torvalds 20401da177e4SLinus Torvalds if (prio == 0) { 2041ea7f8277SDaniel Borkmann /* If no priority is provided by the user, 2042ea7f8277SDaniel Borkmann * we allocate one. 2043ea7f8277SDaniel Borkmann */ 2044ea7f8277SDaniel Borkmann if (n->nlmsg_flags & NLM_F_CREATE) { 20451da177e4SLinus Torvalds prio = TC_H_MAKE(0x80000000U, 0U); 20469d36d9e5SJiri Pirko prio_allocate = true; 2047c431f89bSVlad Buslov } else { 2048c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 2049ea7f8277SDaniel Borkmann return -ENOENT; 2050ea7f8277SDaniel Borkmann } 20511da177e4SLinus Torvalds } 20521da177e4SLinus Torvalds 20531da177e4SLinus Torvalds /* Find head of filter chain. */ 20541da177e4SLinus Torvalds 2055470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2056470502deSVlad Buslov if (err) 2057470502deSVlad Buslov return err; 2058470502deSVlad Buslov 20596f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 20606f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 20616f96c3c6SCong Wang err = -EINVAL; 20626f96c3c6SCong Wang goto errout; 20636f96c3c6SCong Wang } 20646f96c3c6SCong Wang 2065470502deSVlad Buslov /* Take rtnl mutex if rtnl_held was set to true on previous iteration, 2066470502deSVlad Buslov * block is shared (no qdisc found), qdisc is not unlocked, classifier 2067470502deSVlad Buslov * type is not specified, classifier is not unlocked. 2068470502deSVlad Buslov */ 2069470502deSVlad Buslov if (rtnl_held || 2070470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 20716f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2072470502deSVlad Buslov rtnl_held = true; 2073470502deSVlad Buslov rtnl_lock(); 2074470502deSVlad Buslov } 2075470502deSVlad Buslov 2076470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2077470502deSVlad Buslov if (err) 2078470502deSVlad Buslov goto errout; 2079470502deSVlad Buslov 2080470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2081470502deSVlad Buslov extack); 2082c431f89bSVlad Buslov if (IS_ERR(block)) { 2083c431f89bSVlad Buslov err = PTR_ERR(block); 20847960d1daSJiri Pirko goto errout; 20857960d1daSJiri Pirko } 2086a7df4870SCong Wang block->classid = parent; 20875bc17018SJiri Pirko 20885bc17018SJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 20895bc17018SJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 2090c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 20915bc17018SJiri Pirko err = -EINVAL; 20925bc17018SJiri Pirko goto errout; 20935bc17018SJiri Pirko } 2094c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, true); 20955bc17018SJiri Pirko if (!chain) { 2096d5ed72a5SJiri Pirko NL_SET_ERR_MSG(extack, "Cannot create specified filter chain"); 2097c431f89bSVlad Buslov err = -ENOMEM; 2098ea7f8277SDaniel Borkmann goto errout; 2099ea7f8277SDaniel Borkmann } 21001da177e4SLinus Torvalds 2101ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 21022190d1d0SJiri Pirko tp = tcf_chain_tp_find(chain, &chain_info, protocol, 21032190d1d0SJiri Pirko prio, prio_allocate); 21042190d1d0SJiri Pirko if (IS_ERR(tp)) { 2105c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 21062190d1d0SJiri Pirko err = PTR_ERR(tp); 2107ed76f5edSVlad Buslov goto errout_locked; 21086bb16e7aSJiri Pirko } 21091da177e4SLinus Torvalds 21101da177e4SLinus Torvalds if (tp == NULL) { 21118b64678eSVlad Buslov struct tcf_proto *tp_new = NULL; 21128b64678eSVlad Buslov 2113726d0612SVlad Buslov if (chain->flushing) { 2114726d0612SVlad Buslov err = -EAGAIN; 2115726d0612SVlad Buslov goto errout_locked; 2116726d0612SVlad Buslov } 2117726d0612SVlad Buslov 21181da177e4SLinus Torvalds /* Proto-tcf does not exist, create new one */ 21191da177e4SLinus Torvalds 21206bb16e7aSJiri Pirko if (tca[TCA_KIND] == NULL || !protocol) { 2121c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified"); 21226bb16e7aSJiri Pirko err = -EINVAL; 2123ed76f5edSVlad Buslov goto errout_locked; 21246bb16e7aSJiri Pirko } 21251da177e4SLinus Torvalds 2126c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2127c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 21286bb16e7aSJiri Pirko err = -ENOENT; 2129ed76f5edSVlad Buslov goto errout_locked; 21306bb16e7aSJiri Pirko } 21311da177e4SLinus Torvalds 21329d36d9e5SJiri Pirko if (prio_allocate) 2133ed76f5edSVlad Buslov prio = tcf_auto_prio(tcf_chain_tp_prev(chain, 2134ed76f5edSVlad Buslov &chain_info)); 21351da177e4SLinus Torvalds 2136ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 213736d79af7SEric Dumazet tp_new = tcf_proto_create(name, protocol, prio, chain, 213836d79af7SEric Dumazet rtnl_held, extack); 21398b64678eSVlad Buslov if (IS_ERR(tp_new)) { 21408b64678eSVlad Buslov err = PTR_ERR(tp_new); 2141726d0612SVlad Buslov goto errout_tp; 21421da177e4SLinus Torvalds } 2143ed76f5edSVlad Buslov 214412186be7SMinoru Usui tp_created = 1; 214512db03b6SVlad Buslov tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio, 214612db03b6SVlad Buslov rtnl_held); 2147726d0612SVlad Buslov if (IS_ERR(tp)) { 2148726d0612SVlad Buslov err = PTR_ERR(tp); 2149726d0612SVlad Buslov goto errout_tp; 2150726d0612SVlad Buslov } 2151ed76f5edSVlad Buslov } else { 2152ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 21536bb16e7aSJiri Pirko } 21541da177e4SLinus Torvalds 21558b64678eSVlad Buslov if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 21568b64678eSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 21578b64678eSVlad Buslov err = -EINVAL; 21588b64678eSVlad Buslov goto errout; 21598b64678eSVlad Buslov } 21608b64678eSVlad Buslov 21611da177e4SLinus Torvalds fh = tp->ops->get(tp, t->tcm_handle); 21621da177e4SLinus Torvalds 21638113c095SWANG Cong if (!fh) { 2164c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2165c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 21666bb16e7aSJiri Pirko err = -ENOENT; 21671da177e4SLinus Torvalds goto errout; 21686bb16e7aSJiri Pirko } 2169c431f89bSVlad Buslov } else if (n->nlmsg_flags & NLM_F_EXCL) { 21707d5509faSVlad Buslov tfilter_put(tp, fh); 2171c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter already exists"); 21726bb16e7aSJiri Pirko err = -EEXIST; 21731da177e4SLinus Torvalds goto errout; 217412186be7SMinoru Usui } 21751da177e4SLinus Torvalds 21769f407f17SJiri Pirko if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) { 21779f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind"); 21789f407f17SJiri Pirko err = -EINVAL; 21799f407f17SJiri Pirko goto errout; 21809f407f17SJiri Pirko } 21819f407f17SJiri Pirko 21822f7ef2f8SCong Wang err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 21837306db38SAlexander Aring n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE, 218412db03b6SVlad Buslov rtnl_held, extack); 21857d5509faSVlad Buslov if (err == 0) { 21867960d1daSJiri Pirko tfilter_notify(net, skb, n, tp, block, q, parent, fh, 218712db03b6SVlad Buslov RTM_NEWTFILTER, false, rtnl_held); 21887d5509faSVlad Buslov tfilter_put(tp, fh); 2189503d81d4SVlad Buslov /* q pointer is NULL for shared blocks */ 2190503d81d4SVlad Buslov if (q) 21913f05e688SCong Wang q->flags &= ~TCQ_F_CAN_BYPASS; 21927d5509faSVlad Buslov } 21931da177e4SLinus Torvalds 21941da177e4SLinus Torvalds errout: 21958b64678eSVlad Buslov if (err && tp_created) 219612db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL); 2197726d0612SVlad Buslov errout_tp: 21984dbfa766SVlad Buslov if (chain) { 21994dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 220012db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 22014dbfa766SVlad Buslov if (!tp_created) 22024dbfa766SVlad Buslov tcf_chain_put(chain); 22034dbfa766SVlad Buslov } 220412db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2205470502deSVlad Buslov 2206470502deSVlad Buslov if (rtnl_held) 2207470502deSVlad Buslov rtnl_unlock(); 2208470502deSVlad Buslov 2209470502deSVlad Buslov if (err == -EAGAIN) { 2210470502deSVlad Buslov /* Take rtnl lock in case EAGAIN is caused by concurrent flush 2211470502deSVlad Buslov * of target chain. 2212470502deSVlad Buslov */ 2213470502deSVlad Buslov rtnl_held = true; 22141da177e4SLinus Torvalds /* Replay the request. */ 22151da177e4SLinus Torvalds goto replay; 2216470502deSVlad Buslov } 22171da177e4SLinus Torvalds return err; 2218ed76f5edSVlad Buslov 2219ed76f5edSVlad Buslov errout_locked: 2220ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2221ed76f5edSVlad Buslov goto errout; 22221da177e4SLinus Torvalds } 22231da177e4SLinus Torvalds 2224c431f89bSVlad Buslov static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2225c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2226c431f89bSVlad Buslov { 2227c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2228c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 22296f96c3c6SCong Wang char name[IFNAMSIZ]; 2230c431f89bSVlad Buslov struct tcmsg *t; 2231c431f89bSVlad Buslov u32 protocol; 2232c431f89bSVlad Buslov u32 prio; 2233c431f89bSVlad Buslov u32 parent; 2234c431f89bSVlad Buslov u32 chain_index; 2235c431f89bSVlad Buslov struct Qdisc *q = NULL; 2236c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2237c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2238470502deSVlad Buslov struct tcf_block *block = NULL; 2239c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2240c431f89bSVlad Buslov unsigned long cl = 0; 2241c431f89bSVlad Buslov void *fh = NULL; 2242c431f89bSVlad Buslov int err; 2243470502deSVlad Buslov bool rtnl_held = false; 2244c431f89bSVlad Buslov 2245c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 2246c431f89bSVlad Buslov return -EPERM; 2247c431f89bSVlad Buslov 22488cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 22498cb08174SJohannes Berg rtm_tca_policy, extack); 2250c431f89bSVlad Buslov if (err < 0) 2251c431f89bSVlad Buslov return err; 2252c431f89bSVlad Buslov 2253c431f89bSVlad Buslov t = nlmsg_data(n); 2254c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2255c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2256c431f89bSVlad Buslov parent = t->tcm_parent; 2257c431f89bSVlad Buslov 2258c431f89bSVlad Buslov if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) { 2259c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set"); 2260c431f89bSVlad Buslov return -ENOENT; 2261c431f89bSVlad Buslov } 2262c431f89bSVlad Buslov 2263c431f89bSVlad Buslov /* Find head of filter chain. */ 2264c431f89bSVlad Buslov 2265470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2266470502deSVlad Buslov if (err) 2267470502deSVlad Buslov return err; 2268470502deSVlad Buslov 22696f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 22706f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 22716f96c3c6SCong Wang err = -EINVAL; 22726f96c3c6SCong Wang goto errout; 22736f96c3c6SCong Wang } 2274470502deSVlad Buslov /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc 2275470502deSVlad Buslov * found), qdisc is not unlocked, classifier type is not specified, 2276470502deSVlad Buslov * classifier is not unlocked. 2277470502deSVlad Buslov */ 2278470502deSVlad Buslov if (!prio || 2279470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 22806f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2281470502deSVlad Buslov rtnl_held = true; 2282470502deSVlad Buslov rtnl_lock(); 2283470502deSVlad Buslov } 2284470502deSVlad Buslov 2285470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2286470502deSVlad Buslov if (err) 2287470502deSVlad Buslov goto errout; 2288470502deSVlad Buslov 2289470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2290470502deSVlad Buslov extack); 2291c431f89bSVlad Buslov if (IS_ERR(block)) { 2292c431f89bSVlad Buslov err = PTR_ERR(block); 2293c431f89bSVlad Buslov goto errout; 2294c431f89bSVlad Buslov } 2295c431f89bSVlad Buslov 2296c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2297c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2298c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2299c431f89bSVlad Buslov err = -EINVAL; 2300c431f89bSVlad Buslov goto errout; 2301c431f89bSVlad Buslov } 2302c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2303c431f89bSVlad Buslov if (!chain) { 23045ca8a25cSJiri Pirko /* User requested flush on non-existent chain. Nothing to do, 23055ca8a25cSJiri Pirko * so just return success. 23065ca8a25cSJiri Pirko */ 23075ca8a25cSJiri Pirko if (prio == 0) { 23085ca8a25cSJiri Pirko err = 0; 23095ca8a25cSJiri Pirko goto errout; 23105ca8a25cSJiri Pirko } 2311c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2312b7b4247dSJiri Pirko err = -ENOENT; 2313c431f89bSVlad Buslov goto errout; 2314c431f89bSVlad Buslov } 2315c431f89bSVlad Buslov 2316c431f89bSVlad Buslov if (prio == 0) { 2317c431f89bSVlad Buslov tfilter_notify_chain(net, skb, block, q, parent, n, 231812db03b6SVlad Buslov chain, RTM_DELTFILTER, rtnl_held); 231912db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 2320c431f89bSVlad Buslov err = 0; 2321c431f89bSVlad Buslov goto errout; 2322c431f89bSVlad Buslov } 2323c431f89bSVlad Buslov 2324ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2325c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2326c431f89bSVlad Buslov prio, false); 2327c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2328c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 23290e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2330ed76f5edSVlad Buslov goto errout_locked; 2331c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2332c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2333c431f89bSVlad Buslov err = -EINVAL; 2334ed76f5edSVlad Buslov goto errout_locked; 2335ed76f5edSVlad Buslov } else if (t->tcm_handle == 0) { 233659eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 2337c431f89bSVlad Buslov tcf_chain_tp_remove(chain, &chain_info, tp); 2338ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2339ed76f5edSVlad Buslov 234012db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2341c431f89bSVlad Buslov tfilter_notify(net, skb, n, tp, block, q, parent, fh, 234212db03b6SVlad Buslov RTM_DELTFILTER, false, rtnl_held); 2343c431f89bSVlad Buslov err = 0; 2344ed76f5edSVlad Buslov goto errout; 2345ed76f5edSVlad Buslov } 2346ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2347ed76f5edSVlad Buslov 2348ed76f5edSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2349ed76f5edSVlad Buslov 2350ed76f5edSVlad Buslov if (!fh) { 2351c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2352c431f89bSVlad Buslov err = -ENOENT; 2353c431f89bSVlad Buslov } else { 2354c431f89bSVlad Buslov bool last; 2355c431f89bSVlad Buslov 2356c431f89bSVlad Buslov err = tfilter_del_notify(net, skb, n, tp, block, 2357c431f89bSVlad Buslov q, parent, fh, false, &last, 235812db03b6SVlad Buslov rtnl_held, extack); 235912db03b6SVlad Buslov 2360c431f89bSVlad Buslov if (err) 2361c431f89bSVlad Buslov goto errout; 23628b64678eSVlad Buslov if (last) 236312db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack); 2364c431f89bSVlad Buslov } 2365c431f89bSVlad Buslov 2366c431f89bSVlad Buslov errout: 23674dbfa766SVlad Buslov if (chain) { 23684dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 236912db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2370c431f89bSVlad Buslov tcf_chain_put(chain); 23714dbfa766SVlad Buslov } 237212db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2373470502deSVlad Buslov 2374470502deSVlad Buslov if (rtnl_held) 2375470502deSVlad Buslov rtnl_unlock(); 2376470502deSVlad Buslov 2377c431f89bSVlad Buslov return err; 2378ed76f5edSVlad Buslov 2379ed76f5edSVlad Buslov errout_locked: 2380ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2381ed76f5edSVlad Buslov goto errout; 2382c431f89bSVlad Buslov } 2383c431f89bSVlad Buslov 2384c431f89bSVlad Buslov static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2385c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2386c431f89bSVlad Buslov { 2387c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2388c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 23896f96c3c6SCong Wang char name[IFNAMSIZ]; 2390c431f89bSVlad Buslov struct tcmsg *t; 2391c431f89bSVlad Buslov u32 protocol; 2392c431f89bSVlad Buslov u32 prio; 2393c431f89bSVlad Buslov u32 parent; 2394c431f89bSVlad Buslov u32 chain_index; 2395c431f89bSVlad Buslov struct Qdisc *q = NULL; 2396c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2397c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2398470502deSVlad Buslov struct tcf_block *block = NULL; 2399c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2400c431f89bSVlad Buslov unsigned long cl = 0; 2401c431f89bSVlad Buslov void *fh = NULL; 2402c431f89bSVlad Buslov int err; 2403470502deSVlad Buslov bool rtnl_held = false; 2404c431f89bSVlad Buslov 24058cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 24068cb08174SJohannes Berg rtm_tca_policy, extack); 2407c431f89bSVlad Buslov if (err < 0) 2408c431f89bSVlad Buslov return err; 2409c431f89bSVlad Buslov 2410c431f89bSVlad Buslov t = nlmsg_data(n); 2411c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2412c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2413c431f89bSVlad Buslov parent = t->tcm_parent; 2414c431f89bSVlad Buslov 2415c431f89bSVlad Buslov if (prio == 0) { 2416c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 2417c431f89bSVlad Buslov return -ENOENT; 2418c431f89bSVlad Buslov } 2419c431f89bSVlad Buslov 2420c431f89bSVlad Buslov /* Find head of filter chain. */ 2421c431f89bSVlad Buslov 2422470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2423470502deSVlad Buslov if (err) 2424470502deSVlad Buslov return err; 2425470502deSVlad Buslov 24266f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 24276f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 24286f96c3c6SCong Wang err = -EINVAL; 24296f96c3c6SCong Wang goto errout; 24306f96c3c6SCong Wang } 2431470502deSVlad Buslov /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not 2432470502deSVlad Buslov * unlocked, classifier type is not specified, classifier is not 2433470502deSVlad Buslov * unlocked. 2434470502deSVlad Buslov */ 2435470502deSVlad Buslov if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 24366f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2437470502deSVlad Buslov rtnl_held = true; 2438470502deSVlad Buslov rtnl_lock(); 2439470502deSVlad Buslov } 2440470502deSVlad Buslov 2441470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2442470502deSVlad Buslov if (err) 2443470502deSVlad Buslov goto errout; 2444470502deSVlad Buslov 2445470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2446470502deSVlad Buslov extack); 2447c431f89bSVlad Buslov if (IS_ERR(block)) { 2448c431f89bSVlad Buslov err = PTR_ERR(block); 2449c431f89bSVlad Buslov goto errout; 2450c431f89bSVlad Buslov } 2451c431f89bSVlad Buslov 2452c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2453c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2454c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2455c431f89bSVlad Buslov err = -EINVAL; 2456c431f89bSVlad Buslov goto errout; 2457c431f89bSVlad Buslov } 2458c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2459c431f89bSVlad Buslov if (!chain) { 2460c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2461c431f89bSVlad Buslov err = -EINVAL; 2462c431f89bSVlad Buslov goto errout; 2463c431f89bSVlad Buslov } 2464c431f89bSVlad Buslov 2465ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2466c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2467c431f89bSVlad Buslov prio, false); 2468ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2469c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2470c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 24710e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2472c431f89bSVlad Buslov goto errout; 2473c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2474c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2475c431f89bSVlad Buslov err = -EINVAL; 2476c431f89bSVlad Buslov goto errout; 2477c431f89bSVlad Buslov } 2478c431f89bSVlad Buslov 2479c431f89bSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2480c431f89bSVlad Buslov 2481c431f89bSVlad Buslov if (!fh) { 2482c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2483c431f89bSVlad Buslov err = -ENOENT; 2484c431f89bSVlad Buslov } else { 2485c431f89bSVlad Buslov err = tfilter_notify(net, skb, n, tp, block, q, parent, 248612db03b6SVlad Buslov fh, RTM_NEWTFILTER, true, rtnl_held); 2487c431f89bSVlad Buslov if (err < 0) 2488c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); 2489c431f89bSVlad Buslov } 2490c431f89bSVlad Buslov 24917d5509faSVlad Buslov tfilter_put(tp, fh); 2492c431f89bSVlad Buslov errout: 24934dbfa766SVlad Buslov if (chain) { 24944dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 249512db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2496c431f89bSVlad Buslov tcf_chain_put(chain); 24974dbfa766SVlad Buslov } 249812db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2499470502deSVlad Buslov 2500470502deSVlad Buslov if (rtnl_held) 2501470502deSVlad Buslov rtnl_unlock(); 2502470502deSVlad Buslov 2503c431f89bSVlad Buslov return err; 2504c431f89bSVlad Buslov } 2505c431f89bSVlad Buslov 2506aa767bfeSStephen Hemminger struct tcf_dump_args { 25071da177e4SLinus Torvalds struct tcf_walker w; 25081da177e4SLinus Torvalds struct sk_buff *skb; 25091da177e4SLinus Torvalds struct netlink_callback *cb; 25107960d1daSJiri Pirko struct tcf_block *block; 2511a10fa201SJiri Pirko struct Qdisc *q; 2512a10fa201SJiri Pirko u32 parent; 2513*f8ab1807SVlad Buslov bool terse_dump; 25141da177e4SLinus Torvalds }; 25151da177e4SLinus Torvalds 25168113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 25171da177e4SLinus Torvalds { 25181da177e4SLinus Torvalds struct tcf_dump_args *a = (void *)arg; 2519832d1d5bSWANG Cong struct net *net = sock_net(a->skb->sk); 25201da177e4SLinus Torvalds 25217960d1daSJiri Pirko return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, 2522a10fa201SJiri Pirko n, NETLINK_CB(a->cb->skb).portid, 25235a7a5555SJamal Hadi Salim a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 2524*f8ab1807SVlad Buslov RTM_NEWTFILTER, a->terse_dump, true); 25251da177e4SLinus Torvalds } 25261da177e4SLinus Torvalds 2527a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 2528a10fa201SJiri Pirko struct sk_buff *skb, struct netlink_callback *cb, 2529*f8ab1807SVlad Buslov long index_start, long *p_index, bool terse) 2530acb31faeSJiri Pirko { 2531acb31faeSJiri Pirko struct net *net = sock_net(skb->sk); 25327960d1daSJiri Pirko struct tcf_block *block = chain->block; 2533acb31faeSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2534fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 2535acb31faeSJiri Pirko struct tcf_dump_args arg; 2536acb31faeSJiri Pirko 2537fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); 2538fe2923afSVlad Buslov tp; 2539fe2923afSVlad Buslov tp_prev = tp, 2540fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 254112db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL), 2542fe2923afSVlad Buslov (*p_index)++) { 2543acb31faeSJiri Pirko if (*p_index < index_start) 2544acb31faeSJiri Pirko continue; 2545acb31faeSJiri Pirko if (TC_H_MAJ(tcm->tcm_info) && 2546acb31faeSJiri Pirko TC_H_MAJ(tcm->tcm_info) != tp->prio) 2547acb31faeSJiri Pirko continue; 2548acb31faeSJiri Pirko if (TC_H_MIN(tcm->tcm_info) && 2549acb31faeSJiri Pirko TC_H_MIN(tcm->tcm_info) != tp->protocol) 2550acb31faeSJiri Pirko continue; 2551acb31faeSJiri Pirko if (*p_index > index_start) 2552acb31faeSJiri Pirko memset(&cb->args[1], 0, 2553acb31faeSJiri Pirko sizeof(cb->args) - sizeof(cb->args[0])); 2554acb31faeSJiri Pirko if (cb->args[1] == 0) { 255553189183SYueHaibing if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, 2556acb31faeSJiri Pirko NETLINK_CB(cb->skb).portid, 2557acb31faeSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 2558*f8ab1807SVlad Buslov RTM_NEWTFILTER, false, true) <= 0) 2559fe2923afSVlad Buslov goto errout; 2560acb31faeSJiri Pirko cb->args[1] = 1; 2561acb31faeSJiri Pirko } 2562acb31faeSJiri Pirko if (!tp->ops->walk) 2563acb31faeSJiri Pirko continue; 2564acb31faeSJiri Pirko arg.w.fn = tcf_node_dump; 2565acb31faeSJiri Pirko arg.skb = skb; 2566acb31faeSJiri Pirko arg.cb = cb; 25677960d1daSJiri Pirko arg.block = block; 2568a10fa201SJiri Pirko arg.q = q; 2569a10fa201SJiri Pirko arg.parent = parent; 2570acb31faeSJiri Pirko arg.w.stop = 0; 2571acb31faeSJiri Pirko arg.w.skip = cb->args[1] - 1; 2572acb31faeSJiri Pirko arg.w.count = 0; 257301683a14SVlad Buslov arg.w.cookie = cb->args[2]; 2574*f8ab1807SVlad Buslov arg.terse_dump = terse; 257512db03b6SVlad Buslov tp->ops->walk(tp, &arg.w, true); 257601683a14SVlad Buslov cb->args[2] = arg.w.cookie; 2577acb31faeSJiri Pirko cb->args[1] = arg.w.count + 1; 2578acb31faeSJiri Pirko if (arg.w.stop) 2579fe2923afSVlad Buslov goto errout; 2580acb31faeSJiri Pirko } 25815bc17018SJiri Pirko return true; 2582fe2923afSVlad Buslov 2583fe2923afSVlad Buslov errout: 258412db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 2585fe2923afSVlad Buslov return false; 2586acb31faeSJiri Pirko } 2587acb31faeSJiri Pirko 2588*f8ab1807SVlad Buslov static const struct nla_policy tcf_tfilter_dump_policy[TCA_MAX + 1] = { 2589*f8ab1807SVlad Buslov [TCA_DUMP_FLAGS] = NLA_POLICY_BITFIELD32(TCA_DUMP_FLAGS_TERSE), 2590*f8ab1807SVlad Buslov }; 2591*f8ab1807SVlad Buslov 2592bd27a875SEric Dumazet /* called with RTNL */ 25931da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 25941da177e4SLinus Torvalds { 2595bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 25963b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 25975bc17018SJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 25987960d1daSJiri Pirko struct Qdisc *q = NULL; 25996529eabaSJiri Pirko struct tcf_block *block; 2600942b8165SDavid S. Miller struct tcmsg *tcm = nlmsg_data(cb->nlh); 2601*f8ab1807SVlad Buslov bool terse_dump = false; 2602acb31faeSJiri Pirko long index_start; 2603acb31faeSJiri Pirko long index; 2604a10fa201SJiri Pirko u32 parent; 26055bc17018SJiri Pirko int err; 26061da177e4SLinus Torvalds 2607573ce260SHong zhi guo if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 26081da177e4SLinus Torvalds return skb->len; 26095bc17018SJiri Pirko 26108cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 2611*f8ab1807SVlad Buslov tcf_tfilter_dump_policy, cb->extack); 26125bc17018SJiri Pirko if (err) 26135bc17018SJiri Pirko return err; 26145bc17018SJiri Pirko 2615*f8ab1807SVlad Buslov if (tca[TCA_DUMP_FLAGS]) { 2616*f8ab1807SVlad Buslov struct nla_bitfield32 flags = 2617*f8ab1807SVlad Buslov nla_get_bitfield32(tca[TCA_DUMP_FLAGS]); 2618*f8ab1807SVlad Buslov 2619*f8ab1807SVlad Buslov terse_dump = flags.value & TCA_DUMP_FLAGS_TERSE; 2620*f8ab1807SVlad Buslov } 2621*f8ab1807SVlad Buslov 26227960d1daSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2623787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 26247960d1daSJiri Pirko if (!block) 26257960d1daSJiri Pirko goto out; 2626d680b352SJiri Pirko /* If we work with block index, q is NULL and parent value 2627d680b352SJiri Pirko * will never be used in the following code. The check 2628d680b352SJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 2629d680b352SJiri Pirko * see that far, so set parent to zero to silence the warning 2630d680b352SJiri Pirko * about parent being uninitialized. 2631d680b352SJiri Pirko */ 2632d680b352SJiri Pirko parent = 0; 26337960d1daSJiri Pirko } else { 26347960d1daSJiri Pirko const struct Qdisc_class_ops *cops; 26357960d1daSJiri Pirko struct net_device *dev; 26367960d1daSJiri Pirko unsigned long cl = 0; 26377960d1daSJiri Pirko 2638cc7ec456SEric Dumazet dev = __dev_get_by_index(net, tcm->tcm_ifindex); 2639cc7ec456SEric Dumazet if (!dev) 26401da177e4SLinus Torvalds return skb->len; 26411da177e4SLinus Torvalds 2642a10fa201SJiri Pirko parent = tcm->tcm_parent; 2643a7df4870SCong Wang if (!parent) 2644af356afaSPatrick McHardy q = dev->qdisc; 2645a7df4870SCong Wang else 26461da177e4SLinus Torvalds q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 26471da177e4SLinus Torvalds if (!q) 26481da177e4SLinus Torvalds goto out; 2649cc7ec456SEric Dumazet cops = q->ops->cl_ops; 2650cc7ec456SEric Dumazet if (!cops) 2651143976ceSWANG Cong goto out; 26526529eabaSJiri Pirko if (!cops->tcf_block) 2653143976ceSWANG Cong goto out; 26541da177e4SLinus Torvalds if (TC_H_MIN(tcm->tcm_parent)) { 2655143976ceSWANG Cong cl = cops->find(q, tcm->tcm_parent); 26561da177e4SLinus Torvalds if (cl == 0) 2657143976ceSWANG Cong goto out; 26581da177e4SLinus Torvalds } 2659cbaacc4eSAlexander Aring block = cops->tcf_block(q, cl, NULL); 26606529eabaSJiri Pirko if (!block) 2661143976ceSWANG Cong goto out; 2662a7df4870SCong Wang parent = block->classid; 26637960d1daSJiri Pirko if (tcf_block_shared(block)) 26647960d1daSJiri Pirko q = NULL; 26657960d1daSJiri Pirko } 26661da177e4SLinus Torvalds 2667acb31faeSJiri Pirko index_start = cb->args[0]; 2668acb31faeSJiri Pirko index = 0; 26695bc17018SJiri Pirko 2670bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 2671bbf73830SVlad Buslov chain; 2672bbf73830SVlad Buslov chain_prev = chain, 2673bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 2674bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 26755bc17018SJiri Pirko if (tca[TCA_CHAIN] && 26765bc17018SJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index) 26775bc17018SJiri Pirko continue; 2678a10fa201SJiri Pirko if (!tcf_chain_dump(chain, q, parent, skb, cb, 2679*f8ab1807SVlad Buslov index_start, &index, terse_dump)) { 2680bbf73830SVlad Buslov tcf_chain_put(chain); 26815ae437adSRoman Kapl err = -EMSGSIZE; 26825bc17018SJiri Pirko break; 26835bc17018SJiri Pirko } 26845ae437adSRoman Kapl } 26855bc17018SJiri Pirko 2686787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 268712db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 2688acb31faeSJiri Pirko cb->args[0] = index; 26891da177e4SLinus Torvalds 26901da177e4SLinus Torvalds out: 26915ae437adSRoman Kapl /* If we did no progress, the error (EMSGSIZE) is real */ 26925ae437adSRoman Kapl if (skb->len == 0 && err) 26935ae437adSRoman Kapl return err; 26941da177e4SLinus Torvalds return skb->len; 26951da177e4SLinus Torvalds } 26961da177e4SLinus Torvalds 2697a5654820SVlad Buslov static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, 2698a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2699a5654820SVlad Buslov struct net *net, struct sk_buff *skb, 2700a5654820SVlad Buslov struct tcf_block *block, 270132a4f5ecSJiri Pirko u32 portid, u32 seq, u16 flags, int event) 270232a4f5ecSJiri Pirko { 270332a4f5ecSJiri Pirko unsigned char *b = skb_tail_pointer(skb); 27049f407f17SJiri Pirko const struct tcf_proto_ops *ops; 270532a4f5ecSJiri Pirko struct nlmsghdr *nlh; 270632a4f5ecSJiri Pirko struct tcmsg *tcm; 27079f407f17SJiri Pirko void *priv; 27089f407f17SJiri Pirko 2709a5654820SVlad Buslov ops = tmplt_ops; 2710a5654820SVlad Buslov priv = tmplt_priv; 271132a4f5ecSJiri Pirko 271232a4f5ecSJiri Pirko nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 271332a4f5ecSJiri Pirko if (!nlh) 271432a4f5ecSJiri Pirko goto out_nlmsg_trim; 271532a4f5ecSJiri Pirko tcm = nlmsg_data(nlh); 271632a4f5ecSJiri Pirko tcm->tcm_family = AF_UNSPEC; 271732a4f5ecSJiri Pirko tcm->tcm__pad1 = 0; 271832a4f5ecSJiri Pirko tcm->tcm__pad2 = 0; 271932a4f5ecSJiri Pirko tcm->tcm_handle = 0; 272032a4f5ecSJiri Pirko if (block->q) { 272132a4f5ecSJiri Pirko tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; 272232a4f5ecSJiri Pirko tcm->tcm_parent = block->q->handle; 272332a4f5ecSJiri Pirko } else { 272432a4f5ecSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 272532a4f5ecSJiri Pirko tcm->tcm_block_index = block->index; 272632a4f5ecSJiri Pirko } 272732a4f5ecSJiri Pirko 2728a5654820SVlad Buslov if (nla_put_u32(skb, TCA_CHAIN, chain_index)) 272932a4f5ecSJiri Pirko goto nla_put_failure; 273032a4f5ecSJiri Pirko 27319f407f17SJiri Pirko if (ops) { 27329f407f17SJiri Pirko if (nla_put_string(skb, TCA_KIND, ops->kind)) 27339f407f17SJiri Pirko goto nla_put_failure; 27349f407f17SJiri Pirko if (ops->tmplt_dump(skb, net, priv) < 0) 27359f407f17SJiri Pirko goto nla_put_failure; 27369f407f17SJiri Pirko } 27379f407f17SJiri Pirko 273832a4f5ecSJiri Pirko nlh->nlmsg_len = skb_tail_pointer(skb) - b; 273932a4f5ecSJiri Pirko return skb->len; 274032a4f5ecSJiri Pirko 274132a4f5ecSJiri Pirko out_nlmsg_trim: 274232a4f5ecSJiri Pirko nla_put_failure: 274332a4f5ecSJiri Pirko nlmsg_trim(skb, b); 274432a4f5ecSJiri Pirko return -EMSGSIZE; 274532a4f5ecSJiri Pirko } 274632a4f5ecSJiri Pirko 274732a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 274832a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast) 274932a4f5ecSJiri Pirko { 275032a4f5ecSJiri Pirko u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 275132a4f5ecSJiri Pirko struct tcf_block *block = chain->block; 275232a4f5ecSJiri Pirko struct net *net = block->net; 275332a4f5ecSJiri Pirko struct sk_buff *skb; 27545b5f99b1SZhike Wang int err = 0; 275532a4f5ecSJiri Pirko 275632a4f5ecSJiri Pirko skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 275732a4f5ecSJiri Pirko if (!skb) 275832a4f5ecSJiri Pirko return -ENOBUFS; 275932a4f5ecSJiri Pirko 2760a5654820SVlad Buslov if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 2761a5654820SVlad Buslov chain->index, net, skb, block, portid, 276232a4f5ecSJiri Pirko seq, flags, event) <= 0) { 276332a4f5ecSJiri Pirko kfree_skb(skb); 276432a4f5ecSJiri Pirko return -EINVAL; 276532a4f5ecSJiri Pirko } 276632a4f5ecSJiri Pirko 276732a4f5ecSJiri Pirko if (unicast) 27685b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 27695b5f99b1SZhike Wang else 27705b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 27715b5f99b1SZhike Wang flags & NLM_F_ECHO); 277232a4f5ecSJiri Pirko 27735b5f99b1SZhike Wang if (err > 0) 27745b5f99b1SZhike Wang err = 0; 27755b5f99b1SZhike Wang return err; 277632a4f5ecSJiri Pirko } 277732a4f5ecSJiri Pirko 2778a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 2779a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2780a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 2781a5654820SVlad Buslov u32 seq, u16 flags, bool unicast) 2782a5654820SVlad Buslov { 2783a5654820SVlad Buslov u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 2784a5654820SVlad Buslov struct net *net = block->net; 2785a5654820SVlad Buslov struct sk_buff *skb; 2786a5654820SVlad Buslov 2787a5654820SVlad Buslov skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 2788a5654820SVlad Buslov if (!skb) 2789a5654820SVlad Buslov return -ENOBUFS; 2790a5654820SVlad Buslov 2791a5654820SVlad Buslov if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, 2792a5654820SVlad Buslov block, portid, seq, flags, RTM_DELCHAIN) <= 0) { 2793a5654820SVlad Buslov kfree_skb(skb); 2794a5654820SVlad Buslov return -EINVAL; 2795a5654820SVlad Buslov } 2796a5654820SVlad Buslov 2797a5654820SVlad Buslov if (unicast) 2798a5654820SVlad Buslov return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 2799a5654820SVlad Buslov 2800a5654820SVlad Buslov return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); 2801a5654820SVlad Buslov } 2802a5654820SVlad Buslov 28039f407f17SJiri Pirko static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, 28049f407f17SJiri Pirko struct nlattr **tca, 28059f407f17SJiri Pirko struct netlink_ext_ack *extack) 28069f407f17SJiri Pirko { 28079f407f17SJiri Pirko const struct tcf_proto_ops *ops; 28082dd5616eSEric Dumazet char name[IFNAMSIZ]; 28099f407f17SJiri Pirko void *tmplt_priv; 28109f407f17SJiri Pirko 28119f407f17SJiri Pirko /* If kind is not set, user did not specify template. */ 28129f407f17SJiri Pirko if (!tca[TCA_KIND]) 28139f407f17SJiri Pirko return 0; 28149f407f17SJiri Pirko 28152dd5616eSEric Dumazet if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 28162dd5616eSEric Dumazet NL_SET_ERR_MSG(extack, "Specified TC chain template name too long"); 28172dd5616eSEric Dumazet return -EINVAL; 28182dd5616eSEric Dumazet } 28192dd5616eSEric Dumazet 28202dd5616eSEric Dumazet ops = tcf_proto_lookup_ops(name, true, extack); 28219f407f17SJiri Pirko if (IS_ERR(ops)) 28229f407f17SJiri Pirko return PTR_ERR(ops); 28239f407f17SJiri Pirko if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { 28249f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); 28259f407f17SJiri Pirko return -EOPNOTSUPP; 28269f407f17SJiri Pirko } 28279f407f17SJiri Pirko 28289f407f17SJiri Pirko tmplt_priv = ops->tmplt_create(net, chain, tca, extack); 28299f407f17SJiri Pirko if (IS_ERR(tmplt_priv)) { 28309f407f17SJiri Pirko module_put(ops->owner); 28319f407f17SJiri Pirko return PTR_ERR(tmplt_priv); 28329f407f17SJiri Pirko } 28339f407f17SJiri Pirko chain->tmplt_ops = ops; 28349f407f17SJiri Pirko chain->tmplt_priv = tmplt_priv; 28359f407f17SJiri Pirko return 0; 28369f407f17SJiri Pirko } 28379f407f17SJiri Pirko 2838a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 2839a5654820SVlad Buslov void *tmplt_priv) 28409f407f17SJiri Pirko { 28419f407f17SJiri Pirko /* If template ops are set, no work to do for us. */ 2842a5654820SVlad Buslov if (!tmplt_ops) 28439f407f17SJiri Pirko return; 28449f407f17SJiri Pirko 2845a5654820SVlad Buslov tmplt_ops->tmplt_destroy(tmplt_priv); 2846a5654820SVlad Buslov module_put(tmplt_ops->owner); 28479f407f17SJiri Pirko } 28489f407f17SJiri Pirko 284932a4f5ecSJiri Pirko /* Add/delete/get a chain */ 285032a4f5ecSJiri Pirko 285132a4f5ecSJiri Pirko static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, 285232a4f5ecSJiri Pirko struct netlink_ext_ack *extack) 285332a4f5ecSJiri Pirko { 285432a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 285532a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 285632a4f5ecSJiri Pirko struct tcmsg *t; 285732a4f5ecSJiri Pirko u32 parent; 285832a4f5ecSJiri Pirko u32 chain_index; 285932a4f5ecSJiri Pirko struct Qdisc *q = NULL; 286032a4f5ecSJiri Pirko struct tcf_chain *chain = NULL; 286132a4f5ecSJiri Pirko struct tcf_block *block; 286232a4f5ecSJiri Pirko unsigned long cl; 286332a4f5ecSJiri Pirko int err; 286432a4f5ecSJiri Pirko 286532a4f5ecSJiri Pirko if (n->nlmsg_type != RTM_GETCHAIN && 286632a4f5ecSJiri Pirko !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 286732a4f5ecSJiri Pirko return -EPERM; 286832a4f5ecSJiri Pirko 286932a4f5ecSJiri Pirko replay: 28708cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 28718cb08174SJohannes Berg rtm_tca_policy, extack); 287232a4f5ecSJiri Pirko if (err < 0) 287332a4f5ecSJiri Pirko return err; 287432a4f5ecSJiri Pirko 287532a4f5ecSJiri Pirko t = nlmsg_data(n); 287632a4f5ecSJiri Pirko parent = t->tcm_parent; 287732a4f5ecSJiri Pirko cl = 0; 287832a4f5ecSJiri Pirko 287932a4f5ecSJiri Pirko block = tcf_block_find(net, &q, &parent, &cl, 288032a4f5ecSJiri Pirko t->tcm_ifindex, t->tcm_block_index, extack); 288132a4f5ecSJiri Pirko if (IS_ERR(block)) 288232a4f5ecSJiri Pirko return PTR_ERR(block); 288332a4f5ecSJiri Pirko 288432a4f5ecSJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 288532a4f5ecSJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 288632a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2887e368fdb6SVlad Buslov err = -EINVAL; 2888e368fdb6SVlad Buslov goto errout_block; 288932a4f5ecSJiri Pirko } 28902cbfab07SVlad Buslov 28912cbfab07SVlad Buslov mutex_lock(&block->lock); 289232a4f5ecSJiri Pirko chain = tcf_chain_lookup(block, chain_index); 289332a4f5ecSJiri Pirko if (n->nlmsg_type == RTM_NEWCHAIN) { 289432a4f5ecSJiri Pirko if (chain) { 28953d32f4c5SJiri Pirko if (tcf_chain_held_by_acts_only(chain)) { 28961f3ed383SJiri Pirko /* The chain exists only because there is 28973d32f4c5SJiri Pirko * some action referencing it. 28981f3ed383SJiri Pirko */ 28991f3ed383SJiri Pirko tcf_chain_hold(chain); 29001f3ed383SJiri Pirko } else { 290132a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Filter chain already exists"); 2902e368fdb6SVlad Buslov err = -EEXIST; 29032cbfab07SVlad Buslov goto errout_block_locked; 290432a4f5ecSJiri Pirko } 29051f3ed383SJiri Pirko } else { 290632a4f5ecSJiri Pirko if (!(n->nlmsg_flags & NLM_F_CREATE)) { 290732a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); 2908e368fdb6SVlad Buslov err = -ENOENT; 29092cbfab07SVlad Buslov goto errout_block_locked; 291032a4f5ecSJiri Pirko } 291132a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 291232a4f5ecSJiri Pirko if (!chain) { 291332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to create filter chain"); 2914e368fdb6SVlad Buslov err = -ENOMEM; 29152cbfab07SVlad Buslov goto errout_block_locked; 291632a4f5ecSJiri Pirko } 29171f3ed383SJiri Pirko } 291832a4f5ecSJiri Pirko } else { 29193d32f4c5SJiri Pirko if (!chain || tcf_chain_held_by_acts_only(chain)) { 292032a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2921e368fdb6SVlad Buslov err = -EINVAL; 29222cbfab07SVlad Buslov goto errout_block_locked; 292332a4f5ecSJiri Pirko } 292432a4f5ecSJiri Pirko tcf_chain_hold(chain); 292532a4f5ecSJiri Pirko } 292632a4f5ecSJiri Pirko 29272cbfab07SVlad Buslov if (n->nlmsg_type == RTM_NEWCHAIN) { 29282cbfab07SVlad Buslov /* Modifying chain requires holding parent block lock. In case 29292cbfab07SVlad Buslov * the chain was successfully added, take a reference to the 29302cbfab07SVlad Buslov * chain. This ensures that an empty chain does not disappear at 29312cbfab07SVlad Buslov * the end of this function. 293232a4f5ecSJiri Pirko */ 293332a4f5ecSJiri Pirko tcf_chain_hold(chain); 293432a4f5ecSJiri Pirko chain->explicitly_created = true; 29352cbfab07SVlad Buslov } 29362cbfab07SVlad Buslov mutex_unlock(&block->lock); 29372cbfab07SVlad Buslov 29382cbfab07SVlad Buslov switch (n->nlmsg_type) { 29392cbfab07SVlad Buslov case RTM_NEWCHAIN: 29402cbfab07SVlad Buslov err = tc_chain_tmplt_add(chain, net, tca, extack); 29412cbfab07SVlad Buslov if (err) { 29422cbfab07SVlad Buslov tcf_chain_put_explicitly_created(chain); 29432cbfab07SVlad Buslov goto errout; 29442cbfab07SVlad Buslov } 29452cbfab07SVlad Buslov 294632a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 294732a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 294832a4f5ecSJiri Pirko break; 294932a4f5ecSJiri Pirko case RTM_DELCHAIN: 2950f5b9bac7SCong Wang tfilter_notify_chain(net, skb, block, q, parent, n, 295112db03b6SVlad Buslov chain, RTM_DELTFILTER, true); 295232a4f5ecSJiri Pirko /* Flush the chain first as the user requested chain removal. */ 295312db03b6SVlad Buslov tcf_chain_flush(chain, true); 295432a4f5ecSJiri Pirko /* In case the chain was successfully deleted, put a reference 295532a4f5ecSJiri Pirko * to the chain previously taken during addition. 295632a4f5ecSJiri Pirko */ 295732a4f5ecSJiri Pirko tcf_chain_put_explicitly_created(chain); 295832a4f5ecSJiri Pirko break; 295932a4f5ecSJiri Pirko case RTM_GETCHAIN: 296032a4f5ecSJiri Pirko err = tc_chain_notify(chain, skb, n->nlmsg_seq, 296132a4f5ecSJiri Pirko n->nlmsg_seq, n->nlmsg_type, true); 296232a4f5ecSJiri Pirko if (err < 0) 296332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); 296432a4f5ecSJiri Pirko break; 296532a4f5ecSJiri Pirko default: 296632a4f5ecSJiri Pirko err = -EOPNOTSUPP; 296732a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Unsupported message type"); 296832a4f5ecSJiri Pirko goto errout; 296932a4f5ecSJiri Pirko } 297032a4f5ecSJiri Pirko 297132a4f5ecSJiri Pirko errout: 297232a4f5ecSJiri Pirko tcf_chain_put(chain); 2973e368fdb6SVlad Buslov errout_block: 297412db03b6SVlad Buslov tcf_block_release(q, block, true); 297532a4f5ecSJiri Pirko if (err == -EAGAIN) 297632a4f5ecSJiri Pirko /* Replay the request. */ 297732a4f5ecSJiri Pirko goto replay; 297832a4f5ecSJiri Pirko return err; 29792cbfab07SVlad Buslov 29802cbfab07SVlad Buslov errout_block_locked: 29812cbfab07SVlad Buslov mutex_unlock(&block->lock); 29822cbfab07SVlad Buslov goto errout_block; 298332a4f5ecSJiri Pirko } 298432a4f5ecSJiri Pirko 298532a4f5ecSJiri Pirko /* called with RTNL */ 298632a4f5ecSJiri Pirko static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) 298732a4f5ecSJiri Pirko { 298832a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 298932a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 299032a4f5ecSJiri Pirko struct Qdisc *q = NULL; 299132a4f5ecSJiri Pirko struct tcf_block *block; 299232a4f5ecSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2993ace4a267SVlad Buslov struct tcf_chain *chain; 299432a4f5ecSJiri Pirko long index_start; 299532a4f5ecSJiri Pirko long index; 299632a4f5ecSJiri Pirko u32 parent; 299732a4f5ecSJiri Pirko int err; 299832a4f5ecSJiri Pirko 299932a4f5ecSJiri Pirko if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 300032a4f5ecSJiri Pirko return skb->len; 300132a4f5ecSJiri Pirko 30028cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 30038cb08174SJohannes Berg rtm_tca_policy, cb->extack); 300432a4f5ecSJiri Pirko if (err) 300532a4f5ecSJiri Pirko return err; 300632a4f5ecSJiri Pirko 300732a4f5ecSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 3008787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 300932a4f5ecSJiri Pirko if (!block) 301032a4f5ecSJiri Pirko goto out; 301132a4f5ecSJiri Pirko /* If we work with block index, q is NULL and parent value 301232a4f5ecSJiri Pirko * will never be used in the following code. The check 301332a4f5ecSJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 301432a4f5ecSJiri Pirko * see that far, so set parent to zero to silence the warning 301532a4f5ecSJiri Pirko * about parent being uninitialized. 301632a4f5ecSJiri Pirko */ 301732a4f5ecSJiri Pirko parent = 0; 301832a4f5ecSJiri Pirko } else { 301932a4f5ecSJiri Pirko const struct Qdisc_class_ops *cops; 302032a4f5ecSJiri Pirko struct net_device *dev; 302132a4f5ecSJiri Pirko unsigned long cl = 0; 302232a4f5ecSJiri Pirko 302332a4f5ecSJiri Pirko dev = __dev_get_by_index(net, tcm->tcm_ifindex); 302432a4f5ecSJiri Pirko if (!dev) 302532a4f5ecSJiri Pirko return skb->len; 302632a4f5ecSJiri Pirko 302732a4f5ecSJiri Pirko parent = tcm->tcm_parent; 302832a4f5ecSJiri Pirko if (!parent) { 302932a4f5ecSJiri Pirko q = dev->qdisc; 303032a4f5ecSJiri Pirko parent = q->handle; 303132a4f5ecSJiri Pirko } else { 303232a4f5ecSJiri Pirko q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 303332a4f5ecSJiri Pirko } 303432a4f5ecSJiri Pirko if (!q) 303532a4f5ecSJiri Pirko goto out; 303632a4f5ecSJiri Pirko cops = q->ops->cl_ops; 303732a4f5ecSJiri Pirko if (!cops) 303832a4f5ecSJiri Pirko goto out; 303932a4f5ecSJiri Pirko if (!cops->tcf_block) 304032a4f5ecSJiri Pirko goto out; 304132a4f5ecSJiri Pirko if (TC_H_MIN(tcm->tcm_parent)) { 304232a4f5ecSJiri Pirko cl = cops->find(q, tcm->tcm_parent); 304332a4f5ecSJiri Pirko if (cl == 0) 304432a4f5ecSJiri Pirko goto out; 304532a4f5ecSJiri Pirko } 304632a4f5ecSJiri Pirko block = cops->tcf_block(q, cl, NULL); 304732a4f5ecSJiri Pirko if (!block) 304832a4f5ecSJiri Pirko goto out; 304932a4f5ecSJiri Pirko if (tcf_block_shared(block)) 305032a4f5ecSJiri Pirko q = NULL; 305132a4f5ecSJiri Pirko } 305232a4f5ecSJiri Pirko 305332a4f5ecSJiri Pirko index_start = cb->args[0]; 305432a4f5ecSJiri Pirko index = 0; 305532a4f5ecSJiri Pirko 3056ace4a267SVlad Buslov mutex_lock(&block->lock); 3057ace4a267SVlad Buslov list_for_each_entry(chain, &block->chain_list, list) { 305832a4f5ecSJiri Pirko if ((tca[TCA_CHAIN] && 305932a4f5ecSJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index)) 306032a4f5ecSJiri Pirko continue; 306132a4f5ecSJiri Pirko if (index < index_start) { 306232a4f5ecSJiri Pirko index++; 306332a4f5ecSJiri Pirko continue; 306432a4f5ecSJiri Pirko } 3065ace4a267SVlad Buslov if (tcf_chain_held_by_acts_only(chain)) 3066ace4a267SVlad Buslov continue; 3067a5654820SVlad Buslov err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 3068a5654820SVlad Buslov chain->index, net, skb, block, 306932a4f5ecSJiri Pirko NETLINK_CB(cb->skb).portid, 307032a4f5ecSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 307132a4f5ecSJiri Pirko RTM_NEWCHAIN); 3072ace4a267SVlad Buslov if (err <= 0) 307332a4f5ecSJiri Pirko break; 307432a4f5ecSJiri Pirko index++; 307532a4f5ecSJiri Pirko } 3076ace4a267SVlad Buslov mutex_unlock(&block->lock); 307732a4f5ecSJiri Pirko 3078787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 307912db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 308032a4f5ecSJiri Pirko cb->args[0] = index; 308132a4f5ecSJiri Pirko 308232a4f5ecSJiri Pirko out: 308332a4f5ecSJiri Pirko /* If we did no progress, the error (EMSGSIZE) is real */ 308432a4f5ecSJiri Pirko if (skb->len == 0 && err) 308532a4f5ecSJiri Pirko return err; 308632a4f5ecSJiri Pirko return skb->len; 308732a4f5ecSJiri Pirko } 308832a4f5ecSJiri Pirko 308918d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts) 30901da177e4SLinus Torvalds { 30911da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30923d66b89cSEric Dumazet if (exts->actions) { 309390b73b77SVlad Buslov tcf_action_destroy(exts->actions, TCA_ACT_UNBIND); 309422dc13c8SWANG Cong kfree(exts->actions); 30953d66b89cSEric Dumazet } 309622dc13c8SWANG Cong exts->nr_actions = 0; 30971da177e4SLinus Torvalds #endif 30981da177e4SLinus Torvalds } 3099aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy); 31001da177e4SLinus Torvalds 3101c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 310250a56190SAlexander Aring struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr, 3103ec6743a1SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 31041da177e4SLinus Torvalds { 31051da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 31061da177e4SLinus Torvalds { 31071da177e4SLinus Torvalds struct tc_action *act; 3108d04e6990SRoman Mashak size_t attr_size = 0; 31091da177e4SLinus Torvalds 31105da57f42SWANG Cong if (exts->police && tb[exts->police]) { 31119fb9f251SJiri Pirko act = tcf_action_init_1(net, tp, tb[exts->police], 31129fb9f251SJiri Pirko rate_tlv, "police", ovr, 3113ec6743a1SVlad Buslov TCA_ACT_BIND, rtnl_held, 3114ec6743a1SVlad Buslov extack); 3115ab27cfb8SPatrick McHardy if (IS_ERR(act)) 3116ab27cfb8SPatrick McHardy return PTR_ERR(act); 31171da177e4SLinus Torvalds 311833be6271SWANG Cong act->type = exts->type = TCA_OLD_COMPAT; 311922dc13c8SWANG Cong exts->actions[0] = act; 312022dc13c8SWANG Cong exts->nr_actions = 1; 31215da57f42SWANG Cong } else if (exts->action && tb[exts->action]) { 312290b73b77SVlad Buslov int err; 312322dc13c8SWANG Cong 31249fb9f251SJiri Pirko err = tcf_action_init(net, tp, tb[exts->action], 31259fb9f251SJiri Pirko rate_tlv, NULL, ovr, TCA_ACT_BIND, 3126ec6743a1SVlad Buslov exts->actions, &attr_size, 3127ec6743a1SVlad Buslov rtnl_held, extack); 312890b73b77SVlad Buslov if (err < 0) 312933be6271SWANG Cong return err; 313090b73b77SVlad Buslov exts->nr_actions = err; 31311da177e4SLinus Torvalds } 31321da177e4SLinus Torvalds } 31331da177e4SLinus Torvalds #else 31345da57f42SWANG Cong if ((exts->action && tb[exts->action]) || 313550a56190SAlexander Aring (exts->police && tb[exts->police])) { 313650a56190SAlexander Aring NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)"); 31371da177e4SLinus Torvalds return -EOPNOTSUPP; 313850a56190SAlexander Aring } 31391da177e4SLinus Torvalds #endif 31401da177e4SLinus Torvalds 31411da177e4SLinus Torvalds return 0; 31421da177e4SLinus Torvalds } 3143aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate); 31441da177e4SLinus Torvalds 31459b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 31461da177e4SLinus Torvalds { 31471da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 314822dc13c8SWANG Cong struct tcf_exts old = *dst; 314922dc13c8SWANG Cong 31509b0d4446SJiri Pirko *dst = *src; 315122dc13c8SWANG Cong tcf_exts_destroy(&old); 31521da177e4SLinus Torvalds #endif 31531da177e4SLinus Torvalds } 3154aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change); 31551da177e4SLinus Torvalds 315622dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT 315722dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 315822dc13c8SWANG Cong { 315922dc13c8SWANG Cong if (exts->nr_actions == 0) 316022dc13c8SWANG Cong return NULL; 316122dc13c8SWANG Cong else 316222dc13c8SWANG Cong return exts->actions[0]; 316322dc13c8SWANG Cong } 316422dc13c8SWANG Cong #endif 316533be6271SWANG Cong 31665da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 31671da177e4SLinus Torvalds { 31681da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 31699cc63db5SCong Wang struct nlattr *nest; 31709cc63db5SCong Wang 3171978dfd8dSJiri Pirko if (exts->action && tcf_exts_has_actions(exts)) { 31721da177e4SLinus Torvalds /* 31731da177e4SLinus Torvalds * again for backward compatible mode - we want 31741da177e4SLinus Torvalds * to work with both old and new modes of entering 31751da177e4SLinus Torvalds * tc data even if iproute2 was newer - jhs 31761da177e4SLinus Torvalds */ 317733be6271SWANG Cong if (exts->type != TCA_OLD_COMPAT) { 3178ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->action); 31794b3550efSPatrick McHardy if (nest == NULL) 31804b3550efSPatrick McHardy goto nla_put_failure; 318122dc13c8SWANG Cong 318290b73b77SVlad Buslov if (tcf_action_dump(skb, exts->actions, 0, 0) < 0) 3183add93b61SPatrick McHardy goto nla_put_failure; 31844b3550efSPatrick McHardy nla_nest_end(skb, nest); 31855da57f42SWANG Cong } else if (exts->police) { 318633be6271SWANG Cong struct tc_action *act = tcf_exts_first_act(exts); 3187ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->police); 318863acd680SJamal Hadi Salim if (nest == NULL || !act) 31894b3550efSPatrick McHardy goto nla_put_failure; 319033be6271SWANG Cong if (tcf_action_dump_old(skb, act, 0, 0) < 0) 3191add93b61SPatrick McHardy goto nla_put_failure; 31924b3550efSPatrick McHardy nla_nest_end(skb, nest); 31931da177e4SLinus Torvalds } 31941da177e4SLinus Torvalds } 31951da177e4SLinus Torvalds return 0; 31969cc63db5SCong Wang 31979cc63db5SCong Wang nla_put_failure: 31989cc63db5SCong Wang nla_nest_cancel(skb, nest); 31991da177e4SLinus Torvalds return -1; 32009cc63db5SCong Wang #else 32019cc63db5SCong Wang return 0; 32029cc63db5SCong Wang #endif 32031da177e4SLinus Torvalds } 3204aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump); 32051da177e4SLinus Torvalds 3206aa767bfeSStephen Hemminger 32075da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 32081da177e4SLinus Torvalds { 32091da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 321033be6271SWANG Cong struct tc_action *a = tcf_exts_first_act(exts); 3211b057df24SIgnacy Gawędzki if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 321233be6271SWANG Cong return -1; 32131da177e4SLinus Torvalds #endif 32141da177e4SLinus Torvalds return 0; 32151da177e4SLinus Torvalds } 3216aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats); 32171da177e4SLinus Torvalds 321840119211SVlad Buslov static void tcf_block_offload_inc(struct tcf_block *block, u32 *flags) 321940119211SVlad Buslov { 322040119211SVlad Buslov if (*flags & TCA_CLS_FLAGS_IN_HW) 322140119211SVlad Buslov return; 322240119211SVlad Buslov *flags |= TCA_CLS_FLAGS_IN_HW; 322340119211SVlad Buslov atomic_inc(&block->offloadcnt); 322440119211SVlad Buslov } 322540119211SVlad Buslov 322640119211SVlad Buslov static void tcf_block_offload_dec(struct tcf_block *block, u32 *flags) 322740119211SVlad Buslov { 322840119211SVlad Buslov if (!(*flags & TCA_CLS_FLAGS_IN_HW)) 322940119211SVlad Buslov return; 323040119211SVlad Buslov *flags &= ~TCA_CLS_FLAGS_IN_HW; 323140119211SVlad Buslov atomic_dec(&block->offloadcnt); 323240119211SVlad Buslov } 323340119211SVlad Buslov 323440119211SVlad Buslov static void tc_cls_offload_cnt_update(struct tcf_block *block, 323540119211SVlad Buslov struct tcf_proto *tp, u32 *cnt, 323640119211SVlad Buslov u32 *flags, u32 diff, bool add) 323740119211SVlad Buslov { 323840119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 323940119211SVlad Buslov 324040119211SVlad Buslov spin_lock(&tp->lock); 324140119211SVlad Buslov if (add) { 324240119211SVlad Buslov if (!*cnt) 324340119211SVlad Buslov tcf_block_offload_inc(block, flags); 324440119211SVlad Buslov *cnt += diff; 324540119211SVlad Buslov } else { 324640119211SVlad Buslov *cnt -= diff; 324740119211SVlad Buslov if (!*cnt) 324840119211SVlad Buslov tcf_block_offload_dec(block, flags); 324940119211SVlad Buslov } 325040119211SVlad Buslov spin_unlock(&tp->lock); 325140119211SVlad Buslov } 325240119211SVlad Buslov 325340119211SVlad Buslov static void 325440119211SVlad Buslov tc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp, 325540119211SVlad Buslov u32 *cnt, u32 *flags) 325640119211SVlad Buslov { 325740119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 325840119211SVlad Buslov 325940119211SVlad Buslov spin_lock(&tp->lock); 326040119211SVlad Buslov tcf_block_offload_dec(block, flags); 326140119211SVlad Buslov *cnt = 0; 326240119211SVlad Buslov spin_unlock(&tp->lock); 326340119211SVlad Buslov } 326440119211SVlad Buslov 326540119211SVlad Buslov static int 326640119211SVlad Buslov __tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 3267aeb3fecdSCong Wang void *type_data, bool err_stop) 3268717503b9SJiri Pirko { 3269955bcb6eSPablo Neira Ayuso struct flow_block_cb *block_cb; 3270aeb3fecdSCong Wang int ok_count = 0; 3271aeb3fecdSCong Wang int err; 3272aeb3fecdSCong Wang 327340119211SVlad Buslov list_for_each_entry(block_cb, &block->flow_block.cb_list, list) { 327440119211SVlad Buslov err = block_cb->cb(type, type_data, block_cb->cb_priv); 327540119211SVlad Buslov if (err) { 327640119211SVlad Buslov if (err_stop) 327740119211SVlad Buslov return err; 327840119211SVlad Buslov } else { 327940119211SVlad Buslov ok_count++; 328040119211SVlad Buslov } 328140119211SVlad Buslov } 328240119211SVlad Buslov return ok_count; 328340119211SVlad Buslov } 328440119211SVlad Buslov 328540119211SVlad Buslov int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 328640119211SVlad Buslov void *type_data, bool err_stop, bool rtnl_held) 328740119211SVlad Buslov { 328811bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 328940119211SVlad Buslov int ok_count; 329040119211SVlad Buslov 329111bd634dSVlad Buslov retry: 329211bd634dSVlad Buslov if (take_rtnl) 329311bd634dSVlad Buslov rtnl_lock(); 329440119211SVlad Buslov down_read(&block->cb_lock); 329511bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 329611bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 329711bd634dSVlad Buslov * obtain the locks in same order here. 329811bd634dSVlad Buslov */ 329911bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 330040119211SVlad Buslov up_read(&block->cb_lock); 330111bd634dSVlad Buslov take_rtnl = true; 330211bd634dSVlad Buslov goto retry; 330311bd634dSVlad Buslov } 330411bd634dSVlad Buslov 330511bd634dSVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 330611bd634dSVlad Buslov 330711bd634dSVlad Buslov up_read(&block->cb_lock); 330811bd634dSVlad Buslov if (take_rtnl) 330911bd634dSVlad Buslov rtnl_unlock(); 331040119211SVlad Buslov return ok_count; 331140119211SVlad Buslov } 331240119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_call); 331340119211SVlad Buslov 331440119211SVlad Buslov /* Non-destructive filter add. If filter that wasn't already in hardware is 331540119211SVlad Buslov * successfully offloaded, increment block offloads counter. On failure, 331640119211SVlad Buslov * previously offloaded filter is considered to be intact and offloads counter 331740119211SVlad Buslov * is not decremented. 331840119211SVlad Buslov */ 331940119211SVlad Buslov 332040119211SVlad Buslov int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp, 332140119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 332240119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 332340119211SVlad Buslov { 332411bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 332540119211SVlad Buslov int ok_count; 332640119211SVlad Buslov 332711bd634dSVlad Buslov retry: 332811bd634dSVlad Buslov if (take_rtnl) 332911bd634dSVlad Buslov rtnl_lock(); 33304f8116c8SVlad Buslov down_read(&block->cb_lock); 333111bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 333211bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 333311bd634dSVlad Buslov * obtain the locks in same order here. 333411bd634dSVlad Buslov */ 333511bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 333611bd634dSVlad Buslov up_read(&block->cb_lock); 333711bd634dSVlad Buslov take_rtnl = true; 333811bd634dSVlad Buslov goto retry; 333911bd634dSVlad Buslov } 334011bd634dSVlad Buslov 3341aeb3fecdSCong Wang /* Make sure all netdevs sharing this block are offload-capable. */ 33424f8116c8SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 33434f8116c8SVlad Buslov ok_count = -EOPNOTSUPP; 33444f8116c8SVlad Buslov goto err_unlock; 33454f8116c8SVlad Buslov } 3346aeb3fecdSCong Wang 334740119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3348a449a3e7SVlad Buslov if (ok_count < 0) 3349a449a3e7SVlad Buslov goto err_unlock; 3350a449a3e7SVlad Buslov 3351a449a3e7SVlad Buslov if (tp->ops->hw_add) 3352a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 335340119211SVlad Buslov if (ok_count > 0) 335440119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 335540119211SVlad Buslov ok_count, true); 33564f8116c8SVlad Buslov err_unlock: 33574f8116c8SVlad Buslov up_read(&block->cb_lock); 335811bd634dSVlad Buslov if (take_rtnl) 335911bd634dSVlad Buslov rtnl_unlock(); 336040119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 3361717503b9SJiri Pirko } 336240119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_add); 336340119211SVlad Buslov 336440119211SVlad Buslov /* Destructive filter replace. If filter that wasn't already in hardware is 336540119211SVlad Buslov * successfully offloaded, increment block offload counter. On failure, 336640119211SVlad Buslov * previously offloaded filter is considered to be destroyed and offload counter 336740119211SVlad Buslov * is decremented. 336840119211SVlad Buslov */ 336940119211SVlad Buslov 337040119211SVlad Buslov int tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp, 337140119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 337240119211SVlad Buslov u32 *old_flags, unsigned int *old_in_hw_count, 337340119211SVlad Buslov u32 *new_flags, unsigned int *new_in_hw_count, 337440119211SVlad Buslov bool rtnl_held) 337540119211SVlad Buslov { 337611bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 337740119211SVlad Buslov int ok_count; 337840119211SVlad Buslov 337911bd634dSVlad Buslov retry: 338011bd634dSVlad Buslov if (take_rtnl) 338111bd634dSVlad Buslov rtnl_lock(); 338240119211SVlad Buslov down_read(&block->cb_lock); 338311bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 338411bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 338511bd634dSVlad Buslov * obtain the locks in same order here. 338611bd634dSVlad Buslov */ 338711bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 338811bd634dSVlad Buslov up_read(&block->cb_lock); 338911bd634dSVlad Buslov take_rtnl = true; 339011bd634dSVlad Buslov goto retry; 339111bd634dSVlad Buslov } 339211bd634dSVlad Buslov 339340119211SVlad Buslov /* Make sure all netdevs sharing this block are offload-capable. */ 339440119211SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 339540119211SVlad Buslov ok_count = -EOPNOTSUPP; 339640119211SVlad Buslov goto err_unlock; 339740119211SVlad Buslov } 339840119211SVlad Buslov 339940119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags); 3400a449a3e7SVlad Buslov if (tp->ops->hw_del) 3401a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 340240119211SVlad Buslov 340340119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3404a449a3e7SVlad Buslov if (ok_count < 0) 3405a449a3e7SVlad Buslov goto err_unlock; 3406a449a3e7SVlad Buslov 3407a449a3e7SVlad Buslov if (tp->ops->hw_add) 3408a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 340940119211SVlad Buslov if (ok_count > 0) 3410a449a3e7SVlad Buslov tc_cls_offload_cnt_update(block, tp, new_in_hw_count, 3411a449a3e7SVlad Buslov new_flags, ok_count, true); 341240119211SVlad Buslov err_unlock: 341340119211SVlad Buslov up_read(&block->cb_lock); 341411bd634dSVlad Buslov if (take_rtnl) 341511bd634dSVlad Buslov rtnl_unlock(); 341640119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 341740119211SVlad Buslov } 341840119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_replace); 341940119211SVlad Buslov 342040119211SVlad Buslov /* Destroy filter and decrement block offload counter, if filter was previously 342140119211SVlad Buslov * offloaded. 342240119211SVlad Buslov */ 342340119211SVlad Buslov 342440119211SVlad Buslov int tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp, 342540119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 342640119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 342740119211SVlad Buslov { 342811bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 342940119211SVlad Buslov int ok_count; 343040119211SVlad Buslov 343111bd634dSVlad Buslov retry: 343211bd634dSVlad Buslov if (take_rtnl) 343311bd634dSVlad Buslov rtnl_lock(); 343440119211SVlad Buslov down_read(&block->cb_lock); 343511bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 343611bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 343711bd634dSVlad Buslov * obtain the locks in same order here. 343811bd634dSVlad Buslov */ 343911bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 344011bd634dSVlad Buslov up_read(&block->cb_lock); 344111bd634dSVlad Buslov take_rtnl = true; 344211bd634dSVlad Buslov goto retry; 344311bd634dSVlad Buslov } 344411bd634dSVlad Buslov 344540119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 344640119211SVlad Buslov 344740119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags); 3448a449a3e7SVlad Buslov if (tp->ops->hw_del) 3449a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 3450a449a3e7SVlad Buslov 345140119211SVlad Buslov up_read(&block->cb_lock); 345211bd634dSVlad Buslov if (take_rtnl) 345311bd634dSVlad Buslov rtnl_unlock(); 345440119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 345540119211SVlad Buslov } 345640119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_destroy); 345740119211SVlad Buslov 345840119211SVlad Buslov int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp, 345940119211SVlad Buslov bool add, flow_setup_cb_t *cb, 346040119211SVlad Buslov enum tc_setup_type type, void *type_data, 346140119211SVlad Buslov void *cb_priv, u32 *flags, unsigned int *in_hw_count) 346240119211SVlad Buslov { 346340119211SVlad Buslov int err = cb(type, type_data, cb_priv); 346440119211SVlad Buslov 346540119211SVlad Buslov if (err) { 346640119211SVlad Buslov if (add && tc_skip_sw(*flags)) 346740119211SVlad Buslov return err; 346840119211SVlad Buslov } else { 346940119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1, 347040119211SVlad Buslov add); 347140119211SVlad Buslov } 347240119211SVlad Buslov 347340119211SVlad Buslov return 0; 347440119211SVlad Buslov } 347540119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_reoffload); 3476b3f55bddSJiri Pirko 34772008495dSJiri Pirko static int tcf_act_get_cookie(struct flow_action_entry *entry, 34782008495dSJiri Pirko const struct tc_action *act) 34792008495dSJiri Pirko { 34802008495dSJiri Pirko struct tc_cookie *cookie; 34812008495dSJiri Pirko int err = 0; 34822008495dSJiri Pirko 34832008495dSJiri Pirko rcu_read_lock(); 34842008495dSJiri Pirko cookie = rcu_dereference(act->act_cookie); 34852008495dSJiri Pirko if (cookie) { 34862008495dSJiri Pirko entry->cookie = flow_action_cookie_create(cookie->data, 34872008495dSJiri Pirko cookie->len, 34882008495dSJiri Pirko GFP_ATOMIC); 34892008495dSJiri Pirko if (!entry->cookie) 34902008495dSJiri Pirko err = -ENOMEM; 34912008495dSJiri Pirko } 34922008495dSJiri Pirko rcu_read_unlock(); 34932008495dSJiri Pirko return err; 34942008495dSJiri Pirko } 34952008495dSJiri Pirko 34962008495dSJiri Pirko static void tcf_act_put_cookie(struct flow_action_entry *entry) 34972008495dSJiri Pirko { 34982008495dSJiri Pirko flow_action_cookie_destroy(entry->cookie); 34992008495dSJiri Pirko } 35002008495dSJiri Pirko 35015a6ff4b1SVlad Buslov void tc_cleanup_flow_action(struct flow_action *flow_action) 35025a6ff4b1SVlad Buslov { 35035a6ff4b1SVlad Buslov struct flow_action_entry *entry; 35045a6ff4b1SVlad Buslov int i; 35055a6ff4b1SVlad Buslov 35062008495dSJiri Pirko flow_action_for_each(i, entry, flow_action) { 35072008495dSJiri Pirko tcf_act_put_cookie(entry); 35081158958aSVlad Buslov if (entry->destructor) 35091158958aSVlad Buslov entry->destructor(entry->destructor_priv); 35105a6ff4b1SVlad Buslov } 35112008495dSJiri Pirko } 35125a6ff4b1SVlad Buslov EXPORT_SYMBOL(tc_cleanup_flow_action); 35135a6ff4b1SVlad Buslov 35141158958aSVlad Buslov static void tcf_mirred_get_dev(struct flow_action_entry *entry, 35151158958aSVlad Buslov const struct tc_action *act) 35161158958aSVlad Buslov { 3517470d5060SVlad Buslov #ifdef CONFIG_NET_CLS_ACT 3518470d5060SVlad Buslov entry->dev = act->ops->get_dev(act, &entry->destructor); 35191158958aSVlad Buslov if (!entry->dev) 35201158958aSVlad Buslov return; 35211158958aSVlad Buslov entry->destructor_priv = entry->dev; 3522470d5060SVlad Buslov #endif 35231158958aSVlad Buslov } 35241158958aSVlad Buslov 35251158958aSVlad Buslov static void tcf_tunnel_encap_put_tunnel(void *priv) 35261158958aSVlad Buslov { 35271158958aSVlad Buslov struct ip_tunnel_info *tunnel = priv; 35281158958aSVlad Buslov 35291158958aSVlad Buslov kfree(tunnel); 35301158958aSVlad Buslov } 35311158958aSVlad Buslov 35321158958aSVlad Buslov static int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry, 35331158958aSVlad Buslov const struct tc_action *act) 35341158958aSVlad Buslov { 35351158958aSVlad Buslov entry->tunnel = tcf_tunnel_info_copy(act); 35361158958aSVlad Buslov if (!entry->tunnel) 35371158958aSVlad Buslov return -ENOMEM; 35381158958aSVlad Buslov entry->destructor = tcf_tunnel_encap_put_tunnel; 35391158958aSVlad Buslov entry->destructor_priv = entry->tunnel; 35401158958aSVlad Buslov return 0; 35411158958aSVlad Buslov } 35421158958aSVlad Buslov 35434a5da47dSVlad Buslov static void tcf_sample_get_group(struct flow_action_entry *entry, 35444a5da47dSVlad Buslov const struct tc_action *act) 35454a5da47dSVlad Buslov { 35464a5da47dSVlad Buslov #ifdef CONFIG_NET_CLS_ACT 35474a5da47dSVlad Buslov entry->sample.psample_group = 35484a5da47dSVlad Buslov act->ops->get_psample_group(act, &entry->destructor); 35494a5da47dSVlad Buslov entry->destructor_priv = entry->sample.psample_group; 35504a5da47dSVlad Buslov #endif 35514a5da47dSVlad Buslov } 35524a5da47dSVlad Buslov 3553d29bdd69SPo Liu static void tcf_gate_entry_destructor(void *priv) 3554d29bdd69SPo Liu { 3555d29bdd69SPo Liu struct action_gate_entry *oe = priv; 3556d29bdd69SPo Liu 3557d29bdd69SPo Liu kfree(oe); 3558d29bdd69SPo Liu } 3559d29bdd69SPo Liu 3560d29bdd69SPo Liu static int tcf_gate_get_entries(struct flow_action_entry *entry, 3561d29bdd69SPo Liu const struct tc_action *act) 3562d29bdd69SPo Liu { 3563d29bdd69SPo Liu entry->gate.entries = tcf_gate_get_list(act); 3564d29bdd69SPo Liu 3565d29bdd69SPo Liu if (!entry->gate.entries) 3566d29bdd69SPo Liu return -EINVAL; 3567d29bdd69SPo Liu 3568d29bdd69SPo Liu entry->destructor = tcf_gate_entry_destructor; 3569d29bdd69SPo Liu entry->destructor_priv = entry->gate.entries; 3570d29bdd69SPo Liu 3571d29bdd69SPo Liu return 0; 3572d29bdd69SPo Liu } 3573d29bdd69SPo Liu 357416f80360SPablo Neira Ayuso static enum flow_action_hw_stats tc_act_hw_stats(u8 hw_stats) 357516f80360SPablo Neira Ayuso { 357616f80360SPablo Neira Ayuso if (WARN_ON_ONCE(hw_stats > TCA_ACT_HW_STATS_ANY)) 357716f80360SPablo Neira Ayuso return FLOW_ACTION_HW_STATS_DONT_CARE; 357816f80360SPablo Neira Ayuso else if (!hw_stats) 357916f80360SPablo Neira Ayuso return FLOW_ACTION_HW_STATS_DISABLED; 358016f80360SPablo Neira Ayuso 358116f80360SPablo Neira Ayuso return hw_stats; 358216f80360SPablo Neira Ayuso } 358316f80360SPablo Neira Ayuso 35843a7b6861SPablo Neira Ayuso int tc_setup_flow_action(struct flow_action *flow_action, 3585b15e7a6eSVlad Buslov const struct tcf_exts *exts) 35863a7b6861SPablo Neira Ayuso { 35877a472814SVlad Buslov struct tc_action *act; 35889838b20aSVlad Buslov int i, j, k, err = 0; 35893a7b6861SPablo Neira Ayuso 35900dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_ANY != FLOW_ACTION_HW_STATS_ANY); 35910dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE); 35920dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED); 359344f86580SJiri Pirko 35943a7b6861SPablo Neira Ayuso if (!exts) 35953a7b6861SPablo Neira Ayuso return 0; 35963a7b6861SPablo Neira Ayuso 35973a7b6861SPablo Neira Ayuso j = 0; 35983a7b6861SPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 35993a7b6861SPablo Neira Ayuso struct flow_action_entry *entry; 36003a7b6861SPablo Neira Ayuso 36013a7b6861SPablo Neira Ayuso entry = &flow_action->entries[j]; 36027a472814SVlad Buslov spin_lock_bh(&act->tcfa_lock); 36032008495dSJiri Pirko err = tcf_act_get_cookie(entry, act); 36042008495dSJiri Pirko if (err) 36052008495dSJiri Pirko goto err_out_locked; 360644f86580SJiri Pirko 360716f80360SPablo Neira Ayuso entry->hw_stats = tc_act_hw_stats(act->hw_stats); 360844f86580SJiri Pirko 36093a7b6861SPablo Neira Ayuso if (is_tcf_gact_ok(act)) { 36103a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ACCEPT; 36113a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_shot(act)) { 36123a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_DROP; 36133a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_trap(act)) { 36143a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TRAP; 36153a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_goto_chain(act)) { 36163a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_GOTO; 36173a7b6861SPablo Neira Ayuso entry->chain_index = tcf_gact_goto_chain_index(act); 36183a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_redirect(act)) { 36193a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_REDIRECT; 36201158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 36213a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_mirror(act)) { 36223a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MIRRED; 36231158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 362448e584acSJohn Hurley } else if (is_tcf_mirred_ingress_redirect(act)) { 362548e584acSJohn Hurley entry->id = FLOW_ACTION_REDIRECT_INGRESS; 36261158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 362748e584acSJohn Hurley } else if (is_tcf_mirred_ingress_mirror(act)) { 362848e584acSJohn Hurley entry->id = FLOW_ACTION_MIRRED_INGRESS; 36291158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 36303a7b6861SPablo Neira Ayuso } else if (is_tcf_vlan(act)) { 36313a7b6861SPablo Neira Ayuso switch (tcf_vlan_action(act)) { 36323a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_PUSH: 36333a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_PUSH; 36343a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 36353a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 36363a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 36373a7b6861SPablo Neira Ayuso break; 36383a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_POP: 36393a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_POP; 36403a7b6861SPablo Neira Ayuso break; 36413a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_MODIFY: 36423a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_MANGLE; 36433a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 36443a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 36453a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 36463a7b6861SPablo Neira Ayuso break; 36473a7b6861SPablo Neira Ayuso default: 36489838b20aSVlad Buslov err = -EOPNOTSUPP; 36497a472814SVlad Buslov goto err_out_locked; 36503a7b6861SPablo Neira Ayuso } 36513a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_set(act)) { 36523a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_ENCAP; 36531158958aSVlad Buslov err = tcf_tunnel_encap_get_tunnel(entry, act); 36541158958aSVlad Buslov if (err) 36557a472814SVlad Buslov goto err_out_locked; 36563a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_release(act)) { 36573a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_DECAP; 36583a7b6861SPablo Neira Ayuso } else if (is_tcf_pedit(act)) { 36593a7b6861SPablo Neira Ayuso for (k = 0; k < tcf_pedit_nkeys(act); k++) { 36603a7b6861SPablo Neira Ayuso switch (tcf_pedit_cmd(act, k)) { 36613a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_SET: 36623a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MANGLE; 36633a7b6861SPablo Neira Ayuso break; 36643a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_ADD: 36653a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ADD; 36663a7b6861SPablo Neira Ayuso break; 36673a7b6861SPablo Neira Ayuso default: 36689838b20aSVlad Buslov err = -EOPNOTSUPP; 36697a472814SVlad Buslov goto err_out_locked; 36703a7b6861SPablo Neira Ayuso } 36713a7b6861SPablo Neira Ayuso entry->mangle.htype = tcf_pedit_htype(act, k); 36723a7b6861SPablo Neira Ayuso entry->mangle.mask = tcf_pedit_mask(act, k); 36733a7b6861SPablo Neira Ayuso entry->mangle.val = tcf_pedit_val(act, k); 36743a7b6861SPablo Neira Ayuso entry->mangle.offset = tcf_pedit_offset(act, k); 367516f80360SPablo Neira Ayuso entry->hw_stats = tc_act_hw_stats(act->hw_stats); 36762c4b58dcSPetr Machata entry = &flow_action->entries[++j]; 36773a7b6861SPablo Neira Ayuso } 36783a7b6861SPablo Neira Ayuso } else if (is_tcf_csum(act)) { 36793a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_CSUM; 36803a7b6861SPablo Neira Ayuso entry->csum_flags = tcf_csum_update_flags(act); 36813a7b6861SPablo Neira Ayuso } else if (is_tcf_skbedit_mark(act)) { 36823a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MARK; 36833a7b6861SPablo Neira Ayuso entry->mark = tcf_skbedit_mark(act); 3684a7a7be60SPieter Jansen van Vuuren } else if (is_tcf_sample(act)) { 3685a7a7be60SPieter Jansen van Vuuren entry->id = FLOW_ACTION_SAMPLE; 3686a7a7be60SPieter Jansen van Vuuren entry->sample.trunc_size = tcf_sample_trunc_size(act); 3687a7a7be60SPieter Jansen van Vuuren entry->sample.truncate = tcf_sample_truncate(act); 3688a7a7be60SPieter Jansen van Vuuren entry->sample.rate = tcf_sample_rate(act); 36894a5da47dSVlad Buslov tcf_sample_get_group(entry, act); 36908c8cfc6eSPieter Jansen van Vuuren } else if (is_tcf_police(act)) { 36918c8cfc6eSPieter Jansen van Vuuren entry->id = FLOW_ACTION_POLICE; 36928c8cfc6eSPieter Jansen van Vuuren entry->police.burst = tcf_police_tcfp_burst(act); 36938c8cfc6eSPieter Jansen van Vuuren entry->police.rate_bytes_ps = 36948c8cfc6eSPieter Jansen van Vuuren tcf_police_rate_bytes_ps(act); 3695b57dc7c1SPaul Blakey } else if (is_tcf_ct(act)) { 3696b57dc7c1SPaul Blakey entry->id = FLOW_ACTION_CT; 3697b57dc7c1SPaul Blakey entry->ct.action = tcf_ct_action(act); 3698b57dc7c1SPaul Blakey entry->ct.zone = tcf_ct_zone(act); 3699edd5861eSPaul Blakey entry->ct.flow_table = tcf_ct_ft(act); 37006749d590SJohn Hurley } else if (is_tcf_mpls(act)) { 37016749d590SJohn Hurley switch (tcf_mpls_action(act)) { 37026749d590SJohn Hurley case TCA_MPLS_ACT_PUSH: 37036749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_PUSH; 37046749d590SJohn Hurley entry->mpls_push.proto = tcf_mpls_proto(act); 37056749d590SJohn Hurley entry->mpls_push.label = tcf_mpls_label(act); 37066749d590SJohn Hurley entry->mpls_push.tc = tcf_mpls_tc(act); 37076749d590SJohn Hurley entry->mpls_push.bos = tcf_mpls_bos(act); 37086749d590SJohn Hurley entry->mpls_push.ttl = tcf_mpls_ttl(act); 37096749d590SJohn Hurley break; 37106749d590SJohn Hurley case TCA_MPLS_ACT_POP: 37116749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_POP; 37126749d590SJohn Hurley entry->mpls_pop.proto = tcf_mpls_proto(act); 37136749d590SJohn Hurley break; 37146749d590SJohn Hurley case TCA_MPLS_ACT_MODIFY: 37156749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_MANGLE; 37166749d590SJohn Hurley entry->mpls_mangle.label = tcf_mpls_label(act); 37176749d590SJohn Hurley entry->mpls_mangle.tc = tcf_mpls_tc(act); 37186749d590SJohn Hurley entry->mpls_mangle.bos = tcf_mpls_bos(act); 37196749d590SJohn Hurley entry->mpls_mangle.ttl = tcf_mpls_ttl(act); 37206749d590SJohn Hurley break; 37216749d590SJohn Hurley default: 37227a472814SVlad Buslov goto err_out_locked; 37236749d590SJohn Hurley } 3724fb1b775aSJohn Hurley } else if (is_tcf_skbedit_ptype(act)) { 3725fb1b775aSJohn Hurley entry->id = FLOW_ACTION_PTYPE; 3726fb1b775aSJohn Hurley entry->ptype = tcf_skbedit_ptype(act); 37272ce12410SPetr Machata } else if (is_tcf_skbedit_priority(act)) { 37282ce12410SPetr Machata entry->id = FLOW_ACTION_PRIORITY; 37292ce12410SPetr Machata entry->priority = tcf_skbedit_priority(act); 3730d29bdd69SPo Liu } else if (is_tcf_gate(act)) { 3731d29bdd69SPo Liu entry->id = FLOW_ACTION_GATE; 3732d29bdd69SPo Liu entry->gate.index = tcf_gate_index(act); 3733d29bdd69SPo Liu entry->gate.prio = tcf_gate_prio(act); 3734d29bdd69SPo Liu entry->gate.basetime = tcf_gate_basetime(act); 3735d29bdd69SPo Liu entry->gate.cycletime = tcf_gate_cycletime(act); 3736d29bdd69SPo Liu entry->gate.cycletimeext = tcf_gate_cycletimeext(act); 3737d29bdd69SPo Liu entry->gate.num_entries = tcf_gate_num_entries(act); 3738d29bdd69SPo Liu err = tcf_gate_get_entries(entry, act); 3739d29bdd69SPo Liu if (err) 3740d29bdd69SPo Liu goto err_out; 37413a7b6861SPablo Neira Ayuso } else { 37429838b20aSVlad Buslov err = -EOPNOTSUPP; 37437a472814SVlad Buslov goto err_out_locked; 37443a7b6861SPablo Neira Ayuso } 37457a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 37463a7b6861SPablo Neira Ayuso 37473a7b6861SPablo Neira Ayuso if (!is_tcf_pedit(act)) 37483a7b6861SPablo Neira Ayuso j++; 37493a7b6861SPablo Neira Ayuso } 37509838b20aSVlad Buslov 37513a7b6861SPablo Neira Ayuso err_out: 37525a6ff4b1SVlad Buslov if (err) 37535a6ff4b1SVlad Buslov tc_cleanup_flow_action(flow_action); 37545a6ff4b1SVlad Buslov 37559838b20aSVlad Buslov return err; 37567a472814SVlad Buslov err_out_locked: 37577a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 37587a472814SVlad Buslov goto err_out; 37593a7b6861SPablo Neira Ayuso } 37603a7b6861SPablo Neira Ayuso EXPORT_SYMBOL(tc_setup_flow_action); 37613a7b6861SPablo Neira Ayuso 3762e3ab786bSPablo Neira Ayuso unsigned int tcf_exts_num_actions(struct tcf_exts *exts) 3763e3ab786bSPablo Neira Ayuso { 3764e3ab786bSPablo Neira Ayuso unsigned int num_acts = 0; 3765e3ab786bSPablo Neira Ayuso struct tc_action *act; 3766e3ab786bSPablo Neira Ayuso int i; 3767e3ab786bSPablo Neira Ayuso 3768e3ab786bSPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 3769e3ab786bSPablo Neira Ayuso if (is_tcf_pedit(act)) 3770e3ab786bSPablo Neira Ayuso num_acts += tcf_pedit_nkeys(act); 3771e3ab786bSPablo Neira Ayuso else 3772e3ab786bSPablo Neira Ayuso num_acts++; 3773e3ab786bSPablo Neira Ayuso } 3774e3ab786bSPablo Neira Ayuso return num_acts; 3775e3ab786bSPablo Neira Ayuso } 3776e3ab786bSPablo Neira Ayuso EXPORT_SYMBOL(tcf_exts_num_actions); 3777e3ab786bSPablo Neira Ayuso 377848617387SJiri Pirko static __net_init int tcf_net_init(struct net *net) 377948617387SJiri Pirko { 378048617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 378148617387SJiri Pirko 3782ab281629SVlad Buslov spin_lock_init(&tn->idr_lock); 378348617387SJiri Pirko idr_init(&tn->idr); 378448617387SJiri Pirko return 0; 378548617387SJiri Pirko } 378648617387SJiri Pirko 378748617387SJiri Pirko static void __net_exit tcf_net_exit(struct net *net) 378848617387SJiri Pirko { 378948617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 379048617387SJiri Pirko 379148617387SJiri Pirko idr_destroy(&tn->idr); 379248617387SJiri Pirko } 379348617387SJiri Pirko 379448617387SJiri Pirko static struct pernet_operations tcf_net_ops = { 379548617387SJiri Pirko .init = tcf_net_init, 379648617387SJiri Pirko .exit = tcf_net_exit, 379748617387SJiri Pirko .id = &tcf_net_id, 379848617387SJiri Pirko .size = sizeof(struct tcf_net), 379948617387SJiri Pirko }; 380048617387SJiri Pirko 380125a443f7SJohn Hurley static struct flow_indr_block_entry block_entry = { 380225a443f7SJohn Hurley .cb = tc_indr_block_get_and_cmd, 380325a443f7SJohn Hurley .list = LIST_HEAD_INIT(block_entry.list), 38041150ab0fSwenxu }; 38051150ab0fSwenxu 38061da177e4SLinus Torvalds static int __init tc_filter_init(void) 38071da177e4SLinus Torvalds { 380848617387SJiri Pirko int err; 380948617387SJiri Pirko 38107aa0045dSCong Wang tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 38117aa0045dSCong Wang if (!tc_filter_wq) 38127aa0045dSCong Wang return -ENOMEM; 38137aa0045dSCong Wang 381448617387SJiri Pirko err = register_pernet_subsys(&tcf_net_ops); 381548617387SJiri Pirko if (err) 381648617387SJiri Pirko goto err_register_pernet_subsys; 381748617387SJiri Pirko 381825a443f7SJohn Hurley flow_indr_add_block_cb(&block_entry); 38191150ab0fSwenxu 3820470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 3821470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3822470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 3823470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3824c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 3825470502deSVlad Buslov tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED); 382632a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0); 382732a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0); 382832a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain, 382932a4f5ecSJiri Pirko tc_dump_chain, 0); 38301da177e4SLinus Torvalds 38311da177e4SLinus Torvalds return 0; 383248617387SJiri Pirko 383348617387SJiri Pirko err_register_pernet_subsys: 383448617387SJiri Pirko destroy_workqueue(tc_filter_wq); 383548617387SJiri Pirko return err; 38361da177e4SLinus Torvalds } 38371da177e4SLinus Torvalds 38381da177e4SLinus Torvalds subsys_initcall(tc_filter_init); 3839