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> 2359eb87cbSJohn Hurley #include <linux/jhash.h> 2443719298SPaul Blakey #include <linux/rculist.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> 41d29bdd69SPo Liu #include <net/tc_act/tc_gate.h> 424e481908Swenxu #include <net/flow_offload.h> 431da177e4SLinus Torvalds 44e331473fSDavide Caratti extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; 45e331473fSDavide Caratti 461da177e4SLinus Torvalds /* The list of all installed classifier types */ 4736272874SWANG Cong static LIST_HEAD(tcf_proto_base); 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */ 501da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock); 511da177e4SLinus Torvalds 5259eb87cbSJohn Hurley static u32 destroy_obj_hashfn(const struct tcf_proto *tp) 5359eb87cbSJohn Hurley { 5459eb87cbSJohn Hurley return jhash_3words(tp->chain->index, tp->prio, 5559eb87cbSJohn Hurley (__force __u32)tp->protocol, 0); 5659eb87cbSJohn Hurley } 5759eb87cbSJohn Hurley 5859eb87cbSJohn Hurley static void tcf_proto_signal_destroying(struct tcf_chain *chain, 5959eb87cbSJohn Hurley struct tcf_proto *tp) 6059eb87cbSJohn Hurley { 6159eb87cbSJohn Hurley struct tcf_block *block = chain->block; 6259eb87cbSJohn Hurley 6359eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 6459eb87cbSJohn Hurley hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node, 6559eb87cbSJohn Hurley destroy_obj_hashfn(tp)); 6659eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 6759eb87cbSJohn Hurley } 6859eb87cbSJohn Hurley 6959eb87cbSJohn Hurley static bool tcf_proto_cmp(const struct tcf_proto *tp1, 7059eb87cbSJohn Hurley const struct tcf_proto *tp2) 7159eb87cbSJohn Hurley { 7259eb87cbSJohn Hurley return tp1->chain->index == tp2->chain->index && 7359eb87cbSJohn Hurley tp1->prio == tp2->prio && 7459eb87cbSJohn Hurley tp1->protocol == tp2->protocol; 7559eb87cbSJohn Hurley } 7659eb87cbSJohn Hurley 7759eb87cbSJohn Hurley static bool tcf_proto_exists_destroying(struct tcf_chain *chain, 7859eb87cbSJohn Hurley struct tcf_proto *tp) 7959eb87cbSJohn Hurley { 8059eb87cbSJohn Hurley u32 hash = destroy_obj_hashfn(tp); 8159eb87cbSJohn Hurley struct tcf_proto *iter; 8259eb87cbSJohn Hurley bool found = false; 8359eb87cbSJohn Hurley 8459eb87cbSJohn Hurley rcu_read_lock(); 8559eb87cbSJohn Hurley hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter, 8659eb87cbSJohn Hurley destroy_ht_node, hash) { 8759eb87cbSJohn Hurley if (tcf_proto_cmp(tp, iter)) { 8859eb87cbSJohn Hurley found = true; 8959eb87cbSJohn Hurley break; 9059eb87cbSJohn Hurley } 9159eb87cbSJohn Hurley } 9259eb87cbSJohn Hurley rcu_read_unlock(); 9359eb87cbSJohn Hurley 9459eb87cbSJohn Hurley return found; 9559eb87cbSJohn Hurley } 9659eb87cbSJohn Hurley 9759eb87cbSJohn Hurley static void 9859eb87cbSJohn Hurley tcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp) 9959eb87cbSJohn Hurley { 10059eb87cbSJohn Hurley struct tcf_block *block = chain->block; 10159eb87cbSJohn Hurley 10259eb87cbSJohn Hurley mutex_lock(&block->proto_destroy_lock); 10359eb87cbSJohn Hurley if (hash_hashed(&tp->destroy_ht_node)) 10459eb87cbSJohn Hurley hash_del_rcu(&tp->destroy_ht_node); 10559eb87cbSJohn Hurley mutex_unlock(&block->proto_destroy_lock); 10659eb87cbSJohn Hurley } 10759eb87cbSJohn Hurley 1081da177e4SLinus Torvalds /* Find classifier type by string name */ 1091da177e4SLinus Torvalds 110f34e8bffSJiri Pirko static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind) 1111da177e4SLinus Torvalds { 112dcd76081SEric Dumazet const struct tcf_proto_ops *t, *res = NULL; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds if (kind) { 1151da177e4SLinus Torvalds read_lock(&cls_mod_lock); 11636272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) { 11733a48927SJiri Pirko if (strcmp(kind, t->kind) == 0) { 118dcd76081SEric Dumazet if (try_module_get(t->owner)) 119dcd76081SEric Dumazet res = t; 1201da177e4SLinus Torvalds break; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds read_unlock(&cls_mod_lock); 1241da177e4SLinus Torvalds } 125dcd76081SEric Dumazet return res; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 128f34e8bffSJiri Pirko static const struct tcf_proto_ops * 12912db03b6SVlad Buslov tcf_proto_lookup_ops(const char *kind, bool rtnl_held, 13012db03b6SVlad Buslov struct netlink_ext_ack *extack) 131f34e8bffSJiri Pirko { 132f34e8bffSJiri Pirko const struct tcf_proto_ops *ops; 133f34e8bffSJiri Pirko 134f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 135f34e8bffSJiri Pirko if (ops) 136f34e8bffSJiri Pirko return ops; 137f34e8bffSJiri Pirko #ifdef CONFIG_MODULES 13812db03b6SVlad Buslov if (rtnl_held) 139f34e8bffSJiri Pirko rtnl_unlock(); 140f34e8bffSJiri Pirko request_module("cls_%s", kind); 14112db03b6SVlad Buslov if (rtnl_held) 142f34e8bffSJiri Pirko rtnl_lock(); 143f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 144f34e8bffSJiri Pirko /* We dropped the RTNL semaphore in order to perform 145f34e8bffSJiri Pirko * the module load. So, even if we succeeded in loading 146f34e8bffSJiri Pirko * the module we have to replay the request. We indicate 147f34e8bffSJiri Pirko * this using -EAGAIN. 148f34e8bffSJiri Pirko */ 149f34e8bffSJiri Pirko if (ops) { 150f34e8bffSJiri Pirko module_put(ops->owner); 151f34e8bffSJiri Pirko return ERR_PTR(-EAGAIN); 152f34e8bffSJiri Pirko } 153f34e8bffSJiri Pirko #endif 154f34e8bffSJiri Pirko NL_SET_ERR_MSG(extack, "TC classifier not found"); 155f34e8bffSJiri Pirko return ERR_PTR(-ENOENT); 156f34e8bffSJiri Pirko } 157f34e8bffSJiri Pirko 1581da177e4SLinus Torvalds /* Register(unregister) new classifier type */ 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops) 1611da177e4SLinus Torvalds { 16236272874SWANG Cong struct tcf_proto_ops *t; 1631da177e4SLinus Torvalds int rc = -EEXIST; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds write_lock(&cls_mod_lock); 16636272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) 1671da177e4SLinus Torvalds if (!strcmp(ops->kind, t->kind)) 1681da177e4SLinus Torvalds goto out; 1691da177e4SLinus Torvalds 17036272874SWANG Cong list_add_tail(&ops->head, &tcf_proto_base); 1711da177e4SLinus Torvalds rc = 0; 1721da177e4SLinus Torvalds out: 1731da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1741da177e4SLinus Torvalds return rc; 1751da177e4SLinus Torvalds } 176aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops); 1771da177e4SLinus Torvalds 1787aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq; 1797aa0045dSCong Wang 1801da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 1811da177e4SLinus Torvalds { 18236272874SWANG Cong struct tcf_proto_ops *t; 1831da177e4SLinus Torvalds int rc = -ENOENT; 1841da177e4SLinus Torvalds 185c78e1746SDaniel Borkmann /* Wait for outstanding call_rcu()s, if any, from a 186c78e1746SDaniel Borkmann * tcf_proto_ops's destroy() handler. 187c78e1746SDaniel Borkmann */ 188c78e1746SDaniel Borkmann rcu_barrier(); 1897aa0045dSCong Wang flush_workqueue(tc_filter_wq); 190c78e1746SDaniel Borkmann 1911da177e4SLinus Torvalds write_lock(&cls_mod_lock); 192dcd76081SEric Dumazet list_for_each_entry(t, &tcf_proto_base, head) { 193dcd76081SEric Dumazet if (t == ops) { 19436272874SWANG Cong list_del(&t->head); 1951da177e4SLinus Torvalds rc = 0; 196dcd76081SEric Dumazet break; 197dcd76081SEric Dumazet } 198dcd76081SEric Dumazet } 1991da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 2001da177e4SLinus Torvalds return rc; 2011da177e4SLinus Torvalds } 202aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops); 2031da177e4SLinus Torvalds 204aaa908ffSCong Wang bool tcf_queue_work(struct rcu_work *rwork, work_func_t func) 2057aa0045dSCong Wang { 206aaa908ffSCong Wang INIT_RCU_WORK(rwork, func); 207aaa908ffSCong Wang return queue_rcu_work(tc_filter_wq, rwork); 2087aa0045dSCong Wang } 2097aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work); 2107aa0045dSCong Wang 2111da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */ 2121da177e4SLinus Torvalds 213aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp) 2141da177e4SLinus Torvalds { 2151da177e4SLinus Torvalds u32 first = TC_H_MAKE(0xC0000000U, 0U); 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds if (tp) 2181da177e4SLinus Torvalds first = tp->prio - 1; 2191da177e4SLinus Torvalds 2207961973aSJiri Pirko return TC_H_MAJ(first); 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds 2236f96c3c6SCong Wang static bool tcf_proto_check_kind(struct nlattr *kind, char *name) 2246f96c3c6SCong Wang { 2256f96c3c6SCong Wang if (kind) 226872f6903SFrancis Laniel return nla_strscpy(name, kind, IFNAMSIZ) < 0; 2276f96c3c6SCong Wang memset(name, 0, IFNAMSIZ); 2286f96c3c6SCong Wang return false; 2296f96c3c6SCong Wang } 2306f96c3c6SCong Wang 231470502deSVlad Buslov static bool tcf_proto_is_unlocked(const char *kind) 232470502deSVlad Buslov { 233470502deSVlad Buslov const struct tcf_proto_ops *ops; 234470502deSVlad Buslov bool ret; 235470502deSVlad Buslov 2366f96c3c6SCong Wang if (strlen(kind) == 0) 2376f96c3c6SCong Wang return false; 2386f96c3c6SCong Wang 239470502deSVlad Buslov ops = tcf_proto_lookup_ops(kind, false, NULL); 240470502deSVlad Buslov /* On error return false to take rtnl lock. Proto lookup/create 241470502deSVlad Buslov * functions will perform lookup again and properly handle errors. 242470502deSVlad Buslov */ 243470502deSVlad Buslov if (IS_ERR(ops)) 244470502deSVlad Buslov return false; 245470502deSVlad Buslov 246470502deSVlad Buslov ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); 247470502deSVlad Buslov module_put(ops->owner); 248470502deSVlad Buslov return ret; 249470502deSVlad Buslov } 250470502deSVlad Buslov 25133a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 252c35a4accSAlexander Aring u32 prio, struct tcf_chain *chain, 25312db03b6SVlad Buslov bool rtnl_held, 254c35a4accSAlexander Aring struct netlink_ext_ack *extack) 25533a48927SJiri Pirko { 25633a48927SJiri Pirko struct tcf_proto *tp; 25733a48927SJiri Pirko int err; 25833a48927SJiri Pirko 25933a48927SJiri Pirko tp = kzalloc(sizeof(*tp), GFP_KERNEL); 26033a48927SJiri Pirko if (!tp) 26133a48927SJiri Pirko return ERR_PTR(-ENOBUFS); 26233a48927SJiri Pirko 26312db03b6SVlad Buslov tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack); 264f34e8bffSJiri Pirko if (IS_ERR(tp->ops)) { 265f34e8bffSJiri Pirko err = PTR_ERR(tp->ops); 266d68d75fdSJiri Pirko goto errout; 26733a48927SJiri Pirko } 26833a48927SJiri Pirko tp->classify = tp->ops->classify; 26933a48927SJiri Pirko tp->protocol = protocol; 27033a48927SJiri Pirko tp->prio = prio; 2715bc17018SJiri Pirko tp->chain = chain; 2728b64678eSVlad Buslov spin_lock_init(&tp->lock); 2734dbfa766SVlad Buslov refcount_set(&tp->refcnt, 1); 27433a48927SJiri Pirko 27533a48927SJiri Pirko err = tp->ops->init(tp); 27633a48927SJiri Pirko if (err) { 27733a48927SJiri Pirko module_put(tp->ops->owner); 27833a48927SJiri Pirko goto errout; 27933a48927SJiri Pirko } 28033a48927SJiri Pirko return tp; 28133a48927SJiri Pirko 28233a48927SJiri Pirko errout: 28333a48927SJiri Pirko kfree(tp); 28433a48927SJiri Pirko return ERR_PTR(err); 28533a48927SJiri Pirko } 28633a48927SJiri Pirko 2874dbfa766SVlad Buslov static void tcf_proto_get(struct tcf_proto *tp) 2884dbfa766SVlad Buslov { 2894dbfa766SVlad Buslov refcount_inc(&tp->refcnt); 2904dbfa766SVlad Buslov } 2914dbfa766SVlad Buslov 2924dbfa766SVlad Buslov static void tcf_chain_put(struct tcf_chain *chain); 2934dbfa766SVlad Buslov 29412db03b6SVlad Buslov static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, 29559eb87cbSJohn Hurley bool sig_destroy, struct netlink_ext_ack *extack) 296cf1facdaSJiri Pirko { 29712db03b6SVlad Buslov tp->ops->destroy(tp, rtnl_held, extack); 29859eb87cbSJohn Hurley if (sig_destroy) 29959eb87cbSJohn Hurley tcf_proto_signal_destroyed(tp->chain, tp); 3004dbfa766SVlad Buslov tcf_chain_put(tp->chain); 301cf1facdaSJiri Pirko module_put(tp->ops->owner); 302cf1facdaSJiri Pirko kfree_rcu(tp, rcu); 303cf1facdaSJiri Pirko } 304cf1facdaSJiri Pirko 30512db03b6SVlad Buslov static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held, 3064dbfa766SVlad Buslov struct netlink_ext_ack *extack) 3074dbfa766SVlad Buslov { 3084dbfa766SVlad Buslov if (refcount_dec_and_test(&tp->refcnt)) 30959eb87cbSJohn Hurley tcf_proto_destroy(tp, rtnl_held, true, extack); 3104dbfa766SVlad Buslov } 3114dbfa766SVlad Buslov 312a5b72a08SDavide Caratti static bool tcf_proto_check_delete(struct tcf_proto *tp) 3138b64678eSVlad Buslov { 314a5b72a08SDavide Caratti if (tp->ops->delete_empty) 315a5b72a08SDavide Caratti return tp->ops->delete_empty(tp); 3168b64678eSVlad Buslov 3178b64678eSVlad Buslov tp->deleting = true; 3188b64678eSVlad Buslov return tp->deleting; 3198b64678eSVlad Buslov } 3208b64678eSVlad Buslov 3218b64678eSVlad Buslov static void tcf_proto_mark_delete(struct tcf_proto *tp) 3228b64678eSVlad Buslov { 3238b64678eSVlad Buslov spin_lock(&tp->lock); 3248b64678eSVlad Buslov tp->deleting = true; 3258b64678eSVlad Buslov spin_unlock(&tp->lock); 3268b64678eSVlad Buslov } 3278b64678eSVlad Buslov 3288b64678eSVlad Buslov static bool tcf_proto_is_deleting(struct tcf_proto *tp) 3298b64678eSVlad Buslov { 3308b64678eSVlad Buslov bool deleting; 3318b64678eSVlad Buslov 3328b64678eSVlad Buslov spin_lock(&tp->lock); 3338b64678eSVlad Buslov deleting = tp->deleting; 3348b64678eSVlad Buslov spin_unlock(&tp->lock); 3358b64678eSVlad Buslov 3368b64678eSVlad Buslov return deleting; 3378b64678eSVlad Buslov } 3388b64678eSVlad Buslov 339c266f64dSVlad Buslov #define ASSERT_BLOCK_LOCKED(block) \ 340c266f64dSVlad Buslov lockdep_assert_held(&(block)->lock) 341c266f64dSVlad Buslov 342a9b19443SJiri Pirko struct tcf_filter_chain_list_item { 343a9b19443SJiri Pirko struct list_head list; 344a9b19443SJiri Pirko tcf_chain_head_change_t *chain_head_change; 345a9b19443SJiri Pirko void *chain_head_change_priv; 346a9b19443SJiri Pirko }; 347a9b19443SJiri Pirko 3485bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block, 3495bc17018SJiri Pirko u32 chain_index) 3502190d1d0SJiri Pirko { 3515bc17018SJiri Pirko struct tcf_chain *chain; 3525bc17018SJiri Pirko 353c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 354c266f64dSVlad Buslov 3555bc17018SJiri Pirko chain = kzalloc(sizeof(*chain), GFP_KERNEL); 3565bc17018SJiri Pirko if (!chain) 3575bc17018SJiri Pirko return NULL; 35843719298SPaul Blakey list_add_tail_rcu(&chain->list, &block->chain_list); 359ed76f5edSVlad Buslov mutex_init(&chain->filter_chain_lock); 3605bc17018SJiri Pirko chain->block = block; 3615bc17018SJiri Pirko chain->index = chain_index; 362e2ef7544SCong Wang chain->refcnt = 1; 363f71e0ca4SJiri Pirko if (!chain->index) 364f71e0ca4SJiri Pirko block->chain0.chain = chain; 3655bc17018SJiri Pirko return chain; 3662190d1d0SJiri Pirko } 3672190d1d0SJiri Pirko 368a9b19443SJiri Pirko static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, 369a9b19443SJiri Pirko struct tcf_proto *tp_head) 370a9b19443SJiri Pirko { 371a9b19443SJiri Pirko if (item->chain_head_change) 372a9b19443SJiri Pirko item->chain_head_change(tp_head, item->chain_head_change_priv); 373a9b19443SJiri Pirko } 374f71e0ca4SJiri Pirko 375f71e0ca4SJiri Pirko static void tcf_chain0_head_change(struct tcf_chain *chain, 376c7eb7d72SJiri Pirko struct tcf_proto *tp_head) 377c7eb7d72SJiri Pirko { 378a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 379f71e0ca4SJiri Pirko struct tcf_block *block = chain->block; 380a9b19443SJiri Pirko 381f71e0ca4SJiri Pirko if (chain->index) 382f71e0ca4SJiri Pirko return; 383165f0135SVlad Buslov 384165f0135SVlad Buslov mutex_lock(&block->lock); 385f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) 386a9b19443SJiri Pirko tcf_chain_head_change_item(item, tp_head); 387165f0135SVlad Buslov mutex_unlock(&block->lock); 388c7eb7d72SJiri Pirko } 389c7eb7d72SJiri Pirko 390c266f64dSVlad Buslov /* Returns true if block can be safely freed. */ 391c266f64dSVlad Buslov 392c266f64dSVlad Buslov static bool tcf_chain_detach(struct tcf_chain *chain) 393f93e1cdcSJiri Pirko { 394efbf7897SCong Wang struct tcf_block *block = chain->block; 395efbf7897SCong Wang 396c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 397c266f64dSVlad Buslov 39843719298SPaul Blakey list_del_rcu(&chain->list); 399f71e0ca4SJiri Pirko if (!chain->index) 400f71e0ca4SJiri Pirko block->chain0.chain = NULL; 401c266f64dSVlad Buslov 402c266f64dSVlad Buslov if (list_empty(&block->chain_list) && 403c266f64dSVlad Buslov refcount_read(&block->refcnt) == 0) 404c266f64dSVlad Buslov return true; 405c266f64dSVlad Buslov 406c266f64dSVlad Buslov return false; 407c266f64dSVlad Buslov } 408c266f64dSVlad Buslov 409c266f64dSVlad Buslov static void tcf_block_destroy(struct tcf_block *block) 410c266f64dSVlad Buslov { 411c266f64dSVlad Buslov mutex_destroy(&block->lock); 41259eb87cbSJohn Hurley mutex_destroy(&block->proto_destroy_lock); 4130607e439SVlad Buslov kfree_rcu(block, rcu); 4142190d1d0SJiri Pirko } 4152190d1d0SJiri Pirko 416c266f64dSVlad Buslov static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block) 417c266f64dSVlad Buslov { 418c266f64dSVlad Buslov struct tcf_block *block = chain->block; 419c266f64dSVlad Buslov 420ed76f5edSVlad Buslov mutex_destroy(&chain->filter_chain_lock); 421ee3bbfe8SDavide Caratti kfree_rcu(chain, rcu); 422c266f64dSVlad Buslov if (free_block) 423c266f64dSVlad Buslov tcf_block_destroy(block); 424c266f64dSVlad Buslov } 425c266f64dSVlad Buslov 426e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain) 427e2ef7544SCong Wang { 428c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 429c266f64dSVlad Buslov 430e2ef7544SCong Wang ++chain->refcnt; 431e2ef7544SCong Wang } 432e2ef7544SCong Wang 4333d32f4c5SJiri Pirko static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain) 4341f3ed383SJiri Pirko { 435c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(chain->block); 436c266f64dSVlad Buslov 4371f3ed383SJiri Pirko /* In case all the references are action references, this 4383d32f4c5SJiri Pirko * chain should not be shown to the user. 4391f3ed383SJiri Pirko */ 4401f3ed383SJiri Pirko return chain->refcnt == chain->action_refcnt; 4411f3ed383SJiri Pirko } 4421f3ed383SJiri Pirko 44332a4f5ecSJiri Pirko static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, 44432a4f5ecSJiri Pirko u32 chain_index) 4455bc17018SJiri Pirko { 4465bc17018SJiri Pirko struct tcf_chain *chain; 4475bc17018SJiri Pirko 448c266f64dSVlad Buslov ASSERT_BLOCK_LOCKED(block); 449c266f64dSVlad Buslov 4505bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 45132a4f5ecSJiri Pirko if (chain->index == chain_index) 45232a4f5ecSJiri Pirko return chain; 45332a4f5ecSJiri Pirko } 45432a4f5ecSJiri Pirko return NULL; 45532a4f5ecSJiri Pirko } 45632a4f5ecSJiri Pirko 457af699626SPaul Blakey #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 458af699626SPaul Blakey static struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block, 459af699626SPaul Blakey u32 chain_index) 460af699626SPaul Blakey { 461af699626SPaul Blakey struct tcf_chain *chain; 462af699626SPaul Blakey 463af699626SPaul Blakey list_for_each_entry_rcu(chain, &block->chain_list, list) { 464af699626SPaul Blakey if (chain->index == chain_index) 465af699626SPaul Blakey return chain; 466af699626SPaul Blakey } 467af699626SPaul Blakey return NULL; 468af699626SPaul Blakey } 469af699626SPaul Blakey #endif 470af699626SPaul Blakey 47132a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 47232a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast); 47332a4f5ecSJiri Pirko 47453681407SJiri Pirko static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, 47553681407SJiri Pirko u32 chain_index, bool create, 47653681407SJiri Pirko bool by_act) 47732a4f5ecSJiri Pirko { 478c266f64dSVlad Buslov struct tcf_chain *chain = NULL; 479c266f64dSVlad Buslov bool is_first_reference; 48032a4f5ecSJiri Pirko 481c266f64dSVlad Buslov mutex_lock(&block->lock); 482c266f64dSVlad Buslov chain = tcf_chain_lookup(block, chain_index); 48332a4f5ecSJiri Pirko if (chain) { 484e2ef7544SCong Wang tcf_chain_hold(chain); 48553681407SJiri Pirko } else { 48632a4f5ecSJiri Pirko if (!create) 487c266f64dSVlad Buslov goto errout; 48832a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 48932a4f5ecSJiri Pirko if (!chain) 490c266f64dSVlad Buslov goto errout; 49153681407SJiri Pirko } 49253681407SJiri Pirko 49353681407SJiri Pirko if (by_act) 49453681407SJiri Pirko ++chain->action_refcnt; 495c266f64dSVlad Buslov is_first_reference = chain->refcnt - chain->action_refcnt == 1; 496c266f64dSVlad Buslov mutex_unlock(&block->lock); 49753681407SJiri Pirko 49853681407SJiri Pirko /* Send notification only in case we got the first 49953681407SJiri Pirko * non-action reference. Until then, the chain acts only as 50053681407SJiri Pirko * a placeholder for actions pointing to it and user ought 50153681407SJiri Pirko * not know about them. 50253681407SJiri Pirko */ 503c266f64dSVlad Buslov if (is_first_reference && !by_act) 50432a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 50532a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 50653681407SJiri Pirko 50732a4f5ecSJiri Pirko return chain; 508c266f64dSVlad Buslov 509c266f64dSVlad Buslov errout: 510c266f64dSVlad Buslov mutex_unlock(&block->lock); 511c266f64dSVlad Buslov return chain; 512e2ef7544SCong Wang } 51353681407SJiri Pirko 514290b1c8bSJiri Pirko static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 51553681407SJiri Pirko bool create) 51653681407SJiri Pirko { 51753681407SJiri Pirko return __tcf_chain_get(block, chain_index, create, false); 51853681407SJiri Pirko } 5195bc17018SJiri Pirko 5201f3ed383SJiri Pirko struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index) 5211f3ed383SJiri Pirko { 52253681407SJiri Pirko return __tcf_chain_get(block, chain_index, true, true); 5231f3ed383SJiri Pirko } 5241f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_get_by_act); 5251f3ed383SJiri Pirko 526a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 527a5654820SVlad Buslov void *tmplt_priv); 528a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 529a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 530a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 531a5654820SVlad Buslov u32 seq, u16 flags, bool unicast); 5329f407f17SJiri Pirko 53391052fa1SVlad Buslov static void __tcf_chain_put(struct tcf_chain *chain, bool by_act, 53491052fa1SVlad Buslov bool explicitly_created) 5355bc17018SJiri Pirko { 536c266f64dSVlad Buslov struct tcf_block *block = chain->block; 537a5654820SVlad Buslov const struct tcf_proto_ops *tmplt_ops; 538b62989fcSVlad Buslov bool free_block = false; 539c266f64dSVlad Buslov unsigned int refcnt; 540a5654820SVlad Buslov void *tmplt_priv; 541c266f64dSVlad Buslov 542c266f64dSVlad Buslov mutex_lock(&block->lock); 54391052fa1SVlad Buslov if (explicitly_created) { 54491052fa1SVlad Buslov if (!chain->explicitly_created) { 54591052fa1SVlad Buslov mutex_unlock(&block->lock); 54691052fa1SVlad Buslov return; 54791052fa1SVlad Buslov } 54891052fa1SVlad Buslov chain->explicitly_created = false; 54991052fa1SVlad Buslov } 55091052fa1SVlad Buslov 55153681407SJiri Pirko if (by_act) 55253681407SJiri Pirko chain->action_refcnt--; 553c266f64dSVlad Buslov 554c266f64dSVlad Buslov /* tc_chain_notify_delete can't be called while holding block lock. 555c266f64dSVlad Buslov * However, when block is unlocked chain can be changed concurrently, so 556c266f64dSVlad Buslov * save these to temporary variables. 557c266f64dSVlad Buslov */ 558c266f64dSVlad Buslov refcnt = --chain->refcnt; 559a5654820SVlad Buslov tmplt_ops = chain->tmplt_ops; 560a5654820SVlad Buslov tmplt_priv = chain->tmplt_priv; 56153681407SJiri Pirko 56253681407SJiri Pirko /* The last dropped non-action reference will trigger notification. */ 563b62989fcSVlad Buslov if (refcnt - chain->action_refcnt == 0 && !by_act) { 564b62989fcSVlad Buslov tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index, 565a5654820SVlad Buslov block, NULL, 0, 0, false); 566726d0612SVlad Buslov /* Last reference to chain, no need to lock. */ 567726d0612SVlad Buslov chain->flushing = false; 568726d0612SVlad Buslov } 56953681407SJiri Pirko 570b62989fcSVlad Buslov if (refcnt == 0) 571b62989fcSVlad Buslov free_block = tcf_chain_detach(chain); 572b62989fcSVlad Buslov mutex_unlock(&block->lock); 573b62989fcSVlad Buslov 574c266f64dSVlad Buslov if (refcnt == 0) { 575a5654820SVlad Buslov tc_chain_tmplt_del(tmplt_ops, tmplt_priv); 576c266f64dSVlad Buslov tcf_chain_destroy(chain, free_block); 5775bc17018SJiri Pirko } 57832a4f5ecSJiri Pirko } 57953681407SJiri Pirko 580290b1c8bSJiri Pirko static void tcf_chain_put(struct tcf_chain *chain) 58153681407SJiri Pirko { 58291052fa1SVlad Buslov __tcf_chain_put(chain, false, false); 58353681407SJiri Pirko } 5845bc17018SJiri Pirko 5851f3ed383SJiri Pirko void tcf_chain_put_by_act(struct tcf_chain *chain) 5861f3ed383SJiri Pirko { 58791052fa1SVlad Buslov __tcf_chain_put(chain, true, false); 5881f3ed383SJiri Pirko } 5891f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_put_by_act); 5901f3ed383SJiri Pirko 59132a4f5ecSJiri Pirko static void tcf_chain_put_explicitly_created(struct tcf_chain *chain) 59232a4f5ecSJiri Pirko { 59391052fa1SVlad Buslov __tcf_chain_put(chain, false, true); 59432a4f5ecSJiri Pirko } 59532a4f5ecSJiri Pirko 59612db03b6SVlad Buslov static void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held) 597290b1c8bSJiri Pirko { 5984dbfa766SVlad Buslov struct tcf_proto *tp, *tp_next; 599290b1c8bSJiri Pirko 600ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 601ed76f5edSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 60259eb87cbSJohn Hurley while (tp) { 60359eb87cbSJohn Hurley tp_next = rcu_dereference_protected(tp->next, 1); 60459eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 60559eb87cbSJohn Hurley tp = tp_next; 60659eb87cbSJohn Hurley } 60759eb87cbSJohn Hurley tp = tcf_chain_dereference(chain->filter_chain, chain); 6084dbfa766SVlad Buslov RCU_INIT_POINTER(chain->filter_chain, NULL); 609290b1c8bSJiri Pirko tcf_chain0_head_change(chain, NULL); 610726d0612SVlad Buslov chain->flushing = true; 611ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 612ed76f5edSVlad Buslov 613290b1c8bSJiri Pirko while (tp) { 6144dbfa766SVlad Buslov tp_next = rcu_dereference_protected(tp->next, 1); 61512db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 6164dbfa766SVlad Buslov tp = tp_next; 617290b1c8bSJiri Pirko } 618290b1c8bSJiri Pirko } 619290b1c8bSJiri Pirko 6204e481908Swenxu static int tcf_block_setup(struct tcf_block *block, 6214e481908Swenxu struct flow_block_offload *bo); 6224e481908Swenxu 623324a823bSPablo Neira Ayuso static void tcf_block_offload_init(struct flow_block_offload *bo, 624c40f4e50SPetr Machata struct net_device *dev, struct Qdisc *sch, 625324a823bSPablo Neira Ayuso enum flow_block_command command, 626324a823bSPablo Neira Ayuso enum flow_block_binder_type binder_type, 627324a823bSPablo Neira Ayuso struct flow_block *flow_block, 628324a823bSPablo Neira Ayuso bool shared, struct netlink_ext_ack *extack) 629324a823bSPablo Neira Ayuso { 630324a823bSPablo Neira Ayuso bo->net = dev_net(dev); 631324a823bSPablo Neira Ayuso bo->command = command; 632324a823bSPablo Neira Ayuso bo->binder_type = binder_type; 633324a823bSPablo Neira Ayuso bo->block = flow_block; 634324a823bSPablo Neira Ayuso bo->block_shared = shared; 635324a823bSPablo Neira Ayuso bo->extack = extack; 636c40f4e50SPetr Machata bo->sch = sch; 637324a823bSPablo Neira Ayuso INIT_LIST_HEAD(&bo->cb_list); 638324a823bSPablo Neira Ayuso } 639324a823bSPablo Neira Ayuso 6400fdcf78dSPablo Neira Ayuso static void tcf_block_unbind(struct tcf_block *block, 6410fdcf78dSPablo Neira Ayuso struct flow_block_offload *bo); 6427f76fa36SJohn Hurley 6430fdcf78dSPablo Neira Ayuso static void tc_block_indr_cleanup(struct flow_block_cb *block_cb) 6440fdcf78dSPablo Neira Ayuso { 6450fdcf78dSPablo Neira Ayuso struct tcf_block *block = block_cb->indr.data; 6460fdcf78dSPablo Neira Ayuso struct net_device *dev = block_cb->indr.dev; 647c40f4e50SPetr Machata struct Qdisc *sch = block_cb->indr.sch; 6480fdcf78dSPablo Neira Ayuso struct netlink_ext_ack extack = {}; 649990b03b0SYunjian Wang struct flow_block_offload bo = {}; 6500fdcf78dSPablo Neira Ayuso 651c40f4e50SPetr Machata tcf_block_offload_init(&bo, dev, sch, FLOW_BLOCK_UNBIND, 6520fdcf78dSPablo Neira Ayuso block_cb->indr.binder_type, 6530fdcf78dSPablo Neira Ayuso &block->flow_block, tcf_block_shared(block), 6540fdcf78dSPablo Neira Ayuso &extack); 655d6535dcaSLeon Romanovsky rtnl_lock(); 6560fdcf78dSPablo Neira Ayuso down_write(&block->cb_lock); 657a1db2178Swenxu list_del(&block_cb->driver_list); 6580fdcf78dSPablo Neira Ayuso list_move(&block_cb->list, &bo.cb_list); 6590fdcf78dSPablo Neira Ayuso tcf_block_unbind(block, &bo); 660d6535dcaSLeon Romanovsky up_write(&block->cb_lock); 6610fdcf78dSPablo Neira Ayuso rtnl_unlock(); 6627f76fa36SJohn Hurley } 6637f76fa36SJohn Hurley 664caa72601SJiri Pirko static bool tcf_block_offload_in_use(struct tcf_block *block) 665caa72601SJiri Pirko { 66697394befSVlad Buslov return atomic_read(&block->offloadcnt); 667caa72601SJiri Pirko } 668caa72601SJiri Pirko 669caa72601SJiri Pirko static int tcf_block_offload_cmd(struct tcf_block *block, 670c40f4e50SPetr Machata struct net_device *dev, struct Qdisc *sch, 6718c4083b3SJiri Pirko struct tcf_block_ext_info *ei, 6729c0e189eSPablo Neira Ayuso enum flow_block_command command, 67360513bd8SJohn Hurley struct netlink_ext_ack *extack) 6748c4083b3SJiri Pirko { 675955bcb6eSPablo Neira Ayuso struct flow_block_offload bo = {}; 6768c4083b3SJiri Pirko 677c40f4e50SPetr Machata tcf_block_offload_init(&bo, dev, sch, command, ei->binder_type, 678324a823bSPablo Neira Ayuso &block->flow_block, tcf_block_shared(block), 679324a823bSPablo Neira Ayuso extack); 68059094b1eSPablo Neira Ayuso 6813c005110Swenxu if (dev->netdev_ops->ndo_setup_tc) { 6823c005110Swenxu int err; 6830fdcf78dSPablo Neira Ayuso 6843c005110Swenxu err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 685b70ba69eSJesper Dangaard Brouer if (err < 0) { 686b70ba69eSJesper Dangaard Brouer if (err != -EOPNOTSUPP) 687b70ba69eSJesper Dangaard Brouer NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed"); 68859094b1eSPablo Neira Ayuso return err; 689b70ba69eSJesper Dangaard Brouer } 69059094b1eSPablo Neira Ayuso 69159094b1eSPablo Neira Ayuso return tcf_block_setup(block, &bo); 6928c4083b3SJiri Pirko } 6938c4083b3SJiri Pirko 694c40f4e50SPetr Machata flow_indr_dev_setup_offload(dev, sch, TC_SETUP_BLOCK, block, &bo, 6953c005110Swenxu tc_block_indr_cleanup); 6963c005110Swenxu tcf_block_setup(block, &bo); 6973c005110Swenxu 6983c005110Swenxu return -EOPNOTSUPP; 6993c005110Swenxu } 7003c005110Swenxu 701caa72601SJiri Pirko static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 70260513bd8SJohn Hurley struct tcf_block_ext_info *ei, 70360513bd8SJohn Hurley struct netlink_ext_ack *extack) 7048c4083b3SJiri Pirko { 705caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 706caa72601SJiri Pirko int err; 707caa72601SJiri Pirko 7084f8116c8SVlad Buslov down_write(&block->cb_lock); 709caa72601SJiri Pirko 710caa72601SJiri Pirko /* If tc offload feature is disabled and the block we try to bind 711caa72601SJiri Pirko * to already has some offloaded filters, forbid to bind. 712caa72601SJiri Pirko */ 7130fdcf78dSPablo Neira Ayuso if (dev->netdev_ops->ndo_setup_tc && 7140fdcf78dSPablo Neira Ayuso !tc_can_offload(dev) && 7150fdcf78dSPablo Neira Ayuso tcf_block_offload_in_use(block)) { 71660513bd8SJohn Hurley NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled"); 7174f8116c8SVlad Buslov err = -EOPNOTSUPP; 7184f8116c8SVlad Buslov goto err_unlock; 71960513bd8SJohn Hurley } 720caa72601SJiri Pirko 721c40f4e50SPetr Machata err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_BIND, extack); 722caa72601SJiri Pirko if (err == -EOPNOTSUPP) 723caa72601SJiri Pirko goto no_offload_dev_inc; 7247f76fa36SJohn Hurley if (err) 7254f8116c8SVlad Buslov goto err_unlock; 726caa72601SJiri Pirko 7274f8116c8SVlad Buslov up_write(&block->cb_lock); 7287f76fa36SJohn Hurley return 0; 7297f76fa36SJohn Hurley 730caa72601SJiri Pirko no_offload_dev_inc: 7310fdcf78dSPablo Neira Ayuso if (tcf_block_offload_in_use(block)) 7324f8116c8SVlad Buslov goto err_unlock; 7330fdcf78dSPablo Neira Ayuso 7344f8116c8SVlad Buslov err = 0; 735caa72601SJiri Pirko block->nooffloaddevcnt++; 7364f8116c8SVlad Buslov err_unlock: 7374f8116c8SVlad Buslov up_write(&block->cb_lock); 7384f8116c8SVlad Buslov return err; 7398c4083b3SJiri Pirko } 7408c4083b3SJiri Pirko 7418c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 7428c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 7438c4083b3SJiri Pirko { 744caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 745caa72601SJiri Pirko int err; 746caa72601SJiri Pirko 7474f8116c8SVlad Buslov down_write(&block->cb_lock); 748c40f4e50SPetr Machata err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_UNBIND, NULL); 749caa72601SJiri Pirko if (err == -EOPNOTSUPP) 750caa72601SJiri Pirko goto no_offload_dev_dec; 7514f8116c8SVlad Buslov up_write(&block->cb_lock); 752caa72601SJiri Pirko return; 753caa72601SJiri Pirko 754caa72601SJiri Pirko no_offload_dev_dec: 755caa72601SJiri Pirko WARN_ON(block->nooffloaddevcnt-- == 0); 7564f8116c8SVlad Buslov up_write(&block->cb_lock); 7578c4083b3SJiri Pirko } 7588c4083b3SJiri Pirko 759a9b19443SJiri Pirko static int 760f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_add(struct tcf_block *block, 761a9b19443SJiri Pirko struct tcf_block_ext_info *ei, 762a9b19443SJiri Pirko struct netlink_ext_ack *extack) 763a9b19443SJiri Pirko { 764a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 765165f0135SVlad Buslov struct tcf_chain *chain0; 766a9b19443SJiri Pirko 767a9b19443SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 768a9b19443SJiri Pirko if (!item) { 769a9b19443SJiri Pirko NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed"); 770a9b19443SJiri Pirko return -ENOMEM; 771a9b19443SJiri Pirko } 772a9b19443SJiri Pirko item->chain_head_change = ei->chain_head_change; 773a9b19443SJiri Pirko item->chain_head_change_priv = ei->chain_head_change_priv; 774165f0135SVlad Buslov 775165f0135SVlad Buslov mutex_lock(&block->lock); 776165f0135SVlad Buslov chain0 = block->chain0.chain; 777ed76f5edSVlad Buslov if (chain0) 778ed76f5edSVlad Buslov tcf_chain_hold(chain0); 779ed76f5edSVlad Buslov else 780f71e0ca4SJiri Pirko list_add(&item->list, &block->chain0.filter_chain_list); 781165f0135SVlad Buslov mutex_unlock(&block->lock); 782165f0135SVlad Buslov 783ed76f5edSVlad Buslov if (chain0) { 784ed76f5edSVlad Buslov struct tcf_proto *tp_head; 785ed76f5edSVlad Buslov 786ed76f5edSVlad Buslov mutex_lock(&chain0->filter_chain_lock); 787ed76f5edSVlad Buslov 788ed76f5edSVlad Buslov tp_head = tcf_chain_dereference(chain0->filter_chain, chain0); 789ed76f5edSVlad Buslov if (tp_head) 790ed76f5edSVlad Buslov tcf_chain_head_change_item(item, tp_head); 791ed76f5edSVlad Buslov 792ed76f5edSVlad Buslov mutex_lock(&block->lock); 793ed76f5edSVlad Buslov list_add(&item->list, &block->chain0.filter_chain_list); 794ed76f5edSVlad Buslov mutex_unlock(&block->lock); 795ed76f5edSVlad Buslov 796ed76f5edSVlad Buslov mutex_unlock(&chain0->filter_chain_lock); 797ed76f5edSVlad Buslov tcf_chain_put(chain0); 798ed76f5edSVlad Buslov } 799ed76f5edSVlad Buslov 800a9b19443SJiri Pirko return 0; 801a9b19443SJiri Pirko } 802a9b19443SJiri Pirko 803a9b19443SJiri Pirko static void 804f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(struct tcf_block *block, 805a9b19443SJiri Pirko struct tcf_block_ext_info *ei) 806a9b19443SJiri Pirko { 807a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 808a9b19443SJiri Pirko 809165f0135SVlad Buslov mutex_lock(&block->lock); 810f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) { 811a9b19443SJiri Pirko if ((!ei->chain_head_change && !ei->chain_head_change_priv) || 812a9b19443SJiri Pirko (item->chain_head_change == ei->chain_head_change && 813a9b19443SJiri Pirko item->chain_head_change_priv == ei->chain_head_change_priv)) { 814165f0135SVlad Buslov if (block->chain0.chain) 815a9b19443SJiri Pirko tcf_chain_head_change_item(item, NULL); 816a9b19443SJiri Pirko list_del(&item->list); 817165f0135SVlad Buslov mutex_unlock(&block->lock); 818165f0135SVlad Buslov 819a9b19443SJiri Pirko kfree(item); 820a9b19443SJiri Pirko return; 821a9b19443SJiri Pirko } 822a9b19443SJiri Pirko } 823165f0135SVlad Buslov mutex_unlock(&block->lock); 824a9b19443SJiri Pirko WARN_ON(1); 825a9b19443SJiri Pirko } 826a9b19443SJiri Pirko 82748617387SJiri Pirko struct tcf_net { 828ab281629SVlad Buslov spinlock_t idr_lock; /* Protects idr */ 82948617387SJiri Pirko struct idr idr; 83048617387SJiri Pirko }; 83148617387SJiri Pirko 83248617387SJiri Pirko static unsigned int tcf_net_id; 83348617387SJiri Pirko 83448617387SJiri Pirko static int tcf_block_insert(struct tcf_block *block, struct net *net, 835bb047dddSJiri Pirko struct netlink_ext_ack *extack) 836a9b19443SJiri Pirko { 83748617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 838ab281629SVlad Buslov int err; 83948617387SJiri Pirko 840ab281629SVlad Buslov idr_preload(GFP_KERNEL); 841ab281629SVlad Buslov spin_lock(&tn->idr_lock); 842ab281629SVlad Buslov err = idr_alloc_u32(&tn->idr, block, &block->index, block->index, 843ab281629SVlad Buslov GFP_NOWAIT); 844ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 845ab281629SVlad Buslov idr_preload_end(); 846ab281629SVlad Buslov 847ab281629SVlad Buslov return err; 848a9b19443SJiri Pirko } 849a9b19443SJiri Pirko 85048617387SJiri Pirko static void tcf_block_remove(struct tcf_block *block, struct net *net) 85148617387SJiri Pirko { 85248617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 85348617387SJiri Pirko 854ab281629SVlad Buslov spin_lock(&tn->idr_lock); 8559c160941SMatthew Wilcox idr_remove(&tn->idr, block->index); 856ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 85748617387SJiri Pirko } 85848617387SJiri Pirko 85948617387SJiri Pirko static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, 860bb047dddSJiri Pirko u32 block_index, 8618d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 8626529eabaSJiri Pirko { 86348617387SJiri Pirko struct tcf_block *block; 8646529eabaSJiri Pirko 86548617387SJiri Pirko block = kzalloc(sizeof(*block), GFP_KERNEL); 8668d1a77f9SAlexander Aring if (!block) { 8678d1a77f9SAlexander Aring NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); 86848617387SJiri Pirko return ERR_PTR(-ENOMEM); 8698d1a77f9SAlexander Aring } 870c266f64dSVlad Buslov mutex_init(&block->lock); 87159eb87cbSJohn Hurley mutex_init(&block->proto_destroy_lock); 8724f8116c8SVlad Buslov init_rwsem(&block->cb_lock); 87314bfb13fSPablo Neira Ayuso flow_block_init(&block->flow_block); 8745bc17018SJiri Pirko INIT_LIST_HEAD(&block->chain_list); 875f36fe1c4SJiri Pirko INIT_LIST_HEAD(&block->owner_list); 876f71e0ca4SJiri Pirko INIT_LIST_HEAD(&block->chain0.filter_chain_list); 877acb67442SJiri Pirko 878cfebd7e2SVlad Buslov refcount_set(&block->refcnt, 1); 87948617387SJiri Pirko block->net = net; 880bb047dddSJiri Pirko block->index = block_index; 881bb047dddSJiri Pirko 882bb047dddSJiri Pirko /* Don't store q pointer for blocks which are shared */ 883bb047dddSJiri Pirko if (!tcf_block_shared(block)) 88448617387SJiri Pirko block->q = q; 88548617387SJiri Pirko return block; 88648617387SJiri Pirko } 88748617387SJiri Pirko 88848617387SJiri Pirko static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) 88948617387SJiri Pirko { 89048617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 89148617387SJiri Pirko 892322d884bSMatthew Wilcox return idr_find(&tn->idr, block_index); 89348617387SJiri Pirko } 89448617387SJiri Pirko 8950607e439SVlad Buslov static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index) 8960607e439SVlad Buslov { 8970607e439SVlad Buslov struct tcf_block *block; 8980607e439SVlad Buslov 8990607e439SVlad Buslov rcu_read_lock(); 9000607e439SVlad Buslov block = tcf_block_lookup(net, block_index); 9010607e439SVlad Buslov if (block && !refcount_inc_not_zero(&block->refcnt)) 9020607e439SVlad Buslov block = NULL; 9030607e439SVlad Buslov rcu_read_unlock(); 9040607e439SVlad Buslov 9050607e439SVlad Buslov return block; 9060607e439SVlad Buslov } 9070607e439SVlad Buslov 908bbf73830SVlad Buslov static struct tcf_chain * 909bbf73830SVlad Buslov __tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 910bbf73830SVlad Buslov { 911bbf73830SVlad Buslov mutex_lock(&block->lock); 912bbf73830SVlad Buslov if (chain) 913bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 914bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 915bbf73830SVlad Buslov else 916bbf73830SVlad Buslov chain = list_first_entry_or_null(&block->chain_list, 917bbf73830SVlad Buslov struct tcf_chain, list); 918bbf73830SVlad Buslov 919bbf73830SVlad Buslov /* skip all action-only chains */ 920bbf73830SVlad Buslov while (chain && tcf_chain_held_by_acts_only(chain)) 921bbf73830SVlad Buslov chain = list_is_last(&chain->list, &block->chain_list) ? 922bbf73830SVlad Buslov NULL : list_next_entry(chain, list); 923bbf73830SVlad Buslov 924bbf73830SVlad Buslov if (chain) 925bbf73830SVlad Buslov tcf_chain_hold(chain); 926bbf73830SVlad Buslov mutex_unlock(&block->lock); 927bbf73830SVlad Buslov 928bbf73830SVlad Buslov return chain; 929bbf73830SVlad Buslov } 930bbf73830SVlad Buslov 931bbf73830SVlad Buslov /* Function to be used by all clients that want to iterate over all chains on 932bbf73830SVlad Buslov * block. It properly obtains block->lock and takes reference to chain before 933bbf73830SVlad Buslov * returning it. Users of this function must be tolerant to concurrent chain 934bbf73830SVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 935bbf73830SVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 936bbf73830SVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 937bbf73830SVlad Buslov * data and sent to user-space. 938bbf73830SVlad Buslov */ 939bbf73830SVlad Buslov 940bbf73830SVlad Buslov struct tcf_chain * 941bbf73830SVlad Buslov tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 942bbf73830SVlad Buslov { 943bbf73830SVlad Buslov struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain); 944bbf73830SVlad Buslov 945bbf73830SVlad Buslov if (chain) 946bbf73830SVlad Buslov tcf_chain_put(chain); 947bbf73830SVlad Buslov 948bbf73830SVlad Buslov return chain_next; 949bbf73830SVlad Buslov } 950bbf73830SVlad Buslov EXPORT_SYMBOL(tcf_get_next_chain); 951bbf73830SVlad Buslov 952fe2923afSVlad Buslov static struct tcf_proto * 953fe2923afSVlad Buslov __tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 954fe2923afSVlad Buslov { 9558b64678eSVlad Buslov u32 prio = 0; 9568b64678eSVlad Buslov 957fe2923afSVlad Buslov ASSERT_RTNL(); 958fe2923afSVlad Buslov mutex_lock(&chain->filter_chain_lock); 959fe2923afSVlad Buslov 9608b64678eSVlad Buslov if (!tp) { 961fe2923afSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 9628b64678eSVlad Buslov } else if (tcf_proto_is_deleting(tp)) { 9638b64678eSVlad Buslov /* 'deleting' flag is set and chain->filter_chain_lock was 9648b64678eSVlad Buslov * unlocked, which means next pointer could be invalid. Restart 9658b64678eSVlad Buslov * search. 9668b64678eSVlad Buslov */ 9678b64678eSVlad Buslov prio = tp->prio + 1; 9688b64678eSVlad Buslov tp = tcf_chain_dereference(chain->filter_chain, chain); 9698b64678eSVlad Buslov 9708b64678eSVlad Buslov for (; tp; tp = tcf_chain_dereference(tp->next, chain)) 9718b64678eSVlad Buslov if (!tp->deleting && tp->prio >= prio) 9728b64678eSVlad Buslov break; 9738b64678eSVlad Buslov } else { 974fe2923afSVlad Buslov tp = tcf_chain_dereference(tp->next, chain); 9758b64678eSVlad Buslov } 976fe2923afSVlad Buslov 977fe2923afSVlad Buslov if (tp) 978fe2923afSVlad Buslov tcf_proto_get(tp); 979fe2923afSVlad Buslov 980fe2923afSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 981fe2923afSVlad Buslov 982fe2923afSVlad Buslov return tp; 983fe2923afSVlad Buslov } 984fe2923afSVlad Buslov 985fe2923afSVlad Buslov /* Function to be used by all clients that want to iterate over all tp's on 986fe2923afSVlad Buslov * chain. Users of this function must be tolerant to concurrent tp 987fe2923afSVlad Buslov * insertion/deletion or ensure that no concurrent chain modification is 988fe2923afSVlad Buslov * possible. Note that all netlink dump callbacks cannot guarantee to provide 989fe2923afSVlad Buslov * consistent dump because rtnl lock is released each time skb is filled with 990fe2923afSVlad Buslov * data and sent to user-space. 991fe2923afSVlad Buslov */ 992fe2923afSVlad Buslov 993fe2923afSVlad Buslov struct tcf_proto * 9940fca55edSVlad Buslov tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 995fe2923afSVlad Buslov { 996fe2923afSVlad Buslov struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp); 997fe2923afSVlad Buslov 998fe2923afSVlad Buslov if (tp) 9990fca55edSVlad Buslov tcf_proto_put(tp, true, NULL); 1000fe2923afSVlad Buslov 1001fe2923afSVlad Buslov return tp_next; 1002fe2923afSVlad Buslov } 1003fe2923afSVlad Buslov EXPORT_SYMBOL(tcf_get_next_proto); 1004fe2923afSVlad Buslov 100512db03b6SVlad Buslov static void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held) 1006f0023436SVlad Buslov { 1007f0023436SVlad Buslov struct tcf_chain *chain; 1008f0023436SVlad Buslov 1009bbf73830SVlad Buslov /* Last reference to block. At this point chains cannot be added or 1010bbf73830SVlad Buslov * removed concurrently. 1011f0023436SVlad Buslov */ 1012bbf73830SVlad Buslov for (chain = tcf_get_next_chain(block, NULL); 1013bbf73830SVlad Buslov chain; 1014bbf73830SVlad Buslov chain = tcf_get_next_chain(block, chain)) { 1015f0023436SVlad Buslov tcf_chain_put_explicitly_created(chain); 101612db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 1017f0023436SVlad Buslov } 1018f0023436SVlad Buslov } 1019f0023436SVlad Buslov 102018d3eefbSVlad Buslov /* Lookup Qdisc and increments its reference counter. 102118d3eefbSVlad Buslov * Set parent, if necessary. 102218d3eefbSVlad Buslov */ 102318d3eefbSVlad Buslov 102418d3eefbSVlad Buslov static int __tcf_qdisc_find(struct net *net, struct Qdisc **q, 102518d3eefbSVlad Buslov u32 *parent, int ifindex, bool rtnl_held, 102618d3eefbSVlad Buslov struct netlink_ext_ack *extack) 102718d3eefbSVlad Buslov { 102818d3eefbSVlad Buslov const struct Qdisc_class_ops *cops; 102918d3eefbSVlad Buslov struct net_device *dev; 103018d3eefbSVlad Buslov int err = 0; 103118d3eefbSVlad Buslov 103218d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 103318d3eefbSVlad Buslov return 0; 103418d3eefbSVlad Buslov 103518d3eefbSVlad Buslov rcu_read_lock(); 103618d3eefbSVlad Buslov 103718d3eefbSVlad Buslov /* Find link */ 103818d3eefbSVlad Buslov dev = dev_get_by_index_rcu(net, ifindex); 103918d3eefbSVlad Buslov if (!dev) { 104018d3eefbSVlad Buslov rcu_read_unlock(); 104118d3eefbSVlad Buslov return -ENODEV; 104218d3eefbSVlad Buslov } 104318d3eefbSVlad Buslov 104418d3eefbSVlad Buslov /* Find qdisc */ 104518d3eefbSVlad Buslov if (!*parent) { 104618d3eefbSVlad Buslov *q = dev->qdisc; 104718d3eefbSVlad Buslov *parent = (*q)->handle; 104818d3eefbSVlad Buslov } else { 104918d3eefbSVlad Buslov *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent)); 105018d3eefbSVlad Buslov if (!*q) { 105118d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 105218d3eefbSVlad Buslov err = -EINVAL; 105318d3eefbSVlad Buslov goto errout_rcu; 105418d3eefbSVlad Buslov } 105518d3eefbSVlad Buslov } 105618d3eefbSVlad Buslov 105718d3eefbSVlad Buslov *q = qdisc_refcount_inc_nz(*q); 105818d3eefbSVlad Buslov if (!*q) { 105918d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 106018d3eefbSVlad Buslov err = -EINVAL; 106118d3eefbSVlad Buslov goto errout_rcu; 106218d3eefbSVlad Buslov } 106318d3eefbSVlad Buslov 106418d3eefbSVlad Buslov /* Is it classful? */ 106518d3eefbSVlad Buslov cops = (*q)->ops->cl_ops; 106618d3eefbSVlad Buslov if (!cops) { 106718d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Qdisc not classful"); 106818d3eefbSVlad Buslov err = -EINVAL; 106918d3eefbSVlad Buslov goto errout_qdisc; 107018d3eefbSVlad Buslov } 107118d3eefbSVlad Buslov 107218d3eefbSVlad Buslov if (!cops->tcf_block) { 107318d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Class doesn't support blocks"); 107418d3eefbSVlad Buslov err = -EOPNOTSUPP; 107518d3eefbSVlad Buslov goto errout_qdisc; 107618d3eefbSVlad Buslov } 107718d3eefbSVlad Buslov 107818d3eefbSVlad Buslov errout_rcu: 107918d3eefbSVlad Buslov /* At this point we know that qdisc is not noop_qdisc, 108018d3eefbSVlad Buslov * which means that qdisc holds a reference to net_device 108118d3eefbSVlad Buslov * and we hold a reference to qdisc, so it is safe to release 108218d3eefbSVlad Buslov * rcu read lock. 108318d3eefbSVlad Buslov */ 108418d3eefbSVlad Buslov rcu_read_unlock(); 108518d3eefbSVlad Buslov return err; 108618d3eefbSVlad Buslov 108718d3eefbSVlad Buslov errout_qdisc: 108818d3eefbSVlad Buslov rcu_read_unlock(); 108918d3eefbSVlad Buslov 109018d3eefbSVlad Buslov if (rtnl_held) 109118d3eefbSVlad Buslov qdisc_put(*q); 109218d3eefbSVlad Buslov else 109318d3eefbSVlad Buslov qdisc_put_unlocked(*q); 109418d3eefbSVlad Buslov *q = NULL; 109518d3eefbSVlad Buslov 109618d3eefbSVlad Buslov return err; 109718d3eefbSVlad Buslov } 109818d3eefbSVlad Buslov 109918d3eefbSVlad Buslov static int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl, 110018d3eefbSVlad Buslov int ifindex, struct netlink_ext_ack *extack) 110118d3eefbSVlad Buslov { 110218d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 110318d3eefbSVlad Buslov return 0; 110418d3eefbSVlad Buslov 110518d3eefbSVlad Buslov /* Do we search for filter, attached to class? */ 110618d3eefbSVlad Buslov if (TC_H_MIN(parent)) { 110718d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 110818d3eefbSVlad Buslov 110918d3eefbSVlad Buslov *cl = cops->find(q, parent); 111018d3eefbSVlad Buslov if (*cl == 0) { 111118d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Specified class doesn't exist"); 111218d3eefbSVlad Buslov return -ENOENT; 111318d3eefbSVlad Buslov } 111418d3eefbSVlad Buslov } 111518d3eefbSVlad Buslov 111618d3eefbSVlad Buslov return 0; 111718d3eefbSVlad Buslov } 111818d3eefbSVlad Buslov 111918d3eefbSVlad Buslov static struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q, 112018d3eefbSVlad Buslov unsigned long cl, int ifindex, 112118d3eefbSVlad Buslov u32 block_index, 112218d3eefbSVlad Buslov struct netlink_ext_ack *extack) 112318d3eefbSVlad Buslov { 112418d3eefbSVlad Buslov struct tcf_block *block; 112518d3eefbSVlad Buslov 112618d3eefbSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 112718d3eefbSVlad Buslov block = tcf_block_refcnt_get(net, block_index); 112818d3eefbSVlad Buslov if (!block) { 112918d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "Block of given index was not found"); 113018d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 113118d3eefbSVlad Buslov } 113218d3eefbSVlad Buslov } else { 113318d3eefbSVlad Buslov const struct Qdisc_class_ops *cops = q->ops->cl_ops; 113418d3eefbSVlad Buslov 113518d3eefbSVlad Buslov block = cops->tcf_block(q, cl, extack); 113618d3eefbSVlad Buslov if (!block) 113718d3eefbSVlad Buslov return ERR_PTR(-EINVAL); 113818d3eefbSVlad Buslov 113918d3eefbSVlad Buslov if (tcf_block_shared(block)) { 114018d3eefbSVlad Buslov NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters"); 114118d3eefbSVlad Buslov return ERR_PTR(-EOPNOTSUPP); 114218d3eefbSVlad Buslov } 114318d3eefbSVlad Buslov 114418d3eefbSVlad Buslov /* Always take reference to block in order to support execution 114518d3eefbSVlad Buslov * of rules update path of cls API without rtnl lock. Caller 114618d3eefbSVlad Buslov * must release block when it is finished using it. 'if' block 114718d3eefbSVlad Buslov * of this conditional obtain reference to block by calling 114818d3eefbSVlad Buslov * tcf_block_refcnt_get(). 114918d3eefbSVlad Buslov */ 115018d3eefbSVlad Buslov refcount_inc(&block->refcnt); 115118d3eefbSVlad Buslov } 115218d3eefbSVlad Buslov 115318d3eefbSVlad Buslov return block; 115418d3eefbSVlad Buslov } 115518d3eefbSVlad Buslov 11560607e439SVlad Buslov static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q, 115712db03b6SVlad Buslov struct tcf_block_ext_info *ei, bool rtnl_held) 11580607e439SVlad Buslov { 1159c266f64dSVlad Buslov if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) { 11600607e439SVlad Buslov /* Flushing/putting all chains will cause the block to be 11610607e439SVlad Buslov * deallocated when last chain is freed. However, if chain_list 11620607e439SVlad Buslov * is empty, block has to be manually deallocated. After block 11630607e439SVlad Buslov * reference counter reached 0, it is no longer possible to 11640607e439SVlad Buslov * increment it or add new chains to block. 11650607e439SVlad Buslov */ 11660607e439SVlad Buslov bool free_block = list_empty(&block->chain_list); 11670607e439SVlad Buslov 1168c266f64dSVlad Buslov mutex_unlock(&block->lock); 11690607e439SVlad Buslov if (tcf_block_shared(block)) 11700607e439SVlad Buslov tcf_block_remove(block, block->net); 11710607e439SVlad Buslov 11720607e439SVlad Buslov if (q) 11730607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 11740607e439SVlad Buslov 11750607e439SVlad Buslov if (free_block) 1176c266f64dSVlad Buslov tcf_block_destroy(block); 11770607e439SVlad Buslov else 117812db03b6SVlad Buslov tcf_block_flush_all_chains(block, rtnl_held); 11790607e439SVlad Buslov } else if (q) { 11800607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 11810607e439SVlad Buslov } 11820607e439SVlad Buslov } 11830607e439SVlad Buslov 118412db03b6SVlad Buslov static void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held) 11850607e439SVlad Buslov { 118612db03b6SVlad Buslov __tcf_block_put(block, NULL, NULL, rtnl_held); 11870607e439SVlad Buslov } 11880607e439SVlad Buslov 1189c431f89bSVlad Buslov /* Find tcf block. 1190c431f89bSVlad Buslov * Set q, parent, cl when appropriate. 1191c431f89bSVlad Buslov */ 1192c431f89bSVlad Buslov 1193c431f89bSVlad Buslov static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q, 1194c431f89bSVlad Buslov u32 *parent, unsigned long *cl, 1195c431f89bSVlad Buslov int ifindex, u32 block_index, 1196c431f89bSVlad Buslov struct netlink_ext_ack *extack) 1197c431f89bSVlad Buslov { 1198c431f89bSVlad Buslov struct tcf_block *block; 1199e368fdb6SVlad Buslov int err = 0; 1200c431f89bSVlad Buslov 120118d3eefbSVlad Buslov ASSERT_RTNL(); 1202c431f89bSVlad Buslov 120318d3eefbSVlad Buslov err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack); 120418d3eefbSVlad Buslov if (err) 120518d3eefbSVlad Buslov goto errout; 1206e368fdb6SVlad Buslov 120718d3eefbSVlad Buslov err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack); 120818d3eefbSVlad Buslov if (err) 1209e368fdb6SVlad Buslov goto errout_qdisc; 1210c431f89bSVlad Buslov 121118d3eefbSVlad Buslov block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack); 1212af736bf0SDan Carpenter if (IS_ERR(block)) { 1213af736bf0SDan Carpenter err = PTR_ERR(block); 1214e368fdb6SVlad Buslov goto errout_qdisc; 1215af736bf0SDan Carpenter } 1216c431f89bSVlad Buslov 1217c431f89bSVlad Buslov return block; 1218e368fdb6SVlad Buslov 1219e368fdb6SVlad Buslov errout_qdisc: 122018d3eefbSVlad Buslov if (*q) 1221e368fdb6SVlad Buslov qdisc_put(*q); 122218d3eefbSVlad Buslov errout: 1223460b3601SCong Wang *q = NULL; 1224e368fdb6SVlad Buslov return ERR_PTR(err); 1225e368fdb6SVlad Buslov } 1226e368fdb6SVlad Buslov 122712db03b6SVlad Buslov static void tcf_block_release(struct Qdisc *q, struct tcf_block *block, 122812db03b6SVlad Buslov bool rtnl_held) 1229e368fdb6SVlad Buslov { 1230787ce6d0SVlad Buslov if (!IS_ERR_OR_NULL(block)) 123112db03b6SVlad Buslov tcf_block_refcnt_put(block, rtnl_held); 1232787ce6d0SVlad Buslov 1233470502deSVlad Buslov if (q) { 1234470502deSVlad Buslov if (rtnl_held) 1235e368fdb6SVlad Buslov qdisc_put(q); 1236470502deSVlad Buslov else 1237470502deSVlad Buslov qdisc_put_unlocked(q); 1238470502deSVlad Buslov } 1239c431f89bSVlad Buslov } 1240c431f89bSVlad Buslov 1241f36fe1c4SJiri Pirko struct tcf_block_owner_item { 1242f36fe1c4SJiri Pirko struct list_head list; 1243f36fe1c4SJiri Pirko struct Qdisc *q; 124432f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type; 1245f36fe1c4SJiri Pirko }; 1246f36fe1c4SJiri Pirko 1247f36fe1c4SJiri Pirko static void 1248f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(struct tcf_block *block, 1249f36fe1c4SJiri Pirko struct Qdisc *q, 125032f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1251f36fe1c4SJiri Pirko { 1252f36fe1c4SJiri Pirko if (block->keep_dst && 125332f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && 125432f8c409SPablo Neira Ayuso binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 1255f36fe1c4SJiri Pirko netif_keep_dst(qdisc_dev(q)); 1256f36fe1c4SJiri Pirko } 1257f36fe1c4SJiri Pirko 1258f36fe1c4SJiri Pirko void tcf_block_netif_keep_dst(struct tcf_block *block) 1259f36fe1c4SJiri Pirko { 1260f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1261f36fe1c4SJiri Pirko 1262f36fe1c4SJiri Pirko block->keep_dst = true; 1263f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) 1264f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, item->q, 1265f36fe1c4SJiri Pirko item->binder_type); 1266f36fe1c4SJiri Pirko } 1267f36fe1c4SJiri Pirko EXPORT_SYMBOL(tcf_block_netif_keep_dst); 1268f36fe1c4SJiri Pirko 1269f36fe1c4SJiri Pirko static int tcf_block_owner_add(struct tcf_block *block, 1270f36fe1c4SJiri Pirko struct Qdisc *q, 127132f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1272f36fe1c4SJiri Pirko { 1273f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1274f36fe1c4SJiri Pirko 1275f36fe1c4SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 1276f36fe1c4SJiri Pirko if (!item) 1277f36fe1c4SJiri Pirko return -ENOMEM; 1278f36fe1c4SJiri Pirko item->q = q; 1279f36fe1c4SJiri Pirko item->binder_type = binder_type; 1280f36fe1c4SJiri Pirko list_add(&item->list, &block->owner_list); 1281f36fe1c4SJiri Pirko return 0; 1282f36fe1c4SJiri Pirko } 1283f36fe1c4SJiri Pirko 1284f36fe1c4SJiri Pirko static void tcf_block_owner_del(struct tcf_block *block, 1285f36fe1c4SJiri Pirko struct Qdisc *q, 128632f8c409SPablo Neira Ayuso enum flow_block_binder_type binder_type) 1287f36fe1c4SJiri Pirko { 1288f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1289f36fe1c4SJiri Pirko 1290f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) { 1291f36fe1c4SJiri Pirko if (item->q == q && item->binder_type == binder_type) { 1292f36fe1c4SJiri Pirko list_del(&item->list); 1293f36fe1c4SJiri Pirko kfree(item); 1294f36fe1c4SJiri Pirko return; 1295f36fe1c4SJiri Pirko } 1296f36fe1c4SJiri Pirko } 1297f36fe1c4SJiri Pirko WARN_ON(1); 1298f36fe1c4SJiri Pirko } 1299f36fe1c4SJiri Pirko 130048617387SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 130148617387SJiri Pirko struct tcf_block_ext_info *ei, 130248617387SJiri Pirko struct netlink_ext_ack *extack) 130348617387SJiri Pirko { 130448617387SJiri Pirko struct net *net = qdisc_net(q); 130548617387SJiri Pirko struct tcf_block *block = NULL; 130648617387SJiri Pirko int err; 130748617387SJiri Pirko 1308787ce6d0SVlad Buslov if (ei->block_index) 130948617387SJiri Pirko /* block_index not 0 means the shared block is requested */ 1310787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, ei->block_index); 131148617387SJiri Pirko 131248617387SJiri Pirko if (!block) { 1313bb047dddSJiri Pirko block = tcf_block_create(net, q, ei->block_index, extack); 131448617387SJiri Pirko if (IS_ERR(block)) 131548617387SJiri Pirko return PTR_ERR(block); 1316bb047dddSJiri Pirko if (tcf_block_shared(block)) { 1317bb047dddSJiri Pirko err = tcf_block_insert(block, net, extack); 131848617387SJiri Pirko if (err) 131948617387SJiri Pirko goto err_block_insert; 132048617387SJiri Pirko } 132148617387SJiri Pirko } 132248617387SJiri Pirko 1323f36fe1c4SJiri Pirko err = tcf_block_owner_add(block, q, ei->binder_type); 1324f36fe1c4SJiri Pirko if (err) 1325f36fe1c4SJiri Pirko goto err_block_owner_add; 1326f36fe1c4SJiri Pirko 1327f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, q, ei->binder_type); 1328f36fe1c4SJiri Pirko 1329f71e0ca4SJiri Pirko err = tcf_chain0_head_change_cb_add(block, ei, extack); 1330a9b19443SJiri Pirko if (err) 1331f71e0ca4SJiri Pirko goto err_chain0_head_change_cb_add; 1332caa72601SJiri Pirko 133360513bd8SJohn Hurley err = tcf_block_offload_bind(block, q, ei, extack); 1334caa72601SJiri Pirko if (err) 1335caa72601SJiri Pirko goto err_block_offload_bind; 1336caa72601SJiri Pirko 13376529eabaSJiri Pirko *p_block = block; 13386529eabaSJiri Pirko return 0; 13392190d1d0SJiri Pirko 1340caa72601SJiri Pirko err_block_offload_bind: 1341f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1342f71e0ca4SJiri Pirko err_chain0_head_change_cb_add: 1343f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 1344f36fe1c4SJiri Pirko err_block_owner_add: 134548617387SJiri Pirko err_block_insert: 134612db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 13472190d1d0SJiri Pirko return err; 13486529eabaSJiri Pirko } 13498c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext); 13508c4083b3SJiri Pirko 1351c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 1352c7eb7d72SJiri Pirko { 1353c7eb7d72SJiri Pirko struct tcf_proto __rcu **p_filter_chain = priv; 1354c7eb7d72SJiri Pirko 1355c7eb7d72SJiri Pirko rcu_assign_pointer(*p_filter_chain, tp_head); 1356c7eb7d72SJiri Pirko } 1357c7eb7d72SJiri Pirko 13588c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block, 13598d1a77f9SAlexander Aring struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, 13608d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 13618c4083b3SJiri Pirko { 1362c7eb7d72SJiri Pirko struct tcf_block_ext_info ei = { 1363c7eb7d72SJiri Pirko .chain_head_change = tcf_chain_head_change_dflt, 1364c7eb7d72SJiri Pirko .chain_head_change_priv = p_filter_chain, 1365c7eb7d72SJiri Pirko }; 13668c4083b3SJiri Pirko 1367c7eb7d72SJiri Pirko WARN_ON(!p_filter_chain); 13688d1a77f9SAlexander Aring return tcf_block_get_ext(p_block, q, &ei, extack); 13698c4083b3SJiri Pirko } 13706529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get); 13716529eabaSJiri Pirko 13727aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound 1373a60b3f51SRoman Kapl * actions should be all removed after flushing. 1374e2ef7544SCong Wang */ 1375c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 1376e1ea2f98SDavid S. Miller struct tcf_block_ext_info *ei) 13777aa0045dSCong Wang { 1378c30abd5eSDavid S. Miller if (!block) 1379c30abd5eSDavid S. Miller return; 1380f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1381f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 138248617387SJiri Pirko 138312db03b6SVlad Buslov __tcf_block_put(block, q, ei, true); 138448617387SJiri Pirko } 13858c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext); 13868c4083b3SJiri Pirko 13878c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block) 13888c4083b3SJiri Pirko { 13898c4083b3SJiri Pirko struct tcf_block_ext_info ei = {0, }; 13908c4083b3SJiri Pirko 13914853f128SJiri Pirko if (!block) 13924853f128SJiri Pirko return; 1393c7eb7d72SJiri Pirko tcf_block_put_ext(block, block->q, &ei); 13948c4083b3SJiri Pirko } 1395e1ea2f98SDavid S. Miller 13966529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put); 1397cf1facdaSJiri Pirko 139832636742SJohn Hurley static int 1399a7323311SPablo Neira Ayuso tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, 140032636742SJohn Hurley void *cb_priv, bool add, bool offload_in_use, 140132636742SJohn Hurley struct netlink_ext_ack *extack) 140232636742SJohn Hurley { 1403bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 1404fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 140532636742SJohn Hurley int err; 140632636742SJohn Hurley 14074f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14084f8116c8SVlad Buslov 1409bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 1410bbf73830SVlad Buslov chain; 1411bbf73830SVlad Buslov chain_prev = chain, 1412bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 1413bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 1414fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); tp; 1415fe2923afSVlad Buslov tp_prev = tp, 1416fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 141712db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL)) { 141832636742SJohn Hurley if (tp->ops->reoffload) { 141932636742SJohn Hurley err = tp->ops->reoffload(tp, add, cb, cb_priv, 142032636742SJohn Hurley extack); 142132636742SJohn Hurley if (err && add) 142232636742SJohn Hurley goto err_playback_remove; 142332636742SJohn Hurley } else if (add && offload_in_use) { 142432636742SJohn Hurley err = -EOPNOTSUPP; 142532636742SJohn Hurley NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support"); 142632636742SJohn Hurley goto err_playback_remove; 142732636742SJohn Hurley } 142832636742SJohn Hurley } 142932636742SJohn Hurley } 143032636742SJohn Hurley 143132636742SJohn Hurley return 0; 143232636742SJohn Hurley 143332636742SJohn Hurley err_playback_remove: 143412db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 1435bbf73830SVlad Buslov tcf_chain_put(chain); 143632636742SJohn Hurley tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use, 143732636742SJohn Hurley extack); 143832636742SJohn Hurley return err; 143932636742SJohn Hurley } 144032636742SJohn Hurley 144159094b1eSPablo Neira Ayuso static int tcf_block_bind(struct tcf_block *block, 144259094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 144359094b1eSPablo Neira Ayuso { 144459094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 144559094b1eSPablo Neira Ayuso int err, i = 0; 144659094b1eSPablo Neira Ayuso 14474f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14484f8116c8SVlad Buslov 144959094b1eSPablo Neira Ayuso list_for_each_entry(block_cb, &bo->cb_list, list) { 145059094b1eSPablo Neira Ayuso err = tcf_block_playback_offloads(block, block_cb->cb, 145159094b1eSPablo Neira Ayuso block_cb->cb_priv, true, 145259094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 145359094b1eSPablo Neira Ayuso bo->extack); 145459094b1eSPablo Neira Ayuso if (err) 145559094b1eSPablo Neira Ayuso goto err_unroll; 1456c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1457c9f14470SVlad Buslov block->lockeddevcnt++; 145859094b1eSPablo Neira Ayuso 145959094b1eSPablo Neira Ayuso i++; 146059094b1eSPablo Neira Ayuso } 146114bfb13fSPablo Neira Ayuso list_splice(&bo->cb_list, &block->flow_block.cb_list); 146259094b1eSPablo Neira Ayuso 146359094b1eSPablo Neira Ayuso return 0; 146459094b1eSPablo Neira Ayuso 146559094b1eSPablo Neira Ayuso err_unroll: 146659094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 146759094b1eSPablo Neira Ayuso if (i-- > 0) { 146859094b1eSPablo Neira Ayuso list_del(&block_cb->list); 146959094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 147059094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 147159094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 147259094b1eSPablo Neira Ayuso NULL); 1473c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1474c9f14470SVlad Buslov block->lockeddevcnt--; 147559094b1eSPablo Neira Ayuso } 147659094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 147759094b1eSPablo Neira Ayuso } 147859094b1eSPablo Neira Ayuso 147959094b1eSPablo Neira Ayuso return err; 148059094b1eSPablo Neira Ayuso } 148159094b1eSPablo Neira Ayuso 148259094b1eSPablo Neira Ayuso static void tcf_block_unbind(struct tcf_block *block, 148359094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 148459094b1eSPablo Neira Ayuso { 148559094b1eSPablo Neira Ayuso struct flow_block_cb *block_cb, *next; 148659094b1eSPablo Neira Ayuso 14874f8116c8SVlad Buslov lockdep_assert_held(&block->cb_lock); 14884f8116c8SVlad Buslov 148959094b1eSPablo Neira Ayuso list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 149059094b1eSPablo Neira Ayuso tcf_block_playback_offloads(block, block_cb->cb, 149159094b1eSPablo Neira Ayuso block_cb->cb_priv, false, 149259094b1eSPablo Neira Ayuso tcf_block_offload_in_use(block), 149359094b1eSPablo Neira Ayuso NULL); 149459094b1eSPablo Neira Ayuso list_del(&block_cb->list); 149559094b1eSPablo Neira Ayuso flow_block_cb_free(block_cb); 1496c9f14470SVlad Buslov if (!bo->unlocked_driver_cb) 1497c9f14470SVlad Buslov block->lockeddevcnt--; 149859094b1eSPablo Neira Ayuso } 149959094b1eSPablo Neira Ayuso } 150059094b1eSPablo Neira Ayuso 150159094b1eSPablo Neira Ayuso static int tcf_block_setup(struct tcf_block *block, 150259094b1eSPablo Neira Ayuso struct flow_block_offload *bo) 150359094b1eSPablo Neira Ayuso { 150459094b1eSPablo Neira Ayuso int err; 150559094b1eSPablo Neira Ayuso 150659094b1eSPablo Neira Ayuso switch (bo->command) { 150759094b1eSPablo Neira Ayuso case FLOW_BLOCK_BIND: 150859094b1eSPablo Neira Ayuso err = tcf_block_bind(block, bo); 150959094b1eSPablo Neira Ayuso break; 151059094b1eSPablo Neira Ayuso case FLOW_BLOCK_UNBIND: 151159094b1eSPablo Neira Ayuso err = 0; 151259094b1eSPablo Neira Ayuso tcf_block_unbind(block, bo); 151359094b1eSPablo Neira Ayuso break; 151459094b1eSPablo Neira Ayuso default: 151559094b1eSPablo Neira Ayuso WARN_ON_ONCE(1); 151659094b1eSPablo Neira Ayuso err = -EOPNOTSUPP; 151759094b1eSPablo Neira Ayuso } 151859094b1eSPablo Neira Ayuso 151959094b1eSPablo Neira Ayuso return err; 152059094b1eSPablo Neira Ayuso } 152159094b1eSPablo Neira Ayuso 152287d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached 152387d83093SJiri Pirko * to this qdisc, (optionally) tests for protocol and asks 152487d83093SJiri Pirko * specific classifiers. 152587d83093SJiri Pirko */ 15269410c940SPaul Blakey static inline int __tcf_classify(struct sk_buff *skb, 15279410c940SPaul Blakey const struct tcf_proto *tp, 1528af699626SPaul Blakey const struct tcf_proto *orig_tp, 15299410c940SPaul Blakey struct tcf_result *res, 15309410c940SPaul Blakey bool compat_mode, 15319410c940SPaul Blakey u32 *last_executed_chain) 153287d83093SJiri Pirko { 153387d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 153405ff8435SDavide Caratti const int max_reclassify_loop = 16; 1535ee538dceSJiri Pirko const struct tcf_proto *first_tp; 153687d83093SJiri Pirko int limit = 0; 153787d83093SJiri Pirko 153887d83093SJiri Pirko reclassify: 153987d83093SJiri Pirko #endif 154087d83093SJiri Pirko for (; tp; tp = rcu_dereference_bh(tp->next)) { 1541d7bf2ebeSToke Høiland-Jørgensen __be16 protocol = skb_protocol(skb, false); 154287d83093SJiri Pirko int err; 154387d83093SJiri Pirko 154487d83093SJiri Pirko if (tp->protocol != protocol && 154587d83093SJiri Pirko tp->protocol != htons(ETH_P_ALL)) 154687d83093SJiri Pirko continue; 154787d83093SJiri Pirko 154887d83093SJiri Pirko err = tp->classify(skb, tp, res); 154987d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 1550db50514fSJiri Pirko if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 1551ee538dceSJiri Pirko first_tp = orig_tp; 15529410c940SPaul Blakey *last_executed_chain = first_tp->chain->index; 155387d83093SJiri Pirko goto reset; 1554db50514fSJiri Pirko } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 1555ee538dceSJiri Pirko first_tp = res->goto_tp; 15569410c940SPaul Blakey *last_executed_chain = err & TC_ACT_EXT_VAL_MASK; 1557db50514fSJiri Pirko goto reset; 1558db50514fSJiri Pirko } 155987d83093SJiri Pirko #endif 156087d83093SJiri Pirko if (err >= 0) 156187d83093SJiri Pirko return err; 156287d83093SJiri Pirko } 156387d83093SJiri Pirko 156487d83093SJiri Pirko return TC_ACT_UNSPEC; /* signal: continue lookup */ 156587d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 156687d83093SJiri Pirko reset: 156787d83093SJiri Pirko if (unlikely(limit++ >= max_reclassify_loop)) { 15689d3aaff3SJiri Pirko net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n", 15699d3aaff3SJiri Pirko tp->chain->block->index, 15709d3aaff3SJiri Pirko tp->prio & 0xffff, 157187d83093SJiri Pirko ntohs(tp->protocol)); 157287d83093SJiri Pirko return TC_ACT_SHOT; 157387d83093SJiri Pirko } 157487d83093SJiri Pirko 1575ee538dceSJiri Pirko tp = first_tp; 157687d83093SJiri Pirko goto reclassify; 157787d83093SJiri Pirko #endif 157887d83093SJiri Pirko } 15799410c940SPaul Blakey 15803aa26055SDavide Caratti int tcf_classify(struct sk_buff *skb, 15813aa26055SDavide Caratti const struct tcf_block *block, 15827d17c544SPaul Blakey const struct tcf_proto *tp, 15839410c940SPaul Blakey struct tcf_result *res, bool compat_mode) 15849410c940SPaul Blakey { 15859410c940SPaul Blakey #if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 15869410c940SPaul Blakey u32 last_executed_chain = 0; 15879410c940SPaul Blakey 1588af699626SPaul Blakey return __tcf_classify(skb, tp, tp, res, compat_mode, 15899410c940SPaul Blakey &last_executed_chain); 15909410c940SPaul Blakey #else 15919410c940SPaul Blakey u32 last_executed_chain = tp ? tp->chain->index : 0; 1592af699626SPaul Blakey const struct tcf_proto *orig_tp = tp; 15939410c940SPaul Blakey struct tc_skb_ext *ext; 15949410c940SPaul Blakey int ret; 15959410c940SPaul Blakey 15963aa26055SDavide Caratti if (block) { 1597af699626SPaul Blakey ext = skb_ext_find(skb, TC_SKB_EXT); 1598af699626SPaul Blakey 1599af699626SPaul Blakey if (ext && ext->chain) { 1600af699626SPaul Blakey struct tcf_chain *fchain; 1601af699626SPaul Blakey 16023aa26055SDavide Caratti fchain = tcf_chain_lookup_rcu(block, ext->chain); 1603af699626SPaul Blakey if (!fchain) 1604af699626SPaul Blakey return TC_ACT_SHOT; 1605af699626SPaul Blakey 1606af699626SPaul Blakey /* Consume, so cloned/redirect skbs won't inherit ext */ 1607af699626SPaul Blakey skb_ext_del(skb, TC_SKB_EXT); 1608af699626SPaul Blakey 1609af699626SPaul Blakey tp = rcu_dereference_bh(fchain->filter_chain); 1610a080da6aSPaul Blakey last_executed_chain = fchain->index; 1611af699626SPaul Blakey } 16123aa26055SDavide Caratti } 1613af699626SPaul Blakey 1614af699626SPaul Blakey ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode, 1615af699626SPaul Blakey &last_executed_chain); 16169410c940SPaul Blakey 16179410c940SPaul Blakey /* If we missed on some chain */ 16189410c940SPaul Blakey if (ret == TC_ACT_UNSPEC && last_executed_chain) { 16199453d45eSVlad Buslov ext = tc_skb_ext_alloc(skb); 16209410c940SPaul Blakey if (WARN_ON_ONCE(!ext)) 16219410c940SPaul Blakey return TC_ACT_SHOT; 16229410c940SPaul Blakey ext->chain = last_executed_chain; 1623038ebb1aSwenxu ext->mru = qdisc_skb_cb(skb)->mru; 1624d29334c1Swenxu ext->post_ct = qdisc_skb_cb(skb)->post_ct; 16259410c940SPaul Blakey } 16269410c940SPaul Blakey 16279410c940SPaul Blakey return ret; 16289410c940SPaul Blakey #endif 16299410c940SPaul Blakey } 16303aa26055SDavide Caratti EXPORT_SYMBOL(tcf_classify); 16319410c940SPaul Blakey 16322190d1d0SJiri Pirko struct tcf_chain_info { 16332190d1d0SJiri Pirko struct tcf_proto __rcu **pprev; 16342190d1d0SJiri Pirko struct tcf_proto __rcu *next; 16352190d1d0SJiri Pirko }; 16362190d1d0SJiri Pirko 1637ed76f5edSVlad Buslov static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain, 1638ed76f5edSVlad Buslov struct tcf_chain_info *chain_info) 16392190d1d0SJiri Pirko { 1640ed76f5edSVlad Buslov return tcf_chain_dereference(*chain_info->pprev, chain); 16412190d1d0SJiri Pirko } 16422190d1d0SJiri Pirko 1643726d0612SVlad Buslov static int tcf_chain_tp_insert(struct tcf_chain *chain, 16442190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16452190d1d0SJiri Pirko struct tcf_proto *tp) 16462190d1d0SJiri Pirko { 1647726d0612SVlad Buslov if (chain->flushing) 1648726d0612SVlad Buslov return -EAGAIN; 1649726d0612SVlad Buslov 1650c7eb7d72SJiri Pirko if (*chain_info->pprev == chain->filter_chain) 1651f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, tp); 16524dbfa766SVlad Buslov tcf_proto_get(tp); 1653ed76f5edSVlad Buslov RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info)); 16542190d1d0SJiri Pirko rcu_assign_pointer(*chain_info->pprev, tp); 1655726d0612SVlad Buslov 1656726d0612SVlad Buslov return 0; 16572190d1d0SJiri Pirko } 16582190d1d0SJiri Pirko 16592190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain, 16602190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16612190d1d0SJiri Pirko struct tcf_proto *tp) 16622190d1d0SJiri Pirko { 1663ed76f5edSVlad Buslov struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain); 16642190d1d0SJiri Pirko 16658b64678eSVlad Buslov tcf_proto_mark_delete(tp); 1666c7eb7d72SJiri Pirko if (tp == chain->filter_chain) 1667f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, next); 16682190d1d0SJiri Pirko RCU_INIT_POINTER(*chain_info->pprev, next); 16692190d1d0SJiri Pirko } 16702190d1d0SJiri Pirko 16712190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 16722190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 16732190d1d0SJiri Pirko u32 protocol, u32 prio, 16748b64678eSVlad Buslov bool prio_allocate); 16758b64678eSVlad Buslov 16768b64678eSVlad Buslov /* Try to insert new proto. 16778b64678eSVlad Buslov * If proto with specified priority already exists, free new proto 16788b64678eSVlad Buslov * and return existing one. 16798b64678eSVlad Buslov */ 16808b64678eSVlad Buslov 16818b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain, 16828b64678eSVlad Buslov struct tcf_proto *tp_new, 168312db03b6SVlad Buslov u32 protocol, u32 prio, 168412db03b6SVlad Buslov bool rtnl_held) 16858b64678eSVlad Buslov { 16868b64678eSVlad Buslov struct tcf_chain_info chain_info; 16878b64678eSVlad Buslov struct tcf_proto *tp; 1688726d0612SVlad Buslov int err = 0; 16898b64678eSVlad Buslov 16908b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 16918b64678eSVlad Buslov 169259eb87cbSJohn Hurley if (tcf_proto_exists_destroying(chain, tp_new)) { 169359eb87cbSJohn Hurley mutex_unlock(&chain->filter_chain_lock); 169459eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 169559eb87cbSJohn Hurley return ERR_PTR(-EAGAIN); 169659eb87cbSJohn Hurley } 169759eb87cbSJohn Hurley 16988b64678eSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, 16998b64678eSVlad Buslov protocol, prio, false); 17008b64678eSVlad Buslov if (!tp) 1701726d0612SVlad Buslov err = tcf_chain_tp_insert(chain, &chain_info, tp_new); 17028b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17038b64678eSVlad Buslov 17048b64678eSVlad Buslov if (tp) { 170559eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 17068b64678eSVlad Buslov tp_new = tp; 1707726d0612SVlad Buslov } else if (err) { 170859eb87cbSJohn Hurley tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 1709726d0612SVlad Buslov tp_new = ERR_PTR(err); 17108b64678eSVlad Buslov } 17118b64678eSVlad Buslov 17128b64678eSVlad Buslov return tp_new; 17138b64678eSVlad Buslov } 17148b64678eSVlad Buslov 17158b64678eSVlad Buslov static void tcf_chain_tp_delete_empty(struct tcf_chain *chain, 171612db03b6SVlad Buslov struct tcf_proto *tp, bool rtnl_held, 17178b64678eSVlad Buslov struct netlink_ext_ack *extack) 17188b64678eSVlad Buslov { 17198b64678eSVlad Buslov struct tcf_chain_info chain_info; 17208b64678eSVlad Buslov struct tcf_proto *tp_iter; 17218b64678eSVlad Buslov struct tcf_proto **pprev; 17228b64678eSVlad Buslov struct tcf_proto *next; 17238b64678eSVlad Buslov 17248b64678eSVlad Buslov mutex_lock(&chain->filter_chain_lock); 17258b64678eSVlad Buslov 17268b64678eSVlad Buslov /* Atomically find and remove tp from chain. */ 17278b64678eSVlad Buslov for (pprev = &chain->filter_chain; 17288b64678eSVlad Buslov (tp_iter = tcf_chain_dereference(*pprev, chain)); 17298b64678eSVlad Buslov pprev = &tp_iter->next) { 17308b64678eSVlad Buslov if (tp_iter == tp) { 17318b64678eSVlad Buslov chain_info.pprev = pprev; 17328b64678eSVlad Buslov chain_info.next = tp_iter->next; 17338b64678eSVlad Buslov WARN_ON(tp_iter->deleting); 17348b64678eSVlad Buslov break; 17358b64678eSVlad Buslov } 17368b64678eSVlad Buslov } 17378b64678eSVlad Buslov /* Verify that tp still exists and no new filters were inserted 17388b64678eSVlad Buslov * concurrently. 17398b64678eSVlad Buslov * Mark tp for deletion if it is empty. 17408b64678eSVlad Buslov */ 1741a5b72a08SDavide Caratti if (!tp_iter || !tcf_proto_check_delete(tp)) { 17428b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17438b64678eSVlad Buslov return; 17448b64678eSVlad Buslov } 17458b64678eSVlad Buslov 174659eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 17478b64678eSVlad Buslov next = tcf_chain_dereference(chain_info.next, chain); 17488b64678eSVlad Buslov if (tp == chain->filter_chain) 17498b64678eSVlad Buslov tcf_chain0_head_change(chain, next); 17508b64678eSVlad Buslov RCU_INIT_POINTER(*chain_info.pprev, next); 17518b64678eSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 17528b64678eSVlad Buslov 175312db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, extack); 17548b64678eSVlad Buslov } 17558b64678eSVlad Buslov 17568b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 17578b64678eSVlad Buslov struct tcf_chain_info *chain_info, 17588b64678eSVlad Buslov u32 protocol, u32 prio, 17592190d1d0SJiri Pirko bool prio_allocate) 17602190d1d0SJiri Pirko { 17612190d1d0SJiri Pirko struct tcf_proto **pprev; 17622190d1d0SJiri Pirko struct tcf_proto *tp; 17632190d1d0SJiri Pirko 17642190d1d0SJiri Pirko /* Check the chain for existence of proto-tcf with this priority */ 17652190d1d0SJiri Pirko for (pprev = &chain->filter_chain; 1766ed76f5edSVlad Buslov (tp = tcf_chain_dereference(*pprev, chain)); 1767ed76f5edSVlad Buslov pprev = &tp->next) { 17682190d1d0SJiri Pirko if (tp->prio >= prio) { 17692190d1d0SJiri Pirko if (tp->prio == prio) { 17702190d1d0SJiri Pirko if (prio_allocate || 17712190d1d0SJiri Pirko (tp->protocol != protocol && protocol)) 17722190d1d0SJiri Pirko return ERR_PTR(-EINVAL); 17732190d1d0SJiri Pirko } else { 17742190d1d0SJiri Pirko tp = NULL; 17752190d1d0SJiri Pirko } 17762190d1d0SJiri Pirko break; 17772190d1d0SJiri Pirko } 17782190d1d0SJiri Pirko } 17792190d1d0SJiri Pirko chain_info->pprev = pprev; 17804dbfa766SVlad Buslov if (tp) { 17814dbfa766SVlad Buslov chain_info->next = tp->next; 17824dbfa766SVlad Buslov tcf_proto_get(tp); 17834dbfa766SVlad Buslov } else { 17844dbfa766SVlad Buslov chain_info->next = NULL; 17854dbfa766SVlad Buslov } 17862190d1d0SJiri Pirko return tp; 17872190d1d0SJiri Pirko } 17882190d1d0SJiri Pirko 17897120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb, 17907960d1daSJiri Pirko struct tcf_proto *tp, struct tcf_block *block, 17917960d1daSJiri Pirko struct Qdisc *q, u32 parent, void *fh, 179212db03b6SVlad Buslov u32 portid, u32 seq, u16 flags, int event, 1793f8ab1807SVlad Buslov bool terse_dump, bool rtnl_held) 17947120371cSWANG Cong { 17957120371cSWANG Cong struct tcmsg *tcm; 17967120371cSWANG Cong struct nlmsghdr *nlh; 17977120371cSWANG Cong unsigned char *b = skb_tail_pointer(skb); 17987120371cSWANG Cong 17997120371cSWANG Cong nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 18007120371cSWANG Cong if (!nlh) 18017120371cSWANG Cong goto out_nlmsg_trim; 18027120371cSWANG Cong tcm = nlmsg_data(nlh); 18037120371cSWANG Cong tcm->tcm_family = AF_UNSPEC; 18047120371cSWANG Cong tcm->tcm__pad1 = 0; 18057120371cSWANG Cong tcm->tcm__pad2 = 0; 18067960d1daSJiri Pirko if (q) { 1807a10fa201SJiri Pirko tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 1808a10fa201SJiri Pirko tcm->tcm_parent = parent; 18097960d1daSJiri Pirko } else { 18107960d1daSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 18117960d1daSJiri Pirko tcm->tcm_block_index = block->index; 18127960d1daSJiri Pirko } 18137120371cSWANG Cong tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 18147120371cSWANG Cong if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 18157120371cSWANG Cong goto nla_put_failure; 18167120371cSWANG Cong if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 18177120371cSWANG Cong goto nla_put_failure; 18187120371cSWANG Cong if (!fh) { 18197120371cSWANG Cong tcm->tcm_handle = 0; 1820f8ab1807SVlad Buslov } else if (terse_dump) { 1821f8ab1807SVlad Buslov if (tp->ops->terse_dump) { 1822f8ab1807SVlad Buslov if (tp->ops->terse_dump(net, tp, fh, skb, tcm, 1823f8ab1807SVlad Buslov rtnl_held) < 0) 1824f8ab1807SVlad Buslov goto nla_put_failure; 1825f8ab1807SVlad Buslov } else { 1826f8ab1807SVlad Buslov goto cls_op_not_supp; 1827f8ab1807SVlad Buslov } 18287120371cSWANG Cong } else { 182912db03b6SVlad Buslov if (tp->ops->dump && 183012db03b6SVlad Buslov tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) 18317120371cSWANG Cong goto nla_put_failure; 18327120371cSWANG Cong } 18337120371cSWANG Cong nlh->nlmsg_len = skb_tail_pointer(skb) - b; 18347120371cSWANG Cong return skb->len; 18357120371cSWANG Cong 18367120371cSWANG Cong out_nlmsg_trim: 18377120371cSWANG Cong nla_put_failure: 1838f8ab1807SVlad Buslov cls_op_not_supp: 18397120371cSWANG Cong nlmsg_trim(skb, b); 18407120371cSWANG Cong return -1; 18417120371cSWANG Cong } 18427120371cSWANG Cong 18437120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb, 18447120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 18457960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 184612db03b6SVlad Buslov u32 parent, void *fh, int event, bool unicast, 184712db03b6SVlad Buslov bool rtnl_held) 18487120371cSWANG Cong { 18497120371cSWANG Cong struct sk_buff *skb; 18507120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 18515b5f99b1SZhike Wang int err = 0; 18527120371cSWANG Cong 18537120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 18547120371cSWANG Cong if (!skb) 18557120371cSWANG Cong return -ENOBUFS; 18567120371cSWANG Cong 18577960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 185812db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, event, 1859f8ab1807SVlad Buslov false, rtnl_held) <= 0) { 18607120371cSWANG Cong kfree_skb(skb); 18617120371cSWANG Cong return -EINVAL; 18627120371cSWANG Cong } 18637120371cSWANG Cong 18647120371cSWANG Cong if (unicast) 1865f79a3bcbSYajun Deng err = rtnl_unicast(skb, net, portid); 18665b5f99b1SZhike Wang else 18675b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 18687120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 18695b5f99b1SZhike Wang return err; 18707120371cSWANG Cong } 18717120371cSWANG Cong 18727120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 18737120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 18747960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 1875c35a4accSAlexander Aring u32 parent, void *fh, bool unicast, bool *last, 187612db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 18777120371cSWANG Cong { 18787120371cSWANG Cong struct sk_buff *skb; 18797120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 18807120371cSWANG Cong int err; 18817120371cSWANG Cong 18827120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 18837120371cSWANG Cong if (!skb) 18847120371cSWANG Cong return -ENOBUFS; 18857120371cSWANG Cong 18867960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 188712db03b6SVlad Buslov n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, 1888f8ab1807SVlad Buslov false, rtnl_held) <= 0) { 1889c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to build del event notification"); 18907120371cSWANG Cong kfree_skb(skb); 18917120371cSWANG Cong return -EINVAL; 18927120371cSWANG Cong } 18937120371cSWANG Cong 189412db03b6SVlad Buslov err = tp->ops->delete(tp, fh, last, rtnl_held, extack); 18957120371cSWANG Cong if (err) { 18967120371cSWANG Cong kfree_skb(skb); 18977120371cSWANG Cong return err; 18987120371cSWANG Cong } 18997120371cSWANG Cong 19007120371cSWANG Cong if (unicast) 1901f79a3bcbSYajun Deng err = rtnl_unicast(skb, net, portid); 19025b5f99b1SZhike Wang else 1903c35a4accSAlexander Aring err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 19047120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 1905c35a4accSAlexander Aring if (err < 0) 1906c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to send filter delete notification"); 19075b5f99b1SZhike Wang 1908c35a4accSAlexander Aring return err; 19097120371cSWANG Cong } 19107120371cSWANG Cong 19117120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 19127960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 19137960d1daSJiri Pirko u32 parent, struct nlmsghdr *n, 19140fca55edSVlad Buslov struct tcf_chain *chain, int event) 19157120371cSWANG Cong { 19167120371cSWANG Cong struct tcf_proto *tp; 19177120371cSWANG Cong 19180fca55edSVlad Buslov for (tp = tcf_get_next_proto(chain, NULL); 19190fca55edSVlad Buslov tp; tp = tcf_get_next_proto(chain, tp)) 19207960d1daSJiri Pirko tfilter_notify(net, oskb, n, tp, block, 19210fca55edSVlad Buslov q, parent, NULL, event, false, true); 19227120371cSWANG Cong } 19237120371cSWANG Cong 19247d5509faSVlad Buslov static void tfilter_put(struct tcf_proto *tp, void *fh) 19257d5509faSVlad Buslov { 19267d5509faSVlad Buslov if (tp->ops->put && fh) 19277d5509faSVlad Buslov tp->ops->put(tp, fh); 19287d5509faSVlad Buslov } 19297d5509faSVlad Buslov 1930c431f89bSVlad Buslov static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1931c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19321da177e4SLinus Torvalds { 19333b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1934add93b61SPatrick McHardy struct nlattr *tca[TCA_MAX + 1]; 19356f96c3c6SCong Wang char name[IFNAMSIZ]; 19361da177e4SLinus Torvalds struct tcmsg *t; 19371da177e4SLinus Torvalds u32 protocol; 19381da177e4SLinus Torvalds u32 prio; 19399d36d9e5SJiri Pirko bool prio_allocate; 19401da177e4SLinus Torvalds u32 parent; 19415bc17018SJiri Pirko u32 chain_index; 19427960d1daSJiri Pirko struct Qdisc *q = NULL; 19432190d1d0SJiri Pirko struct tcf_chain_info chain_info; 19445bc17018SJiri Pirko struct tcf_chain *chain = NULL; 19456529eabaSJiri Pirko struct tcf_block *block; 19461da177e4SLinus Torvalds struct tcf_proto *tp; 19471da177e4SLinus Torvalds unsigned long cl; 19488113c095SWANG Cong void *fh; 19491da177e4SLinus Torvalds int err; 1950628185cfSDaniel Borkmann int tp_created; 1951470502deSVlad Buslov bool rtnl_held = false; 1952*a5397d68SMark Bloch u32 flags; 19531da177e4SLinus Torvalds 1954c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 1955dfc47ef8SEric W. Biederman return -EPERM; 1956de179c8cSHong zhi guo 19571da177e4SLinus Torvalds replay: 1958628185cfSDaniel Borkmann tp_created = 0; 1959628185cfSDaniel Borkmann 19608cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 19618cb08174SJohannes Berg rtm_tca_policy, extack); 1962de179c8cSHong zhi guo if (err < 0) 1963de179c8cSHong zhi guo return err; 1964de179c8cSHong zhi guo 1965942b8165SDavid S. Miller t = nlmsg_data(n); 19661da177e4SLinus Torvalds protocol = TC_H_MIN(t->tcm_info); 19671da177e4SLinus Torvalds prio = TC_H_MAJ(t->tcm_info); 19689d36d9e5SJiri Pirko prio_allocate = false; 19691da177e4SLinus Torvalds parent = t->tcm_parent; 19704dbfa766SVlad Buslov tp = NULL; 19711da177e4SLinus Torvalds cl = 0; 1972470502deSVlad Buslov block = NULL; 1973*a5397d68SMark Bloch flags = 0; 19741da177e4SLinus Torvalds 19751da177e4SLinus Torvalds if (prio == 0) { 1976ea7f8277SDaniel Borkmann /* If no priority is provided by the user, 1977ea7f8277SDaniel Borkmann * we allocate one. 1978ea7f8277SDaniel Borkmann */ 1979ea7f8277SDaniel Borkmann if (n->nlmsg_flags & NLM_F_CREATE) { 19801da177e4SLinus Torvalds prio = TC_H_MAKE(0x80000000U, 0U); 19819d36d9e5SJiri Pirko prio_allocate = true; 1982c431f89bSVlad Buslov } else { 1983c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 1984ea7f8277SDaniel Borkmann return -ENOENT; 1985ea7f8277SDaniel Borkmann } 19861da177e4SLinus Torvalds } 19871da177e4SLinus Torvalds 19881da177e4SLinus Torvalds /* Find head of filter chain. */ 19891da177e4SLinus Torvalds 1990470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 1991470502deSVlad Buslov if (err) 1992470502deSVlad Buslov return err; 1993470502deSVlad Buslov 19946f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 19956f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 19966f96c3c6SCong Wang err = -EINVAL; 19976f96c3c6SCong Wang goto errout; 19986f96c3c6SCong Wang } 19996f96c3c6SCong Wang 2000470502deSVlad Buslov /* Take rtnl mutex if rtnl_held was set to true on previous iteration, 2001470502deSVlad Buslov * block is shared (no qdisc found), qdisc is not unlocked, classifier 2002470502deSVlad Buslov * type is not specified, classifier is not unlocked. 2003470502deSVlad Buslov */ 2004470502deSVlad Buslov if (rtnl_held || 2005470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 20066f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2007470502deSVlad Buslov rtnl_held = true; 2008470502deSVlad Buslov rtnl_lock(); 2009470502deSVlad Buslov } 2010470502deSVlad Buslov 2011470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2012470502deSVlad Buslov if (err) 2013470502deSVlad Buslov goto errout; 2014470502deSVlad Buslov 2015470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2016470502deSVlad Buslov extack); 2017c431f89bSVlad Buslov if (IS_ERR(block)) { 2018c431f89bSVlad Buslov err = PTR_ERR(block); 20197960d1daSJiri Pirko goto errout; 20207960d1daSJiri Pirko } 2021a7df4870SCong Wang block->classid = parent; 20225bc17018SJiri Pirko 20235bc17018SJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 20245bc17018SJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 2025c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 20265bc17018SJiri Pirko err = -EINVAL; 20275bc17018SJiri Pirko goto errout; 20285bc17018SJiri Pirko } 2029c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, true); 20305bc17018SJiri Pirko if (!chain) { 2031d5ed72a5SJiri Pirko NL_SET_ERR_MSG(extack, "Cannot create specified filter chain"); 2032c431f89bSVlad Buslov err = -ENOMEM; 2033ea7f8277SDaniel Borkmann goto errout; 2034ea7f8277SDaniel Borkmann } 20351da177e4SLinus Torvalds 2036ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 20372190d1d0SJiri Pirko tp = tcf_chain_tp_find(chain, &chain_info, protocol, 20382190d1d0SJiri Pirko prio, prio_allocate); 20392190d1d0SJiri Pirko if (IS_ERR(tp)) { 2040c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 20412190d1d0SJiri Pirko err = PTR_ERR(tp); 2042ed76f5edSVlad Buslov goto errout_locked; 20436bb16e7aSJiri Pirko } 20441da177e4SLinus Torvalds 20451da177e4SLinus Torvalds if (tp == NULL) { 20468b64678eSVlad Buslov struct tcf_proto *tp_new = NULL; 20478b64678eSVlad Buslov 2048726d0612SVlad Buslov if (chain->flushing) { 2049726d0612SVlad Buslov err = -EAGAIN; 2050726d0612SVlad Buslov goto errout_locked; 2051726d0612SVlad Buslov } 2052726d0612SVlad Buslov 20531da177e4SLinus Torvalds /* Proto-tcf does not exist, create new one */ 20541da177e4SLinus Torvalds 20556bb16e7aSJiri Pirko if (tca[TCA_KIND] == NULL || !protocol) { 2056c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified"); 20576bb16e7aSJiri Pirko err = -EINVAL; 2058ed76f5edSVlad Buslov goto errout_locked; 20596bb16e7aSJiri Pirko } 20601da177e4SLinus Torvalds 2061c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2062c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 20636bb16e7aSJiri Pirko err = -ENOENT; 2064ed76f5edSVlad Buslov goto errout_locked; 20656bb16e7aSJiri Pirko } 20661da177e4SLinus Torvalds 20679d36d9e5SJiri Pirko if (prio_allocate) 2068ed76f5edSVlad Buslov prio = tcf_auto_prio(tcf_chain_tp_prev(chain, 2069ed76f5edSVlad Buslov &chain_info)); 20701da177e4SLinus Torvalds 2071ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 207236d79af7SEric Dumazet tp_new = tcf_proto_create(name, protocol, prio, chain, 207336d79af7SEric Dumazet rtnl_held, extack); 20748b64678eSVlad Buslov if (IS_ERR(tp_new)) { 20758b64678eSVlad Buslov err = PTR_ERR(tp_new); 2076726d0612SVlad Buslov goto errout_tp; 20771da177e4SLinus Torvalds } 2078ed76f5edSVlad Buslov 207912186be7SMinoru Usui tp_created = 1; 208012db03b6SVlad Buslov tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio, 208112db03b6SVlad Buslov rtnl_held); 2082726d0612SVlad Buslov if (IS_ERR(tp)) { 2083726d0612SVlad Buslov err = PTR_ERR(tp); 2084726d0612SVlad Buslov goto errout_tp; 2085726d0612SVlad Buslov } 2086ed76f5edSVlad Buslov } else { 2087ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 20886bb16e7aSJiri Pirko } 20891da177e4SLinus Torvalds 20908b64678eSVlad Buslov if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 20918b64678eSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 20928b64678eSVlad Buslov err = -EINVAL; 20938b64678eSVlad Buslov goto errout; 20948b64678eSVlad Buslov } 20958b64678eSVlad Buslov 20961da177e4SLinus Torvalds fh = tp->ops->get(tp, t->tcm_handle); 20971da177e4SLinus Torvalds 20988113c095SWANG Cong if (!fh) { 2099c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 2100c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 21016bb16e7aSJiri Pirko err = -ENOENT; 21021da177e4SLinus Torvalds goto errout; 21036bb16e7aSJiri Pirko } 2104c431f89bSVlad Buslov } else if (n->nlmsg_flags & NLM_F_EXCL) { 21057d5509faSVlad Buslov tfilter_put(tp, fh); 2106c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter already exists"); 21076bb16e7aSJiri Pirko err = -EEXIST; 21081da177e4SLinus Torvalds goto errout; 210912186be7SMinoru Usui } 21101da177e4SLinus Torvalds 21119f407f17SJiri Pirko if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) { 21129f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind"); 21139f407f17SJiri Pirko err = -EINVAL; 21149f407f17SJiri Pirko goto errout; 21159f407f17SJiri Pirko } 21169f407f17SJiri Pirko 2117695176bfSCong Wang if (!(n->nlmsg_flags & NLM_F_CREATE)) 2118695176bfSCong Wang flags |= TCA_ACT_FLAGS_REPLACE; 2119695176bfSCong Wang if (!rtnl_held) 2120695176bfSCong Wang flags |= TCA_ACT_FLAGS_NO_RTNL; 21212f7ef2f8SCong Wang err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 2122695176bfSCong Wang flags, extack); 21237d5509faSVlad Buslov if (err == 0) { 21247960d1daSJiri Pirko tfilter_notify(net, skb, n, tp, block, q, parent, fh, 212512db03b6SVlad Buslov RTM_NEWTFILTER, false, rtnl_held); 21267d5509faSVlad Buslov tfilter_put(tp, fh); 2127503d81d4SVlad Buslov /* q pointer is NULL for shared blocks */ 2128503d81d4SVlad Buslov if (q) 21293f05e688SCong Wang q->flags &= ~TCQ_F_CAN_BYPASS; 21307d5509faSVlad Buslov } 21311da177e4SLinus Torvalds 21321da177e4SLinus Torvalds errout: 21338b64678eSVlad Buslov if (err && tp_created) 213412db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL); 2135726d0612SVlad Buslov errout_tp: 21364dbfa766SVlad Buslov if (chain) { 21374dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 213812db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 21394dbfa766SVlad Buslov if (!tp_created) 21404dbfa766SVlad Buslov tcf_chain_put(chain); 21414dbfa766SVlad Buslov } 214212db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2143470502deSVlad Buslov 2144470502deSVlad Buslov if (rtnl_held) 2145470502deSVlad Buslov rtnl_unlock(); 2146470502deSVlad Buslov 2147470502deSVlad Buslov if (err == -EAGAIN) { 2148470502deSVlad Buslov /* Take rtnl lock in case EAGAIN is caused by concurrent flush 2149470502deSVlad Buslov * of target chain. 2150470502deSVlad Buslov */ 2151470502deSVlad Buslov rtnl_held = true; 21521da177e4SLinus Torvalds /* Replay the request. */ 21531da177e4SLinus Torvalds goto replay; 2154470502deSVlad Buslov } 21551da177e4SLinus Torvalds return err; 2156ed76f5edSVlad Buslov 2157ed76f5edSVlad Buslov errout_locked: 2158ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2159ed76f5edSVlad Buslov goto errout; 21601da177e4SLinus Torvalds } 21611da177e4SLinus Torvalds 2162c431f89bSVlad Buslov static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2163c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2164c431f89bSVlad Buslov { 2165c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2166c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 21676f96c3c6SCong Wang char name[IFNAMSIZ]; 2168c431f89bSVlad Buslov struct tcmsg *t; 2169c431f89bSVlad Buslov u32 protocol; 2170c431f89bSVlad Buslov u32 prio; 2171c431f89bSVlad Buslov u32 parent; 2172c431f89bSVlad Buslov u32 chain_index; 2173c431f89bSVlad Buslov struct Qdisc *q = NULL; 2174c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2175c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2176470502deSVlad Buslov struct tcf_block *block = NULL; 2177c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2178c431f89bSVlad Buslov unsigned long cl = 0; 2179c431f89bSVlad Buslov void *fh = NULL; 2180c431f89bSVlad Buslov int err; 2181470502deSVlad Buslov bool rtnl_held = false; 2182c431f89bSVlad Buslov 2183c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 2184c431f89bSVlad Buslov return -EPERM; 2185c431f89bSVlad Buslov 21868cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 21878cb08174SJohannes Berg rtm_tca_policy, extack); 2188c431f89bSVlad Buslov if (err < 0) 2189c431f89bSVlad Buslov return err; 2190c431f89bSVlad Buslov 2191c431f89bSVlad Buslov t = nlmsg_data(n); 2192c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2193c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2194c431f89bSVlad Buslov parent = t->tcm_parent; 2195c431f89bSVlad Buslov 2196c431f89bSVlad Buslov if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) { 2197c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set"); 2198c431f89bSVlad Buslov return -ENOENT; 2199c431f89bSVlad Buslov } 2200c431f89bSVlad Buslov 2201c431f89bSVlad Buslov /* Find head of filter chain. */ 2202c431f89bSVlad Buslov 2203470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2204470502deSVlad Buslov if (err) 2205470502deSVlad Buslov return err; 2206470502deSVlad Buslov 22076f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 22086f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 22096f96c3c6SCong Wang err = -EINVAL; 22106f96c3c6SCong Wang goto errout; 22116f96c3c6SCong Wang } 2212470502deSVlad Buslov /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc 2213470502deSVlad Buslov * found), qdisc is not unlocked, classifier type is not specified, 2214470502deSVlad Buslov * classifier is not unlocked. 2215470502deSVlad Buslov */ 2216470502deSVlad Buslov if (!prio || 2217470502deSVlad Buslov (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 22186f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2219470502deSVlad Buslov rtnl_held = true; 2220470502deSVlad Buslov rtnl_lock(); 2221470502deSVlad Buslov } 2222470502deSVlad Buslov 2223470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2224470502deSVlad Buslov if (err) 2225470502deSVlad Buslov goto errout; 2226470502deSVlad Buslov 2227470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2228470502deSVlad Buslov extack); 2229c431f89bSVlad Buslov if (IS_ERR(block)) { 2230c431f89bSVlad Buslov err = PTR_ERR(block); 2231c431f89bSVlad Buslov goto errout; 2232c431f89bSVlad Buslov } 2233c431f89bSVlad Buslov 2234c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2235c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2236c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2237c431f89bSVlad Buslov err = -EINVAL; 2238c431f89bSVlad Buslov goto errout; 2239c431f89bSVlad Buslov } 2240c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2241c431f89bSVlad Buslov if (!chain) { 22425ca8a25cSJiri Pirko /* User requested flush on non-existent chain. Nothing to do, 22435ca8a25cSJiri Pirko * so just return success. 22445ca8a25cSJiri Pirko */ 22455ca8a25cSJiri Pirko if (prio == 0) { 22465ca8a25cSJiri Pirko err = 0; 22475ca8a25cSJiri Pirko goto errout; 22485ca8a25cSJiri Pirko } 2249c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2250b7b4247dSJiri Pirko err = -ENOENT; 2251c431f89bSVlad Buslov goto errout; 2252c431f89bSVlad Buslov } 2253c431f89bSVlad Buslov 2254c431f89bSVlad Buslov if (prio == 0) { 2255c431f89bSVlad Buslov tfilter_notify_chain(net, skb, block, q, parent, n, 22560fca55edSVlad Buslov chain, RTM_DELTFILTER); 225712db03b6SVlad Buslov tcf_chain_flush(chain, rtnl_held); 2258c431f89bSVlad Buslov err = 0; 2259c431f89bSVlad Buslov goto errout; 2260c431f89bSVlad Buslov } 2261c431f89bSVlad Buslov 2262ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2263c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2264c431f89bSVlad Buslov prio, false); 2265c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2266c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 22670e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2268ed76f5edSVlad Buslov goto errout_locked; 2269c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2270c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2271c431f89bSVlad Buslov err = -EINVAL; 2272ed76f5edSVlad Buslov goto errout_locked; 2273ed76f5edSVlad Buslov } else if (t->tcm_handle == 0) { 227459eb87cbSJohn Hurley tcf_proto_signal_destroying(chain, tp); 2275c431f89bSVlad Buslov tcf_chain_tp_remove(chain, &chain_info, tp); 2276ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2277ed76f5edSVlad Buslov 227812db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2279c431f89bSVlad Buslov tfilter_notify(net, skb, n, tp, block, q, parent, fh, 228012db03b6SVlad Buslov RTM_DELTFILTER, false, rtnl_held); 2281c431f89bSVlad Buslov err = 0; 2282ed76f5edSVlad Buslov goto errout; 2283ed76f5edSVlad Buslov } 2284ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2285ed76f5edSVlad Buslov 2286ed76f5edSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2287ed76f5edSVlad Buslov 2288ed76f5edSVlad Buslov if (!fh) { 2289c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2290c431f89bSVlad Buslov err = -ENOENT; 2291c431f89bSVlad Buslov } else { 2292c431f89bSVlad Buslov bool last; 2293c431f89bSVlad Buslov 2294c431f89bSVlad Buslov err = tfilter_del_notify(net, skb, n, tp, block, 2295c431f89bSVlad Buslov q, parent, fh, false, &last, 229612db03b6SVlad Buslov rtnl_held, extack); 229712db03b6SVlad Buslov 2298c431f89bSVlad Buslov if (err) 2299c431f89bSVlad Buslov goto errout; 23008b64678eSVlad Buslov if (last) 230112db03b6SVlad Buslov tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack); 2302c431f89bSVlad Buslov } 2303c431f89bSVlad Buslov 2304c431f89bSVlad Buslov errout: 23054dbfa766SVlad Buslov if (chain) { 23064dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 230712db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2308c431f89bSVlad Buslov tcf_chain_put(chain); 23094dbfa766SVlad Buslov } 231012db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2311470502deSVlad Buslov 2312470502deSVlad Buslov if (rtnl_held) 2313470502deSVlad Buslov rtnl_unlock(); 2314470502deSVlad Buslov 2315c431f89bSVlad Buslov return err; 2316ed76f5edSVlad Buslov 2317ed76f5edSVlad Buslov errout_locked: 2318ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2319ed76f5edSVlad Buslov goto errout; 2320c431f89bSVlad Buslov } 2321c431f89bSVlad Buslov 2322c431f89bSVlad Buslov static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 2323c431f89bSVlad Buslov struct netlink_ext_ack *extack) 2324c431f89bSVlad Buslov { 2325c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 2326c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 23276f96c3c6SCong Wang char name[IFNAMSIZ]; 2328c431f89bSVlad Buslov struct tcmsg *t; 2329c431f89bSVlad Buslov u32 protocol; 2330c431f89bSVlad Buslov u32 prio; 2331c431f89bSVlad Buslov u32 parent; 2332c431f89bSVlad Buslov u32 chain_index; 2333c431f89bSVlad Buslov struct Qdisc *q = NULL; 2334c431f89bSVlad Buslov struct tcf_chain_info chain_info; 2335c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 2336470502deSVlad Buslov struct tcf_block *block = NULL; 2337c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 2338c431f89bSVlad Buslov unsigned long cl = 0; 2339c431f89bSVlad Buslov void *fh = NULL; 2340c431f89bSVlad Buslov int err; 2341470502deSVlad Buslov bool rtnl_held = false; 2342c431f89bSVlad Buslov 23438cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 23448cb08174SJohannes Berg rtm_tca_policy, extack); 2345c431f89bSVlad Buslov if (err < 0) 2346c431f89bSVlad Buslov return err; 2347c431f89bSVlad Buslov 2348c431f89bSVlad Buslov t = nlmsg_data(n); 2349c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 2350c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 2351c431f89bSVlad Buslov parent = t->tcm_parent; 2352c431f89bSVlad Buslov 2353c431f89bSVlad Buslov if (prio == 0) { 2354c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 2355c431f89bSVlad Buslov return -ENOENT; 2356c431f89bSVlad Buslov } 2357c431f89bSVlad Buslov 2358c431f89bSVlad Buslov /* Find head of filter chain. */ 2359c431f89bSVlad Buslov 2360470502deSVlad Buslov err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 2361470502deSVlad Buslov if (err) 2362470502deSVlad Buslov return err; 2363470502deSVlad Buslov 23646f96c3c6SCong Wang if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 23656f96c3c6SCong Wang NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 23666f96c3c6SCong Wang err = -EINVAL; 23676f96c3c6SCong Wang goto errout; 23686f96c3c6SCong Wang } 2369470502deSVlad Buslov /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not 2370470502deSVlad Buslov * unlocked, classifier type is not specified, classifier is not 2371470502deSVlad Buslov * unlocked. 2372470502deSVlad Buslov */ 2373470502deSVlad Buslov if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 23746f96c3c6SCong Wang !tcf_proto_is_unlocked(name)) { 2375470502deSVlad Buslov rtnl_held = true; 2376470502deSVlad Buslov rtnl_lock(); 2377470502deSVlad Buslov } 2378470502deSVlad Buslov 2379470502deSVlad Buslov err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 2380470502deSVlad Buslov if (err) 2381470502deSVlad Buslov goto errout; 2382470502deSVlad Buslov 2383470502deSVlad Buslov block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 2384470502deSVlad Buslov extack); 2385c431f89bSVlad Buslov if (IS_ERR(block)) { 2386c431f89bSVlad Buslov err = PTR_ERR(block); 2387c431f89bSVlad Buslov goto errout; 2388c431f89bSVlad Buslov } 2389c431f89bSVlad Buslov 2390c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 2391c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 2392c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2393c431f89bSVlad Buslov err = -EINVAL; 2394c431f89bSVlad Buslov goto errout; 2395c431f89bSVlad Buslov } 2396c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 2397c431f89bSVlad Buslov if (!chain) { 2398c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2399c431f89bSVlad Buslov err = -EINVAL; 2400c431f89bSVlad Buslov goto errout; 2401c431f89bSVlad Buslov } 2402c431f89bSVlad Buslov 2403ed76f5edSVlad Buslov mutex_lock(&chain->filter_chain_lock); 2404c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 2405c431f89bSVlad Buslov prio, false); 2406ed76f5edSVlad Buslov mutex_unlock(&chain->filter_chain_lock); 2407c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 2408c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 24090e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 2410c431f89bSVlad Buslov goto errout; 2411c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 2412c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 2413c431f89bSVlad Buslov err = -EINVAL; 2414c431f89bSVlad Buslov goto errout; 2415c431f89bSVlad Buslov } 2416c431f89bSVlad Buslov 2417c431f89bSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 2418c431f89bSVlad Buslov 2419c431f89bSVlad Buslov if (!fh) { 2420c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 2421c431f89bSVlad Buslov err = -ENOENT; 2422c431f89bSVlad Buslov } else { 2423c431f89bSVlad Buslov err = tfilter_notify(net, skb, n, tp, block, q, parent, 242412db03b6SVlad Buslov fh, RTM_NEWTFILTER, true, rtnl_held); 2425c431f89bSVlad Buslov if (err < 0) 2426c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); 2427c431f89bSVlad Buslov } 2428c431f89bSVlad Buslov 24297d5509faSVlad Buslov tfilter_put(tp, fh); 2430c431f89bSVlad Buslov errout: 24314dbfa766SVlad Buslov if (chain) { 24324dbfa766SVlad Buslov if (tp && !IS_ERR(tp)) 243312db03b6SVlad Buslov tcf_proto_put(tp, rtnl_held, NULL); 2434c431f89bSVlad Buslov tcf_chain_put(chain); 24354dbfa766SVlad Buslov } 243612db03b6SVlad Buslov tcf_block_release(q, block, rtnl_held); 2437470502deSVlad Buslov 2438470502deSVlad Buslov if (rtnl_held) 2439470502deSVlad Buslov rtnl_unlock(); 2440470502deSVlad Buslov 2441c431f89bSVlad Buslov return err; 2442c431f89bSVlad Buslov } 2443c431f89bSVlad Buslov 2444aa767bfeSStephen Hemminger struct tcf_dump_args { 24451da177e4SLinus Torvalds struct tcf_walker w; 24461da177e4SLinus Torvalds struct sk_buff *skb; 24471da177e4SLinus Torvalds struct netlink_callback *cb; 24487960d1daSJiri Pirko struct tcf_block *block; 2449a10fa201SJiri Pirko struct Qdisc *q; 2450a10fa201SJiri Pirko u32 parent; 2451f8ab1807SVlad Buslov bool terse_dump; 24521da177e4SLinus Torvalds }; 24531da177e4SLinus Torvalds 24548113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 24551da177e4SLinus Torvalds { 24561da177e4SLinus Torvalds struct tcf_dump_args *a = (void *)arg; 2457832d1d5bSWANG Cong struct net *net = sock_net(a->skb->sk); 24581da177e4SLinus Torvalds 24597960d1daSJiri Pirko return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, 2460a10fa201SJiri Pirko n, NETLINK_CB(a->cb->skb).portid, 24615a7a5555SJamal Hadi Salim a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 2462f8ab1807SVlad Buslov RTM_NEWTFILTER, a->terse_dump, true); 24631da177e4SLinus Torvalds } 24641da177e4SLinus Torvalds 2465a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 2466a10fa201SJiri Pirko struct sk_buff *skb, struct netlink_callback *cb, 2467f8ab1807SVlad Buslov long index_start, long *p_index, bool terse) 2468acb31faeSJiri Pirko { 2469acb31faeSJiri Pirko struct net *net = sock_net(skb->sk); 24707960d1daSJiri Pirko struct tcf_block *block = chain->block; 2471acb31faeSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2472fe2923afSVlad Buslov struct tcf_proto *tp, *tp_prev; 2473acb31faeSJiri Pirko struct tcf_dump_args arg; 2474acb31faeSJiri Pirko 2475fe2923afSVlad Buslov for (tp = __tcf_get_next_proto(chain, NULL); 2476fe2923afSVlad Buslov tp; 2477fe2923afSVlad Buslov tp_prev = tp, 2478fe2923afSVlad Buslov tp = __tcf_get_next_proto(chain, tp), 247912db03b6SVlad Buslov tcf_proto_put(tp_prev, true, NULL), 2480fe2923afSVlad Buslov (*p_index)++) { 2481acb31faeSJiri Pirko if (*p_index < index_start) 2482acb31faeSJiri Pirko continue; 2483acb31faeSJiri Pirko if (TC_H_MAJ(tcm->tcm_info) && 2484acb31faeSJiri Pirko TC_H_MAJ(tcm->tcm_info) != tp->prio) 2485acb31faeSJiri Pirko continue; 2486acb31faeSJiri Pirko if (TC_H_MIN(tcm->tcm_info) && 2487acb31faeSJiri Pirko TC_H_MIN(tcm->tcm_info) != tp->protocol) 2488acb31faeSJiri Pirko continue; 2489acb31faeSJiri Pirko if (*p_index > index_start) 2490acb31faeSJiri Pirko memset(&cb->args[1], 0, 2491acb31faeSJiri Pirko sizeof(cb->args) - sizeof(cb->args[0])); 2492acb31faeSJiri Pirko if (cb->args[1] == 0) { 249353189183SYueHaibing if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, 2494acb31faeSJiri Pirko NETLINK_CB(cb->skb).portid, 2495acb31faeSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 2496f8ab1807SVlad Buslov RTM_NEWTFILTER, false, true) <= 0) 2497fe2923afSVlad Buslov goto errout; 2498acb31faeSJiri Pirko cb->args[1] = 1; 2499acb31faeSJiri Pirko } 2500acb31faeSJiri Pirko if (!tp->ops->walk) 2501acb31faeSJiri Pirko continue; 2502acb31faeSJiri Pirko arg.w.fn = tcf_node_dump; 2503acb31faeSJiri Pirko arg.skb = skb; 2504acb31faeSJiri Pirko arg.cb = cb; 25057960d1daSJiri Pirko arg.block = block; 2506a10fa201SJiri Pirko arg.q = q; 2507a10fa201SJiri Pirko arg.parent = parent; 2508acb31faeSJiri Pirko arg.w.stop = 0; 2509acb31faeSJiri Pirko arg.w.skip = cb->args[1] - 1; 2510acb31faeSJiri Pirko arg.w.count = 0; 251101683a14SVlad Buslov arg.w.cookie = cb->args[2]; 2512f8ab1807SVlad Buslov arg.terse_dump = terse; 251312db03b6SVlad Buslov tp->ops->walk(tp, &arg.w, true); 251401683a14SVlad Buslov cb->args[2] = arg.w.cookie; 2515acb31faeSJiri Pirko cb->args[1] = arg.w.count + 1; 2516acb31faeSJiri Pirko if (arg.w.stop) 2517fe2923afSVlad Buslov goto errout; 2518acb31faeSJiri Pirko } 25195bc17018SJiri Pirko return true; 2520fe2923afSVlad Buslov 2521fe2923afSVlad Buslov errout: 252212db03b6SVlad Buslov tcf_proto_put(tp, true, NULL); 2523fe2923afSVlad Buslov return false; 2524acb31faeSJiri Pirko } 2525acb31faeSJiri Pirko 2526f8ab1807SVlad Buslov static const struct nla_policy tcf_tfilter_dump_policy[TCA_MAX + 1] = { 2527f8ab1807SVlad Buslov [TCA_DUMP_FLAGS] = NLA_POLICY_BITFIELD32(TCA_DUMP_FLAGS_TERSE), 2528f8ab1807SVlad Buslov }; 2529f8ab1807SVlad Buslov 2530bd27a875SEric Dumazet /* called with RTNL */ 25311da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 25321da177e4SLinus Torvalds { 2533bbf73830SVlad Buslov struct tcf_chain *chain, *chain_prev; 25343b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 25355bc17018SJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 25367960d1daSJiri Pirko struct Qdisc *q = NULL; 25376529eabaSJiri Pirko struct tcf_block *block; 2538942b8165SDavid S. Miller struct tcmsg *tcm = nlmsg_data(cb->nlh); 2539f8ab1807SVlad Buslov bool terse_dump = false; 2540acb31faeSJiri Pirko long index_start; 2541acb31faeSJiri Pirko long index; 2542a10fa201SJiri Pirko u32 parent; 25435bc17018SJiri Pirko int err; 25441da177e4SLinus Torvalds 2545573ce260SHong zhi guo if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 25461da177e4SLinus Torvalds return skb->len; 25475bc17018SJiri Pirko 25488cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 2549f8ab1807SVlad Buslov tcf_tfilter_dump_policy, cb->extack); 25505bc17018SJiri Pirko if (err) 25515bc17018SJiri Pirko return err; 25525bc17018SJiri Pirko 2553f8ab1807SVlad Buslov if (tca[TCA_DUMP_FLAGS]) { 2554f8ab1807SVlad Buslov struct nla_bitfield32 flags = 2555f8ab1807SVlad Buslov nla_get_bitfield32(tca[TCA_DUMP_FLAGS]); 2556f8ab1807SVlad Buslov 2557f8ab1807SVlad Buslov terse_dump = flags.value & TCA_DUMP_FLAGS_TERSE; 2558f8ab1807SVlad Buslov } 2559f8ab1807SVlad Buslov 25607960d1daSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2561787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 25627960d1daSJiri Pirko if (!block) 25637960d1daSJiri Pirko goto out; 2564d680b352SJiri Pirko /* If we work with block index, q is NULL and parent value 2565d680b352SJiri Pirko * will never be used in the following code. The check 2566d680b352SJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 2567d680b352SJiri Pirko * see that far, so set parent to zero to silence the warning 2568d680b352SJiri Pirko * about parent being uninitialized. 2569d680b352SJiri Pirko */ 2570d680b352SJiri Pirko parent = 0; 25717960d1daSJiri Pirko } else { 25727960d1daSJiri Pirko const struct Qdisc_class_ops *cops; 25737960d1daSJiri Pirko struct net_device *dev; 25747960d1daSJiri Pirko unsigned long cl = 0; 25757960d1daSJiri Pirko 2576cc7ec456SEric Dumazet dev = __dev_get_by_index(net, tcm->tcm_ifindex); 2577cc7ec456SEric Dumazet if (!dev) 25781da177e4SLinus Torvalds return skb->len; 25791da177e4SLinus Torvalds 2580a10fa201SJiri Pirko parent = tcm->tcm_parent; 2581a7df4870SCong Wang if (!parent) 2582af356afaSPatrick McHardy q = dev->qdisc; 2583a7df4870SCong Wang else 25841da177e4SLinus Torvalds q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 25851da177e4SLinus Torvalds if (!q) 25861da177e4SLinus Torvalds goto out; 2587cc7ec456SEric Dumazet cops = q->ops->cl_ops; 2588cc7ec456SEric Dumazet if (!cops) 2589143976ceSWANG Cong goto out; 25906529eabaSJiri Pirko if (!cops->tcf_block) 2591143976ceSWANG Cong goto out; 25921da177e4SLinus Torvalds if (TC_H_MIN(tcm->tcm_parent)) { 2593143976ceSWANG Cong cl = cops->find(q, tcm->tcm_parent); 25941da177e4SLinus Torvalds if (cl == 0) 2595143976ceSWANG Cong goto out; 25961da177e4SLinus Torvalds } 2597cbaacc4eSAlexander Aring block = cops->tcf_block(q, cl, NULL); 25986529eabaSJiri Pirko if (!block) 2599143976ceSWANG Cong goto out; 2600a7df4870SCong Wang parent = block->classid; 26017960d1daSJiri Pirko if (tcf_block_shared(block)) 26027960d1daSJiri Pirko q = NULL; 26037960d1daSJiri Pirko } 26041da177e4SLinus Torvalds 2605acb31faeSJiri Pirko index_start = cb->args[0]; 2606acb31faeSJiri Pirko index = 0; 26075bc17018SJiri Pirko 2608bbf73830SVlad Buslov for (chain = __tcf_get_next_chain(block, NULL); 2609bbf73830SVlad Buslov chain; 2610bbf73830SVlad Buslov chain_prev = chain, 2611bbf73830SVlad Buslov chain = __tcf_get_next_chain(block, chain), 2612bbf73830SVlad Buslov tcf_chain_put(chain_prev)) { 26135bc17018SJiri Pirko if (tca[TCA_CHAIN] && 26145bc17018SJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index) 26155bc17018SJiri Pirko continue; 2616a10fa201SJiri Pirko if (!tcf_chain_dump(chain, q, parent, skb, cb, 2617f8ab1807SVlad Buslov index_start, &index, terse_dump)) { 2618bbf73830SVlad Buslov tcf_chain_put(chain); 26195ae437adSRoman Kapl err = -EMSGSIZE; 26205bc17018SJiri Pirko break; 26215bc17018SJiri Pirko } 26225ae437adSRoman Kapl } 26235bc17018SJiri Pirko 2624787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 262512db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 2626acb31faeSJiri Pirko cb->args[0] = index; 26271da177e4SLinus Torvalds 26281da177e4SLinus Torvalds out: 26295ae437adSRoman Kapl /* If we did no progress, the error (EMSGSIZE) is real */ 26305ae437adSRoman Kapl if (skb->len == 0 && err) 26315ae437adSRoman Kapl return err; 26321da177e4SLinus Torvalds return skb->len; 26331da177e4SLinus Torvalds } 26341da177e4SLinus Torvalds 2635a5654820SVlad Buslov static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, 2636a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2637a5654820SVlad Buslov struct net *net, struct sk_buff *skb, 2638a5654820SVlad Buslov struct tcf_block *block, 263932a4f5ecSJiri Pirko u32 portid, u32 seq, u16 flags, int event) 264032a4f5ecSJiri Pirko { 264132a4f5ecSJiri Pirko unsigned char *b = skb_tail_pointer(skb); 26429f407f17SJiri Pirko const struct tcf_proto_ops *ops; 264332a4f5ecSJiri Pirko struct nlmsghdr *nlh; 264432a4f5ecSJiri Pirko struct tcmsg *tcm; 26459f407f17SJiri Pirko void *priv; 26469f407f17SJiri Pirko 2647a5654820SVlad Buslov ops = tmplt_ops; 2648a5654820SVlad Buslov priv = tmplt_priv; 264932a4f5ecSJiri Pirko 265032a4f5ecSJiri Pirko nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 265132a4f5ecSJiri Pirko if (!nlh) 265232a4f5ecSJiri Pirko goto out_nlmsg_trim; 265332a4f5ecSJiri Pirko tcm = nlmsg_data(nlh); 265432a4f5ecSJiri Pirko tcm->tcm_family = AF_UNSPEC; 265532a4f5ecSJiri Pirko tcm->tcm__pad1 = 0; 265632a4f5ecSJiri Pirko tcm->tcm__pad2 = 0; 265732a4f5ecSJiri Pirko tcm->tcm_handle = 0; 265832a4f5ecSJiri Pirko if (block->q) { 265932a4f5ecSJiri Pirko tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; 266032a4f5ecSJiri Pirko tcm->tcm_parent = block->q->handle; 266132a4f5ecSJiri Pirko } else { 266232a4f5ecSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 266332a4f5ecSJiri Pirko tcm->tcm_block_index = block->index; 266432a4f5ecSJiri Pirko } 266532a4f5ecSJiri Pirko 2666a5654820SVlad Buslov if (nla_put_u32(skb, TCA_CHAIN, chain_index)) 266732a4f5ecSJiri Pirko goto nla_put_failure; 266832a4f5ecSJiri Pirko 26699f407f17SJiri Pirko if (ops) { 26709f407f17SJiri Pirko if (nla_put_string(skb, TCA_KIND, ops->kind)) 26719f407f17SJiri Pirko goto nla_put_failure; 26729f407f17SJiri Pirko if (ops->tmplt_dump(skb, net, priv) < 0) 26739f407f17SJiri Pirko goto nla_put_failure; 26749f407f17SJiri Pirko } 26759f407f17SJiri Pirko 267632a4f5ecSJiri Pirko nlh->nlmsg_len = skb_tail_pointer(skb) - b; 267732a4f5ecSJiri Pirko return skb->len; 267832a4f5ecSJiri Pirko 267932a4f5ecSJiri Pirko out_nlmsg_trim: 268032a4f5ecSJiri Pirko nla_put_failure: 268132a4f5ecSJiri Pirko nlmsg_trim(skb, b); 268232a4f5ecSJiri Pirko return -EMSGSIZE; 268332a4f5ecSJiri Pirko } 268432a4f5ecSJiri Pirko 268532a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 268632a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast) 268732a4f5ecSJiri Pirko { 268832a4f5ecSJiri Pirko u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 268932a4f5ecSJiri Pirko struct tcf_block *block = chain->block; 269032a4f5ecSJiri Pirko struct net *net = block->net; 269132a4f5ecSJiri Pirko struct sk_buff *skb; 26925b5f99b1SZhike Wang int err = 0; 269332a4f5ecSJiri Pirko 269432a4f5ecSJiri Pirko skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 269532a4f5ecSJiri Pirko if (!skb) 269632a4f5ecSJiri Pirko return -ENOBUFS; 269732a4f5ecSJiri Pirko 2698a5654820SVlad Buslov if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 2699a5654820SVlad Buslov chain->index, net, skb, block, portid, 270032a4f5ecSJiri Pirko seq, flags, event) <= 0) { 270132a4f5ecSJiri Pirko kfree_skb(skb); 270232a4f5ecSJiri Pirko return -EINVAL; 270332a4f5ecSJiri Pirko } 270432a4f5ecSJiri Pirko 270532a4f5ecSJiri Pirko if (unicast) 2706f79a3bcbSYajun Deng err = rtnl_unicast(skb, net, portid); 27075b5f99b1SZhike Wang else 27085b5f99b1SZhike Wang err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 27095b5f99b1SZhike Wang flags & NLM_F_ECHO); 271032a4f5ecSJiri Pirko 27115b5f99b1SZhike Wang return err; 271232a4f5ecSJiri Pirko } 271332a4f5ecSJiri Pirko 2714a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 2715a5654820SVlad Buslov void *tmplt_priv, u32 chain_index, 2716a5654820SVlad Buslov struct tcf_block *block, struct sk_buff *oskb, 2717a5654820SVlad Buslov u32 seq, u16 flags, bool unicast) 2718a5654820SVlad Buslov { 2719a5654820SVlad Buslov u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 2720a5654820SVlad Buslov struct net *net = block->net; 2721a5654820SVlad Buslov struct sk_buff *skb; 2722a5654820SVlad Buslov 2723a5654820SVlad Buslov skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 2724a5654820SVlad Buslov if (!skb) 2725a5654820SVlad Buslov return -ENOBUFS; 2726a5654820SVlad Buslov 2727a5654820SVlad Buslov if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, 2728a5654820SVlad Buslov block, portid, seq, flags, RTM_DELCHAIN) <= 0) { 2729a5654820SVlad Buslov kfree_skb(skb); 2730a5654820SVlad Buslov return -EINVAL; 2731a5654820SVlad Buslov } 2732a5654820SVlad Buslov 2733a5654820SVlad Buslov if (unicast) 2734f79a3bcbSYajun Deng return rtnl_unicast(skb, net, portid); 2735a5654820SVlad Buslov 2736a5654820SVlad Buslov return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); 2737a5654820SVlad Buslov } 2738a5654820SVlad Buslov 27399f407f17SJiri Pirko static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, 27409f407f17SJiri Pirko struct nlattr **tca, 27419f407f17SJiri Pirko struct netlink_ext_ack *extack) 27429f407f17SJiri Pirko { 27439f407f17SJiri Pirko const struct tcf_proto_ops *ops; 27442dd5616eSEric Dumazet char name[IFNAMSIZ]; 27459f407f17SJiri Pirko void *tmplt_priv; 27469f407f17SJiri Pirko 27479f407f17SJiri Pirko /* If kind is not set, user did not specify template. */ 27489f407f17SJiri Pirko if (!tca[TCA_KIND]) 27499f407f17SJiri Pirko return 0; 27509f407f17SJiri Pirko 27512dd5616eSEric Dumazet if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 27522dd5616eSEric Dumazet NL_SET_ERR_MSG(extack, "Specified TC chain template name too long"); 27532dd5616eSEric Dumazet return -EINVAL; 27542dd5616eSEric Dumazet } 27552dd5616eSEric Dumazet 27562dd5616eSEric Dumazet ops = tcf_proto_lookup_ops(name, true, extack); 27579f407f17SJiri Pirko if (IS_ERR(ops)) 27589f407f17SJiri Pirko return PTR_ERR(ops); 27599f407f17SJiri Pirko if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { 27609f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); 27619f407f17SJiri Pirko return -EOPNOTSUPP; 27629f407f17SJiri Pirko } 27639f407f17SJiri Pirko 27649f407f17SJiri Pirko tmplt_priv = ops->tmplt_create(net, chain, tca, extack); 27659f407f17SJiri Pirko if (IS_ERR(tmplt_priv)) { 27669f407f17SJiri Pirko module_put(ops->owner); 27679f407f17SJiri Pirko return PTR_ERR(tmplt_priv); 27689f407f17SJiri Pirko } 27699f407f17SJiri Pirko chain->tmplt_ops = ops; 27709f407f17SJiri Pirko chain->tmplt_priv = tmplt_priv; 27719f407f17SJiri Pirko return 0; 27729f407f17SJiri Pirko } 27739f407f17SJiri Pirko 2774a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 2775a5654820SVlad Buslov void *tmplt_priv) 27769f407f17SJiri Pirko { 27779f407f17SJiri Pirko /* If template ops are set, no work to do for us. */ 2778a5654820SVlad Buslov if (!tmplt_ops) 27799f407f17SJiri Pirko return; 27809f407f17SJiri Pirko 2781a5654820SVlad Buslov tmplt_ops->tmplt_destroy(tmplt_priv); 2782a5654820SVlad Buslov module_put(tmplt_ops->owner); 27839f407f17SJiri Pirko } 27849f407f17SJiri Pirko 278532a4f5ecSJiri Pirko /* Add/delete/get a chain */ 278632a4f5ecSJiri Pirko 278732a4f5ecSJiri Pirko static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, 278832a4f5ecSJiri Pirko struct netlink_ext_ack *extack) 278932a4f5ecSJiri Pirko { 279032a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 279132a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 279232a4f5ecSJiri Pirko struct tcmsg *t; 279332a4f5ecSJiri Pirko u32 parent; 279432a4f5ecSJiri Pirko u32 chain_index; 279532a4f5ecSJiri Pirko struct Qdisc *q = NULL; 279632a4f5ecSJiri Pirko struct tcf_chain *chain = NULL; 279732a4f5ecSJiri Pirko struct tcf_block *block; 279832a4f5ecSJiri Pirko unsigned long cl; 279932a4f5ecSJiri Pirko int err; 280032a4f5ecSJiri Pirko 280132a4f5ecSJiri Pirko if (n->nlmsg_type != RTM_GETCHAIN && 280232a4f5ecSJiri Pirko !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 280332a4f5ecSJiri Pirko return -EPERM; 280432a4f5ecSJiri Pirko 280532a4f5ecSJiri Pirko replay: 28068cb08174SJohannes Berg err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 28078cb08174SJohannes Berg rtm_tca_policy, extack); 280832a4f5ecSJiri Pirko if (err < 0) 280932a4f5ecSJiri Pirko return err; 281032a4f5ecSJiri Pirko 281132a4f5ecSJiri Pirko t = nlmsg_data(n); 281232a4f5ecSJiri Pirko parent = t->tcm_parent; 281332a4f5ecSJiri Pirko cl = 0; 281432a4f5ecSJiri Pirko 281532a4f5ecSJiri Pirko block = tcf_block_find(net, &q, &parent, &cl, 281632a4f5ecSJiri Pirko t->tcm_ifindex, t->tcm_block_index, extack); 281732a4f5ecSJiri Pirko if (IS_ERR(block)) 281832a4f5ecSJiri Pirko return PTR_ERR(block); 281932a4f5ecSJiri Pirko 282032a4f5ecSJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 282132a4f5ecSJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 282232a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2823e368fdb6SVlad Buslov err = -EINVAL; 2824e368fdb6SVlad Buslov goto errout_block; 282532a4f5ecSJiri Pirko } 28262cbfab07SVlad Buslov 28272cbfab07SVlad Buslov mutex_lock(&block->lock); 282832a4f5ecSJiri Pirko chain = tcf_chain_lookup(block, chain_index); 282932a4f5ecSJiri Pirko if (n->nlmsg_type == RTM_NEWCHAIN) { 283032a4f5ecSJiri Pirko if (chain) { 28313d32f4c5SJiri Pirko if (tcf_chain_held_by_acts_only(chain)) { 28321f3ed383SJiri Pirko /* The chain exists only because there is 28333d32f4c5SJiri Pirko * some action referencing it. 28341f3ed383SJiri Pirko */ 28351f3ed383SJiri Pirko tcf_chain_hold(chain); 28361f3ed383SJiri Pirko } else { 283732a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Filter chain already exists"); 2838e368fdb6SVlad Buslov err = -EEXIST; 28392cbfab07SVlad Buslov goto errout_block_locked; 284032a4f5ecSJiri Pirko } 28411f3ed383SJiri Pirko } else { 284232a4f5ecSJiri Pirko if (!(n->nlmsg_flags & NLM_F_CREATE)) { 284332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); 2844e368fdb6SVlad Buslov err = -ENOENT; 28452cbfab07SVlad Buslov goto errout_block_locked; 284632a4f5ecSJiri Pirko } 284732a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 284832a4f5ecSJiri Pirko if (!chain) { 284932a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to create filter chain"); 2850e368fdb6SVlad Buslov err = -ENOMEM; 28512cbfab07SVlad Buslov goto errout_block_locked; 285232a4f5ecSJiri Pirko } 28531f3ed383SJiri Pirko } 285432a4f5ecSJiri Pirko } else { 28553d32f4c5SJiri Pirko if (!chain || tcf_chain_held_by_acts_only(chain)) { 285632a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2857e368fdb6SVlad Buslov err = -EINVAL; 28582cbfab07SVlad Buslov goto errout_block_locked; 285932a4f5ecSJiri Pirko } 286032a4f5ecSJiri Pirko tcf_chain_hold(chain); 286132a4f5ecSJiri Pirko } 286232a4f5ecSJiri Pirko 28632cbfab07SVlad Buslov if (n->nlmsg_type == RTM_NEWCHAIN) { 28642cbfab07SVlad Buslov /* Modifying chain requires holding parent block lock. In case 28652cbfab07SVlad Buslov * the chain was successfully added, take a reference to the 28662cbfab07SVlad Buslov * chain. This ensures that an empty chain does not disappear at 28672cbfab07SVlad Buslov * the end of this function. 286832a4f5ecSJiri Pirko */ 286932a4f5ecSJiri Pirko tcf_chain_hold(chain); 287032a4f5ecSJiri Pirko chain->explicitly_created = true; 28712cbfab07SVlad Buslov } 28722cbfab07SVlad Buslov mutex_unlock(&block->lock); 28732cbfab07SVlad Buslov 28742cbfab07SVlad Buslov switch (n->nlmsg_type) { 28752cbfab07SVlad Buslov case RTM_NEWCHAIN: 28762cbfab07SVlad Buslov err = tc_chain_tmplt_add(chain, net, tca, extack); 28772cbfab07SVlad Buslov if (err) { 28782cbfab07SVlad Buslov tcf_chain_put_explicitly_created(chain); 28792cbfab07SVlad Buslov goto errout; 28802cbfab07SVlad Buslov } 28812cbfab07SVlad Buslov 288232a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 288332a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 288432a4f5ecSJiri Pirko break; 288532a4f5ecSJiri Pirko case RTM_DELCHAIN: 2886f5b9bac7SCong Wang tfilter_notify_chain(net, skb, block, q, parent, n, 28870fca55edSVlad Buslov chain, RTM_DELTFILTER); 288832a4f5ecSJiri Pirko /* Flush the chain first as the user requested chain removal. */ 288912db03b6SVlad Buslov tcf_chain_flush(chain, true); 289032a4f5ecSJiri Pirko /* In case the chain was successfully deleted, put a reference 289132a4f5ecSJiri Pirko * to the chain previously taken during addition. 289232a4f5ecSJiri Pirko */ 289332a4f5ecSJiri Pirko tcf_chain_put_explicitly_created(chain); 289432a4f5ecSJiri Pirko break; 289532a4f5ecSJiri Pirko case RTM_GETCHAIN: 289632a4f5ecSJiri Pirko err = tc_chain_notify(chain, skb, n->nlmsg_seq, 28979d85a6f4SYajun Deng n->nlmsg_flags, n->nlmsg_type, true); 289832a4f5ecSJiri Pirko if (err < 0) 289932a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); 290032a4f5ecSJiri Pirko break; 290132a4f5ecSJiri Pirko default: 290232a4f5ecSJiri Pirko err = -EOPNOTSUPP; 290332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Unsupported message type"); 290432a4f5ecSJiri Pirko goto errout; 290532a4f5ecSJiri Pirko } 290632a4f5ecSJiri Pirko 290732a4f5ecSJiri Pirko errout: 290832a4f5ecSJiri Pirko tcf_chain_put(chain); 2909e368fdb6SVlad Buslov errout_block: 291012db03b6SVlad Buslov tcf_block_release(q, block, true); 291132a4f5ecSJiri Pirko if (err == -EAGAIN) 291232a4f5ecSJiri Pirko /* Replay the request. */ 291332a4f5ecSJiri Pirko goto replay; 291432a4f5ecSJiri Pirko return err; 29152cbfab07SVlad Buslov 29162cbfab07SVlad Buslov errout_block_locked: 29172cbfab07SVlad Buslov mutex_unlock(&block->lock); 29182cbfab07SVlad Buslov goto errout_block; 291932a4f5ecSJiri Pirko } 292032a4f5ecSJiri Pirko 292132a4f5ecSJiri Pirko /* called with RTNL */ 292232a4f5ecSJiri Pirko static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) 292332a4f5ecSJiri Pirko { 292432a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 292532a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 292632a4f5ecSJiri Pirko struct Qdisc *q = NULL; 292732a4f5ecSJiri Pirko struct tcf_block *block; 292832a4f5ecSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 2929ace4a267SVlad Buslov struct tcf_chain *chain; 293032a4f5ecSJiri Pirko long index_start; 293132a4f5ecSJiri Pirko long index; 293232a4f5ecSJiri Pirko int err; 293332a4f5ecSJiri Pirko 293432a4f5ecSJiri Pirko if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 293532a4f5ecSJiri Pirko return skb->len; 293632a4f5ecSJiri Pirko 29378cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 29388cb08174SJohannes Berg rtm_tca_policy, cb->extack); 293932a4f5ecSJiri Pirko if (err) 294032a4f5ecSJiri Pirko return err; 294132a4f5ecSJiri Pirko 294232a4f5ecSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2943787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 294432a4f5ecSJiri Pirko if (!block) 294532a4f5ecSJiri Pirko goto out; 294632a4f5ecSJiri Pirko } else { 294732a4f5ecSJiri Pirko const struct Qdisc_class_ops *cops; 294832a4f5ecSJiri Pirko struct net_device *dev; 294932a4f5ecSJiri Pirko unsigned long cl = 0; 295032a4f5ecSJiri Pirko 295132a4f5ecSJiri Pirko dev = __dev_get_by_index(net, tcm->tcm_ifindex); 295232a4f5ecSJiri Pirko if (!dev) 295332a4f5ecSJiri Pirko return skb->len; 295432a4f5ecSJiri Pirko 29550ad41b24SLukas Bulwahn if (!tcm->tcm_parent) 295632a4f5ecSJiri Pirko q = dev->qdisc; 29570ad41b24SLukas Bulwahn else 295832a4f5ecSJiri Pirko q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 29590ad41b24SLukas Bulwahn 296032a4f5ecSJiri Pirko if (!q) 296132a4f5ecSJiri Pirko goto out; 296232a4f5ecSJiri Pirko cops = q->ops->cl_ops; 296332a4f5ecSJiri Pirko if (!cops) 296432a4f5ecSJiri Pirko goto out; 296532a4f5ecSJiri Pirko if (!cops->tcf_block) 296632a4f5ecSJiri Pirko goto out; 296732a4f5ecSJiri Pirko if (TC_H_MIN(tcm->tcm_parent)) { 296832a4f5ecSJiri Pirko cl = cops->find(q, tcm->tcm_parent); 296932a4f5ecSJiri Pirko if (cl == 0) 297032a4f5ecSJiri Pirko goto out; 297132a4f5ecSJiri Pirko } 297232a4f5ecSJiri Pirko block = cops->tcf_block(q, cl, NULL); 297332a4f5ecSJiri Pirko if (!block) 297432a4f5ecSJiri Pirko goto out; 297532a4f5ecSJiri Pirko if (tcf_block_shared(block)) 297632a4f5ecSJiri Pirko q = NULL; 297732a4f5ecSJiri Pirko } 297832a4f5ecSJiri Pirko 297932a4f5ecSJiri Pirko index_start = cb->args[0]; 298032a4f5ecSJiri Pirko index = 0; 298132a4f5ecSJiri Pirko 2982ace4a267SVlad Buslov mutex_lock(&block->lock); 2983ace4a267SVlad Buslov list_for_each_entry(chain, &block->chain_list, list) { 298432a4f5ecSJiri Pirko if ((tca[TCA_CHAIN] && 298532a4f5ecSJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index)) 298632a4f5ecSJiri Pirko continue; 298732a4f5ecSJiri Pirko if (index < index_start) { 298832a4f5ecSJiri Pirko index++; 298932a4f5ecSJiri Pirko continue; 299032a4f5ecSJiri Pirko } 2991ace4a267SVlad Buslov if (tcf_chain_held_by_acts_only(chain)) 2992ace4a267SVlad Buslov continue; 2993a5654820SVlad Buslov err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 2994a5654820SVlad Buslov chain->index, net, skb, block, 299532a4f5ecSJiri Pirko NETLINK_CB(cb->skb).portid, 299632a4f5ecSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 299732a4f5ecSJiri Pirko RTM_NEWCHAIN); 2998ace4a267SVlad Buslov if (err <= 0) 299932a4f5ecSJiri Pirko break; 300032a4f5ecSJiri Pirko index++; 300132a4f5ecSJiri Pirko } 3002ace4a267SVlad Buslov mutex_unlock(&block->lock); 300332a4f5ecSJiri Pirko 3004787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 300512db03b6SVlad Buslov tcf_block_refcnt_put(block, true); 300632a4f5ecSJiri Pirko cb->args[0] = index; 300732a4f5ecSJiri Pirko 300832a4f5ecSJiri Pirko out: 300932a4f5ecSJiri Pirko /* If we did no progress, the error (EMSGSIZE) is real */ 301032a4f5ecSJiri Pirko if (skb->len == 0 && err) 301132a4f5ecSJiri Pirko return err; 301232a4f5ecSJiri Pirko return skb->len; 301332a4f5ecSJiri Pirko } 301432a4f5ecSJiri Pirko 301518d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts) 30161da177e4SLinus Torvalds { 30171da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30183d66b89cSEric Dumazet if (exts->actions) { 301990b73b77SVlad Buslov tcf_action_destroy(exts->actions, TCA_ACT_UNBIND); 302022dc13c8SWANG Cong kfree(exts->actions); 30213d66b89cSEric Dumazet } 302222dc13c8SWANG Cong exts->nr_actions = 0; 30231da177e4SLinus Torvalds #endif 30241da177e4SLinus Torvalds } 3025aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy); 30261da177e4SLinus Torvalds 3027c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 3028695176bfSCong Wang struct nlattr *rate_tlv, struct tcf_exts *exts, 3029695176bfSCong Wang u32 flags, struct netlink_ext_ack *extack) 30301da177e4SLinus Torvalds { 30311da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 30321da177e4SLinus Torvalds { 303387c750e8SVlad Buslov int init_res[TCA_ACT_MAX_PRIO] = {}; 30341da177e4SLinus Torvalds struct tc_action *act; 3035d04e6990SRoman Mashak size_t attr_size = 0; 30361da177e4SLinus Torvalds 30375da57f42SWANG Cong if (exts->police && tb[exts->police]) { 3038d349f997SCong Wang struct tc_action_ops *a_o; 3039d349f997SCong Wang 3040695176bfSCong Wang a_o = tc_action_load_ops(tb[exts->police], true, 3041695176bfSCong Wang !(flags & TCA_ACT_FLAGS_NO_RTNL), 3042695176bfSCong Wang extack); 3043d349f997SCong Wang if (IS_ERR(a_o)) 3044d349f997SCong Wang return PTR_ERR(a_o); 3045695176bfSCong Wang flags |= TCA_ACT_FLAGS_POLICE | TCA_ACT_FLAGS_BIND; 30469fb9f251SJiri Pirko act = tcf_action_init_1(net, tp, tb[exts->police], 3047695176bfSCong Wang rate_tlv, a_o, init_res, flags, 3048695176bfSCong Wang extack); 3049d349f997SCong Wang module_put(a_o->owner); 3050b3650bf7SVlad Buslov if (IS_ERR(act)) 3051ab27cfb8SPatrick McHardy return PTR_ERR(act); 30521da177e4SLinus Torvalds 305333be6271SWANG Cong act->type = exts->type = TCA_OLD_COMPAT; 305422dc13c8SWANG Cong exts->actions[0] = act; 305522dc13c8SWANG Cong exts->nr_actions = 1; 3056396d7f23SVlad Buslov tcf_idr_insert_many(exts->actions); 30575da57f42SWANG Cong } else if (exts->action && tb[exts->action]) { 305890b73b77SVlad Buslov int err; 305922dc13c8SWANG Cong 3060695176bfSCong Wang flags |= TCA_ACT_FLAGS_BIND; 30619fb9f251SJiri Pirko err = tcf_action_init(net, tp, tb[exts->action], 3062695176bfSCong Wang rate_tlv, exts->actions, init_res, 3063695176bfSCong Wang &attr_size, flags, extack); 306490b73b77SVlad Buslov if (err < 0) 306533be6271SWANG Cong return err; 306690b73b77SVlad Buslov exts->nr_actions = err; 30671da177e4SLinus Torvalds } 30681da177e4SLinus Torvalds } 30691da177e4SLinus Torvalds #else 30705da57f42SWANG Cong if ((exts->action && tb[exts->action]) || 307150a56190SAlexander Aring (exts->police && tb[exts->police])) { 307250a56190SAlexander Aring NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)"); 30731da177e4SLinus Torvalds return -EOPNOTSUPP; 307450a56190SAlexander Aring } 30751da177e4SLinus Torvalds #endif 30761da177e4SLinus Torvalds 30771da177e4SLinus Torvalds return 0; 30781da177e4SLinus Torvalds } 3079aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate); 30801da177e4SLinus Torvalds 30819b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 30821da177e4SLinus Torvalds { 30831da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 308422dc13c8SWANG Cong struct tcf_exts old = *dst; 308522dc13c8SWANG Cong 30869b0d4446SJiri Pirko *dst = *src; 308722dc13c8SWANG Cong tcf_exts_destroy(&old); 30881da177e4SLinus Torvalds #endif 30891da177e4SLinus Torvalds } 3090aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change); 30911da177e4SLinus Torvalds 309222dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT 309322dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 309422dc13c8SWANG Cong { 309522dc13c8SWANG Cong if (exts->nr_actions == 0) 309622dc13c8SWANG Cong return NULL; 309722dc13c8SWANG Cong else 309822dc13c8SWANG Cong return exts->actions[0]; 309922dc13c8SWANG Cong } 310022dc13c8SWANG Cong #endif 310133be6271SWANG Cong 31025da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 31031da177e4SLinus Torvalds { 31041da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 31059cc63db5SCong Wang struct nlattr *nest; 31069cc63db5SCong Wang 3107978dfd8dSJiri Pirko if (exts->action && tcf_exts_has_actions(exts)) { 31081da177e4SLinus Torvalds /* 31091da177e4SLinus Torvalds * again for backward compatible mode - we want 31101da177e4SLinus Torvalds * to work with both old and new modes of entering 31111da177e4SLinus Torvalds * tc data even if iproute2 was newer - jhs 31121da177e4SLinus Torvalds */ 311333be6271SWANG Cong if (exts->type != TCA_OLD_COMPAT) { 3114ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->action); 31154b3550efSPatrick McHardy if (nest == NULL) 31164b3550efSPatrick McHardy goto nla_put_failure; 311722dc13c8SWANG Cong 3118ca44b738SVlad Buslov if (tcf_action_dump(skb, exts->actions, 0, 0, false) 3119ca44b738SVlad Buslov < 0) 3120add93b61SPatrick McHardy goto nla_put_failure; 31214b3550efSPatrick McHardy nla_nest_end(skb, nest); 31225da57f42SWANG Cong } else if (exts->police) { 312333be6271SWANG Cong struct tc_action *act = tcf_exts_first_act(exts); 3124ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, exts->police); 312563acd680SJamal Hadi Salim if (nest == NULL || !act) 31264b3550efSPatrick McHardy goto nla_put_failure; 312733be6271SWANG Cong if (tcf_action_dump_old(skb, act, 0, 0) < 0) 3128add93b61SPatrick McHardy goto nla_put_failure; 31294b3550efSPatrick McHardy nla_nest_end(skb, nest); 31301da177e4SLinus Torvalds } 31311da177e4SLinus Torvalds } 31321da177e4SLinus Torvalds return 0; 31339cc63db5SCong Wang 31349cc63db5SCong Wang nla_put_failure: 31359cc63db5SCong Wang nla_nest_cancel(skb, nest); 31361da177e4SLinus Torvalds return -1; 31379cc63db5SCong Wang #else 31389cc63db5SCong Wang return 0; 31399cc63db5SCong Wang #endif 31401da177e4SLinus Torvalds } 3141aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump); 31421da177e4SLinus Torvalds 3143ca44b738SVlad Buslov int tcf_exts_terse_dump(struct sk_buff *skb, struct tcf_exts *exts) 3144ca44b738SVlad Buslov { 3145ca44b738SVlad Buslov #ifdef CONFIG_NET_CLS_ACT 3146ca44b738SVlad Buslov struct nlattr *nest; 3147ca44b738SVlad Buslov 3148ca44b738SVlad Buslov if (!exts->action || !tcf_exts_has_actions(exts)) 3149ca44b738SVlad Buslov return 0; 3150ca44b738SVlad Buslov 3151ca44b738SVlad Buslov nest = nla_nest_start_noflag(skb, exts->action); 3152ca44b738SVlad Buslov if (!nest) 3153ca44b738SVlad Buslov goto nla_put_failure; 3154ca44b738SVlad Buslov 3155ca44b738SVlad Buslov if (tcf_action_dump(skb, exts->actions, 0, 0, true) < 0) 3156ca44b738SVlad Buslov goto nla_put_failure; 3157ca44b738SVlad Buslov nla_nest_end(skb, nest); 3158ca44b738SVlad Buslov return 0; 3159ca44b738SVlad Buslov 3160ca44b738SVlad Buslov nla_put_failure: 3161ca44b738SVlad Buslov nla_nest_cancel(skb, nest); 3162ca44b738SVlad Buslov return -1; 3163ca44b738SVlad Buslov #else 3164ca44b738SVlad Buslov return 0; 3165ca44b738SVlad Buslov #endif 3166ca44b738SVlad Buslov } 3167ca44b738SVlad Buslov EXPORT_SYMBOL(tcf_exts_terse_dump); 3168aa767bfeSStephen Hemminger 31695da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 31701da177e4SLinus Torvalds { 31711da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 317233be6271SWANG Cong struct tc_action *a = tcf_exts_first_act(exts); 3173b057df24SIgnacy Gawędzki if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 317433be6271SWANG Cong return -1; 31751da177e4SLinus Torvalds #endif 31761da177e4SLinus Torvalds return 0; 31771da177e4SLinus Torvalds } 3178aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats); 31791da177e4SLinus Torvalds 318040119211SVlad Buslov static void tcf_block_offload_inc(struct tcf_block *block, u32 *flags) 318140119211SVlad Buslov { 318240119211SVlad Buslov if (*flags & TCA_CLS_FLAGS_IN_HW) 318340119211SVlad Buslov return; 318440119211SVlad Buslov *flags |= TCA_CLS_FLAGS_IN_HW; 318540119211SVlad Buslov atomic_inc(&block->offloadcnt); 318640119211SVlad Buslov } 318740119211SVlad Buslov 318840119211SVlad Buslov static void tcf_block_offload_dec(struct tcf_block *block, u32 *flags) 318940119211SVlad Buslov { 319040119211SVlad Buslov if (!(*flags & TCA_CLS_FLAGS_IN_HW)) 319140119211SVlad Buslov return; 319240119211SVlad Buslov *flags &= ~TCA_CLS_FLAGS_IN_HW; 319340119211SVlad Buslov atomic_dec(&block->offloadcnt); 319440119211SVlad Buslov } 319540119211SVlad Buslov 319640119211SVlad Buslov static void tc_cls_offload_cnt_update(struct tcf_block *block, 319740119211SVlad Buslov struct tcf_proto *tp, u32 *cnt, 319840119211SVlad Buslov u32 *flags, u32 diff, bool add) 319940119211SVlad Buslov { 320040119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 320140119211SVlad Buslov 320240119211SVlad Buslov spin_lock(&tp->lock); 320340119211SVlad Buslov if (add) { 320440119211SVlad Buslov if (!*cnt) 320540119211SVlad Buslov tcf_block_offload_inc(block, flags); 320640119211SVlad Buslov *cnt += diff; 320740119211SVlad Buslov } else { 320840119211SVlad Buslov *cnt -= diff; 320940119211SVlad Buslov if (!*cnt) 321040119211SVlad Buslov tcf_block_offload_dec(block, flags); 321140119211SVlad Buslov } 321240119211SVlad Buslov spin_unlock(&tp->lock); 321340119211SVlad Buslov } 321440119211SVlad Buslov 321540119211SVlad Buslov static void 321640119211SVlad Buslov tc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp, 321740119211SVlad Buslov u32 *cnt, u32 *flags) 321840119211SVlad Buslov { 321940119211SVlad Buslov lockdep_assert_held(&block->cb_lock); 322040119211SVlad Buslov 322140119211SVlad Buslov spin_lock(&tp->lock); 322240119211SVlad Buslov tcf_block_offload_dec(block, flags); 322340119211SVlad Buslov *cnt = 0; 322440119211SVlad Buslov spin_unlock(&tp->lock); 322540119211SVlad Buslov } 322640119211SVlad Buslov 322740119211SVlad Buslov static int 322840119211SVlad Buslov __tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 3229aeb3fecdSCong Wang void *type_data, bool err_stop) 3230717503b9SJiri Pirko { 3231955bcb6eSPablo Neira Ayuso struct flow_block_cb *block_cb; 3232aeb3fecdSCong Wang int ok_count = 0; 3233aeb3fecdSCong Wang int err; 3234aeb3fecdSCong Wang 323540119211SVlad Buslov list_for_each_entry(block_cb, &block->flow_block.cb_list, list) { 323640119211SVlad Buslov err = block_cb->cb(type, type_data, block_cb->cb_priv); 323740119211SVlad Buslov if (err) { 323840119211SVlad Buslov if (err_stop) 323940119211SVlad Buslov return err; 324040119211SVlad Buslov } else { 324140119211SVlad Buslov ok_count++; 324240119211SVlad Buslov } 324340119211SVlad Buslov } 324440119211SVlad Buslov return ok_count; 324540119211SVlad Buslov } 324640119211SVlad Buslov 324740119211SVlad Buslov int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 324840119211SVlad Buslov void *type_data, bool err_stop, bool rtnl_held) 324940119211SVlad Buslov { 325011bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 325140119211SVlad Buslov int ok_count; 325240119211SVlad Buslov 325311bd634dSVlad Buslov retry: 325411bd634dSVlad Buslov if (take_rtnl) 325511bd634dSVlad Buslov rtnl_lock(); 325640119211SVlad Buslov down_read(&block->cb_lock); 325711bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 325811bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 325911bd634dSVlad Buslov * obtain the locks in same order here. 326011bd634dSVlad Buslov */ 326111bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 326240119211SVlad Buslov up_read(&block->cb_lock); 326311bd634dSVlad Buslov take_rtnl = true; 326411bd634dSVlad Buslov goto retry; 326511bd634dSVlad Buslov } 326611bd634dSVlad Buslov 326711bd634dSVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 326811bd634dSVlad Buslov 326911bd634dSVlad Buslov up_read(&block->cb_lock); 327011bd634dSVlad Buslov if (take_rtnl) 327111bd634dSVlad Buslov rtnl_unlock(); 327240119211SVlad Buslov return ok_count; 327340119211SVlad Buslov } 327440119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_call); 327540119211SVlad Buslov 327640119211SVlad Buslov /* Non-destructive filter add. If filter that wasn't already in hardware is 327740119211SVlad Buslov * successfully offloaded, increment block offloads counter. On failure, 327840119211SVlad Buslov * previously offloaded filter is considered to be intact and offloads counter 327940119211SVlad Buslov * is not decremented. 328040119211SVlad Buslov */ 328140119211SVlad Buslov 328240119211SVlad Buslov int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp, 328340119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 328440119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 328540119211SVlad Buslov { 328611bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 328740119211SVlad Buslov int ok_count; 328840119211SVlad Buslov 328911bd634dSVlad Buslov retry: 329011bd634dSVlad Buslov if (take_rtnl) 329111bd634dSVlad Buslov rtnl_lock(); 32924f8116c8SVlad Buslov down_read(&block->cb_lock); 329311bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 329411bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 329511bd634dSVlad Buslov * obtain the locks in same order here. 329611bd634dSVlad Buslov */ 329711bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 329811bd634dSVlad Buslov up_read(&block->cb_lock); 329911bd634dSVlad Buslov take_rtnl = true; 330011bd634dSVlad Buslov goto retry; 330111bd634dSVlad Buslov } 330211bd634dSVlad Buslov 3303aeb3fecdSCong Wang /* Make sure all netdevs sharing this block are offload-capable. */ 33044f8116c8SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 33054f8116c8SVlad Buslov ok_count = -EOPNOTSUPP; 33064f8116c8SVlad Buslov goto err_unlock; 33074f8116c8SVlad Buslov } 3308aeb3fecdSCong Wang 330940119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3310a449a3e7SVlad Buslov if (ok_count < 0) 3311a449a3e7SVlad Buslov goto err_unlock; 3312a449a3e7SVlad Buslov 3313a449a3e7SVlad Buslov if (tp->ops->hw_add) 3314a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 331540119211SVlad Buslov if (ok_count > 0) 331640119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 331740119211SVlad Buslov ok_count, true); 33184f8116c8SVlad Buslov err_unlock: 33194f8116c8SVlad Buslov up_read(&block->cb_lock); 332011bd634dSVlad Buslov if (take_rtnl) 332111bd634dSVlad Buslov rtnl_unlock(); 332240119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 3323717503b9SJiri Pirko } 332440119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_add); 332540119211SVlad Buslov 332640119211SVlad Buslov /* Destructive filter replace. If filter that wasn't already in hardware is 332740119211SVlad Buslov * successfully offloaded, increment block offload counter. On failure, 332840119211SVlad Buslov * previously offloaded filter is considered to be destroyed and offload counter 332940119211SVlad Buslov * is decremented. 333040119211SVlad Buslov */ 333140119211SVlad Buslov 333240119211SVlad Buslov int tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp, 333340119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 333440119211SVlad Buslov u32 *old_flags, unsigned int *old_in_hw_count, 333540119211SVlad Buslov u32 *new_flags, unsigned int *new_in_hw_count, 333640119211SVlad Buslov bool rtnl_held) 333740119211SVlad Buslov { 333811bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 333940119211SVlad Buslov int ok_count; 334040119211SVlad Buslov 334111bd634dSVlad Buslov retry: 334211bd634dSVlad Buslov if (take_rtnl) 334311bd634dSVlad Buslov rtnl_lock(); 334440119211SVlad Buslov down_read(&block->cb_lock); 334511bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 334611bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 334711bd634dSVlad Buslov * obtain the locks in same order here. 334811bd634dSVlad Buslov */ 334911bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 335011bd634dSVlad Buslov up_read(&block->cb_lock); 335111bd634dSVlad Buslov take_rtnl = true; 335211bd634dSVlad Buslov goto retry; 335311bd634dSVlad Buslov } 335411bd634dSVlad Buslov 335540119211SVlad Buslov /* Make sure all netdevs sharing this block are offload-capable. */ 335640119211SVlad Buslov if (block->nooffloaddevcnt && err_stop) { 335740119211SVlad Buslov ok_count = -EOPNOTSUPP; 335840119211SVlad Buslov goto err_unlock; 335940119211SVlad Buslov } 336040119211SVlad Buslov 336140119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags); 3362a449a3e7SVlad Buslov if (tp->ops->hw_del) 3363a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 336440119211SVlad Buslov 336540119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 3366a449a3e7SVlad Buslov if (ok_count < 0) 3367a449a3e7SVlad Buslov goto err_unlock; 3368a449a3e7SVlad Buslov 3369a449a3e7SVlad Buslov if (tp->ops->hw_add) 3370a449a3e7SVlad Buslov tp->ops->hw_add(tp, type_data); 337140119211SVlad Buslov if (ok_count > 0) 3372a449a3e7SVlad Buslov tc_cls_offload_cnt_update(block, tp, new_in_hw_count, 3373a449a3e7SVlad Buslov new_flags, ok_count, true); 337440119211SVlad Buslov err_unlock: 337540119211SVlad Buslov up_read(&block->cb_lock); 337611bd634dSVlad Buslov if (take_rtnl) 337711bd634dSVlad Buslov rtnl_unlock(); 337840119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 337940119211SVlad Buslov } 338040119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_replace); 338140119211SVlad Buslov 338240119211SVlad Buslov /* Destroy filter and decrement block offload counter, if filter was previously 338340119211SVlad Buslov * offloaded. 338440119211SVlad Buslov */ 338540119211SVlad Buslov 338640119211SVlad Buslov int tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp, 338740119211SVlad Buslov enum tc_setup_type type, void *type_data, bool err_stop, 338840119211SVlad Buslov u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 338940119211SVlad Buslov { 339011bd634dSVlad Buslov bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 339140119211SVlad Buslov int ok_count; 339240119211SVlad Buslov 339311bd634dSVlad Buslov retry: 339411bd634dSVlad Buslov if (take_rtnl) 339511bd634dSVlad Buslov rtnl_lock(); 339640119211SVlad Buslov down_read(&block->cb_lock); 339711bd634dSVlad Buslov /* Need to obtain rtnl lock if block is bound to devs that require it. 339811bd634dSVlad Buslov * In block bind code cb_lock is obtained while holding rtnl, so we must 339911bd634dSVlad Buslov * obtain the locks in same order here. 340011bd634dSVlad Buslov */ 340111bd634dSVlad Buslov if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 340211bd634dSVlad Buslov up_read(&block->cb_lock); 340311bd634dSVlad Buslov take_rtnl = true; 340411bd634dSVlad Buslov goto retry; 340511bd634dSVlad Buslov } 340611bd634dSVlad Buslov 340740119211SVlad Buslov ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 340840119211SVlad Buslov 340940119211SVlad Buslov tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags); 3410a449a3e7SVlad Buslov if (tp->ops->hw_del) 3411a449a3e7SVlad Buslov tp->ops->hw_del(tp, type_data); 3412a449a3e7SVlad Buslov 341340119211SVlad Buslov up_read(&block->cb_lock); 341411bd634dSVlad Buslov if (take_rtnl) 341511bd634dSVlad Buslov rtnl_unlock(); 341640119211SVlad Buslov return ok_count < 0 ? ok_count : 0; 341740119211SVlad Buslov } 341840119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_destroy); 341940119211SVlad Buslov 342040119211SVlad Buslov int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp, 342140119211SVlad Buslov bool add, flow_setup_cb_t *cb, 342240119211SVlad Buslov enum tc_setup_type type, void *type_data, 342340119211SVlad Buslov void *cb_priv, u32 *flags, unsigned int *in_hw_count) 342440119211SVlad Buslov { 342540119211SVlad Buslov int err = cb(type, type_data, cb_priv); 342640119211SVlad Buslov 342740119211SVlad Buslov if (err) { 342840119211SVlad Buslov if (add && tc_skip_sw(*flags)) 342940119211SVlad Buslov return err; 343040119211SVlad Buslov } else { 343140119211SVlad Buslov tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1, 343240119211SVlad Buslov add); 343340119211SVlad Buslov } 343440119211SVlad Buslov 343540119211SVlad Buslov return 0; 343640119211SVlad Buslov } 343740119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_reoffload); 3438b3f55bddSJiri Pirko 34392008495dSJiri Pirko static int tcf_act_get_cookie(struct flow_action_entry *entry, 34402008495dSJiri Pirko const struct tc_action *act) 34412008495dSJiri Pirko { 34422008495dSJiri Pirko struct tc_cookie *cookie; 34432008495dSJiri Pirko int err = 0; 34442008495dSJiri Pirko 34452008495dSJiri Pirko rcu_read_lock(); 34462008495dSJiri Pirko cookie = rcu_dereference(act->act_cookie); 34472008495dSJiri Pirko if (cookie) { 34482008495dSJiri Pirko entry->cookie = flow_action_cookie_create(cookie->data, 34492008495dSJiri Pirko cookie->len, 34502008495dSJiri Pirko GFP_ATOMIC); 34512008495dSJiri Pirko if (!entry->cookie) 34522008495dSJiri Pirko err = -ENOMEM; 34532008495dSJiri Pirko } 34542008495dSJiri Pirko rcu_read_unlock(); 34552008495dSJiri Pirko return err; 34562008495dSJiri Pirko } 34572008495dSJiri Pirko 34582008495dSJiri Pirko static void tcf_act_put_cookie(struct flow_action_entry *entry) 34592008495dSJiri Pirko { 34602008495dSJiri Pirko flow_action_cookie_destroy(entry->cookie); 34612008495dSJiri Pirko } 34622008495dSJiri Pirko 34635a6ff4b1SVlad Buslov void tc_cleanup_flow_action(struct flow_action *flow_action) 34645a6ff4b1SVlad Buslov { 34655a6ff4b1SVlad Buslov struct flow_action_entry *entry; 34665a6ff4b1SVlad Buslov int i; 34675a6ff4b1SVlad Buslov 34682008495dSJiri Pirko flow_action_for_each(i, entry, flow_action) { 34692008495dSJiri Pirko tcf_act_put_cookie(entry); 34701158958aSVlad Buslov if (entry->destructor) 34711158958aSVlad Buslov entry->destructor(entry->destructor_priv); 34725a6ff4b1SVlad Buslov } 34732008495dSJiri Pirko } 34745a6ff4b1SVlad Buslov EXPORT_SYMBOL(tc_cleanup_flow_action); 34755a6ff4b1SVlad Buslov 34761158958aSVlad Buslov static void tcf_mirred_get_dev(struct flow_action_entry *entry, 34771158958aSVlad Buslov const struct tc_action *act) 34781158958aSVlad Buslov { 3479470d5060SVlad Buslov #ifdef CONFIG_NET_CLS_ACT 3480470d5060SVlad Buslov entry->dev = act->ops->get_dev(act, &entry->destructor); 34811158958aSVlad Buslov if (!entry->dev) 34821158958aSVlad Buslov return; 34831158958aSVlad Buslov entry->destructor_priv = entry->dev; 3484470d5060SVlad Buslov #endif 34851158958aSVlad Buslov } 34861158958aSVlad Buslov 34871158958aSVlad Buslov static void tcf_tunnel_encap_put_tunnel(void *priv) 34881158958aSVlad Buslov { 34891158958aSVlad Buslov struct ip_tunnel_info *tunnel = priv; 34901158958aSVlad Buslov 34911158958aSVlad Buslov kfree(tunnel); 34921158958aSVlad Buslov } 34931158958aSVlad Buslov 34941158958aSVlad Buslov static int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry, 34951158958aSVlad Buslov const struct tc_action *act) 34961158958aSVlad Buslov { 34971158958aSVlad Buslov entry->tunnel = tcf_tunnel_info_copy(act); 34981158958aSVlad Buslov if (!entry->tunnel) 34991158958aSVlad Buslov return -ENOMEM; 35001158958aSVlad Buslov entry->destructor = tcf_tunnel_encap_put_tunnel; 35011158958aSVlad Buslov entry->destructor_priv = entry->tunnel; 35021158958aSVlad Buslov return 0; 35031158958aSVlad Buslov } 35041158958aSVlad Buslov 35054a5da47dSVlad Buslov static void tcf_sample_get_group(struct flow_action_entry *entry, 35064a5da47dSVlad Buslov const struct tc_action *act) 35074a5da47dSVlad Buslov { 35084a5da47dSVlad Buslov #ifdef CONFIG_NET_CLS_ACT 35094a5da47dSVlad Buslov entry->sample.psample_group = 35104a5da47dSVlad Buslov act->ops->get_psample_group(act, &entry->destructor); 35114a5da47dSVlad Buslov entry->destructor_priv = entry->sample.psample_group; 35124a5da47dSVlad Buslov #endif 35134a5da47dSVlad Buslov } 35144a5da47dSVlad Buslov 3515d29bdd69SPo Liu static void tcf_gate_entry_destructor(void *priv) 3516d29bdd69SPo Liu { 3517d29bdd69SPo Liu struct action_gate_entry *oe = priv; 3518d29bdd69SPo Liu 3519d29bdd69SPo Liu kfree(oe); 3520d29bdd69SPo Liu } 3521d29bdd69SPo Liu 3522d29bdd69SPo Liu static int tcf_gate_get_entries(struct flow_action_entry *entry, 3523d29bdd69SPo Liu const struct tc_action *act) 3524d29bdd69SPo Liu { 3525d29bdd69SPo Liu entry->gate.entries = tcf_gate_get_list(act); 3526d29bdd69SPo Liu 3527d29bdd69SPo Liu if (!entry->gate.entries) 3528d29bdd69SPo Liu return -EINVAL; 3529d29bdd69SPo Liu 3530d29bdd69SPo Liu entry->destructor = tcf_gate_entry_destructor; 3531d29bdd69SPo Liu entry->destructor_priv = entry->gate.entries; 3532d29bdd69SPo Liu 3533d29bdd69SPo Liu return 0; 3534d29bdd69SPo Liu } 3535d29bdd69SPo Liu 353616f80360SPablo Neira Ayuso static enum flow_action_hw_stats tc_act_hw_stats(u8 hw_stats) 353716f80360SPablo Neira Ayuso { 353816f80360SPablo Neira Ayuso if (WARN_ON_ONCE(hw_stats > TCA_ACT_HW_STATS_ANY)) 353916f80360SPablo Neira Ayuso return FLOW_ACTION_HW_STATS_DONT_CARE; 354016f80360SPablo Neira Ayuso else if (!hw_stats) 354116f80360SPablo Neira Ayuso return FLOW_ACTION_HW_STATS_DISABLED; 354216f80360SPablo Neira Ayuso 354316f80360SPablo Neira Ayuso return hw_stats; 354416f80360SPablo Neira Ayuso } 354516f80360SPablo Neira Ayuso 35463a7b6861SPablo Neira Ayuso int tc_setup_flow_action(struct flow_action *flow_action, 3547b15e7a6eSVlad Buslov const struct tcf_exts *exts) 35483a7b6861SPablo Neira Ayuso { 35497a472814SVlad Buslov struct tc_action *act; 35509838b20aSVlad Buslov int i, j, k, err = 0; 35513a7b6861SPablo Neira Ayuso 35520dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_ANY != FLOW_ACTION_HW_STATS_ANY); 35530dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE); 35540dfb2d82SJakub Kicinski BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED); 355544f86580SJiri Pirko 35563a7b6861SPablo Neira Ayuso if (!exts) 35573a7b6861SPablo Neira Ayuso return 0; 35583a7b6861SPablo Neira Ayuso 35593a7b6861SPablo Neira Ayuso j = 0; 35603a7b6861SPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 35613a7b6861SPablo Neira Ayuso struct flow_action_entry *entry; 35623a7b6861SPablo Neira Ayuso 35633a7b6861SPablo Neira Ayuso entry = &flow_action->entries[j]; 35647a472814SVlad Buslov spin_lock_bh(&act->tcfa_lock); 35652008495dSJiri Pirko err = tcf_act_get_cookie(entry, act); 35662008495dSJiri Pirko if (err) 35672008495dSJiri Pirko goto err_out_locked; 356844f86580SJiri Pirko 356916f80360SPablo Neira Ayuso entry->hw_stats = tc_act_hw_stats(act->hw_stats); 357044f86580SJiri Pirko 35713a7b6861SPablo Neira Ayuso if (is_tcf_gact_ok(act)) { 35723a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ACCEPT; 35733a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_shot(act)) { 35743a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_DROP; 35753a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_trap(act)) { 35763a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TRAP; 35773a7b6861SPablo Neira Ayuso } else if (is_tcf_gact_goto_chain(act)) { 35783a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_GOTO; 35793a7b6861SPablo Neira Ayuso entry->chain_index = tcf_gact_goto_chain_index(act); 35803a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_redirect(act)) { 35813a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_REDIRECT; 35821158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 35833a7b6861SPablo Neira Ayuso } else if (is_tcf_mirred_egress_mirror(act)) { 35843a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MIRRED; 35851158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 358648e584acSJohn Hurley } else if (is_tcf_mirred_ingress_redirect(act)) { 358748e584acSJohn Hurley entry->id = FLOW_ACTION_REDIRECT_INGRESS; 35881158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 358948e584acSJohn Hurley } else if (is_tcf_mirred_ingress_mirror(act)) { 359048e584acSJohn Hurley entry->id = FLOW_ACTION_MIRRED_INGRESS; 35911158958aSVlad Buslov tcf_mirred_get_dev(entry, act); 35923a7b6861SPablo Neira Ayuso } else if (is_tcf_vlan(act)) { 35933a7b6861SPablo Neira Ayuso switch (tcf_vlan_action(act)) { 35943a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_PUSH: 35953a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_PUSH; 35963a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 35973a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 35983a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 35993a7b6861SPablo Neira Ayuso break; 36003a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_POP: 36013a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_POP; 36023a7b6861SPablo Neira Ayuso break; 36033a7b6861SPablo Neira Ayuso case TCA_VLAN_ACT_MODIFY: 36043a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_VLAN_MANGLE; 36053a7b6861SPablo Neira Ayuso entry->vlan.vid = tcf_vlan_push_vid(act); 36063a7b6861SPablo Neira Ayuso entry->vlan.proto = tcf_vlan_push_proto(act); 36073a7b6861SPablo Neira Ayuso entry->vlan.prio = tcf_vlan_push_prio(act); 36083a7b6861SPablo Neira Ayuso break; 36093a7b6861SPablo Neira Ayuso default: 36109838b20aSVlad Buslov err = -EOPNOTSUPP; 36117a472814SVlad Buslov goto err_out_locked; 36123a7b6861SPablo Neira Ayuso } 36133a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_set(act)) { 36143a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_ENCAP; 36151158958aSVlad Buslov err = tcf_tunnel_encap_get_tunnel(entry, act); 36161158958aSVlad Buslov if (err) 36177a472814SVlad Buslov goto err_out_locked; 36183a7b6861SPablo Neira Ayuso } else if (is_tcf_tunnel_release(act)) { 36193a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_TUNNEL_DECAP; 36203a7b6861SPablo Neira Ayuso } else if (is_tcf_pedit(act)) { 36213a7b6861SPablo Neira Ayuso for (k = 0; k < tcf_pedit_nkeys(act); k++) { 36223a7b6861SPablo Neira Ayuso switch (tcf_pedit_cmd(act, k)) { 36233a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_SET: 36243a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MANGLE; 36253a7b6861SPablo Neira Ayuso break; 36263a7b6861SPablo Neira Ayuso case TCA_PEDIT_KEY_EX_CMD_ADD: 36273a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_ADD; 36283a7b6861SPablo Neira Ayuso break; 36293a7b6861SPablo Neira Ayuso default: 36309838b20aSVlad Buslov err = -EOPNOTSUPP; 36317a472814SVlad Buslov goto err_out_locked; 36323a7b6861SPablo Neira Ayuso } 36333a7b6861SPablo Neira Ayuso entry->mangle.htype = tcf_pedit_htype(act, k); 36343a7b6861SPablo Neira Ayuso entry->mangle.mask = tcf_pedit_mask(act, k); 36353a7b6861SPablo Neira Ayuso entry->mangle.val = tcf_pedit_val(act, k); 36363a7b6861SPablo Neira Ayuso entry->mangle.offset = tcf_pedit_offset(act, k); 363716f80360SPablo Neira Ayuso entry->hw_stats = tc_act_hw_stats(act->hw_stats); 36382c4b58dcSPetr Machata entry = &flow_action->entries[++j]; 36393a7b6861SPablo Neira Ayuso } 36403a7b6861SPablo Neira Ayuso } else if (is_tcf_csum(act)) { 36413a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_CSUM; 36423a7b6861SPablo Neira Ayuso entry->csum_flags = tcf_csum_update_flags(act); 36433a7b6861SPablo Neira Ayuso } else if (is_tcf_skbedit_mark(act)) { 36443a7b6861SPablo Neira Ayuso entry->id = FLOW_ACTION_MARK; 36453a7b6861SPablo Neira Ayuso entry->mark = tcf_skbedit_mark(act); 3646a7a7be60SPieter Jansen van Vuuren } else if (is_tcf_sample(act)) { 3647a7a7be60SPieter Jansen van Vuuren entry->id = FLOW_ACTION_SAMPLE; 3648a7a7be60SPieter Jansen van Vuuren entry->sample.trunc_size = tcf_sample_trunc_size(act); 3649a7a7be60SPieter Jansen van Vuuren entry->sample.truncate = tcf_sample_truncate(act); 3650a7a7be60SPieter Jansen van Vuuren entry->sample.rate = tcf_sample_rate(act); 36514a5da47dSVlad Buslov tcf_sample_get_group(entry, act); 36528c8cfc6eSPieter Jansen van Vuuren } else if (is_tcf_police(act)) { 36538c8cfc6eSPieter Jansen van Vuuren entry->id = FLOW_ACTION_POLICE; 36545f035af7SPo Liu entry->police.burst = tcf_police_burst(act); 36558c8cfc6eSPieter Jansen van Vuuren entry->police.rate_bytes_ps = 36568c8cfc6eSPieter Jansen van Vuuren tcf_police_rate_bytes_ps(act); 365725660156SXingfeng Hu entry->police.burst_pkt = tcf_police_burst_pkt(act); 365825660156SXingfeng Hu entry->police.rate_pkt_ps = 365925660156SXingfeng Hu tcf_police_rate_pkt_ps(act); 366019e528dcSPo Liu entry->police.mtu = tcf_police_tcfp_mtu(act); 3661627e39b1SPo Liu entry->police.index = act->tcfa_index; 3662b57dc7c1SPaul Blakey } else if (is_tcf_ct(act)) { 3663b57dc7c1SPaul Blakey entry->id = FLOW_ACTION_CT; 3664b57dc7c1SPaul Blakey entry->ct.action = tcf_ct_action(act); 3665b57dc7c1SPaul Blakey entry->ct.zone = tcf_ct_zone(act); 3666edd5861eSPaul Blakey entry->ct.flow_table = tcf_ct_ft(act); 36676749d590SJohn Hurley } else if (is_tcf_mpls(act)) { 36686749d590SJohn Hurley switch (tcf_mpls_action(act)) { 36696749d590SJohn Hurley case TCA_MPLS_ACT_PUSH: 36706749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_PUSH; 36716749d590SJohn Hurley entry->mpls_push.proto = tcf_mpls_proto(act); 36726749d590SJohn Hurley entry->mpls_push.label = tcf_mpls_label(act); 36736749d590SJohn Hurley entry->mpls_push.tc = tcf_mpls_tc(act); 36746749d590SJohn Hurley entry->mpls_push.bos = tcf_mpls_bos(act); 36756749d590SJohn Hurley entry->mpls_push.ttl = tcf_mpls_ttl(act); 36766749d590SJohn Hurley break; 36776749d590SJohn Hurley case TCA_MPLS_ACT_POP: 36786749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_POP; 36796749d590SJohn Hurley entry->mpls_pop.proto = tcf_mpls_proto(act); 36806749d590SJohn Hurley break; 36816749d590SJohn Hurley case TCA_MPLS_ACT_MODIFY: 36826749d590SJohn Hurley entry->id = FLOW_ACTION_MPLS_MANGLE; 36836749d590SJohn Hurley entry->mpls_mangle.label = tcf_mpls_label(act); 36846749d590SJohn Hurley entry->mpls_mangle.tc = tcf_mpls_tc(act); 36856749d590SJohn Hurley entry->mpls_mangle.bos = tcf_mpls_bos(act); 36866749d590SJohn Hurley entry->mpls_mangle.ttl = tcf_mpls_ttl(act); 36876749d590SJohn Hurley break; 36886749d590SJohn Hurley default: 36897a472814SVlad Buslov goto err_out_locked; 36906749d590SJohn Hurley } 3691fb1b775aSJohn Hurley } else if (is_tcf_skbedit_ptype(act)) { 3692fb1b775aSJohn Hurley entry->id = FLOW_ACTION_PTYPE; 3693fb1b775aSJohn Hurley entry->ptype = tcf_skbedit_ptype(act); 36942ce12410SPetr Machata } else if (is_tcf_skbedit_priority(act)) { 36952ce12410SPetr Machata entry->id = FLOW_ACTION_PRIORITY; 36962ce12410SPetr Machata entry->priority = tcf_skbedit_priority(act); 3697d29bdd69SPo Liu } else if (is_tcf_gate(act)) { 3698d29bdd69SPo Liu entry->id = FLOW_ACTION_GATE; 3699d29bdd69SPo Liu entry->gate.index = tcf_gate_index(act); 3700d29bdd69SPo Liu entry->gate.prio = tcf_gate_prio(act); 3701d29bdd69SPo Liu entry->gate.basetime = tcf_gate_basetime(act); 3702d29bdd69SPo Liu entry->gate.cycletime = tcf_gate_cycletime(act); 3703d29bdd69SPo Liu entry->gate.cycletimeext = tcf_gate_cycletimeext(act); 3704d29bdd69SPo Liu entry->gate.num_entries = tcf_gate_num_entries(act); 3705d29bdd69SPo Liu err = tcf_gate_get_entries(entry, act); 3706d29bdd69SPo Liu if (err) 3707b1307621SGuillaume Nault goto err_out_locked; 37083a7b6861SPablo Neira Ayuso } else { 37099838b20aSVlad Buslov err = -EOPNOTSUPP; 37107a472814SVlad Buslov goto err_out_locked; 37113a7b6861SPablo Neira Ayuso } 37127a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 37133a7b6861SPablo Neira Ayuso 37143a7b6861SPablo Neira Ayuso if (!is_tcf_pedit(act)) 37153a7b6861SPablo Neira Ayuso j++; 37163a7b6861SPablo Neira Ayuso } 37179838b20aSVlad Buslov 37183a7b6861SPablo Neira Ayuso err_out: 37195a6ff4b1SVlad Buslov if (err) 37205a6ff4b1SVlad Buslov tc_cleanup_flow_action(flow_action); 37215a6ff4b1SVlad Buslov 37229838b20aSVlad Buslov return err; 37237a472814SVlad Buslov err_out_locked: 37247a472814SVlad Buslov spin_unlock_bh(&act->tcfa_lock); 37257a472814SVlad Buslov goto err_out; 37263a7b6861SPablo Neira Ayuso } 37273a7b6861SPablo Neira Ayuso EXPORT_SYMBOL(tc_setup_flow_action); 37283a7b6861SPablo Neira Ayuso 3729e3ab786bSPablo Neira Ayuso unsigned int tcf_exts_num_actions(struct tcf_exts *exts) 3730e3ab786bSPablo Neira Ayuso { 3731e3ab786bSPablo Neira Ayuso unsigned int num_acts = 0; 3732e3ab786bSPablo Neira Ayuso struct tc_action *act; 3733e3ab786bSPablo Neira Ayuso int i; 3734e3ab786bSPablo Neira Ayuso 3735e3ab786bSPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 3736e3ab786bSPablo Neira Ayuso if (is_tcf_pedit(act)) 3737e3ab786bSPablo Neira Ayuso num_acts += tcf_pedit_nkeys(act); 3738e3ab786bSPablo Neira Ayuso else 3739e3ab786bSPablo Neira Ayuso num_acts++; 3740e3ab786bSPablo Neira Ayuso } 3741e3ab786bSPablo Neira Ayuso return num_acts; 3742e3ab786bSPablo Neira Ayuso } 3743e3ab786bSPablo Neira Ayuso EXPORT_SYMBOL(tcf_exts_num_actions); 3744e3ab786bSPablo Neira Ayuso 37453625750fSPetr Machata #ifdef CONFIG_NET_CLS_ACT 37463625750fSPetr Machata static int tcf_qevent_parse_block_index(struct nlattr *block_index_attr, 37473625750fSPetr Machata u32 *p_block_index, 37483625750fSPetr Machata struct netlink_ext_ack *extack) 37493625750fSPetr Machata { 37503625750fSPetr Machata *p_block_index = nla_get_u32(block_index_attr); 37513625750fSPetr Machata if (!*p_block_index) { 37523625750fSPetr Machata NL_SET_ERR_MSG(extack, "Block number may not be zero"); 37533625750fSPetr Machata return -EINVAL; 37543625750fSPetr Machata } 37553625750fSPetr Machata 37563625750fSPetr Machata return 0; 37573625750fSPetr Machata } 37583625750fSPetr Machata 37593625750fSPetr Machata int tcf_qevent_init(struct tcf_qevent *qe, struct Qdisc *sch, 37603625750fSPetr Machata enum flow_block_binder_type binder_type, 37613625750fSPetr Machata struct nlattr *block_index_attr, 37623625750fSPetr Machata struct netlink_ext_ack *extack) 37633625750fSPetr Machata { 37643625750fSPetr Machata u32 block_index; 37653625750fSPetr Machata int err; 37663625750fSPetr Machata 37673625750fSPetr Machata if (!block_index_attr) 37683625750fSPetr Machata return 0; 37693625750fSPetr Machata 37703625750fSPetr Machata err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack); 37713625750fSPetr Machata if (err) 37723625750fSPetr Machata return err; 37733625750fSPetr Machata 37743625750fSPetr Machata if (!block_index) 37753625750fSPetr Machata return 0; 37763625750fSPetr Machata 37773625750fSPetr Machata qe->info.binder_type = binder_type; 37783625750fSPetr Machata qe->info.chain_head_change = tcf_chain_head_change_dflt; 37793625750fSPetr Machata qe->info.chain_head_change_priv = &qe->filter_chain; 37803625750fSPetr Machata qe->info.block_index = block_index; 37813625750fSPetr Machata 37823625750fSPetr Machata return tcf_block_get_ext(&qe->block, sch, &qe->info, extack); 37833625750fSPetr Machata } 37843625750fSPetr Machata EXPORT_SYMBOL(tcf_qevent_init); 37853625750fSPetr Machata 37863625750fSPetr Machata void tcf_qevent_destroy(struct tcf_qevent *qe, struct Qdisc *sch) 37873625750fSPetr Machata { 37883625750fSPetr Machata if (qe->info.block_index) 37893625750fSPetr Machata tcf_block_put_ext(qe->block, sch, &qe->info); 37903625750fSPetr Machata } 37913625750fSPetr Machata EXPORT_SYMBOL(tcf_qevent_destroy); 37923625750fSPetr Machata 37933625750fSPetr Machata int tcf_qevent_validate_change(struct tcf_qevent *qe, struct nlattr *block_index_attr, 37943625750fSPetr Machata struct netlink_ext_ack *extack) 37953625750fSPetr Machata { 37963625750fSPetr Machata u32 block_index; 37973625750fSPetr Machata int err; 37983625750fSPetr Machata 37993625750fSPetr Machata if (!block_index_attr) 38003625750fSPetr Machata return 0; 38013625750fSPetr Machata 38023625750fSPetr Machata err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack); 38033625750fSPetr Machata if (err) 38043625750fSPetr Machata return err; 38053625750fSPetr Machata 38063625750fSPetr Machata /* Bounce newly-configured block or change in block. */ 38073625750fSPetr Machata if (block_index != qe->info.block_index) { 38083625750fSPetr Machata NL_SET_ERR_MSG(extack, "Change of blocks is not supported"); 38093625750fSPetr Machata return -EINVAL; 38103625750fSPetr Machata } 38113625750fSPetr Machata 38123625750fSPetr Machata return 0; 38133625750fSPetr Machata } 38143625750fSPetr Machata EXPORT_SYMBOL(tcf_qevent_validate_change); 38153625750fSPetr Machata 38163625750fSPetr Machata struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, struct sk_buff *skb, 381755f656cdSPetr Machata struct sk_buff **to_free, int *ret) 38183625750fSPetr Machata { 38193625750fSPetr Machata struct tcf_result cl_res; 38203625750fSPetr Machata struct tcf_proto *fl; 38213625750fSPetr Machata 38223625750fSPetr Machata if (!qe->info.block_index) 38233625750fSPetr Machata return skb; 38243625750fSPetr Machata 38253625750fSPetr Machata fl = rcu_dereference_bh(qe->filter_chain); 38263625750fSPetr Machata 38273aa26055SDavide Caratti switch (tcf_classify(skb, NULL, fl, &cl_res, false)) { 38283625750fSPetr Machata case TC_ACT_SHOT: 38293625750fSPetr Machata qdisc_qstats_drop(sch); 38303625750fSPetr Machata __qdisc_drop(skb, to_free); 38313625750fSPetr Machata *ret = __NET_XMIT_BYPASS; 38323625750fSPetr Machata return NULL; 38333625750fSPetr Machata case TC_ACT_STOLEN: 38343625750fSPetr Machata case TC_ACT_QUEUED: 38353625750fSPetr Machata case TC_ACT_TRAP: 38363625750fSPetr Machata __qdisc_drop(skb, to_free); 38373625750fSPetr Machata *ret = __NET_XMIT_STOLEN; 38383625750fSPetr Machata return NULL; 38393625750fSPetr Machata case TC_ACT_REDIRECT: 38403625750fSPetr Machata skb_do_redirect(skb); 38413625750fSPetr Machata *ret = __NET_XMIT_STOLEN; 38423625750fSPetr Machata return NULL; 38433625750fSPetr Machata } 38443625750fSPetr Machata 38453625750fSPetr Machata return skb; 38463625750fSPetr Machata } 38473625750fSPetr Machata EXPORT_SYMBOL(tcf_qevent_handle); 38483625750fSPetr Machata 38493625750fSPetr Machata int tcf_qevent_dump(struct sk_buff *skb, int attr_name, struct tcf_qevent *qe) 38503625750fSPetr Machata { 38513625750fSPetr Machata if (!qe->info.block_index) 38523625750fSPetr Machata return 0; 38533625750fSPetr Machata return nla_put_u32(skb, attr_name, qe->info.block_index); 38543625750fSPetr Machata } 38553625750fSPetr Machata EXPORT_SYMBOL(tcf_qevent_dump); 38563625750fSPetr Machata #endif 38573625750fSPetr Machata 385848617387SJiri Pirko static __net_init int tcf_net_init(struct net *net) 385948617387SJiri Pirko { 386048617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 386148617387SJiri Pirko 3862ab281629SVlad Buslov spin_lock_init(&tn->idr_lock); 386348617387SJiri Pirko idr_init(&tn->idr); 386448617387SJiri Pirko return 0; 386548617387SJiri Pirko } 386648617387SJiri Pirko 386748617387SJiri Pirko static void __net_exit tcf_net_exit(struct net *net) 386848617387SJiri Pirko { 386948617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 387048617387SJiri Pirko 387148617387SJiri Pirko idr_destroy(&tn->idr); 387248617387SJiri Pirko } 387348617387SJiri Pirko 387448617387SJiri Pirko static struct pernet_operations tcf_net_ops = { 387548617387SJiri Pirko .init = tcf_net_init, 387648617387SJiri Pirko .exit = tcf_net_exit, 387748617387SJiri Pirko .id = &tcf_net_id, 387848617387SJiri Pirko .size = sizeof(struct tcf_net), 387948617387SJiri Pirko }; 388048617387SJiri Pirko 38811da177e4SLinus Torvalds static int __init tc_filter_init(void) 38821da177e4SLinus Torvalds { 388348617387SJiri Pirko int err; 388448617387SJiri Pirko 38857aa0045dSCong Wang tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 38867aa0045dSCong Wang if (!tc_filter_wq) 38877aa0045dSCong Wang return -ENOMEM; 38887aa0045dSCong Wang 388948617387SJiri Pirko err = register_pernet_subsys(&tcf_net_ops); 389048617387SJiri Pirko if (err) 389148617387SJiri Pirko goto err_register_pernet_subsys; 389248617387SJiri Pirko 3893470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 3894470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3895470502deSVlad Buslov rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 3896470502deSVlad Buslov RTNL_FLAG_DOIT_UNLOCKED); 3897c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 3898470502deSVlad Buslov tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED); 389932a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0); 390032a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0); 390132a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain, 390232a4f5ecSJiri Pirko tc_dump_chain, 0); 39031da177e4SLinus Torvalds 39041da177e4SLinus Torvalds return 0; 390548617387SJiri Pirko 390648617387SJiri Pirko err_register_pernet_subsys: 390748617387SJiri Pirko destroy_workqueue(tc_filter_wq); 390848617387SJiri Pirko return err; 39091da177e4SLinus Torvalds } 39101da177e4SLinus Torvalds 39111da177e4SLinus Torvalds subsys_initcall(tc_filter_init); 3912