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> 26ab27cfb8SPatrick McHardy #include <linux/err.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 28b854272bSDenis V. Lunev #include <net/net_namespace.h> 29b854272bSDenis V. Lunev #include <net/sock.h> 30dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 311da177e4SLinus Torvalds #include <net/pkt_sched.h> 321da177e4SLinus Torvalds #include <net/pkt_cls.h> 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds /* The list of all installed classifier types */ 3536272874SWANG Cong static LIST_HEAD(tcf_proto_base); 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */ 381da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds /* Find classifier type by string name */ 411da177e4SLinus Torvalds 4233a48927SJiri Pirko static const struct tcf_proto_ops *tcf_proto_lookup_ops(const char *kind) 431da177e4SLinus Torvalds { 44dcd76081SEric Dumazet const struct tcf_proto_ops *t, *res = NULL; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds if (kind) { 471da177e4SLinus Torvalds read_lock(&cls_mod_lock); 4836272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) { 4933a48927SJiri Pirko if (strcmp(kind, t->kind) == 0) { 50dcd76081SEric Dumazet if (try_module_get(t->owner)) 51dcd76081SEric Dumazet res = t; 521da177e4SLinus Torvalds break; 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds read_unlock(&cls_mod_lock); 561da177e4SLinus Torvalds } 57dcd76081SEric Dumazet return res; 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds /* Register(unregister) new classifier type */ 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops) 631da177e4SLinus Torvalds { 6436272874SWANG Cong struct tcf_proto_ops *t; 651da177e4SLinus Torvalds int rc = -EEXIST; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds write_lock(&cls_mod_lock); 6836272874SWANG Cong list_for_each_entry(t, &tcf_proto_base, head) 691da177e4SLinus Torvalds if (!strcmp(ops->kind, t->kind)) 701da177e4SLinus Torvalds goto out; 711da177e4SLinus Torvalds 7236272874SWANG Cong list_add_tail(&ops->head, &tcf_proto_base); 731da177e4SLinus Torvalds rc = 0; 741da177e4SLinus Torvalds out: 751da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 761da177e4SLinus Torvalds return rc; 771da177e4SLinus Torvalds } 78aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops); 791da177e4SLinus Torvalds 807aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq; 817aa0045dSCong Wang 821da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 831da177e4SLinus Torvalds { 8436272874SWANG Cong struct tcf_proto_ops *t; 851da177e4SLinus Torvalds int rc = -ENOENT; 861da177e4SLinus Torvalds 87c78e1746SDaniel Borkmann /* Wait for outstanding call_rcu()s, if any, from a 88c78e1746SDaniel Borkmann * tcf_proto_ops's destroy() handler. 89c78e1746SDaniel Borkmann */ 90c78e1746SDaniel Borkmann rcu_barrier(); 917aa0045dSCong Wang flush_workqueue(tc_filter_wq); 92c78e1746SDaniel Borkmann 931da177e4SLinus Torvalds write_lock(&cls_mod_lock); 94dcd76081SEric Dumazet list_for_each_entry(t, &tcf_proto_base, head) { 95dcd76081SEric Dumazet if (t == ops) { 9636272874SWANG Cong list_del(&t->head); 971da177e4SLinus Torvalds rc = 0; 98dcd76081SEric Dumazet break; 99dcd76081SEric Dumazet } 100dcd76081SEric Dumazet } 1011da177e4SLinus Torvalds write_unlock(&cls_mod_lock); 1021da177e4SLinus Torvalds return rc; 1031da177e4SLinus Torvalds } 104aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops); 1051da177e4SLinus Torvalds 1067aa0045dSCong Wang bool tcf_queue_work(struct work_struct *work) 1077aa0045dSCong Wang { 1087aa0045dSCong Wang return queue_work(tc_filter_wq, work); 1097aa0045dSCong Wang } 1107aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work); 1117aa0045dSCong Wang 1121da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */ 1131da177e4SLinus Torvalds 114aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp) 1151da177e4SLinus Torvalds { 1161da177e4SLinus Torvalds u32 first = TC_H_MAKE(0xC0000000U, 0U); 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds if (tp) 1191da177e4SLinus Torvalds first = tp->prio - 1; 1201da177e4SLinus Torvalds 1217961973aSJiri Pirko return TC_H_MAJ(first); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 12433a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 1256529eabaSJiri Pirko u32 prio, u32 parent, struct Qdisc *q, 1265bc17018SJiri Pirko struct tcf_chain *chain) 12733a48927SJiri Pirko { 12833a48927SJiri Pirko struct tcf_proto *tp; 12933a48927SJiri Pirko int err; 13033a48927SJiri Pirko 13133a48927SJiri Pirko tp = kzalloc(sizeof(*tp), GFP_KERNEL); 13233a48927SJiri Pirko if (!tp) 13333a48927SJiri Pirko return ERR_PTR(-ENOBUFS); 13433a48927SJiri Pirko 13533a48927SJiri Pirko err = -ENOENT; 13633a48927SJiri Pirko tp->ops = tcf_proto_lookup_ops(kind); 13733a48927SJiri Pirko if (!tp->ops) { 13833a48927SJiri Pirko #ifdef CONFIG_MODULES 13933a48927SJiri Pirko rtnl_unlock(); 14033a48927SJiri Pirko request_module("cls_%s", kind); 14133a48927SJiri Pirko rtnl_lock(); 14233a48927SJiri Pirko tp->ops = tcf_proto_lookup_ops(kind); 14333a48927SJiri Pirko /* We dropped the RTNL semaphore in order to perform 14433a48927SJiri Pirko * the module load. So, even if we succeeded in loading 14533a48927SJiri Pirko * the module we have to replay the request. We indicate 14633a48927SJiri Pirko * this using -EAGAIN. 14733a48927SJiri Pirko */ 14833a48927SJiri Pirko if (tp->ops) { 14933a48927SJiri Pirko module_put(tp->ops->owner); 15033a48927SJiri Pirko err = -EAGAIN; 15133a48927SJiri Pirko } else { 15233a48927SJiri Pirko err = -ENOENT; 15333a48927SJiri Pirko } 15433a48927SJiri Pirko goto errout; 15533a48927SJiri Pirko #endif 15633a48927SJiri Pirko } 15733a48927SJiri Pirko tp->classify = tp->ops->classify; 15833a48927SJiri Pirko tp->protocol = protocol; 15933a48927SJiri Pirko tp->prio = prio; 16033a48927SJiri Pirko tp->classid = parent; 16133a48927SJiri Pirko tp->q = q; 1625bc17018SJiri Pirko tp->chain = chain; 16333a48927SJiri Pirko 16433a48927SJiri Pirko err = tp->ops->init(tp); 16533a48927SJiri Pirko if (err) { 16633a48927SJiri Pirko module_put(tp->ops->owner); 16733a48927SJiri Pirko goto errout; 16833a48927SJiri Pirko } 16933a48927SJiri Pirko return tp; 17033a48927SJiri Pirko 17133a48927SJiri Pirko errout: 17233a48927SJiri Pirko kfree(tp); 17333a48927SJiri Pirko return ERR_PTR(err); 17433a48927SJiri Pirko } 17533a48927SJiri Pirko 176763dbf63SWANG Cong static void tcf_proto_destroy(struct tcf_proto *tp) 177cf1facdaSJiri Pirko { 178763dbf63SWANG Cong tp->ops->destroy(tp); 179cf1facdaSJiri Pirko module_put(tp->ops->owner); 180cf1facdaSJiri Pirko kfree_rcu(tp, rcu); 181cf1facdaSJiri Pirko } 182cf1facdaSJiri Pirko 1835bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block, 1845bc17018SJiri Pirko u32 chain_index) 1852190d1d0SJiri Pirko { 1865bc17018SJiri Pirko struct tcf_chain *chain; 1875bc17018SJiri Pirko 1885bc17018SJiri Pirko chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1895bc17018SJiri Pirko if (!chain) 1905bc17018SJiri Pirko return NULL; 1915bc17018SJiri Pirko list_add_tail(&chain->list, &block->chain_list); 1925bc17018SJiri Pirko chain->block = block; 1935bc17018SJiri Pirko chain->index = chain_index; 194e2ef7544SCong Wang chain->refcnt = 1; 1955bc17018SJiri Pirko return chain; 1962190d1d0SJiri Pirko } 1972190d1d0SJiri Pirko 198*c7eb7d72SJiri Pirko static void tcf_chain_head_change(struct tcf_chain *chain, 199*c7eb7d72SJiri Pirko struct tcf_proto *tp_head) 200*c7eb7d72SJiri Pirko { 201*c7eb7d72SJiri Pirko if (chain->chain_head_change) 202*c7eb7d72SJiri Pirko chain->chain_head_change(tp_head, 203*c7eb7d72SJiri Pirko chain->chain_head_change_priv); 204*c7eb7d72SJiri Pirko } 205*c7eb7d72SJiri Pirko 206f93e1cdcSJiri Pirko static void tcf_chain_flush(struct tcf_chain *chain) 207cf1facdaSJiri Pirko { 208cf1facdaSJiri Pirko struct tcf_proto *tp; 209cf1facdaSJiri Pirko 210*c7eb7d72SJiri Pirko tcf_chain_head_change(chain, NULL); 2112190d1d0SJiri Pirko while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) { 2122190d1d0SJiri Pirko RCU_INIT_POINTER(chain->filter_chain, tp->next); 213e2ef7544SCong Wang tcf_chain_put(chain); 214763dbf63SWANG Cong tcf_proto_destroy(tp); 215cf1facdaSJiri Pirko } 216f93e1cdcSJiri Pirko } 217f93e1cdcSJiri Pirko 218f93e1cdcSJiri Pirko static void tcf_chain_destroy(struct tcf_chain *chain) 219f93e1cdcSJiri Pirko { 220e2ef7544SCong Wang list_del(&chain->list); 2212190d1d0SJiri Pirko kfree(chain); 2222190d1d0SJiri Pirko } 2232190d1d0SJiri Pirko 224e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain) 225e2ef7544SCong Wang { 226e2ef7544SCong Wang ++chain->refcnt; 227e2ef7544SCong Wang } 228e2ef7544SCong Wang 229367a8ce8SWANG Cong struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 230367a8ce8SWANG Cong bool create) 2315bc17018SJiri Pirko { 2325bc17018SJiri Pirko struct tcf_chain *chain; 2335bc17018SJiri Pirko 2345bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 235e2ef7544SCong Wang if (chain->index == chain_index) { 236e2ef7544SCong Wang tcf_chain_hold(chain); 2375bc17018SJiri Pirko return chain; 2385bc17018SJiri Pirko } 239e2ef7544SCong Wang } 240e2ef7544SCong Wang 241e2ef7544SCong Wang return create ? tcf_chain_create(block, chain_index) : NULL; 242e2ef7544SCong Wang } 2435bc17018SJiri Pirko EXPORT_SYMBOL(tcf_chain_get); 2445bc17018SJiri Pirko 2455bc17018SJiri Pirko void tcf_chain_put(struct tcf_chain *chain) 2465bc17018SJiri Pirko { 247e2ef7544SCong Wang if (--chain->refcnt == 0) 2485bc17018SJiri Pirko tcf_chain_destroy(chain); 2495bc17018SJiri Pirko } 2505bc17018SJiri Pirko EXPORT_SYMBOL(tcf_chain_put); 2515bc17018SJiri Pirko 2528c4083b3SJiri Pirko static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q, 2538c4083b3SJiri Pirko struct tcf_block_ext_info *ei, 2548c4083b3SJiri Pirko enum tc_block_command command) 2558c4083b3SJiri Pirko { 2568c4083b3SJiri Pirko struct net_device *dev = q->dev_queue->dev; 2578c4083b3SJiri Pirko struct tc_block_offload bo = {}; 2588c4083b3SJiri Pirko 25944ae12a7SJiri Pirko if (!dev->netdev_ops->ndo_setup_tc) 2608c4083b3SJiri Pirko return; 2618c4083b3SJiri Pirko bo.command = command; 2628c4083b3SJiri Pirko bo.binder_type = ei->binder_type; 2638c4083b3SJiri Pirko bo.block = block; 2648c4083b3SJiri Pirko dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 2658c4083b3SJiri Pirko } 2668c4083b3SJiri Pirko 2678c4083b3SJiri Pirko static void tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 2688c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 2698c4083b3SJiri Pirko { 2708c4083b3SJiri Pirko tcf_block_offload_cmd(block, q, ei, TC_BLOCK_BIND); 2718c4083b3SJiri Pirko } 2728c4083b3SJiri Pirko 2738c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 2748c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 2758c4083b3SJiri Pirko { 2768c4083b3SJiri Pirko tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND); 2778c4083b3SJiri Pirko } 2788c4083b3SJiri Pirko 279*c7eb7d72SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 2808c4083b3SJiri Pirko struct tcf_block_ext_info *ei) 2816529eabaSJiri Pirko { 2826529eabaSJiri Pirko struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); 2835bc17018SJiri Pirko struct tcf_chain *chain; 2842190d1d0SJiri Pirko int err; 2856529eabaSJiri Pirko 2866529eabaSJiri Pirko if (!block) 2876529eabaSJiri Pirko return -ENOMEM; 2885bc17018SJiri Pirko INIT_LIST_HEAD(&block->chain_list); 289acb67442SJiri Pirko INIT_LIST_HEAD(&block->cb_list); 290acb67442SJiri Pirko 2915bc17018SJiri Pirko /* Create chain 0 by default, it has to be always present. */ 2925bc17018SJiri Pirko chain = tcf_chain_create(block, 0); 2935bc17018SJiri Pirko if (!chain) { 2942190d1d0SJiri Pirko err = -ENOMEM; 2952190d1d0SJiri Pirko goto err_chain_create; 2962190d1d0SJiri Pirko } 297*c7eb7d72SJiri Pirko WARN_ON(!ei->chain_head_change); 298*c7eb7d72SJiri Pirko chain->chain_head_change = ei->chain_head_change; 299*c7eb7d72SJiri Pirko chain->chain_head_change_priv = ei->chain_head_change_priv; 300855319beSJiri Pirko block->net = qdisc_net(q); 30169d78ef2SJiri Pirko block->q = q; 3028c4083b3SJiri Pirko tcf_block_offload_bind(block, q, ei); 3036529eabaSJiri Pirko *p_block = block; 3046529eabaSJiri Pirko return 0; 3052190d1d0SJiri Pirko 3062190d1d0SJiri Pirko err_chain_create: 3072190d1d0SJiri Pirko kfree(block); 3082190d1d0SJiri Pirko return err; 3096529eabaSJiri Pirko } 3108c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext); 3118c4083b3SJiri Pirko 312*c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 313*c7eb7d72SJiri Pirko { 314*c7eb7d72SJiri Pirko struct tcf_proto __rcu **p_filter_chain = priv; 315*c7eb7d72SJiri Pirko 316*c7eb7d72SJiri Pirko rcu_assign_pointer(*p_filter_chain, tp_head); 317*c7eb7d72SJiri Pirko } 318*c7eb7d72SJiri Pirko 3198c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block, 3208c4083b3SJiri Pirko struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) 3218c4083b3SJiri Pirko { 322*c7eb7d72SJiri Pirko struct tcf_block_ext_info ei = { 323*c7eb7d72SJiri Pirko .chain_head_change = tcf_chain_head_change_dflt, 324*c7eb7d72SJiri Pirko .chain_head_change_priv = p_filter_chain, 325*c7eb7d72SJiri Pirko }; 3268c4083b3SJiri Pirko 327*c7eb7d72SJiri Pirko WARN_ON(!p_filter_chain); 328*c7eb7d72SJiri Pirko return tcf_block_get_ext(p_block, q, &ei); 3298c4083b3SJiri Pirko } 3306529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get); 3316529eabaSJiri Pirko 3327aa0045dSCong Wang static void tcf_block_put_final(struct work_struct *work) 3336529eabaSJiri Pirko { 3347aa0045dSCong Wang struct tcf_block *block = container_of(work, struct tcf_block, work); 3355bc17018SJiri Pirko struct tcf_chain *chain, *tmp; 3365bc17018SJiri Pirko 3377aa0045dSCong Wang rtnl_lock(); 338822e86d9SCong Wang /* Only chain 0 should be still here. */ 3397aa0045dSCong Wang list_for_each_entry_safe(chain, tmp, &block->chain_list, list) 3407aa0045dSCong Wang tcf_chain_put(chain); 3417aa0045dSCong Wang rtnl_unlock(); 3427aa0045dSCong Wang kfree(block); 3437aa0045dSCong Wang } 3445bc17018SJiri Pirko 3457aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound 346822e86d9SCong Wang * actions should be all removed after flushing. However, filters are now 347822e86d9SCong Wang * destroyed in tc filter workqueue with RTNL lock, they can not race here. 348e2ef7544SCong Wang */ 349*c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 350e1ea2f98SDavid S. Miller struct tcf_block_ext_info *ei) 3517aa0045dSCong Wang { 352822e86d9SCong Wang struct tcf_chain *chain, *tmp; 3536529eabaSJiri Pirko 354822e86d9SCong Wang list_for_each_entry_safe(chain, tmp, &block->chain_list, list) 3551697c4bbSCong Wang tcf_chain_flush(chain); 3561697c4bbSCong Wang 3574bb1b116SJiri Pirko tcf_block_offload_unbind(block, q, ei); 3584bb1b116SJiri Pirko 3591697c4bbSCong Wang INIT_WORK(&block->work, tcf_block_put_final); 3607aa0045dSCong Wang /* Wait for existing RCU callbacks to cool down, make sure their works 3617aa0045dSCong Wang * have been queued before this. We can not flush pending works here 3627aa0045dSCong Wang * because we are holding the RTNL lock. 3637aa0045dSCong Wang */ 3647aa0045dSCong Wang rcu_barrier(); 3657aa0045dSCong Wang tcf_queue_work(&block->work); 3666529eabaSJiri Pirko } 3678c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext); 3688c4083b3SJiri Pirko 3698c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block) 3708c4083b3SJiri Pirko { 3718c4083b3SJiri Pirko struct tcf_block_ext_info ei = {0, }; 3728c4083b3SJiri Pirko 3738f918d3fSCong Wang if (!block) 3748f918d3fSCong Wang return; 375*c7eb7d72SJiri Pirko tcf_block_put_ext(block, block->q, &ei); 3768c4083b3SJiri Pirko } 377e1ea2f98SDavid S. Miller 3786529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put); 379cf1facdaSJiri Pirko 380acb67442SJiri Pirko struct tcf_block_cb { 381acb67442SJiri Pirko struct list_head list; 382acb67442SJiri Pirko tc_setup_cb_t *cb; 383acb67442SJiri Pirko void *cb_ident; 384acb67442SJiri Pirko void *cb_priv; 385acb67442SJiri Pirko unsigned int refcnt; 386acb67442SJiri Pirko }; 387acb67442SJiri Pirko 388acb67442SJiri Pirko void *tcf_block_cb_priv(struct tcf_block_cb *block_cb) 389acb67442SJiri Pirko { 390acb67442SJiri Pirko return block_cb->cb_priv; 391acb67442SJiri Pirko } 392acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_priv); 393acb67442SJiri Pirko 394acb67442SJiri Pirko struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block, 395acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident) 396acb67442SJiri Pirko { struct tcf_block_cb *block_cb; 397acb67442SJiri Pirko 398acb67442SJiri Pirko list_for_each_entry(block_cb, &block->cb_list, list) 399acb67442SJiri Pirko if (block_cb->cb == cb && block_cb->cb_ident == cb_ident) 400acb67442SJiri Pirko return block_cb; 401acb67442SJiri Pirko return NULL; 402acb67442SJiri Pirko } 403acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_lookup); 404acb67442SJiri Pirko 405acb67442SJiri Pirko void tcf_block_cb_incref(struct tcf_block_cb *block_cb) 406acb67442SJiri Pirko { 407acb67442SJiri Pirko block_cb->refcnt++; 408acb67442SJiri Pirko } 409acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_incref); 410acb67442SJiri Pirko 411acb67442SJiri Pirko unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb) 412acb67442SJiri Pirko { 413acb67442SJiri Pirko return --block_cb->refcnt; 414acb67442SJiri Pirko } 415acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_decref); 416acb67442SJiri Pirko 417acb67442SJiri Pirko struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block, 418acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident, 419acb67442SJiri Pirko void *cb_priv) 420acb67442SJiri Pirko { 421acb67442SJiri Pirko struct tcf_block_cb *block_cb; 422acb67442SJiri Pirko 423acb67442SJiri Pirko block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL); 424acb67442SJiri Pirko if (!block_cb) 425acb67442SJiri Pirko return NULL; 426acb67442SJiri Pirko block_cb->cb = cb; 427acb67442SJiri Pirko block_cb->cb_ident = cb_ident; 428acb67442SJiri Pirko block_cb->cb_priv = cb_priv; 429acb67442SJiri Pirko list_add(&block_cb->list, &block->cb_list); 430acb67442SJiri Pirko return block_cb; 431acb67442SJiri Pirko } 432acb67442SJiri Pirko EXPORT_SYMBOL(__tcf_block_cb_register); 433acb67442SJiri Pirko 434acb67442SJiri Pirko int tcf_block_cb_register(struct tcf_block *block, 435acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident, 436acb67442SJiri Pirko void *cb_priv) 437acb67442SJiri Pirko { 438acb67442SJiri Pirko struct tcf_block_cb *block_cb; 439acb67442SJiri Pirko 440acb67442SJiri Pirko block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv); 441acb67442SJiri Pirko return block_cb ? 0 : -ENOMEM; 442acb67442SJiri Pirko } 443acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_register); 444acb67442SJiri Pirko 445acb67442SJiri Pirko void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb) 446acb67442SJiri Pirko { 447acb67442SJiri Pirko list_del(&block_cb->list); 448acb67442SJiri Pirko kfree(block_cb); 449acb67442SJiri Pirko } 450acb67442SJiri Pirko EXPORT_SYMBOL(__tcf_block_cb_unregister); 451acb67442SJiri Pirko 452acb67442SJiri Pirko void tcf_block_cb_unregister(struct tcf_block *block, 453acb67442SJiri Pirko tc_setup_cb_t *cb, void *cb_ident) 454acb67442SJiri Pirko { 455acb67442SJiri Pirko struct tcf_block_cb *block_cb; 456acb67442SJiri Pirko 457acb67442SJiri Pirko block_cb = tcf_block_cb_lookup(block, cb, cb_ident); 458acb67442SJiri Pirko if (!block_cb) 459acb67442SJiri Pirko return; 460acb67442SJiri Pirko __tcf_block_cb_unregister(block_cb); 461acb67442SJiri Pirko } 462acb67442SJiri Pirko EXPORT_SYMBOL(tcf_block_cb_unregister); 463acb67442SJiri Pirko 464acb67442SJiri Pirko static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type, 465acb67442SJiri Pirko void *type_data, bool err_stop) 466acb67442SJiri Pirko { 467acb67442SJiri Pirko struct tcf_block_cb *block_cb; 468acb67442SJiri Pirko int ok_count = 0; 469acb67442SJiri Pirko int err; 470acb67442SJiri Pirko 471acb67442SJiri Pirko list_for_each_entry(block_cb, &block->cb_list, list) { 472acb67442SJiri Pirko err = block_cb->cb(type, type_data, block_cb->cb_priv); 473acb67442SJiri Pirko if (err) { 474acb67442SJiri Pirko if (err_stop) 475acb67442SJiri Pirko return err; 476acb67442SJiri Pirko } else { 477acb67442SJiri Pirko ok_count++; 478acb67442SJiri Pirko } 479acb67442SJiri Pirko } 480acb67442SJiri Pirko return ok_count; 481acb67442SJiri Pirko } 482acb67442SJiri Pirko 48387d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached 48487d83093SJiri Pirko * to this qdisc, (optionally) tests for protocol and asks 48587d83093SJiri Pirko * specific classifiers. 48687d83093SJiri Pirko */ 48787d83093SJiri Pirko int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, 48887d83093SJiri Pirko struct tcf_result *res, bool compat_mode) 48987d83093SJiri Pirko { 49087d83093SJiri Pirko __be16 protocol = tc_skb_protocol(skb); 49187d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 49287d83093SJiri Pirko const int max_reclassify_loop = 4; 493ee538dceSJiri Pirko const struct tcf_proto *orig_tp = tp; 494ee538dceSJiri Pirko const struct tcf_proto *first_tp; 49587d83093SJiri Pirko int limit = 0; 49687d83093SJiri Pirko 49787d83093SJiri Pirko reclassify: 49887d83093SJiri Pirko #endif 49987d83093SJiri Pirko for (; tp; tp = rcu_dereference_bh(tp->next)) { 50087d83093SJiri Pirko int err; 50187d83093SJiri Pirko 50287d83093SJiri Pirko if (tp->protocol != protocol && 50387d83093SJiri Pirko tp->protocol != htons(ETH_P_ALL)) 50487d83093SJiri Pirko continue; 50587d83093SJiri Pirko 50687d83093SJiri Pirko err = tp->classify(skb, tp, res); 50787d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 508db50514fSJiri Pirko if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 509ee538dceSJiri Pirko first_tp = orig_tp; 51087d83093SJiri Pirko goto reset; 511db50514fSJiri Pirko } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 512ee538dceSJiri Pirko first_tp = res->goto_tp; 513db50514fSJiri Pirko goto reset; 514db50514fSJiri Pirko } 51587d83093SJiri Pirko #endif 51687d83093SJiri Pirko if (err >= 0) 51787d83093SJiri Pirko return err; 51887d83093SJiri Pirko } 51987d83093SJiri Pirko 52087d83093SJiri Pirko return TC_ACT_UNSPEC; /* signal: continue lookup */ 52187d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT 52287d83093SJiri Pirko reset: 52387d83093SJiri Pirko if (unlikely(limit++ >= max_reclassify_loop)) { 52487d83093SJiri Pirko net_notice_ratelimited("%s: reclassify loop, rule prio %u, protocol %02x\n", 52587d83093SJiri Pirko tp->q->ops->id, tp->prio & 0xffff, 52687d83093SJiri Pirko ntohs(tp->protocol)); 52787d83093SJiri Pirko return TC_ACT_SHOT; 52887d83093SJiri Pirko } 52987d83093SJiri Pirko 530ee538dceSJiri Pirko tp = first_tp; 53187d83093SJiri Pirko protocol = tc_skb_protocol(skb); 53287d83093SJiri Pirko goto reclassify; 53387d83093SJiri Pirko #endif 53487d83093SJiri Pirko } 53587d83093SJiri Pirko EXPORT_SYMBOL(tcf_classify); 53687d83093SJiri Pirko 5372190d1d0SJiri Pirko struct tcf_chain_info { 5382190d1d0SJiri Pirko struct tcf_proto __rcu **pprev; 5392190d1d0SJiri Pirko struct tcf_proto __rcu *next; 5402190d1d0SJiri Pirko }; 5412190d1d0SJiri Pirko 5422190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain_info *chain_info) 5432190d1d0SJiri Pirko { 5442190d1d0SJiri Pirko return rtnl_dereference(*chain_info->pprev); 5452190d1d0SJiri Pirko } 5462190d1d0SJiri Pirko 5472190d1d0SJiri Pirko static void tcf_chain_tp_insert(struct tcf_chain *chain, 5482190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 5492190d1d0SJiri Pirko struct tcf_proto *tp) 5502190d1d0SJiri Pirko { 551*c7eb7d72SJiri Pirko if (*chain_info->pprev == chain->filter_chain) 552*c7eb7d72SJiri Pirko tcf_chain_head_change(chain, tp); 5532190d1d0SJiri Pirko RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info)); 5542190d1d0SJiri Pirko rcu_assign_pointer(*chain_info->pprev, tp); 555e2ef7544SCong Wang tcf_chain_hold(chain); 5562190d1d0SJiri Pirko } 5572190d1d0SJiri Pirko 5582190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain, 5592190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 5602190d1d0SJiri Pirko struct tcf_proto *tp) 5612190d1d0SJiri Pirko { 5622190d1d0SJiri Pirko struct tcf_proto *next = rtnl_dereference(chain_info->next); 5632190d1d0SJiri Pirko 564*c7eb7d72SJiri Pirko if (tp == chain->filter_chain) 565*c7eb7d72SJiri Pirko tcf_chain_head_change(chain, next); 5662190d1d0SJiri Pirko RCU_INIT_POINTER(*chain_info->pprev, next); 567e2ef7544SCong Wang tcf_chain_put(chain); 5682190d1d0SJiri Pirko } 5692190d1d0SJiri Pirko 5702190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 5712190d1d0SJiri Pirko struct tcf_chain_info *chain_info, 5722190d1d0SJiri Pirko u32 protocol, u32 prio, 5732190d1d0SJiri Pirko bool prio_allocate) 5742190d1d0SJiri Pirko { 5752190d1d0SJiri Pirko struct tcf_proto **pprev; 5762190d1d0SJiri Pirko struct tcf_proto *tp; 5772190d1d0SJiri Pirko 5782190d1d0SJiri Pirko /* Check the chain for existence of proto-tcf with this priority */ 5792190d1d0SJiri Pirko for (pprev = &chain->filter_chain; 5802190d1d0SJiri Pirko (tp = rtnl_dereference(*pprev)); pprev = &tp->next) { 5812190d1d0SJiri Pirko if (tp->prio >= prio) { 5822190d1d0SJiri Pirko if (tp->prio == prio) { 5832190d1d0SJiri Pirko if (prio_allocate || 5842190d1d0SJiri Pirko (tp->protocol != protocol && protocol)) 5852190d1d0SJiri Pirko return ERR_PTR(-EINVAL); 5862190d1d0SJiri Pirko } else { 5872190d1d0SJiri Pirko tp = NULL; 5882190d1d0SJiri Pirko } 5892190d1d0SJiri Pirko break; 5902190d1d0SJiri Pirko } 5912190d1d0SJiri Pirko } 5922190d1d0SJiri Pirko chain_info->pprev = pprev; 5932190d1d0SJiri Pirko chain_info->next = tp ? tp->next : NULL; 5942190d1d0SJiri Pirko return tp; 5952190d1d0SJiri Pirko } 5962190d1d0SJiri Pirko 5977120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb, 598a10fa201SJiri Pirko struct tcf_proto *tp, struct Qdisc *q, u32 parent, 599a10fa201SJiri Pirko void *fh, u32 portid, u32 seq, u16 flags, int event) 6007120371cSWANG Cong { 6017120371cSWANG Cong struct tcmsg *tcm; 6027120371cSWANG Cong struct nlmsghdr *nlh; 6037120371cSWANG Cong unsigned char *b = skb_tail_pointer(skb); 6047120371cSWANG Cong 6057120371cSWANG Cong nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 6067120371cSWANG Cong if (!nlh) 6077120371cSWANG Cong goto out_nlmsg_trim; 6087120371cSWANG Cong tcm = nlmsg_data(nlh); 6097120371cSWANG Cong tcm->tcm_family = AF_UNSPEC; 6107120371cSWANG Cong tcm->tcm__pad1 = 0; 6117120371cSWANG Cong tcm->tcm__pad2 = 0; 612a10fa201SJiri Pirko tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 613a10fa201SJiri Pirko tcm->tcm_parent = parent; 6147120371cSWANG Cong tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 6157120371cSWANG Cong if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 6167120371cSWANG Cong goto nla_put_failure; 6177120371cSWANG Cong if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 6187120371cSWANG Cong goto nla_put_failure; 6197120371cSWANG Cong if (!fh) { 6207120371cSWANG Cong tcm->tcm_handle = 0; 6217120371cSWANG Cong } else { 6227120371cSWANG Cong if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) 6237120371cSWANG Cong goto nla_put_failure; 6247120371cSWANG Cong } 6257120371cSWANG Cong nlh->nlmsg_len = skb_tail_pointer(skb) - b; 6267120371cSWANG Cong return skb->len; 6277120371cSWANG Cong 6287120371cSWANG Cong out_nlmsg_trim: 6297120371cSWANG Cong nla_put_failure: 6307120371cSWANG Cong nlmsg_trim(skb, b); 6317120371cSWANG Cong return -1; 6327120371cSWANG Cong } 6337120371cSWANG Cong 6347120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb, 6357120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 636a10fa201SJiri Pirko struct Qdisc *q, u32 parent, 6377120371cSWANG Cong void *fh, int event, bool unicast) 6387120371cSWANG Cong { 6397120371cSWANG Cong struct sk_buff *skb; 6407120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 6417120371cSWANG Cong 6427120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 6437120371cSWANG Cong if (!skb) 6447120371cSWANG Cong return -ENOBUFS; 6457120371cSWANG Cong 646a10fa201SJiri Pirko if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq, 6477120371cSWANG Cong n->nlmsg_flags, event) <= 0) { 6487120371cSWANG Cong kfree_skb(skb); 6497120371cSWANG Cong return -EINVAL; 6507120371cSWANG Cong } 6517120371cSWANG Cong 6527120371cSWANG Cong if (unicast) 6537120371cSWANG Cong return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 6547120371cSWANG Cong 6557120371cSWANG Cong return rtnetlink_send(skb, net, portid, RTNLGRP_TC, 6567120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 6577120371cSWANG Cong } 6587120371cSWANG Cong 6597120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 6607120371cSWANG Cong struct nlmsghdr *n, struct tcf_proto *tp, 661a10fa201SJiri Pirko struct Qdisc *q, u32 parent, 6627120371cSWANG Cong void *fh, bool unicast, bool *last) 6637120371cSWANG Cong { 6647120371cSWANG Cong struct sk_buff *skb; 6657120371cSWANG Cong u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 6667120371cSWANG Cong int err; 6677120371cSWANG Cong 6687120371cSWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 6697120371cSWANG Cong if (!skb) 6707120371cSWANG Cong return -ENOBUFS; 6717120371cSWANG Cong 672a10fa201SJiri Pirko if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq, 6737120371cSWANG Cong n->nlmsg_flags, RTM_DELTFILTER) <= 0) { 6747120371cSWANG Cong kfree_skb(skb); 6757120371cSWANG Cong return -EINVAL; 6767120371cSWANG Cong } 6777120371cSWANG Cong 6787120371cSWANG Cong err = tp->ops->delete(tp, fh, last); 6797120371cSWANG Cong if (err) { 6807120371cSWANG Cong kfree_skb(skb); 6817120371cSWANG Cong return err; 6827120371cSWANG Cong } 6837120371cSWANG Cong 6847120371cSWANG Cong if (unicast) 6857120371cSWANG Cong return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); 6867120371cSWANG Cong 6877120371cSWANG Cong return rtnetlink_send(skb, net, portid, RTNLGRP_TC, 6887120371cSWANG Cong n->nlmsg_flags & NLM_F_ECHO); 6897120371cSWANG Cong } 6907120371cSWANG Cong 6917120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 692a10fa201SJiri Pirko struct Qdisc *q, u32 parent, 6937120371cSWANG Cong struct nlmsghdr *n, 6947120371cSWANG Cong struct tcf_chain *chain, int event) 6957120371cSWANG Cong { 6967120371cSWANG Cong struct tcf_proto *tp; 6977120371cSWANG Cong 6987120371cSWANG Cong for (tp = rtnl_dereference(chain->filter_chain); 6997120371cSWANG Cong tp; tp = rtnl_dereference(tp->next)) 700a10fa201SJiri Pirko tfilter_notify(net, oskb, n, tp, q, parent, 0, event, false); 7017120371cSWANG Cong } 7027120371cSWANG Cong 7031da177e4SLinus Torvalds /* Add/change/delete/get a filter node */ 7041da177e4SLinus Torvalds 705c21ef3e3SDavid Ahern static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 706c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 7071da177e4SLinus Torvalds { 7083b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 709add93b61SPatrick McHardy struct nlattr *tca[TCA_MAX + 1]; 7101da177e4SLinus Torvalds struct tcmsg *t; 7111da177e4SLinus Torvalds u32 protocol; 7121da177e4SLinus Torvalds u32 prio; 7139d36d9e5SJiri Pirko bool prio_allocate; 7141da177e4SLinus Torvalds u32 parent; 7155bc17018SJiri Pirko u32 chain_index; 7161da177e4SLinus Torvalds struct net_device *dev; 7171da177e4SLinus Torvalds struct Qdisc *q; 7182190d1d0SJiri Pirko struct tcf_chain_info chain_info; 7195bc17018SJiri Pirko struct tcf_chain *chain = NULL; 7206529eabaSJiri Pirko struct tcf_block *block; 7211da177e4SLinus Torvalds struct tcf_proto *tp; 72220fea08bSEric Dumazet const struct Qdisc_class_ops *cops; 7231da177e4SLinus Torvalds unsigned long cl; 7248113c095SWANG Cong void *fh; 7251da177e4SLinus Torvalds int err; 726628185cfSDaniel Borkmann int tp_created; 7271da177e4SLinus Torvalds 7284e8bbb81SStéphane Graber if ((n->nlmsg_type != RTM_GETTFILTER) && 7295f013c9bSDavid S. Miller !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 730dfc47ef8SEric W. Biederman return -EPERM; 731de179c8cSHong zhi guo 7321da177e4SLinus Torvalds replay: 733628185cfSDaniel Borkmann tp_created = 0; 734628185cfSDaniel Borkmann 735c21ef3e3SDavid Ahern err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, extack); 736de179c8cSHong zhi guo if (err < 0) 737de179c8cSHong zhi guo return err; 738de179c8cSHong zhi guo 739942b8165SDavid S. Miller t = nlmsg_data(n); 7401da177e4SLinus Torvalds protocol = TC_H_MIN(t->tcm_info); 7411da177e4SLinus Torvalds prio = TC_H_MAJ(t->tcm_info); 7429d36d9e5SJiri Pirko prio_allocate = false; 7431da177e4SLinus Torvalds parent = t->tcm_parent; 7441da177e4SLinus Torvalds cl = 0; 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds if (prio == 0) { 747ea7f8277SDaniel Borkmann switch (n->nlmsg_type) { 748ea7f8277SDaniel Borkmann case RTM_DELTFILTER: 7499f6ed032SDaniel Borkmann if (protocol || t->tcm_handle || tca[TCA_KIND]) 7501da177e4SLinus Torvalds return -ENOENT; 751ea7f8277SDaniel Borkmann break; 752ea7f8277SDaniel Borkmann case RTM_NEWTFILTER: 753ea7f8277SDaniel Borkmann /* If no priority is provided by the user, 754ea7f8277SDaniel Borkmann * we allocate one. 755ea7f8277SDaniel Borkmann */ 756ea7f8277SDaniel Borkmann if (n->nlmsg_flags & NLM_F_CREATE) { 7571da177e4SLinus Torvalds prio = TC_H_MAKE(0x80000000U, 0U); 7589d36d9e5SJiri Pirko prio_allocate = true; 759ea7f8277SDaniel Borkmann break; 760ea7f8277SDaniel Borkmann } 761ea7f8277SDaniel Borkmann /* fall-through */ 762ea7f8277SDaniel Borkmann default: 763ea7f8277SDaniel Borkmann return -ENOENT; 764ea7f8277SDaniel Borkmann } 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds /* Find head of filter chain. */ 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds /* Find link */ 7707316ae88STom Goff dev = __dev_get_by_index(net, t->tcm_ifindex); 771aa767bfeSStephen Hemminger if (dev == NULL) 7721da177e4SLinus Torvalds return -ENODEV; 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds /* Find qdisc */ 7751da177e4SLinus Torvalds if (!parent) { 776af356afaSPatrick McHardy q = dev->qdisc; 7771da177e4SLinus Torvalds parent = q->handle; 778aa767bfeSStephen Hemminger } else { 779aa767bfeSStephen Hemminger q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); 780aa767bfeSStephen Hemminger if (q == NULL) 7811da177e4SLinus Torvalds return -EINVAL; 782aa767bfeSStephen Hemminger } 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds /* Is it classful? */ 785cc7ec456SEric Dumazet cops = q->ops->cl_ops; 786cc7ec456SEric Dumazet if (!cops) 7871da177e4SLinus Torvalds return -EINVAL; 7881da177e4SLinus Torvalds 7896529eabaSJiri Pirko if (!cops->tcf_block) 79071ebe5e9SPatrick McHardy return -EOPNOTSUPP; 79171ebe5e9SPatrick McHardy 7921da177e4SLinus Torvalds /* Do we search for filter, attached to class? */ 7931da177e4SLinus Torvalds if (TC_H_MIN(parent)) { 794143976ceSWANG Cong cl = cops->find(q, parent); 7951da177e4SLinus Torvalds if (cl == 0) 7961da177e4SLinus Torvalds return -ENOENT; 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds /* And the last stroke */ 8006529eabaSJiri Pirko block = cops->tcf_block(q, cl); 8016529eabaSJiri Pirko if (!block) { 8021da177e4SLinus Torvalds err = -EINVAL; 8031da177e4SLinus Torvalds goto errout; 8046bb16e7aSJiri Pirko } 8055bc17018SJiri Pirko 8065bc17018SJiri Pirko chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 8075bc17018SJiri Pirko if (chain_index > TC_ACT_EXT_VAL_MASK) { 8085bc17018SJiri Pirko err = -EINVAL; 8095bc17018SJiri Pirko goto errout; 8105bc17018SJiri Pirko } 811367a8ce8SWANG Cong chain = tcf_chain_get(block, chain_index, 812367a8ce8SWANG Cong n->nlmsg_type == RTM_NEWTFILTER); 8135bc17018SJiri Pirko if (!chain) { 814367a8ce8SWANG Cong err = n->nlmsg_type == RTM_NEWTFILTER ? -ENOMEM : -EINVAL; 8155bc17018SJiri Pirko goto errout; 8165bc17018SJiri Pirko } 8176529eabaSJiri Pirko 818ea7f8277SDaniel Borkmann if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) { 819a10fa201SJiri Pirko tfilter_notify_chain(net, skb, q, parent, n, 820a10fa201SJiri Pirko chain, RTM_DELTFILTER); 821f93e1cdcSJiri Pirko tcf_chain_flush(chain); 822ea7f8277SDaniel Borkmann err = 0; 823ea7f8277SDaniel Borkmann goto errout; 824ea7f8277SDaniel Borkmann } 8251da177e4SLinus Torvalds 8262190d1d0SJiri Pirko tp = tcf_chain_tp_find(chain, &chain_info, protocol, 8272190d1d0SJiri Pirko prio, prio_allocate); 8282190d1d0SJiri Pirko if (IS_ERR(tp)) { 8292190d1d0SJiri Pirko err = PTR_ERR(tp); 8301da177e4SLinus Torvalds goto errout; 8316bb16e7aSJiri Pirko } 8321da177e4SLinus Torvalds 8331da177e4SLinus Torvalds if (tp == NULL) { 8341da177e4SLinus Torvalds /* Proto-tcf does not exist, create new one */ 8351da177e4SLinus Torvalds 8366bb16e7aSJiri Pirko if (tca[TCA_KIND] == NULL || !protocol) { 8376bb16e7aSJiri Pirko err = -EINVAL; 8381da177e4SLinus Torvalds goto errout; 8396bb16e7aSJiri Pirko } 8401da177e4SLinus Torvalds 841cc7ec456SEric Dumazet if (n->nlmsg_type != RTM_NEWTFILTER || 8426bb16e7aSJiri Pirko !(n->nlmsg_flags & NLM_F_CREATE)) { 8436bb16e7aSJiri Pirko err = -ENOENT; 8441da177e4SLinus Torvalds goto errout; 8456bb16e7aSJiri Pirko } 8461da177e4SLinus Torvalds 8479d36d9e5SJiri Pirko if (prio_allocate) 8482190d1d0SJiri Pirko prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info)); 8491da177e4SLinus Torvalds 85033a48927SJiri Pirko tp = tcf_proto_create(nla_data(tca[TCA_KIND]), 8515bc17018SJiri Pirko protocol, prio, parent, q, chain); 85233a48927SJiri Pirko if (IS_ERR(tp)) { 85333a48927SJiri Pirko err = PTR_ERR(tp); 8541da177e4SLinus Torvalds goto errout; 8551da177e4SLinus Torvalds } 85612186be7SMinoru Usui tp_created = 1; 8576bb16e7aSJiri Pirko } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 8586bb16e7aSJiri Pirko err = -EINVAL; 8591da177e4SLinus Torvalds goto errout; 8606bb16e7aSJiri Pirko } 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds fh = tp->ops->get(tp, t->tcm_handle); 8631da177e4SLinus Torvalds 8648113c095SWANG Cong if (!fh) { 8651da177e4SLinus Torvalds if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { 8662190d1d0SJiri Pirko tcf_chain_tp_remove(chain, &chain_info, tp); 867a10fa201SJiri Pirko tfilter_notify(net, skb, n, tp, q, parent, fh, 868fa59b27cSEric Dumazet RTM_DELTFILTER, false); 869763dbf63SWANG Cong tcf_proto_destroy(tp); 8701da177e4SLinus Torvalds err = 0; 8711da177e4SLinus Torvalds goto errout; 8721da177e4SLinus Torvalds } 8731da177e4SLinus Torvalds 874aa767bfeSStephen Hemminger if (n->nlmsg_type != RTM_NEWTFILTER || 8756bb16e7aSJiri Pirko !(n->nlmsg_flags & NLM_F_CREATE)) { 8766bb16e7aSJiri Pirko err = -ENOENT; 8771da177e4SLinus Torvalds goto errout; 8786bb16e7aSJiri Pirko } 8791da177e4SLinus Torvalds } else { 880763dbf63SWANG Cong bool last; 881763dbf63SWANG Cong 8821da177e4SLinus Torvalds switch (n->nlmsg_type) { 8831da177e4SLinus Torvalds case RTM_NEWTFILTER: 88412186be7SMinoru Usui if (n->nlmsg_flags & NLM_F_EXCL) { 88512186be7SMinoru Usui if (tp_created) 886763dbf63SWANG Cong tcf_proto_destroy(tp); 8876bb16e7aSJiri Pirko err = -EEXIST; 8881da177e4SLinus Torvalds goto errout; 88912186be7SMinoru Usui } 8901da177e4SLinus Torvalds break; 8911da177e4SLinus Torvalds case RTM_DELTFILTER: 892a10fa201SJiri Pirko err = tfilter_del_notify(net, skb, n, tp, q, parent, 893a10fa201SJiri Pirko fh, false, &last); 89440c81b25SJiri Pirko if (err) 89540c81b25SJiri Pirko goto errout; 896763dbf63SWANG Cong if (last) { 8972190d1d0SJiri Pirko tcf_chain_tp_remove(chain, &chain_info, tp); 898763dbf63SWANG Cong tcf_proto_destroy(tp); 899763dbf63SWANG Cong } 900d7cf52c2SJiri Pirko goto errout; 9011da177e4SLinus Torvalds case RTM_GETTFILTER: 902a10fa201SJiri Pirko err = tfilter_notify(net, skb, n, tp, q, parent, fh, 903fa59b27cSEric Dumazet RTM_NEWTFILTER, true); 9041da177e4SLinus Torvalds goto errout; 9051da177e4SLinus Torvalds default: 9061da177e4SLinus Torvalds err = -EINVAL; 9071da177e4SLinus Torvalds goto errout; 9081da177e4SLinus Torvalds } 9091da177e4SLinus Torvalds } 9101da177e4SLinus Torvalds 9112f7ef2f8SCong Wang err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 9122f7ef2f8SCong Wang n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE); 91312186be7SMinoru Usui if (err == 0) { 9142190d1d0SJiri Pirko if (tp_created) 9152190d1d0SJiri Pirko tcf_chain_tp_insert(chain, &chain_info, tp); 916a10fa201SJiri Pirko tfilter_notify(net, skb, n, tp, q, parent, fh, 917a10fa201SJiri Pirko RTM_NEWTFILTER, false); 91812186be7SMinoru Usui } else { 91912186be7SMinoru Usui if (tp_created) 920763dbf63SWANG Cong tcf_proto_destroy(tp); 92112186be7SMinoru Usui } 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds errout: 9245bc17018SJiri Pirko if (chain) 9255bc17018SJiri Pirko tcf_chain_put(chain); 9261da177e4SLinus Torvalds if (err == -EAGAIN) 9271da177e4SLinus Torvalds /* Replay the request. */ 9281da177e4SLinus Torvalds goto replay; 9291da177e4SLinus Torvalds return err; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 932aa767bfeSStephen Hemminger struct tcf_dump_args { 9331da177e4SLinus Torvalds struct tcf_walker w; 9341da177e4SLinus Torvalds struct sk_buff *skb; 9351da177e4SLinus Torvalds struct netlink_callback *cb; 936a10fa201SJiri Pirko struct Qdisc *q; 937a10fa201SJiri Pirko u32 parent; 9381da177e4SLinus Torvalds }; 9391da177e4SLinus Torvalds 9408113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 9411da177e4SLinus Torvalds { 9421da177e4SLinus Torvalds struct tcf_dump_args *a = (void *)arg; 943832d1d5bSWANG Cong struct net *net = sock_net(a->skb->sk); 9441da177e4SLinus Torvalds 945a10fa201SJiri Pirko return tcf_fill_node(net, a->skb, tp, a->q, a->parent, 946a10fa201SJiri Pirko n, NETLINK_CB(a->cb->skb).portid, 9475a7a5555SJamal Hadi Salim a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 9485a7a5555SJamal Hadi Salim RTM_NEWTFILTER); 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds 951a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 952a10fa201SJiri Pirko struct sk_buff *skb, struct netlink_callback *cb, 953acb31faeSJiri Pirko long index_start, long *p_index) 954acb31faeSJiri Pirko { 955acb31faeSJiri Pirko struct net *net = sock_net(skb->sk); 956acb31faeSJiri Pirko struct tcmsg *tcm = nlmsg_data(cb->nlh); 957acb31faeSJiri Pirko struct tcf_dump_args arg; 958acb31faeSJiri Pirko struct tcf_proto *tp; 959acb31faeSJiri Pirko 960acb31faeSJiri Pirko for (tp = rtnl_dereference(chain->filter_chain); 961acb31faeSJiri Pirko tp; tp = rtnl_dereference(tp->next), (*p_index)++) { 962acb31faeSJiri Pirko if (*p_index < index_start) 963acb31faeSJiri Pirko continue; 964acb31faeSJiri Pirko if (TC_H_MAJ(tcm->tcm_info) && 965acb31faeSJiri Pirko TC_H_MAJ(tcm->tcm_info) != tp->prio) 966acb31faeSJiri Pirko continue; 967acb31faeSJiri Pirko if (TC_H_MIN(tcm->tcm_info) && 968acb31faeSJiri Pirko TC_H_MIN(tcm->tcm_info) != tp->protocol) 969acb31faeSJiri Pirko continue; 970acb31faeSJiri Pirko if (*p_index > index_start) 971acb31faeSJiri Pirko memset(&cb->args[1], 0, 972acb31faeSJiri Pirko sizeof(cb->args) - sizeof(cb->args[0])); 973acb31faeSJiri Pirko if (cb->args[1] == 0) { 974a10fa201SJiri Pirko if (tcf_fill_node(net, skb, tp, q, parent, 0, 975acb31faeSJiri Pirko NETLINK_CB(cb->skb).portid, 976acb31faeSJiri Pirko cb->nlh->nlmsg_seq, NLM_F_MULTI, 977acb31faeSJiri Pirko RTM_NEWTFILTER) <= 0) 9785bc17018SJiri Pirko return false; 979acb31faeSJiri Pirko 980acb31faeSJiri Pirko cb->args[1] = 1; 981acb31faeSJiri Pirko } 982acb31faeSJiri Pirko if (!tp->ops->walk) 983acb31faeSJiri Pirko continue; 984acb31faeSJiri Pirko arg.w.fn = tcf_node_dump; 985acb31faeSJiri Pirko arg.skb = skb; 986acb31faeSJiri Pirko arg.cb = cb; 987a10fa201SJiri Pirko arg.q = q; 988a10fa201SJiri Pirko arg.parent = parent; 989acb31faeSJiri Pirko arg.w.stop = 0; 990acb31faeSJiri Pirko arg.w.skip = cb->args[1] - 1; 991acb31faeSJiri Pirko arg.w.count = 0; 992acb31faeSJiri Pirko tp->ops->walk(tp, &arg.w); 993acb31faeSJiri Pirko cb->args[1] = arg.w.count + 1; 994acb31faeSJiri Pirko if (arg.w.stop) 9955bc17018SJiri Pirko return false; 996acb31faeSJiri Pirko } 9975bc17018SJiri Pirko return true; 998acb31faeSJiri Pirko } 999acb31faeSJiri Pirko 1000bd27a875SEric Dumazet /* called with RTNL */ 10011da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 10021da177e4SLinus Torvalds { 10033b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 10045bc17018SJiri Pirko struct nlattr *tca[TCA_MAX + 1]; 10051da177e4SLinus Torvalds struct net_device *dev; 10061da177e4SLinus Torvalds struct Qdisc *q; 10076529eabaSJiri Pirko struct tcf_block *block; 10082190d1d0SJiri Pirko struct tcf_chain *chain; 1009942b8165SDavid S. Miller struct tcmsg *tcm = nlmsg_data(cb->nlh); 10101da177e4SLinus Torvalds unsigned long cl = 0; 101120fea08bSEric Dumazet const struct Qdisc_class_ops *cops; 1012acb31faeSJiri Pirko long index_start; 1013acb31faeSJiri Pirko long index; 1014a10fa201SJiri Pirko u32 parent; 10155bc17018SJiri Pirko int err; 10161da177e4SLinus Torvalds 1017573ce260SHong zhi guo if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 10181da177e4SLinus Torvalds return skb->len; 10195bc17018SJiri Pirko 10205bc17018SJiri Pirko err = nlmsg_parse(cb->nlh, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); 10215bc17018SJiri Pirko if (err) 10225bc17018SJiri Pirko return err; 10235bc17018SJiri Pirko 1024cc7ec456SEric Dumazet dev = __dev_get_by_index(net, tcm->tcm_ifindex); 1025cc7ec456SEric Dumazet if (!dev) 10261da177e4SLinus Torvalds return skb->len; 10271da177e4SLinus Torvalds 1028a10fa201SJiri Pirko parent = tcm->tcm_parent; 1029a10fa201SJiri Pirko if (!parent) { 1030af356afaSPatrick McHardy q = dev->qdisc; 1031a10fa201SJiri Pirko parent = q->handle; 1032a10fa201SJiri Pirko } else { 10331da177e4SLinus Torvalds q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 1034a10fa201SJiri Pirko } 10351da177e4SLinus Torvalds if (!q) 10361da177e4SLinus Torvalds goto out; 1037cc7ec456SEric Dumazet cops = q->ops->cl_ops; 1038cc7ec456SEric Dumazet if (!cops) 1039143976ceSWANG Cong goto out; 10406529eabaSJiri Pirko if (!cops->tcf_block) 1041143976ceSWANG Cong goto out; 10421da177e4SLinus Torvalds if (TC_H_MIN(tcm->tcm_parent)) { 1043143976ceSWANG Cong cl = cops->find(q, tcm->tcm_parent); 10441da177e4SLinus Torvalds if (cl == 0) 1045143976ceSWANG Cong goto out; 10461da177e4SLinus Torvalds } 10476529eabaSJiri Pirko block = cops->tcf_block(q, cl); 10486529eabaSJiri Pirko if (!block) 1049143976ceSWANG Cong goto out; 10501da177e4SLinus Torvalds 1051acb31faeSJiri Pirko index_start = cb->args[0]; 1052acb31faeSJiri Pirko index = 0; 10535bc17018SJiri Pirko 10545bc17018SJiri Pirko list_for_each_entry(chain, &block->chain_list, list) { 10555bc17018SJiri Pirko if (tca[TCA_CHAIN] && 10565bc17018SJiri Pirko nla_get_u32(tca[TCA_CHAIN]) != chain->index) 10575bc17018SJiri Pirko continue; 1058a10fa201SJiri Pirko if (!tcf_chain_dump(chain, q, parent, skb, cb, 1059a10fa201SJiri Pirko index_start, &index)) 10605bc17018SJiri Pirko break; 10615bc17018SJiri Pirko } 10625bc17018SJiri Pirko 1063acb31faeSJiri Pirko cb->args[0] = index; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds out: 10661da177e4SLinus Torvalds return skb->len; 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds 106918d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts) 10701da177e4SLinus Torvalds { 10711da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 107222dc13c8SWANG Cong LIST_HEAD(actions); 107322dc13c8SWANG Cong 10742d132ebaSCong Wang ASSERT_RTNL(); 107522dc13c8SWANG Cong tcf_exts_to_list(exts, &actions); 107622dc13c8SWANG Cong tcf_action_destroy(&actions, TCA_ACT_UNBIND); 107722dc13c8SWANG Cong kfree(exts->actions); 107822dc13c8SWANG Cong exts->nr_actions = 0; 10791da177e4SLinus Torvalds #endif 10801da177e4SLinus Torvalds } 1081aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy); 10821da177e4SLinus Torvalds 1083c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 10842f7ef2f8SCong Wang struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr) 10851da177e4SLinus Torvalds { 10861da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 10871da177e4SLinus Torvalds { 10881da177e4SLinus Torvalds struct tc_action *act; 10891da177e4SLinus Torvalds 10905da57f42SWANG Cong if (exts->police && tb[exts->police]) { 10919fb9f251SJiri Pirko act = tcf_action_init_1(net, tp, tb[exts->police], 10929fb9f251SJiri Pirko rate_tlv, "police", ovr, 10939fb9f251SJiri Pirko TCA_ACT_BIND); 1094ab27cfb8SPatrick McHardy if (IS_ERR(act)) 1095ab27cfb8SPatrick McHardy return PTR_ERR(act); 10961da177e4SLinus Torvalds 109733be6271SWANG Cong act->type = exts->type = TCA_OLD_COMPAT; 109822dc13c8SWANG Cong exts->actions[0] = act; 109922dc13c8SWANG Cong exts->nr_actions = 1; 11005da57f42SWANG Cong } else if (exts->action && tb[exts->action]) { 110122dc13c8SWANG Cong LIST_HEAD(actions); 110222dc13c8SWANG Cong int err, i = 0; 110322dc13c8SWANG Cong 11049fb9f251SJiri Pirko err = tcf_action_init(net, tp, tb[exts->action], 11059fb9f251SJiri Pirko rate_tlv, NULL, ovr, TCA_ACT_BIND, 11065a7a5555SJamal Hadi Salim &actions); 110733be6271SWANG Cong if (err) 110833be6271SWANG Cong return err; 110922dc13c8SWANG Cong list_for_each_entry(act, &actions, list) 111022dc13c8SWANG Cong exts->actions[i++] = act; 111122dc13c8SWANG Cong exts->nr_actions = i; 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds #else 11155da57f42SWANG Cong if ((exts->action && tb[exts->action]) || 11165da57f42SWANG Cong (exts->police && tb[exts->police])) 11171da177e4SLinus Torvalds return -EOPNOTSUPP; 11181da177e4SLinus Torvalds #endif 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds return 0; 11211da177e4SLinus Torvalds } 1122aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate); 11231da177e4SLinus Torvalds 11249b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 11251da177e4SLinus Torvalds { 11261da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 112722dc13c8SWANG Cong struct tcf_exts old = *dst; 112822dc13c8SWANG Cong 11299b0d4446SJiri Pirko *dst = *src; 113022dc13c8SWANG Cong tcf_exts_destroy(&old); 11311da177e4SLinus Torvalds #endif 11321da177e4SLinus Torvalds } 1133aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change); 11341da177e4SLinus Torvalds 113522dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT 113622dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 113722dc13c8SWANG Cong { 113822dc13c8SWANG Cong if (exts->nr_actions == 0) 113922dc13c8SWANG Cong return NULL; 114022dc13c8SWANG Cong else 114122dc13c8SWANG Cong return exts->actions[0]; 114222dc13c8SWANG Cong } 114322dc13c8SWANG Cong #endif 114433be6271SWANG Cong 11455da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 11461da177e4SLinus Torvalds { 11471da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 11489cc63db5SCong Wang struct nlattr *nest; 11499cc63db5SCong Wang 1150978dfd8dSJiri Pirko if (exts->action && tcf_exts_has_actions(exts)) { 11511da177e4SLinus Torvalds /* 11521da177e4SLinus Torvalds * again for backward compatible mode - we want 11531da177e4SLinus Torvalds * to work with both old and new modes of entering 11541da177e4SLinus Torvalds * tc data even if iproute2 was newer - jhs 11551da177e4SLinus Torvalds */ 115633be6271SWANG Cong if (exts->type != TCA_OLD_COMPAT) { 115722dc13c8SWANG Cong LIST_HEAD(actions); 115822dc13c8SWANG Cong 11595da57f42SWANG Cong nest = nla_nest_start(skb, exts->action); 11604b3550efSPatrick McHardy if (nest == NULL) 11614b3550efSPatrick McHardy goto nla_put_failure; 116222dc13c8SWANG Cong 116322dc13c8SWANG Cong tcf_exts_to_list(exts, &actions); 116422dc13c8SWANG Cong if (tcf_action_dump(skb, &actions, 0, 0) < 0) 1165add93b61SPatrick McHardy goto nla_put_failure; 11664b3550efSPatrick McHardy nla_nest_end(skb, nest); 11675da57f42SWANG Cong } else if (exts->police) { 116833be6271SWANG Cong struct tc_action *act = tcf_exts_first_act(exts); 11695da57f42SWANG Cong nest = nla_nest_start(skb, exts->police); 117063acd680SJamal Hadi Salim if (nest == NULL || !act) 11714b3550efSPatrick McHardy goto nla_put_failure; 117233be6271SWANG Cong if (tcf_action_dump_old(skb, act, 0, 0) < 0) 1173add93b61SPatrick McHardy goto nla_put_failure; 11744b3550efSPatrick McHardy nla_nest_end(skb, nest); 11751da177e4SLinus Torvalds } 11761da177e4SLinus Torvalds } 11771da177e4SLinus Torvalds return 0; 11789cc63db5SCong Wang 11799cc63db5SCong Wang nla_put_failure: 11809cc63db5SCong Wang nla_nest_cancel(skb, nest); 11811da177e4SLinus Torvalds return -1; 11829cc63db5SCong Wang #else 11839cc63db5SCong Wang return 0; 11849cc63db5SCong Wang #endif 11851da177e4SLinus Torvalds } 1186aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump); 11871da177e4SLinus Torvalds 1188aa767bfeSStephen Hemminger 11895da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 11901da177e4SLinus Torvalds { 11911da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT 119233be6271SWANG Cong struct tc_action *a = tcf_exts_first_act(exts); 1193b057df24SIgnacy Gawędzki if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 119433be6271SWANG Cong return -1; 11951da177e4SLinus Torvalds #endif 11961da177e4SLinus Torvalds return 0; 11971da177e4SLinus Torvalds } 1198aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats); 11991da177e4SLinus Torvalds 1200717503b9SJiri Pirko static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts, 1201717503b9SJiri Pirko enum tc_setup_type type, 1202b3f55bddSJiri Pirko void *type_data, bool err_stop) 1203b3f55bddSJiri Pirko { 1204b3f55bddSJiri Pirko int ok_count = 0; 1205b3f55bddSJiri Pirko #ifdef CONFIG_NET_CLS_ACT 1206b3f55bddSJiri Pirko const struct tc_action *a; 1207b3f55bddSJiri Pirko struct net_device *dev; 12089d452cebSOr Gerlitz int i, ret; 1209b3f55bddSJiri Pirko 1210b3f55bddSJiri Pirko if (!tcf_exts_has_actions(exts)) 1211b3f55bddSJiri Pirko return 0; 1212b3f55bddSJiri Pirko 12139d452cebSOr Gerlitz for (i = 0; i < exts->nr_actions; i++) { 12149d452cebSOr Gerlitz a = exts->actions[i]; 1215b3f55bddSJiri Pirko if (!a->ops->get_dev) 1216b3f55bddSJiri Pirko continue; 1217b3f55bddSJiri Pirko dev = a->ops->get_dev(a); 12187612fb03SJiri Pirko if (!dev) 1219b3f55bddSJiri Pirko continue; 1220b3f55bddSJiri Pirko ret = tc_setup_cb_egdev_call(dev, type, type_data, err_stop); 1221b3f55bddSJiri Pirko if (ret < 0) 1222b3f55bddSJiri Pirko return ret; 1223b3f55bddSJiri Pirko ok_count += ret; 1224b3f55bddSJiri Pirko } 1225b3f55bddSJiri Pirko #endif 1226b3f55bddSJiri Pirko return ok_count; 1227b3f55bddSJiri Pirko } 1228717503b9SJiri Pirko 1229208c0f4bSJiri Pirko int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts, 1230208c0f4bSJiri Pirko enum tc_setup_type type, void *type_data, bool err_stop) 1231717503b9SJiri Pirko { 1232208c0f4bSJiri Pirko int ok_count; 1233208c0f4bSJiri Pirko int ret; 1234208c0f4bSJiri Pirko 1235208c0f4bSJiri Pirko ret = tcf_block_cb_call(block, type, type_data, err_stop); 1236208c0f4bSJiri Pirko if (ret < 0) 1237208c0f4bSJiri Pirko return ret; 1238208c0f4bSJiri Pirko ok_count = ret; 1239208c0f4bSJiri Pirko 1240208c0f4bSJiri Pirko if (!exts) 1241208c0f4bSJiri Pirko return ok_count; 1242208c0f4bSJiri Pirko ret = tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop); 1243208c0f4bSJiri Pirko if (ret < 0) 1244208c0f4bSJiri Pirko return ret; 1245208c0f4bSJiri Pirko ok_count += ret; 1246208c0f4bSJiri Pirko 1247208c0f4bSJiri Pirko return ok_count; 1248717503b9SJiri Pirko } 1249717503b9SJiri Pirko EXPORT_SYMBOL(tc_setup_cb_call); 1250b3f55bddSJiri Pirko 12511da177e4SLinus Torvalds static int __init tc_filter_init(void) 12521da177e4SLinus Torvalds { 12537aa0045dSCong Wang tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 12547aa0045dSCong Wang if (!tc_filter_wq) 12557aa0045dSCong Wang return -ENOMEM; 12567aa0045dSCong Wang 1257b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, 0); 1258b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, 0); 125982623c0dSThomas Graf rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter, 1260b97bac64SFlorian Westphal tc_dump_tfilter, 0); 12611da177e4SLinus Torvalds 12621da177e4SLinus Torvalds return 0; 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds subsys_initcall(tc_filter_init); 1266