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> 25b854272bSDenis V. Lunev #include <net/net_namespace.h> 26b854272bSDenis V. Lunev #include <net/sock.h> 27dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 281da177e4SLinus Torvalds #include <net/pkt_sched.h> 291da177e4SLinus Torvalds #include <net/pkt_cls.h> 30e3ab786bSPablo Neira Ayuso #include <net/tc_act/tc_pedit.h> 313a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_mirred.h> 323a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_vlan.h> 333a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_tunnel_key.h> 343a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_csum.h> 353a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_gact.h> 368c8cfc6eSPieter Jansen van Vuuren #include <net/tc_act/tc_police.h> 37a7a7be60SPieter Jansen van Vuuren #include <net/tc_act/tc_sample.h> 383a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_skbedit.h> 39b57dc7c1SPaul Blakey #include <net/tc_act/tc_ct.h> 406749d590SJohn Hurley #include <net/tc_act/tc_mpls.h> 414e481908Swenxu #include <net/flow_offload.h> 421da177e4SLinus Torvalds 43e331473fSDavide Caratti extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; 44e331473fSDavide Caratti 451da177e4SLinus Torvalds /* The list of all installed classifier types */ 4636272874SWANG Cong static LIST_HEAD(tcf_proto_base); 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */ 491da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock); 501da177e4SLinus Torvalds 5159eb87cbSJohn Hurley static u32 destroy_obj_hashfn(const struct tcf_proto *tp) 5259eb87cbSJohn Hurley { 5359eb87cbSJohn Hurley return jhash_3words(tp->chain->index, tp->prio, 5459eb87cbSJohn Hurley (__force __u32)tp->protocol, 0); 5559eb87cbSJohn Hurley } 5659eb87cbSJohn Hurley 5759eb87cbSJohn Hurley static void tcf_proto_signal_destroying(struct tcf_chain *chain, 5859eb87cbSJohn Hurley struct tcf_proto *tp) 5959eb87cbSJohn Hurley { 6059eb87cbSJohn Hurley struct tcf_block *block = chain->block; 6159eb87cbSJohn Hurley 6259eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 6359eb87cbSJohn Hurley hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node, 6459eb87cbSJohn Hurley destroy_obj_hashfn(tp)); 6559eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 6659eb87cbSJohn Hurley } 6759eb87cbSJohn Hurley 6859eb87cbSJohn Hurley static bool tcf_proto_cmp(const struct tcf_proto *tp1, 6959eb87cbSJohn Hurley const struct tcf_proto *tp2) 7059eb87cbSJohn Hurley { 7159eb87cbSJohn Hurley return tp1->chain->index == tp2->chain->index && 7259eb87cbSJohn Hurley tp1->prio == tp2->prio && 7359eb87cbSJohn Hurley tp1->protocol == tp2->protocol; 7459eb87cbSJohn Hurley } 7559eb87cbSJohn Hurley 7659eb87cbSJohn Hurley static bool tcf_proto_exists_destroying(struct tcf_chain *chain, 7759eb87cbSJohn Hurley struct tcf_proto *tp) 7859eb87cbSJohn Hurley { 7959eb87cbSJohn Hurley u32 hash = destroy_obj_hashfn(tp); 8059eb87cbSJohn Hurley struct tcf_proto *iter; 8159eb87cbSJohn Hurley bool found = false; 8259eb87cbSJohn Hurley 8359eb87cbSJohn Hurley rcu_read_lock(); 8459eb87cbSJohn Hurley hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter, 8559eb87cbSJohn Hurley destroy_ht_node, hash) { 8659eb87cbSJohn Hurley if (tcf_proto_cmp(tp, iter)) { 8759eb87cbSJohn Hurley found = true; 8859eb87cbSJohn Hurley break; 8959eb87cbSJohn Hurley } 9059eb87cbSJohn Hurley } 9159eb87cbSJohn Hurley rcu_read_unlock(); 9259eb87cbSJohn Hurley 9359eb87cbSJohn Hurley return found; 9459eb87cbSJohn Hurley } 9559eb87cbSJohn Hurley 9659eb87cbSJohn Hurley static void 9759eb87cbSJohn Hurley tcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp) 9859eb87cbSJohn Hurley { 9959eb87cbSJohn Hurley struct tcf_block *block = chain->block; 10059eb87cbSJohn Hurley 10159eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 10259eb87cbSJohn Hurley if (hash_hashed(&tp->destroy_ht_node)) 10359eb87cbSJohn Hurley hash_del_rcu(&tp->destroy_ht_node); 10459eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 10559eb87cbSJohn Hurley } 10659eb87cbSJohn Hurley 1071da177e4SLinus Torvalds /* Find classifier type by string name */ 1081da177e4SLinus Torvalds 109f34e8bffSJiri Pirko static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind) 1101da177e4SLinus Torvalds { 111dcd76081SEric Dumazet const struct tcf_proto_ops *t, *res = NULL; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if (kind) { 1141da177e4SLinus Torvalds read_lock(&cls_mod_lock); 11536272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) { 11633a48927SJiri Pirko if (strcmp(kind, t->kind) == 0) { 117dcd76081SEric Dumazet if (try_module_get(t->owner)) 118dcd76081SEric Dumazet res = t; 1191da177e4SLinus Torvalds break; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds read_unlock(&cls_mod_lock); 1231da177e4SLinus Torvalds } 124dcd76081SEric Dumazet return res; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 127f34e8bffSJiri Pirko static const struct tcf_proto_ops * 12812db03b6SVlad Buslov tcf_proto_lookup_ops(const char *kind, bool rtnl_held, 12912db03b6SVlad Buslov struct netlink_ext_ack *extack) 130f34e8bffSJiri Pirko { 131f34e8bffSJiri Pirko const struct tcf_proto_ops *ops; 132f34e8bffSJiri Pirko 133f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 134f34e8bffSJiri Pirko if (ops) 135f34e8bffSJiri Pirko return ops; 136f34e8bffSJiri Pirko #ifdef CONFIG_MODULES 13712db03b6SVlad Buslov if (rtnl_held) 138f34e8bffSJiri Pirko rtnl_unlock(); 139f34e8bffSJiri Pirko request_module("cls_%s", kind); 14012db03b6SVlad Buslov if (rtnl_held) 141f34e8bffSJiri Pirko rtnl_lock(); 142f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 143f34e8bffSJiri Pirko /* We dropped the RTNL semaphore in order to perform 144f34e8bffSJiri Pirko * the module load. So, even if we succeeded in loading 145f34e8bffSJiri Pirko * the module we have to replay the request. We indicate 146f34e8bffSJiri Pirko * this using -EAGAIN. 147f34e8bffSJiri Pirko */ 148f34e8bffSJiri Pirko if (ops) { 149f34e8bffSJiri Pirko module_put(ops->owner); 150f34e8bffSJiri Pirko return ERR_PTR(-EAGAIN); 151f34e8bffSJiri Pirko } 152f34e8bffSJiri Pirko #endif 153f34e8bffSJiri Pirko NL_SET_ERR_MSG(extack, "TC classifier not found"); 154f34e8bffSJiri Pirko return ERR_PTR(-ENOENT); 155f34e8bffSJiri Pirko } 156f34e8bffSJiri Pirko 1571da177e4SLinus Torvalds /* Register(unregister) new classifier type */ 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops) 1601da177e4SLinus Torvalds { 16136272874SWANG Cong struct tcf_proto_ops *t; 1621da177e4SLinus Torvalds int rc = -EEXIST; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds write_lock(&cls_mod_lock); 16536272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) 1661da177e4SLinus Torvalds if (!strcmp(ops->kind, t->kind)) 1671da177e4SLinus Torvalds goto out; 1681da177e4SLinus Torvalds 16936272874SWANG Cong list_add_tail(&ops->head, &tcf_proto_base); 1701da177e4SLinus Torvalds rc = 0; 1711da177e4SLinus Torvalds out: 1721da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1731da177e4SLinus Torvalds return rc; 1741da177e4SLinus Torvalds } 175aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops); 1761da177e4SLinus Torvalds 1777aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq; 1787aa0045dSCong Wang 1791da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 1801da177e4SLinus Torvalds { 18136272874SWANG Cong struct tcf_proto_ops *t; 1821da177e4SLinus Torvalds int rc = -ENOENT; 1831da177e4SLinus Torvalds 184c78e1746SDaniel Borkmann /* Wait for outstanding call_rcu()s, if any, from a 185c78e1746SDaniel Borkmann * tcf_proto_ops's destroy() handler. 186c78e1746SDaniel Borkmann */ 187c78e1746SDaniel Borkmann rcu_barrier(); 1887aa0045dSCong Wang flush_workqueue(tc_filter_wq); 189c78e1746SDaniel Borkmann 1901da177e4SLinus Torvalds write_lock(&cls_mod_lock); 191dcd76081SEric Dumazet list_for_each_entry(t, &tcf_proto_base, head) { 192dcd76081SEric Dumazet if (t == ops) { 19336272874SWANG Cong list_del(&t->head); 1941da177e4SLinus Torvalds rc = 0; 195dcd76081SEric Dumazet break; 196dcd76081SEric Dumazet } 197dcd76081SEric Dumazet } 1981da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1991da177e4SLinus Torvalds return rc; 2001da177e4SLinus Torvalds } 201aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops); 2021da177e4SLinus Torvalds 203aaa908ffSCong Wang bool tcf_queue_work(struct rcu_work *rwork, work_func_t func) 2047aa0045dSCong Wang { 205aaa908ffSCong Wang INIT_RCU_WORK(rwork, func); 206aaa908ffSCong Wang return queue_rcu_work(tc_filter_wq, rwork); 2077aa0045dSCong Wang } 2087aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work); 2097aa0045dSCong Wang 2101da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */ 2111da177e4SLinus Torvalds 212aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds u32 first = TC_H_MAKE(0xC0000000U, 0U); 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds if (tp) 2171da177e4SLinus Torvalds first = tp->prio - 1; 2181da177e4SLinus Torvalds 2197961973aSJiri Pirko return TC_H_MAJ(first); 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 2226f96c3c6SCong Wang static bool tcf_proto_check_kind(struct nlattr *kind, char *name) 2236f96c3c6SCong Wang { 2246f96c3c6SCong Wang if (kind) 2256f96c3c6SCong Wang return nla_strlcpy(name, kind, IFNAMSIZ) >= IFNAMSIZ; 2266f96c3c6SCong Wang memset(name, 0, IFNAMSIZ); 2276f96c3c6SCong Wang return false; 2286f96c3c6SCong Wang } 2296f96c3c6SCong Wang 230470502deSVlad Buslov static bool tcf_proto_is_unlocked(const char *kind) 231470502deSVlad Buslov { 232470502deSVlad Buslov const struct tcf_proto_ops *ops; 233470502deSVlad Buslov bool ret; 234470502deSVlad Buslov 2356f96c3c6SCong Wang if (strlen(kind) == 0) 2366f96c3c6SCong Wang return false; 2376f96c3c6SCong Wang 238470502deSVlad Buslov ops = tcf_proto_lookup_ops(kind, false, NULL); 239470502deSVlad Buslov /* On error return false to take rtnl lock. Proto lookup/create 240470502deSVlad Buslov * functions will perform lookup again and properly handle errors. 241470502deSVlad Buslov */ 242470502deSVlad Buslov if (IS_ERR(ops)) 243470502deSVlad Buslov return false; 244470502deSVlad Buslov 245470502deSVlad Buslov ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); 246470502deSVlad Buslov module_put(ops->owner); 247470502deSVlad Buslov return ret; 248470502deSVlad Buslov } 249470502deSVlad Buslov 25033a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 251c35a4accSAlexander Aring u32 prio, struct tcf_chain *chain, 25212db03b6SVlad Buslov bool rtnl_held, 253c35a4accSAlexander Aring struct netlink_ext_ack *extack) 25433a48927SJiri Pirko { 25533a48927SJiri Pirko struct tcf_proto *tp; 25633a48927SJiri Pirko int err; 25733a48927SJiri Pirko 25833a48927SJiri Pirko tp = kzalloc(sizeof(*tp), GFP_KERNEL); 25933a48927SJiri Pirko if (!tp) 26033a48927SJiri Pirko return ERR_PTR(-ENOBUFS); 26133a48927SJiri Pirko 26212db03b6SVlad Buslov tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack); 263f34e8bffSJiri Pirko if (IS_ERR(tp->ops)) { 264f34e8bffSJiri Pirko err = PTR_ERR(tp->ops); 265d68d75fdSJiri Pirko goto errout; 26633a48927SJiri Pirko } 26733a48927SJiri Pirko tp->classify = tp->ops->classify; 26833a48927SJiri Pirko tp->protocol = protocol; 26933a48927SJiri Pirko tp->prio = prio; 2705bc17018SJiri Pirko tp->chain = chain; 2718b64678eSVlad Buslov spin_lock_init(&tp->lock); 2724dbfa766SVlad Buslov refcount_set(&tp->refcnt, 1); 27333a48927SJiri Pirko 27433a48927SJiri Pirko err = tp->ops->init(tp); 27533a48927SJiri Pirko if (err) { 27633a48927SJiri Pirko module_put(tp->ops->owner); 27733a48927SJiri Pirko goto errout; 27833a48927SJiri Pirko } 27933a48927SJiri Pirko return tp; 28033a48927SJiri Pirko 28133a48927SJiri Pirko errout: 28233a48927SJiri Pirko kfree(tp); 28333a48927SJiri Pirko return ERR_PTR(err); 28433a48927SJiri Pirko } 28533a48927SJiri Pirko 2864dbfa766SVlad Buslov static void tcf_proto_get(struct tcf_proto *tp) 2874dbfa766SVlad Buslov { 2884dbfa766SVlad Buslov refcount_inc(&tp->refcnt); 2894dbfa766SVlad Buslov } 2904dbfa766SVlad Buslov 2914dbfa766SVlad Buslov static void tcf_chain_put(struct tcf_chain *chain); 2924dbfa766SVlad Buslov 29312db03b6SVlad Buslov static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, 29459eb87cbSJohn Hurley bool sig_destroy, struct netlink_ext_ack *extack) 295cf1facdaSJiri Pirko { 29612db03b6SVlad Buslov tp->ops->destroy(tp, rtnl_held, extack); 29759eb87cbSJohn Hurley if (sig_destroy) 29859eb87cbSJohn Hurley tcf_proto_signal_destroyed(tp->chain, tp); 2994dbfa766SVlad Buslov tcf_chain_put(tp->chain); 300cf1facdaSJiri Pirko module_put(tp->ops->owner); 301cf1facdaSJiri Pirko kfree_rcu(tp, rcu); 302cf1facdaSJiri Pirko } 303cf1facdaSJiri Pirko 30412db03b6SVlad Buslov static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held, 3054dbfa766SVlad Buslov struct netlink_ext_ack *extack) 3064dbfa766SVlad Buslov { 3074dbfa766SVlad Buslov if (refcount_dec_and_test(&tp->refcnt)) 30859eb87cbSJohn Hurley tcf_proto_destroy(tp, rtnl_held, true, extack); 3094dbfa766SVlad Buslov } 3104dbfa766SVlad Buslov 311a5b72a08SDavide Caratti static bool tcf_proto_check_delete(struct tcf_proto *tp) 3128b64678eSVlad Buslov { 313a5b72a08SDavide Caratti if (tp->ops->delete_empty) 314a5b72a08SDavide Caratti return tp->ops->delete_empty(tp); 3158b64678eSVlad Buslov 3168b64678eSVlad Buslov tp->deleting = true; 3178b64678eSVlad Buslov return tp->deleting; 3188b64678eSVlad Buslov } 3198b64678eSVlad Buslov 3208b64678eSVlad Buslov static void tcf_proto_mark_delete(struct tcf_proto *tp) 3218b64678eSVlad Buslov { 3228b64678eSVlad Buslov spin_lock(&tp->lock); 3238b64678eSVlad Buslov tp->deleting = true; 3248b64678eSVlad Buslov spin_unlock(&tp->lock); 3258b64678eSVlad Buslov } 3268b64678eSVlad Buslov 3278b64678eSVlad Buslov static bool tcf_proto_is_deleting(struct tcf_proto *tp) 3288b64678eSVlad Buslov { 3298b64678eSVlad Buslov bool deleting; 3308b64678eSVlad Buslov 3318b64678eSVlad Buslov spin_lock(&tp->lock); 3328b64678eSVlad Buslov deleting = tp->deleting; 3338b64678eSVlad Buslov spin_unlock(&tp->lock); 3348b64678eSVlad Buslov 3358b64678eSVlad Buslov return deleting; 3368b64678eSVlad Buslov } 3378b64678eSVlad Buslov 338c266f64dSVlad Buslov #define ASSERT_BLOCK_LOCKED(block) \ 339c266f64dSVlad Buslov lockdep_assert_held(&(block)->lock) 340c266f64dSVlad Buslov 341a9b19443SJiri Pirko struct tcf_filter_chain_list_item { 342a9b19443SJiri Pirko struct list_head list; 343a9b19443SJiri Pirko tcf_chain_head_change_t *chain_head_change; 344a9b19443SJiri Pirko void *chain_head_change_priv; 345a9b19443SJiri Pirko }; 346a9b19443SJiri Pirko 3475bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block, 3485bc17018SJiri Pirko u32 chain_index) 3492190d1d0SJiri Pirko { 3505bc17018SJiri Pirko struct tcf_chain *chain; 3515bc17018SJiri Pirko 352c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 353c266f64dSVlad Buslov 3545bc17018SJiri Pirko chain = kzalloc(sizeof(*chain), GFP_KERNEL); 3555bc17018SJiri Pirko if (!chain) 3565bc17018SJiri Pirko return NULL; 3575bc17018SJiri Pirko list_add_tail(&chain->list, &block->chain_list); 358ed76f5edSVlad Buslov mutex_init(&chain->filter_chain_lock); 3595bc17018SJiri Pirko chain->block = block; 3605bc17018SJiri Pirko chain->index = chain_index; 361e2ef7544SCong Wang chain->refcnt = 1; 362f71e0ca4SJiri Pirko if (!chain->index) 363f71e0ca4SJiri Pirko block->chain0.chain = chain; 3645bc17018SJiri Pirko return chain; 3652190d1d0SJiri Pirko } 3662190d1d0SJiri Pirko 367a9b19443SJiri Pirko static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, 368a9b19443SJiri Pirko struct tcf_proto *tp_head) 369a9b19443SJiri Pirko { 370a9b19443SJiri Pirko if (item->chain_head_change) 371a9b19443SJiri Pirko item->chain_head_change(tp_head, item->chain_head_change_priv); 372a9b19443SJiri Pirko } 373f71e0ca4SJiri Pirko 374f71e0ca4SJiri Pirko static void tcf_chain0_head_change(struct tcf_chain *chain, 375c7eb7d72SJiri Pirko struct tcf_proto *tp_head) 376c7eb7d72SJiri Pirko { 377a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 378f71e0ca4SJiri Pirko struct tcf_block *block = chain->block; 379a9b19443SJiri Pirko 380f71e0ca4SJiri Pirko if (chain->index) 381f71e0ca4SJiri Pirko return; 382165f0135SVlad Buslov 383165f0135SVlad Buslov mutex_lock(&block->lock); 384f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) 385a9b19443SJiri Pirko tcf_chain_head_change_item(item, tp_head); 386165f0135SVlad Buslov mutex_unlock(&block->lock); 387c7eb7d72SJiri Pirko } 388c7eb7d72SJiri Pirko 389c266f64dSVlad Buslov /* Returns true if block can be safely freed. */ 390c266f64dSVlad Buslov 391c266f64dSVlad Buslov static bool tcf_chain_detach(struct tcf_chain *chain) 392f93e1cdcSJiri Pirko { 393efbf7897SCong Wang struct tcf_block *block = chain->block; 394efbf7897SCong Wang 395c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 396c266f64dSVlad Buslov 397e2ef7544SCong Wang list_del(&chain->list); 398f71e0ca4SJiri Pirko if (!chain->index) 399f71e0ca4SJiri Pirko block->chain0.chain = NULL; 400c266f64dSVlad Buslov 401c266f64dSVlad Buslov if (list_empty(&block->chain_list) && 402c266f64dSVlad Buslov refcount_read(&block->refcnt) == 0) 403c266f64dSVlad Buslov return true; 404c266f64dSVlad Buslov 405c266f64dSVlad Buslov return false; 406c266f64dSVlad Buslov } 407c266f64dSVlad Buslov 408c266f64dSVlad Buslov static void tcf_block_destroy(struct tcf_block *block) 409c266f64dSVlad Buslov { 410c266f64dSVlad Buslov mutex_destroy(&block->lock); 41159eb87cbSJohn Hurley mutex_destroy(&block->proto_destroy_lock); 4120607e439SVlad Buslov kfree_rcu(block, rcu); 4132190d1d0SJiri Pirko } 4142190d1d0SJiri Pirko 415c266f64dSVlad Buslov static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block) 416c266f64dSVlad Buslov { 417c266f64dSVlad Buslov struct tcf_block *block = chain->block; 418c266f64dSVlad Buslov 419ed76f5edSVlad Buslov mutex_destroy(&chain->filter_chain_lock); 420ee3bbfe8SDavide Caratti kfree_rcu(chain, rcu); 421c266f64dSVlad Buslov if (free_block) 422c266f64dSVlad Buslov tcf_block_destroy(block); 423c266f64dSVlad Buslov } 424c266f64dSVlad Buslov 425e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain) 426e2ef7544SCong Wang { 427c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 428c266f64dSVlad Buslov 429e2ef7544SCong Wang ++chain->refcnt; 430e2ef7544SCong Wang } 431e2ef7544SCong Wang 4323d32f4c5SJiri Pirko static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain) 4331f3ed383SJiri Pirko { 434c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 435c266f64dSVlad Buslov 4361f3ed383SJiri Pirko /* In case all the references are action references, this 4373d32f4c5SJiri Pirko * chain should not be shown to the user. 4381f3ed383SJiri Pirko */ 4391f3ed383SJiri Pirko return chain->refcnt == chain->action_refcnt; 4401f3ed383SJiri Pirko } 4411f3ed383SJiri Pirko 44232a4f5ecSJiri Pirko static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, 44332a4f5ecSJiri Pirko u32 chain_index) 4445bc17018SJiri Pirko { 4455bc17018SJiri Pirko struct tcf_chain *chain; 4465bc17018SJiri Pirko 447c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 448c266f64dSVlad Buslov 4495bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 45032a4f5ecSJiri Pirko if (chain->index == chain_index) 45132a4f5ecSJiri Pirko return chain; 45232a4f5ecSJiri Pirko } 45332a4f5ecSJiri Pirko return NULL; 45432a4f5ecSJiri Pirko } 45532a4f5ecSJiri Pirko 45632a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 45732a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast); 45832a4f5ecSJiri Pirko 45953681407SJiri Pirko static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, 46053681407SJiri Pirko u32 chain_index, bool create, 46153681407SJiri Pirko bool by_act) 46232a4f5ecSJiri Pirko { 463c266f64dSVlad Buslov struct tcf_chain *chain = NULL; 464c266f64dSVlad Buslov bool is_first_reference; 46532a4f5ecSJiri Pirko 466c266f64dSVlad Buslov mutex_lock(&block->lock); 467c266f64dSVlad Buslov chain = tcf_chain_lookup(block, chain_index); 46832a4f5ecSJiri Pirko if (chain) { 469e2ef7544SCong Wang tcf_chain_hold(chain); 47053681407SJiri Pirko } else { 47132a4f5ecSJiri Pirko if (!create) 472c266f64dSVlad Buslov goto errout; 47332a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 47432a4f5ecSJiri Pirko if (!chain) 475c266f64dSVlad Buslov goto errout; 47653681407SJiri Pirko } 47753681407SJiri Pirko 47853681407SJiri Pirko if (by_act) 47953681407SJiri Pirko ++chain->action_refcnt; 480c266f64dSVlad Buslov is_first_reference = chain->refcnt - chain->action_refcnt == 1; 481c266f64dSVlad Buslov mutex_unlock(&block->lock); 48253681407SJiri Pirko 48353681407SJiri Pirko /* Send notification only in case we got the first 48453681407SJiri Pirko * non-action reference. Until then, the chain acts only as 48553681407SJiri Pirko * a placeholder for actions pointing to it and user ought 48653681407SJiri Pirko * not know about them. 48753681407SJiri Pirko */ 488c266f64dSVlad Buslov if (is_first_reference && !by_act) 48932a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 49032a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 49153681407SJiri Pirko 49232a4f5ecSJiri Pirko return chain; 493c266f64dSVlad Buslov 494c266f64dSVlad Buslov errout: 495c266f64dSVlad Buslov mutex_unlock(&block->lock); 496c266f64dSVlad Buslov return chain; 497e2ef7544SCong Wang } 49853681407SJiri Pirko 499290b1c8bSJiri Pirko static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 50053681407SJiri Pirko bool create) 50153681407SJiri Pirko { 50253681407SJiri Pirko return __tcf_chain_get(block, chain_index, create, false); 50353681407SJiri Pirko } 5045bc17018SJiri Pirko 5051f3ed383SJiri Pirko struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index) 5061f3ed383SJiri Pirko { 50753681407SJiri Pirko return __tcf_chain_get(block, chain_index, true, true); 5081f3ed383SJiri Pirko } 5091f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_get_by_act); 5101f3ed383SJiri Pirko 511a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 512a5654820SVlad Buslov void *tmplt_priv); 513a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 514a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 515a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 516a5654820SVlad Buslov u32 seq, u16 flags, bool unicast); 5179f407f17SJiri Pirko 51891052fa1SVlad Buslov static void __tcf_chain_put(struct tcf_chain *chain, bool by_act, 51991052fa1SVlad Buslov bool explicitly_created) 5205bc17018SJiri Pirko { 521c266f64dSVlad Buslov struct tcf_block *block = chain->block; 522a5654820SVlad Buslov const struct tcf_proto_ops *tmplt_ops; 523b62989fcSVlad Buslov bool free_block = false; 524c266f64dSVlad Buslov unsigned int refcnt; 525a5654820SVlad Buslov void *tmplt_priv; 526c266f64dSVlad Buslov 527c266f64dSVlad Buslov mutex_lock(&block->lock); 52891052fa1SVlad Buslov if (explicitly_created) { 52991052fa1SVlad Buslov if (!chain->explicitly_created) { 53091052fa1SVlad Buslov mutex_unlock(&block->lock); 53191052fa1SVlad Buslov return; 53291052fa1SVlad Buslov } 53391052fa1SVlad Buslov chain->explicitly_created = false; 53491052fa1SVlad Buslov } 53591052fa1SVlad Buslov 53653681407SJiri Pirko if (by_act) 53753681407SJiri Pirko chain->action_refcnt--; 538c266f64dSVlad Buslov 539c266f64dSVlad Buslov /* tc_chain_notify_delete can't be called while holding block lock. 540c266f64dSVlad Buslov * However, when block is unlocked chain can be changed concurrently, so 541c266f64dSVlad Buslov * save these to temporary variables. 542c266f64dSVlad Buslov */ 543c266f64dSVlad Buslov refcnt = --chain->refcnt; 544a5654820SVlad Buslov tmplt_ops = chain->tmplt_ops; 545a5654820SVlad Buslov tmplt_priv = chain->tmplt_priv; 54653681407SJiri Pirko 54753681407SJiri Pirko /* The last dropped non-action reference will trigger notification. */ 548b62989fcSVlad Buslov if (refcnt - chain->action_refcnt == 0 && !by_act) { 549b62989fcSVlad Buslov tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index, 550a5654820SVlad Buslov block, NULL, 0, 0, false); 551726d0612SVlad Buslov /* Last reference to chain, no need to lock. */ 552726d0612SVlad Buslov chain->flushing = false; 553726d0612SVlad Buslov } 55453681407SJiri Pirko 555b62989fcSVlad Buslov if (refcnt == 0) 556b62989fcSVlad Buslov free_block = tcf_chain_detach(chain); 557b62989fcSVlad Buslov mutex_unlock(&block->lock); 558b62989fcSVlad Buslov 559c266f64dSVlad Buslov if (refcnt == 0) { 560a5654820SVlad Buslov tc_chain_tmplt_del(tmplt_ops, tmplt_priv); 561c266f64dSVlad Buslov tcf_chain_destroy(chain, free_block); 5625bc17018SJiri Pirko } 56332a4f5ecSJiri Pirko } 56453681407SJiri Pirko 565290b1c8bSJiri Pirko static void tcf_chain_put(struct tcf_chain *chain) 56653681407SJiri Pirko { 56791052fa1SVlad Buslov __tcf_chain_put(chain, false, false); 56853681407SJiri Pirko } 5695bc17018SJiri Pirko 5701f3ed383SJiri Pirko void tcf_chain_put_by_act(struct tcf_chain *chain) 5711f3ed383SJiri Pirko { 57291052fa1SVlad Buslov __tcf_chain_put(chain, true, false); 5731f3ed383SJiri Pirko } 5741f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_put_by_act); 5751f3ed383SJiri Pirko 57632a4f5ecSJiri Pirko static void tcf_chain_put_explicitly_created(struct tcf_chain *chain) 57732a4f5ecSJiri Pirko { 57891052fa1SVlad Buslov __tcf_chain_put(chain, false, true); 57932a4f5ecSJiri Pirko } 58032a4f5ecSJiri Pirko 58112db03b6SVlad Buslov static void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held) 582290b1c8bSJiri Pirko { 5834dbfa766SVlad Buslov struct tcf_proto *tp, *tp_next; 584290b1c8bSJiri Pirko 585ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 586ed76f5edSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 58759eb87cbSJohn Hurley while (tp) { 58859eb87cbSJohn Hurley tp_next = rcu_dereference_protected(tp->next, 1); 58959eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 59059eb87cbSJohn Hurley tp = tp_next; 59159eb87cbSJohn Hurley } 59259eb87cbSJohn Hurley tp = tcf_chain_dereference(chain->filter_chain, chain); 5934dbfa766SVlad Buslov RCU_INIT_POINTER(chain->filter_chain, NULL); 594290b1c8bSJiri Pirko tcf_chain0_head_change(chain, NULL); 595726d0612SVlad Buslov chain->flushing = true; 596ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 597ed76f5edSVlad Buslov 598290b1c8bSJiri Pirko while (tp) { 5994dbfa766SVlad Buslov tp_next = rcu_dereference_protected(tp->next, 1); 60012db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 6014dbfa766SVlad Buslov tp = tp_next; 602290b1c8bSJiri Pirko } 603290b1c8bSJiri Pirko } 604290b1c8bSJiri Pirko 6054e481908Swenxu static int tcf_block_setup(struct tcf_block *block, 6064e481908Swenxu struct flow_block_offload *bo); 6074e481908Swenxu 60825a443f7SJohn Hurley static void tc_indr_block_cmd(struct net_device *dev, struct tcf_block *block, 60925a443f7SJohn Hurley flow_indr_block_bind_cb_t *cb, void *cb_priv, 61025a443f7SJohn Hurley enum flow_block_command command, bool ingress) 6114e481908Swenxu { 6124e481908Swenxu struct flow_block_offload bo = { 6134e481908Swenxu .command = command, 61425a443f7SJohn Hurley .binder_type = ingress ? 61525a443f7SJohn Hurley FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS : 61625a443f7SJohn Hurley FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS, 6174e481908Swenxu .net = dev_net(dev), 6184e481908Swenxu .block_shared = tcf_block_non_null_shared(block), 6194e481908Swenxu }; 6204e481908Swenxu INIT_LIST_HEAD(&bo.cb_list); 6214e481908Swenxu 6224e481908Swenxu if (!block) 6234e481908Swenxu return; 6244e481908Swenxu 6254e481908Swenxu bo.block = &block->flow_block; 6264e481908Swenxu 6274f8116c8SVlad Buslov down_write(&block->cb_lock); 6284e481908Swenxu cb(dev, cb_priv, TC_SETUP_BLOCK, &bo); 6294e481908Swenxu 6304e481908Swenxu tcf_block_setup(block, &bo); 6314f8116c8SVlad Buslov up_write(&block->cb_lock); 6324e481908Swenxu } 6334e481908Swenxu 63425a443f7SJohn Hurley static struct tcf_block *tc_dev_block(struct net_device *dev, bool ingress) 6357f76fa36SJohn Hurley { 6367f76fa36SJohn Hurley const struct Qdisc_class_ops *cops; 63725a443f7SJohn Hurley const struct Qdisc_ops *ops; 6387f76fa36SJohn Hurley struct Qdisc *qdisc; 6397f76fa36SJohn Hurley 6407f76fa36SJohn Hurley if (!dev_ingress_queue(dev)) 6417f76fa36SJohn Hurley return NULL; 6427f76fa36SJohn Hurley 6437f76fa36SJohn Hurley qdisc = dev_ingress_queue(dev)->qdisc_sleeping; 6447f76fa36SJohn Hurley if (!qdisc) 6457f76fa36SJohn Hurley return NULL; 6467f76fa36SJohn Hurley 64725a443f7SJohn Hurley ops = qdisc->ops; 64825a443f7SJohn Hurley if (!ops) 64925a443f7SJohn Hurley return NULL; 65025a443f7SJohn Hurley 65125a443f7SJohn Hurley if (!ingress && !strcmp("ingress", ops->id)) 65225a443f7SJohn Hurley return NULL; 65325a443f7SJohn Hurley 65425a443f7SJohn Hurley cops = ops->cl_ops; 6557f76fa36SJohn Hurley if (!cops) 6567f76fa36SJohn Hurley return NULL; 6577f76fa36SJohn Hurley 6587f76fa36SJohn Hurley if (!cops->tcf_block) 6597f76fa36SJohn Hurley return NULL; 6607f76fa36SJohn Hurley 66125a443f7SJohn Hurley return cops->tcf_block(qdisc, 66225a443f7SJohn Hurley ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS, 66325a443f7SJohn Hurley NULL); 6647f76fa36SJohn Hurley } 6657f76fa36SJohn Hurley 66625a443f7SJohn Hurley static void tc_indr_block_get_and_cmd(struct net_device *dev, 6674e481908Swenxu flow_indr_block_bind_cb_t *cb, 668242453c2Swenxu void *cb_priv, 6699c0e189eSPablo Neira Ayuso enum flow_block_command command) 6707f76fa36SJohn Hurley { 67125a443f7SJohn Hurley struct tcf_block *block; 6727f76fa36SJohn Hurley 67325a443f7SJohn Hurley block = tc_dev_block(dev, true); 67425a443f7SJohn Hurley tc_indr_block_cmd(dev, block, cb, cb_priv, command, true); 67525a443f7SJohn Hurley 67625a443f7SJohn Hurley block = tc_dev_block(dev, false); 67725a443f7SJohn Hurley tc_indr_block_cmd(dev, block, cb, cb_priv, command, false); 6787f76fa36SJohn Hurley } 6797f76fa36SJohn Hurley 6804e481908Swenxu static void tc_indr_block_call(struct tcf_block *block, 6814e481908Swenxu struct net_device *dev, 6827f76fa36SJohn Hurley struct tcf_block_ext_info *ei, 6839c0e189eSPablo Neira Ayuso enum flow_block_command command, 6847f76fa36SJohn Hurley struct netlink_ext_ack *extack) 6857f76fa36SJohn Hurley { 686955bcb6eSPablo Neira Ayuso struct flow_block_offload bo = { 6877f76fa36SJohn Hurley .command = command, 6887f76fa36SJohn Hurley .binder_type = ei->binder_type, 689da3eeb90SPablo Neira Ayuso .net = dev_net(dev), 69014bfb13fSPablo Neira Ayuso .block = &block->flow_block, 691955bcb6eSPablo Neira Ayuso .block_shared = tcf_block_shared(block), 6927f76fa36SJohn Hurley .extack = extack, 6937f76fa36SJohn Hurley }; 69459094b1eSPablo Neira Ayuso INIT_LIST_HEAD(&bo.cb_list); 6957f76fa36SJohn Hurley 6961150ab0fSwenxu flow_indr_block_call(dev, &bo, command); 69759094b1eSPablo Neira Ayuso tcf_block_setup(block, &bo); 6987f76fa36SJohn Hurley } 6997f76fa36SJohn Hurley 700caa72601SJiri Pirko static bool tcf_block_offload_in_use(struct tcf_block *block) 701caa72601SJiri Pirko { 70297394befSVlad Buslov return atomic_read(&block->offloadcnt); 703caa72601SJiri Pirko } 704caa72601SJiri Pirko 705caa72601SJiri Pirko static int tcf_block_offload_cmd(struct tcf_block *block, 706caa72601SJiri Pirko struct net_device *dev, 7078c4083b3SJiri Pirko struct tcf_block_ext_info *ei, 7089c0e189eSPablo Neira Ayuso enum flow_block_command command, 70960513bd8SJohn Hurley struct netlink_ext_ack *extack) 7108c4083b3SJiri Pirko { 711955bcb6eSPablo Neira Ayuso struct flow_block_offload bo = {}; 71259094b1eSPablo Neira Ayuso int err; 7138c4083b3SJiri Pirko 714da3eeb90SPablo Neira Ayuso bo.net = dev_net(dev); 7158c4083b3SJiri Pirko bo.command = command; 7168c4083b3SJiri Pirko bo.binder_type = ei->binder_type; 71714bfb13fSPablo Neira Ayuso bo.block = &block->flow_block; 718955bcb6eSPablo Neira Ayuso bo.block_shared = tcf_block_shared(block); 71960513bd8SJohn Hurley bo.extack = extack; 72059094b1eSPablo Neira Ayuso INIT_LIST_HEAD(&bo.cb_list); 72159094b1eSPablo Neira Ayuso 72259094b1eSPablo Neira Ayuso err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 72359094b1eSPablo Neira Ayuso if (err < 0) 72459094b1eSPablo Neira Ayuso return err; 72559094b1eSPablo Neira Ayuso 72659094b1eSPablo Neira Ayuso return tcf_block_setup(block, &bo); 7278c4083b3SJiri Pirko } 7288c4083b3SJiri Pirko 729caa72601SJiri Pirko static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 73060513bd8SJohn Hurley struct tcf_block_ext_info *ei, 73160513bd8SJohn Hurley struct netlink_ext_ack *extack) 7328c4083b3SJiri Pirko { 733caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 734caa72601SJiri Pirko int err; 735caa72601SJiri Pirko 7364f8116c8SVlad Buslov down_write(&block->cb_lock); 737caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 738caa72601SJiri Pirko goto no_offload_dev_inc; 739caa72601SJiri Pirko 740caa72601SJiri Pirko /* If tc offload feature is disabled and the block we try to bind 741caa72601SJiri Pirko * to already has some offloaded filters, forbid to bind. 742caa72601SJiri Pirko */ 74360513bd8SJohn Hurley if (!tc_can_offload(dev) && tcf_block_offload_in_use(block)) { 74460513bd8SJohn Hurley NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled"); 7454f8116c8SVlad Buslov err = -EOPNOTSUPP; 7464f8116c8SVlad Buslov goto err_unlock; 74760513bd8SJohn Hurley } 748caa72601SJiri Pirko 7499c0e189eSPablo Neira Ayuso err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_BIND, extack); 750caa72601SJiri Pirko if (err == -EOPNOTSUPP) 751caa72601SJiri Pirko goto no_offload_dev_inc; 7527f76fa36SJohn Hurley if (err) 7534f8116c8SVlad Buslov goto err_unlock; 754caa72601SJiri Pirko 7559c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack); 7564f8116c8SVlad Buslov up_write(&block->cb_lock); 7577f76fa36SJohn Hurley return 0; 7587f76fa36SJohn Hurley 759caa72601SJiri Pirko no_offload_dev_inc: 7604f8116c8SVlad Buslov if (tcf_block_offload_in_use(block)) { 7614f8116c8SVlad Buslov err = -EOPNOTSUPP; 7624f8116c8SVlad Buslov goto err_unlock; 7634f8116c8SVlad Buslov } 7644f8116c8SVlad Buslov err = 0; 765caa72601SJiri Pirko block->nooffloaddevcnt++; 7669c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack); 7674f8116c8SVlad Buslov err_unlock: 7684f8116c8SVlad Buslov up_write(&block->cb_lock); 7694f8116c8SVlad Buslov return err; 7708c4083b3SJiri Pirko } 7718c4083b3SJiri Pirko 7728c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 7738c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 7748c4083b3SJiri Pirko { 775caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 776caa72601SJiri Pirko int err; 777caa72601SJiri Pirko 7784f8116c8SVlad Buslov down_write(&block->cb_lock); 7799c0e189eSPablo Neira Ayuso tc_indr_block_call(block, dev, ei, FLOW_BLOCK_UNBIND, NULL); 7807f76fa36SJohn Hurley 781caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 782caa72601SJiri Pirko goto no_offload_dev_dec; 7839c0e189eSPablo Neira Ayuso err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_UNBIND, NULL); 784caa72601SJiri Pirko if (err == -EOPNOTSUPP) 785caa72601SJiri Pirko goto no_offload_dev_dec; 7864f8116c8SVlad Buslov up_write(&block->cb_lock); 787caa72601SJiri Pirko return; 788caa72601SJiri Pirko 789caa72601SJiri Pirko no_offload_dev_dec: 790caa72601SJiri Pirko WARN_ON(block->nooffloaddevcnt-- == 0); 7914f8116c8SVlad Buslov up_write(&block->cb_lock); 7928c4083b3SJiri Pirko } 7938c4083b3SJiri Pirko 794a9b19443SJiri Pirko static int 795f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_add(struct tcf_block *block, 796a9b19443SJiri Pirko struct tcf_block_ext_info *ei, 797a9b19443SJiri Pirko struct netlink_ext_ack *extack) 798a9b19443SJiri Pirko { 799a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 800165f0135SVlad Buslov struct tcf_chain *chain0; 801a9b19443SJiri Pirko 802a9b19443SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 803a9b19443SJiri Pirko if (!item) { 804a9b19443SJiri Pirko NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed"); 805a9b19443SJiri Pirko return -ENOMEM; 806a9b19443SJiri Pirko } 807a9b19443SJiri Pirko item->chain_head_change = ei->chain_head_change; 808a9b19443SJiri Pirko item->chain_head_change_priv = ei->chain_head_change_priv; 809165f0135SVlad Buslov 810165f0135SVlad Buslov mutex_lock(&block->lock); 811165f0135SVlad Buslov chain0 = block->chain0.chain; 812ed76f5edSVlad Buslov if (chain0) 813ed76f5edSVlad Buslov tcf_chain_hold(chain0); 814ed76f5edSVlad Buslov else 815f71e0ca4SJiri Pirko list_add(&item->list, &block->chain0.filter_chain_list); 816165f0135SVlad Buslov mutex_unlock(&block->lock); 817165f0135SVlad Buslov 818ed76f5edSVlad Buslov if (chain0) { 819ed76f5edSVlad Buslov struct tcf_proto *tp_head; 820ed76f5edSVlad Buslov 821ed76f5edSVlad Buslov mutex_lock(&chain0->filter_chain_lock); 822ed76f5edSVlad Buslov 823ed76f5edSVlad Buslov tp_head = tcf_chain_dereference(chain0->filter_chain, chain0); 824ed76f5edSVlad Buslov if (tp_head) 825ed76f5edSVlad Buslov tcf_chain_head_change_item(item, tp_head); 826ed76f5edSVlad Buslov 827ed76f5edSVlad Buslov mutex_lock(&block->lock); 828ed76f5edSVlad Buslov list_add(&item->list, &block->chain0.filter_chain_list); 829ed76f5edSVlad Buslov mutex_unlock(&block->lock); 830ed76f5edSVlad Buslov 831ed76f5edSVlad Buslov mutex_unlock(&chain0->filter_chain_lock); 832ed76f5edSVlad Buslov tcf_chain_put(chain0); 833ed76f5edSVlad Buslov } 834ed76f5edSVlad Buslov 835a9b19443SJiri Pirko return 0; 836a9b19443SJiri Pirko } 837a9b19443SJiri Pirko 838a9b19443SJiri Pirko static void 839f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(struct tcf_block *block, 840a9b19443SJiri Pirko struct tcf_block_ext_info *ei) 841a9b19443SJiri Pirko { 842a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 843a9b19443SJiri Pirko 844165f0135SVlad Buslov mutex_lock(&block->lock); 845f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) { 846a9b19443SJiri Pirko if ((!ei->chain_head_change && !ei->chain_head_change_priv) || 847a9b19443SJiri Pirko (item->chain_head_change == ei->chain_head_change && 848a9b19443SJiri Pirko item->chain_head_change_priv == ei->chain_head_change_priv)) { 849165f0135SVlad Buslov if (block->chain0.chain) 850a9b19443SJiri Pirko tcf_chain_head_change_item(item, NULL); 851a9b19443SJiri Pirko list_del(&item->list); 852165f0135SVlad Buslov mutex_unlock(&block->lock); 853165f0135SVlad Buslov 854a9b19443SJiri Pirko kfree(item); 855a9b19443SJiri Pirko return; 856a9b19443SJiri Pirko } 857a9b19443SJiri Pirko } 858165f0135SVlad Buslov mutex_unlock(&block->lock); 859a9b19443SJiri Pirko WARN_ON(1); 860a9b19443SJiri Pirko } 861a9b19443SJiri Pirko 86248617387SJiri Pirko struct tcf_net { 863ab281629SVlad Buslov spinlock_t idr_lock; /* Protects idr */ 86448617387SJiri Pirko struct idr idr; 86548617387SJiri Pirko }; 86648617387SJiri Pirko 86748617387SJiri Pirko static unsigned int tcf_net_id; 86848617387SJiri Pirko 86948617387SJiri Pirko static int tcf_block_insert(struct tcf_block *block, struct net *net, 870bb047dddSJiri Pirko struct netlink_ext_ack *extack) 871a9b19443SJiri Pirko { 87248617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 873ab281629SVlad Buslov int err; 87448617387SJiri Pirko 875ab281629SVlad Buslov idr_preload(GFP_KERNEL); 876ab281629SVlad Buslov spin_lock(&tn->idr_lock); 877ab281629SVlad Buslov err = idr_alloc_u32(&tn->idr, block, &block->index, block->index, 878ab281629SVlad Buslov GFP_NOWAIT); 879ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 880ab281629SVlad Buslov idr_preload_end(); 881ab281629SVlad Buslov 882ab281629SVlad Buslov return err; 883a9b19443SJiri Pirko } 884a9b19443SJiri Pirko 88548617387SJiri Pirko static void tcf_block_remove(struct tcf_block *block, struct net *net) 88648617387SJiri Pirko { 88748617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 88848617387SJiri Pirko 889ab281629SVlad Buslov spin_lock(&tn->idr_lock); 8909c160941SMatthew Wilcox idr_remove(&tn->idr, block->index); 891ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 89248617387SJiri Pirko } 89348617387SJiri Pirko 89448617387SJiri Pirko static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, 895bb047dddSJiri Pirko u32 block_index, 8968d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 8976529eabaSJiri Pirko { 89848617387SJiri Pirko struct tcf_block *block; 8996529eabaSJiri Pirko 90048617387SJiri Pirko block = kzalloc(sizeof(*block), GFP_KERNEL); 9018d1a77f9SAlexander Aring if (!block) { 9028d1a77f9SAlexander Aring NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); 90348617387SJiri Pirko return ERR_PTR(-ENOMEM); 9048d1a77f9SAlexander Aring } 905c266f64dSVlad Buslov mutex_init(&block->lock); 90659eb87cbSJohn Hurley mutex_init(&block->proto_destroy_lock); 9074f8116c8SVlad Buslov init_rwsem(&block->cb_lock); 90814bfb13fSPablo Neira Ayuso flow_block_init(&block->flow_block); 9095bc17018SJiri Pirko INIT_LIST_HEAD(&block->chain_list); 910f36fe1c4SJiri Pirko INIT_LIST_HEAD(&block->owner_list); 911f71e0ca4SJiri Pirko INIT_LIST_HEAD(&block->chain0.filter_chain_list); 912acb67442SJiri Pirko 913cfebd7e2SVlad Buslov refcount_set(&block->refcnt, 1); 91448617387SJiri Pirko block->net = net; 915bb047dddSJiri Pirko block->index = block_index; 916bb047dddSJiri Pirko 917bb047dddSJiri Pirko /* Don't store q pointer for blocks which are shared */ 918bb047dddSJiri Pirko if (!tcf_block_shared(block)) 91948617387SJiri Pirko block->q = q; 92048617387SJiri Pirko return block; 92148617387SJiri Pirko } 92248617387SJiri Pirko 92348617387SJiri Pirko static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) 92448617387SJiri Pirko { 92548617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 92648617387SJiri Pirko 927322d884bSMatthew Wilcox return idr_find(&tn->idr, block_index); 92848617387SJiri Pirko } 92948617387SJiri Pirko 9300607e439SVlad Buslov static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index) 9310607e439SVlad Buslov { 9320607e439SVlad Buslov struct tcf_block *block; 9330607e439SVlad Buslov 9340607e439SVlad Buslov rcu_read_lock(); 9350607e439SVlad Buslov block = tcf_block_lookup(net, block_index); 9360607e439SVlad Buslov if (block && !refcount_inc_not_zero(&block->refcnt)) 9370607e439SVlad Buslov block = NULL; 9380607e439SVlad Buslov rcu_read_unlock(); 9390607e439SVlad Buslov 9400607e439SVlad Buslov return block; 9410607e439SVlad Buslov } 9420607e439SVlad Buslov 943bbf73830SVlad Buslov static struct tcf_chain * 944bbf73830SVlad Buslov __tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 945bbf73830SVlad Buslov { 946bbf73830SVlad Buslov mutex_lock(&block->lock); 947bbf73830SVlad Buslov if (chain) 948bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 949bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 950bbf73830SVlad Buslov else 951bbf73830SVlad Buslov chain = list_first_entry_or_null(&block->chain_list, 952bbf73830SVlad Buslov struct tcf_chain, list); 953bbf73830SVlad Buslov 954bbf73830SVlad Buslov /* skip all action-only chains */ 955bbf73830SVlad Buslov while (chain && tcf_chain_held_by_acts_only(chain)) 956bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 957bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 958bbf73830SVlad Buslov 959bbf73830SVlad Buslov if (chain) 960bbf73830SVlad Buslov tcf_chain_hold(chain); 961bbf73830SVlad Buslov mutex_unlock(&block->lock); 962bbf73830SVlad Buslov 963bbf73830SVlad Buslov return chain; 964bbf73830SVlad Buslov } 965bbf73830SVlad Buslov 966bbf73830SVlad Buslov /* Function to be used by all clients that want to iterate over all chains on 967bbf73830SVlad Buslov * block. It properly obtains block->lock and takes reference to chain before 968bbf73830SVlad Buslov * returning it. Users of this function must be tolerant to concurrent chain 969bbf73830SVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 970bbf73830SVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 971bbf73830SVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 972bbf73830SVlad Buslov * data and sent to user-space. 973bbf73830SVlad Buslov */ 974bbf73830SVlad Buslov 975bbf73830SVlad Buslov struct tcf_chain * 976bbf73830SVlad Buslov tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 977bbf73830SVlad Buslov { 978bbf73830SVlad Buslov struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain); 979bbf73830SVlad Buslov 980bbf73830SVlad Buslov if (chain) 981bbf73830SVlad Buslov tcf_chain_put(chain); 982bbf73830SVlad Buslov 983bbf73830SVlad Buslov return chain_next; 984bbf73830SVlad Buslov } 985bbf73830SVlad Buslov EXPORT_SYMBOL(tcf_get_next_chain); 986bbf73830SVlad Buslov 987fe2923afSVlad Buslov static struct tcf_proto * 988fe2923afSVlad Buslov __tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 989fe2923afSVlad Buslov { 9908b64678eSVlad Buslov u32 prio = 0; 9918b64678eSVlad Buslov 992fe2923afSVlad Buslov ASSERT_RTNL(); 993fe2923afSVlad Buslov mutex_lock(&chain->filter_chain_lock); 994fe2923afSVlad Buslov 9958b64678eSVlad Buslov if (!tp) { 996fe2923afSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 9978b64678eSVlad Buslov } else if (tcf_proto_is_deleting(tp)) { 9988b64678eSVlad Buslov /* 'deleting' flag is set and chain->filter_chain_lock was 9998b64678eSVlad Buslov * unlocked, which means next pointer could be invalid. Restart 10008b64678eSVlad Buslov * search. 10018b64678eSVlad Buslov */ 10028b64678eSVlad Buslov prio = tp->prio + 1; 10038b64678eSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 10048b64678eSVlad Buslov 10058b64678eSVlad Buslov for (; tp; tp = tcf_chain_dereference(tp->next, chain)) 10068b64678eSVlad Buslov if (!tp->deleting && tp->prio >= prio) 10078b64678eSVlad Buslov break; 10088b64678eSVlad Buslov } else { 1009fe2923afSVlad Buslov tp = tcf_chain_dereference(tp->next, chain); 10108b64678eSVlad Buslov } 1011fe2923afSVlad Buslov 1012fe2923afSVlad Buslov if (tp) 1013fe2923afSVlad Buslov tcf_proto_get(tp); 1014fe2923afSVlad Buslov 1015fe2923afSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 1016fe2923afSVlad Buslov 1017fe2923afSVlad Buslov return tp; 1018fe2923afSVlad Buslov } 1019fe2923afSVlad Buslov 1020fe2923afSVlad Buslov /* Function to be used by all clients that want to iterate over all tp's on 1021fe2923afSVlad Buslov * chain. Users of this function must be tolerant to concurrent tp 1022fe2923afSVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 1023fe2923afSVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 1024fe2923afSVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 1025fe2923afSVlad Buslov * data and sent to user-space. 1026fe2923afSVlad Buslov */ 1027fe2923afSVlad Buslov 1028fe2923afSVlad Buslov struct tcf_proto * 102912db03b6SVlad Buslov tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp, 103012db03b6SVlad Buslov bool rtnl_held) 1031fe2923afSVlad Buslov { 1032fe2923afSVlad Buslov struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp); 1033fe2923afSVlad Buslov 1034fe2923afSVlad Buslov if (tp) 103512db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 1036fe2923afSVlad Buslov 1037fe2923afSVlad Buslov return tp_next; 1038fe2923afSVlad Buslov } 1039fe2923afSVlad Buslov EXPORT_SYMBOL(tcf_get_next_proto); 1040fe2923afSVlad Buslov 104112db03b6SVlad Buslov static void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held) 1042f0023436SVlad Buslov { 1043f0023436SVlad Buslov struct tcf_chain *chain; 1044f0023436SVlad Buslov 1045bbf73830SVlad Buslov /* Last reference to block. At this point chains cannot be added or 1046bbf73830SVlad Buslov * removed concurrently. 1047f0023436SVlad Buslov */ 1048bbf73830SVlad Buslov for (chain = tcf_get_next_chain(block, NULL); 1049bbf73830SVlad Buslov chain; 1050bbf73830SVlad Buslov chain = tcf_get_next_chain(block, chain)) { 1051f0023436SVlad Buslov tcf_chain_put_explicitly_created(chain); 105212db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 1053f0023436SVlad Buslov } 1054f0023436SVlad Buslov } 1055f0023436SVlad Buslov 105618d3eefbSVlad Buslov /* Lookup Qdisc and increments its reference counter. 105718d3eefbSVlad Buslov * Set parent, if necessary. 105818d3eefbSVlad Buslov */ 105918d3eefbSVlad Buslov 106018d3eefbSVlad Buslov static int __tcf_qdisc_find(struct net *net, struct Qdisc **q, 106118d3eefbSVlad Buslov u32 *parent, int ifindex, bool rtnl_held, 106218d3eefbSVlad Buslov struct netlink_ext_ack *extack) 106318d3eefbSVlad Buslov { 106418d3eefbSVlad Buslov const struct Qdisc_class_ops *cops; 106518d3eefbSVlad Buslov struct net_device *dev; 106618d3eefbSVlad Buslov int err = 0; 106718d3eefbSVlad Buslov 106818d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 106918d3eefbSVlad Buslov return 0; 107018d3eefbSVlad Buslov 107118d3eefbSVlad Buslov rcu_read_lock(); 107218d3eefbSVlad Buslov 107318d3eefbSVlad Buslov /* Find link */ 107418d3eefbSVlad Buslov dev = dev_get_by_index_rcu(net, ifindex); 107518d3eefbSVlad Buslov if (!dev) { 107618d3eefbSVlad Buslov rcu_read_unlock(); 107718d3eefbSVlad Buslov return -ENODEV; 107818d3eefbSVlad Buslov } 107918d3eefbSVlad Buslov 108018d3eefbSVlad Buslov /* Find qdisc */ 108118d3eefbSVlad Buslov if (!*parent) { 108218d3eefbSVlad Buslov *q = dev->qdisc; 108318d3eefbSVlad Buslov *parent = (*q)->handle; 108418d3eefbSVlad Buslov } else { 108518d3eefbSVlad Buslov *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent)); 108618d3eefbSVlad Buslov if (!*q) { 108718d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 108818d3eefbSVlad Buslov err = -EINVAL; 108918d3eefbSVlad Buslov goto errout_rcu; 109018d3eefbSVlad Buslov } 109118d3eefbSVlad Buslov } 109218d3eefbSVlad Buslov 109318d3eefbSVlad Buslov *q = qdisc_refcount_inc_nz(*q); 109418d3eefbSVlad Buslov if (!*q) { 109518d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 109618d3eefbSVlad Buslov err = -EINVAL; 109718d3eefbSVlad Buslov goto errout_rcu; 109818d3eefbSVlad Buslov } 109918d3eefbSVlad Buslov 110018d3eefbSVlad Buslov /* Is it classful? */ 110118d3eefbSVlad Buslov cops = (*q)->ops->cl_ops; 110218d3eefbSVlad Buslov if (!cops) { 110318d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Qdisc not classful"); 110418d3eefbSVlad Buslov err = -EINVAL; 110518d3eefbSVlad Buslov goto errout_qdisc; 110618d3eefbSVlad Buslov } 110718d3eefbSVlad Buslov 110818d3eefbSVlad Buslov if (!cops->tcf_block) { 110918d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Class doesn't support blocks"); 111018d3eefbSVlad Buslov err = -EOPNOTSUPP; 111118d3eefbSVlad Buslov goto errout_qdisc; 111218d3eefbSVlad Buslov } 111318d3eefbSVlad Buslov 111418d3eefbSVlad Buslov errout_rcu: 111518d3eefbSVlad Buslov /* At this point we know that qdisc is not noop_qdisc, 111618d3eefbSVlad Buslov * which means that qdisc holds a reference to net_device 111718d3eefbSVlad Buslov * and we hold a reference to qdisc, so it is safe to release 111818d3eefbSVlad Buslov * rcu read lock. 111918d3eefbSVlad Buslov */ 112018d3eefbSVlad Buslov rcu_read_unlock(); 112118d3eefbSVlad Buslov return err; 112218d3eefbSVlad Buslov 112318d3eefbSVlad Buslov errout_qdisc: 112418d3eefbSVlad Buslov rcu_read_unlock(); 112518d3eefbSVlad Buslov 112618d3eefbSVlad Buslov if (rtnl_held) 112718d3eefbSVlad Buslov qdisc_put(*q); 112818d3eefbSVlad Buslov else 112918d3eefbSVlad Buslov qdisc_put_unlocked(*q); 113018d3eefbSVlad Buslov *q = NULL; 113118d3eefbSVlad Buslov 113218d3eefbSVlad Buslov return err; 113318d3eefbSVlad Buslov } 113418d3eefbSVlad Buslov 113518d3eefbSVlad Buslov static int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl, 113618d3eefbSVlad Buslov int ifindex, struct netlink_ext_ack *extack) 113718d3eefbSVlad Buslov { 113818d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 113918d3eefbSVlad Buslov return 0; 114018d3eefbSVlad Buslov 114118d3eefbSVlad Buslov /* Do we search for filter, attached to class? */ 114218d3eefbSVlad Buslov if (TC_H_MIN(parent)) { 114318d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 114418d3eefbSVlad Buslov 114518d3eefbSVlad Buslov *cl = cops->find(q, parent); 114618d3eefbSVlad Buslov if (*cl == 0) { 114718d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Specified class doesn't exist"); 114818d3eefbSVlad Buslov return -ENOENT; 114918d3eefbSVlad Buslov } 115018d3eefbSVlad Buslov } 115118d3eefbSVlad Buslov 115218d3eefbSVlad Buslov return 0; 115318d3eefbSVlad Buslov } 115418d3eefbSVlad Buslov 115518d3eefbSVlad Buslov static struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q, 115618d3eefbSVlad Buslov unsigned long cl, int ifindex, 115718d3eefbSVlad Buslov u32 block_index, 115818d3eefbSVlad Buslov struct netlink_ext_ack *extack) 115918d3eefbSVlad Buslov { 116018d3eefbSVlad Buslov struct tcf_block *block; 116118d3eefbSVlad Buslov 116218d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 116318d3eefbSVlad Buslov block = tcf_block_refcnt_get(net, block_index); 116418d3eefbSVlad Buslov if (!block) { 116518d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Block of given index was not found"); 116618d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 116718d3eefbSVlad Buslov } 116818d3eefbSVlad Buslov } else { 116918d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 117018d3eefbSVlad Buslov 117118d3eefbSVlad Buslov block = cops->tcf_block(q, cl, extack); 117218d3eefbSVlad Buslov if (!block) 117318d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 117418d3eefbSVlad Buslov 117518d3eefbSVlad Buslov if (tcf_block_shared(block)) { 117618d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters"); 117718d3eefbSVlad Buslov return ERR_PTR(-EOPNOTSUPP); 117818d3eefbSVlad Buslov } 117918d3eefbSVlad Buslov 118018d3eefbSVlad Buslov /* Always take reference to block in order to support execution 118118d3eefbSVlad Buslov * of rules update path of cls API without rtnl lock. Caller 118218d3eefbSVlad Buslov * must release block when it is finished using it. 'if' block 118318d3eefbSVlad Buslov * of this conditional obtain reference to block by calling 118418d3eefbSVlad Buslov * tcf_block_refcnt_get(). 118518d3eefbSVlad Buslov */ 118618d3eefbSVlad Buslov refcount_inc(&block->refcnt); 118718d3eefbSVlad Buslov } 118818d3eefbSVlad Buslov 118918d3eefbSVlad Buslov return block; 119018d3eefbSVlad Buslov } 119118d3eefbSVlad Buslov 11920607e439SVlad Buslov static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q, 119312db03b6SVlad Buslov struct tcf_block_ext_info *ei, bool rtnl_held) 11940607e439SVlad Buslov { 1195c266f64dSVlad Buslov if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) { 11960607e439SVlad Buslov /* Flushing/putting all chains will cause the block to be 11970607e439SVlad Buslov * deallocated when last chain is freed. However, if chain_list 11980607e439SVlad Buslov * is empty, block has to be manually deallocated. After block 11990607e439SVlad Buslov * reference counter reached 0, it is no longer possible to 12000607e439SVlad Buslov * increment it or add new chains to block. 12010607e439SVlad Buslov */ 12020607e439SVlad Buslov bool free_block = list_empty(&block->chain_list); 12030607e439SVlad Buslov 1204c266f64dSVlad Buslov mutex_unlock(&block->lock); 12050607e439SVlad Buslov if (tcf_block_shared(block)) 12060607e439SVlad Buslov tcf_block_remove(block, block->net); 12070607e439SVlad Buslov 12080607e439SVlad Buslov if (q) 12090607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 12100607e439SVlad Buslov 12110607e439SVlad Buslov if (free_block) 1212c266f64dSVlad Buslov tcf_block_destroy(block); 12130607e439SVlad Buslov else 121412db03b6SVlad Buslov tcf_block_flush_all_chains(block, rtnl_held); 12150607e439SVlad Buslov } else if (q) { 12160607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 12170607e439SVlad Buslov } 12180607e439SVlad Buslov } 12190607e439SVlad Buslov 122012db03b6SVlad Buslov static void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held) 12210607e439SVlad Buslov { 122212db03b6SVlad Buslov __tcf_block_put(block, NULL, NULL, rtnl_held); 12230607e439SVlad Buslov } 12240607e439SVlad Buslov 1225c431f89bSVlad Buslov /* Find tcf block. 1226c431f89bSVlad Buslov * Set q, parent, cl when appropriate. 1227c431f89bSVlad Buslov */ 1228c431f89bSVlad Buslov 1229c431f89bSVlad Buslov static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q, 1230c431f89bSVlad Buslov u32 *parent, unsigned long *cl, 1231c431f89bSVlad Buslov int ifindex, u32 block_index, 1232c431f89bSVlad Buslov struct netlink_ext_ack *extack) 1233c431f89bSVlad Buslov { 1234c431f89bSVlad Buslov struct tcf_block *block; 1235e368fdb6SVlad Buslov int err = 0; 1236c431f89bSVlad Buslov 123718d3eefbSVlad Buslov ASSERT_RTNL(); 1238c431f89bSVlad Buslov 123918d3eefbSVlad Buslov err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack); 124018d3eefbSVlad Buslov if (err) 124118d3eefbSVlad Buslov goto errout; 1242e368fdb6SVlad Buslov 124318d3eefbSVlad Buslov err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack); 124418d3eefbSVlad Buslov if (err) 1245e368fdb6SVlad Buslov goto errout_qdisc; 1246c431f89bSVlad Buslov 124718d3eefbSVlad Buslov block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack); 1248af736bf0SDan Carpenter if (IS_ERR(block)) { 1249af736bf0SDan Carpenter err = PTR_ERR(block); 1250e368fdb6SVlad Buslov goto errout_qdisc; 1251af736bf0SDan Carpenter } 1252c431f89bSVlad Buslov 1253c431f89bSVlad Buslov return block; 1254e368fdb6SVlad Buslov 1255e368fdb6SVlad Buslov errout_qdisc: 125618d3eefbSVlad Buslov if (*q) 1257e368fdb6SVlad Buslov qdisc_put(*q); 125818d3eefbSVlad Buslov errout: 1259460b3601SCong Wang *q = NULL; 1260e368fdb6SVlad Buslov return ERR_PTR(err); 1261e368fdb6SVlad Buslov } 1262e368fdb6SVlad Buslov 126312db03b6SVlad Buslov static void tcf_block_release(struct Qdisc *q, struct tcf_block *block, 126412db03b6SVlad Buslov bool rtnl_held) 1265e368fdb6SVlad Buslov { 1266787ce6d0SVlad Buslov if (!IS_ERR_OR_NULL(block)) 126712db03b6SVlad Buslov tcf_block_refcnt_put(block, rtnl_held); 1268787ce6d0SVlad Buslov 1269470502deSVlad Buslov if (q) { 1270470502deSVlad Buslov if (rtnl_held) 1271e368fdb6SVlad Buslov qdisc_put(q); 1272470502deSVlad Buslov else 1273470502deSVlad Buslov qdisc_put_unlocked(q); 1274470502deSVlad Buslov } 1275c431f89bSVlad Buslov } 1276c431f89bSVlad Buslov 1277f36fe1c4SJiri Pirko struct tcf_block_owner_item { 1278f36fe1c4SJiri Pirko struct list_head list; 1279f36fe1c4SJiri Pirko struct Qdisc *q; 128032f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type; 1281f36fe1c4SJiri Pirko }; 1282f36fe1c4SJiri Pirko 1283f36fe1c4SJiri Pirko static void 1284f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(struct tcf_block *block, 1285f36fe1c4SJiri Pirko struct Qdisc *q, 128632f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1287f36fe1c4SJiri Pirko { 1288f36fe1c4SJiri Pirko if (block->keep_dst && 128932f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && 129032f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 1291f36fe1c4SJiri Pirko netif_keep_dst(qdisc_dev(q)); 1292f36fe1c4SJiri Pirko } 1293f36fe1c4SJiri Pirko 1294f36fe1c4SJiri Pirko void tcf_block_netif_keep_dst(struct tcf_block *block) 1295f36fe1c4SJiri Pirko { 1296f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1297f36fe1c4SJiri Pirko 1298f36fe1c4SJiri Pirko block->keep_dst = true; 1299f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) 1300f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, item->q, 1301f36fe1c4SJiri Pirko item->binder_type); 1302f36fe1c4SJiri Pirko } 1303f36fe1c4SJiri Pirko EXPORT_SYMBOL(tcf_block_netif_keep_dst); 1304f36fe1c4SJiri Pirko 1305f36fe1c4SJiri Pirko static int tcf_block_owner_add(struct tcf_block *block, 1306f36fe1c4SJiri Pirko struct Qdisc *q, 130732f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1308f36fe1c4SJiri Pirko { 1309f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1310f36fe1c4SJiri Pirko 1311f36fe1c4SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 1312f36fe1c4SJiri Pirko if (!item) 1313f36fe1c4SJiri Pirko return -ENOMEM; 1314f36fe1c4SJiri Pirko item->q = q; 1315f36fe1c4SJiri Pirko item->binder_type = binder_type; 1316f36fe1c4SJiri Pirko list_add(&item->list, &block->owner_list); 1317f36fe1c4SJiri Pirko return 0; 1318f36fe1c4SJiri Pirko } 1319f36fe1c4SJiri Pirko 1320f36fe1c4SJiri Pirko static void tcf_block_owner_del(struct tcf_block *block, 1321f36fe1c4SJiri Pirko struct Qdisc *q, 132232f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1323f36fe1c4SJiri Pirko { 1324f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1325f36fe1c4SJiri Pirko 1326f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) { 1327f36fe1c4SJiri Pirko if (item->q == q && item->binder_type == binder_type) { 1328f36fe1c4SJiri Pirko list_del(&item->list); 1329f36fe1c4SJiri Pirko kfree(item); 1330f36fe1c4SJiri Pirko return; 1331f36fe1c4SJiri Pirko } 1332f36fe1c4SJiri Pirko } 1333f36fe1c4SJiri Pirko WARN_ON(1); 1334f36fe1c4SJiri Pirko } 1335f36fe1c4SJiri Pirko 133648617387SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 133748617387SJiri Pirko struct tcf_block_ext_info *ei, 133848617387SJiri Pirko struct netlink_ext_ack *extack) 133948617387SJiri Pirko { 134048617387SJiri Pirko struct net *net = qdisc_net(q); 134148617387SJiri Pirko struct tcf_block *block = NULL; 134248617387SJiri Pirko int err; 134348617387SJiri Pirko 1344787ce6d0SVlad Buslov if (ei->block_index) 134548617387SJiri Pirko /* block_index not 0 means the shared block is requested */ 1346787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, ei->block_index); 134748617387SJiri Pirko 134848617387SJiri Pirko if (!block) { 1349bb047dddSJiri Pirko block = tcf_block_create(net, q, ei->block_index, extack); 135048617387SJiri Pirko if (IS_ERR(block)) 135148617387SJiri Pirko return PTR_ERR(block); 1352bb047dddSJiri Pirko if (tcf_block_shared(block)) { 1353bb047dddSJiri Pirko err = tcf_block_insert(block, net, extack); 135448617387SJiri Pirko if (err) 135548617387SJiri Pirko goto err_block_insert; 135648617387SJiri Pirko } 135748617387SJiri Pirko } 135848617387SJiri Pirko 1359f36fe1c4SJiri Pirko err = tcf_block_owner_add(block, q, ei->binder_type); 1360f36fe1c4SJiri Pirko if (err) 1361f36fe1c4SJiri Pirko goto err_block_owner_add; 1362f36fe1c4SJiri Pirko 1363f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, q, ei->binder_type); 1364f36fe1c4SJiri Pirko 1365f71e0ca4SJiri Pirko err = tcf_chain0_head_change_cb_add(block, ei, extack); 1366a9b19443SJiri Pirko if (err) 1367f71e0ca4SJiri Pirko goto err_chain0_head_change_cb_add; 1368caa72601SJiri Pirko 136960513bd8SJohn Hurley err = tcf_block_offload_bind(block, q, ei, extack); 1370caa72601SJiri Pirko if (err) 1371caa72601SJiri Pirko goto err_block_offload_bind; 1372caa72601SJiri Pirko 13736529eabaSJiri Pirko *p_block = block; 13746529eabaSJiri Pirko return 0; 13752190d1d0SJiri Pirko 1376caa72601SJiri Pirko err_block_offload_bind: 1377f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1378f71e0ca4SJiri Pirko err_chain0_head_change_cb_add: 1379f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 1380f36fe1c4SJiri Pirko err_block_owner_add: 138148617387SJiri Pirko err_block_insert: 138212db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 13832190d1d0SJiri Pirko return err; 13846529eabaSJiri Pirko } 13858c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext); 13868c4083b3SJiri Pirko 1387c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 1388c7eb7d72SJiri Pirko { 1389c7eb7d72SJiri Pirko struct tcf_proto __rcu **p_filter_chain = priv; 1390c7eb7d72SJiri Pirko 1391c7eb7d72SJiri Pirko rcu_assign_pointer(*p_filter_chain, tp_head); 1392c7eb7d72SJiri Pirko } 1393c7eb7d72SJiri Pirko 13948c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block, 13958d1a77f9SAlexander Aring struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, 13968d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 13978c4083b3SJiri Pirko { 1398c7eb7d72SJiri Pirko struct tcf_block_ext_info ei = { 1399c7eb7d72SJiri Pirko .chain_head_change = tcf_chain_head_change_dflt, 1400c7eb7d72SJiri Pirko .chain_head_change_priv = p_filter_chain, 1401c7eb7d72SJiri Pirko }; 14028c4083b3SJiri Pirko 1403c7eb7d72SJiri Pirko WARN_ON(!p_filter_chain); 14048d1a77f9SAlexander Aring return tcf_block_get_ext(p_block, q, &ei, extack); 14058c4083b3SJiri Pirko } 14066529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get); 14076529eabaSJiri Pirko 14087aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound 1409a60b3f51SRoman Kapl * actions should be all removed after flushing. 1410e2ef7544SCong Wang */ 1411c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 1412e1ea2f98SDavid S. Miller struct tcf_block_ext_info *ei) 14137aa0045dSCong Wang { 1414c30abd5eSDavid S. Miller if (!block) 1415c30abd5eSDavid S. Miller return; 1416f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1417f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 141848617387SJiri Pirko 141912db03b6SVlad Buslov __tcf_block_put(block, q, ei, true); 142048617387SJiri Pirko } 14218c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext); 14228c4083b3SJiri Pirko 14238c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block) 14248c4083b3SJiri Pirko { 14258c4083b3SJiri Pirko struct tcf_block_ext_info ei = {0, }; 14268c4083b3SJiri Pirko 14274853f128SJiri Pirko if (!block) 14284853f128SJiri Pirko return; 1429c7eb7d72SJiri Pirko tcf_block_put_ext(block, block->q, &ei); 14308c4083b3SJiri Pirko } 1431e1ea2f98SDavid S. Miller 14326529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put); 1433cf1facdaSJiri Pirko 143432636742SJohn Hurley static int 1435a7323311SPablo Neira Ayuso tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, 143632636742SJohn Hurley void *cb_priv, bool add, bool offload_in_use, 143732636742SJohn Hurley struct netlink_ext_ack *extack) 143832636742SJohn Hurley { 1439bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 1440fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 144132636742SJohn Hurley int err; 144232636742SJohn Hurley 14434f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14444f8116c8SVlad Buslov 1445bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 1446bbf73830SVlad Buslov chain; 1447bbf73830SVlad Buslov chain_prev = chain, 1448bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 1449bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 1450fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); tp; 1451fe2923afSVlad Buslov tp_prev = tp, 1452fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 145312db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL)) { 145432636742SJohn Hurley if (tp->ops->reoffload) { 145532636742SJohn Hurley err = tp->ops->reoffload(tp, add, cb, cb_priv, 145632636742SJohn Hurley extack); 145732636742SJohn Hurley if (err && add) 145832636742SJohn Hurley goto err_playback_remove; 145932636742SJohn Hurley } else if (add && offload_in_use) { 146032636742SJohn Hurley err = -EOPNOTSUPP; 146132636742SJohn Hurley NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support"); 146232636742SJohn Hurley goto err_playback_remove; 146332636742SJohn Hurley } 146432636742SJohn Hurley } 146532636742SJohn Hurley } 146632636742SJohn Hurley 146732636742SJohn Hurley return 0; 146832636742SJohn Hurley 146932636742SJohn Hurley err_playback_remove: 147012db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 1471bbf73830SVlad Buslov tcf_chain_put(chain); 147232636742SJohn Hurley tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use, 147332636742SJohn Hurley extack); 147432636742SJohn Hurley return err; 147532636742SJohn Hurley } 147632636742SJohn Hurley 147759094b1eSPablo Neira Ayuso static int tcf_block_bind(struct tcf_block *block, 147859094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 147959094b1eSPablo Neira Ayuso { 148059094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 148159094b1eSPablo Neira Ayuso int err, i = 0; 148259094b1eSPablo Neira Ayuso 14834f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14844f8116c8SVlad Buslov 148559094b1eSPablo Neira Ayuso list_for_each_entry(block_cb, &bo->cb_list, list) { 148659094b1eSPablo Neira Ayuso err = tcf_block_playback_offloads(block, block_cb->cb, 148759094b1eSPablo Neira Ayuso block_cb->cb_priv, true, 148859094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 148959094b1eSPablo Neira Ayuso bo->extack); 149059094b1eSPablo Neira Ayuso if (err) 149159094b1eSPablo Neira Ayuso goto err_unroll; 1492c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1493c9f14470SVlad Buslov block->lockeddevcnt++; 149459094b1eSPablo Neira Ayuso 149559094b1eSPablo Neira Ayuso i++; 149659094b1eSPablo Neira Ayuso } 149714bfb13fSPablo Neira Ayuso list_splice(&bo->cb_list, &block->flow_block.cb_list); 149859094b1eSPablo Neira Ayuso 149959094b1eSPablo Neira Ayuso return 0; 150059094b1eSPablo Neira Ayuso 150159094b1eSPablo Neira Ayuso err_unroll: 150259094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 150359094b1eSPablo Neira Ayuso if (i-- > 0) { 150459094b1eSPablo Neira Ayuso list_del(&block_cb->list); 150559094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 150659094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 150759094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 150859094b1eSPablo Neira Ayuso NULL); 1509c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1510c9f14470SVlad Buslov block->lockeddevcnt--; 151159094b1eSPablo Neira Ayuso } 151259094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 151359094b1eSPablo Neira Ayuso } 151459094b1eSPablo Neira Ayuso 151559094b1eSPablo Neira Ayuso return err; 151659094b1eSPablo Neira Ayuso } 151759094b1eSPablo Neira Ayuso 151859094b1eSPablo Neira Ayuso static void tcf_block_unbind(struct tcf_block *block, 151959094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 152059094b1eSPablo Neira Ayuso { 152159094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 152259094b1eSPablo Neira Ayuso 15234f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 15244f8116c8SVlad Buslov 152559094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 152659094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 152759094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 152859094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 152959094b1eSPablo Neira Ayuso NULL); 153059094b1eSPablo Neira Ayuso list_del(&block_cb->list); 153159094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 1532c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1533c9f14470SVlad Buslov block->lockeddevcnt--; 153459094b1eSPablo Neira Ayuso } 153559094b1eSPablo Neira Ayuso } 153659094b1eSPablo Neira Ayuso 153759094b1eSPablo Neira Ayuso static int tcf_block_setup(struct tcf_block *block, 153859094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 153959094b1eSPablo Neira Ayuso { 154059094b1eSPablo Neira Ayuso int err; 154159094b1eSPablo Neira Ayuso 154259094b1eSPablo Neira Ayuso switch (bo->command) { 154359094b1eSPablo Neira Ayuso case FLOW_BLOCK_BIND: 154459094b1eSPablo Neira Ayuso err = tcf_block_bind(block, bo); 154559094b1eSPablo Neira Ayuso break; 154659094b1eSPablo Neira Ayuso case FLOW_BLOCK_UNBIND: 154759094b1eSPablo Neira Ayuso err = 0; 154859094b1eSPablo Neira Ayuso tcf_block_unbind(block, bo); 154959094b1eSPablo Neira Ayuso break; 155059094b1eSPablo Neira Ayuso default: 155159094b1eSPablo Neira Ayuso WARN_ON_ONCE(1); 155259094b1eSPablo Neira Ayuso err = -EOPNOTSUPP; 155359094b1eSPablo Neira Ayuso } 155459094b1eSPablo Neira Ayuso 155559094b1eSPablo Neira Ayuso return err; 155659094b1eSPablo Neira Ayuso } 155759094b1eSPablo Neira Ayuso 155887d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached 155987d83093SJiri Pirko * to this qdisc, (optionally) tests for protocol and asks 156087d83093SJiri Pirko * specific classifiers. 156187d83093SJiri Pirko */ 156287d83093SJiri Pirko int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, 156387d83093SJiri Pirko struct tcf_result *res, bool compat_mode) 156487d83093SJiri Pirko { 156587d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 156687d83093SJiri Pirko const int max_reclassify_loop = 4; 1567ee538dceSJiri Pirko const struct tcf_proto *orig_tp = tp; 1568ee538dceSJiri Pirko const struct tcf_proto *first_tp; 156987d83093SJiri Pirko int limit = 0; 157087d83093SJiri Pirko 157187d83093SJiri Pirko reclassify: 157287d83093SJiri Pirko #endif 157387d83093SJiri Pirko for (; tp; tp = rcu_dereference_bh(tp->next)) { 1574cd0c4e70SCong Wang __be16 protocol = tc_skb_protocol(skb); 157587d83093SJiri Pirko int err; 157687d83093SJiri Pirko 157787d83093SJiri Pirko if (tp->protocol != protocol && 157887d83093SJiri Pirko tp->protocol != htons(ETH_P_ALL)) 157987d83093SJiri Pirko continue; 158087d83093SJiri Pirko 158187d83093SJiri Pirko err = tp->classify(skb, tp, res); 158287d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 1583db50514fSJiri Pirko if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 1584ee538dceSJiri Pirko first_tp = orig_tp; 158587d83093SJiri Pirko goto reset; 1586db50514fSJiri Pirko } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 1587ee538dceSJiri Pirko first_tp = res->goto_tp; 158895a7233cSPaul Blakey 158995a7233cSPaul Blakey #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 159095a7233cSPaul Blakey { 159195a7233cSPaul Blakey struct tc_skb_ext *ext; 159295a7233cSPaul Blakey 159395a7233cSPaul Blakey ext = skb_ext_add(skb, TC_SKB_EXT); 159495a7233cSPaul Blakey if (WARN_ON_ONCE(!ext)) 159595a7233cSPaul Blakey return TC_ACT_SHOT; 159695a7233cSPaul Blakey 159795a7233cSPaul Blakey ext->chain = err & TC_ACT_EXT_VAL_MASK; 159895a7233cSPaul Blakey } 159995a7233cSPaul Blakey #endif 1600db50514fSJiri Pirko goto reset; 1601db50514fSJiri Pirko } 160287d83093SJiri Pirko #endif 160387d83093SJiri Pirko if (err >= 0) 160487d83093SJiri Pirko return err; 160587d83093SJiri Pirko } 160687d83093SJiri Pirko 160787d83093SJiri Pirko return TC_ACT_UNSPEC; /* signal: continue lookup */ 160887d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 160987d83093SJiri Pirko reset: 161087d83093SJiri Pirko if (unlikely(limit++ >= max_reclassify_loop)) { 16119d3aaff3SJiri Pirko net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n", 16129d3aaff3SJiri Pirko tp->chain->block->index, 16139d3aaff3SJiri Pirko tp->prio & 0xffff, 161487d83093SJiri Pirko ntohs(tp->protocol)); 161587d83093SJiri Pirko return TC_ACT_SHOT; 161687d83093SJiri Pirko } 161787d83093SJiri Pirko 1618ee538dceSJiri Pirko tp = first_tp; 161987d83093SJiri Pirko goto reclassify; 162087d83093SJiri Pirko #endif 162187d83093SJiri Pirko } 162287d83093SJiri Pirko EXPORT_SYMBOL(tcf_classify); 162387d83093SJiri Pirko 16242190d1d0SJiri Pirko struct tcf_chain_info { 16252190d1d0SJiri Pirko struct tcf_proto __rcu **pprev; 16262190d1d0SJiri Pirko struct tcf_proto __rcu *next; 16272190d1d0SJiri Pirko }; 16282190d1d0SJiri Pirko 1629ed76f5edSVlad Buslov static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain, 1630ed76f5edSVlad Buslov struct tcf_chain_info *chain_info) 16312190d1d0SJiri Pirko { 1632ed76f5edSVlad Buslov return tcf_chain_dereference(*chain_info->pprev, chain); 16332190d1d0SJiri Pirko } 16342190d1d0SJiri Pirko 1635726d0612SVlad Buslov static int tcf_chain_tp_insert(struct tcf_chain *chain, 16362190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16372190d1d0SJiri Pirko struct tcf_proto *tp) 16382190d1d0SJiri Pirko { 1639726d0612SVlad Buslov if (chain->flushing) 1640726d0612SVlad Buslov return -EAGAIN; 1641726d0612SVlad Buslov 1642c7eb7d72SJiri Pirko if (*chain_info->pprev == chain->filter_chain) 1643f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, tp); 16444dbfa766SVlad Buslov tcf_proto_get(tp); 1645ed76f5edSVlad Buslov RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info)); 16462190d1d0SJiri Pirko rcu_assign_pointer(*chain_info->pprev, tp); 1647726d0612SVlad Buslov 1648726d0612SVlad Buslov return 0; 16492190d1d0SJiri Pirko } 16502190d1d0SJiri Pirko 16512190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain, 16522190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16532190d1d0SJiri Pirko struct tcf_proto *tp) 16542190d1d0SJiri Pirko { 1655ed76f5edSVlad Buslov struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain); 16562190d1d0SJiri Pirko 16578b64678eSVlad Buslov tcf_proto_mark_delete(tp); 1658c7eb7d72SJiri Pirko if (tp == chain->filter_chain) 1659f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, next); 16602190d1d0SJiri Pirko RCU_INIT_POINTER(*chain_info->pprev, next); 16612190d1d0SJiri Pirko } 16622190d1d0SJiri Pirko 16632190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 16642190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16652190d1d0SJiri Pirko u32 protocol, u32 prio, 16668b64678eSVlad Buslov bool prio_allocate); 16678b64678eSVlad Buslov 16688b64678eSVlad Buslov /* Try to insert new proto. 16698b64678eSVlad Buslov * If proto with specified priority already exists, free new proto 16708b64678eSVlad Buslov * and return existing one. 16718b64678eSVlad Buslov */ 16728b64678eSVlad Buslov 16738b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain, 16748b64678eSVlad Buslov struct tcf_proto *tp_new, 167512db03b6SVlad Buslov u32 protocol, u32 prio, 167612db03b6SVlad Buslov bool rtnl_held) 16778b64678eSVlad Buslov { 16788b64678eSVlad Buslov struct tcf_chain_info chain_info; 16798b64678eSVlad Buslov struct tcf_proto *tp; 1680726d0612SVlad Buslov int err = 0; 16818b64678eSVlad Buslov 16828b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 16838b64678eSVlad Buslov 168459eb87cbSJohn Hurley if (tcf_proto_exists_destroying(chain, tp_new)) { 168559eb87cbSJohn Hurley mutex_unlock(&chain->filter_chain_lock); 168659eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 168759eb87cbSJohn Hurley return ERR_PTR(-EAGAIN); 168859eb87cbSJohn Hurley } 168959eb87cbSJohn Hurley 16908b64678eSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, 16918b64678eSVlad Buslov protocol, prio, false); 16928b64678eSVlad Buslov if (!tp) 1693726d0612SVlad Buslov err = tcf_chain_tp_insert(chain, &chain_info, tp_new); 16948b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 16958b64678eSVlad Buslov 16968b64678eSVlad Buslov if (tp) { 169759eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 16988b64678eSVlad Buslov tp_new = tp; 1699726d0612SVlad Buslov } else if (err) { 170059eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 1701726d0612SVlad Buslov tp_new = ERR_PTR(err); 17028b64678eSVlad Buslov } 17038b64678eSVlad Buslov 17048b64678eSVlad Buslov return tp_new; 17058b64678eSVlad Buslov } 17068b64678eSVlad Buslov 17078b64678eSVlad Buslov static void tcf_chain_tp_delete_empty(struct tcf_chain *chain, 170812db03b6SVlad Buslov struct tcf_proto *tp, bool rtnl_held, 17098b64678eSVlad Buslov struct netlink_ext_ack *extack) 17108b64678eSVlad Buslov { 17118b64678eSVlad Buslov struct tcf_chain_info chain_info; 17128b64678eSVlad Buslov struct tcf_proto *tp_iter; 17138b64678eSVlad Buslov struct tcf_proto **pprev; 17148b64678eSVlad Buslov struct tcf_proto *next; 17158b64678eSVlad Buslov 17168b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 17178b64678eSVlad Buslov 17188b64678eSVlad Buslov /* Atomically find and remove tp from chain. */ 17198b64678eSVlad Buslov for (pprev = &chain->filter_chain; 17208b64678eSVlad Buslov (tp_iter = tcf_chain_dereference(*pprev, chain)); 17218b64678eSVlad Buslov pprev = &tp_iter->next) { 17228b64678eSVlad Buslov if (tp_iter == tp) { 17238b64678eSVlad Buslov chain_info.pprev = pprev; 17248b64678eSVlad Buslov chain_info.next = tp_iter->next; 17258b64678eSVlad Buslov WARN_ON(tp_iter->deleting); 17268b64678eSVlad Buslov break; 17278b64678eSVlad Buslov } 17288b64678eSVlad Buslov } 17298b64678eSVlad Buslov /* Verify that tp still exists and no new filters were inserted 17308b64678eSVlad Buslov * concurrently. 17318b64678eSVlad Buslov * Mark tp for deletion if it is empty. 17328b64678eSVlad Buslov */ 1733a5b72a08SDavide Caratti if (!tp_iter || !tcf_proto_check_delete(tp)) { 17348b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17358b64678eSVlad Buslov return; 17368b64678eSVlad Buslov } 17378b64678eSVlad Buslov 173859eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 17398b64678eSVlad Buslov next = tcf_chain_dereference(chain_info.next, chain); 17408b64678eSVlad Buslov if (tp == chain->filter_chain) 17418b64678eSVlad Buslov tcf_chain0_head_change(chain, next); 17428b64678eSVlad Buslov RCU_INIT_POINTER(*chain_info.pprev, next); 17438b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17448b64678eSVlad Buslov 174512db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, extack); 17468b64678eSVlad Buslov } 17478b64678eSVlad Buslov 17488b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 17498b64678eSVlad Buslov struct tcf_chain_info *chain_info, 17508b64678eSVlad Buslov u32 protocol, u32 prio, 17512190d1d0SJiri Pirko bool prio_allocate) 17522190d1d0SJiri Pirko { 17532190d1d0SJiri Pirko struct tcf_proto **pprev; 17542190d1d0SJiri Pirko struct tcf_proto *tp; 17552190d1d0SJiri Pirko 17562190d1d0SJiri Pirko /* Check the chain for existence of proto-tcf with this priority */ 17572190d1d0SJiri Pirko for (pprev = &chain->filter_chain; 1758ed76f5edSVlad Buslov (tp = tcf_chain_dereference(*pprev, chain)); 1759ed76f5edSVlad Buslov pprev = &tp->next) { 17602190d1d0SJiri Pirko if (tp->prio >= prio) { 17612190d1d0SJiri Pirko if (tp->prio == prio) { 17622190d1d0SJiri Pirko if (prio_allocate || 17632190d1d0SJiri Pirko (tp->protocol != protocol && protocol)) 17642190d1d0SJiri Pirko return ERR_PTR(-EINVAL); 17652190d1d0SJiri Pirko } else { 17662190d1d0SJiri Pirko tp = NULL; 17672190d1d0SJiri Pirko } 17682190d1d0SJiri Pirko break; 17692190d1d0SJiri Pirko } 17702190d1d0SJiri Pirko } 17712190d1d0SJiri Pirko chain_info->pprev = pprev; 17724dbfa766SVlad Buslov if (tp) { 17734dbfa766SVlad Buslov chain_info->next = tp->next; 17744dbfa766SVlad Buslov tcf_proto_get(tp); 17754dbfa766SVlad Buslov } else { 17764dbfa766SVlad Buslov chain_info->next = NULL; 17774dbfa766SVlad Buslov } 17782190d1d0SJiri Pirko return tp; 17792190d1d0SJiri Pirko } 17802190d1d0SJiri Pirko 17817120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb, 17827960d1daSJiri Pirko struct tcf_proto *tp, struct tcf_block *block, 17837960d1daSJiri Pirko struct Qdisc *q, u32 parent, void *fh, 178412db03b6SVlad Buslov u32 portid, u32 seq, u16 flags, int event, 178512db03b6SVlad Buslov bool rtnl_held) 17867120371cSWANG Cong { 17877120371cSWANG Cong struct tcmsg *tcm; 17887120371cSWANG Cong struct nlmsghdr *nlh; 17897120371cSWANG Cong unsigned char *b = skb_tail_pointer(skb); 17907120371cSWANG Cong 17917120371cSWANG Cong nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 17927120371cSWANG Cong if (!nlh) 17937120371cSWANG Cong goto out_nlmsg_trim; 17947120371cSWANG Cong tcm = nlmsg_data(nlh); 17957120371cSWANG Cong tcm->tcm_family = AF_UNSPEC; 17967120371cSWANG Cong tcm->tcm__pad1 = 0; 17977120371cSWANG Cong tcm->tcm__pad2 = 0; 17987960d1daSJiri Pirko if (q) { 1799a10fa201SJiri Pirko tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 1800a10fa201SJiri Pirko tcm->tcm_parent = parent; 18017960d1daSJiri Pirko } else { 18027960d1daSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 18037960d1daSJiri Pirko tcm->tcm_block_index = block->index; 18047960d1daSJiri Pirko } 18057120371cSWANG Cong tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 18067120371cSWANG Cong if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 18077120371cSWANG Cong goto nla_put_failure; 18087120371cSWANG Cong if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 18097120371cSWANG Cong goto nla_put_failure; 18107120371cSWANG Cong if (!fh) { 18117120371cSWANG Cong tcm->tcm_handle = 0; 18127120371cSWANG Cong } else { 181312db03b6SVlad Buslov if (tp->ops->dump && 181412db03b6SVlad Buslov tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) 18157120371cSWANG Cong goto nla_put_failure; 18167120371cSWANG Cong } 18177120371cSWANG Cong nlh->nlmsg_len = skb_tail_pointer(skb) - b; 18187120371cSWANG Cong return skb->len; 18197120371cSWANG Cong 18207120371cSWANG Cong out_nlmsg_trim: 18217120371cSWANG Cong nla_put_failure: 18227120371cSWANG Cong nlmsg_trim(skb, b); 18237120371cSWANG Cong return -1; 18247120371cSWANG Cong } 18257120371cSWANG Cong 18267120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb, 18277120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 18287960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 182912db03b6SVlad Buslov u32 parent, void *fh, int event, bool unicast, 183012db03b6SVlad Buslov bool rtnl_held) 18317120371cSWANG Cong { 18327120371cSWANG Cong struct sk_buff *skb; 18337120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 18345b5f99b1SZhike Wang int err = 0; 18357120371cSWANG Cong 18367120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 18377120371cSWANG Cong if (!skb) 18387120371cSWANG Cong return -ENOBUFS; 18397120371cSWANG Cong 18407960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 184112db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, event, 184212db03b6SVlad Buslov rtnl_held) <= 0) { 18437120371cSWANG Cong kfree_skb(skb); 18447120371cSWANG Cong return -EINVAL; 18457120371cSWANG Cong } 18467120371cSWANG Cong 18477120371cSWANG Cong if (unicast) 18485b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 18495b5f99b1SZhike Wang else 18505b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 18517120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 18525b5f99b1SZhike Wang 18535b5f99b1SZhike Wang if (err > 0) 18545b5f99b1SZhike Wang err = 0; 18555b5f99b1SZhike Wang return err; 18567120371cSWANG Cong } 18577120371cSWANG Cong 18587120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 18597120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 18607960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 1861c35a4accSAlexander Aring u32 parent, void *fh, bool unicast, bool *last, 186212db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 18637120371cSWANG Cong { 18647120371cSWANG Cong struct sk_buff *skb; 18657120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 18667120371cSWANG Cong int err; 18677120371cSWANG Cong 18687120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 18697120371cSWANG Cong if (!skb) 18707120371cSWANG Cong return -ENOBUFS; 18717120371cSWANG Cong 18727960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 187312db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, 187412db03b6SVlad Buslov rtnl_held) <= 0) { 1875c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to build del event notification"); 18767120371cSWANG Cong kfree_skb(skb); 18777120371cSWANG Cong return -EINVAL; 18787120371cSWANG Cong } 18797120371cSWANG Cong 188012db03b6SVlad Buslov err = tp->ops->delete(tp, fh, last, rtnl_held, extack); 18817120371cSWANG Cong if (err) { 18827120371cSWANG Cong kfree_skb(skb); 18837120371cSWANG Cong return err; 18847120371cSWANG Cong } 18857120371cSWANG Cong 18867120371cSWANG Cong if (unicast) 18875b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 18885b5f99b1SZhike Wang else 1889c35a4accSAlexander Aring err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 18907120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 1891c35a4accSAlexander Aring if (err < 0) 1892c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to send filter delete notification"); 18935b5f99b1SZhike Wang 18945b5f99b1SZhike Wang if (err > 0) 18955b5f99b1SZhike Wang err = 0; 1896c35a4accSAlexander Aring return err; 18977120371cSWANG Cong } 18987120371cSWANG Cong 18997120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 19007960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 19017960d1daSJiri Pirko u32 parent, struct nlmsghdr *n, 190212db03b6SVlad Buslov struct tcf_chain *chain, int event, 190312db03b6SVlad Buslov bool rtnl_held) 19047120371cSWANG Cong { 19057120371cSWANG Cong struct tcf_proto *tp; 19067120371cSWANG Cong 190712db03b6SVlad Buslov for (tp = tcf_get_next_proto(chain, NULL, rtnl_held); 190812db03b6SVlad Buslov tp; tp = tcf_get_next_proto(chain, tp, rtnl_held)) 19097960d1daSJiri Pirko tfilter_notify(net, oskb, n, tp, block, 191012db03b6SVlad Buslov q, parent, NULL, event, false, rtnl_held); 19117120371cSWANG Cong } 19127120371cSWANG Cong 19137d5509faSVlad Buslov static void tfilter_put(struct tcf_proto *tp, void *fh) 19147d5509faSVlad Buslov { 19157d5509faSVlad Buslov if (tp->ops->put && fh) 19167d5509faSVlad Buslov tp->ops->put(tp, fh); 19177d5509faSVlad Buslov } 19187d5509faSVlad Buslov 1919c431f89bSVlad Buslov static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1920c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19211da177e4SLinus Torvalds { 19223b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1923add93b61SPatrick McHardy struct nlattr *tca[TCA_MAX + 1]; 19246f96c3c6SCong Wang char name[IFNAMSIZ]; 19251da177e4SLinus Torvalds struct tcmsg *t; 19261da177e4SLinus Torvalds u32 protocol; 19271da177e4SLinus Torvalds u32 prio; 19289d36d9e5SJiri Pirko bool prio_allocate; 19291da177e4SLinus Torvalds u32 parent; 19305bc17018SJiri Pirko u32 chain_index; 19317960d1daSJiri Pirko struct Qdisc *q = NULL; 19322190d1d0SJiri Pirko struct tcf_chain_info chain_info; 19335bc17018SJiri Pirko struct tcf_chain *chain = NULL; 19346529eabaSJiri Pirko struct tcf_block *block; 19351da177e4SLinus Torvalds struct tcf_proto *tp; 19361da177e4SLinus Torvalds unsigned long cl; 19378113c095SWANG Cong void *fh; 19381da177e4SLinus Torvalds int err; 1939628185cfSDaniel Borkmann int tp_created; 1940470502deSVlad Buslov bool rtnl_held = false; 19411da177e4SLinus Torvalds 1942c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 1943dfc47ef8SEric W. Biederman return -EPERM; 1944de179c8cSHong zhi guo 19451da177e4SLinus Torvalds replay: 1946628185cfSDaniel Borkmann tp_created = 0; 1947628185cfSDaniel Borkmann 19488cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 19498cb08174SJohannes Berg rtm_tca_policy, extack); 1950de179c8cSHong zhi guo if (err < 0) 1951de179c8cSHong zhi guo return err; 1952de179c8cSHong zhi guo 1953942b8165SDavid S. Miller t = nlmsg_data(n); 19541da177e4SLinus Torvalds protocol = TC_H_MIN(t->tcm_info); 19551da177e4SLinus Torvalds prio = TC_H_MAJ(t->tcm_info); 19569d36d9e5SJiri Pirko prio_allocate = false; 19571da177e4SLinus Torvalds parent = t->tcm_parent; 19584dbfa766SVlad Buslov tp = NULL; 19591da177e4SLinus Torvalds cl = 0; 1960470502deSVlad Buslov block = NULL; 19611da177e4SLinus Torvalds 19621da177e4SLinus Torvalds if (prio == 0) { 1963ea7f8277SDaniel Borkmann /* If no priority is provided by the user, 1964ea7f8277SDaniel Borkmann * we allocate one. 1965ea7f8277SDaniel Borkmann */ 1966ea7f8277SDaniel Borkmann if (n->nlmsg_flags & NLM_F_CREATE) { 19671da177e4SLinus Torvalds prio = TC_H_MAKE(0x80000000U, 0U); 19689d36d9e5SJiri Pirko prio_allocate = true; 1969c431f89bSVlad Buslov } else { 1970c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 1971ea7f8277SDaniel Borkmann return -ENOENT; 1972ea7f8277SDaniel Borkmann } 19731da177e4SLinus Torvalds } 19741da177e4SLinus Torvalds 19751da177e4SLinus Torvalds /* Find head of filter chain. */ 19761da177e4SLinus Torvalds 1977470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 1978470502deSVlad Buslov if (err) 1979470502deSVlad Buslov return err; 1980470502deSVlad Buslov 19816f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 19826f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 19836f96c3c6SCong Wang err = -EINVAL; 19846f96c3c6SCong Wang goto errout; 19856f96c3c6SCong Wang } 19866f96c3c6SCong Wang 1987470502deSVlad Buslov /* Take rtnl mutex if rtnl_held was set to true on previous iteration, 1988470502deSVlad Buslov * block is shared (no qdisc found), qdisc is not unlocked, classifier 1989470502deSVlad Buslov * type is not specified, classifier is not unlocked. 1990470502deSVlad Buslov */ 1991470502deSVlad Buslov if (rtnl_held || 1992470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 19936f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 1994470502deSVlad Buslov rtnl_held = true; 1995470502deSVlad Buslov rtnl_lock(); 1996470502deSVlad Buslov } 1997470502deSVlad Buslov 1998470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 1999470502deSVlad Buslov if (err) 2000470502deSVlad Buslov goto errout; 2001470502deSVlad Buslov 2002470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2003470502deSVlad Buslov extack); 2004c431f89bSVlad Buslov if (IS_ERR(block)) { 2005c431f89bSVlad Buslov err = PTR_ERR(block); 20067960d1daSJiri Pirko goto errout; 20077960d1daSJiri Pirko } 20085bc17018SJiri Pirko 20095bc17018SJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 20105bc17018SJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 2011c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 20125bc17018SJiri Pirko err = -EINVAL; 20135bc17018SJiri Pirko goto errout; 20145bc17018SJiri Pirko } 2015c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, true); 20165bc17018SJiri Pirko if (!chain) { 2017d5ed72a5SJiri Pirko NL_SET_ERR_MSG(extack, "Cannot create specified filter chain"); 2018c431f89bSVlad Buslov err = -ENOMEM; 2019ea7f8277SDaniel Borkmann goto errout; 2020ea7f8277SDaniel Borkmann } 20211da177e4SLinus Torvalds 2022ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 20232190d1d0SJiri Pirko tp = tcf_chain_tp_find(chain, &chain_info, protocol, 20242190d1d0SJiri Pirko prio, prio_allocate); 20252190d1d0SJiri Pirko if (IS_ERR(tp)) { 2026c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 20272190d1d0SJiri Pirko err = PTR_ERR(tp); 2028ed76f5edSVlad Buslov goto errout_locked; 20296bb16e7aSJiri Pirko } 20301da177e4SLinus Torvalds 20311da177e4SLinus Torvalds if (tp == NULL) { 20328b64678eSVlad Buslov struct tcf_proto *tp_new = NULL; 20338b64678eSVlad Buslov 2034726d0612SVlad Buslov if (chain->flushing) { 2035726d0612SVlad Buslov err = -EAGAIN; 2036726d0612SVlad Buslov goto errout_locked; 2037726d0612SVlad Buslov } 2038726d0612SVlad Buslov 20391da177e4SLinus Torvalds /* Proto-tcf does not exist, create new one */ 20401da177e4SLinus Torvalds 20416bb16e7aSJiri Pirko if (tca[TCA_KIND] == NULL || !protocol) { 2042c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified"); 20436bb16e7aSJiri Pirko err = -EINVAL; 2044ed76f5edSVlad Buslov goto errout_locked; 20456bb16e7aSJiri Pirko } 20461da177e4SLinus Torvalds 2047c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2048c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 20496bb16e7aSJiri Pirko err = -ENOENT; 2050ed76f5edSVlad Buslov goto errout_locked; 20516bb16e7aSJiri Pirko } 20521da177e4SLinus Torvalds 20539d36d9e5SJiri Pirko if (prio_allocate) 2054ed76f5edSVlad Buslov prio = tcf_auto_prio(tcf_chain_tp_prev(chain, 2055ed76f5edSVlad Buslov &chain_info)); 20561da177e4SLinus Torvalds 2057ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 205836d79af7SEric Dumazet tp_new = tcf_proto_create(name, protocol, prio, chain, 205936d79af7SEric Dumazet rtnl_held, extack); 20608b64678eSVlad Buslov if (IS_ERR(tp_new)) { 20618b64678eSVlad Buslov err = PTR_ERR(tp_new); 2062726d0612SVlad Buslov goto errout_tp; 20631da177e4SLinus Torvalds } 2064ed76f5edSVlad Buslov 206512186be7SMinoru Usui tp_created = 1; 206612db03b6SVlad Buslov tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio, 206712db03b6SVlad Buslov rtnl_held); 2068726d0612SVlad Buslov if (IS_ERR(tp)) { 2069726d0612SVlad Buslov err = PTR_ERR(tp); 2070726d0612SVlad Buslov goto errout_tp; 2071726d0612SVlad Buslov } 2072ed76f5edSVlad Buslov } else { 2073ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 20746bb16e7aSJiri Pirko } 20751da177e4SLinus Torvalds 20768b64678eSVlad Buslov if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 20778b64678eSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 20788b64678eSVlad Buslov err = -EINVAL; 20798b64678eSVlad Buslov goto errout; 20808b64678eSVlad Buslov } 20818b64678eSVlad Buslov 20821da177e4SLinus Torvalds fh = tp->ops->get(tp, t->tcm_handle); 20831da177e4SLinus Torvalds 20848113c095SWANG Cong if (!fh) { 2085c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2086c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 20876bb16e7aSJiri Pirko err = -ENOENT; 20881da177e4SLinus Torvalds goto errout; 20896bb16e7aSJiri Pirko } 2090c431f89bSVlad Buslov } else if (n->nlmsg_flags & NLM_F_EXCL) { 20917d5509faSVlad Buslov tfilter_put(tp, fh); 2092c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter already exists"); 20936bb16e7aSJiri Pirko err = -EEXIST; 20941da177e4SLinus Torvalds goto errout; 209512186be7SMinoru Usui } 20961da177e4SLinus Torvalds 20979f407f17SJiri Pirko if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) { 20989f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind"); 20999f407f17SJiri Pirko err = -EINVAL; 21009f407f17SJiri Pirko goto errout; 21019f407f17SJiri Pirko } 21029f407f17SJiri Pirko 21032f7ef2f8SCong Wang err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 21047306db38SAlexander Aring n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE, 210512db03b6SVlad Buslov rtnl_held, extack); 21067d5509faSVlad Buslov if (err == 0) { 21077960d1daSJiri Pirko tfilter_notify(net, skb, n, tp, block, q, parent, fh, 210812db03b6SVlad Buslov RTM_NEWTFILTER, false, rtnl_held); 21097d5509faSVlad Buslov tfilter_put(tp, fh); 2110503d81d4SVlad Buslov /* q pointer is NULL for shared blocks */ 2111503d81d4SVlad Buslov if (q) 21123f05e688SCong Wang q->flags &= ~TCQ_F_CAN_BYPASS; 21137d5509faSVlad Buslov } 21141da177e4SLinus Torvalds 21151da177e4SLinus Torvalds errout: 21168b64678eSVlad Buslov if (err && tp_created) 211712db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL); 2118726d0612SVlad Buslov errout_tp: 21194dbfa766SVlad Buslov if (chain) { 21204dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 212112db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 21224dbfa766SVlad Buslov if (!tp_created) 21234dbfa766SVlad Buslov tcf_chain_put(chain); 21244dbfa766SVlad Buslov } 212512db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2126470502deSVlad Buslov 2127470502deSVlad Buslov if (rtnl_held) 2128470502deSVlad Buslov rtnl_unlock(); 2129470502deSVlad Buslov 2130470502deSVlad Buslov if (err == -EAGAIN) { 2131470502deSVlad Buslov /* Take rtnl lock in case EAGAIN is caused by concurrent flush 2132470502deSVlad Buslov * of target chain. 2133470502deSVlad Buslov */ 2134470502deSVlad Buslov rtnl_held = true; 21351da177e4SLinus Torvalds /* Replay the request. */ 21361da177e4SLinus Torvalds goto replay; 2137470502deSVlad Buslov } 21381da177e4SLinus Torvalds return err; 2139ed76f5edSVlad Buslov 2140ed76f5edSVlad Buslov errout_locked: 2141ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2142ed76f5edSVlad Buslov goto errout; 21431da177e4SLinus Torvalds } 21441da177e4SLinus Torvalds 2145c431f89bSVlad Buslov static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2146c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2147c431f89bSVlad Buslov { 2148c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2149c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 21506f96c3c6SCong Wang char name[IFNAMSIZ]; 2151c431f89bSVlad Buslov struct tcmsg *t; 2152c431f89bSVlad Buslov u32 protocol; 2153c431f89bSVlad Buslov u32 prio; 2154c431f89bSVlad Buslov u32 parent; 2155c431f89bSVlad Buslov u32 chain_index; 2156c431f89bSVlad Buslov struct Qdisc *q = NULL; 2157c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2158c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2159470502deSVlad Buslov struct tcf_block *block = NULL; 2160c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2161c431f89bSVlad Buslov unsigned long cl = 0; 2162c431f89bSVlad Buslov void *fh = NULL; 2163c431f89bSVlad Buslov int err; 2164470502deSVlad Buslov bool rtnl_held = false; 2165c431f89bSVlad Buslov 2166c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 2167c431f89bSVlad Buslov return -EPERM; 2168c431f89bSVlad Buslov 21698cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 21708cb08174SJohannes Berg rtm_tca_policy, extack); 2171c431f89bSVlad Buslov if (err < 0) 2172c431f89bSVlad Buslov return err; 2173c431f89bSVlad Buslov 2174c431f89bSVlad Buslov t = nlmsg_data(n); 2175c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2176c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2177c431f89bSVlad Buslov parent = t->tcm_parent; 2178c431f89bSVlad Buslov 2179c431f89bSVlad Buslov if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) { 2180c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set"); 2181c431f89bSVlad Buslov return -ENOENT; 2182c431f89bSVlad Buslov } 2183c431f89bSVlad Buslov 2184c431f89bSVlad Buslov /* Find head of filter chain. */ 2185c431f89bSVlad Buslov 2186470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2187470502deSVlad Buslov if (err) 2188470502deSVlad Buslov return err; 2189470502deSVlad Buslov 21906f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 21916f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 21926f96c3c6SCong Wang err = -EINVAL; 21936f96c3c6SCong Wang goto errout; 21946f96c3c6SCong Wang } 2195470502deSVlad Buslov /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc 2196470502deSVlad Buslov * found), qdisc is not unlocked, classifier type is not specified, 2197470502deSVlad Buslov * classifier is not unlocked. 2198470502deSVlad Buslov */ 2199470502deSVlad Buslov if (!prio || 2200470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 22016f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2202470502deSVlad Buslov rtnl_held = true; 2203470502deSVlad Buslov rtnl_lock(); 2204470502deSVlad Buslov } 2205470502deSVlad Buslov 2206470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2207470502deSVlad Buslov if (err) 2208470502deSVlad Buslov goto errout; 2209470502deSVlad Buslov 2210470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2211470502deSVlad Buslov extack); 2212c431f89bSVlad Buslov if (IS_ERR(block)) { 2213c431f89bSVlad Buslov err = PTR_ERR(block); 2214c431f89bSVlad Buslov goto errout; 2215c431f89bSVlad Buslov } 2216c431f89bSVlad Buslov 2217c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2218c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2219c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2220c431f89bSVlad Buslov err = -EINVAL; 2221c431f89bSVlad Buslov goto errout; 2222c431f89bSVlad Buslov } 2223c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2224c431f89bSVlad Buslov if (!chain) { 22255ca8a25cSJiri Pirko /* User requested flush on non-existent chain. Nothing to do, 22265ca8a25cSJiri Pirko * so just return success. 22275ca8a25cSJiri Pirko */ 22285ca8a25cSJiri Pirko if (prio == 0) { 22295ca8a25cSJiri Pirko err = 0; 22305ca8a25cSJiri Pirko goto errout; 22315ca8a25cSJiri Pirko } 2232c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2233b7b4247dSJiri Pirko err = -ENOENT; 2234c431f89bSVlad Buslov goto errout; 2235c431f89bSVlad Buslov } 2236c431f89bSVlad Buslov 2237c431f89bSVlad Buslov if (prio == 0) { 2238c431f89bSVlad Buslov tfilter_notify_chain(net, skb, block, q, parent, n, 223912db03b6SVlad Buslov chain, RTM_DELTFILTER, rtnl_held); 224012db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 2241c431f89bSVlad Buslov err = 0; 2242c431f89bSVlad Buslov goto errout; 2243c431f89bSVlad Buslov } 2244c431f89bSVlad Buslov 2245ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2246c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2247c431f89bSVlad Buslov prio, false); 2248c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2249c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 22500e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2251ed76f5edSVlad Buslov goto errout_locked; 2252c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2253c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2254c431f89bSVlad Buslov err = -EINVAL; 2255ed76f5edSVlad Buslov goto errout_locked; 2256ed76f5edSVlad Buslov } else if (t->tcm_handle == 0) { 225759eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 2258c431f89bSVlad Buslov tcf_chain_tp_remove(chain, &chain_info, tp); 2259ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2260ed76f5edSVlad Buslov 226112db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2262c431f89bSVlad Buslov tfilter_notify(net, skb, n, tp, block, q, parent, fh, 226312db03b6SVlad Buslov RTM_DELTFILTER, false, rtnl_held); 2264c431f89bSVlad Buslov err = 0; 2265ed76f5edSVlad Buslov goto errout; 2266ed76f5edSVlad Buslov } 2267ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2268ed76f5edSVlad Buslov 2269ed76f5edSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2270ed76f5edSVlad Buslov 2271ed76f5edSVlad Buslov if (!fh) { 2272c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2273c431f89bSVlad Buslov err = -ENOENT; 2274c431f89bSVlad Buslov } else { 2275c431f89bSVlad Buslov bool last; 2276c431f89bSVlad Buslov 2277c431f89bSVlad Buslov err = tfilter_del_notify(net, skb, n, tp, block, 2278c431f89bSVlad Buslov q, parent, fh, false, &last, 227912db03b6SVlad Buslov rtnl_held, extack); 228012db03b6SVlad Buslov 2281c431f89bSVlad Buslov if (err) 2282c431f89bSVlad Buslov goto errout; 22838b64678eSVlad Buslov if (last) 228412db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack); 2285c431f89bSVlad Buslov } 2286c431f89bSVlad Buslov 2287c431f89bSVlad Buslov errout: 22884dbfa766SVlad Buslov if (chain) { 22894dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 229012db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2291c431f89bSVlad Buslov tcf_chain_put(chain); 22924dbfa766SVlad Buslov } 229312db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2294470502deSVlad Buslov 2295470502deSVlad Buslov if (rtnl_held) 2296470502deSVlad Buslov rtnl_unlock(); 2297470502deSVlad Buslov 2298c431f89bSVlad Buslov return err; 2299ed76f5edSVlad Buslov 2300ed76f5edSVlad Buslov errout_locked: 2301ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2302ed76f5edSVlad Buslov goto errout; 2303c431f89bSVlad Buslov } 2304c431f89bSVlad Buslov 2305c431f89bSVlad Buslov static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2306c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2307c431f89bSVlad Buslov { 2308c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2309c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 23106f96c3c6SCong Wang char name[IFNAMSIZ]; 2311c431f89bSVlad Buslov struct tcmsg *t; 2312c431f89bSVlad Buslov u32 protocol; 2313c431f89bSVlad Buslov u32 prio; 2314c431f89bSVlad Buslov u32 parent; 2315c431f89bSVlad Buslov u32 chain_index; 2316c431f89bSVlad Buslov struct Qdisc *q = NULL; 2317c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2318c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2319470502deSVlad Buslov struct tcf_block *block = NULL; 2320c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2321c431f89bSVlad Buslov unsigned long cl = 0; 2322c431f89bSVlad Buslov void *fh = NULL; 2323c431f89bSVlad Buslov int err; 2324470502deSVlad Buslov bool rtnl_held = false; 2325c431f89bSVlad Buslov 23268cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 23278cb08174SJohannes Berg rtm_tca_policy, extack); 2328c431f89bSVlad Buslov if (err < 0) 2329c431f89bSVlad Buslov return err; 2330c431f89bSVlad Buslov 2331c431f89bSVlad Buslov t = nlmsg_data(n); 2332c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2333c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2334c431f89bSVlad Buslov parent = t->tcm_parent; 2335c431f89bSVlad Buslov 2336c431f89bSVlad Buslov if (prio == 0) { 2337c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 2338c431f89bSVlad Buslov return -ENOENT; 2339c431f89bSVlad Buslov } 2340c431f89bSVlad Buslov 2341c431f89bSVlad Buslov /* Find head of filter chain. */ 2342c431f89bSVlad Buslov 2343470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2344470502deSVlad Buslov if (err) 2345470502deSVlad Buslov return err; 2346470502deSVlad Buslov 23476f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 23486f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 23496f96c3c6SCong Wang err = -EINVAL; 23506f96c3c6SCong Wang goto errout; 23516f96c3c6SCong Wang } 2352470502deSVlad Buslov /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not 2353470502deSVlad Buslov * unlocked, classifier type is not specified, classifier is not 2354470502deSVlad Buslov * unlocked. 2355470502deSVlad Buslov */ 2356470502deSVlad Buslov if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 23576f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2358470502deSVlad Buslov rtnl_held = true; 2359470502deSVlad Buslov rtnl_lock(); 2360470502deSVlad Buslov } 2361470502deSVlad Buslov 2362470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2363470502deSVlad Buslov if (err) 2364470502deSVlad Buslov goto errout; 2365470502deSVlad Buslov 2366470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2367470502deSVlad Buslov extack); 2368c431f89bSVlad Buslov if (IS_ERR(block)) { 2369c431f89bSVlad Buslov err = PTR_ERR(block); 2370c431f89bSVlad Buslov goto errout; 2371c431f89bSVlad Buslov } 2372c431f89bSVlad Buslov 2373c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2374c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2375c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2376c431f89bSVlad Buslov err = -EINVAL; 2377c431f89bSVlad Buslov goto errout; 2378c431f89bSVlad Buslov } 2379c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2380c431f89bSVlad Buslov if (!chain) { 2381c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2382c431f89bSVlad Buslov err = -EINVAL; 2383c431f89bSVlad Buslov goto errout; 2384c431f89bSVlad Buslov } 2385c431f89bSVlad Buslov 2386ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2387c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2388c431f89bSVlad Buslov prio, false); 2389ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2390c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2391c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 23920e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2393c431f89bSVlad Buslov goto errout; 2394c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2395c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2396c431f89bSVlad Buslov err = -EINVAL; 2397c431f89bSVlad Buslov goto errout; 2398c431f89bSVlad Buslov } 2399c431f89bSVlad Buslov 2400c431f89bSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2401c431f89bSVlad Buslov 2402c431f89bSVlad Buslov if (!fh) { 2403c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2404c431f89bSVlad Buslov err = -ENOENT; 2405c431f89bSVlad Buslov } else { 2406c431f89bSVlad Buslov err = tfilter_notify(net, skb, n, tp, block, q, parent, 240712db03b6SVlad Buslov fh, RTM_NEWTFILTER, true, rtnl_held); 2408c431f89bSVlad Buslov if (err < 0) 2409c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); 2410c431f89bSVlad Buslov } 2411c431f89bSVlad Buslov 24127d5509faSVlad Buslov tfilter_put(tp, fh); 2413c431f89bSVlad Buslov errout: 24144dbfa766SVlad Buslov if (chain) { 24154dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 241612db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2417c431f89bSVlad Buslov tcf_chain_put(chain); 24184dbfa766SVlad Buslov } 241912db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2420470502deSVlad Buslov 2421470502deSVlad Buslov if (rtnl_held) 2422470502deSVlad Buslov rtnl_unlock(); 2423470502deSVlad Buslov 2424c431f89bSVlad Buslov return err; 2425c431f89bSVlad Buslov } 2426c431f89bSVlad Buslov 2427aa767bfeSStephen Hemminger struct tcf_dump_args { 24281da177e4SLinus Torvalds struct tcf_walker w; 24291da177e4SLinus Torvalds struct sk_buff *skb; 24301da177e4SLinus Torvalds struct netlink_callback *cb; 24317960d1daSJiri Pirko struct tcf_block *block; 2432a10fa201SJiri Pirko struct Qdisc *q; 2433a10fa201SJiri Pirko u32 parent; 24341da177e4SLinus Torvalds }; 24351da177e4SLinus Torvalds 24368113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 24371da177e4SLinus Torvalds { 24381da177e4SLinus Torvalds struct tcf_dump_args *a = (void *)arg; 2439832d1d5bSWANG Cong struct net *net = sock_net(a->skb->sk); 24401da177e4SLinus Torvalds 24417960d1daSJiri Pirko return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, 2442a10fa201SJiri Pirko n, NETLINK_CB(a->cb->skb).portid, 24435a7a5555SJamal Hadi Salim a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 244412db03b6SVlad Buslov RTM_NEWTFILTER, true); 24451da177e4SLinus Torvalds } 24461da177e4SLinus Torvalds 2447a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 2448a10fa201SJiri Pirko struct sk_buff *skb, struct netlink_callback *cb, 2449acb31faeSJiri Pirko long index_start, long *p_index) 2450acb31faeSJiri Pirko { 2451acb31faeSJiri Pirko struct net *net = sock_net(skb->sk); 24527960d1daSJiri Pirko struct tcf_block *block = chain->block; 2453acb31faeSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2454fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 2455acb31faeSJiri Pirko struct tcf_dump_args arg; 2456acb31faeSJiri Pirko 2457fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); 2458fe2923afSVlad Buslov tp; 2459fe2923afSVlad Buslov tp_prev = tp, 2460fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 246112db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL), 2462fe2923afSVlad Buslov (*p_index)++) { 2463acb31faeSJiri Pirko if (*p_index < index_start) 2464acb31faeSJiri Pirko continue; 2465acb31faeSJiri Pirko if (TC_H_MAJ(tcm->tcm_info) && 2466acb31faeSJiri Pirko TC_H_MAJ(tcm->tcm_info) != tp->prio) 2467acb31faeSJiri Pirko continue; 2468acb31faeSJiri Pirko if (TC_H_MIN(tcm->tcm_info) && 2469acb31faeSJiri Pirko TC_H_MIN(tcm->tcm_info) != tp->protocol) 2470acb31faeSJiri Pirko continue; 2471acb31faeSJiri Pirko if (*p_index > index_start) 2472acb31faeSJiri Pirko memset(&cb->args[1], 0, 2473acb31faeSJiri Pirko sizeof(cb->args) - sizeof(cb->args[0])); 2474acb31faeSJiri Pirko if (cb->args[1] == 0) { 247553189183SYueHaibing if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, 2476acb31faeSJiri Pirko NETLINK_CB(cb->skb).portid, 2477acb31faeSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 247812db03b6SVlad Buslov RTM_NEWTFILTER, true) <= 0) 2479fe2923afSVlad Buslov goto errout; 2480acb31faeSJiri Pirko cb->args[1] = 1; 2481acb31faeSJiri Pirko } 2482acb31faeSJiri Pirko if (!tp->ops->walk) 2483acb31faeSJiri Pirko continue; 2484acb31faeSJiri Pirko arg.w.fn = tcf_node_dump; 2485acb31faeSJiri Pirko arg.skb = skb; 2486acb31faeSJiri Pirko arg.cb = cb; 24877960d1daSJiri Pirko arg.block = block; 2488a10fa201SJiri Pirko arg.q = q; 2489a10fa201SJiri Pirko arg.parent = parent; 2490acb31faeSJiri Pirko arg.w.stop = 0; 2491acb31faeSJiri Pirko arg.w.skip = cb->args[1] - 1; 2492acb31faeSJiri Pirko arg.w.count = 0; 249301683a14SVlad Buslov arg.w.cookie = cb->args[2]; 249412db03b6SVlad Buslov tp->ops->walk(tp, &arg.w, true); 249501683a14SVlad Buslov cb->args[2] = arg.w.cookie; 2496acb31faeSJiri Pirko cb->args[1] = arg.w.count + 1; 2497acb31faeSJiri Pirko if (arg.w.stop) 2498fe2923afSVlad Buslov goto errout; 2499acb31faeSJiri Pirko } 25005bc17018SJiri Pirko return true; 2501fe2923afSVlad Buslov 2502fe2923afSVlad Buslov errout: 250312db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 2504fe2923afSVlad Buslov return false; 2505acb31faeSJiri Pirko } 2506acb31faeSJiri Pirko 2507bd27a875SEric Dumazet /* called with RTNL */ 25081da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 25091da177e4SLinus Torvalds { 2510bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 25113b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 25125bc17018SJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 25137960d1daSJiri Pirko struct Qdisc *q = NULL; 25146529eabaSJiri Pirko struct tcf_block *block; 2515942b8165SDavid S. Miller struct tcmsg *tcm = nlmsg_data(cb->nlh); 2516acb31faeSJiri Pirko long index_start; 2517acb31faeSJiri Pirko long index; 2518a10fa201SJiri Pirko u32 parent; 25195bc17018SJiri Pirko int err; 25201da177e4SLinus Torvalds 2521573ce260SHong zhi guo if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 25221da177e4SLinus Torvalds return skb->len; 25235bc17018SJiri Pirko 25248cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 25258cb08174SJohannes Berg NULL, cb->extack); 25265bc17018SJiri Pirko if (err) 25275bc17018SJiri Pirko return err; 25285bc17018SJiri Pirko 25297960d1daSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2530787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 25317960d1daSJiri Pirko if (!block) 25327960d1daSJiri Pirko goto out; 2533d680b352SJiri Pirko /* If we work with block index, q is NULL and parent value 2534d680b352SJiri Pirko * will never be used in the following code. The check 2535d680b352SJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 2536d680b352SJiri Pirko * see that far, so set parent to zero to silence the warning 2537d680b352SJiri Pirko * about parent being uninitialized. 2538d680b352SJiri Pirko */ 2539d680b352SJiri Pirko parent = 0; 25407960d1daSJiri Pirko } else { 25417960d1daSJiri Pirko const struct Qdisc_class_ops *cops; 25427960d1daSJiri Pirko struct net_device *dev; 25437960d1daSJiri Pirko unsigned long cl = 0; 25447960d1daSJiri Pirko 2545cc7ec456SEric Dumazet dev = __dev_get_by_index(net, tcm->tcm_ifindex); 2546cc7ec456SEric Dumazet if (!dev) 25471da177e4SLinus Torvalds return skb->len; 25481da177e4SLinus Torvalds 2549a10fa201SJiri Pirko parent = tcm->tcm_parent; 2550a10fa201SJiri Pirko if (!parent) { 2551af356afaSPatrick McHardy q = dev->qdisc; 2552a10fa201SJiri Pirko parent = q->handle; 2553a10fa201SJiri Pirko } else { 25541da177e4SLinus Torvalds q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 2555a10fa201SJiri Pirko } 25561da177e4SLinus Torvalds if (!q) 25571da177e4SLinus Torvalds goto out; 2558cc7ec456SEric Dumazet cops = q->ops->cl_ops; 2559cc7ec456SEric Dumazet if (!cops) 2560143976ceSWANG Cong goto out; 25616529eabaSJiri Pirko if (!cops->tcf_block) 2562143976ceSWANG Cong goto out; 25631da177e4SLinus Torvalds if (TC_H_MIN(tcm->tcm_parent)) { 2564143976ceSWANG Cong cl = cops->find(q, tcm->tcm_parent); 25651da177e4SLinus Torvalds if (cl == 0) 2566143976ceSWANG Cong goto out; 25671da177e4SLinus Torvalds } 2568cbaacc4eSAlexander Aring block = cops->tcf_block(q, cl, NULL); 25696529eabaSJiri Pirko if (!block) 2570143976ceSWANG Cong goto out; 25717960d1daSJiri Pirko if (tcf_block_shared(block)) 25727960d1daSJiri Pirko q = NULL; 25737960d1daSJiri Pirko } 25741da177e4SLinus Torvalds 2575acb31faeSJiri Pirko index_start = cb->args[0]; 2576acb31faeSJiri Pirko index = 0; 25775bc17018SJiri Pirko 2578bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 2579bbf73830SVlad Buslov chain; 2580bbf73830SVlad Buslov chain_prev = chain, 2581bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 2582bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 25835bc17018SJiri Pirko if (tca[TCA_CHAIN] && 25845bc17018SJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index) 25855bc17018SJiri Pirko continue; 2586a10fa201SJiri Pirko if (!tcf_chain_dump(chain, q, parent, skb, cb, 25875ae437adSRoman Kapl index_start, &index)) { 2588bbf73830SVlad Buslov tcf_chain_put(chain); 25895ae437adSRoman Kapl err = -EMSGSIZE; 25905bc17018SJiri Pirko break; 25915bc17018SJiri Pirko } 25925ae437adSRoman Kapl } 25935bc17018SJiri Pirko 2594787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 259512db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 2596acb31faeSJiri Pirko cb->args[0] = index; 25971da177e4SLinus Torvalds 25981da177e4SLinus Torvalds out: 25995ae437adSRoman Kapl /* If we did no progress, the error (EMSGSIZE) is real */ 26005ae437adSRoman Kapl if (skb->len == 0 && err) 26015ae437adSRoman Kapl return err; 26021da177e4SLinus Torvalds return skb->len; 26031da177e4SLinus Torvalds } 26041da177e4SLinus Torvalds 2605a5654820SVlad Buslov static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, 2606a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2607a5654820SVlad Buslov struct net *net, struct sk_buff *skb, 2608a5654820SVlad Buslov struct tcf_block *block, 260932a4f5ecSJiri Pirko u32 portid, u32 seq, u16 flags, int event) 261032a4f5ecSJiri Pirko { 261132a4f5ecSJiri Pirko unsigned char *b = skb_tail_pointer(skb); 26129f407f17SJiri Pirko const struct tcf_proto_ops *ops; 261332a4f5ecSJiri Pirko struct nlmsghdr *nlh; 261432a4f5ecSJiri Pirko struct tcmsg *tcm; 26159f407f17SJiri Pirko void *priv; 26169f407f17SJiri Pirko 2617a5654820SVlad Buslov ops = tmplt_ops; 2618a5654820SVlad Buslov priv = tmplt_priv; 261932a4f5ecSJiri Pirko 262032a4f5ecSJiri Pirko nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 262132a4f5ecSJiri Pirko if (!nlh) 262232a4f5ecSJiri Pirko goto out_nlmsg_trim; 262332a4f5ecSJiri Pirko tcm = nlmsg_data(nlh); 262432a4f5ecSJiri Pirko tcm->tcm_family = AF_UNSPEC; 262532a4f5ecSJiri Pirko tcm->tcm__pad1 = 0; 262632a4f5ecSJiri Pirko tcm->tcm__pad2 = 0; 262732a4f5ecSJiri Pirko tcm->tcm_handle = 0; 262832a4f5ecSJiri Pirko if (block->q) { 262932a4f5ecSJiri Pirko tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; 263032a4f5ecSJiri Pirko tcm->tcm_parent = block->q->handle; 263132a4f5ecSJiri Pirko } else { 263232a4f5ecSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 263332a4f5ecSJiri Pirko tcm->tcm_block_index = block->index; 263432a4f5ecSJiri Pirko } 263532a4f5ecSJiri Pirko 2636a5654820SVlad Buslov if (nla_put_u32(skb, TCA_CHAIN, chain_index)) 263732a4f5ecSJiri Pirko goto nla_put_failure; 263832a4f5ecSJiri Pirko 26399f407f17SJiri Pirko if (ops) { 26409f407f17SJiri Pirko if (nla_put_string(skb, TCA_KIND, ops->kind)) 26419f407f17SJiri Pirko goto nla_put_failure; 26429f407f17SJiri Pirko if (ops->tmplt_dump(skb, net, priv) < 0) 26439f407f17SJiri Pirko goto nla_put_failure; 26449f407f17SJiri Pirko } 26459f407f17SJiri Pirko 264632a4f5ecSJiri Pirko nlh->nlmsg_len = skb_tail_pointer(skb) - b; 264732a4f5ecSJiri Pirko return skb->len; 264832a4f5ecSJiri Pirko 264932a4f5ecSJiri Pirko out_nlmsg_trim: 265032a4f5ecSJiri Pirko nla_put_failure: 265132a4f5ecSJiri Pirko nlmsg_trim(skb, b); 265232a4f5ecSJiri Pirko return -EMSGSIZE; 265332a4f5ecSJiri Pirko } 265432a4f5ecSJiri Pirko 265532a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 265632a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast) 265732a4f5ecSJiri Pirko { 265832a4f5ecSJiri Pirko u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 265932a4f5ecSJiri Pirko struct tcf_block *block = chain->block; 266032a4f5ecSJiri Pirko struct net *net = block->net; 266132a4f5ecSJiri Pirko struct sk_buff *skb; 26625b5f99b1SZhike Wang int err = 0; 266332a4f5ecSJiri Pirko 266432a4f5ecSJiri Pirko skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 266532a4f5ecSJiri Pirko if (!skb) 266632a4f5ecSJiri Pirko return -ENOBUFS; 266732a4f5ecSJiri Pirko 2668a5654820SVlad Buslov if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 2669a5654820SVlad Buslov chain->index, net, skb, block, portid, 267032a4f5ecSJiri Pirko seq, flags, event) <= 0) { 267132a4f5ecSJiri Pirko kfree_skb(skb); 267232a4f5ecSJiri Pirko return -EINVAL; 267332a4f5ecSJiri Pirko } 267432a4f5ecSJiri Pirko 267532a4f5ecSJiri Pirko if (unicast) 26765b5f99b1SZhike Wang err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 26775b5f99b1SZhike Wang else 26785b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 26795b5f99b1SZhike Wang flags & NLM_F_ECHO); 268032a4f5ecSJiri Pirko 26815b5f99b1SZhike Wang if (err > 0) 26825b5f99b1SZhike Wang err = 0; 26835b5f99b1SZhike Wang return err; 268432a4f5ecSJiri Pirko } 268532a4f5ecSJiri Pirko 2686a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 2687a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2688a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 2689a5654820SVlad Buslov u32 seq, u16 flags, bool unicast) 2690a5654820SVlad Buslov { 2691a5654820SVlad Buslov u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 2692a5654820SVlad Buslov struct net *net = block->net; 2693a5654820SVlad Buslov struct sk_buff *skb; 2694a5654820SVlad Buslov 2695a5654820SVlad Buslov skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 2696a5654820SVlad Buslov if (!skb) 2697a5654820SVlad Buslov return -ENOBUFS; 2698a5654820SVlad Buslov 2699a5654820SVlad Buslov if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, 2700a5654820SVlad Buslov block, portid, seq, flags, RTM_DELCHAIN) <= 0) { 2701a5654820SVlad Buslov kfree_skb(skb); 2702a5654820SVlad Buslov return -EINVAL; 2703a5654820SVlad Buslov } 2704a5654820SVlad Buslov 2705a5654820SVlad Buslov if (unicast) 2706a5654820SVlad Buslov return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 2707a5654820SVlad Buslov 2708a5654820SVlad Buslov return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); 2709a5654820SVlad Buslov } 2710a5654820SVlad Buslov 27119f407f17SJiri Pirko static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, 27129f407f17SJiri Pirko struct nlattr **tca, 27139f407f17SJiri Pirko struct netlink_ext_ack *extack) 27149f407f17SJiri Pirko { 27159f407f17SJiri Pirko const struct tcf_proto_ops *ops; 27162dd5616eSEric Dumazet char name[IFNAMSIZ]; 27179f407f17SJiri Pirko void *tmplt_priv; 27189f407f17SJiri Pirko 27199f407f17SJiri Pirko /* If kind is not set, user did not specify template. */ 27209f407f17SJiri Pirko if (!tca[TCA_KIND]) 27219f407f17SJiri Pirko return 0; 27229f407f17SJiri Pirko 27232dd5616eSEric Dumazet if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 27242dd5616eSEric Dumazet NL_SET_ERR_MSG(extack, "Specified TC chain template name too long"); 27252dd5616eSEric Dumazet return -EINVAL; 27262dd5616eSEric Dumazet } 27272dd5616eSEric Dumazet 27282dd5616eSEric Dumazet ops = tcf_proto_lookup_ops(name, true, extack); 27299f407f17SJiri Pirko if (IS_ERR(ops)) 27309f407f17SJiri Pirko return PTR_ERR(ops); 27319f407f17SJiri Pirko if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { 27329f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); 27339f407f17SJiri Pirko return -EOPNOTSUPP; 27349f407f17SJiri Pirko } 27359f407f17SJiri Pirko 27369f407f17SJiri Pirko tmplt_priv = ops->tmplt_create(net, chain, tca, extack); 27379f407f17SJiri Pirko if (IS_ERR(tmplt_priv)) { 27389f407f17SJiri Pirko module_put(ops->owner); 27399f407f17SJiri Pirko return PTR_ERR(tmplt_priv); 27409f407f17SJiri Pirko } 27419f407f17SJiri Pirko chain->tmplt_ops = ops; 27429f407f17SJiri Pirko chain->tmplt_priv = tmplt_priv; 27439f407f17SJiri Pirko return 0; 27449f407f17SJiri Pirko } 27459f407f17SJiri Pirko 2746a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 2747a5654820SVlad Buslov void *tmplt_priv) 27489f407f17SJiri Pirko { 27499f407f17SJiri Pirko /* If template ops are set, no work to do for us. */ 2750a5654820SVlad Buslov if (!tmplt_ops) 27519f407f17SJiri Pirko return; 27529f407f17SJiri Pirko 2753a5654820SVlad Buslov tmplt_ops->tmplt_destroy(tmplt_priv); 2754a5654820SVlad Buslov module_put(tmplt_ops->owner); 27559f407f17SJiri Pirko } 27569f407f17SJiri Pirko 275732a4f5ecSJiri Pirko /* Add/delete/get a chain */ 275832a4f5ecSJiri Pirko 275932a4f5ecSJiri Pirko static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, 276032a4f5ecSJiri Pirko struct netlink_ext_ack *extack) 276132a4f5ecSJiri Pirko { 276232a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 276332a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 276432a4f5ecSJiri Pirko struct tcmsg *t; 276532a4f5ecSJiri Pirko u32 parent; 276632a4f5ecSJiri Pirko u32 chain_index; 276732a4f5ecSJiri Pirko struct Qdisc *q = NULL; 276832a4f5ecSJiri Pirko struct tcf_chain *chain = NULL; 276932a4f5ecSJiri Pirko struct tcf_block *block; 277032a4f5ecSJiri Pirko unsigned long cl; 277132a4f5ecSJiri Pirko int err; 277232a4f5ecSJiri Pirko 277332a4f5ecSJiri Pirko if (n->nlmsg_type != RTM_GETCHAIN && 277432a4f5ecSJiri Pirko !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 277532a4f5ecSJiri Pirko return -EPERM; 277632a4f5ecSJiri Pirko 277732a4f5ecSJiri Pirko replay: 27788cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 27798cb08174SJohannes Berg rtm_tca_policy, extack); 278032a4f5ecSJiri Pirko if (err < 0) 278132a4f5ecSJiri Pirko return err; 278232a4f5ecSJiri Pirko 278332a4f5ecSJiri Pirko t = nlmsg_data(n); 278432a4f5ecSJiri Pirko parent = t->tcm_parent; 278532a4f5ecSJiri Pirko cl = 0; 278632a4f5ecSJiri Pirko 278732a4f5ecSJiri Pirko block = tcf_block_find(net, &q, &parent, &cl, 278832a4f5ecSJiri Pirko t->tcm_ifindex, t->tcm_block_index, extack); 278932a4f5ecSJiri Pirko if (IS_ERR(block)) 279032a4f5ecSJiri Pirko return PTR_ERR(block); 279132a4f5ecSJiri Pirko 279232a4f5ecSJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 279332a4f5ecSJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 279432a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2795e368fdb6SVlad Buslov err = -EINVAL; 2796e368fdb6SVlad Buslov goto errout_block; 279732a4f5ecSJiri Pirko } 27982cbfab07SVlad Buslov 27992cbfab07SVlad Buslov mutex_lock(&block->lock); 280032a4f5ecSJiri Pirko chain = tcf_chain_lookup(block, chain_index); 280132a4f5ecSJiri Pirko if (n->nlmsg_type == RTM_NEWCHAIN) { 280232a4f5ecSJiri Pirko if (chain) { 28033d32f4c5SJiri Pirko if (tcf_chain_held_by_acts_only(chain)) { 28041f3ed383SJiri Pirko /* The chain exists only because there is 28053d32f4c5SJiri Pirko * some action referencing it. 28061f3ed383SJiri Pirko */ 28071f3ed383SJiri Pirko tcf_chain_hold(chain); 28081f3ed383SJiri Pirko } else { 280932a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Filter chain already exists"); 2810e368fdb6SVlad Buslov err = -EEXIST; 28112cbfab07SVlad Buslov goto errout_block_locked; 281232a4f5ecSJiri Pirko } 28131f3ed383SJiri Pirko } else { 281432a4f5ecSJiri Pirko if (!(n->nlmsg_flags & NLM_F_CREATE)) { 281532a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); 2816e368fdb6SVlad Buslov err = -ENOENT; 28172cbfab07SVlad Buslov goto errout_block_locked; 281832a4f5ecSJiri Pirko } 281932a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 282032a4f5ecSJiri Pirko if (!chain) { 282132a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to create filter chain"); 2822e368fdb6SVlad Buslov err = -ENOMEM; 28232cbfab07SVlad Buslov goto errout_block_locked; 282432a4f5ecSJiri Pirko } 28251f3ed383SJiri Pirko } 282632a4f5ecSJiri Pirko } else { 28273d32f4c5SJiri Pirko if (!chain || tcf_chain_held_by_acts_only(chain)) { 282832a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2829e368fdb6SVlad Buslov err = -EINVAL; 28302cbfab07SVlad Buslov goto errout_block_locked; 283132a4f5ecSJiri Pirko } 283232a4f5ecSJiri Pirko tcf_chain_hold(chain); 283332a4f5ecSJiri Pirko } 283432a4f5ecSJiri Pirko 28352cbfab07SVlad Buslov if (n->nlmsg_type == RTM_NEWCHAIN) { 28362cbfab07SVlad Buslov /* Modifying chain requires holding parent block lock. In case 28372cbfab07SVlad Buslov * the chain was successfully added, take a reference to the 28382cbfab07SVlad Buslov * chain. This ensures that an empty chain does not disappear at 28392cbfab07SVlad Buslov * the end of this function. 284032a4f5ecSJiri Pirko */ 284132a4f5ecSJiri Pirko tcf_chain_hold(chain); 284232a4f5ecSJiri Pirko chain->explicitly_created = true; 28432cbfab07SVlad Buslov } 28442cbfab07SVlad Buslov mutex_unlock(&block->lock); 28452cbfab07SVlad Buslov 28462cbfab07SVlad Buslov switch (n->nlmsg_type) { 28472cbfab07SVlad Buslov case RTM_NEWCHAIN: 28482cbfab07SVlad Buslov err = tc_chain_tmplt_add(chain, net, tca, extack); 28492cbfab07SVlad Buslov if (err) { 28502cbfab07SVlad Buslov tcf_chain_put_explicitly_created(chain); 28512cbfab07SVlad Buslov goto errout; 28522cbfab07SVlad Buslov } 28532cbfab07SVlad Buslov 285432a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 285532a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 285632a4f5ecSJiri Pirko break; 285732a4f5ecSJiri Pirko case RTM_DELCHAIN: 2858f5b9bac7SCong Wang tfilter_notify_chain(net, skb, block, q, parent, n, 285912db03b6SVlad Buslov chain, RTM_DELTFILTER, true); 286032a4f5ecSJiri Pirko /* Flush the chain first as the user requested chain removal. */ 286112db03b6SVlad Buslov tcf_chain_flush(chain, true); 286232a4f5ecSJiri Pirko /* In case the chain was successfully deleted, put a reference 286332a4f5ecSJiri Pirko * to the chain previously taken during addition. 286432a4f5ecSJiri Pirko */ 286532a4f5ecSJiri Pirko tcf_chain_put_explicitly_created(chain); 286632a4f5ecSJiri Pirko break; 286732a4f5ecSJiri Pirko case RTM_GETCHAIN: 286832a4f5ecSJiri Pirko err = tc_chain_notify(chain, skb, n->nlmsg_seq, 286932a4f5ecSJiri Pirko n->nlmsg_seq, n->nlmsg_type, true); 287032a4f5ecSJiri Pirko if (err < 0) 287132a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); 287232a4f5ecSJiri Pirko break; 287332a4f5ecSJiri Pirko default: 287432a4f5ecSJiri Pirko err = -EOPNOTSUPP; 287532a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Unsupported message type"); 287632a4f5ecSJiri Pirko goto errout; 287732a4f5ecSJiri Pirko } 287832a4f5ecSJiri Pirko 287932a4f5ecSJiri Pirko errout: 288032a4f5ecSJiri Pirko tcf_chain_put(chain); 2881e368fdb6SVlad Buslov errout_block: 288212db03b6SVlad Buslov tcf_block_release(q, block, true); 288332a4f5ecSJiri Pirko if (err == -EAGAIN) 288432a4f5ecSJiri Pirko /* Replay the request. */ 288532a4f5ecSJiri Pirko goto replay; 288632a4f5ecSJiri Pirko return err; 28872cbfab07SVlad Buslov 28882cbfab07SVlad Buslov errout_block_locked: 28892cbfab07SVlad Buslov mutex_unlock(&block->lock); 28902cbfab07SVlad Buslov goto errout_block; 289132a4f5ecSJiri Pirko } 289232a4f5ecSJiri Pirko 289332a4f5ecSJiri Pirko /* called with RTNL */ 289432a4f5ecSJiri Pirko static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) 289532a4f5ecSJiri Pirko { 289632a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 289732a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 289832a4f5ecSJiri Pirko struct Qdisc *q = NULL; 289932a4f5ecSJiri Pirko struct tcf_block *block; 290032a4f5ecSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2901ace4a267SVlad Buslov struct tcf_chain *chain; 290232a4f5ecSJiri Pirko long index_start; 290332a4f5ecSJiri Pirko long index; 290432a4f5ecSJiri Pirko u32 parent; 290532a4f5ecSJiri Pirko int err; 290632a4f5ecSJiri Pirko 290732a4f5ecSJiri Pirko if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 290832a4f5ecSJiri Pirko return skb->len; 290932a4f5ecSJiri Pirko 29108cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 29118cb08174SJohannes Berg rtm_tca_policy, cb->extack); 291232a4f5ecSJiri Pirko if (err) 291332a4f5ecSJiri Pirko return err; 291432a4f5ecSJiri Pirko 291532a4f5ecSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2916787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 291732a4f5ecSJiri Pirko if (!block) 291832a4f5ecSJiri Pirko goto out; 291932a4f5ecSJiri Pirko /* If we work with block index, q is NULL and parent value 292032a4f5ecSJiri Pirko * will never be used in the following code. The check 292132a4f5ecSJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 292232a4f5ecSJiri Pirko * see that far, so set parent to zero to silence the warning 292332a4f5ecSJiri Pirko * about parent being uninitialized. 292432a4f5ecSJiri Pirko */ 292532a4f5ecSJiri Pirko parent = 0; 292632a4f5ecSJiri Pirko } else { 292732a4f5ecSJiri Pirko const struct Qdisc_class_ops *cops; 292832a4f5ecSJiri Pirko struct net_device *dev; 292932a4f5ecSJiri Pirko unsigned long cl = 0; 293032a4f5ecSJiri Pirko 293132a4f5ecSJiri Pirko dev = __dev_get_by_index(net, tcm->tcm_ifindex); 293232a4f5ecSJiri Pirko if (!dev) 293332a4f5ecSJiri Pirko return skb->len; 293432a4f5ecSJiri Pirko 293532a4f5ecSJiri Pirko parent = tcm->tcm_parent; 293632a4f5ecSJiri Pirko if (!parent) { 293732a4f5ecSJiri Pirko q = dev->qdisc; 293832a4f5ecSJiri Pirko parent = q->handle; 293932a4f5ecSJiri Pirko } else { 294032a4f5ecSJiri Pirko q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 294132a4f5ecSJiri Pirko } 294232a4f5ecSJiri Pirko if (!q) 294332a4f5ecSJiri Pirko goto out; 294432a4f5ecSJiri Pirko cops = q->ops->cl_ops; 294532a4f5ecSJiri Pirko if (!cops) 294632a4f5ecSJiri Pirko goto out; 294732a4f5ecSJiri Pirko if (!cops->tcf_block) 294832a4f5ecSJiri Pirko goto out; 294932a4f5ecSJiri Pirko if (TC_H_MIN(tcm->tcm_parent)) { 295032a4f5ecSJiri Pirko cl = cops->find(q, tcm->tcm_parent); 295132a4f5ecSJiri Pirko if (cl == 0) 295232a4f5ecSJiri Pirko goto out; 295332a4f5ecSJiri Pirko } 295432a4f5ecSJiri Pirko block = cops->tcf_block(q, cl, NULL); 295532a4f5ecSJiri Pirko if (!block) 295632a4f5ecSJiri Pirko goto out; 295732a4f5ecSJiri Pirko if (tcf_block_shared(block)) 295832a4f5ecSJiri Pirko q = NULL; 295932a4f5ecSJiri Pirko } 296032a4f5ecSJiri Pirko 296132a4f5ecSJiri Pirko index_start = cb->args[0]; 296232a4f5ecSJiri Pirko index = 0; 296332a4f5ecSJiri Pirko 2964ace4a267SVlad Buslov mutex_lock(&block->lock); 2965ace4a267SVlad Buslov list_for_each_entry(chain, &block->chain_list, list) { 296632a4f5ecSJiri Pirko if ((tca[TCA_CHAIN] && 296732a4f5ecSJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index)) 296832a4f5ecSJiri Pirko continue; 296932a4f5ecSJiri Pirko if (index < index_start) { 297032a4f5ecSJiri Pirko index++; 297132a4f5ecSJiri Pirko continue; 297232a4f5ecSJiri Pirko } 2973ace4a267SVlad Buslov if (tcf_chain_held_by_acts_only(chain)) 2974ace4a267SVlad Buslov continue; 2975a5654820SVlad Buslov err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 2976a5654820SVlad Buslov chain->index, net, skb, block, 297732a4f5ecSJiri Pirko NETLINK_CB(cb->skb).portid, 297832a4f5ecSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 297932a4f5ecSJiri Pirko RTM_NEWCHAIN); 2980ace4a267SVlad Buslov if (err <= 0) 298132a4f5ecSJiri Pirko break; 298232a4f5ecSJiri Pirko index++; 298332a4f5ecSJiri Pirko } 2984ace4a267SVlad Buslov mutex_unlock(&block->lock); 298532a4f5ecSJiri Pirko 2986787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 298712db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 298832a4f5ecSJiri Pirko cb->args[0] = index; 298932a4f5ecSJiri Pirko 299032a4f5ecSJiri Pirko out: 299132a4f5ecSJiri Pirko /* If we did no progress, the error (EMSGSIZE) is real */ 299232a4f5ecSJiri Pirko if (skb->len == 0 && err) 299332a4f5ecSJiri Pirko return err; 299432a4f5ecSJiri Pirko return skb->len; 299532a4f5ecSJiri Pirko } 299632a4f5ecSJiri Pirko 299718d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts) 29981da177e4SLinus Torvalds { 29991da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30003d66b89cSEric Dumazet if (exts->actions) { 300190b73b77SVlad Buslov tcf_action_destroy(exts->actions, TCA_ACT_UNBIND); 300222dc13c8SWANG Cong kfree(exts->actions); 30033d66b89cSEric Dumazet } 300422dc13c8SWANG Cong exts->nr_actions = 0; 30051da177e4SLinus Torvalds #endif 30061da177e4SLinus Torvalds } 3007aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy); 30081da177e4SLinus Torvalds 3009c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 301050a56190SAlexander Aring struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr, 3011ec6743a1SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 30121da177e4SLinus Torvalds { 30131da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30141da177e4SLinus Torvalds { 30151da177e4SLinus Torvalds struct tc_action *act; 3016d04e6990SRoman Mashak size_t attr_size = 0; 30171da177e4SLinus Torvalds 30185da57f42SWANG Cong if (exts->police && tb[exts->police]) { 30199fb9f251SJiri Pirko act = tcf_action_init_1(net, tp, tb[exts->police], 30209fb9f251SJiri Pirko rate_tlv, "police", ovr, 3021ec6743a1SVlad Buslov TCA_ACT_BIND, rtnl_held, 3022ec6743a1SVlad Buslov extack); 3023ab27cfb8SPatrick McHardy if (IS_ERR(act)) 3024ab27cfb8SPatrick McHardy return PTR_ERR(act); 30251da177e4SLinus Torvalds 302633be6271SWANG Cong act->type = exts->type = TCA_OLD_COMPAT; 302722dc13c8SWANG Cong exts->actions[0] = act; 302822dc13c8SWANG Cong exts->nr_actions = 1; 30295da57f42SWANG Cong } else if (exts->action && tb[exts->action]) { 303090b73b77SVlad Buslov int err; 303122dc13c8SWANG Cong 30329fb9f251SJiri Pirko err = tcf_action_init(net, tp, tb[exts->action], 30339fb9f251SJiri Pirko rate_tlv, NULL, ovr, TCA_ACT_BIND, 3034ec6743a1SVlad Buslov exts->actions, &attr_size, 3035ec6743a1SVlad Buslov rtnl_held, extack); 303690b73b77SVlad Buslov if (err < 0) 303733be6271SWANG Cong return err; 303890b73b77SVlad Buslov exts->nr_actions = err; 30391da177e4SLinus Torvalds } 30401da177e4SLinus Torvalds } 30411da177e4SLinus Torvalds #else 30425da57f42SWANG Cong if ((exts->action && tb[exts->action]) || 304350a56190SAlexander Aring (exts->police && tb[exts->police])) { 304450a56190SAlexander Aring NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)"); 30451da177e4SLinus Torvalds return -EOPNOTSUPP; 304650a56190SAlexander Aring } 30471da177e4SLinus Torvalds #endif 30481da177e4SLinus Torvalds 30491da177e4SLinus Torvalds return 0; 30501da177e4SLinus Torvalds } 3051aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate); 30521da177e4SLinus Torvalds 30539b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 30541da177e4SLinus Torvalds { 30551da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 305622dc13c8SWANG Cong struct tcf_exts old = *dst; 305722dc13c8SWANG Cong 30589b0d4446SJiri Pirko *dst = *src; 305922dc13c8SWANG Cong tcf_exts_destroy(&old); 30601da177e4SLinus Torvalds #endif 30611da177e4SLinus Torvalds } 3062aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change); 30631da177e4SLinus Torvalds 306422dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT 306522dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 306622dc13c8SWANG Cong { 306722dc13c8SWANG Cong if (exts->nr_actions == 0) 306822dc13c8SWANG Cong return NULL; 306922dc13c8SWANG Cong else 307022dc13c8SWANG Cong return exts->actions[0]; 307122dc13c8SWANG Cong } 307222dc13c8SWANG Cong #endif 307333be6271SWANG Cong 30745da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 30751da177e4SLinus Torvalds { 30761da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30779cc63db5SCong Wang struct nlattr *nest; 30789cc63db5SCong Wang 3079978dfd8dSJiri Pirko if (exts->action && tcf_exts_has_actions(exts)) { 30801da177e4SLinus Torvalds /* 30811da177e4SLinus Torvalds * again for backward compatible mode - we want 30821da177e4SLinus Torvalds * to work with both old and new modes of entering 30831da177e4SLinus Torvalds * tc data even if iproute2 was newer - jhs 30841da177e4SLinus Torvalds */ 308533be6271SWANG Cong if (exts->type != TCA_OLD_COMPAT) { 3086ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->action); 30874b3550efSPatrick McHardy if (nest == NULL) 30884b3550efSPatrick McHardy goto nla_put_failure; 308922dc13c8SWANG Cong 309090b73b77SVlad Buslov if (tcf_action_dump(skb, exts->actions, 0, 0) < 0) 3091add93b61SPatrick McHardy goto nla_put_failure; 30924b3550efSPatrick McHardy nla_nest_end(skb, nest); 30935da57f42SWANG Cong } else if (exts->police) { 309433be6271SWANG Cong struct tc_action *act = tcf_exts_first_act(exts); 3095ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->police); 309663acd680SJamal Hadi Salim if (nest == NULL || !act) 30974b3550efSPatrick McHardy goto nla_put_failure; 309833be6271SWANG Cong if (tcf_action_dump_old(skb, act, 0, 0) < 0) 3099add93b61SPatrick McHardy goto nla_put_failure; 31004b3550efSPatrick McHardy nla_nest_end(skb, nest); 31011da177e4SLinus Torvalds } 31021da177e4SLinus Torvalds } 31031da177e4SLinus Torvalds return 0; 31049cc63db5SCong Wang 31059cc63db5SCong Wang nla_put_failure: 31069cc63db5SCong Wang nla_nest_cancel(skb, nest); 31071da177e4SLinus Torvalds return -1; 31089cc63db5SCong Wang #else 31099cc63db5SCong Wang return 0; 31109cc63db5SCong Wang #endif 31111da177e4SLinus Torvalds } 3112aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump); 31131da177e4SLinus Torvalds 3114aa767bfeSStephen Hemminger 31155da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 31161da177e4SLinus Torvalds { 31171da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 311833be6271SWANG Cong struct tc_action *a = tcf_exts_first_act(exts); 3119b057df24SIgnacy Gawędzki if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 312033be6271SWANG Cong return -1; 31211da177e4SLinus Torvalds #endif 31221da177e4SLinus Torvalds return 0; 31231da177e4SLinus Torvalds } 3124aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats); 31251da177e4SLinus Torvalds 312640119211SVlad Buslov static void tcf_block_offload_inc(struct tcf_block *block, u32 *flags) 312740119211SVlad Buslov { 312840119211SVlad Buslov if (*flags & TCA_CLS_FLAGS_IN_HW) 312940119211SVlad Buslov return; 313040119211SVlad Buslov *flags |= TCA_CLS_FLAGS_IN_HW; 313140119211SVlad Buslov atomic_inc(&block->offloadcnt); 313240119211SVlad Buslov } 313340119211SVlad Buslov 313440119211SVlad Buslov static void tcf_block_offload_dec(struct tcf_block *block, u32 *flags) 313540119211SVlad Buslov { 313640119211SVlad Buslov if (!(*flags & TCA_CLS_FLAGS_IN_HW)) 313740119211SVlad Buslov return; 313840119211SVlad Buslov *flags &= ~TCA_CLS_FLAGS_IN_HW; 313940119211SVlad Buslov atomic_dec(&block->offloadcnt); 314040119211SVlad Buslov } 314140119211SVlad Buslov 314240119211SVlad Buslov static void tc_cls_offload_cnt_update(struct tcf_block *block, 314340119211SVlad Buslov struct tcf_proto *tp, u32 *cnt, 314440119211SVlad Buslov u32 *flags, u32 diff, bool add) 314540119211SVlad Buslov { 314640119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 314740119211SVlad Buslov 314840119211SVlad Buslov spin_lock(&tp->lock); 314940119211SVlad Buslov if (add) { 315040119211SVlad Buslov if (!*cnt) 315140119211SVlad Buslov tcf_block_offload_inc(block, flags); 315240119211SVlad Buslov *cnt += diff; 315340119211SVlad Buslov } else { 315440119211SVlad Buslov *cnt -= diff; 315540119211SVlad Buslov if (!*cnt) 315640119211SVlad Buslov tcf_block_offload_dec(block, flags); 315740119211SVlad Buslov } 315840119211SVlad Buslov spin_unlock(&tp->lock); 315940119211SVlad Buslov } 316040119211SVlad Buslov 316140119211SVlad Buslov static void 316240119211SVlad Buslov tc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp, 316340119211SVlad Buslov u32 *cnt, u32 *flags) 316440119211SVlad Buslov { 316540119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 316640119211SVlad Buslov 316740119211SVlad Buslov spin_lock(&tp->lock); 316840119211SVlad Buslov tcf_block_offload_dec(block, flags); 316940119211SVlad Buslov *cnt = 0; 317040119211SVlad Buslov spin_unlock(&tp->lock); 317140119211SVlad Buslov } 317240119211SVlad Buslov 317340119211SVlad Buslov static int 317440119211SVlad Buslov __tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 3175aeb3fecdSCong Wang void *type_data, bool err_stop) 3176717503b9SJiri Pirko { 3177955bcb6eSPablo Neira Ayuso struct flow_block_cb *block_cb; 3178aeb3fecdSCong Wang int ok_count = 0; 3179aeb3fecdSCong Wang int err; 3180aeb3fecdSCong Wang 318140119211SVlad Buslov list_for_each_entry(block_cb, &block->flow_block.cb_list, list) { 318240119211SVlad Buslov err = block_cb->cb(type, type_data, block_cb->cb_priv); 318340119211SVlad Buslov if (err) { 318440119211SVlad Buslov if (err_stop) 318540119211SVlad Buslov return err; 318640119211SVlad Buslov } else { 318740119211SVlad Buslov ok_count++; 318840119211SVlad Buslov } 318940119211SVlad Buslov } 319040119211SVlad Buslov return ok_count; 319140119211SVlad Buslov } 319240119211SVlad Buslov 319340119211SVlad Buslov int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 319440119211SVlad Buslov void *type_data, bool err_stop, bool rtnl_held) 319540119211SVlad Buslov { 319611bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 319740119211SVlad Buslov int ok_count; 319840119211SVlad Buslov 319911bd634dSVlad Buslov retry: 320011bd634dSVlad Buslov if (take_rtnl) 320111bd634dSVlad Buslov rtnl_lock(); 320240119211SVlad Buslov down_read(&block->cb_lock); 320311bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 320411bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 320511bd634dSVlad Buslov * obtain the locks in same order here. 320611bd634dSVlad Buslov */ 320711bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 320840119211SVlad Buslov up_read(&block->cb_lock); 320911bd634dSVlad Buslov take_rtnl = true; 321011bd634dSVlad Buslov goto retry; 321111bd634dSVlad Buslov } 321211bd634dSVlad Buslov 321311bd634dSVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 321411bd634dSVlad Buslov 321511bd634dSVlad Buslov up_read(&block->cb_lock); 321611bd634dSVlad Buslov if (take_rtnl) 321711bd634dSVlad Buslov rtnl_unlock(); 321840119211SVlad Buslov return ok_count; 321940119211SVlad Buslov } 322040119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_call); 322140119211SVlad Buslov 322240119211SVlad Buslov /* Non-destructive filter add. If filter that wasn't already in hardware is 322340119211SVlad Buslov * successfully offloaded, increment block offloads counter. On failure, 322440119211SVlad Buslov * previously offloaded filter is considered to be intact and offloads counter 322540119211SVlad Buslov * is not decremented. 322640119211SVlad Buslov */ 322740119211SVlad Buslov 322840119211SVlad Buslov int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp, 322940119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 323040119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 323140119211SVlad Buslov { 323211bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 323340119211SVlad Buslov int ok_count; 323440119211SVlad Buslov 323511bd634dSVlad Buslov retry: 323611bd634dSVlad Buslov if (take_rtnl) 323711bd634dSVlad Buslov rtnl_lock(); 32384f8116c8SVlad Buslov down_read(&block->cb_lock); 323911bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 324011bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 324111bd634dSVlad Buslov * obtain the locks in same order here. 324211bd634dSVlad Buslov */ 324311bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 324411bd634dSVlad Buslov up_read(&block->cb_lock); 324511bd634dSVlad Buslov take_rtnl = true; 324611bd634dSVlad Buslov goto retry; 324711bd634dSVlad Buslov } 324811bd634dSVlad Buslov 3249aeb3fecdSCong Wang /* Make sure all netdevs sharing this block are offload-capable. */ 32504f8116c8SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 32514f8116c8SVlad Buslov ok_count = -EOPNOTSUPP; 32524f8116c8SVlad Buslov goto err_unlock; 32534f8116c8SVlad Buslov } 3254aeb3fecdSCong Wang 325540119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3256a449a3e7SVlad Buslov if (ok_count < 0) 3257a449a3e7SVlad Buslov goto err_unlock; 3258a449a3e7SVlad Buslov 3259a449a3e7SVlad Buslov if (tp->ops->hw_add) 3260a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 326140119211SVlad Buslov if (ok_count > 0) 326240119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 326340119211SVlad Buslov ok_count, true); 32644f8116c8SVlad Buslov err_unlock: 32654f8116c8SVlad Buslov up_read(&block->cb_lock); 326611bd634dSVlad Buslov if (take_rtnl) 326711bd634dSVlad Buslov rtnl_unlock(); 326840119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 3269717503b9SJiri Pirko } 327040119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_add); 327140119211SVlad Buslov 327240119211SVlad Buslov /* Destructive filter replace. If filter that wasn't already in hardware is 327340119211SVlad Buslov * successfully offloaded, increment block offload counter. On failure, 327440119211SVlad Buslov * previously offloaded filter is considered to be destroyed and offload counter 327540119211SVlad Buslov * is decremented. 327640119211SVlad Buslov */ 327740119211SVlad Buslov 327840119211SVlad Buslov int tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp, 327940119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 328040119211SVlad Buslov u32 *old_flags, unsigned int *old_in_hw_count, 328140119211SVlad Buslov u32 *new_flags, unsigned int *new_in_hw_count, 328240119211SVlad Buslov bool rtnl_held) 328340119211SVlad Buslov { 328411bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 328540119211SVlad Buslov int ok_count; 328640119211SVlad Buslov 328711bd634dSVlad Buslov retry: 328811bd634dSVlad Buslov if (take_rtnl) 328911bd634dSVlad Buslov rtnl_lock(); 329040119211SVlad Buslov down_read(&block->cb_lock); 329111bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 329211bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 329311bd634dSVlad Buslov * obtain the locks in same order here. 329411bd634dSVlad Buslov */ 329511bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 329611bd634dSVlad Buslov up_read(&block->cb_lock); 329711bd634dSVlad Buslov take_rtnl = true; 329811bd634dSVlad Buslov goto retry; 329911bd634dSVlad Buslov } 330011bd634dSVlad Buslov 330140119211SVlad Buslov /* Make sure all netdevs sharing this block are offload-capable. */ 330240119211SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 330340119211SVlad Buslov ok_count = -EOPNOTSUPP; 330440119211SVlad Buslov goto err_unlock; 330540119211SVlad Buslov } 330640119211SVlad Buslov 330740119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags); 3308a449a3e7SVlad Buslov if (tp->ops->hw_del) 3309a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 331040119211SVlad Buslov 331140119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3312a449a3e7SVlad Buslov if (ok_count < 0) 3313a449a3e7SVlad Buslov goto err_unlock; 3314a449a3e7SVlad Buslov 3315a449a3e7SVlad Buslov if (tp->ops->hw_add) 3316a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 331740119211SVlad Buslov if (ok_count > 0) 3318a449a3e7SVlad Buslov tc_cls_offload_cnt_update(block, tp, new_in_hw_count, 3319a449a3e7SVlad Buslov new_flags, ok_count, true); 332040119211SVlad Buslov err_unlock: 332140119211SVlad Buslov up_read(&block->cb_lock); 332211bd634dSVlad Buslov if (take_rtnl) 332311bd634dSVlad Buslov rtnl_unlock(); 332440119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 332540119211SVlad Buslov } 332640119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_replace); 332740119211SVlad Buslov 332840119211SVlad Buslov /* Destroy filter and decrement block offload counter, if filter was previously 332940119211SVlad Buslov * offloaded. 333040119211SVlad Buslov */ 333140119211SVlad Buslov 333240119211SVlad Buslov int tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp, 333340119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 333440119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 333540119211SVlad Buslov { 333611bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 333740119211SVlad Buslov int ok_count; 333840119211SVlad Buslov 333911bd634dSVlad Buslov retry: 334011bd634dSVlad Buslov if (take_rtnl) 334111bd634dSVlad Buslov rtnl_lock(); 334240119211SVlad Buslov down_read(&block->cb_lock); 334311bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 334411bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 334511bd634dSVlad Buslov * obtain the locks in same order here. 334611bd634dSVlad Buslov */ 334711bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 334811bd634dSVlad Buslov up_read(&block->cb_lock); 334911bd634dSVlad Buslov take_rtnl = true; 335011bd634dSVlad Buslov goto retry; 335111bd634dSVlad Buslov } 335211bd634dSVlad Buslov 335340119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 335440119211SVlad Buslov 335540119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags); 3356a449a3e7SVlad Buslov if (tp->ops->hw_del) 3357a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 3358a449a3e7SVlad Buslov 335940119211SVlad Buslov up_read(&block->cb_lock); 336011bd634dSVlad Buslov if (take_rtnl) 336111bd634dSVlad Buslov rtnl_unlock(); 336240119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 336340119211SVlad Buslov } 336440119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_destroy); 336540119211SVlad Buslov 336640119211SVlad Buslov int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp, 336740119211SVlad Buslov bool add, flow_setup_cb_t *cb, 336840119211SVlad Buslov enum tc_setup_type type, void *type_data, 336940119211SVlad Buslov void *cb_priv, u32 *flags, unsigned int *in_hw_count) 337040119211SVlad Buslov { 337140119211SVlad Buslov int err = cb(type, type_data, cb_priv); 337240119211SVlad Buslov 337340119211SVlad Buslov if (err) { 337440119211SVlad Buslov if (add && tc_skip_sw(*flags)) 337540119211SVlad Buslov return err; 337640119211SVlad Buslov } else { 337740119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1, 337840119211SVlad Buslov add); 337940119211SVlad Buslov } 338040119211SVlad Buslov 338140119211SVlad Buslov return 0; 338240119211SVlad Buslov } 338340119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_reoffload); 3384b3f55bddSJiri Pirko 33855a6ff4b1SVlad Buslov void tc_cleanup_flow_action(struct flow_action *flow_action) 33865a6ff4b1SVlad Buslov { 33875a6ff4b1SVlad Buslov struct flow_action_entry *entry; 33885a6ff4b1SVlad Buslov int i; 33895a6ff4b1SVlad Buslov 33901158958aSVlad Buslov flow_action_for_each(i, entry, flow_action) 33911158958aSVlad Buslov if (entry->destructor) 33921158958aSVlad Buslov entry->destructor(entry->destructor_priv); 33935a6ff4b1SVlad Buslov } 33945a6ff4b1SVlad Buslov EXPORT_SYMBOL(tc_cleanup_flow_action); 33955a6ff4b1SVlad Buslov 33961158958aSVlad Buslov static void tcf_mirred_get_dev(struct flow_action_entry *entry, 33971158958aSVlad Buslov const struct tc_action *act) 33981158958aSVlad Buslov { 3399470d5060SVlad Buslov #ifdef CONFIG_NET_CLS_ACT 3400470d5060SVlad Buslov entry->dev = act->ops->get_dev(act, &entry->destructor); 34011158958aSVlad Buslov if (!entry->dev) 34021158958aSVlad Buslov return; 34031158958aSVlad Buslov entry->destructor_priv = entry->dev; 3404470d5060SVlad Buslov #endif 34051158958aSVlad Buslov } 34061158958aSVlad Buslov 34071158958aSVlad Buslov static void tcf_tunnel_encap_put_tunnel(void *priv) 34081158958aSVlad Buslov { 34091158958aSVlad Buslov struct ip_tunnel_info *tunnel = priv; 34101158958aSVlad Buslov 34111158958aSVlad Buslov kfree(tunnel); 34121158958aSVlad Buslov } 34131158958aSVlad Buslov 34141158958aSVlad Buslov static int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry, 34151158958aSVlad Buslov const struct tc_action *act) 34161158958aSVlad Buslov { 34171158958aSVlad Buslov entry->tunnel = tcf_tunnel_info_copy(act); 34181158958aSVlad Buslov if (!entry->tunnel) 34191158958aSVlad Buslov return -ENOMEM; 34201158958aSVlad Buslov entry->destructor = tcf_tunnel_encap_put_tunnel; 34211158958aSVlad Buslov entry->destructor_priv = entry->tunnel; 34221158958aSVlad Buslov return 0; 34231158958aSVlad Buslov } 34241158958aSVlad Buslov 34254a5da47dSVlad Buslov static void tcf_sample_get_group(struct flow_action_entry *entry, 34264a5da47dSVlad Buslov const struct tc_action *act) 34274a5da47dSVlad Buslov { 34284a5da47dSVlad Buslov #ifdef CONFIG_NET_CLS_ACT 34294a5da47dSVlad Buslov entry->sample.psample_group = 34304a5da47dSVlad Buslov act->ops->get_psample_group(act, &entry->destructor); 34314a5da47dSVlad Buslov entry->destructor_priv = entry->sample.psample_group; 34324a5da47dSVlad Buslov #endif 34334a5da47dSVlad Buslov } 34344a5da47dSVlad Buslov 34353a7b6861SPablo Neira Ayuso int tc_setup_flow_action(struct flow_action *flow_action, 34369838b20aSVlad Buslov const struct tcf_exts *exts, bool rtnl_held) 34373a7b6861SPablo Neira Ayuso { 3438*7a472814SVlad Buslov struct tc_action *act; 34399838b20aSVlad Buslov int i, j, k, err = 0; 34403a7b6861SPablo Neira Ayuso 34413a7b6861SPablo Neira Ayuso if (!exts) 34423a7b6861SPablo Neira Ayuso return 0; 34433a7b6861SPablo Neira Ayuso 34449838b20aSVlad Buslov if (!rtnl_held) 34459838b20aSVlad Buslov rtnl_lock(); 34469838b20aSVlad Buslov 34473a7b6861SPablo Neira Ayuso j = 0; 34483a7b6861SPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 34493a7b6861SPablo Neira Ayuso struct flow_action_entry *entry; 34503a7b6861SPablo Neira Ayuso 34513a7b6861SPablo Neira Ayuso entry = &flow_action->entries[j]; 3452*7a472814SVlad Buslov spin_lock_bh(&act->tcfa_lock); 34533a7b6861SPablo Neira Ayuso if (is_tcf_gact_ok(act)) { 34543a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ACCEPT; 34553a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_shot(act)) { 34563a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_DROP; 34573a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_trap(act)) { 34583a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TRAP; 34593a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_goto_chain(act)) { 34603a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_GOTO; 34613a7b6861SPablo Neira Ayuso entry->chain_index = tcf_gact_goto_chain_index(act); 34623a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_redirect(act)) { 34633a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_REDIRECT; 34641158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 34653a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_mirror(act)) { 34663a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MIRRED; 34671158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 346848e584acSJohn Hurley } else if (is_tcf_mirred_ingress_redirect(act)) { 346948e584acSJohn Hurley entry->id = FLOW_ACTION_REDIRECT_INGRESS; 34701158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 347148e584acSJohn Hurley } else if (is_tcf_mirred_ingress_mirror(act)) { 347248e584acSJohn Hurley entry->id = FLOW_ACTION_MIRRED_INGRESS; 34731158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 34743a7b6861SPablo Neira Ayuso } else if (is_tcf_vlan(act)) { 34753a7b6861SPablo Neira Ayuso switch (tcf_vlan_action(act)) { 34763a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_PUSH: 34773a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_PUSH; 34783a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 34793a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 34803a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 34813a7b6861SPablo Neira Ayuso break; 34823a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_POP: 34833a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_POP; 34843a7b6861SPablo Neira Ayuso break; 34853a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_MODIFY: 34863a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_MANGLE; 34873a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 34883a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 34893a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 34903a7b6861SPablo Neira Ayuso break; 34913a7b6861SPablo Neira Ayuso default: 34929838b20aSVlad Buslov err = -EOPNOTSUPP; 3493*7a472814SVlad Buslov goto err_out_locked; 34943a7b6861SPablo Neira Ayuso } 34953a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_set(act)) { 34963a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_ENCAP; 34971158958aSVlad Buslov err = tcf_tunnel_encap_get_tunnel(entry, act); 34981158958aSVlad Buslov if (err) 3499*7a472814SVlad Buslov goto err_out_locked; 35003a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_release(act)) { 35013a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_DECAP; 35023a7b6861SPablo Neira Ayuso } else if (is_tcf_pedit(act)) { 35033a7b6861SPablo Neira Ayuso for (k = 0; k < tcf_pedit_nkeys(act); k++) { 35043a7b6861SPablo Neira Ayuso switch (tcf_pedit_cmd(act, k)) { 35053a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_SET: 35063a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MANGLE; 35073a7b6861SPablo Neira Ayuso break; 35083a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_ADD: 35093a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ADD; 35103a7b6861SPablo Neira Ayuso break; 35113a7b6861SPablo Neira Ayuso default: 35129838b20aSVlad Buslov err = -EOPNOTSUPP; 3513*7a472814SVlad Buslov goto err_out_locked; 35143a7b6861SPablo Neira Ayuso } 35153a7b6861SPablo Neira Ayuso entry->mangle.htype = tcf_pedit_htype(act, k); 35163a7b6861SPablo Neira Ayuso entry->mangle.mask = tcf_pedit_mask(act, k); 35173a7b6861SPablo Neira Ayuso entry->mangle.val = tcf_pedit_val(act, k); 35183a7b6861SPablo Neira Ayuso entry->mangle.offset = tcf_pedit_offset(act, k); 35193a7b6861SPablo Neira Ayuso entry = &flow_action->entries[++j]; 35203a7b6861SPablo Neira Ayuso } 35213a7b6861SPablo Neira Ayuso } else if (is_tcf_csum(act)) { 35223a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_CSUM; 35233a7b6861SPablo Neira Ayuso entry->csum_flags = tcf_csum_update_flags(act); 35243a7b6861SPablo Neira Ayuso } else if (is_tcf_skbedit_mark(act)) { 35253a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MARK; 35263a7b6861SPablo Neira Ayuso entry->mark = tcf_skbedit_mark(act); 3527a7a7be60SPieter Jansen van Vuuren } else if (is_tcf_sample(act)) { 3528a7a7be60SPieter Jansen van Vuuren entry->id = FLOW_ACTION_SAMPLE; 3529a7a7be60SPieter Jansen van Vuuren entry->sample.trunc_size = tcf_sample_trunc_size(act); 3530a7a7be60SPieter Jansen van Vuuren entry->sample.truncate = tcf_sample_truncate(act); 3531a7a7be60SPieter Jansen van Vuuren entry->sample.rate = tcf_sample_rate(act); 35324a5da47dSVlad Buslov tcf_sample_get_group(entry, act); 35338c8cfc6eSPieter Jansen van Vuuren } else if (is_tcf_police(act)) { 35348c8cfc6eSPieter Jansen van Vuuren entry->id = FLOW_ACTION_POLICE; 35358c8cfc6eSPieter Jansen van Vuuren entry->police.burst = tcf_police_tcfp_burst(act); 35368c8cfc6eSPieter Jansen van Vuuren entry->police.rate_bytes_ps = 35378c8cfc6eSPieter Jansen van Vuuren tcf_police_rate_bytes_ps(act); 3538b57dc7c1SPaul Blakey } else if (is_tcf_ct(act)) { 3539b57dc7c1SPaul Blakey entry->id = FLOW_ACTION_CT; 3540b57dc7c1SPaul Blakey entry->ct.action = tcf_ct_action(act); 3541b57dc7c1SPaul Blakey entry->ct.zone = tcf_ct_zone(act); 35426749d590SJohn Hurley } else if (is_tcf_mpls(act)) { 35436749d590SJohn Hurley switch (tcf_mpls_action(act)) { 35446749d590SJohn Hurley case TCA_MPLS_ACT_PUSH: 35456749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_PUSH; 35466749d590SJohn Hurley entry->mpls_push.proto = tcf_mpls_proto(act); 35476749d590SJohn Hurley entry->mpls_push.label = tcf_mpls_label(act); 35486749d590SJohn Hurley entry->mpls_push.tc = tcf_mpls_tc(act); 35496749d590SJohn Hurley entry->mpls_push.bos = tcf_mpls_bos(act); 35506749d590SJohn Hurley entry->mpls_push.ttl = tcf_mpls_ttl(act); 35516749d590SJohn Hurley break; 35526749d590SJohn Hurley case TCA_MPLS_ACT_POP: 35536749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_POP; 35546749d590SJohn Hurley entry->mpls_pop.proto = tcf_mpls_proto(act); 35556749d590SJohn Hurley break; 35566749d590SJohn Hurley case TCA_MPLS_ACT_MODIFY: 35576749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_MANGLE; 35586749d590SJohn Hurley entry->mpls_mangle.label = tcf_mpls_label(act); 35596749d590SJohn Hurley entry->mpls_mangle.tc = tcf_mpls_tc(act); 35606749d590SJohn Hurley entry->mpls_mangle.bos = tcf_mpls_bos(act); 35616749d590SJohn Hurley entry->mpls_mangle.ttl = tcf_mpls_ttl(act); 35626749d590SJohn Hurley break; 35636749d590SJohn Hurley default: 3564*7a472814SVlad Buslov goto err_out_locked; 35656749d590SJohn Hurley } 3566fb1b775aSJohn Hurley } else if (is_tcf_skbedit_ptype(act)) { 3567fb1b775aSJohn Hurley entry->id = FLOW_ACTION_PTYPE; 3568fb1b775aSJohn Hurley entry->ptype = tcf_skbedit_ptype(act); 35693a7b6861SPablo Neira Ayuso } else { 35709838b20aSVlad Buslov err = -EOPNOTSUPP; 3571*7a472814SVlad Buslov goto err_out_locked; 35723a7b6861SPablo Neira Ayuso } 3573*7a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 35743a7b6861SPablo Neira Ayuso 35753a7b6861SPablo Neira Ayuso if (!is_tcf_pedit(act)) 35763a7b6861SPablo Neira Ayuso j++; 35773a7b6861SPablo Neira Ayuso } 35789838b20aSVlad Buslov 35793a7b6861SPablo Neira Ayuso err_out: 35809838b20aSVlad Buslov if (!rtnl_held) 35819838b20aSVlad Buslov rtnl_unlock(); 35829838b20aSVlad Buslov 35835a6ff4b1SVlad Buslov if (err) 35845a6ff4b1SVlad Buslov tc_cleanup_flow_action(flow_action); 35855a6ff4b1SVlad Buslov 35869838b20aSVlad Buslov return err; 3587*7a472814SVlad Buslov err_out_locked: 3588*7a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 3589*7a472814SVlad Buslov goto err_out; 35903a7b6861SPablo Neira Ayuso } 35913a7b6861SPablo Neira Ayuso EXPORT_SYMBOL(tc_setup_flow_action); 35923a7b6861SPablo Neira Ayuso 3593e3ab786bSPablo Neira Ayuso unsigned int tcf_exts_num_actions(struct tcf_exts *exts) 3594e3ab786bSPablo Neira Ayuso { 3595e3ab786bSPablo Neira Ayuso unsigned int num_acts = 0; 3596e3ab786bSPablo Neira Ayuso struct tc_action *act; 3597e3ab786bSPablo Neira Ayuso int i; 3598e3ab786bSPablo Neira Ayuso 3599e3ab786bSPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 3600e3ab786bSPablo Neira Ayuso if (is_tcf_pedit(act)) 3601e3ab786bSPablo Neira Ayuso num_acts += tcf_pedit_nkeys(act); 3602e3ab786bSPablo Neira Ayuso else 3603e3ab786bSPablo Neira Ayuso num_acts++; 3604e3ab786bSPablo Neira Ayuso } 3605e3ab786bSPablo Neira Ayuso return num_acts; 3606e3ab786bSPablo Neira Ayuso } 3607e3ab786bSPablo Neira Ayuso EXPORT_SYMBOL(tcf_exts_num_actions); 3608e3ab786bSPablo Neira Ayuso 360948617387SJiri Pirko static __net_init int tcf_net_init(struct net *net) 361048617387SJiri Pirko { 361148617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 361248617387SJiri Pirko 3613ab281629SVlad Buslov spin_lock_init(&tn->idr_lock); 361448617387SJiri Pirko idr_init(&tn->idr); 361548617387SJiri Pirko return 0; 361648617387SJiri Pirko } 361748617387SJiri Pirko 361848617387SJiri Pirko static void __net_exit tcf_net_exit(struct net *net) 361948617387SJiri Pirko { 362048617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 362148617387SJiri Pirko 362248617387SJiri Pirko idr_destroy(&tn->idr); 362348617387SJiri Pirko } 362448617387SJiri Pirko 362548617387SJiri Pirko static struct pernet_operations tcf_net_ops = { 362648617387SJiri Pirko .init = tcf_net_init, 362748617387SJiri Pirko .exit = tcf_net_exit, 362848617387SJiri Pirko .id = &tcf_net_id, 362948617387SJiri Pirko .size = sizeof(struct tcf_net), 363048617387SJiri Pirko }; 363148617387SJiri Pirko 363225a443f7SJohn Hurley static struct flow_indr_block_entry block_entry = { 363325a443f7SJohn Hurley .cb = tc_indr_block_get_and_cmd, 363425a443f7SJohn Hurley .list = LIST_HEAD_INIT(block_entry.list), 36351150ab0fSwenxu }; 36361150ab0fSwenxu 36371da177e4SLinus Torvalds static int __init tc_filter_init(void) 36381da177e4SLinus Torvalds { 363948617387SJiri Pirko int err; 364048617387SJiri Pirko 36417aa0045dSCong Wang tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 36427aa0045dSCong Wang if (!tc_filter_wq) 36437aa0045dSCong Wang return -ENOMEM; 36447aa0045dSCong Wang 364548617387SJiri Pirko err = register_pernet_subsys(&tcf_net_ops); 364648617387SJiri Pirko if (err) 364748617387SJiri Pirko goto err_register_pernet_subsys; 364848617387SJiri Pirko 364925a443f7SJohn Hurley flow_indr_add_block_cb(&block_entry); 36501150ab0fSwenxu 3651470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 3652470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3653470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 3654470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3655c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 3656470502deSVlad Buslov tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED); 365732a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0); 365832a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0); 365932a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain, 366032a4f5ecSJiri Pirko tc_dump_chain, 0); 36611da177e4SLinus Torvalds 36621da177e4SLinus Torvalds return 0; 366348617387SJiri Pirko 366448617387SJiri Pirko err_register_pernet_subsys: 366548617387SJiri Pirko destroy_workqueue(tc_filter_wq); 366648617387SJiri Pirko return err; 36671da177e4SLinus Torvalds } 36681da177e4SLinus Torvalds 36691da177e4SLinus Torvalds subsys_initcall(tc_filter_init); 3670