11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * net/sched/act_api.c Packet action 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 * Author: Jamal Hadi Salim 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/types.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/string.h> 171da177e4SLinus Torvalds #include <linux/errno.h> 185a0e3ad6STejun Heo #include <linux/slab.h> 191da177e4SLinus Torvalds #include <linux/skbuff.h> 201da177e4SLinus Torvalds #include <linux/init.h> 211da177e4SLinus Torvalds #include <linux/kmod.h> 22ab27cfb8SPatrick McHardy #include <linux/err.h> 233a9a231dSPaul Gortmaker #include <linux/module.h> 24b854272bSDenis V. Lunev #include <net/net_namespace.h> 25b854272bSDenis V. Lunev #include <net/sock.h> 261da177e4SLinus Torvalds #include <net/sch_generic.h> 271da177e4SLinus Torvalds #include <net/act_api.h> 28dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 291da177e4SLinus Torvalds 3086062033SWANG Cong void tcf_hash_destroy(struct tc_action *a) 31e9ce1cd3SDavid S. Miller { 3286062033SWANG Cong struct tcf_common *p = a->priv; 3386062033SWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 3486062033SWANG Cong 3589819dc0SWANG Cong spin_lock_bh(&hinfo->lock); 3689819dc0SWANG Cong hlist_del(&p->tcfc_head); 3789819dc0SWANG Cong spin_unlock_bh(&hinfo->lock); 38e9ce1cd3SDavid S. Miller gen_kill_estimator(&p->tcfc_bstats, 39e9ce1cd3SDavid S. Miller &p->tcfc_rate_est); 40c7de2cf0SEric Dumazet /* 41c7de2cf0SEric Dumazet * gen_estimator est_timer() might access p->tcfc_lock 42c7de2cf0SEric Dumazet * or bstats, wait a RCU grace period before freeing p 43c7de2cf0SEric Dumazet */ 44f5c8593cSLai Jiangshan kfree_rcu(p, tcfc_rcu); 45e9ce1cd3SDavid S. Miller } 46e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_destroy); 47e9ce1cd3SDavid S. Miller 4886062033SWANG Cong int tcf_hash_release(struct tc_action *a, int bind) 49e9ce1cd3SDavid S. Miller { 5086062033SWANG Cong struct tcf_common *p = a->priv; 51e9ce1cd3SDavid S. Miller int ret = 0; 52e9ce1cd3SDavid S. Miller 53e9ce1cd3SDavid S. Miller if (p) { 54e9ce1cd3SDavid S. Miller if (bind) 55e9ce1cd3SDavid S. Miller p->tcfc_bindcnt--; 56*55334a5dSWANG Cong else if (p->tcfc_bindcnt > 0) 57*55334a5dSWANG Cong return -EPERM; 58e9ce1cd3SDavid S. Miller 59e9ce1cd3SDavid S. Miller p->tcfc_refcnt--; 60e9ce1cd3SDavid S. Miller if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { 61a5b5c958SWANG Cong if (a->ops->cleanup) 62a5b5c958SWANG Cong a->ops->cleanup(a, bind); 6386062033SWANG Cong tcf_hash_destroy(a); 64e9ce1cd3SDavid S. Miller ret = 1; 65e9ce1cd3SDavid S. Miller } 66e9ce1cd3SDavid S. Miller } 67e9ce1cd3SDavid S. Miller return ret; 68e9ce1cd3SDavid S. Miller } 69e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_release); 70e9ce1cd3SDavid S. Miller 71e9ce1cd3SDavid S. Miller static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, 72c779f7afSWANG Cong struct tc_action *a) 73e9ce1cd3SDavid S. Miller { 74c779f7afSWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 7589819dc0SWANG Cong struct hlist_head *head; 76e9ce1cd3SDavid S. Miller struct tcf_common *p; 77e9ce1cd3SDavid S. Miller int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; 784b3550efSPatrick McHardy struct nlattr *nest; 79e9ce1cd3SDavid S. Miller 8089819dc0SWANG Cong spin_lock_bh(&hinfo->lock); 81e9ce1cd3SDavid S. Miller 82e9ce1cd3SDavid S. Miller s_i = cb->args[0]; 83e9ce1cd3SDavid S. Miller 84e9ce1cd3SDavid S. Miller for (i = 0; i < (hinfo->hmask + 1); i++) { 8589819dc0SWANG Cong head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; 86e9ce1cd3SDavid S. Miller 8789819dc0SWANG Cong hlist_for_each_entry_rcu(p, head, tcfc_head) { 88e9ce1cd3SDavid S. Miller index++; 89e9ce1cd3SDavid S. Miller if (index < s_i) 90e9ce1cd3SDavid S. Miller continue; 91e9ce1cd3SDavid S. Miller a->priv = p; 92e9ce1cd3SDavid S. Miller a->order = n_i; 934b3550efSPatrick McHardy 944b3550efSPatrick McHardy nest = nla_nest_start(skb, a->order); 954b3550efSPatrick McHardy if (nest == NULL) 964b3550efSPatrick McHardy goto nla_put_failure; 97e9ce1cd3SDavid S. Miller err = tcf_action_dump_1(skb, a, 0, 0); 98e9ce1cd3SDavid S. Miller if (err < 0) { 99e9ce1cd3SDavid S. Miller index--; 1004b3550efSPatrick McHardy nlmsg_trim(skb, nest); 101e9ce1cd3SDavid S. Miller goto done; 102e9ce1cd3SDavid S. Miller } 1034b3550efSPatrick McHardy nla_nest_end(skb, nest); 104e9ce1cd3SDavid S. Miller n_i++; 105e9ce1cd3SDavid S. Miller if (n_i >= TCA_ACT_MAX_PRIO) 106e9ce1cd3SDavid S. Miller goto done; 107e9ce1cd3SDavid S. Miller } 108e9ce1cd3SDavid S. Miller } 109e9ce1cd3SDavid S. Miller done: 11089819dc0SWANG Cong spin_unlock_bh(&hinfo->lock); 111e9ce1cd3SDavid S. Miller if (n_i) 112e9ce1cd3SDavid S. Miller cb->args[0] += n_i; 113e9ce1cd3SDavid S. Miller return n_i; 114e9ce1cd3SDavid S. Miller 1157ba699c6SPatrick McHardy nla_put_failure: 1164b3550efSPatrick McHardy nla_nest_cancel(skb, nest); 117e9ce1cd3SDavid S. Miller goto done; 118e9ce1cd3SDavid S. Miller } 119e9ce1cd3SDavid S. Miller 120c779f7afSWANG Cong static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) 121e9ce1cd3SDavid S. Miller { 122c779f7afSWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 12389819dc0SWANG Cong struct hlist_head *head; 12489819dc0SWANG Cong struct hlist_node *n; 12589819dc0SWANG Cong struct tcf_common *p; 1264b3550efSPatrick McHardy struct nlattr *nest; 127e9ce1cd3SDavid S. Miller int i = 0, n_i = 0; 128*55334a5dSWANG Cong int ret = -EINVAL; 129e9ce1cd3SDavid S. Miller 1304b3550efSPatrick McHardy nest = nla_nest_start(skb, a->order); 1314b3550efSPatrick McHardy if (nest == NULL) 1324b3550efSPatrick McHardy goto nla_put_failure; 1331b34ec43SDavid S. Miller if (nla_put_string(skb, TCA_KIND, a->ops->kind)) 1341b34ec43SDavid S. Miller goto nla_put_failure; 135e9ce1cd3SDavid S. Miller for (i = 0; i < (hinfo->hmask + 1); i++) { 13689819dc0SWANG Cong head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; 13789819dc0SWANG Cong hlist_for_each_entry_safe(p, n, head, tcfc_head) { 13886062033SWANG Cong a->priv = p; 139*55334a5dSWANG Cong ret = tcf_hash_release(a, 0); 140*55334a5dSWANG Cong if (ret == ACT_P_DELETED) { 141e9ce1cd3SDavid S. Miller module_put(a->ops->owner); 142e9ce1cd3SDavid S. Miller n_i++; 143*55334a5dSWANG Cong } else if (ret < 0) 144*55334a5dSWANG Cong goto nla_put_failure; 145e9ce1cd3SDavid S. Miller } 146805c1f4aSJamal Hadi Salim } 1471b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_FCNT, n_i)) 1481b34ec43SDavid S. Miller goto nla_put_failure; 1494b3550efSPatrick McHardy nla_nest_end(skb, nest); 150e9ce1cd3SDavid S. Miller 151e9ce1cd3SDavid S. Miller return n_i; 1527ba699c6SPatrick McHardy nla_put_failure: 1534b3550efSPatrick McHardy nla_nest_cancel(skb, nest); 154*55334a5dSWANG Cong return ret; 155e9ce1cd3SDavid S. Miller } 156e9ce1cd3SDavid S. Miller 1579c75f402Sstephen hemminger static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, 158e9ce1cd3SDavid S. Miller int type, struct tc_action *a) 159e9ce1cd3SDavid S. Miller { 160e9ce1cd3SDavid S. Miller if (type == RTM_DELACTION) { 161c779f7afSWANG Cong return tcf_del_walker(skb, a); 162e9ce1cd3SDavid S. Miller } else if (type == RTM_GETACTION) { 163c779f7afSWANG Cong return tcf_dump_walker(skb, cb, a); 164e9ce1cd3SDavid S. Miller } else { 1656ff9c364Sstephen hemminger WARN(1, "tcf_generic_walker: unknown action %d\n", type); 166e9ce1cd3SDavid S. Miller return -EINVAL; 167e9ce1cd3SDavid S. Miller } 168e9ce1cd3SDavid S. Miller } 169e9ce1cd3SDavid S. Miller 1706e6a50c2SWANG Cong static struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) 171e9ce1cd3SDavid S. Miller { 17289819dc0SWANG Cong struct tcf_common *p = NULL; 17389819dc0SWANG Cong struct hlist_head *head; 174e9ce1cd3SDavid S. Miller 17589819dc0SWANG Cong spin_lock_bh(&hinfo->lock); 17689819dc0SWANG Cong head = &hinfo->htab[tcf_hash(index, hinfo->hmask)]; 17789819dc0SWANG Cong hlist_for_each_entry_rcu(p, head, tcfc_head) 178e9ce1cd3SDavid S. Miller if (p->tcfc_index == index) 179e9ce1cd3SDavid S. Miller break; 18089819dc0SWANG Cong spin_unlock_bh(&hinfo->lock); 181e9ce1cd3SDavid S. Miller 182e9ce1cd3SDavid S. Miller return p; 183e9ce1cd3SDavid S. Miller } 184e9ce1cd3SDavid S. Miller 185ddafd34fSWANG Cong u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo) 186e9ce1cd3SDavid S. Miller { 187ddafd34fSWANG Cong u32 val = hinfo->index; 188e9ce1cd3SDavid S. Miller 189e9ce1cd3SDavid S. Miller do { 190e9ce1cd3SDavid S. Miller if (++val == 0) 191e9ce1cd3SDavid S. Miller val = 1; 192e9ce1cd3SDavid S. Miller } while (tcf_hash_lookup(val, hinfo)); 193e9ce1cd3SDavid S. Miller 194ddafd34fSWANG Cong hinfo->index = val; 19517569faeSYang Yingliang return val; 196e9ce1cd3SDavid S. Miller } 197e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_new_index); 198e9ce1cd3SDavid S. Miller 1996e6a50c2SWANG Cong int tcf_hash_search(struct tc_action *a, u32 index) 200e9ce1cd3SDavid S. Miller { 201e9ce1cd3SDavid S. Miller struct tcf_hashinfo *hinfo = a->ops->hinfo; 202e9ce1cd3SDavid S. Miller struct tcf_common *p = tcf_hash_lookup(index, hinfo); 203e9ce1cd3SDavid S. Miller 204e9ce1cd3SDavid S. Miller if (p) { 205e9ce1cd3SDavid S. Miller a->priv = p; 206e9ce1cd3SDavid S. Miller return 1; 207e9ce1cd3SDavid S. Miller } 208e9ce1cd3SDavid S. Miller return 0; 209e9ce1cd3SDavid S. Miller } 2106e6a50c2SWANG Cong EXPORT_SYMBOL(tcf_hash_search); 211e9ce1cd3SDavid S. Miller 21286062033SWANG Cong int tcf_hash_check(u32 index, struct tc_action *a, int bind) 213e9ce1cd3SDavid S. Miller { 214c779f7afSWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 215e9ce1cd3SDavid S. Miller struct tcf_common *p = NULL; 216e9ce1cd3SDavid S. Miller if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) { 21776aab2c1SJamal Hadi Salim if (bind) 218e9ce1cd3SDavid S. Miller p->tcfc_bindcnt++; 219e9ce1cd3SDavid S. Miller p->tcfc_refcnt++; 220e9ce1cd3SDavid S. Miller a->priv = p; 22186062033SWANG Cong return 1; 222e9ce1cd3SDavid S. Miller } 22386062033SWANG Cong return 0; 224e9ce1cd3SDavid S. Miller } 225e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_check); 226e9ce1cd3SDavid S. Miller 22786062033SWANG Cong void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est) 22886062033SWANG Cong { 22986062033SWANG Cong struct tcf_common *pc = a->priv; 23086062033SWANG Cong if (est) 23186062033SWANG Cong gen_kill_estimator(&pc->tcfc_bstats, 23286062033SWANG Cong &pc->tcfc_rate_est); 23386062033SWANG Cong kfree_rcu(pc, tcfc_rcu); 23486062033SWANG Cong } 23586062033SWANG Cong EXPORT_SYMBOL(tcf_hash_cleanup); 23686062033SWANG Cong 23786062033SWANG Cong int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, 23886062033SWANG Cong int size, int bind) 239e9ce1cd3SDavid S. Miller { 240c779f7afSWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 241e9ce1cd3SDavid S. Miller struct tcf_common *p = kzalloc(size, GFP_KERNEL); 242e9ce1cd3SDavid S. Miller 243e9ce1cd3SDavid S. Miller if (unlikely(!p)) 24486062033SWANG Cong return -ENOMEM; 245e9ce1cd3SDavid S. Miller p->tcfc_refcnt = 1; 246e9ce1cd3SDavid S. Miller if (bind) 247e9ce1cd3SDavid S. Miller p->tcfc_bindcnt = 1; 248e9ce1cd3SDavid S. Miller 249e9ce1cd3SDavid S. Miller spin_lock_init(&p->tcfc_lock); 25089819dc0SWANG Cong INIT_HLIST_NODE(&p->tcfc_head); 251ddafd34fSWANG Cong p->tcfc_index = index ? index : tcf_hash_new_index(hinfo); 252e9ce1cd3SDavid S. Miller p->tcfc_tm.install = jiffies; 253e9ce1cd3SDavid S. Miller p->tcfc_tm.lastuse = jiffies; 2540e991ec6SStephen Hemminger if (est) { 2550e991ec6SStephen Hemminger int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est, 2567ba699c6SPatrick McHardy &p->tcfc_lock, est); 2570e991ec6SStephen Hemminger if (err) { 2580e991ec6SStephen Hemminger kfree(p); 25986062033SWANG Cong return err; 2600e991ec6SStephen Hemminger } 2610e991ec6SStephen Hemminger } 2620e991ec6SStephen Hemminger 263e9ce1cd3SDavid S. Miller a->priv = (void *) p; 26486062033SWANG Cong return 0; 265e9ce1cd3SDavid S. Miller } 266e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_create); 267e9ce1cd3SDavid S. Miller 26886062033SWANG Cong void tcf_hash_insert(struct tc_action *a) 269e9ce1cd3SDavid S. Miller { 27086062033SWANG Cong struct tcf_common *p = a->priv; 27186062033SWANG Cong struct tcf_hashinfo *hinfo = a->ops->hinfo; 272e9ce1cd3SDavid S. Miller unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); 273e9ce1cd3SDavid S. Miller 27489819dc0SWANG Cong spin_lock_bh(&hinfo->lock); 27589819dc0SWANG Cong hlist_add_head(&p->tcfc_head, &hinfo->htab[h]); 27689819dc0SWANG Cong spin_unlock_bh(&hinfo->lock); 277e9ce1cd3SDavid S. Miller } 278e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_insert); 2791da177e4SLinus Torvalds 2801f747c26SWANG Cong static LIST_HEAD(act_base); 2811da177e4SLinus Torvalds static DEFINE_RWLOCK(act_mod_lock); 2821da177e4SLinus Torvalds 2834f1e9d89SWANG Cong int tcf_register_action(struct tc_action_ops *act, unsigned int mask) 2841da177e4SLinus Torvalds { 2851f747c26SWANG Cong struct tc_action_ops *a; 2864f1e9d89SWANG Cong int err; 2871da177e4SLinus Torvalds 288a5b5c958SWANG Cong /* Must supply act, dump and init */ 289a5b5c958SWANG Cong if (!act->act || !act->dump || !act->init) 29076c82d7aSJamal Hadi Salim return -EINVAL; 29176c82d7aSJamal Hadi Salim 292382ca8a1SJamal Hadi Salim /* Supply defaults */ 29363ef6174SJamal Hadi Salim if (!act->lookup) 29463ef6174SJamal Hadi Salim act->lookup = tcf_hash_search; 295382ca8a1SJamal Hadi Salim if (!act->walk) 296382ca8a1SJamal Hadi Salim act->walk = tcf_generic_walker; 29763ef6174SJamal Hadi Salim 2984f1e9d89SWANG Cong act->hinfo = kmalloc(sizeof(struct tcf_hashinfo), GFP_KERNEL); 2994f1e9d89SWANG Cong if (!act->hinfo) 3004f1e9d89SWANG Cong return -ENOMEM; 3014f1e9d89SWANG Cong err = tcf_hashinfo_init(act->hinfo, mask); 3024f1e9d89SWANG Cong if (err) { 3034f1e9d89SWANG Cong kfree(act->hinfo); 3044f1e9d89SWANG Cong return err; 3054f1e9d89SWANG Cong } 3064f1e9d89SWANG Cong 3071da177e4SLinus Torvalds write_lock(&act_mod_lock); 3081f747c26SWANG Cong list_for_each_entry(a, &act_base, head) { 3091da177e4SLinus Torvalds if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) { 3101da177e4SLinus Torvalds write_unlock(&act_mod_lock); 3114f1e9d89SWANG Cong tcf_hashinfo_destroy(act->hinfo); 3124f1e9d89SWANG Cong kfree(act->hinfo); 3131da177e4SLinus Torvalds return -EEXIST; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds } 3161f747c26SWANG Cong list_add_tail(&act->head, &act_base); 3171da177e4SLinus Torvalds write_unlock(&act_mod_lock); 3181da177e4SLinus Torvalds return 0; 3191da177e4SLinus Torvalds } 32062e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_register_action); 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds int tcf_unregister_action(struct tc_action_ops *act) 3231da177e4SLinus Torvalds { 3241f747c26SWANG Cong struct tc_action_ops *a; 3251da177e4SLinus Torvalds int err = -ENOENT; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds write_lock(&act_mod_lock); 328a792866aSEric Dumazet list_for_each_entry(a, &act_base, head) { 329a792866aSEric Dumazet if (a == act) { 3301f747c26SWANG Cong list_del(&act->head); 3314f1e9d89SWANG Cong tcf_hashinfo_destroy(act->hinfo); 3324f1e9d89SWANG Cong kfree(act->hinfo); 3331da177e4SLinus Torvalds err = 0; 334a792866aSEric Dumazet break; 335a792866aSEric Dumazet } 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds write_unlock(&act_mod_lock); 3381da177e4SLinus Torvalds return err; 3391da177e4SLinus Torvalds } 34062e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_unregister_action); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds /* lookup by name */ 3431da177e4SLinus Torvalds static struct tc_action_ops *tc_lookup_action_n(char *kind) 3441da177e4SLinus Torvalds { 345a792866aSEric Dumazet struct tc_action_ops *a, *res = NULL; 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds if (kind) { 3481da177e4SLinus Torvalds read_lock(&act_mod_lock); 3491f747c26SWANG Cong list_for_each_entry(a, &act_base, head) { 3501da177e4SLinus Torvalds if (strcmp(kind, a->kind) == 0) { 351a792866aSEric Dumazet if (try_module_get(a->owner)) 352a792866aSEric Dumazet res = a; 3531da177e4SLinus Torvalds break; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds read_unlock(&act_mod_lock); 3571da177e4SLinus Torvalds } 358a792866aSEric Dumazet return res; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3617ba699c6SPatrick McHardy /* lookup by nlattr */ 3627ba699c6SPatrick McHardy static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) 3631da177e4SLinus Torvalds { 364a792866aSEric Dumazet struct tc_action_ops *a, *res = NULL; 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds if (kind) { 3671da177e4SLinus Torvalds read_lock(&act_mod_lock); 3681f747c26SWANG Cong list_for_each_entry(a, &act_base, head) { 3697ba699c6SPatrick McHardy if (nla_strcmp(kind, a->kind) == 0) { 370a792866aSEric Dumazet if (try_module_get(a->owner)) 371a792866aSEric Dumazet res = a; 3721da177e4SLinus Torvalds break; 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds read_unlock(&act_mod_lock); 3761da177e4SLinus Torvalds } 377a792866aSEric Dumazet return res; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 38033be6271SWANG Cong int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, 3811da177e4SLinus Torvalds struct tcf_result *res) 3821da177e4SLinus Torvalds { 383dc7f9f6eSEric Dumazet const struct tc_action *a; 3841da177e4SLinus Torvalds int ret = -1; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds if (skb->tc_verd & TC_NCLS) { 3871da177e4SLinus Torvalds skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); 3881da177e4SLinus Torvalds ret = TC_ACT_OK; 3891da177e4SLinus Torvalds goto exec_done; 3901da177e4SLinus Torvalds } 39133be6271SWANG Cong list_for_each_entry(a, actions, list) { 3921da177e4SLinus Torvalds repeat: 393f43c5a0dSPatrick McHardy ret = a->ops->act(skb, a, res); 3941da177e4SLinus Torvalds if (TC_MUNGED & skb->tc_verd) { 3951da177e4SLinus Torvalds /* copied already, allow trampling */ 3961da177e4SLinus Torvalds skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd); 3971da177e4SLinus Torvalds skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd); 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds if (ret == TC_ACT_REPEAT) 4001da177e4SLinus Torvalds goto repeat; /* we need a ttl - JHS */ 40114d50e78SJ Hadi Salim if (ret != TC_ACT_PIPE) 40214d50e78SJ Hadi Salim goto exec_done; 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds exec_done: 4051da177e4SLinus Torvalds return ret; 4061da177e4SLinus Torvalds } 40762e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_exec); 4081da177e4SLinus Torvalds 409*55334a5dSWANG Cong int tcf_action_destroy(struct list_head *actions, int bind) 4101da177e4SLinus Torvalds { 41133be6271SWANG Cong struct tc_action *a, *tmp; 412*55334a5dSWANG Cong int ret = 0; 4131da177e4SLinus Torvalds 41433be6271SWANG Cong list_for_each_entry_safe(a, tmp, actions, list) { 415*55334a5dSWANG Cong ret = tcf_hash_release(a, bind); 416*55334a5dSWANG Cong if (ret == ACT_P_DELETED) 4171da177e4SLinus Torvalds module_put(a->ops->owner); 418*55334a5dSWANG Cong else if (ret < 0) 419*55334a5dSWANG Cong return ret; 42033be6271SWANG Cong list_del(&a->list); 4211da177e4SLinus Torvalds kfree(a); 4221da177e4SLinus Torvalds } 423*55334a5dSWANG Cong return ret; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds int 4271da177e4SLinus Torvalds tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 4281da177e4SLinus Torvalds { 4291da177e4SLinus Torvalds return a->ops->dump(skb, a, bind, ref); 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds int 4331da177e4SLinus Torvalds tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 4341da177e4SLinus Torvalds { 4351da177e4SLinus Torvalds int err = -EINVAL; 43627a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 4374b3550efSPatrick McHardy struct nlattr *nest; 4381da177e4SLinus Torvalds 4391b34ec43SDavid S. Miller if (nla_put_string(skb, TCA_KIND, a->ops->kind)) 4401b34ec43SDavid S. Miller goto nla_put_failure; 4411da177e4SLinus Torvalds if (tcf_action_copy_stats(skb, a, 0)) 4427ba699c6SPatrick McHardy goto nla_put_failure; 4434b3550efSPatrick McHardy nest = nla_nest_start(skb, TCA_OPTIONS); 4444b3550efSPatrick McHardy if (nest == NULL) 4454b3550efSPatrick McHardy goto nla_put_failure; 446cc7ec456SEric Dumazet err = tcf_action_dump_old(skb, a, bind, ref); 447cc7ec456SEric Dumazet if (err > 0) { 4484b3550efSPatrick McHardy nla_nest_end(skb, nest); 4491da177e4SLinus Torvalds return err; 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds 4527ba699c6SPatrick McHardy nla_put_failure: 453dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 4541da177e4SLinus Torvalds return -1; 4551da177e4SLinus Torvalds } 45662e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_dump_1); 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds int 45933be6271SWANG Cong tcf_action_dump(struct sk_buff *skb, struct list_head *actions, int bind, int ref) 4601da177e4SLinus Torvalds { 4611da177e4SLinus Torvalds struct tc_action *a; 4621da177e4SLinus Torvalds int err = -EINVAL; 4634b3550efSPatrick McHardy struct nlattr *nest; 4641da177e4SLinus Torvalds 46533be6271SWANG Cong list_for_each_entry(a, actions, list) { 4664b3550efSPatrick McHardy nest = nla_nest_start(skb, a->order); 4674b3550efSPatrick McHardy if (nest == NULL) 4684b3550efSPatrick McHardy goto nla_put_failure; 4691da177e4SLinus Torvalds err = tcf_action_dump_1(skb, a, bind, ref); 4701da177e4SLinus Torvalds if (err < 0) 4714fe683f5SThomas Graf goto errout; 4724b3550efSPatrick McHardy nla_nest_end(skb, nest); 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds return 0; 4761da177e4SLinus Torvalds 4777ba699c6SPatrick McHardy nla_put_failure: 4784fe683f5SThomas Graf err = -EINVAL; 4794fe683f5SThomas Graf errout: 4804b3550efSPatrick McHardy nla_nest_cancel(skb, nest); 4814fe683f5SThomas Graf return err; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 484c1b52739SBenjamin LaHaise struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, 485c1b52739SBenjamin LaHaise struct nlattr *est, char *name, int ovr, 486c1b52739SBenjamin LaHaise int bind) 4871da177e4SLinus Torvalds { 4881da177e4SLinus Torvalds struct tc_action *a; 4891da177e4SLinus Torvalds struct tc_action_ops *a_o; 4901da177e4SLinus Torvalds char act_name[IFNAMSIZ]; 4917ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1]; 4927ba699c6SPatrick McHardy struct nlattr *kind; 493ab27cfb8SPatrick McHardy int err; 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds if (name == NULL) { 496cee63723SPatrick McHardy err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); 497cee63723SPatrick McHardy if (err < 0) 4981da177e4SLinus Torvalds goto err_out; 499cee63723SPatrick McHardy err = -EINVAL; 5007ba699c6SPatrick McHardy kind = tb[TCA_ACT_KIND]; 5011da177e4SLinus Torvalds if (kind == NULL) 5021da177e4SLinus Torvalds goto err_out; 5037ba699c6SPatrick McHardy if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ) 5041da177e4SLinus Torvalds goto err_out; 5051da177e4SLinus Torvalds } else { 506cee63723SPatrick McHardy err = -EINVAL; 5071da177e4SLinus Torvalds if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) 5081da177e4SLinus Torvalds goto err_out; 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds a_o = tc_lookup_action_n(act_name); 5121da177e4SLinus Torvalds if (a_o == NULL) { 51395a5afcaSJohannes Berg #ifdef CONFIG_MODULES 5141da177e4SLinus Torvalds rtnl_unlock(); 5154bba3925SPatrick McHardy request_module("act_%s", act_name); 5161da177e4SLinus Torvalds rtnl_lock(); 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds a_o = tc_lookup_action_n(act_name); 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds /* We dropped the RTNL semaphore in order to 5211da177e4SLinus Torvalds * perform the module load. So, even if we 5221da177e4SLinus Torvalds * succeeded in loading the module we have to 5231da177e4SLinus Torvalds * tell the caller to replay the request. We 5241da177e4SLinus Torvalds * indicate this using -EAGAIN. 5251da177e4SLinus Torvalds */ 5261da177e4SLinus Torvalds if (a_o != NULL) { 527ab27cfb8SPatrick McHardy err = -EAGAIN; 5281da177e4SLinus Torvalds goto err_mod; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds #endif 531ab27cfb8SPatrick McHardy err = -ENOENT; 5321da177e4SLinus Torvalds goto err_out; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds 535ab27cfb8SPatrick McHardy err = -ENOMEM; 5360da974f4SPanagiotis Issaris a = kzalloc(sizeof(*a), GFP_KERNEL); 5371da177e4SLinus Torvalds if (a == NULL) 5381da177e4SLinus Torvalds goto err_mod; 5391da177e4SLinus Torvalds 540c779f7afSWANG Cong a->ops = a_o; 54133be6271SWANG Cong INIT_LIST_HEAD(&a->list); 5421da177e4SLinus Torvalds /* backward compatibility for policer */ 5431da177e4SLinus Torvalds if (name == NULL) 544c1b52739SBenjamin LaHaise err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind); 5451da177e4SLinus Torvalds else 546c1b52739SBenjamin LaHaise err = a_o->init(net, nla, est, a, ovr, bind); 547ab27cfb8SPatrick McHardy if (err < 0) 5481da177e4SLinus Torvalds goto err_free; 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds /* module count goes up only when brand new policy is created 551cc7ec456SEric Dumazet * if it exists and is only bound to in a_o->init() then 552cc7ec456SEric Dumazet * ACT_P_CREATED is not returned (a zero is). 5531da177e4SLinus Torvalds */ 554ab27cfb8SPatrick McHardy if (err != ACT_P_CREATED) 5551da177e4SLinus Torvalds module_put(a_o->owner); 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds return a; 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds err_free: 5601da177e4SLinus Torvalds kfree(a); 5611da177e4SLinus Torvalds err_mod: 5621da177e4SLinus Torvalds module_put(a_o->owner); 5631da177e4SLinus Torvalds err_out: 564ab27cfb8SPatrick McHardy return ERR_PTR(err); 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 56733be6271SWANG Cong int tcf_action_init(struct net *net, struct nlattr *nla, 568c1b52739SBenjamin LaHaise struct nlattr *est, char *name, int ovr, 56933be6271SWANG Cong int bind, struct list_head *actions) 5701da177e4SLinus Torvalds { 5717ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 57233be6271SWANG Cong struct tc_action *act; 573cee63723SPatrick McHardy int err; 5741da177e4SLinus Torvalds int i; 5751da177e4SLinus Torvalds 576cee63723SPatrick McHardy err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); 577cee63723SPatrick McHardy if (err < 0) 57833be6271SWANG Cong return err; 5791da177e4SLinus Torvalds 5807ba699c6SPatrick McHardy for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { 581c1b52739SBenjamin LaHaise act = tcf_action_init_1(net, tb[i], est, name, ovr, bind); 58233be6271SWANG Cong if (IS_ERR(act)) { 58333be6271SWANG Cong err = PTR_ERR(act); 5841da177e4SLinus Torvalds goto err; 5851da177e4SLinus Torvalds } 58633be6271SWANG Cong act->order = i; 58733be6271SWANG Cong list_add_tail(&act->list, actions); 58833be6271SWANG Cong } 58933be6271SWANG Cong return 0; 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds err: 59233be6271SWANG Cong tcf_action_destroy(actions, bind); 59333be6271SWANG Cong return err; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, 5971da177e4SLinus Torvalds int compat_mode) 5981da177e4SLinus Torvalds { 5991da177e4SLinus Torvalds int err = 0; 6001da177e4SLinus Torvalds struct gnet_dump d; 6017eb8896dSWANG Cong struct tcf_common *p = a->priv; 6021da177e4SLinus Torvalds 6037eb8896dSWANG Cong if (p == NULL) 6041da177e4SLinus Torvalds goto errout; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds /* compat_mode being true specifies a call that is supposed 60706fe9fb4SDirk Hohndel * to add additional backward compatibility statistic TLVs. 6081da177e4SLinus Torvalds */ 6091da177e4SLinus Torvalds if (compat_mode) { 6101da177e4SLinus Torvalds if (a->type == TCA_OLD_COMPAT) 6111da177e4SLinus Torvalds err = gnet_stats_start_copy_compat(skb, 0, 6127eb8896dSWANG Cong TCA_STATS, TCA_XSTATS, &p->tcfc_lock, &d); 6131da177e4SLinus Torvalds else 6141da177e4SLinus Torvalds return 0; 6151da177e4SLinus Torvalds } else 6161da177e4SLinus Torvalds err = gnet_stats_start_copy(skb, TCA_ACT_STATS, 6177eb8896dSWANG Cong &p->tcfc_lock, &d); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds if (err < 0) 6201da177e4SLinus Torvalds goto errout; 6211da177e4SLinus Torvalds 6227eb8896dSWANG Cong if (gnet_stats_copy_basic(&d, &p->tcfc_bstats) < 0 || 6237eb8896dSWANG Cong gnet_stats_copy_rate_est(&d, &p->tcfc_bstats, 6247eb8896dSWANG Cong &p->tcfc_rate_est) < 0 || 6257eb8896dSWANG Cong gnet_stats_copy_queue(&d, &p->tcfc_qstats) < 0) 6261da177e4SLinus Torvalds goto errout; 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds if (gnet_stats_finish_copy(&d) < 0) 6291da177e4SLinus Torvalds goto errout; 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds return 0; 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds errout: 6341da177e4SLinus Torvalds return -1; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds static int 63833be6271SWANG Cong tca_get_fill(struct sk_buff *skb, struct list_head *actions, u32 portid, u32 seq, 639e431b8c0SJamal Hadi Salim u16 flags, int event, int bind, int ref) 6401da177e4SLinus Torvalds { 6411da177e4SLinus Torvalds struct tcamsg *t; 6421da177e4SLinus Torvalds struct nlmsghdr *nlh; 64327a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 6444b3550efSPatrick McHardy struct nlattr *nest; 6451da177e4SLinus Torvalds 64615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags); 6478b00a53cSDavid S. Miller if (!nlh) 6488b00a53cSDavid S. Miller goto out_nlmsg_trim; 6498b00a53cSDavid S. Miller t = nlmsg_data(nlh); 6501da177e4SLinus Torvalds t->tca_family = AF_UNSPEC; 6519ef1d4c7SPatrick McHardy t->tca__pad1 = 0; 6529ef1d4c7SPatrick McHardy t->tca__pad2 = 0; 6531da177e4SLinus Torvalds 6544b3550efSPatrick McHardy nest = nla_nest_start(skb, TCA_ACT_TAB); 6554b3550efSPatrick McHardy if (nest == NULL) 6568b00a53cSDavid S. Miller goto out_nlmsg_trim; 6571da177e4SLinus Torvalds 65833be6271SWANG Cong if (tcf_action_dump(skb, actions, bind, ref) < 0) 6598b00a53cSDavid S. Miller goto out_nlmsg_trim; 6601da177e4SLinus Torvalds 6614b3550efSPatrick McHardy nla_nest_end(skb, nest); 6621da177e4SLinus Torvalds 66327a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 6641da177e4SLinus Torvalds return skb->len; 6651da177e4SLinus Torvalds 6668b00a53cSDavid S. Miller out_nlmsg_trim: 667dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 6681da177e4SLinus Torvalds return -1; 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds static int 67215e47304SEric W. Biederman act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, 67333be6271SWANG Cong struct list_head *actions, int event) 6741da177e4SLinus Torvalds { 6751da177e4SLinus Torvalds struct sk_buff *skb; 6761da177e4SLinus Torvalds 6771da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 6781da177e4SLinus Torvalds if (!skb) 6791da177e4SLinus Torvalds return -ENOBUFS; 68033be6271SWANG Cong if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { 6811da177e4SLinus Torvalds kfree_skb(skb); 6821da177e4SLinus Torvalds return -EINVAL; 6831da177e4SLinus Torvalds } 6842942e900SThomas Graf 68515e47304SEric W. Biederman return rtnl_unicast(skb, net, portid); 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds static struct tc_action * 68915e47304SEric W. Biederman tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid) 6901da177e4SLinus Torvalds { 6917ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1]; 6921da177e4SLinus Torvalds struct tc_action *a; 6931da177e4SLinus Torvalds int index; 694ab27cfb8SPatrick McHardy int err; 6951da177e4SLinus Torvalds 696cee63723SPatrick McHardy err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); 697cee63723SPatrick McHardy if (err < 0) 698ab27cfb8SPatrick McHardy goto err_out; 6991da177e4SLinus Torvalds 700cee63723SPatrick McHardy err = -EINVAL; 7017ba699c6SPatrick McHardy if (tb[TCA_ACT_INDEX] == NULL || 7027ba699c6SPatrick McHardy nla_len(tb[TCA_ACT_INDEX]) < sizeof(index)) 703ab27cfb8SPatrick McHardy goto err_out; 7041587bac4SPatrick McHardy index = nla_get_u32(tb[TCA_ACT_INDEX]); 7051da177e4SLinus Torvalds 706ab27cfb8SPatrick McHardy err = -ENOMEM; 7070da974f4SPanagiotis Issaris a = kzalloc(sizeof(struct tc_action), GFP_KERNEL); 7081da177e4SLinus Torvalds if (a == NULL) 709ab27cfb8SPatrick McHardy goto err_out; 7101da177e4SLinus Torvalds 71133be6271SWANG Cong INIT_LIST_HEAD(&a->list); 712ab27cfb8SPatrick McHardy err = -EINVAL; 7137ba699c6SPatrick McHardy a->ops = tc_lookup_action(tb[TCA_ACT_KIND]); 71463acd680SJamal Hadi Salim if (a->ops == NULL) /* could happen in batch of actions */ 7151da177e4SLinus Torvalds goto err_free; 716ab27cfb8SPatrick McHardy err = -ENOENT; 7171da177e4SLinus Torvalds if (a->ops->lookup(a, index) == 0) 7181da177e4SLinus Torvalds goto err_mod; 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds module_put(a->ops->owner); 7211da177e4SLinus Torvalds return a; 722ab27cfb8SPatrick McHardy 7231da177e4SLinus Torvalds err_mod: 7241da177e4SLinus Torvalds module_put(a->ops->owner); 7251da177e4SLinus Torvalds err_free: 7261da177e4SLinus Torvalds kfree(a); 727ab27cfb8SPatrick McHardy err_out: 728ab27cfb8SPatrick McHardy return ERR_PTR(err); 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds 73133be6271SWANG Cong static void cleanup_a(struct list_head *actions) 7321da177e4SLinus Torvalds { 73333be6271SWANG Cong struct tc_action *a, *tmp; 7341da177e4SLinus Torvalds 73533be6271SWANG Cong list_for_each_entry_safe(a, tmp, actions, list) { 73633be6271SWANG Cong list_del(&a->list); 7371da177e4SLinus Torvalds kfree(a); 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds } 7401da177e4SLinus Torvalds 7411da177e4SLinus Torvalds static struct tc_action *create_a(int i) 7421da177e4SLinus Torvalds { 7431da177e4SLinus Torvalds struct tc_action *act; 7441da177e4SLinus Torvalds 7450da974f4SPanagiotis Issaris act = kzalloc(sizeof(*act), GFP_KERNEL); 7461da177e4SLinus Torvalds if (act == NULL) { 7476ff9c364Sstephen hemminger pr_debug("create_a: failed to alloc!\n"); 7481da177e4SLinus Torvalds return NULL; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds act->order = i; 75133be6271SWANG Cong INIT_LIST_HEAD(&act->list); 7521da177e4SLinus Torvalds return act; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 7557316ae88STom Goff static int tca_action_flush(struct net *net, struct nlattr *nla, 75615e47304SEric W. Biederman struct nlmsghdr *n, u32 portid) 7571da177e4SLinus Torvalds { 7581da177e4SLinus Torvalds struct sk_buff *skb; 7591da177e4SLinus Torvalds unsigned char *b; 7601da177e4SLinus Torvalds struct nlmsghdr *nlh; 7611da177e4SLinus Torvalds struct tcamsg *t; 7621da177e4SLinus Torvalds struct netlink_callback dcb; 7634b3550efSPatrick McHardy struct nlattr *nest; 7647ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1]; 7657ba699c6SPatrick McHardy struct nlattr *kind; 7661da177e4SLinus Torvalds struct tc_action *a = create_a(0); 76736723873SJamal Hadi Salim int err = -ENOMEM; 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds if (a == NULL) { 7706ff9c364Sstephen hemminger pr_debug("tca_action_flush: couldnt create tc_action\n"); 7711da177e4SLinus Torvalds return err; 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 7751da177e4SLinus Torvalds if (!skb) { 7766ff9c364Sstephen hemminger pr_debug("tca_action_flush: failed skb alloc\n"); 7771da177e4SLinus Torvalds kfree(a); 77836723873SJamal Hadi Salim return err; 7791da177e4SLinus Torvalds } 7801da177e4SLinus Torvalds 78127a884dcSArnaldo Carvalho de Melo b = skb_tail_pointer(skb); 7821da177e4SLinus Torvalds 783cee63723SPatrick McHardy err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); 784cee63723SPatrick McHardy if (err < 0) 7851da177e4SLinus Torvalds goto err_out; 7861da177e4SLinus Torvalds 787cee63723SPatrick McHardy err = -EINVAL; 7887ba699c6SPatrick McHardy kind = tb[TCA_ACT_KIND]; 7891da177e4SLinus Torvalds a->ops = tc_lookup_action(kind); 79063acd680SJamal Hadi Salim if (a->ops == NULL) /*some idjot trying to flush unknown action */ 7911da177e4SLinus Torvalds goto err_out; 7921da177e4SLinus Torvalds 79315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t), 0); 7948b00a53cSDavid S. Miller if (!nlh) 7958b00a53cSDavid S. Miller goto out_module_put; 7968b00a53cSDavid S. Miller t = nlmsg_data(nlh); 7971da177e4SLinus Torvalds t->tca_family = AF_UNSPEC; 7989ef1d4c7SPatrick McHardy t->tca__pad1 = 0; 7999ef1d4c7SPatrick McHardy t->tca__pad2 = 0; 8001da177e4SLinus Torvalds 8014b3550efSPatrick McHardy nest = nla_nest_start(skb, TCA_ACT_TAB); 8024b3550efSPatrick McHardy if (nest == NULL) 8038b00a53cSDavid S. Miller goto out_module_put; 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds err = a->ops->walk(skb, &dcb, RTM_DELACTION, a); 8061da177e4SLinus Torvalds if (err < 0) 8078b00a53cSDavid S. Miller goto out_module_put; 808f97017cdSJamal Hadi Salim if (err == 0) 809f97017cdSJamal Hadi Salim goto noflush_out; 8101da177e4SLinus Torvalds 8114b3550efSPatrick McHardy nla_nest_end(skb, nest); 8121da177e4SLinus Torvalds 81327a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 8141da177e4SLinus Torvalds nlh->nlmsg_flags |= NLM_F_ROOT; 8151da177e4SLinus Torvalds module_put(a->ops->owner); 8161da177e4SLinus Torvalds kfree(a); 81715e47304SEric W. Biederman err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 818cc7ec456SEric Dumazet n->nlmsg_flags & NLM_F_ECHO); 8191da177e4SLinus Torvalds if (err > 0) 8201da177e4SLinus Torvalds return 0; 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds return err; 8231da177e4SLinus Torvalds 8248b00a53cSDavid S. Miller out_module_put: 825ebbaeab1SThomas Graf module_put(a->ops->owner); 8261da177e4SLinus Torvalds err_out: 827f97017cdSJamal Hadi Salim noflush_out: 8281da177e4SLinus Torvalds kfree_skb(skb); 8291da177e4SLinus Torvalds kfree(a); 8301da177e4SLinus Torvalds return err; 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds 8331da177e4SLinus Torvalds static int 834a56e1953SWANG Cong tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions, 835a56e1953SWANG Cong u32 portid) 836a56e1953SWANG Cong { 837a56e1953SWANG Cong int ret; 838a56e1953SWANG Cong struct sk_buff *skb; 839a56e1953SWANG Cong 840a56e1953SWANG Cong skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 841a56e1953SWANG Cong if (!skb) 842a56e1953SWANG Cong return -ENOBUFS; 843a56e1953SWANG Cong 844a56e1953SWANG Cong if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, 845a56e1953SWANG Cong 0, 1) <= 0) { 846a56e1953SWANG Cong kfree_skb(skb); 847a56e1953SWANG Cong return -EINVAL; 848a56e1953SWANG Cong } 849a56e1953SWANG Cong 850a56e1953SWANG Cong /* now do the delete */ 851*55334a5dSWANG Cong ret = tcf_action_destroy(actions, 0); 852*55334a5dSWANG Cong if (ret < 0) { 853*55334a5dSWANG Cong kfree_skb(skb); 854*55334a5dSWANG Cong return ret; 855*55334a5dSWANG Cong } 856a56e1953SWANG Cong 857a56e1953SWANG Cong ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 858a56e1953SWANG Cong n->nlmsg_flags & NLM_F_ECHO); 859a56e1953SWANG Cong if (ret > 0) 860a56e1953SWANG Cong return 0; 861a56e1953SWANG Cong return ret; 862a56e1953SWANG Cong } 863a56e1953SWANG Cong 864a56e1953SWANG Cong static int 8657316ae88STom Goff tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, 86615e47304SEric W. Biederman u32 portid, int event) 8671da177e4SLinus Torvalds { 868cee63723SPatrick McHardy int i, ret; 8697ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 87033be6271SWANG Cong struct tc_action *act; 87133be6271SWANG Cong LIST_HEAD(actions); 8721da177e4SLinus Torvalds 873cee63723SPatrick McHardy ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); 874cee63723SPatrick McHardy if (ret < 0) 875cee63723SPatrick McHardy return ret; 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) { 878f97017cdSJamal Hadi Salim if (tb[1] != NULL) 87915e47304SEric W. Biederman return tca_action_flush(net, tb[1], n, portid); 880f97017cdSJamal Hadi Salim else 881f97017cdSJamal Hadi Salim return -EINVAL; 8821da177e4SLinus Torvalds } 8831da177e4SLinus Torvalds 8847ba699c6SPatrick McHardy for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { 88515e47304SEric W. Biederman act = tcf_action_get_1(tb[i], n, portid); 886ab27cfb8SPatrick McHardy if (IS_ERR(act)) { 887ab27cfb8SPatrick McHardy ret = PTR_ERR(act); 8881da177e4SLinus Torvalds goto err; 889ab27cfb8SPatrick McHardy } 8907ba699c6SPatrick McHardy act->order = i; 89133be6271SWANG Cong list_add_tail(&act->list, &actions); 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds if (event == RTM_GETACTION) 89533be6271SWANG Cong ret = act_get_notify(net, portid, n, &actions, event); 8961da177e4SLinus Torvalds else { /* delete */ 897a56e1953SWANG Cong ret = tcf_del_notify(net, n, &actions, portid); 898a56e1953SWANG Cong if (ret) 8991da177e4SLinus Torvalds goto err; 9001da177e4SLinus Torvalds return ret; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds err: 90333be6271SWANG Cong cleanup_a(&actions); 9041da177e4SLinus Torvalds return ret; 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 907a56e1953SWANG Cong static int 908a56e1953SWANG Cong tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions, 909a56e1953SWANG Cong u32 portid) 9101da177e4SLinus Torvalds { 9111da177e4SLinus Torvalds struct sk_buff *skb; 9121da177e4SLinus Torvalds int err = 0; 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 9151da177e4SLinus Torvalds if (!skb) 9161da177e4SLinus Torvalds return -ENOBUFS; 9171da177e4SLinus Torvalds 918a56e1953SWANG Cong if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags, 919a56e1953SWANG Cong RTM_NEWACTION, 0, 0) <= 0) { 920a56e1953SWANG Cong kfree_skb(skb); 921a56e1953SWANG Cong return -EINVAL; 922a56e1953SWANG Cong } 9231da177e4SLinus Torvalds 924a56e1953SWANG Cong err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 925a56e1953SWANG Cong n->nlmsg_flags & NLM_F_ECHO); 9261da177e4SLinus Torvalds if (err > 0) 9271da177e4SLinus Torvalds err = 0; 9281da177e4SLinus Torvalds return err; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds static int 9327316ae88STom Goff tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, 93315e47304SEric W. Biederman u32 portid, int ovr) 9341da177e4SLinus Torvalds { 9351da177e4SLinus Torvalds int ret = 0; 93633be6271SWANG Cong LIST_HEAD(actions); 9371da177e4SLinus Torvalds 93833be6271SWANG Cong ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions); 93933be6271SWANG Cong if (ret) 9401da177e4SLinus Torvalds goto done; 9411da177e4SLinus Torvalds 9421da177e4SLinus Torvalds /* dump then free all the actions after update; inserted policy 9431da177e4SLinus Torvalds * stays intact 944cc7ec456SEric Dumazet */ 945a56e1953SWANG Cong ret = tcf_add_notify(net, n, &actions, portid); 94633be6271SWANG Cong cleanup_a(&actions); 9471da177e4SLinus Torvalds done: 9481da177e4SLinus Torvalds return ret; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds 951661d2967SThomas Graf static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n) 9521da177e4SLinus Torvalds { 9533b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 9547ba699c6SPatrick McHardy struct nlattr *tca[TCA_ACT_MAX + 1]; 95515e47304SEric W. Biederman u32 portid = skb ? NETLINK_CB(skb).portid : 0; 9561da177e4SLinus Torvalds int ret = 0, ovr = 0; 9571da177e4SLinus Torvalds 958dfc47ef8SEric W. Biederman if ((n->nlmsg_type != RTM_GETACTION) && !capable(CAP_NET_ADMIN)) 959dfc47ef8SEric W. Biederman return -EPERM; 960dfc47ef8SEric W. Biederman 9617ba699c6SPatrick McHardy ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL); 9627ba699c6SPatrick McHardy if (ret < 0) 9637ba699c6SPatrick McHardy return ret; 9647ba699c6SPatrick McHardy 9657ba699c6SPatrick McHardy if (tca[TCA_ACT_TAB] == NULL) { 9666ff9c364Sstephen hemminger pr_notice("tc_ctl_action: received NO action attribs\n"); 9671da177e4SLinus Torvalds return -EINVAL; 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds 970cc7ec456SEric Dumazet /* n->nlmsg_flags & NLM_F_CREATE */ 9711da177e4SLinus Torvalds switch (n->nlmsg_type) { 9721da177e4SLinus Torvalds case RTM_NEWACTION: 9731da177e4SLinus Torvalds /* we are going to assume all other flags 97425985edcSLucas De Marchi * imply create only if it doesn't exist 9751da177e4SLinus Torvalds * Note that CREATE | EXCL implies that 9761da177e4SLinus Torvalds * but since we want avoid ambiguity (eg when flags 9771da177e4SLinus Torvalds * is zero) then just set this 9781da177e4SLinus Torvalds */ 9791da177e4SLinus Torvalds if (n->nlmsg_flags & NLM_F_REPLACE) 9801da177e4SLinus Torvalds ovr = 1; 9811da177e4SLinus Torvalds replay: 98215e47304SEric W. Biederman ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr); 9831da177e4SLinus Torvalds if (ret == -EAGAIN) 9841da177e4SLinus Torvalds goto replay; 9851da177e4SLinus Torvalds break; 9861da177e4SLinus Torvalds case RTM_DELACTION: 9877316ae88STom Goff ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, 98815e47304SEric W. Biederman portid, RTM_DELACTION); 9891da177e4SLinus Torvalds break; 9901da177e4SLinus Torvalds case RTM_GETACTION: 9917316ae88STom Goff ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, 99215e47304SEric W. Biederman portid, RTM_GETACTION); 9931da177e4SLinus Torvalds break; 9941da177e4SLinus Torvalds default: 9951da177e4SLinus Torvalds BUG(); 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds return ret; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds 10017ba699c6SPatrick McHardy static struct nlattr * 10023a6c2b41SPatrick McHardy find_dump_kind(const struct nlmsghdr *n) 10031da177e4SLinus Torvalds { 10047ba699c6SPatrick McHardy struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1]; 10057ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 10067ba699c6SPatrick McHardy struct nlattr *nla[TCAA_MAX + 1]; 10077ba699c6SPatrick McHardy struct nlattr *kind; 10081da177e4SLinus Torvalds 1009c96c9471SPatrick McHardy if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, NULL) < 0) 10101da177e4SLinus Torvalds return NULL; 10117ba699c6SPatrick McHardy tb1 = nla[TCA_ACT_TAB]; 10121da177e4SLinus Torvalds if (tb1 == NULL) 10131da177e4SLinus Torvalds return NULL; 10141da177e4SLinus Torvalds 10157ba699c6SPatrick McHardy if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1), 10167ba699c6SPatrick McHardy NLMSG_ALIGN(nla_len(tb1)), NULL) < 0) 10171da177e4SLinus Torvalds return NULL; 10181da177e4SLinus Torvalds 10196d834e04SPatrick McHardy if (tb[1] == NULL) 10206d834e04SPatrick McHardy return NULL; 10216d834e04SPatrick McHardy if (nla_parse(tb2, TCA_ACT_MAX, nla_data(tb[1]), 10226d834e04SPatrick McHardy nla_len(tb[1]), NULL) < 0) 10231da177e4SLinus Torvalds return NULL; 10247ba699c6SPatrick McHardy kind = tb2[TCA_ACT_KIND]; 10251da177e4SLinus Torvalds 102626dab893SThomas Graf return kind; 10271da177e4SLinus Torvalds } 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds static int 10301da177e4SLinus Torvalds tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) 10311da177e4SLinus Torvalds { 10321da177e4SLinus Torvalds struct nlmsghdr *nlh; 103327a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 10344b3550efSPatrick McHardy struct nlattr *nest; 10351da177e4SLinus Torvalds struct tc_action_ops *a_o; 10361da177e4SLinus Torvalds struct tc_action a; 10371da177e4SLinus Torvalds int ret = 0; 10388b00a53cSDavid S. Miller struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh); 10397ba699c6SPatrick McHardy struct nlattr *kind = find_dump_kind(cb->nlh); 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds if (kind == NULL) { 10426ff9c364Sstephen hemminger pr_info("tc_dump_action: action bad kind\n"); 10431da177e4SLinus Torvalds return 0; 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds 104626dab893SThomas Graf a_o = tc_lookup_action(kind); 1047cc7ec456SEric Dumazet if (a_o == NULL) 10481da177e4SLinus Torvalds return 0; 10491da177e4SLinus Torvalds 10501da177e4SLinus Torvalds memset(&a, 0, sizeof(struct tc_action)); 10511da177e4SLinus Torvalds a.ops = a_o; 10521da177e4SLinus Torvalds 105315e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 10548b00a53cSDavid S. Miller cb->nlh->nlmsg_type, sizeof(*t), 0); 10558b00a53cSDavid S. Miller if (!nlh) 10568b00a53cSDavid S. Miller goto out_module_put; 10578b00a53cSDavid S. Miller t = nlmsg_data(nlh); 10581da177e4SLinus Torvalds t->tca_family = AF_UNSPEC; 10599ef1d4c7SPatrick McHardy t->tca__pad1 = 0; 10609ef1d4c7SPatrick McHardy t->tca__pad2 = 0; 10611da177e4SLinus Torvalds 10624b3550efSPatrick McHardy nest = nla_nest_start(skb, TCA_ACT_TAB); 10634b3550efSPatrick McHardy if (nest == NULL) 10648b00a53cSDavid S. Miller goto out_module_put; 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds ret = a_o->walk(skb, cb, RTM_GETACTION, &a); 10671da177e4SLinus Torvalds if (ret < 0) 10688b00a53cSDavid S. Miller goto out_module_put; 10691da177e4SLinus Torvalds 10701da177e4SLinus Torvalds if (ret > 0) { 10714b3550efSPatrick McHardy nla_nest_end(skb, nest); 10721da177e4SLinus Torvalds ret = skb->len; 10731da177e4SLinus Torvalds } else 10744b3550efSPatrick McHardy nla_nest_cancel(skb, nest); 10751da177e4SLinus Torvalds 107627a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 107715e47304SEric W. Biederman if (NETLINK_CB(cb->skb).portid && ret) 10781da177e4SLinus Torvalds nlh->nlmsg_flags |= NLM_F_MULTI; 10791da177e4SLinus Torvalds module_put(a_o->owner); 10801da177e4SLinus Torvalds return skb->len; 10811da177e4SLinus Torvalds 10828b00a53cSDavid S. Miller out_module_put: 10831da177e4SLinus Torvalds module_put(a_o->owner); 1084dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 10851da177e4SLinus Torvalds return skb->len; 10861da177e4SLinus Torvalds } 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds static int __init tc_action_init(void) 10891da177e4SLinus Torvalds { 1090c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL); 1091c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL); 1092c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, 1093c7ac8679SGreg Rose NULL); 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds return 0; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds subsys_initcall(tc_action_init); 1099