11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * net/sched/cls_api.c Packet classifier API. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Changes: 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds */ 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/module.h> 181da177e4SLinus Torvalds #include <linux/types.h> 191da177e4SLinus Torvalds #include <linux/kernel.h> 201da177e4SLinus Torvalds #include <linux/string.h> 211da177e4SLinus Torvalds #include <linux/errno.h> 2233a48927SJiri Pirko #include <linux/err.h> 231da177e4SLinus Torvalds #include <linux/skbuff.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/kmod.h> 265a0e3ad6STejun Heo #include <linux/slab.h> 2748617387SJiri Pirko #include <linux/idr.h> 287f76fa36SJohn Hurley #include <linux/rhashtable.h> 29b854272bSDenis V. Lunev #include <net/net_namespace.h> 30b854272bSDenis V. Lunev #include <net/sock.h> 31dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 321da177e4SLinus Torvalds #include <net/pkt_sched.h> 331da177e4SLinus Torvalds #include <net/pkt_cls.h> 34*e3ab786bSPablo Neira Ayuso #include <net/tc_act/tc_pedit.h> 351da177e4SLinus Torvalds 36e331473fSDavide Caratti extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; 37e331473fSDavide Caratti 381da177e4SLinus Torvalds /* The list of all installed classifier types */ 3936272874SWANG Cong static LIST_HEAD(tcf_proto_base); 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */ 421da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock); 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* Find classifier type by string name */ 451da177e4SLinus Torvalds 46f34e8bffSJiri Pirko static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind) 471da177e4SLinus Torvalds { 48dcd76081SEric Dumazet const struct tcf_proto_ops *t, *res = NULL; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds if (kind) { 511da177e4SLinus Torvalds read_lock(&cls_mod_lock); 5236272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) { 5333a48927SJiri Pirko if (strcmp(kind, t->kind) == 0) { 54dcd76081SEric Dumazet if (try_module_get(t->owner)) 55dcd76081SEric Dumazet res = t; 561da177e4SLinus Torvalds break; 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds read_unlock(&cls_mod_lock); 601da177e4SLinus Torvalds } 61dcd76081SEric Dumazet return res; 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 64f34e8bffSJiri Pirko static const struct tcf_proto_ops * 65f34e8bffSJiri Pirko tcf_proto_lookup_ops(const char *kind, struct netlink_ext_ack *extack) 66f34e8bffSJiri Pirko { 67f34e8bffSJiri Pirko const struct tcf_proto_ops *ops; 68f34e8bffSJiri Pirko 69f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 70f34e8bffSJiri Pirko if (ops) 71f34e8bffSJiri Pirko return ops; 72f34e8bffSJiri Pirko #ifdef CONFIG_MODULES 73f34e8bffSJiri Pirko rtnl_unlock(); 74f34e8bffSJiri Pirko request_module("cls_%s", kind); 75f34e8bffSJiri Pirko rtnl_lock(); 76f34e8bffSJiri Pirko ops = __tcf_proto_lookup_ops(kind); 77f34e8bffSJiri Pirko /* We dropped the RTNL semaphore in order to perform 78f34e8bffSJiri Pirko * the module load. So, even if we succeeded in loading 79f34e8bffSJiri Pirko * the module we have to replay the request. We indicate 80f34e8bffSJiri Pirko * this using -EAGAIN. 81f34e8bffSJiri Pirko */ 82f34e8bffSJiri Pirko if (ops) { 83f34e8bffSJiri Pirko module_put(ops->owner); 84f34e8bffSJiri Pirko return ERR_PTR(-EAGAIN); 85f34e8bffSJiri Pirko } 86f34e8bffSJiri Pirko #endif 87f34e8bffSJiri Pirko NL_SET_ERR_MSG(extack, "TC classifier not found"); 88f34e8bffSJiri Pirko return ERR_PTR(-ENOENT); 89f34e8bffSJiri Pirko } 90f34e8bffSJiri Pirko 911da177e4SLinus Torvalds /* Register(unregister) new classifier type */ 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops) 941da177e4SLinus Torvalds { 9536272874SWANG Cong struct tcf_proto_ops *t; 961da177e4SLinus Torvalds int rc = -EEXIST; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds write_lock(&cls_mod_lock); 9936272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) 1001da177e4SLinus Torvalds if (!strcmp(ops->kind, t->kind)) 1011da177e4SLinus Torvalds goto out; 1021da177e4SLinus Torvalds 10336272874SWANG Cong list_add_tail(&ops->head, &tcf_proto_base); 1041da177e4SLinus Torvalds rc = 0; 1051da177e4SLinus Torvalds out: 1061da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1071da177e4SLinus Torvalds return rc; 1081da177e4SLinus Torvalds } 109aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops); 1101da177e4SLinus Torvalds 1117aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq; 1127aa0045dSCong Wang 1131da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 1141da177e4SLinus Torvalds { 11536272874SWANG Cong struct tcf_proto_ops *t; 1161da177e4SLinus Torvalds int rc = -ENOENT; 1171da177e4SLinus Torvalds 118c78e1746SDaniel Borkmann /* Wait for outstanding call_rcu()s, if any, from a 119c78e1746SDaniel Borkmann * tcf_proto_ops's destroy() handler. 120c78e1746SDaniel Borkmann */ 121c78e1746SDaniel Borkmann rcu_barrier(); 1227aa0045dSCong Wang flush_workqueue(tc_filter_wq); 123c78e1746SDaniel Borkmann 1241da177e4SLinus Torvalds write_lock(&cls_mod_lock); 125dcd76081SEric Dumazet list_for_each_entry(t, &tcf_proto_base, head) { 126dcd76081SEric Dumazet if (t == ops) { 12736272874SWANG Cong list_del(&t->head); 1281da177e4SLinus Torvalds rc = 0; 129dcd76081SEric Dumazet break; 130dcd76081SEric Dumazet } 131dcd76081SEric Dumazet } 1321da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1331da177e4SLinus Torvalds return rc; 1341da177e4SLinus Torvalds } 135aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops); 1361da177e4SLinus Torvalds 137aaa908ffSCong Wang bool tcf_queue_work(struct rcu_work *rwork, work_func_t func) 1387aa0045dSCong Wang { 139aaa908ffSCong Wang INIT_RCU_WORK(rwork, func); 140aaa908ffSCong Wang return queue_rcu_work(tc_filter_wq, rwork); 1417aa0045dSCong Wang } 1427aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work); 1437aa0045dSCong Wang 1441da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */ 1451da177e4SLinus Torvalds 146aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds u32 first = TC_H_MAKE(0xC0000000U, 0U); 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds if (tp) 1511da177e4SLinus Torvalds first = tp->prio - 1; 1521da177e4SLinus Torvalds 1537961973aSJiri Pirko return TC_H_MAJ(first); 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 15633a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 157c35a4accSAlexander Aring u32 prio, struct tcf_chain *chain, 158c35a4accSAlexander Aring struct netlink_ext_ack *extack) 15933a48927SJiri Pirko { 16033a48927SJiri Pirko struct tcf_proto *tp; 16133a48927SJiri Pirko int err; 16233a48927SJiri Pirko 16333a48927SJiri Pirko tp = kzalloc(sizeof(*tp), GFP_KERNEL); 16433a48927SJiri Pirko if (!tp) 16533a48927SJiri Pirko return ERR_PTR(-ENOBUFS); 16633a48927SJiri Pirko 167f34e8bffSJiri Pirko tp->ops = tcf_proto_lookup_ops(kind, extack); 168f34e8bffSJiri Pirko if (IS_ERR(tp->ops)) { 169f34e8bffSJiri Pirko err = PTR_ERR(tp->ops); 170d68d75fdSJiri Pirko goto errout; 17133a48927SJiri Pirko } 17233a48927SJiri Pirko tp->classify = tp->ops->classify; 17333a48927SJiri Pirko tp->protocol = protocol; 17433a48927SJiri Pirko tp->prio = prio; 1755bc17018SJiri Pirko tp->chain = chain; 17633a48927SJiri Pirko 17733a48927SJiri Pirko err = tp->ops->init(tp); 17833a48927SJiri Pirko if (err) { 17933a48927SJiri Pirko module_put(tp->ops->owner); 18033a48927SJiri Pirko goto errout; 18133a48927SJiri Pirko } 18233a48927SJiri Pirko return tp; 18333a48927SJiri Pirko 18433a48927SJiri Pirko errout: 18533a48927SJiri Pirko kfree(tp); 18633a48927SJiri Pirko return ERR_PTR(err); 18733a48927SJiri Pirko } 18833a48927SJiri Pirko 189715df5ecSJakub Kicinski static void tcf_proto_destroy(struct tcf_proto *tp, 190715df5ecSJakub Kicinski struct netlink_ext_ack *extack) 191cf1facdaSJiri Pirko { 192715df5ecSJakub Kicinski tp->ops->destroy(tp, extack); 193cf1facdaSJiri Pirko module_put(tp->ops->owner); 194cf1facdaSJiri Pirko kfree_rcu(tp, rcu); 195cf1facdaSJiri Pirko } 196cf1facdaSJiri Pirko 197a9b19443SJiri Pirko struct tcf_filter_chain_list_item { 198a9b19443SJiri Pirko struct list_head list; 199a9b19443SJiri Pirko tcf_chain_head_change_t *chain_head_change; 200a9b19443SJiri Pirko void *chain_head_change_priv; 201a9b19443SJiri Pirko }; 202a9b19443SJiri Pirko 2035bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block, 2045bc17018SJiri Pirko u32 chain_index) 2052190d1d0SJiri Pirko { 2065bc17018SJiri Pirko struct tcf_chain *chain; 2075bc17018SJiri Pirko 2085bc17018SJiri Pirko chain = kzalloc(sizeof(*chain), GFP_KERNEL); 2095bc17018SJiri Pirko if (!chain) 2105bc17018SJiri Pirko return NULL; 2115bc17018SJiri Pirko list_add_tail(&chain->list, &block->chain_list); 2125bc17018SJiri Pirko chain->block = block; 2135bc17018SJiri Pirko chain->index = chain_index; 214e2ef7544SCong Wang chain->refcnt = 1; 215f71e0ca4SJiri Pirko if (!chain->index) 216f71e0ca4SJiri Pirko block->chain0.chain = chain; 2175bc17018SJiri Pirko return chain; 2182190d1d0SJiri Pirko } 2192190d1d0SJiri Pirko 220a9b19443SJiri Pirko static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, 221a9b19443SJiri Pirko struct tcf_proto *tp_head) 222a9b19443SJiri Pirko { 223a9b19443SJiri Pirko if (item->chain_head_change) 224a9b19443SJiri Pirko item->chain_head_change(tp_head, item->chain_head_change_priv); 225a9b19443SJiri Pirko } 226f71e0ca4SJiri Pirko 227f71e0ca4SJiri Pirko static void tcf_chain0_head_change(struct tcf_chain *chain, 228c7eb7d72SJiri Pirko struct tcf_proto *tp_head) 229c7eb7d72SJiri Pirko { 230a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 231f71e0ca4SJiri Pirko struct tcf_block *block = chain->block; 232a9b19443SJiri Pirko 233f71e0ca4SJiri Pirko if (chain->index) 234f71e0ca4SJiri Pirko return; 235f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) 236a9b19443SJiri Pirko tcf_chain_head_change_item(item, tp_head); 237c7eb7d72SJiri Pirko } 238c7eb7d72SJiri Pirko 239f93e1cdcSJiri Pirko static void tcf_chain_destroy(struct tcf_chain *chain) 240f93e1cdcSJiri Pirko { 241efbf7897SCong Wang struct tcf_block *block = chain->block; 242efbf7897SCong Wang 243e2ef7544SCong Wang list_del(&chain->list); 244f71e0ca4SJiri Pirko if (!chain->index) 245f71e0ca4SJiri Pirko block->chain0.chain = NULL; 2462190d1d0SJiri Pirko kfree(chain); 247cfebd7e2SVlad Buslov if (list_empty(&block->chain_list) && !refcount_read(&block->refcnt)) 2480607e439SVlad Buslov kfree_rcu(block, rcu); 2492190d1d0SJiri Pirko } 2502190d1d0SJiri Pirko 251e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain) 252e2ef7544SCong Wang { 253e2ef7544SCong Wang ++chain->refcnt; 254e2ef7544SCong Wang } 255e2ef7544SCong Wang 2563d32f4c5SJiri Pirko static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain) 2571f3ed383SJiri Pirko { 2581f3ed383SJiri Pirko /* In case all the references are action references, this 2593d32f4c5SJiri Pirko * chain should not be shown to the user. 2601f3ed383SJiri Pirko */ 2611f3ed383SJiri Pirko return chain->refcnt == chain->action_refcnt; 2621f3ed383SJiri Pirko } 2631f3ed383SJiri Pirko 26432a4f5ecSJiri Pirko static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, 26532a4f5ecSJiri Pirko u32 chain_index) 2665bc17018SJiri Pirko { 2675bc17018SJiri Pirko struct tcf_chain *chain; 2685bc17018SJiri Pirko 2695bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 27032a4f5ecSJiri Pirko if (chain->index == chain_index) 27132a4f5ecSJiri Pirko return chain; 27232a4f5ecSJiri Pirko } 27332a4f5ecSJiri Pirko return NULL; 27432a4f5ecSJiri Pirko } 27532a4f5ecSJiri Pirko 27632a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 27732a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast); 27832a4f5ecSJiri Pirko 27953681407SJiri Pirko static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, 28053681407SJiri Pirko u32 chain_index, bool create, 28153681407SJiri Pirko bool by_act) 28232a4f5ecSJiri Pirko { 28332a4f5ecSJiri Pirko struct tcf_chain *chain = tcf_chain_lookup(block, chain_index); 28432a4f5ecSJiri Pirko 28532a4f5ecSJiri Pirko if (chain) { 286e2ef7544SCong Wang tcf_chain_hold(chain); 28753681407SJiri Pirko } else { 28832a4f5ecSJiri Pirko if (!create) 28932a4f5ecSJiri Pirko return NULL; 29032a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 29132a4f5ecSJiri Pirko if (!chain) 29232a4f5ecSJiri Pirko return NULL; 29353681407SJiri Pirko } 29453681407SJiri Pirko 29553681407SJiri Pirko if (by_act) 29653681407SJiri Pirko ++chain->action_refcnt; 29753681407SJiri Pirko 29853681407SJiri Pirko /* Send notification only in case we got the first 29953681407SJiri Pirko * non-action reference. Until then, the chain acts only as 30053681407SJiri Pirko * a placeholder for actions pointing to it and user ought 30153681407SJiri Pirko * not know about them. 30253681407SJiri Pirko */ 30353681407SJiri Pirko if (chain->refcnt - chain->action_refcnt == 1 && !by_act) 30432a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 30532a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 30653681407SJiri Pirko 30732a4f5ecSJiri Pirko return chain; 308e2ef7544SCong Wang } 30953681407SJiri Pirko 310290b1c8bSJiri Pirko static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 31153681407SJiri Pirko bool create) 31253681407SJiri Pirko { 31353681407SJiri Pirko return __tcf_chain_get(block, chain_index, create, false); 31453681407SJiri Pirko } 3155bc17018SJiri Pirko 3161f3ed383SJiri Pirko struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index) 3171f3ed383SJiri Pirko { 31853681407SJiri Pirko return __tcf_chain_get(block, chain_index, true, true); 3191f3ed383SJiri Pirko } 3201f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_get_by_act); 3211f3ed383SJiri Pirko 3229f407f17SJiri Pirko static void tc_chain_tmplt_del(struct tcf_chain *chain); 3239f407f17SJiri Pirko 32453681407SJiri Pirko static void __tcf_chain_put(struct tcf_chain *chain, bool by_act) 3255bc17018SJiri Pirko { 32653681407SJiri Pirko if (by_act) 32753681407SJiri Pirko chain->action_refcnt--; 32853681407SJiri Pirko chain->refcnt--; 32953681407SJiri Pirko 33053681407SJiri Pirko /* The last dropped non-action reference will trigger notification. */ 33153681407SJiri Pirko if (chain->refcnt - chain->action_refcnt == 0 && !by_act) 33232a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, 0, RTM_DELCHAIN, false); 33353681407SJiri Pirko 33453681407SJiri Pirko if (chain->refcnt == 0) { 3359f407f17SJiri Pirko tc_chain_tmplt_del(chain); 3365bc17018SJiri Pirko tcf_chain_destroy(chain); 3375bc17018SJiri Pirko } 33832a4f5ecSJiri Pirko } 33953681407SJiri Pirko 340290b1c8bSJiri Pirko static void tcf_chain_put(struct tcf_chain *chain) 34153681407SJiri Pirko { 34253681407SJiri Pirko __tcf_chain_put(chain, false); 34353681407SJiri Pirko } 3445bc17018SJiri Pirko 3451f3ed383SJiri Pirko void tcf_chain_put_by_act(struct tcf_chain *chain) 3461f3ed383SJiri Pirko { 34753681407SJiri Pirko __tcf_chain_put(chain, true); 3481f3ed383SJiri Pirko } 3491f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_put_by_act); 3501f3ed383SJiri Pirko 35132a4f5ecSJiri Pirko static void tcf_chain_put_explicitly_created(struct tcf_chain *chain) 35232a4f5ecSJiri Pirko { 35332a4f5ecSJiri Pirko if (chain->explicitly_created) 35432a4f5ecSJiri Pirko tcf_chain_put(chain); 35532a4f5ecSJiri Pirko } 35632a4f5ecSJiri Pirko 357290b1c8bSJiri Pirko static void tcf_chain_flush(struct tcf_chain *chain) 358290b1c8bSJiri Pirko { 359290b1c8bSJiri Pirko struct tcf_proto *tp = rtnl_dereference(chain->filter_chain); 360290b1c8bSJiri Pirko 361290b1c8bSJiri Pirko tcf_chain0_head_change(chain, NULL); 362290b1c8bSJiri Pirko while (tp) { 363290b1c8bSJiri Pirko RCU_INIT_POINTER(chain->filter_chain, tp->next); 364290b1c8bSJiri Pirko tcf_proto_destroy(tp, NULL); 365290b1c8bSJiri Pirko tp = rtnl_dereference(chain->filter_chain); 366290b1c8bSJiri Pirko tcf_chain_put(chain); 367290b1c8bSJiri Pirko } 368290b1c8bSJiri Pirko } 369290b1c8bSJiri Pirko 3707f76fa36SJohn Hurley static struct tcf_block *tc_dev_ingress_block(struct net_device *dev) 3717f76fa36SJohn Hurley { 3727f76fa36SJohn Hurley const struct Qdisc_class_ops *cops; 3737f76fa36SJohn Hurley struct Qdisc *qdisc; 3747f76fa36SJohn Hurley 3757f76fa36SJohn Hurley if (!dev_ingress_queue(dev)) 3767f76fa36SJohn Hurley return NULL; 3777f76fa36SJohn Hurley 3787f76fa36SJohn Hurley qdisc = dev_ingress_queue(dev)->qdisc_sleeping; 3797f76fa36SJohn Hurley if (!qdisc) 3807f76fa36SJohn Hurley return NULL; 3817f76fa36SJohn Hurley 3827f76fa36SJohn Hurley cops = qdisc->ops->cl_ops; 3837f76fa36SJohn Hurley if (!cops) 3847f76fa36SJohn Hurley return NULL; 3857f76fa36SJohn Hurley 3867f76fa36SJohn Hurley if (!cops->tcf_block) 3877f76fa36SJohn Hurley return NULL; 3887f76fa36SJohn Hurley 3897f76fa36SJohn Hurley return cops->tcf_block(qdisc, TC_H_MIN_INGRESS, NULL); 3907f76fa36SJohn Hurley } 3917f76fa36SJohn Hurley 3927f76fa36SJohn Hurley static struct rhashtable indr_setup_block_ht; 3937f76fa36SJohn Hurley 3947f76fa36SJohn Hurley struct tc_indr_block_dev { 3957f76fa36SJohn Hurley struct rhash_head ht_node; 3967f76fa36SJohn Hurley struct net_device *dev; 3977f76fa36SJohn Hurley unsigned int refcnt; 3987f76fa36SJohn Hurley struct list_head cb_list; 3997f76fa36SJohn Hurley struct tcf_block *block; 4007f76fa36SJohn Hurley }; 4017f76fa36SJohn Hurley 4027f76fa36SJohn Hurley struct tc_indr_block_cb { 4037f76fa36SJohn Hurley struct list_head list; 4047f76fa36SJohn Hurley void *cb_priv; 4057f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb; 4067f76fa36SJohn Hurley void *cb_ident; 4077f76fa36SJohn Hurley }; 4087f76fa36SJohn Hurley 4097f76fa36SJohn Hurley static const struct rhashtable_params tc_indr_setup_block_ht_params = { 4107f76fa36SJohn Hurley .key_offset = offsetof(struct tc_indr_block_dev, dev), 4117f76fa36SJohn Hurley .head_offset = offsetof(struct tc_indr_block_dev, ht_node), 4127f76fa36SJohn Hurley .key_len = sizeof(struct net_device *), 4137f76fa36SJohn Hurley }; 4147f76fa36SJohn Hurley 4157f76fa36SJohn Hurley static struct tc_indr_block_dev * 4167f76fa36SJohn Hurley tc_indr_block_dev_lookup(struct net_device *dev) 4177f76fa36SJohn Hurley { 4187f76fa36SJohn Hurley return rhashtable_lookup_fast(&indr_setup_block_ht, &dev, 4197f76fa36SJohn Hurley tc_indr_setup_block_ht_params); 4207f76fa36SJohn Hurley } 4217f76fa36SJohn Hurley 4227f76fa36SJohn Hurley static struct tc_indr_block_dev *tc_indr_block_dev_get(struct net_device *dev) 4237f76fa36SJohn Hurley { 4247f76fa36SJohn Hurley struct tc_indr_block_dev *indr_dev; 4257f76fa36SJohn Hurley 4267f76fa36SJohn Hurley indr_dev = tc_indr_block_dev_lookup(dev); 4277f76fa36SJohn Hurley if (indr_dev) 4287f76fa36SJohn Hurley goto inc_ref; 4297f76fa36SJohn Hurley 4307f76fa36SJohn Hurley indr_dev = kzalloc(sizeof(*indr_dev), GFP_KERNEL); 4317f76fa36SJohn Hurley if (!indr_dev) 4327f76fa36SJohn Hurley return NULL; 4337f76fa36SJohn Hurley 4347f76fa36SJohn Hurley INIT_LIST_HEAD(&indr_dev->cb_list); 4357f76fa36SJohn Hurley indr_dev->dev = dev; 4367f76fa36SJohn Hurley indr_dev->block = tc_dev_ingress_block(dev); 4377f76fa36SJohn Hurley if (rhashtable_insert_fast(&indr_setup_block_ht, &indr_dev->ht_node, 4387f76fa36SJohn Hurley tc_indr_setup_block_ht_params)) { 4397f76fa36SJohn Hurley kfree(indr_dev); 4407f76fa36SJohn Hurley return NULL; 4417f76fa36SJohn Hurley } 4427f76fa36SJohn Hurley 4437f76fa36SJohn Hurley inc_ref: 4447f76fa36SJohn Hurley indr_dev->refcnt++; 4457f76fa36SJohn Hurley return indr_dev; 4467f76fa36SJohn Hurley } 4477f76fa36SJohn Hurley 4487f76fa36SJohn Hurley static void tc_indr_block_dev_put(struct tc_indr_block_dev *indr_dev) 4497f76fa36SJohn Hurley { 4507f76fa36SJohn Hurley if (--indr_dev->refcnt) 4517f76fa36SJohn Hurley return; 4527f76fa36SJohn Hurley 4537f76fa36SJohn Hurley rhashtable_remove_fast(&indr_setup_block_ht, &indr_dev->ht_node, 4547f76fa36SJohn Hurley tc_indr_setup_block_ht_params); 4557f76fa36SJohn Hurley kfree(indr_dev); 4567f76fa36SJohn Hurley } 4577f76fa36SJohn Hurley 4587f76fa36SJohn Hurley static struct tc_indr_block_cb * 4597f76fa36SJohn Hurley tc_indr_block_cb_lookup(struct tc_indr_block_dev *indr_dev, 4607f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 4617f76fa36SJohn Hurley { 4627f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb; 4637f76fa36SJohn Hurley 4647f76fa36SJohn Hurley list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list) 4657f76fa36SJohn Hurley if (indr_block_cb->cb == cb && 4667f76fa36SJohn Hurley indr_block_cb->cb_ident == cb_ident) 4677f76fa36SJohn Hurley return indr_block_cb; 4687f76fa36SJohn Hurley return NULL; 4697f76fa36SJohn Hurley } 4707f76fa36SJohn Hurley 4717f76fa36SJohn Hurley static struct tc_indr_block_cb * 4727f76fa36SJohn Hurley tc_indr_block_cb_add(struct tc_indr_block_dev *indr_dev, void *cb_priv, 4737f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 4747f76fa36SJohn Hurley { 4757f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb; 4767f76fa36SJohn Hurley 4777f76fa36SJohn Hurley indr_block_cb = tc_indr_block_cb_lookup(indr_dev, cb, cb_ident); 4787f76fa36SJohn Hurley if (indr_block_cb) 4797f76fa36SJohn Hurley return ERR_PTR(-EEXIST); 4807f76fa36SJohn Hurley 4817f76fa36SJohn Hurley indr_block_cb = kzalloc(sizeof(*indr_block_cb), GFP_KERNEL); 4827f76fa36SJohn Hurley if (!indr_block_cb) 4837f76fa36SJohn Hurley return ERR_PTR(-ENOMEM); 4847f76fa36SJohn Hurley 4857f76fa36SJohn Hurley indr_block_cb->cb_priv = cb_priv; 4867f76fa36SJohn Hurley indr_block_cb->cb = cb; 4877f76fa36SJohn Hurley indr_block_cb->cb_ident = cb_ident; 4887f76fa36SJohn Hurley list_add(&indr_block_cb->list, &indr_dev->cb_list); 4897f76fa36SJohn Hurley 4907f76fa36SJohn Hurley return indr_block_cb; 4917f76fa36SJohn Hurley } 4927f76fa36SJohn Hurley 4937f76fa36SJohn Hurley static void tc_indr_block_cb_del(struct tc_indr_block_cb *indr_block_cb) 4947f76fa36SJohn Hurley { 4957f76fa36SJohn Hurley list_del(&indr_block_cb->list); 4967f76fa36SJohn Hurley kfree(indr_block_cb); 4977f76fa36SJohn Hurley } 4987f76fa36SJohn Hurley 4997f76fa36SJohn Hurley static void tc_indr_block_ing_cmd(struct tc_indr_block_dev *indr_dev, 5007f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb, 5017f76fa36SJohn Hurley enum tc_block_command command) 5027f76fa36SJohn Hurley { 5037f76fa36SJohn Hurley struct tc_block_offload bo = { 5047f76fa36SJohn Hurley .command = command, 5057f76fa36SJohn Hurley .binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS, 5067f76fa36SJohn Hurley .block = indr_dev->block, 5077f76fa36SJohn Hurley }; 5087f76fa36SJohn Hurley 5097f76fa36SJohn Hurley if (!indr_dev->block) 5107f76fa36SJohn Hurley return; 5117f76fa36SJohn Hurley 5127f76fa36SJohn Hurley indr_block_cb->cb(indr_dev->dev, indr_block_cb->cb_priv, TC_SETUP_BLOCK, 5137f76fa36SJohn Hurley &bo); 5147f76fa36SJohn Hurley } 5157f76fa36SJohn Hurley 5167f76fa36SJohn Hurley int __tc_indr_block_cb_register(struct net_device *dev, void *cb_priv, 5177f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 5187f76fa36SJohn Hurley { 5197f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb; 5207f76fa36SJohn Hurley struct tc_indr_block_dev *indr_dev; 5217f76fa36SJohn Hurley int err; 5227f76fa36SJohn Hurley 5237f76fa36SJohn Hurley indr_dev = tc_indr_block_dev_get(dev); 5247f76fa36SJohn Hurley if (!indr_dev) 5257f76fa36SJohn Hurley return -ENOMEM; 5267f76fa36SJohn Hurley 5277f76fa36SJohn Hurley indr_block_cb = tc_indr_block_cb_add(indr_dev, cb_priv, cb, cb_ident); 5287f76fa36SJohn Hurley err = PTR_ERR_OR_ZERO(indr_block_cb); 5297f76fa36SJohn Hurley if (err) 5307f76fa36SJohn Hurley goto err_dev_put; 5317f76fa36SJohn Hurley 5327f76fa36SJohn Hurley tc_indr_block_ing_cmd(indr_dev, indr_block_cb, TC_BLOCK_BIND); 5337f76fa36SJohn Hurley return 0; 5347f76fa36SJohn Hurley 5357f76fa36SJohn Hurley err_dev_put: 5367f76fa36SJohn Hurley tc_indr_block_dev_put(indr_dev); 5377f76fa36SJohn Hurley return err; 5387f76fa36SJohn Hurley } 5397f76fa36SJohn Hurley EXPORT_SYMBOL_GPL(__tc_indr_block_cb_register); 5407f76fa36SJohn Hurley 5417f76fa36SJohn Hurley int tc_indr_block_cb_register(struct net_device *dev, void *cb_priv, 5427f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 5437f76fa36SJohn Hurley { 5447f76fa36SJohn Hurley int err; 5457f76fa36SJohn Hurley 5467f76fa36SJohn Hurley rtnl_lock(); 5477f76fa36SJohn Hurley err = __tc_indr_block_cb_register(dev, cb_priv, cb, cb_ident); 5487f76fa36SJohn Hurley rtnl_unlock(); 5497f76fa36SJohn Hurley 5507f76fa36SJohn Hurley return err; 5517f76fa36SJohn Hurley } 5527f76fa36SJohn Hurley EXPORT_SYMBOL_GPL(tc_indr_block_cb_register); 5537f76fa36SJohn Hurley 5547f76fa36SJohn Hurley void __tc_indr_block_cb_unregister(struct net_device *dev, 5557f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 5567f76fa36SJohn Hurley { 5577f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb; 5587f76fa36SJohn Hurley struct tc_indr_block_dev *indr_dev; 5597f76fa36SJohn Hurley 5607f76fa36SJohn Hurley indr_dev = tc_indr_block_dev_lookup(dev); 5617f76fa36SJohn Hurley if (!indr_dev) 5627f76fa36SJohn Hurley return; 5637f76fa36SJohn Hurley 5647f76fa36SJohn Hurley indr_block_cb = tc_indr_block_cb_lookup(indr_dev, cb, cb_ident); 5657f76fa36SJohn Hurley if (!indr_block_cb) 5667f76fa36SJohn Hurley return; 5677f76fa36SJohn Hurley 5687f76fa36SJohn Hurley /* Send unbind message if required to free any block cbs. */ 5697f76fa36SJohn Hurley tc_indr_block_ing_cmd(indr_dev, indr_block_cb, TC_BLOCK_UNBIND); 5707f76fa36SJohn Hurley tc_indr_block_cb_del(indr_block_cb); 5717f76fa36SJohn Hurley tc_indr_block_dev_put(indr_dev); 5727f76fa36SJohn Hurley } 5737f76fa36SJohn Hurley EXPORT_SYMBOL_GPL(__tc_indr_block_cb_unregister); 5747f76fa36SJohn Hurley 5757f76fa36SJohn Hurley void tc_indr_block_cb_unregister(struct net_device *dev, 5767f76fa36SJohn Hurley tc_indr_block_bind_cb_t *cb, void *cb_ident) 5777f76fa36SJohn Hurley { 5787f76fa36SJohn Hurley rtnl_lock(); 5797f76fa36SJohn Hurley __tc_indr_block_cb_unregister(dev, cb, cb_ident); 5807f76fa36SJohn Hurley rtnl_unlock(); 5817f76fa36SJohn Hurley } 5827f76fa36SJohn Hurley EXPORT_SYMBOL_GPL(tc_indr_block_cb_unregister); 5837f76fa36SJohn Hurley 5847f76fa36SJohn Hurley static void tc_indr_block_call(struct tcf_block *block, struct net_device *dev, 5857f76fa36SJohn Hurley struct tcf_block_ext_info *ei, 5867f76fa36SJohn Hurley enum tc_block_command command, 5877f76fa36SJohn Hurley struct netlink_ext_ack *extack) 5887f76fa36SJohn Hurley { 5897f76fa36SJohn Hurley struct tc_indr_block_cb *indr_block_cb; 5907f76fa36SJohn Hurley struct tc_indr_block_dev *indr_dev; 5917f76fa36SJohn Hurley struct tc_block_offload bo = { 5927f76fa36SJohn Hurley .command = command, 5937f76fa36SJohn Hurley .binder_type = ei->binder_type, 5947f76fa36SJohn Hurley .block = block, 5957f76fa36SJohn Hurley .extack = extack, 5967f76fa36SJohn Hurley }; 5977f76fa36SJohn Hurley 5987f76fa36SJohn Hurley indr_dev = tc_indr_block_dev_lookup(dev); 5997f76fa36SJohn Hurley if (!indr_dev) 6007f76fa36SJohn Hurley return; 6017f76fa36SJohn Hurley 6027f76fa36SJohn Hurley indr_dev->block = command == TC_BLOCK_BIND ? block : NULL; 6037f76fa36SJohn Hurley 6047f76fa36SJohn Hurley list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list) 6057f76fa36SJohn Hurley indr_block_cb->cb(dev, indr_block_cb->cb_priv, TC_SETUP_BLOCK, 6067f76fa36SJohn Hurley &bo); 6077f76fa36SJohn Hurley } 6087f76fa36SJohn Hurley 609caa72601SJiri Pirko static bool tcf_block_offload_in_use(struct tcf_block *block) 610caa72601SJiri Pirko { 611caa72601SJiri Pirko return block->offloadcnt; 612caa72601SJiri Pirko } 613caa72601SJiri Pirko 614caa72601SJiri Pirko static int tcf_block_offload_cmd(struct tcf_block *block, 615caa72601SJiri Pirko struct net_device *dev, 6168c4083b3SJiri Pirko struct tcf_block_ext_info *ei, 61760513bd8SJohn Hurley enum tc_block_command command, 61860513bd8SJohn Hurley struct netlink_ext_ack *extack) 6198c4083b3SJiri Pirko { 6208c4083b3SJiri Pirko struct tc_block_offload bo = {}; 6218c4083b3SJiri Pirko 6228c4083b3SJiri Pirko bo.command = command; 6238c4083b3SJiri Pirko bo.binder_type = ei->binder_type; 6248c4083b3SJiri Pirko bo.block = block; 62560513bd8SJohn Hurley bo.extack = extack; 626caa72601SJiri Pirko return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 6278c4083b3SJiri Pirko } 6288c4083b3SJiri Pirko 629caa72601SJiri Pirko static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 63060513bd8SJohn Hurley struct tcf_block_ext_info *ei, 63160513bd8SJohn Hurley struct netlink_ext_ack *extack) 6328c4083b3SJiri Pirko { 633caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 634caa72601SJiri Pirko int err; 635caa72601SJiri Pirko 636caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 637caa72601SJiri Pirko goto no_offload_dev_inc; 638caa72601SJiri Pirko 639caa72601SJiri Pirko /* If tc offload feature is disabled and the block we try to bind 640caa72601SJiri Pirko * to already has some offloaded filters, forbid to bind. 641caa72601SJiri Pirko */ 64260513bd8SJohn Hurley if (!tc_can_offload(dev) && tcf_block_offload_in_use(block)) { 64360513bd8SJohn Hurley NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled"); 644caa72601SJiri Pirko return -EOPNOTSUPP; 64560513bd8SJohn Hurley } 646caa72601SJiri Pirko 64760513bd8SJohn Hurley err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_BIND, extack); 648caa72601SJiri Pirko if (err == -EOPNOTSUPP) 649caa72601SJiri Pirko goto no_offload_dev_inc; 6507f76fa36SJohn Hurley if (err) 651caa72601SJiri Pirko return err; 652caa72601SJiri Pirko 6537f76fa36SJohn Hurley tc_indr_block_call(block, dev, ei, TC_BLOCK_BIND, extack); 6547f76fa36SJohn Hurley return 0; 6557f76fa36SJohn Hurley 656caa72601SJiri Pirko no_offload_dev_inc: 657caa72601SJiri Pirko if (tcf_block_offload_in_use(block)) 658caa72601SJiri Pirko return -EOPNOTSUPP; 659caa72601SJiri Pirko block->nooffloaddevcnt++; 6607f76fa36SJohn Hurley tc_indr_block_call(block, dev, ei, TC_BLOCK_BIND, extack); 661caa72601SJiri Pirko return 0; 6628c4083b3SJiri Pirko } 6638c4083b3SJiri Pirko 6648c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 6658c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 6668c4083b3SJiri Pirko { 667caa72601SJiri Pirko struct net_device *dev = q->dev_queue->dev; 668caa72601SJiri Pirko int err; 669caa72601SJiri Pirko 6707f76fa36SJohn Hurley tc_indr_block_call(block, dev, ei, TC_BLOCK_UNBIND, NULL); 6717f76fa36SJohn Hurley 672caa72601SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 673caa72601SJiri Pirko goto no_offload_dev_dec; 67460513bd8SJohn Hurley err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_UNBIND, NULL); 675caa72601SJiri Pirko if (err == -EOPNOTSUPP) 676caa72601SJiri Pirko goto no_offload_dev_dec; 677caa72601SJiri Pirko return; 678caa72601SJiri Pirko 679caa72601SJiri Pirko no_offload_dev_dec: 680caa72601SJiri Pirko WARN_ON(block->nooffloaddevcnt-- == 0); 6818c4083b3SJiri Pirko } 6828c4083b3SJiri Pirko 683a9b19443SJiri Pirko static int 684f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_add(struct tcf_block *block, 685a9b19443SJiri Pirko struct tcf_block_ext_info *ei, 686a9b19443SJiri Pirko struct netlink_ext_ack *extack) 687a9b19443SJiri Pirko { 688f71e0ca4SJiri Pirko struct tcf_chain *chain0 = block->chain0.chain; 689a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 690a9b19443SJiri Pirko 691a9b19443SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 692a9b19443SJiri Pirko if (!item) { 693a9b19443SJiri Pirko NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed"); 694a9b19443SJiri Pirko return -ENOMEM; 695a9b19443SJiri Pirko } 696a9b19443SJiri Pirko item->chain_head_change = ei->chain_head_change; 697a9b19443SJiri Pirko item->chain_head_change_priv = ei->chain_head_change_priv; 698f71e0ca4SJiri Pirko if (chain0 && chain0->filter_chain) 699f71e0ca4SJiri Pirko tcf_chain_head_change_item(item, chain0->filter_chain); 700f71e0ca4SJiri Pirko list_add(&item->list, &block->chain0.filter_chain_list); 701a9b19443SJiri Pirko return 0; 702a9b19443SJiri Pirko } 703a9b19443SJiri Pirko 704a9b19443SJiri Pirko static void 705f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(struct tcf_block *block, 706a9b19443SJiri Pirko struct tcf_block_ext_info *ei) 707a9b19443SJiri Pirko { 708f71e0ca4SJiri Pirko struct tcf_chain *chain0 = block->chain0.chain; 709a9b19443SJiri Pirko struct tcf_filter_chain_list_item *item; 710a9b19443SJiri Pirko 711f71e0ca4SJiri Pirko list_for_each_entry(item, &block->chain0.filter_chain_list, list) { 712a9b19443SJiri Pirko if ((!ei->chain_head_change && !ei->chain_head_change_priv) || 713a9b19443SJiri Pirko (item->chain_head_change == ei->chain_head_change && 714a9b19443SJiri Pirko item->chain_head_change_priv == ei->chain_head_change_priv)) { 715f71e0ca4SJiri Pirko if (chain0) 716a9b19443SJiri Pirko tcf_chain_head_change_item(item, NULL); 717a9b19443SJiri Pirko list_del(&item->list); 718a9b19443SJiri Pirko kfree(item); 719a9b19443SJiri Pirko return; 720a9b19443SJiri Pirko } 721a9b19443SJiri Pirko } 722a9b19443SJiri Pirko WARN_ON(1); 723a9b19443SJiri Pirko } 724a9b19443SJiri Pirko 72548617387SJiri Pirko struct tcf_net { 726ab281629SVlad Buslov spinlock_t idr_lock; /* Protects idr */ 72748617387SJiri Pirko struct idr idr; 72848617387SJiri Pirko }; 72948617387SJiri Pirko 73048617387SJiri Pirko static unsigned int tcf_net_id; 73148617387SJiri Pirko 73248617387SJiri Pirko static int tcf_block_insert(struct tcf_block *block, struct net *net, 733bb047dddSJiri Pirko struct netlink_ext_ack *extack) 734a9b19443SJiri Pirko { 73548617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 736ab281629SVlad Buslov int err; 73748617387SJiri Pirko 738ab281629SVlad Buslov idr_preload(GFP_KERNEL); 739ab281629SVlad Buslov spin_lock(&tn->idr_lock); 740ab281629SVlad Buslov err = idr_alloc_u32(&tn->idr, block, &block->index, block->index, 741ab281629SVlad Buslov GFP_NOWAIT); 742ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 743ab281629SVlad Buslov idr_preload_end(); 744ab281629SVlad Buslov 745ab281629SVlad Buslov return err; 746a9b19443SJiri Pirko } 747a9b19443SJiri Pirko 74848617387SJiri Pirko static void tcf_block_remove(struct tcf_block *block, struct net *net) 74948617387SJiri Pirko { 75048617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 75148617387SJiri Pirko 752ab281629SVlad Buslov spin_lock(&tn->idr_lock); 7539c160941SMatthew Wilcox idr_remove(&tn->idr, block->index); 754ab281629SVlad Buslov spin_unlock(&tn->idr_lock); 75548617387SJiri Pirko } 75648617387SJiri Pirko 75748617387SJiri Pirko static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, 758bb047dddSJiri Pirko u32 block_index, 7598d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 7606529eabaSJiri Pirko { 76148617387SJiri Pirko struct tcf_block *block; 7626529eabaSJiri Pirko 76348617387SJiri Pirko block = kzalloc(sizeof(*block), GFP_KERNEL); 7648d1a77f9SAlexander Aring if (!block) { 7658d1a77f9SAlexander Aring NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); 76648617387SJiri Pirko return ERR_PTR(-ENOMEM); 7678d1a77f9SAlexander Aring } 7685bc17018SJiri Pirko INIT_LIST_HEAD(&block->chain_list); 769acb67442SJiri Pirko INIT_LIST_HEAD(&block->cb_list); 770f36fe1c4SJiri Pirko INIT_LIST_HEAD(&block->owner_list); 771f71e0ca4SJiri Pirko INIT_LIST_HEAD(&block->chain0.filter_chain_list); 772acb67442SJiri Pirko 773cfebd7e2SVlad Buslov refcount_set(&block->refcnt, 1); 77448617387SJiri Pirko block->net = net; 775bb047dddSJiri Pirko block->index = block_index; 776bb047dddSJiri Pirko 777bb047dddSJiri Pirko /* Don't store q pointer for blocks which are shared */ 778bb047dddSJiri Pirko if (!tcf_block_shared(block)) 77948617387SJiri Pirko block->q = q; 78048617387SJiri Pirko return block; 78148617387SJiri Pirko } 78248617387SJiri Pirko 78348617387SJiri Pirko static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) 78448617387SJiri Pirko { 78548617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 78648617387SJiri Pirko 787322d884bSMatthew Wilcox return idr_find(&tn->idr, block_index); 78848617387SJiri Pirko } 78948617387SJiri Pirko 7900607e439SVlad Buslov static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index) 7910607e439SVlad Buslov { 7920607e439SVlad Buslov struct tcf_block *block; 7930607e439SVlad Buslov 7940607e439SVlad Buslov rcu_read_lock(); 7950607e439SVlad Buslov block = tcf_block_lookup(net, block_index); 7960607e439SVlad Buslov if (block && !refcount_inc_not_zero(&block->refcnt)) 7970607e439SVlad Buslov block = NULL; 7980607e439SVlad Buslov rcu_read_unlock(); 7990607e439SVlad Buslov 8000607e439SVlad Buslov return block; 8010607e439SVlad Buslov } 8020607e439SVlad Buslov 803f0023436SVlad Buslov static void tcf_block_flush_all_chains(struct tcf_block *block) 804f0023436SVlad Buslov { 805f0023436SVlad Buslov struct tcf_chain *chain; 806f0023436SVlad Buslov 807f0023436SVlad Buslov /* Hold a refcnt for all chains, so that they don't disappear 808f0023436SVlad Buslov * while we are iterating. 809f0023436SVlad Buslov */ 810f0023436SVlad Buslov list_for_each_entry(chain, &block->chain_list, list) 811f0023436SVlad Buslov tcf_chain_hold(chain); 812f0023436SVlad Buslov 813f0023436SVlad Buslov list_for_each_entry(chain, &block->chain_list, list) 814f0023436SVlad Buslov tcf_chain_flush(chain); 815f0023436SVlad Buslov } 816f0023436SVlad Buslov 817f0023436SVlad Buslov static void tcf_block_put_all_chains(struct tcf_block *block) 818f0023436SVlad Buslov { 819f0023436SVlad Buslov struct tcf_chain *chain, *tmp; 820f0023436SVlad Buslov 821f0023436SVlad Buslov /* At this point, all the chains should have refcnt >= 1. */ 822f0023436SVlad Buslov list_for_each_entry_safe(chain, tmp, &block->chain_list, list) { 823f0023436SVlad Buslov tcf_chain_put_explicitly_created(chain); 824f0023436SVlad Buslov tcf_chain_put(chain); 825f0023436SVlad Buslov } 826f0023436SVlad Buslov } 827f0023436SVlad Buslov 8280607e439SVlad Buslov static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q, 8290607e439SVlad Buslov struct tcf_block_ext_info *ei) 8300607e439SVlad Buslov { 8310607e439SVlad Buslov if (refcount_dec_and_test(&block->refcnt)) { 8320607e439SVlad Buslov /* Flushing/putting all chains will cause the block to be 8330607e439SVlad Buslov * deallocated when last chain is freed. However, if chain_list 8340607e439SVlad Buslov * is empty, block has to be manually deallocated. After block 8350607e439SVlad Buslov * reference counter reached 0, it is no longer possible to 8360607e439SVlad Buslov * increment it or add new chains to block. 8370607e439SVlad Buslov */ 8380607e439SVlad Buslov bool free_block = list_empty(&block->chain_list); 8390607e439SVlad Buslov 8400607e439SVlad Buslov if (tcf_block_shared(block)) 8410607e439SVlad Buslov tcf_block_remove(block, block->net); 8420607e439SVlad Buslov if (!free_block) 8430607e439SVlad Buslov tcf_block_flush_all_chains(block); 8440607e439SVlad Buslov 8450607e439SVlad Buslov if (q) 8460607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 8470607e439SVlad Buslov 8480607e439SVlad Buslov if (free_block) 8490607e439SVlad Buslov kfree_rcu(block, rcu); 8500607e439SVlad Buslov else 8510607e439SVlad Buslov tcf_block_put_all_chains(block); 8520607e439SVlad Buslov } else if (q) { 8530607e439SVlad Buslov tcf_block_offload_unbind(block, q, ei); 8540607e439SVlad Buslov } 8550607e439SVlad Buslov } 8560607e439SVlad Buslov 8570607e439SVlad Buslov static void tcf_block_refcnt_put(struct tcf_block *block) 8580607e439SVlad Buslov { 8590607e439SVlad Buslov __tcf_block_put(block, NULL, NULL); 8600607e439SVlad Buslov } 8610607e439SVlad Buslov 862c431f89bSVlad Buslov /* Find tcf block. 863c431f89bSVlad Buslov * Set q, parent, cl when appropriate. 864c431f89bSVlad Buslov */ 865c431f89bSVlad Buslov 866c431f89bSVlad Buslov static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q, 867c431f89bSVlad Buslov u32 *parent, unsigned long *cl, 868c431f89bSVlad Buslov int ifindex, u32 block_index, 869c431f89bSVlad Buslov struct netlink_ext_ack *extack) 870c431f89bSVlad Buslov { 871c431f89bSVlad Buslov struct tcf_block *block; 872e368fdb6SVlad Buslov int err = 0; 873c431f89bSVlad Buslov 874c431f89bSVlad Buslov if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 875787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, block_index); 876c431f89bSVlad Buslov if (!block) { 877c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Block of given index was not found"); 878c431f89bSVlad Buslov return ERR_PTR(-EINVAL); 879c431f89bSVlad Buslov } 880c431f89bSVlad Buslov } else { 881c431f89bSVlad Buslov const struct Qdisc_class_ops *cops; 882c431f89bSVlad Buslov struct net_device *dev; 883c431f89bSVlad Buslov 884e368fdb6SVlad Buslov rcu_read_lock(); 885e368fdb6SVlad Buslov 886c431f89bSVlad Buslov /* Find link */ 887e368fdb6SVlad Buslov dev = dev_get_by_index_rcu(net, ifindex); 888e368fdb6SVlad Buslov if (!dev) { 889e368fdb6SVlad Buslov rcu_read_unlock(); 890c431f89bSVlad Buslov return ERR_PTR(-ENODEV); 891e368fdb6SVlad Buslov } 892c431f89bSVlad Buslov 893c431f89bSVlad Buslov /* Find qdisc */ 894c431f89bSVlad Buslov if (!*parent) { 895c431f89bSVlad Buslov *q = dev->qdisc; 896c431f89bSVlad Buslov *parent = (*q)->handle; 897c431f89bSVlad Buslov } else { 898e368fdb6SVlad Buslov *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent)); 899c431f89bSVlad Buslov if (!*q) { 900c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 901e368fdb6SVlad Buslov err = -EINVAL; 902e368fdb6SVlad Buslov goto errout_rcu; 903c431f89bSVlad Buslov } 904c431f89bSVlad Buslov } 905c431f89bSVlad Buslov 906e368fdb6SVlad Buslov *q = qdisc_refcount_inc_nz(*q); 907e368fdb6SVlad Buslov if (!*q) { 908e368fdb6SVlad Buslov NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 909e368fdb6SVlad Buslov err = -EINVAL; 910e368fdb6SVlad Buslov goto errout_rcu; 911e368fdb6SVlad Buslov } 912e368fdb6SVlad Buslov 913c431f89bSVlad Buslov /* Is it classful? */ 914c431f89bSVlad Buslov cops = (*q)->ops->cl_ops; 915c431f89bSVlad Buslov if (!cops) { 916c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Qdisc not classful"); 917e368fdb6SVlad Buslov err = -EINVAL; 918e368fdb6SVlad Buslov goto errout_rcu; 919c431f89bSVlad Buslov } 920c431f89bSVlad Buslov 921c431f89bSVlad Buslov if (!cops->tcf_block) { 922c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Class doesn't support blocks"); 923e368fdb6SVlad Buslov err = -EOPNOTSUPP; 924e368fdb6SVlad Buslov goto errout_rcu; 925c431f89bSVlad Buslov } 926c431f89bSVlad Buslov 927e368fdb6SVlad Buslov /* At this point we know that qdisc is not noop_qdisc, 928e368fdb6SVlad Buslov * which means that qdisc holds a reference to net_device 929e368fdb6SVlad Buslov * and we hold a reference to qdisc, so it is safe to release 930e368fdb6SVlad Buslov * rcu read lock. 931e368fdb6SVlad Buslov */ 932e368fdb6SVlad Buslov rcu_read_unlock(); 933e368fdb6SVlad Buslov 934c431f89bSVlad Buslov /* Do we search for filter, attached to class? */ 935c431f89bSVlad Buslov if (TC_H_MIN(*parent)) { 936c431f89bSVlad Buslov *cl = cops->find(*q, *parent); 937c431f89bSVlad Buslov if (*cl == 0) { 938c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified class doesn't exist"); 939e368fdb6SVlad Buslov err = -ENOENT; 940e368fdb6SVlad Buslov goto errout_qdisc; 941c431f89bSVlad Buslov } 942c431f89bSVlad Buslov } 943c431f89bSVlad Buslov 944c431f89bSVlad Buslov /* And the last stroke */ 945c431f89bSVlad Buslov block = cops->tcf_block(*q, *cl, extack); 946e368fdb6SVlad Buslov if (!block) { 947e368fdb6SVlad Buslov err = -EINVAL; 948e368fdb6SVlad Buslov goto errout_qdisc; 949e368fdb6SVlad Buslov } 950c431f89bSVlad Buslov if (tcf_block_shared(block)) { 951c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters"); 952e368fdb6SVlad Buslov err = -EOPNOTSUPP; 953e368fdb6SVlad Buslov goto errout_qdisc; 954c431f89bSVlad Buslov } 955787ce6d0SVlad Buslov 956787ce6d0SVlad Buslov /* Always take reference to block in order to support execution 957787ce6d0SVlad Buslov * of rules update path of cls API without rtnl lock. Caller 958787ce6d0SVlad Buslov * must release block when it is finished using it. 'if' block 959787ce6d0SVlad Buslov * of this conditional obtain reference to block by calling 960787ce6d0SVlad Buslov * tcf_block_refcnt_get(). 961787ce6d0SVlad Buslov */ 962787ce6d0SVlad Buslov refcount_inc(&block->refcnt); 963c431f89bSVlad Buslov } 964c431f89bSVlad Buslov 965c431f89bSVlad Buslov return block; 966e368fdb6SVlad Buslov 967e368fdb6SVlad Buslov errout_rcu: 968e368fdb6SVlad Buslov rcu_read_unlock(); 969e368fdb6SVlad Buslov errout_qdisc: 970460b3601SCong Wang if (*q) { 971e368fdb6SVlad Buslov qdisc_put(*q); 972460b3601SCong Wang *q = NULL; 973460b3601SCong Wang } 974e368fdb6SVlad Buslov return ERR_PTR(err); 975e368fdb6SVlad Buslov } 976e368fdb6SVlad Buslov 977e368fdb6SVlad Buslov static void tcf_block_release(struct Qdisc *q, struct tcf_block *block) 978e368fdb6SVlad Buslov { 979787ce6d0SVlad Buslov if (!IS_ERR_OR_NULL(block)) 980787ce6d0SVlad Buslov tcf_block_refcnt_put(block); 981787ce6d0SVlad Buslov 982e368fdb6SVlad Buslov if (q) 983e368fdb6SVlad Buslov qdisc_put(q); 984c431f89bSVlad Buslov } 985c431f89bSVlad Buslov 986f36fe1c4SJiri Pirko struct tcf_block_owner_item { 987f36fe1c4SJiri Pirko struct list_head list; 988f36fe1c4SJiri Pirko struct Qdisc *q; 989f36fe1c4SJiri Pirko enum tcf_block_binder_type binder_type; 990f36fe1c4SJiri Pirko }; 991f36fe1c4SJiri Pirko 992f36fe1c4SJiri Pirko static void 993f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(struct tcf_block *block, 994f36fe1c4SJiri Pirko struct Qdisc *q, 995f36fe1c4SJiri Pirko enum tcf_block_binder_type binder_type) 996f36fe1c4SJiri Pirko { 997f36fe1c4SJiri Pirko if (block->keep_dst && 998f36fe1c4SJiri Pirko binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS && 999f36fe1c4SJiri Pirko binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 1000f36fe1c4SJiri Pirko netif_keep_dst(qdisc_dev(q)); 1001f36fe1c4SJiri Pirko } 1002f36fe1c4SJiri Pirko 1003f36fe1c4SJiri Pirko void tcf_block_netif_keep_dst(struct tcf_block *block) 1004f36fe1c4SJiri Pirko { 1005f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1006f36fe1c4SJiri Pirko 1007f36fe1c4SJiri Pirko block->keep_dst = true; 1008f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) 1009f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, item->q, 1010f36fe1c4SJiri Pirko item->binder_type); 1011f36fe1c4SJiri Pirko } 1012f36fe1c4SJiri Pirko EXPORT_SYMBOL(tcf_block_netif_keep_dst); 1013f36fe1c4SJiri Pirko 1014f36fe1c4SJiri Pirko static int tcf_block_owner_add(struct tcf_block *block, 1015f36fe1c4SJiri Pirko struct Qdisc *q, 1016f36fe1c4SJiri Pirko enum tcf_block_binder_type binder_type) 1017f36fe1c4SJiri Pirko { 1018f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1019f36fe1c4SJiri Pirko 1020f36fe1c4SJiri Pirko item = kmalloc(sizeof(*item), GFP_KERNEL); 1021f36fe1c4SJiri Pirko if (!item) 1022f36fe1c4SJiri Pirko return -ENOMEM; 1023f36fe1c4SJiri Pirko item->q = q; 1024f36fe1c4SJiri Pirko item->binder_type = binder_type; 1025f36fe1c4SJiri Pirko list_add(&item->list, &block->owner_list); 1026f36fe1c4SJiri Pirko return 0; 1027f36fe1c4SJiri Pirko } 1028f36fe1c4SJiri Pirko 1029f36fe1c4SJiri Pirko static void tcf_block_owner_del(struct tcf_block *block, 1030f36fe1c4SJiri Pirko struct Qdisc *q, 1031f36fe1c4SJiri Pirko enum tcf_block_binder_type binder_type) 1032f36fe1c4SJiri Pirko { 1033f36fe1c4SJiri Pirko struct tcf_block_owner_item *item; 1034f36fe1c4SJiri Pirko 1035f36fe1c4SJiri Pirko list_for_each_entry(item, &block->owner_list, list) { 1036f36fe1c4SJiri Pirko if (item->q == q && item->binder_type == binder_type) { 1037f36fe1c4SJiri Pirko list_del(&item->list); 1038f36fe1c4SJiri Pirko kfree(item); 1039f36fe1c4SJiri Pirko return; 1040f36fe1c4SJiri Pirko } 1041f36fe1c4SJiri Pirko } 1042f36fe1c4SJiri Pirko WARN_ON(1); 1043f36fe1c4SJiri Pirko } 1044f36fe1c4SJiri Pirko 104548617387SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 104648617387SJiri Pirko struct tcf_block_ext_info *ei, 104748617387SJiri Pirko struct netlink_ext_ack *extack) 104848617387SJiri Pirko { 104948617387SJiri Pirko struct net *net = qdisc_net(q); 105048617387SJiri Pirko struct tcf_block *block = NULL; 105148617387SJiri Pirko int err; 105248617387SJiri Pirko 1053787ce6d0SVlad Buslov if (ei->block_index) 105448617387SJiri Pirko /* block_index not 0 means the shared block is requested */ 1055787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, ei->block_index); 105648617387SJiri Pirko 105748617387SJiri Pirko if (!block) { 1058bb047dddSJiri Pirko block = tcf_block_create(net, q, ei->block_index, extack); 105948617387SJiri Pirko if (IS_ERR(block)) 106048617387SJiri Pirko return PTR_ERR(block); 1061bb047dddSJiri Pirko if (tcf_block_shared(block)) { 1062bb047dddSJiri Pirko err = tcf_block_insert(block, net, extack); 106348617387SJiri Pirko if (err) 106448617387SJiri Pirko goto err_block_insert; 106548617387SJiri Pirko } 106648617387SJiri Pirko } 106748617387SJiri Pirko 1068f36fe1c4SJiri Pirko err = tcf_block_owner_add(block, q, ei->binder_type); 1069f36fe1c4SJiri Pirko if (err) 1070f36fe1c4SJiri Pirko goto err_block_owner_add; 1071f36fe1c4SJiri Pirko 1072f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(block, q, ei->binder_type); 1073f36fe1c4SJiri Pirko 1074f71e0ca4SJiri Pirko err = tcf_chain0_head_change_cb_add(block, ei, extack); 1075a9b19443SJiri Pirko if (err) 1076f71e0ca4SJiri Pirko goto err_chain0_head_change_cb_add; 1077caa72601SJiri Pirko 107860513bd8SJohn Hurley err = tcf_block_offload_bind(block, q, ei, extack); 1079caa72601SJiri Pirko if (err) 1080caa72601SJiri Pirko goto err_block_offload_bind; 1081caa72601SJiri Pirko 10826529eabaSJiri Pirko *p_block = block; 10836529eabaSJiri Pirko return 0; 10842190d1d0SJiri Pirko 1085caa72601SJiri Pirko err_block_offload_bind: 1086f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1087f71e0ca4SJiri Pirko err_chain0_head_change_cb_add: 1088f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 1089f36fe1c4SJiri Pirko err_block_owner_add: 109048617387SJiri Pirko err_block_insert: 1091787ce6d0SVlad Buslov tcf_block_refcnt_put(block); 10922190d1d0SJiri Pirko return err; 10936529eabaSJiri Pirko } 10948c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext); 10958c4083b3SJiri Pirko 1096c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 1097c7eb7d72SJiri Pirko { 1098c7eb7d72SJiri Pirko struct tcf_proto __rcu **p_filter_chain = priv; 1099c7eb7d72SJiri Pirko 1100c7eb7d72SJiri Pirko rcu_assign_pointer(*p_filter_chain, tp_head); 1101c7eb7d72SJiri Pirko } 1102c7eb7d72SJiri Pirko 11038c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block, 11048d1a77f9SAlexander Aring struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, 11058d1a77f9SAlexander Aring struct netlink_ext_ack *extack) 11068c4083b3SJiri Pirko { 1107c7eb7d72SJiri Pirko struct tcf_block_ext_info ei = { 1108c7eb7d72SJiri Pirko .chain_head_change = tcf_chain_head_change_dflt, 1109c7eb7d72SJiri Pirko .chain_head_change_priv = p_filter_chain, 1110c7eb7d72SJiri Pirko }; 11118c4083b3SJiri Pirko 1112c7eb7d72SJiri Pirko WARN_ON(!p_filter_chain); 11138d1a77f9SAlexander Aring return tcf_block_get_ext(p_block, q, &ei, extack); 11148c4083b3SJiri Pirko } 11156529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get); 11166529eabaSJiri Pirko 11177aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound 1118a60b3f51SRoman Kapl * actions should be all removed after flushing. 1119e2ef7544SCong Wang */ 1120c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 1121e1ea2f98SDavid S. Miller struct tcf_block_ext_info *ei) 11227aa0045dSCong Wang { 1123c30abd5eSDavid S. Miller if (!block) 1124c30abd5eSDavid S. Miller return; 1125f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(block, ei); 1126f36fe1c4SJiri Pirko tcf_block_owner_del(block, q, ei->binder_type); 112748617387SJiri Pirko 11280607e439SVlad Buslov __tcf_block_put(block, q, ei); 112948617387SJiri Pirko } 11308c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext); 11318c4083b3SJiri Pirko 11328c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block) 11338c4083b3SJiri Pirko { 11348c4083b3SJiri Pirko struct tcf_block_ext_info ei = {0, }; 11358c4083b3SJiri Pirko 11364853f128SJiri Pirko if (!block) 11374853f128SJiri Pirko return; 1138c7eb7d72SJiri Pirko tcf_block_put_ext(block, block->q, &ei); 11398c4083b3SJiri Pirko } 1140e1ea2f98SDavid S. Miller 11416529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put); 1142cf1facdaSJiri Pirko 1143acb67442SJiri Pirko struct tcf_block_cb { 1144acb67442SJiri Pirko struct list_head list; 1145acb67442SJiri Pirko tc_setup_cb_t *cb; 1146acb67442SJiri Pirko void *cb_ident; 1147acb67442SJiri Pirko void *cb_priv; 1148acb67442SJiri Pirko unsigned int refcnt; 1149acb67442SJiri Pirko }; 1150acb67442SJiri Pirko 1151acb67442SJiri Pirko void *tcf_block_cb_priv(struct tcf_block_cb *block_cb) 1152acb67442SJiri Pirko { 1153acb67442SJiri Pirko return block_cb->cb_priv; 1154acb67442SJiri Pirko } 1155acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_priv); 1156acb67442SJiri Pirko 1157acb67442SJiri Pirko struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block, 1158acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident) 1159acb67442SJiri Pirko { struct tcf_block_cb *block_cb; 1160acb67442SJiri Pirko 1161acb67442SJiri Pirko list_for_each_entry(block_cb, &block->cb_list, list) 1162acb67442SJiri Pirko if (block_cb->cb == cb && block_cb->cb_ident == cb_ident) 1163acb67442SJiri Pirko return block_cb; 1164acb67442SJiri Pirko return NULL; 1165acb67442SJiri Pirko } 1166acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_lookup); 1167acb67442SJiri Pirko 1168acb67442SJiri Pirko void tcf_block_cb_incref(struct tcf_block_cb *block_cb) 1169acb67442SJiri Pirko { 1170acb67442SJiri Pirko block_cb->refcnt++; 1171acb67442SJiri Pirko } 1172acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_incref); 1173acb67442SJiri Pirko 1174acb67442SJiri Pirko unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb) 1175acb67442SJiri Pirko { 1176acb67442SJiri Pirko return --block_cb->refcnt; 1177acb67442SJiri Pirko } 1178acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_decref); 1179acb67442SJiri Pirko 118032636742SJohn Hurley static int 118132636742SJohn Hurley tcf_block_playback_offloads(struct tcf_block *block, tc_setup_cb_t *cb, 118232636742SJohn Hurley void *cb_priv, bool add, bool offload_in_use, 118332636742SJohn Hurley struct netlink_ext_ack *extack) 118432636742SJohn Hurley { 118532636742SJohn Hurley struct tcf_chain *chain; 118632636742SJohn Hurley struct tcf_proto *tp; 118732636742SJohn Hurley int err; 118832636742SJohn Hurley 118932636742SJohn Hurley list_for_each_entry(chain, &block->chain_list, list) { 119032636742SJohn Hurley for (tp = rtnl_dereference(chain->filter_chain); tp; 119132636742SJohn Hurley tp = rtnl_dereference(tp->next)) { 119232636742SJohn Hurley if (tp->ops->reoffload) { 119332636742SJohn Hurley err = tp->ops->reoffload(tp, add, cb, cb_priv, 119432636742SJohn Hurley extack); 119532636742SJohn Hurley if (err && add) 119632636742SJohn Hurley goto err_playback_remove; 119732636742SJohn Hurley } else if (add && offload_in_use) { 119832636742SJohn Hurley err = -EOPNOTSUPP; 119932636742SJohn Hurley NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support"); 120032636742SJohn Hurley goto err_playback_remove; 120132636742SJohn Hurley } 120232636742SJohn Hurley } 120332636742SJohn Hurley } 120432636742SJohn Hurley 120532636742SJohn Hurley return 0; 120632636742SJohn Hurley 120732636742SJohn Hurley err_playback_remove: 120832636742SJohn Hurley tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use, 120932636742SJohn Hurley extack); 121032636742SJohn Hurley return err; 121132636742SJohn Hurley } 121232636742SJohn Hurley 1213acb67442SJiri Pirko struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block, 1214acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident, 121560513bd8SJohn Hurley void *cb_priv, 121660513bd8SJohn Hurley struct netlink_ext_ack *extack) 1217acb67442SJiri Pirko { 1218acb67442SJiri Pirko struct tcf_block_cb *block_cb; 121932636742SJohn Hurley int err; 1220acb67442SJiri Pirko 122132636742SJohn Hurley /* Replay any already present rules */ 122232636742SJohn Hurley err = tcf_block_playback_offloads(block, cb, cb_priv, true, 122332636742SJohn Hurley tcf_block_offload_in_use(block), 122432636742SJohn Hurley extack); 122532636742SJohn Hurley if (err) 122632636742SJohn Hurley return ERR_PTR(err); 1227caa72601SJiri Pirko 1228acb67442SJiri Pirko block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL); 1229acb67442SJiri Pirko if (!block_cb) 1230caa72601SJiri Pirko return ERR_PTR(-ENOMEM); 1231acb67442SJiri Pirko block_cb->cb = cb; 1232acb67442SJiri Pirko block_cb->cb_ident = cb_ident; 1233acb67442SJiri Pirko block_cb->cb_priv = cb_priv; 1234acb67442SJiri Pirko list_add(&block_cb->list, &block->cb_list); 1235acb67442SJiri Pirko return block_cb; 1236acb67442SJiri Pirko } 1237acb67442SJiri Pirko EXPORT_SYMBOL(__tcf_block_cb_register); 1238acb67442SJiri Pirko 1239acb67442SJiri Pirko int tcf_block_cb_register(struct tcf_block *block, 1240acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident, 124160513bd8SJohn Hurley void *cb_priv, struct netlink_ext_ack *extack) 1242acb67442SJiri Pirko { 1243acb67442SJiri Pirko struct tcf_block_cb *block_cb; 1244acb67442SJiri Pirko 124560513bd8SJohn Hurley block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv, 124660513bd8SJohn Hurley extack); 1247baa2d2b1SGustavo A. R. Silva return PTR_ERR_OR_ZERO(block_cb); 1248acb67442SJiri Pirko } 1249acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_register); 1250acb67442SJiri Pirko 125132636742SJohn Hurley void __tcf_block_cb_unregister(struct tcf_block *block, 125232636742SJohn Hurley struct tcf_block_cb *block_cb) 1253acb67442SJiri Pirko { 125432636742SJohn Hurley tcf_block_playback_offloads(block, block_cb->cb, block_cb->cb_priv, 125532636742SJohn Hurley false, tcf_block_offload_in_use(block), 125632636742SJohn Hurley NULL); 1257acb67442SJiri Pirko list_del(&block_cb->list); 1258acb67442SJiri Pirko kfree(block_cb); 1259acb67442SJiri Pirko } 1260acb67442SJiri Pirko EXPORT_SYMBOL(__tcf_block_cb_unregister); 1261acb67442SJiri Pirko 1262acb67442SJiri Pirko void tcf_block_cb_unregister(struct tcf_block *block, 1263acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident) 1264acb67442SJiri Pirko { 1265acb67442SJiri Pirko struct tcf_block_cb *block_cb; 1266acb67442SJiri Pirko 1267acb67442SJiri Pirko block_cb = tcf_block_cb_lookup(block, cb, cb_ident); 1268acb67442SJiri Pirko if (!block_cb) 1269acb67442SJiri Pirko return; 127032636742SJohn Hurley __tcf_block_cb_unregister(block, block_cb); 1271acb67442SJiri Pirko } 1272acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_unregister); 1273acb67442SJiri Pirko 127487d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached 127587d83093SJiri Pirko * to this qdisc, (optionally) tests for protocol and asks 127687d83093SJiri Pirko * specific classifiers. 127787d83093SJiri Pirko */ 127887d83093SJiri Pirko int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, 127987d83093SJiri Pirko struct tcf_result *res, bool compat_mode) 128087d83093SJiri Pirko { 128187d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 128287d83093SJiri Pirko const int max_reclassify_loop = 4; 1283ee538dceSJiri Pirko const struct tcf_proto *orig_tp = tp; 1284ee538dceSJiri Pirko const struct tcf_proto *first_tp; 128587d83093SJiri Pirko int limit = 0; 128687d83093SJiri Pirko 128787d83093SJiri Pirko reclassify: 128887d83093SJiri Pirko #endif 128987d83093SJiri Pirko for (; tp; tp = rcu_dereference_bh(tp->next)) { 1290cd0c4e70SCong Wang __be16 protocol = tc_skb_protocol(skb); 129187d83093SJiri Pirko int err; 129287d83093SJiri Pirko 129387d83093SJiri Pirko if (tp->protocol != protocol && 129487d83093SJiri Pirko tp->protocol != htons(ETH_P_ALL)) 129587d83093SJiri Pirko continue; 129687d83093SJiri Pirko 129787d83093SJiri Pirko err = tp->classify(skb, tp, res); 129887d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 1299db50514fSJiri Pirko if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 1300ee538dceSJiri Pirko first_tp = orig_tp; 130187d83093SJiri Pirko goto reset; 1302db50514fSJiri Pirko } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 1303ee538dceSJiri Pirko first_tp = res->goto_tp; 1304db50514fSJiri Pirko goto reset; 1305db50514fSJiri Pirko } 130687d83093SJiri Pirko #endif 130787d83093SJiri Pirko if (err >= 0) 130887d83093SJiri Pirko return err; 130987d83093SJiri Pirko } 131087d83093SJiri Pirko 131187d83093SJiri Pirko return TC_ACT_UNSPEC; /* signal: continue lookup */ 131287d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 131387d83093SJiri Pirko reset: 131487d83093SJiri Pirko if (unlikely(limit++ >= max_reclassify_loop)) { 13159d3aaff3SJiri Pirko net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n", 13169d3aaff3SJiri Pirko tp->chain->block->index, 13179d3aaff3SJiri Pirko tp->prio & 0xffff, 131887d83093SJiri Pirko ntohs(tp->protocol)); 131987d83093SJiri Pirko return TC_ACT_SHOT; 132087d83093SJiri Pirko } 132187d83093SJiri Pirko 1322ee538dceSJiri Pirko tp = first_tp; 132387d83093SJiri Pirko goto reclassify; 132487d83093SJiri Pirko #endif 132587d83093SJiri Pirko } 132687d83093SJiri Pirko EXPORT_SYMBOL(tcf_classify); 132787d83093SJiri Pirko 13282190d1d0SJiri Pirko struct tcf_chain_info { 13292190d1d0SJiri Pirko struct tcf_proto __rcu **pprev; 13302190d1d0SJiri Pirko struct tcf_proto __rcu *next; 13312190d1d0SJiri Pirko }; 13322190d1d0SJiri Pirko 13332190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain_info *chain_info) 13342190d1d0SJiri Pirko { 13352190d1d0SJiri Pirko return rtnl_dereference(*chain_info->pprev); 13362190d1d0SJiri Pirko } 13372190d1d0SJiri Pirko 13382190d1d0SJiri Pirko static void tcf_chain_tp_insert(struct tcf_chain *chain, 13392190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 13402190d1d0SJiri Pirko struct tcf_proto *tp) 13412190d1d0SJiri Pirko { 1342c7eb7d72SJiri Pirko if (*chain_info->pprev == chain->filter_chain) 1343f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, tp); 13442190d1d0SJiri Pirko RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info)); 13452190d1d0SJiri Pirko rcu_assign_pointer(*chain_info->pprev, tp); 1346e2ef7544SCong Wang tcf_chain_hold(chain); 13472190d1d0SJiri Pirko } 13482190d1d0SJiri Pirko 13492190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain, 13502190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 13512190d1d0SJiri Pirko struct tcf_proto *tp) 13522190d1d0SJiri Pirko { 13532190d1d0SJiri Pirko struct tcf_proto *next = rtnl_dereference(chain_info->next); 13542190d1d0SJiri Pirko 1355c7eb7d72SJiri Pirko if (tp == chain->filter_chain) 1356f71e0ca4SJiri Pirko tcf_chain0_head_change(chain, next); 13572190d1d0SJiri Pirko RCU_INIT_POINTER(*chain_info->pprev, next); 1358e2ef7544SCong Wang tcf_chain_put(chain); 13592190d1d0SJiri Pirko } 13602190d1d0SJiri Pirko 13612190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 13622190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 13632190d1d0SJiri Pirko u32 protocol, u32 prio, 13642190d1d0SJiri Pirko bool prio_allocate) 13652190d1d0SJiri Pirko { 13662190d1d0SJiri Pirko struct tcf_proto **pprev; 13672190d1d0SJiri Pirko struct tcf_proto *tp; 13682190d1d0SJiri Pirko 13692190d1d0SJiri Pirko /* Check the chain for existence of proto-tcf with this priority */ 13702190d1d0SJiri Pirko for (pprev = &chain->filter_chain; 13712190d1d0SJiri Pirko (tp = rtnl_dereference(*pprev)); pprev = &tp->next) { 13722190d1d0SJiri Pirko if (tp->prio >= prio) { 13732190d1d0SJiri Pirko if (tp->prio == prio) { 13742190d1d0SJiri Pirko if (prio_allocate || 13752190d1d0SJiri Pirko (tp->protocol != protocol && protocol)) 13762190d1d0SJiri Pirko return ERR_PTR(-EINVAL); 13772190d1d0SJiri Pirko } else { 13782190d1d0SJiri Pirko tp = NULL; 13792190d1d0SJiri Pirko } 13802190d1d0SJiri Pirko break; 13812190d1d0SJiri Pirko } 13822190d1d0SJiri Pirko } 13832190d1d0SJiri Pirko chain_info->pprev = pprev; 13842190d1d0SJiri Pirko chain_info->next = tp ? tp->next : NULL; 13852190d1d0SJiri Pirko return tp; 13862190d1d0SJiri Pirko } 13872190d1d0SJiri Pirko 13887120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb, 13897960d1daSJiri Pirko struct tcf_proto *tp, struct tcf_block *block, 13907960d1daSJiri Pirko struct Qdisc *q, u32 parent, void *fh, 13917960d1daSJiri Pirko u32 portid, u32 seq, u16 flags, int event) 13927120371cSWANG Cong { 13937120371cSWANG Cong struct tcmsg *tcm; 13947120371cSWANG Cong struct nlmsghdr *nlh; 13957120371cSWANG Cong unsigned char *b = skb_tail_pointer(skb); 13967120371cSWANG Cong 13977120371cSWANG Cong nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 13987120371cSWANG Cong if (!nlh) 13997120371cSWANG Cong goto out_nlmsg_trim; 14007120371cSWANG Cong tcm = nlmsg_data(nlh); 14017120371cSWANG Cong tcm->tcm_family = AF_UNSPEC; 14027120371cSWANG Cong tcm->tcm__pad1 = 0; 14037120371cSWANG Cong tcm->tcm__pad2 = 0; 14047960d1daSJiri Pirko if (q) { 1405a10fa201SJiri Pirko tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 1406a10fa201SJiri Pirko tcm->tcm_parent = parent; 14077960d1daSJiri Pirko } else { 14087960d1daSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 14097960d1daSJiri Pirko tcm->tcm_block_index = block->index; 14107960d1daSJiri Pirko } 14117120371cSWANG Cong tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 14127120371cSWANG Cong if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 14137120371cSWANG Cong goto nla_put_failure; 14147120371cSWANG Cong if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 14157120371cSWANG Cong goto nla_put_failure; 14167120371cSWANG Cong if (!fh) { 14177120371cSWANG Cong tcm->tcm_handle = 0; 14187120371cSWANG Cong } else { 14197120371cSWANG Cong if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) 14207120371cSWANG Cong goto nla_put_failure; 14217120371cSWANG Cong } 14227120371cSWANG Cong nlh->nlmsg_len = skb_tail_pointer(skb) - b; 14237120371cSWANG Cong return skb->len; 14247120371cSWANG Cong 14257120371cSWANG Cong out_nlmsg_trim: 14267120371cSWANG Cong nla_put_failure: 14277120371cSWANG Cong nlmsg_trim(skb, b); 14287120371cSWANG Cong return -1; 14297120371cSWANG Cong } 14307120371cSWANG Cong 14317120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb, 14327120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 14337960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 14347960d1daSJiri Pirko u32 parent, void *fh, int event, bool unicast) 14357120371cSWANG Cong { 14367120371cSWANG Cong struct sk_buff *skb; 14377120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 14387120371cSWANG Cong 14397120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 14407120371cSWANG Cong if (!skb) 14417120371cSWANG Cong return -ENOBUFS; 14427120371cSWANG Cong 14437960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 14447960d1daSJiri Pirko n->nlmsg_seq, n->nlmsg_flags, event) <= 0) { 14457120371cSWANG Cong kfree_skb(skb); 14467120371cSWANG Cong return -EINVAL; 14477120371cSWANG Cong } 14487120371cSWANG Cong 14497120371cSWANG Cong if (unicast) 14507120371cSWANG Cong return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 14517120371cSWANG Cong 14527120371cSWANG Cong return rtnetlink_send(skb, net, portid, RTNLGRP_TC, 14537120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 14547120371cSWANG Cong } 14557120371cSWANG Cong 14567120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 14577120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 14587960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 1459c35a4accSAlexander Aring u32 parent, void *fh, bool unicast, bool *last, 1460c35a4accSAlexander Aring struct netlink_ext_ack *extack) 14617120371cSWANG Cong { 14627120371cSWANG Cong struct sk_buff *skb; 14637120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 14647120371cSWANG Cong int err; 14657120371cSWANG Cong 14667120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 14677120371cSWANG Cong if (!skb) 14687120371cSWANG Cong return -ENOBUFS; 14697120371cSWANG Cong 14707960d1daSJiri Pirko if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 14717960d1daSJiri Pirko n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER) <= 0) { 1472c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to build del event notification"); 14737120371cSWANG Cong kfree_skb(skb); 14747120371cSWANG Cong return -EINVAL; 14757120371cSWANG Cong } 14767120371cSWANG Cong 1477571acf21SAlexander Aring err = tp->ops->delete(tp, fh, last, extack); 14787120371cSWANG Cong if (err) { 14797120371cSWANG Cong kfree_skb(skb); 14807120371cSWANG Cong return err; 14817120371cSWANG Cong } 14827120371cSWANG Cong 14837120371cSWANG Cong if (unicast) 14847120371cSWANG Cong return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 14857120371cSWANG Cong 1486c35a4accSAlexander Aring err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 14877120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 1488c35a4accSAlexander Aring if (err < 0) 1489c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to send filter delete notification"); 1490c35a4accSAlexander Aring return err; 14917120371cSWANG Cong } 14927120371cSWANG Cong 14937120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 14947960d1daSJiri Pirko struct tcf_block *block, struct Qdisc *q, 14957960d1daSJiri Pirko u32 parent, struct nlmsghdr *n, 14967120371cSWANG Cong struct tcf_chain *chain, int event) 14977120371cSWANG Cong { 14987120371cSWANG Cong struct tcf_proto *tp; 14997120371cSWANG Cong 15007120371cSWANG Cong for (tp = rtnl_dereference(chain->filter_chain); 15017120371cSWANG Cong tp; tp = rtnl_dereference(tp->next)) 15027960d1daSJiri Pirko tfilter_notify(net, oskb, n, tp, block, 150353189183SYueHaibing q, parent, NULL, event, false); 15047120371cSWANG Cong } 15057120371cSWANG Cong 1506c431f89bSVlad Buslov static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1507c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 15081da177e4SLinus Torvalds { 15093b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1510add93b61SPatrick McHardy struct nlattr *tca[TCA_MAX + 1]; 15111da177e4SLinus Torvalds struct tcmsg *t; 15121da177e4SLinus Torvalds u32 protocol; 15131da177e4SLinus Torvalds u32 prio; 15149d36d9e5SJiri Pirko bool prio_allocate; 15151da177e4SLinus Torvalds u32 parent; 15165bc17018SJiri Pirko u32 chain_index; 15177960d1daSJiri Pirko struct Qdisc *q = NULL; 15182190d1d0SJiri Pirko struct tcf_chain_info chain_info; 15195bc17018SJiri Pirko struct tcf_chain *chain = NULL; 15206529eabaSJiri Pirko struct tcf_block *block; 15211da177e4SLinus Torvalds struct tcf_proto *tp; 15221da177e4SLinus Torvalds unsigned long cl; 15238113c095SWANG Cong void *fh; 15241da177e4SLinus Torvalds int err; 1525628185cfSDaniel Borkmann int tp_created; 15261da177e4SLinus Torvalds 1527c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 1528dfc47ef8SEric W. Biederman return -EPERM; 1529de179c8cSHong zhi guo 15301da177e4SLinus Torvalds replay: 1531628185cfSDaniel Borkmann tp_created = 0; 1532628185cfSDaniel Borkmann 1533e331473fSDavide Caratti err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack); 1534de179c8cSHong zhi guo if (err < 0) 1535de179c8cSHong zhi guo return err; 1536de179c8cSHong zhi guo 1537942b8165SDavid S. Miller t = nlmsg_data(n); 15381da177e4SLinus Torvalds protocol = TC_H_MIN(t->tcm_info); 15391da177e4SLinus Torvalds prio = TC_H_MAJ(t->tcm_info); 15409d36d9e5SJiri Pirko prio_allocate = false; 15411da177e4SLinus Torvalds parent = t->tcm_parent; 15421da177e4SLinus Torvalds cl = 0; 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds if (prio == 0) { 1545ea7f8277SDaniel Borkmann /* If no priority is provided by the user, 1546ea7f8277SDaniel Borkmann * we allocate one. 1547ea7f8277SDaniel Borkmann */ 1548ea7f8277SDaniel Borkmann if (n->nlmsg_flags & NLM_F_CREATE) { 15491da177e4SLinus Torvalds prio = TC_H_MAKE(0x80000000U, 0U); 15509d36d9e5SJiri Pirko prio_allocate = true; 1551c431f89bSVlad Buslov } else { 1552c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 1553ea7f8277SDaniel Borkmann return -ENOENT; 1554ea7f8277SDaniel Borkmann } 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds 15571da177e4SLinus Torvalds /* Find head of filter chain. */ 15581da177e4SLinus Torvalds 1559c431f89bSVlad Buslov block = tcf_block_find(net, &q, &parent, &cl, 1560c431f89bSVlad Buslov t->tcm_ifindex, t->tcm_block_index, extack); 1561c431f89bSVlad Buslov if (IS_ERR(block)) { 1562c431f89bSVlad Buslov err = PTR_ERR(block); 15637960d1daSJiri Pirko goto errout; 15647960d1daSJiri Pirko } 15655bc17018SJiri Pirko 15665bc17018SJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 15675bc17018SJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 1568c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 15695bc17018SJiri Pirko err = -EINVAL; 15705bc17018SJiri Pirko goto errout; 15715bc17018SJiri Pirko } 1572c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, true); 15735bc17018SJiri Pirko if (!chain) { 1574d5ed72a5SJiri Pirko NL_SET_ERR_MSG(extack, "Cannot create specified filter chain"); 1575c431f89bSVlad Buslov err = -ENOMEM; 1576ea7f8277SDaniel Borkmann goto errout; 1577ea7f8277SDaniel Borkmann } 15781da177e4SLinus Torvalds 15792190d1d0SJiri Pirko tp = tcf_chain_tp_find(chain, &chain_info, protocol, 15802190d1d0SJiri Pirko prio, prio_allocate); 15812190d1d0SJiri Pirko if (IS_ERR(tp)) { 1582c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 15832190d1d0SJiri Pirko err = PTR_ERR(tp); 15841da177e4SLinus Torvalds goto errout; 15856bb16e7aSJiri Pirko } 15861da177e4SLinus Torvalds 15871da177e4SLinus Torvalds if (tp == NULL) { 15881da177e4SLinus Torvalds /* Proto-tcf does not exist, create new one */ 15891da177e4SLinus Torvalds 15906bb16e7aSJiri Pirko if (tca[TCA_KIND] == NULL || !protocol) { 1591c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified"); 15926bb16e7aSJiri Pirko err = -EINVAL; 15931da177e4SLinus Torvalds goto errout; 15946bb16e7aSJiri Pirko } 15951da177e4SLinus Torvalds 1596c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 1597c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 15986bb16e7aSJiri Pirko err = -ENOENT; 15991da177e4SLinus Torvalds goto errout; 16006bb16e7aSJiri Pirko } 16011da177e4SLinus Torvalds 16029d36d9e5SJiri Pirko if (prio_allocate) 16032190d1d0SJiri Pirko prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info)); 16041da177e4SLinus Torvalds 160533a48927SJiri Pirko tp = tcf_proto_create(nla_data(tca[TCA_KIND]), 1606c35a4accSAlexander Aring protocol, prio, chain, extack); 160733a48927SJiri Pirko if (IS_ERR(tp)) { 160833a48927SJiri Pirko err = PTR_ERR(tp); 16091da177e4SLinus Torvalds goto errout; 16101da177e4SLinus Torvalds } 161112186be7SMinoru Usui tp_created = 1; 16126bb16e7aSJiri Pirko } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 1613c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 16146bb16e7aSJiri Pirko err = -EINVAL; 16151da177e4SLinus Torvalds goto errout; 16166bb16e7aSJiri Pirko } 16171da177e4SLinus Torvalds 16181da177e4SLinus Torvalds fh = tp->ops->get(tp, t->tcm_handle); 16191da177e4SLinus Torvalds 16208113c095SWANG Cong if (!fh) { 1621c431f89bSVlad Buslov if (!(n->nlmsg_flags & NLM_F_CREATE)) { 1622c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 16236bb16e7aSJiri Pirko err = -ENOENT; 16241da177e4SLinus Torvalds goto errout; 16256bb16e7aSJiri Pirko } 1626c431f89bSVlad Buslov } else if (n->nlmsg_flags & NLM_F_EXCL) { 1627c35a4accSAlexander Aring NL_SET_ERR_MSG(extack, "Filter already exists"); 16286bb16e7aSJiri Pirko err = -EEXIST; 16291da177e4SLinus Torvalds goto errout; 163012186be7SMinoru Usui } 16311da177e4SLinus Torvalds 16329f407f17SJiri Pirko if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) { 16339f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind"); 16349f407f17SJiri Pirko err = -EINVAL; 16359f407f17SJiri Pirko goto errout; 16369f407f17SJiri Pirko } 16379f407f17SJiri Pirko 16382f7ef2f8SCong Wang err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 16397306db38SAlexander Aring n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE, 16407306db38SAlexander Aring extack); 164112186be7SMinoru Usui if (err == 0) { 16422190d1d0SJiri Pirko if (tp_created) 16432190d1d0SJiri Pirko tcf_chain_tp_insert(chain, &chain_info, tp); 16447960d1daSJiri Pirko tfilter_notify(net, skb, n, tp, block, q, parent, fh, 1645a10fa201SJiri Pirko RTM_NEWTFILTER, false); 164612186be7SMinoru Usui } else { 164712186be7SMinoru Usui if (tp_created) 1648715df5ecSJakub Kicinski tcf_proto_destroy(tp, NULL); 164912186be7SMinoru Usui } 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds errout: 16525bc17018SJiri Pirko if (chain) 16535bc17018SJiri Pirko tcf_chain_put(chain); 1654e368fdb6SVlad Buslov tcf_block_release(q, block); 16551da177e4SLinus Torvalds if (err == -EAGAIN) 16561da177e4SLinus Torvalds /* Replay the request. */ 16571da177e4SLinus Torvalds goto replay; 16581da177e4SLinus Torvalds return err; 16591da177e4SLinus Torvalds } 16601da177e4SLinus Torvalds 1661c431f89bSVlad Buslov static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1662c431f89bSVlad Buslov struct netlink_ext_ack *extack) 1663c431f89bSVlad Buslov { 1664c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 1665c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 1666c431f89bSVlad Buslov struct tcmsg *t; 1667c431f89bSVlad Buslov u32 protocol; 1668c431f89bSVlad Buslov u32 prio; 1669c431f89bSVlad Buslov u32 parent; 1670c431f89bSVlad Buslov u32 chain_index; 1671c431f89bSVlad Buslov struct Qdisc *q = NULL; 1672c431f89bSVlad Buslov struct tcf_chain_info chain_info; 1673c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 1674c431f89bSVlad Buslov struct tcf_block *block; 1675c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 1676c431f89bSVlad Buslov unsigned long cl = 0; 1677c431f89bSVlad Buslov void *fh = NULL; 1678c431f89bSVlad Buslov int err; 1679c431f89bSVlad Buslov 1680c431f89bSVlad Buslov if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 1681c431f89bSVlad Buslov return -EPERM; 1682c431f89bSVlad Buslov 1683e331473fSDavide Caratti err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack); 1684c431f89bSVlad Buslov if (err < 0) 1685c431f89bSVlad Buslov return err; 1686c431f89bSVlad Buslov 1687c431f89bSVlad Buslov t = nlmsg_data(n); 1688c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 1689c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 1690c431f89bSVlad Buslov parent = t->tcm_parent; 1691c431f89bSVlad Buslov 1692c431f89bSVlad Buslov if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) { 1693c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set"); 1694c431f89bSVlad Buslov return -ENOENT; 1695c431f89bSVlad Buslov } 1696c431f89bSVlad Buslov 1697c431f89bSVlad Buslov /* Find head of filter chain. */ 1698c431f89bSVlad Buslov 1699c431f89bSVlad Buslov block = tcf_block_find(net, &q, &parent, &cl, 1700c431f89bSVlad Buslov t->tcm_ifindex, t->tcm_block_index, extack); 1701c431f89bSVlad Buslov if (IS_ERR(block)) { 1702c431f89bSVlad Buslov err = PTR_ERR(block); 1703c431f89bSVlad Buslov goto errout; 1704c431f89bSVlad Buslov } 1705c431f89bSVlad Buslov 1706c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 1707c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 1708c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 1709c431f89bSVlad Buslov err = -EINVAL; 1710c431f89bSVlad Buslov goto errout; 1711c431f89bSVlad Buslov } 1712c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 1713c431f89bSVlad Buslov if (!chain) { 17145ca8a25cSJiri Pirko /* User requested flush on non-existent chain. Nothing to do, 17155ca8a25cSJiri Pirko * so just return success. 17165ca8a25cSJiri Pirko */ 17175ca8a25cSJiri Pirko if (prio == 0) { 17185ca8a25cSJiri Pirko err = 0; 17195ca8a25cSJiri Pirko goto errout; 17205ca8a25cSJiri Pirko } 1721c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 1722b7b4247dSJiri Pirko err = -ENOENT; 1723c431f89bSVlad Buslov goto errout; 1724c431f89bSVlad Buslov } 1725c431f89bSVlad Buslov 1726c431f89bSVlad Buslov if (prio == 0) { 1727c431f89bSVlad Buslov tfilter_notify_chain(net, skb, block, q, parent, n, 1728c431f89bSVlad Buslov chain, RTM_DELTFILTER); 1729c431f89bSVlad Buslov tcf_chain_flush(chain); 1730c431f89bSVlad Buslov err = 0; 1731c431f89bSVlad Buslov goto errout; 1732c431f89bSVlad Buslov } 1733c431f89bSVlad Buslov 1734c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 1735c431f89bSVlad Buslov prio, false); 1736c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 1737c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 17380e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 1739c431f89bSVlad Buslov goto errout; 1740c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 1741c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 1742c431f89bSVlad Buslov err = -EINVAL; 1743c431f89bSVlad Buslov goto errout; 1744c431f89bSVlad Buslov } 1745c431f89bSVlad Buslov 1746c431f89bSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 1747c431f89bSVlad Buslov 1748c431f89bSVlad Buslov if (!fh) { 1749c431f89bSVlad Buslov if (t->tcm_handle == 0) { 1750c431f89bSVlad Buslov tcf_chain_tp_remove(chain, &chain_info, tp); 1751c431f89bSVlad Buslov tfilter_notify(net, skb, n, tp, block, q, parent, fh, 1752c431f89bSVlad Buslov RTM_DELTFILTER, false); 1753c431f89bSVlad Buslov tcf_proto_destroy(tp, extack); 1754c431f89bSVlad Buslov err = 0; 1755c431f89bSVlad Buslov } else { 1756c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 1757c431f89bSVlad Buslov err = -ENOENT; 1758c431f89bSVlad Buslov } 1759c431f89bSVlad Buslov } else { 1760c431f89bSVlad Buslov bool last; 1761c431f89bSVlad Buslov 1762c431f89bSVlad Buslov err = tfilter_del_notify(net, skb, n, tp, block, 1763c431f89bSVlad Buslov q, parent, fh, false, &last, 1764c431f89bSVlad Buslov extack); 1765c431f89bSVlad Buslov if (err) 1766c431f89bSVlad Buslov goto errout; 1767c431f89bSVlad Buslov if (last) { 1768c431f89bSVlad Buslov tcf_chain_tp_remove(chain, &chain_info, tp); 1769c431f89bSVlad Buslov tcf_proto_destroy(tp, extack); 1770c431f89bSVlad Buslov } 1771c431f89bSVlad Buslov } 1772c431f89bSVlad Buslov 1773c431f89bSVlad Buslov errout: 1774c431f89bSVlad Buslov if (chain) 1775c431f89bSVlad Buslov tcf_chain_put(chain); 1776e368fdb6SVlad Buslov tcf_block_release(q, block); 1777c431f89bSVlad Buslov return err; 1778c431f89bSVlad Buslov } 1779c431f89bSVlad Buslov 1780c431f89bSVlad Buslov static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 1781c431f89bSVlad Buslov struct netlink_ext_ack *extack) 1782c431f89bSVlad Buslov { 1783c431f89bSVlad Buslov struct net *net = sock_net(skb->sk); 1784c431f89bSVlad Buslov struct nlattr *tca[TCA_MAX + 1]; 1785c431f89bSVlad Buslov struct tcmsg *t; 1786c431f89bSVlad Buslov u32 protocol; 1787c431f89bSVlad Buslov u32 prio; 1788c431f89bSVlad Buslov u32 parent; 1789c431f89bSVlad Buslov u32 chain_index; 1790c431f89bSVlad Buslov struct Qdisc *q = NULL; 1791c431f89bSVlad Buslov struct tcf_chain_info chain_info; 1792c431f89bSVlad Buslov struct tcf_chain *chain = NULL; 1793c431f89bSVlad Buslov struct tcf_block *block; 1794c431f89bSVlad Buslov struct tcf_proto *tp = NULL; 1795c431f89bSVlad Buslov unsigned long cl = 0; 1796c431f89bSVlad Buslov void *fh = NULL; 1797c431f89bSVlad Buslov int err; 1798c431f89bSVlad Buslov 1799e331473fSDavide Caratti err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack); 1800c431f89bSVlad Buslov if (err < 0) 1801c431f89bSVlad Buslov return err; 1802c431f89bSVlad Buslov 1803c431f89bSVlad Buslov t = nlmsg_data(n); 1804c431f89bSVlad Buslov protocol = TC_H_MIN(t->tcm_info); 1805c431f89bSVlad Buslov prio = TC_H_MAJ(t->tcm_info); 1806c431f89bSVlad Buslov parent = t->tcm_parent; 1807c431f89bSVlad Buslov 1808c431f89bSVlad Buslov if (prio == 0) { 1809c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 1810c431f89bSVlad Buslov return -ENOENT; 1811c431f89bSVlad Buslov } 1812c431f89bSVlad Buslov 1813c431f89bSVlad Buslov /* Find head of filter chain. */ 1814c431f89bSVlad Buslov 1815c431f89bSVlad Buslov block = tcf_block_find(net, &q, &parent, &cl, 1816c431f89bSVlad Buslov t->tcm_ifindex, t->tcm_block_index, extack); 1817c431f89bSVlad Buslov if (IS_ERR(block)) { 1818c431f89bSVlad Buslov err = PTR_ERR(block); 1819c431f89bSVlad Buslov goto errout; 1820c431f89bSVlad Buslov } 1821c431f89bSVlad Buslov 1822c431f89bSVlad Buslov chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 1823c431f89bSVlad Buslov if (chain_index > TC_ACT_EXT_VAL_MASK) { 1824c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 1825c431f89bSVlad Buslov err = -EINVAL; 1826c431f89bSVlad Buslov goto errout; 1827c431f89bSVlad Buslov } 1828c431f89bSVlad Buslov chain = tcf_chain_get(block, chain_index, false); 1829c431f89bSVlad Buslov if (!chain) { 1830c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 1831c431f89bSVlad Buslov err = -EINVAL; 1832c431f89bSVlad Buslov goto errout; 1833c431f89bSVlad Buslov } 1834c431f89bSVlad Buslov 1835c431f89bSVlad Buslov tp = tcf_chain_tp_find(chain, &chain_info, protocol, 1836c431f89bSVlad Buslov prio, false); 1837c431f89bSVlad Buslov if (!tp || IS_ERR(tp)) { 1838c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 18390e399035SVlad Buslov err = tp ? PTR_ERR(tp) : -ENOENT; 1840c431f89bSVlad Buslov goto errout; 1841c431f89bSVlad Buslov } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 1842c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 1843c431f89bSVlad Buslov err = -EINVAL; 1844c431f89bSVlad Buslov goto errout; 1845c431f89bSVlad Buslov } 1846c431f89bSVlad Buslov 1847c431f89bSVlad Buslov fh = tp->ops->get(tp, t->tcm_handle); 1848c431f89bSVlad Buslov 1849c431f89bSVlad Buslov if (!fh) { 1850c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 1851c431f89bSVlad Buslov err = -ENOENT; 1852c431f89bSVlad Buslov } else { 1853c431f89bSVlad Buslov err = tfilter_notify(net, skb, n, tp, block, q, parent, 1854c431f89bSVlad Buslov fh, RTM_NEWTFILTER, true); 1855c431f89bSVlad Buslov if (err < 0) 1856c431f89bSVlad Buslov NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); 1857c431f89bSVlad Buslov } 1858c431f89bSVlad Buslov 1859c431f89bSVlad Buslov errout: 1860c431f89bSVlad Buslov if (chain) 1861c431f89bSVlad Buslov tcf_chain_put(chain); 1862e368fdb6SVlad Buslov tcf_block_release(q, block); 1863c431f89bSVlad Buslov return err; 1864c431f89bSVlad Buslov } 1865c431f89bSVlad Buslov 1866aa767bfeSStephen Hemminger struct tcf_dump_args { 18671da177e4SLinus Torvalds struct tcf_walker w; 18681da177e4SLinus Torvalds struct sk_buff *skb; 18691da177e4SLinus Torvalds struct netlink_callback *cb; 18707960d1daSJiri Pirko struct tcf_block *block; 1871a10fa201SJiri Pirko struct Qdisc *q; 1872a10fa201SJiri Pirko u32 parent; 18731da177e4SLinus Torvalds }; 18741da177e4SLinus Torvalds 18758113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 18761da177e4SLinus Torvalds { 18771da177e4SLinus Torvalds struct tcf_dump_args *a = (void *)arg; 1878832d1d5bSWANG Cong struct net *net = sock_net(a->skb->sk); 18791da177e4SLinus Torvalds 18807960d1daSJiri Pirko return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, 1881a10fa201SJiri Pirko n, NETLINK_CB(a->cb->skb).portid, 18825a7a5555SJamal Hadi Salim a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 18835a7a5555SJamal Hadi Salim RTM_NEWTFILTER); 18841da177e4SLinus Torvalds } 18851da177e4SLinus Torvalds 1886a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 1887a10fa201SJiri Pirko struct sk_buff *skb, struct netlink_callback *cb, 1888acb31faeSJiri Pirko long index_start, long *p_index) 1889acb31faeSJiri Pirko { 1890acb31faeSJiri Pirko struct net *net = sock_net(skb->sk); 18917960d1daSJiri Pirko struct tcf_block *block = chain->block; 1892acb31faeSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 1893acb31faeSJiri Pirko struct tcf_dump_args arg; 1894acb31faeSJiri Pirko struct tcf_proto *tp; 1895acb31faeSJiri Pirko 1896acb31faeSJiri Pirko for (tp = rtnl_dereference(chain->filter_chain); 1897acb31faeSJiri Pirko tp; tp = rtnl_dereference(tp->next), (*p_index)++) { 1898acb31faeSJiri Pirko if (*p_index < index_start) 1899acb31faeSJiri Pirko continue; 1900acb31faeSJiri Pirko if (TC_H_MAJ(tcm->tcm_info) && 1901acb31faeSJiri Pirko TC_H_MAJ(tcm->tcm_info) != tp->prio) 1902acb31faeSJiri Pirko continue; 1903acb31faeSJiri Pirko if (TC_H_MIN(tcm->tcm_info) && 1904acb31faeSJiri Pirko TC_H_MIN(tcm->tcm_info) != tp->protocol) 1905acb31faeSJiri Pirko continue; 1906acb31faeSJiri Pirko if (*p_index > index_start) 1907acb31faeSJiri Pirko memset(&cb->args[1], 0, 1908acb31faeSJiri Pirko sizeof(cb->args) - sizeof(cb->args[0])); 1909acb31faeSJiri Pirko if (cb->args[1] == 0) { 191053189183SYueHaibing if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, 1911acb31faeSJiri Pirko NETLINK_CB(cb->skb).portid, 1912acb31faeSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 1913acb31faeSJiri Pirko RTM_NEWTFILTER) <= 0) 19145bc17018SJiri Pirko return false; 1915acb31faeSJiri Pirko 1916acb31faeSJiri Pirko cb->args[1] = 1; 1917acb31faeSJiri Pirko } 1918acb31faeSJiri Pirko if (!tp->ops->walk) 1919acb31faeSJiri Pirko continue; 1920acb31faeSJiri Pirko arg.w.fn = tcf_node_dump; 1921acb31faeSJiri Pirko arg.skb = skb; 1922acb31faeSJiri Pirko arg.cb = cb; 19237960d1daSJiri Pirko arg.block = block; 1924a10fa201SJiri Pirko arg.q = q; 1925a10fa201SJiri Pirko arg.parent = parent; 1926acb31faeSJiri Pirko arg.w.stop = 0; 1927acb31faeSJiri Pirko arg.w.skip = cb->args[1] - 1; 1928acb31faeSJiri Pirko arg.w.count = 0; 192901683a14SVlad Buslov arg.w.cookie = cb->args[2]; 1930acb31faeSJiri Pirko tp->ops->walk(tp, &arg.w); 193101683a14SVlad Buslov cb->args[2] = arg.w.cookie; 1932acb31faeSJiri Pirko cb->args[1] = arg.w.count + 1; 1933acb31faeSJiri Pirko if (arg.w.stop) 19345bc17018SJiri Pirko return false; 1935acb31faeSJiri Pirko } 19365bc17018SJiri Pirko return true; 1937acb31faeSJiri Pirko } 1938acb31faeSJiri Pirko 1939bd27a875SEric Dumazet /* called with RTNL */ 19401da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 19411da177e4SLinus Torvalds { 19423b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 19435bc17018SJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 19447960d1daSJiri Pirko struct Qdisc *q = NULL; 19456529eabaSJiri Pirko struct tcf_block *block; 19462190d1d0SJiri Pirko struct tcf_chain *chain; 1947942b8165SDavid S. Miller struct tcmsg *tcm = nlmsg_data(cb->nlh); 1948acb31faeSJiri Pirko long index_start; 1949acb31faeSJiri Pirko long index; 1950a10fa201SJiri Pirko u32 parent; 19515bc17018SJiri Pirko int err; 19521da177e4SLinus Torvalds 1953573ce260SHong zhi guo if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 19541da177e4SLinus Torvalds return skb->len; 19555bc17018SJiri Pirko 1956dac9c979SDavid Ahern err = nlmsg_parse(cb->nlh, sizeof(*tcm), tca, TCA_MAX, NULL, 1957dac9c979SDavid Ahern cb->extack); 19585bc17018SJiri Pirko if (err) 19595bc17018SJiri Pirko return err; 19605bc17018SJiri Pirko 19617960d1daSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 1962787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 19637960d1daSJiri Pirko if (!block) 19647960d1daSJiri Pirko goto out; 1965d680b352SJiri Pirko /* If we work with block index, q is NULL and parent value 1966d680b352SJiri Pirko * will never be used in the following code. The check 1967d680b352SJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 1968d680b352SJiri Pirko * see that far, so set parent to zero to silence the warning 1969d680b352SJiri Pirko * about parent being uninitialized. 1970d680b352SJiri Pirko */ 1971d680b352SJiri Pirko parent = 0; 19727960d1daSJiri Pirko } else { 19737960d1daSJiri Pirko const struct Qdisc_class_ops *cops; 19747960d1daSJiri Pirko struct net_device *dev; 19757960d1daSJiri Pirko unsigned long cl = 0; 19767960d1daSJiri Pirko 1977cc7ec456SEric Dumazet dev = __dev_get_by_index(net, tcm->tcm_ifindex); 1978cc7ec456SEric Dumazet if (!dev) 19791da177e4SLinus Torvalds return skb->len; 19801da177e4SLinus Torvalds 1981a10fa201SJiri Pirko parent = tcm->tcm_parent; 1982a10fa201SJiri Pirko if (!parent) { 1983af356afaSPatrick McHardy q = dev->qdisc; 1984a10fa201SJiri Pirko parent = q->handle; 1985a10fa201SJiri Pirko } else { 19861da177e4SLinus Torvalds q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 1987a10fa201SJiri Pirko } 19881da177e4SLinus Torvalds if (!q) 19891da177e4SLinus Torvalds goto out; 1990cc7ec456SEric Dumazet cops = q->ops->cl_ops; 1991cc7ec456SEric Dumazet if (!cops) 1992143976ceSWANG Cong goto out; 19936529eabaSJiri Pirko if (!cops->tcf_block) 1994143976ceSWANG Cong goto out; 19951da177e4SLinus Torvalds if (TC_H_MIN(tcm->tcm_parent)) { 1996143976ceSWANG Cong cl = cops->find(q, tcm->tcm_parent); 19971da177e4SLinus Torvalds if (cl == 0) 1998143976ceSWANG Cong goto out; 19991da177e4SLinus Torvalds } 2000cbaacc4eSAlexander Aring block = cops->tcf_block(q, cl, NULL); 20016529eabaSJiri Pirko if (!block) 2002143976ceSWANG Cong goto out; 20037960d1daSJiri Pirko if (tcf_block_shared(block)) 20047960d1daSJiri Pirko q = NULL; 20057960d1daSJiri Pirko } 20061da177e4SLinus Torvalds 2007acb31faeSJiri Pirko index_start = cb->args[0]; 2008acb31faeSJiri Pirko index = 0; 20095bc17018SJiri Pirko 20105bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 20115bc17018SJiri Pirko if (tca[TCA_CHAIN] && 20125bc17018SJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index) 20135bc17018SJiri Pirko continue; 2014a10fa201SJiri Pirko if (!tcf_chain_dump(chain, q, parent, skb, cb, 20155ae437adSRoman Kapl index_start, &index)) { 20165ae437adSRoman Kapl err = -EMSGSIZE; 20175bc17018SJiri Pirko break; 20185bc17018SJiri Pirko } 20195ae437adSRoman Kapl } 20205bc17018SJiri Pirko 2021787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 2022787ce6d0SVlad Buslov tcf_block_refcnt_put(block); 2023acb31faeSJiri Pirko cb->args[0] = index; 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds out: 20265ae437adSRoman Kapl /* If we did no progress, the error (EMSGSIZE) is real */ 20275ae437adSRoman Kapl if (skb->len == 0 && err) 20285ae437adSRoman Kapl return err; 20291da177e4SLinus Torvalds return skb->len; 20301da177e4SLinus Torvalds } 20311da177e4SLinus Torvalds 203232a4f5ecSJiri Pirko static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net, 203332a4f5ecSJiri Pirko struct sk_buff *skb, struct tcf_block *block, 203432a4f5ecSJiri Pirko u32 portid, u32 seq, u16 flags, int event) 203532a4f5ecSJiri Pirko { 203632a4f5ecSJiri Pirko unsigned char *b = skb_tail_pointer(skb); 20379f407f17SJiri Pirko const struct tcf_proto_ops *ops; 203832a4f5ecSJiri Pirko struct nlmsghdr *nlh; 203932a4f5ecSJiri Pirko struct tcmsg *tcm; 20409f407f17SJiri Pirko void *priv; 20419f407f17SJiri Pirko 20429f407f17SJiri Pirko ops = chain->tmplt_ops; 20439f407f17SJiri Pirko priv = chain->tmplt_priv; 204432a4f5ecSJiri Pirko 204532a4f5ecSJiri Pirko nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 204632a4f5ecSJiri Pirko if (!nlh) 204732a4f5ecSJiri Pirko goto out_nlmsg_trim; 204832a4f5ecSJiri Pirko tcm = nlmsg_data(nlh); 204932a4f5ecSJiri Pirko tcm->tcm_family = AF_UNSPEC; 205032a4f5ecSJiri Pirko tcm->tcm__pad1 = 0; 205132a4f5ecSJiri Pirko tcm->tcm__pad2 = 0; 205232a4f5ecSJiri Pirko tcm->tcm_handle = 0; 205332a4f5ecSJiri Pirko if (block->q) { 205432a4f5ecSJiri Pirko tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; 205532a4f5ecSJiri Pirko tcm->tcm_parent = block->q->handle; 205632a4f5ecSJiri Pirko } else { 205732a4f5ecSJiri Pirko tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 205832a4f5ecSJiri Pirko tcm->tcm_block_index = block->index; 205932a4f5ecSJiri Pirko } 206032a4f5ecSJiri Pirko 206132a4f5ecSJiri Pirko if (nla_put_u32(skb, TCA_CHAIN, chain->index)) 206232a4f5ecSJiri Pirko goto nla_put_failure; 206332a4f5ecSJiri Pirko 20649f407f17SJiri Pirko if (ops) { 20659f407f17SJiri Pirko if (nla_put_string(skb, TCA_KIND, ops->kind)) 20669f407f17SJiri Pirko goto nla_put_failure; 20679f407f17SJiri Pirko if (ops->tmplt_dump(skb, net, priv) < 0) 20689f407f17SJiri Pirko goto nla_put_failure; 20699f407f17SJiri Pirko } 20709f407f17SJiri Pirko 207132a4f5ecSJiri Pirko nlh->nlmsg_len = skb_tail_pointer(skb) - b; 207232a4f5ecSJiri Pirko return skb->len; 207332a4f5ecSJiri Pirko 207432a4f5ecSJiri Pirko out_nlmsg_trim: 207532a4f5ecSJiri Pirko nla_put_failure: 207632a4f5ecSJiri Pirko nlmsg_trim(skb, b); 207732a4f5ecSJiri Pirko return -EMSGSIZE; 207832a4f5ecSJiri Pirko } 207932a4f5ecSJiri Pirko 208032a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 208132a4f5ecSJiri Pirko u32 seq, u16 flags, int event, bool unicast) 208232a4f5ecSJiri Pirko { 208332a4f5ecSJiri Pirko u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 208432a4f5ecSJiri Pirko struct tcf_block *block = chain->block; 208532a4f5ecSJiri Pirko struct net *net = block->net; 208632a4f5ecSJiri Pirko struct sk_buff *skb; 208732a4f5ecSJiri Pirko 208832a4f5ecSJiri Pirko skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 208932a4f5ecSJiri Pirko if (!skb) 209032a4f5ecSJiri Pirko return -ENOBUFS; 209132a4f5ecSJiri Pirko 209232a4f5ecSJiri Pirko if (tc_chain_fill_node(chain, net, skb, block, portid, 209332a4f5ecSJiri Pirko seq, flags, event) <= 0) { 209432a4f5ecSJiri Pirko kfree_skb(skb); 209532a4f5ecSJiri Pirko return -EINVAL; 209632a4f5ecSJiri Pirko } 209732a4f5ecSJiri Pirko 209832a4f5ecSJiri Pirko if (unicast) 209932a4f5ecSJiri Pirko return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 210032a4f5ecSJiri Pirko 210132a4f5ecSJiri Pirko return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); 210232a4f5ecSJiri Pirko } 210332a4f5ecSJiri Pirko 21049f407f17SJiri Pirko static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, 21059f407f17SJiri Pirko struct nlattr **tca, 21069f407f17SJiri Pirko struct netlink_ext_ack *extack) 21079f407f17SJiri Pirko { 21089f407f17SJiri Pirko const struct tcf_proto_ops *ops; 21099f407f17SJiri Pirko void *tmplt_priv; 21109f407f17SJiri Pirko 21119f407f17SJiri Pirko /* If kind is not set, user did not specify template. */ 21129f407f17SJiri Pirko if (!tca[TCA_KIND]) 21139f407f17SJiri Pirko return 0; 21149f407f17SJiri Pirko 21159f407f17SJiri Pirko ops = tcf_proto_lookup_ops(nla_data(tca[TCA_KIND]), extack); 21169f407f17SJiri Pirko if (IS_ERR(ops)) 21179f407f17SJiri Pirko return PTR_ERR(ops); 21189f407f17SJiri Pirko if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { 21199f407f17SJiri Pirko NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); 21209f407f17SJiri Pirko return -EOPNOTSUPP; 21219f407f17SJiri Pirko } 21229f407f17SJiri Pirko 21239f407f17SJiri Pirko tmplt_priv = ops->tmplt_create(net, chain, tca, extack); 21249f407f17SJiri Pirko if (IS_ERR(tmplt_priv)) { 21259f407f17SJiri Pirko module_put(ops->owner); 21269f407f17SJiri Pirko return PTR_ERR(tmplt_priv); 21279f407f17SJiri Pirko } 21289f407f17SJiri Pirko chain->tmplt_ops = ops; 21299f407f17SJiri Pirko chain->tmplt_priv = tmplt_priv; 21309f407f17SJiri Pirko return 0; 21319f407f17SJiri Pirko } 21329f407f17SJiri Pirko 21339f407f17SJiri Pirko static void tc_chain_tmplt_del(struct tcf_chain *chain) 21349f407f17SJiri Pirko { 21359f407f17SJiri Pirko const struct tcf_proto_ops *ops = chain->tmplt_ops; 21369f407f17SJiri Pirko 21379f407f17SJiri Pirko /* If template ops are set, no work to do for us. */ 21389f407f17SJiri Pirko if (!ops) 21399f407f17SJiri Pirko return; 21409f407f17SJiri Pirko 21419f407f17SJiri Pirko ops->tmplt_destroy(chain->tmplt_priv); 21429f407f17SJiri Pirko module_put(ops->owner); 21439f407f17SJiri Pirko } 21449f407f17SJiri Pirko 214532a4f5ecSJiri Pirko /* Add/delete/get a chain */ 214632a4f5ecSJiri Pirko 214732a4f5ecSJiri Pirko static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, 214832a4f5ecSJiri Pirko struct netlink_ext_ack *extack) 214932a4f5ecSJiri Pirko { 215032a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 215132a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 215232a4f5ecSJiri Pirko struct tcmsg *t; 215332a4f5ecSJiri Pirko u32 parent; 215432a4f5ecSJiri Pirko u32 chain_index; 215532a4f5ecSJiri Pirko struct Qdisc *q = NULL; 215632a4f5ecSJiri Pirko struct tcf_chain *chain = NULL; 215732a4f5ecSJiri Pirko struct tcf_block *block; 215832a4f5ecSJiri Pirko unsigned long cl; 215932a4f5ecSJiri Pirko int err; 216032a4f5ecSJiri Pirko 216132a4f5ecSJiri Pirko if (n->nlmsg_type != RTM_GETCHAIN && 216232a4f5ecSJiri Pirko !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 216332a4f5ecSJiri Pirko return -EPERM; 216432a4f5ecSJiri Pirko 216532a4f5ecSJiri Pirko replay: 2166e331473fSDavide Caratti err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack); 216732a4f5ecSJiri Pirko if (err < 0) 216832a4f5ecSJiri Pirko return err; 216932a4f5ecSJiri Pirko 217032a4f5ecSJiri Pirko t = nlmsg_data(n); 217132a4f5ecSJiri Pirko parent = t->tcm_parent; 217232a4f5ecSJiri Pirko cl = 0; 217332a4f5ecSJiri Pirko 217432a4f5ecSJiri Pirko block = tcf_block_find(net, &q, &parent, &cl, 217532a4f5ecSJiri Pirko t->tcm_ifindex, t->tcm_block_index, extack); 217632a4f5ecSJiri Pirko if (IS_ERR(block)) 217732a4f5ecSJiri Pirko return PTR_ERR(block); 217832a4f5ecSJiri Pirko 217932a4f5ecSJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 218032a4f5ecSJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 218132a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 2182e368fdb6SVlad Buslov err = -EINVAL; 2183e368fdb6SVlad Buslov goto errout_block; 218432a4f5ecSJiri Pirko } 218532a4f5ecSJiri Pirko chain = tcf_chain_lookup(block, chain_index); 218632a4f5ecSJiri Pirko if (n->nlmsg_type == RTM_NEWCHAIN) { 218732a4f5ecSJiri Pirko if (chain) { 21883d32f4c5SJiri Pirko if (tcf_chain_held_by_acts_only(chain)) { 21891f3ed383SJiri Pirko /* The chain exists only because there is 21903d32f4c5SJiri Pirko * some action referencing it. 21911f3ed383SJiri Pirko */ 21921f3ed383SJiri Pirko tcf_chain_hold(chain); 21931f3ed383SJiri Pirko } else { 219432a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Filter chain already exists"); 2195e368fdb6SVlad Buslov err = -EEXIST; 2196e368fdb6SVlad Buslov goto errout_block; 219732a4f5ecSJiri Pirko } 21981f3ed383SJiri Pirko } else { 219932a4f5ecSJiri Pirko if (!(n->nlmsg_flags & NLM_F_CREATE)) { 220032a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); 2201e368fdb6SVlad Buslov err = -ENOENT; 2202e368fdb6SVlad Buslov goto errout_block; 220332a4f5ecSJiri Pirko } 220432a4f5ecSJiri Pirko chain = tcf_chain_create(block, chain_index); 220532a4f5ecSJiri Pirko if (!chain) { 220632a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to create filter chain"); 2207e368fdb6SVlad Buslov err = -ENOMEM; 2208e368fdb6SVlad Buslov goto errout_block; 220932a4f5ecSJiri Pirko } 22101f3ed383SJiri Pirko } 221132a4f5ecSJiri Pirko } else { 22123d32f4c5SJiri Pirko if (!chain || tcf_chain_held_by_acts_only(chain)) { 221332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 2214e368fdb6SVlad Buslov err = -EINVAL; 2215e368fdb6SVlad Buslov goto errout_block; 221632a4f5ecSJiri Pirko } 221732a4f5ecSJiri Pirko tcf_chain_hold(chain); 221832a4f5ecSJiri Pirko } 221932a4f5ecSJiri Pirko 222032a4f5ecSJiri Pirko switch (n->nlmsg_type) { 222132a4f5ecSJiri Pirko case RTM_NEWCHAIN: 22229f407f17SJiri Pirko err = tc_chain_tmplt_add(chain, net, tca, extack); 22239f407f17SJiri Pirko if (err) 22249f407f17SJiri Pirko goto errout; 222532a4f5ecSJiri Pirko /* In case the chain was successfully added, take a reference 222632a4f5ecSJiri Pirko * to the chain. This ensures that an empty chain 222732a4f5ecSJiri Pirko * does not disappear at the end of this function. 222832a4f5ecSJiri Pirko */ 222932a4f5ecSJiri Pirko tcf_chain_hold(chain); 223032a4f5ecSJiri Pirko chain->explicitly_created = true; 223132a4f5ecSJiri Pirko tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 223232a4f5ecSJiri Pirko RTM_NEWCHAIN, false); 223332a4f5ecSJiri Pirko break; 223432a4f5ecSJiri Pirko case RTM_DELCHAIN: 2235f5b9bac7SCong Wang tfilter_notify_chain(net, skb, block, q, parent, n, 2236f5b9bac7SCong Wang chain, RTM_DELTFILTER); 223732a4f5ecSJiri Pirko /* Flush the chain first as the user requested chain removal. */ 223832a4f5ecSJiri Pirko tcf_chain_flush(chain); 223932a4f5ecSJiri Pirko /* In case the chain was successfully deleted, put a reference 224032a4f5ecSJiri Pirko * to the chain previously taken during addition. 224132a4f5ecSJiri Pirko */ 224232a4f5ecSJiri Pirko tcf_chain_put_explicitly_created(chain); 2243c921d7dbSJiri Pirko chain->explicitly_created = false; 224432a4f5ecSJiri Pirko break; 224532a4f5ecSJiri Pirko case RTM_GETCHAIN: 224632a4f5ecSJiri Pirko err = tc_chain_notify(chain, skb, n->nlmsg_seq, 224732a4f5ecSJiri Pirko n->nlmsg_seq, n->nlmsg_type, true); 224832a4f5ecSJiri Pirko if (err < 0) 224932a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); 225032a4f5ecSJiri Pirko break; 225132a4f5ecSJiri Pirko default: 225232a4f5ecSJiri Pirko err = -EOPNOTSUPP; 225332a4f5ecSJiri Pirko NL_SET_ERR_MSG(extack, "Unsupported message type"); 225432a4f5ecSJiri Pirko goto errout; 225532a4f5ecSJiri Pirko } 225632a4f5ecSJiri Pirko 225732a4f5ecSJiri Pirko errout: 225832a4f5ecSJiri Pirko tcf_chain_put(chain); 2259e368fdb6SVlad Buslov errout_block: 2260e368fdb6SVlad Buslov tcf_block_release(q, block); 226132a4f5ecSJiri Pirko if (err == -EAGAIN) 226232a4f5ecSJiri Pirko /* Replay the request. */ 226332a4f5ecSJiri Pirko goto replay; 226432a4f5ecSJiri Pirko return err; 226532a4f5ecSJiri Pirko } 226632a4f5ecSJiri Pirko 226732a4f5ecSJiri Pirko /* called with RTNL */ 226832a4f5ecSJiri Pirko static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) 226932a4f5ecSJiri Pirko { 227032a4f5ecSJiri Pirko struct net *net = sock_net(skb->sk); 227132a4f5ecSJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 227232a4f5ecSJiri Pirko struct Qdisc *q = NULL; 227332a4f5ecSJiri Pirko struct tcf_block *block; 227432a4f5ecSJiri Pirko struct tcf_chain *chain; 227532a4f5ecSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 227632a4f5ecSJiri Pirko long index_start; 227732a4f5ecSJiri Pirko long index; 227832a4f5ecSJiri Pirko u32 parent; 227932a4f5ecSJiri Pirko int err; 228032a4f5ecSJiri Pirko 228132a4f5ecSJiri Pirko if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 228232a4f5ecSJiri Pirko return skb->len; 228332a4f5ecSJiri Pirko 2284e331473fSDavide Caratti err = nlmsg_parse(cb->nlh, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, 2285dac9c979SDavid Ahern cb->extack); 228632a4f5ecSJiri Pirko if (err) 228732a4f5ecSJiri Pirko return err; 228832a4f5ecSJiri Pirko 228932a4f5ecSJiri Pirko if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 2290787ce6d0SVlad Buslov block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 229132a4f5ecSJiri Pirko if (!block) 229232a4f5ecSJiri Pirko goto out; 229332a4f5ecSJiri Pirko /* If we work with block index, q is NULL and parent value 229432a4f5ecSJiri Pirko * will never be used in the following code. The check 229532a4f5ecSJiri Pirko * in tcf_fill_node prevents it. However, compiler does not 229632a4f5ecSJiri Pirko * see that far, so set parent to zero to silence the warning 229732a4f5ecSJiri Pirko * about parent being uninitialized. 229832a4f5ecSJiri Pirko */ 229932a4f5ecSJiri Pirko parent = 0; 230032a4f5ecSJiri Pirko } else { 230132a4f5ecSJiri Pirko const struct Qdisc_class_ops *cops; 230232a4f5ecSJiri Pirko struct net_device *dev; 230332a4f5ecSJiri Pirko unsigned long cl = 0; 230432a4f5ecSJiri Pirko 230532a4f5ecSJiri Pirko dev = __dev_get_by_index(net, tcm->tcm_ifindex); 230632a4f5ecSJiri Pirko if (!dev) 230732a4f5ecSJiri Pirko return skb->len; 230832a4f5ecSJiri Pirko 230932a4f5ecSJiri Pirko parent = tcm->tcm_parent; 231032a4f5ecSJiri Pirko if (!parent) { 231132a4f5ecSJiri Pirko q = dev->qdisc; 231232a4f5ecSJiri Pirko parent = q->handle; 231332a4f5ecSJiri Pirko } else { 231432a4f5ecSJiri Pirko q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 231532a4f5ecSJiri Pirko } 231632a4f5ecSJiri Pirko if (!q) 231732a4f5ecSJiri Pirko goto out; 231832a4f5ecSJiri Pirko cops = q->ops->cl_ops; 231932a4f5ecSJiri Pirko if (!cops) 232032a4f5ecSJiri Pirko goto out; 232132a4f5ecSJiri Pirko if (!cops->tcf_block) 232232a4f5ecSJiri Pirko goto out; 232332a4f5ecSJiri Pirko if (TC_H_MIN(tcm->tcm_parent)) { 232432a4f5ecSJiri Pirko cl = cops->find(q, tcm->tcm_parent); 232532a4f5ecSJiri Pirko if (cl == 0) 232632a4f5ecSJiri Pirko goto out; 232732a4f5ecSJiri Pirko } 232832a4f5ecSJiri Pirko block = cops->tcf_block(q, cl, NULL); 232932a4f5ecSJiri Pirko if (!block) 233032a4f5ecSJiri Pirko goto out; 233132a4f5ecSJiri Pirko if (tcf_block_shared(block)) 233232a4f5ecSJiri Pirko q = NULL; 233332a4f5ecSJiri Pirko } 233432a4f5ecSJiri Pirko 233532a4f5ecSJiri Pirko index_start = cb->args[0]; 233632a4f5ecSJiri Pirko index = 0; 233732a4f5ecSJiri Pirko 233832a4f5ecSJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 233932a4f5ecSJiri Pirko if ((tca[TCA_CHAIN] && 234032a4f5ecSJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index)) 234132a4f5ecSJiri Pirko continue; 234232a4f5ecSJiri Pirko if (index < index_start) { 234332a4f5ecSJiri Pirko index++; 234432a4f5ecSJiri Pirko continue; 234532a4f5ecSJiri Pirko } 23463d32f4c5SJiri Pirko if (tcf_chain_held_by_acts_only(chain)) 23471f3ed383SJiri Pirko continue; 234832a4f5ecSJiri Pirko err = tc_chain_fill_node(chain, net, skb, block, 234932a4f5ecSJiri Pirko NETLINK_CB(cb->skb).portid, 235032a4f5ecSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 235132a4f5ecSJiri Pirko RTM_NEWCHAIN); 235232a4f5ecSJiri Pirko if (err <= 0) 235332a4f5ecSJiri Pirko break; 235432a4f5ecSJiri Pirko index++; 235532a4f5ecSJiri Pirko } 235632a4f5ecSJiri Pirko 2357787ce6d0SVlad Buslov if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 2358787ce6d0SVlad Buslov tcf_block_refcnt_put(block); 235932a4f5ecSJiri Pirko cb->args[0] = index; 236032a4f5ecSJiri Pirko 236132a4f5ecSJiri Pirko out: 236232a4f5ecSJiri Pirko /* If we did no progress, the error (EMSGSIZE) is real */ 236332a4f5ecSJiri Pirko if (skb->len == 0 && err) 236432a4f5ecSJiri Pirko return err; 236532a4f5ecSJiri Pirko return skb->len; 236632a4f5ecSJiri Pirko } 236732a4f5ecSJiri Pirko 236818d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts) 23691da177e4SLinus Torvalds { 23701da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 237190b73b77SVlad Buslov tcf_action_destroy(exts->actions, TCA_ACT_UNBIND); 237222dc13c8SWANG Cong kfree(exts->actions); 237322dc13c8SWANG Cong exts->nr_actions = 0; 23741da177e4SLinus Torvalds #endif 23751da177e4SLinus Torvalds } 2376aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy); 23771da177e4SLinus Torvalds 2378c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 237950a56190SAlexander Aring struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr, 238050a56190SAlexander Aring struct netlink_ext_ack *extack) 23811da177e4SLinus Torvalds { 23821da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 23831da177e4SLinus Torvalds { 23841da177e4SLinus Torvalds struct tc_action *act; 2385d04e6990SRoman Mashak size_t attr_size = 0; 23861da177e4SLinus Torvalds 23875da57f42SWANG Cong if (exts->police && tb[exts->police]) { 23889fb9f251SJiri Pirko act = tcf_action_init_1(net, tp, tb[exts->police], 23899fb9f251SJiri Pirko rate_tlv, "police", ovr, 2390789871bbSVlad Buslov TCA_ACT_BIND, true, extack); 2391ab27cfb8SPatrick McHardy if (IS_ERR(act)) 2392ab27cfb8SPatrick McHardy return PTR_ERR(act); 23931da177e4SLinus Torvalds 239433be6271SWANG Cong act->type = exts->type = TCA_OLD_COMPAT; 239522dc13c8SWANG Cong exts->actions[0] = act; 239622dc13c8SWANG Cong exts->nr_actions = 1; 23975da57f42SWANG Cong } else if (exts->action && tb[exts->action]) { 239890b73b77SVlad Buslov int err; 239922dc13c8SWANG Cong 24009fb9f251SJiri Pirko err = tcf_action_init(net, tp, tb[exts->action], 24019fb9f251SJiri Pirko rate_tlv, NULL, ovr, TCA_ACT_BIND, 240290b73b77SVlad Buslov exts->actions, &attr_size, true, 2403789871bbSVlad Buslov extack); 240490b73b77SVlad Buslov if (err < 0) 240533be6271SWANG Cong return err; 240690b73b77SVlad Buslov exts->nr_actions = err; 24071da177e4SLinus Torvalds } 2408e4b95c41SCong Wang exts->net = net; 24091da177e4SLinus Torvalds } 24101da177e4SLinus Torvalds #else 24115da57f42SWANG Cong if ((exts->action && tb[exts->action]) || 241250a56190SAlexander Aring (exts->police && tb[exts->police])) { 241350a56190SAlexander Aring NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)"); 24141da177e4SLinus Torvalds return -EOPNOTSUPP; 241550a56190SAlexander Aring } 24161da177e4SLinus Torvalds #endif 24171da177e4SLinus Torvalds 24181da177e4SLinus Torvalds return 0; 24191da177e4SLinus Torvalds } 2420aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate); 24211da177e4SLinus Torvalds 24229b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 24231da177e4SLinus Torvalds { 24241da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 242522dc13c8SWANG Cong struct tcf_exts old = *dst; 242622dc13c8SWANG Cong 24279b0d4446SJiri Pirko *dst = *src; 242822dc13c8SWANG Cong tcf_exts_destroy(&old); 24291da177e4SLinus Torvalds #endif 24301da177e4SLinus Torvalds } 2431aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change); 24321da177e4SLinus Torvalds 243322dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT 243422dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 243522dc13c8SWANG Cong { 243622dc13c8SWANG Cong if (exts->nr_actions == 0) 243722dc13c8SWANG Cong return NULL; 243822dc13c8SWANG Cong else 243922dc13c8SWANG Cong return exts->actions[0]; 244022dc13c8SWANG Cong } 244122dc13c8SWANG Cong #endif 244233be6271SWANG Cong 24435da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 24441da177e4SLinus Torvalds { 24451da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 24469cc63db5SCong Wang struct nlattr *nest; 24479cc63db5SCong Wang 2448978dfd8dSJiri Pirko if (exts->action && tcf_exts_has_actions(exts)) { 24491da177e4SLinus Torvalds /* 24501da177e4SLinus Torvalds * again for backward compatible mode - we want 24511da177e4SLinus Torvalds * to work with both old and new modes of entering 24521da177e4SLinus Torvalds * tc data even if iproute2 was newer - jhs 24531da177e4SLinus Torvalds */ 245433be6271SWANG Cong if (exts->type != TCA_OLD_COMPAT) { 24555da57f42SWANG Cong nest = nla_nest_start(skb, exts->action); 24564b3550efSPatrick McHardy if (nest == NULL) 24574b3550efSPatrick McHardy goto nla_put_failure; 245822dc13c8SWANG Cong 245990b73b77SVlad Buslov if (tcf_action_dump(skb, exts->actions, 0, 0) < 0) 2460add93b61SPatrick McHardy goto nla_put_failure; 24614b3550efSPatrick McHardy nla_nest_end(skb, nest); 24625da57f42SWANG Cong } else if (exts->police) { 246333be6271SWANG Cong struct tc_action *act = tcf_exts_first_act(exts); 24645da57f42SWANG Cong nest = nla_nest_start(skb, exts->police); 246563acd680SJamal Hadi Salim if (nest == NULL || !act) 24664b3550efSPatrick McHardy goto nla_put_failure; 246733be6271SWANG Cong if (tcf_action_dump_old(skb, act, 0, 0) < 0) 2468add93b61SPatrick McHardy goto nla_put_failure; 24694b3550efSPatrick McHardy nla_nest_end(skb, nest); 24701da177e4SLinus Torvalds } 24711da177e4SLinus Torvalds } 24721da177e4SLinus Torvalds return 0; 24739cc63db5SCong Wang 24749cc63db5SCong Wang nla_put_failure: 24759cc63db5SCong Wang nla_nest_cancel(skb, nest); 24761da177e4SLinus Torvalds return -1; 24779cc63db5SCong Wang #else 24789cc63db5SCong Wang return 0; 24799cc63db5SCong Wang #endif 24801da177e4SLinus Torvalds } 2481aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump); 24821da177e4SLinus Torvalds 2483aa767bfeSStephen Hemminger 24845da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 24851da177e4SLinus Torvalds { 24861da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 248733be6271SWANG Cong struct tc_action *a = tcf_exts_first_act(exts); 2488b057df24SIgnacy Gawędzki if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 248933be6271SWANG Cong return -1; 24901da177e4SLinus Torvalds #endif 24911da177e4SLinus Torvalds return 0; 24921da177e4SLinus Torvalds } 2493aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats); 24941da177e4SLinus Torvalds 2495aeb3fecdSCong Wang int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 2496aeb3fecdSCong Wang void *type_data, bool err_stop) 2497717503b9SJiri Pirko { 2498aeb3fecdSCong Wang struct tcf_block_cb *block_cb; 2499aeb3fecdSCong Wang int ok_count = 0; 2500aeb3fecdSCong Wang int err; 2501aeb3fecdSCong Wang 2502aeb3fecdSCong Wang /* Make sure all netdevs sharing this block are offload-capable. */ 2503aeb3fecdSCong Wang if (block->nooffloaddevcnt && err_stop) 2504aeb3fecdSCong Wang return -EOPNOTSUPP; 2505aeb3fecdSCong Wang 2506aeb3fecdSCong Wang list_for_each_entry(block_cb, &block->cb_list, list) { 2507aeb3fecdSCong Wang err = block_cb->cb(type, type_data, block_cb->cb_priv); 2508aeb3fecdSCong Wang if (err) { 2509aeb3fecdSCong Wang if (err_stop) 2510aeb3fecdSCong Wang return err; 2511aeb3fecdSCong Wang } else { 2512aeb3fecdSCong Wang ok_count++; 2513aeb3fecdSCong Wang } 2514aeb3fecdSCong Wang } 2515aeb3fecdSCong Wang return ok_count; 2516717503b9SJiri Pirko } 2517717503b9SJiri Pirko EXPORT_SYMBOL(tc_setup_cb_call); 2518b3f55bddSJiri Pirko 2519*e3ab786bSPablo Neira Ayuso unsigned int tcf_exts_num_actions(struct tcf_exts *exts) 2520*e3ab786bSPablo Neira Ayuso { 2521*e3ab786bSPablo Neira Ayuso unsigned int num_acts = 0; 2522*e3ab786bSPablo Neira Ayuso struct tc_action *act; 2523*e3ab786bSPablo Neira Ayuso int i; 2524*e3ab786bSPablo Neira Ayuso 2525*e3ab786bSPablo Neira Ayuso tcf_exts_for_each_action(i, act, exts) { 2526*e3ab786bSPablo Neira Ayuso if (is_tcf_pedit(act)) 2527*e3ab786bSPablo Neira Ayuso num_acts += tcf_pedit_nkeys(act); 2528*e3ab786bSPablo Neira Ayuso else 2529*e3ab786bSPablo Neira Ayuso num_acts++; 2530*e3ab786bSPablo Neira Ayuso } 2531*e3ab786bSPablo Neira Ayuso return num_acts; 2532*e3ab786bSPablo Neira Ayuso } 2533*e3ab786bSPablo Neira Ayuso EXPORT_SYMBOL(tcf_exts_num_actions); 2534*e3ab786bSPablo Neira Ayuso 253548617387SJiri Pirko static __net_init int tcf_net_init(struct net *net) 253648617387SJiri Pirko { 253748617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 253848617387SJiri Pirko 2539ab281629SVlad Buslov spin_lock_init(&tn->idr_lock); 254048617387SJiri Pirko idr_init(&tn->idr); 254148617387SJiri Pirko return 0; 254248617387SJiri Pirko } 254348617387SJiri Pirko 254448617387SJiri Pirko static void __net_exit tcf_net_exit(struct net *net) 254548617387SJiri Pirko { 254648617387SJiri Pirko struct tcf_net *tn = net_generic(net, tcf_net_id); 254748617387SJiri Pirko 254848617387SJiri Pirko idr_destroy(&tn->idr); 254948617387SJiri Pirko } 255048617387SJiri Pirko 255148617387SJiri Pirko static struct pernet_operations tcf_net_ops = { 255248617387SJiri Pirko .init = tcf_net_init, 255348617387SJiri Pirko .exit = tcf_net_exit, 255448617387SJiri Pirko .id = &tcf_net_id, 255548617387SJiri Pirko .size = sizeof(struct tcf_net), 255648617387SJiri Pirko }; 255748617387SJiri Pirko 25581da177e4SLinus Torvalds static int __init tc_filter_init(void) 25591da177e4SLinus Torvalds { 256048617387SJiri Pirko int err; 256148617387SJiri Pirko 25627aa0045dSCong Wang tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 25637aa0045dSCong Wang if (!tc_filter_wq) 25647aa0045dSCong Wang return -ENOMEM; 25657aa0045dSCong Wang 256648617387SJiri Pirko err = register_pernet_subsys(&tcf_net_ops); 256748617387SJiri Pirko if (err) 256848617387SJiri Pirko goto err_register_pernet_subsys; 256948617387SJiri Pirko 25707f76fa36SJohn Hurley err = rhashtable_init(&indr_setup_block_ht, 25717f76fa36SJohn Hurley &tc_indr_setup_block_ht_params); 25727f76fa36SJohn Hurley if (err) 25737f76fa36SJohn Hurley goto err_rhash_setup_block_ht; 25747f76fa36SJohn Hurley 2575c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 0); 2576c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0); 2577c431f89bSVlad Buslov rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 2578b97bac64SFlorian Westphal tc_dump_tfilter, 0); 257932a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0); 258032a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0); 258132a4f5ecSJiri Pirko rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain, 258232a4f5ecSJiri Pirko tc_dump_chain, 0); 25831da177e4SLinus Torvalds 25841da177e4SLinus Torvalds return 0; 258548617387SJiri Pirko 25867f76fa36SJohn Hurley err_rhash_setup_block_ht: 25877f76fa36SJohn Hurley unregister_pernet_subsys(&tcf_net_ops); 258848617387SJiri Pirko err_register_pernet_subsys: 258948617387SJiri Pirko destroy_workqueue(tc_filter_wq); 259048617387SJiri Pirko return err; 25911da177e4SLinus Torvalds } 25921da177e4SLinus Torvalds 25931da177e4SLinus Torvalds subsys_initcall(tc_filter_init); 2594