linux/net/sched/act_simple.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * net/sched/act_simple.c       Simple example of an action
   4 *
   5 * Authors:     Jamal Hadi Salim (2005-8)
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/init.h>
  11#include <linux/kernel.h>
  12#include <linux/skbuff.h>
  13#include <linux/rtnetlink.h>
  14#include <net/netlink.h>
  15#include <net/pkt_sched.h>
  16#include <net/pkt_cls.h>
  17
  18#include <linux/tc_act/tc_defact.h>
  19#include <net/tc_act/tc_defact.h>
  20
  21static unsigned int simp_net_id;
  22static struct tc_action_ops act_simp_ops;
  23
  24#define SIMP_MAX_DATA   32
  25static int tcf_simp_act(struct sk_buff *skb, const struct tc_action *a,
  26                        struct tcf_result *res)
  27{
  28        struct tcf_defact *d = to_defact(a);
  29
  30        spin_lock(&d->tcf_lock);
  31        tcf_lastuse_update(&d->tcf_tm);
  32        bstats_update(&d->tcf_bstats, skb);
  33
  34        /* print policy string followed by _ then packet count
  35         * Example if this was the 3rd packet and the string was "hello"
  36         * then it would look like "hello_3" (without quotes)
  37         */
  38        pr_info("simple: %s_%llu\n",
  39               (char *)d->tcfd_defdata, d->tcf_bstats.packets);
  40        spin_unlock(&d->tcf_lock);
  41        return d->tcf_action;
  42}
  43
  44static void tcf_simp_release(struct tc_action *a)
  45{
  46        struct tcf_defact *d = to_defact(a);
  47        kfree(d->tcfd_defdata);
  48}
  49
  50static int alloc_defdata(struct tcf_defact *d, const struct nlattr *defdata)
  51{
  52        d->tcfd_defdata = kzalloc(SIMP_MAX_DATA, GFP_KERNEL);
  53        if (unlikely(!d->tcfd_defdata))
  54                return -ENOMEM;
  55        nla_strscpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
  56        return 0;
  57}
  58
  59static int reset_policy(struct tc_action *a, const struct nlattr *defdata,
  60                        struct tc_defact *p, struct tcf_proto *tp,
  61                        struct netlink_ext_ack *extack)
  62{
  63        struct tcf_chain *goto_ch = NULL;
  64        struct tcf_defact *d;
  65        int err;
  66
  67        err = tcf_action_check_ctrlact(p->action, tp, &goto_ch, extack);
  68        if (err < 0)
  69                return err;
  70        d = to_defact(a);
  71        spin_lock_bh(&d->tcf_lock);
  72        goto_ch = tcf_action_set_ctrlact(a, p->action, goto_ch);
  73        memset(d->tcfd_defdata, 0, SIMP_MAX_DATA);
  74        nla_strscpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
  75        spin_unlock_bh(&d->tcf_lock);
  76        if (goto_ch)
  77                tcf_chain_put_by_act(goto_ch);
  78        return 0;
  79}
  80
  81static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
  82        [TCA_DEF_PARMS] = { .len = sizeof(struct tc_defact) },
  83        [TCA_DEF_DATA]  = { .type = NLA_STRING, .len = SIMP_MAX_DATA },
  84};
  85
  86static int tcf_simp_init(struct net *net, struct nlattr *nla,
  87                         struct nlattr *est, struct tc_action **a,
  88                         struct tcf_proto *tp, u32 flags,
  89                         struct netlink_ext_ack *extack)
  90{
  91        struct tc_action_net *tn = net_generic(net, simp_net_id);
  92        bool bind = flags & TCA_ACT_FLAGS_BIND;
  93        struct nlattr *tb[TCA_DEF_MAX + 1];
  94        struct tcf_chain *goto_ch = NULL;
  95        struct tc_defact *parm;
  96        struct tcf_defact *d;
  97        bool exists = false;
  98        int ret = 0, err;
  99        u32 index;
 100
 101        if (nla == NULL)
 102                return -EINVAL;
 103
 104        err = nla_parse_nested_deprecated(tb, TCA_DEF_MAX, nla, simple_policy,
 105                                          NULL);
 106        if (err < 0)
 107                return err;
 108
 109        if (tb[TCA_DEF_PARMS] == NULL)
 110                return -EINVAL;
 111
 112        parm = nla_data(tb[TCA_DEF_PARMS]);
 113        index = parm->index;
 114        err = tcf_idr_check_alloc(tn, &index, a, bind);
 115        if (err < 0)
 116                return err;
 117        exists = err;
 118        if (exists && bind)
 119                return 0;
 120
 121        if (tb[TCA_DEF_DATA] == NULL) {
 122                if (exists)
 123                        tcf_idr_release(*a, bind);
 124                else
 125                        tcf_idr_cleanup(tn, index);
 126                return -EINVAL;
 127        }
 128
 129        if (!exists) {
 130                ret = tcf_idr_create(tn, index, est, a,
 131                                     &act_simp_ops, bind, false, 0);
 132                if (ret) {
 133                        tcf_idr_cleanup(tn, index);
 134                        return ret;
 135                }
 136
 137                d = to_defact(*a);
 138                err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
 139                                               extack);
 140                if (err < 0)
 141                        goto release_idr;
 142
 143                err = alloc_defdata(d, tb[TCA_DEF_DATA]);
 144                if (err < 0)
 145                        goto put_chain;
 146
 147                tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 148                ret = ACT_P_CREATED;
 149        } else {
 150                if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
 151                        err = -EEXIST;
 152                        goto release_idr;
 153                }
 154
 155                err = reset_policy(*a, tb[TCA_DEF_DATA], parm, tp, extack);
 156                if (err)
 157                        goto release_idr;
 158        }
 159
 160        return ret;
 161put_chain:
 162        if (goto_ch)
 163                tcf_chain_put_by_act(goto_ch);
 164release_idr:
 165        tcf_idr_release(*a, bind);
 166        return err;
 167}
 168
 169static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
 170                         int bind, int ref)
 171{
 172        unsigned char *b = skb_tail_pointer(skb);
 173        struct tcf_defact *d = to_defact(a);
 174        struct tc_defact opt = {
 175                .index   = d->tcf_index,
 176                .refcnt  = refcount_read(&d->tcf_refcnt) - ref,
 177                .bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
 178        };
 179        struct tcf_t t;
 180
 181        spin_lock_bh(&d->tcf_lock);
 182        opt.action = d->tcf_action;
 183        if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt) ||
 184            nla_put_string(skb, TCA_DEF_DATA, d->tcfd_defdata))
 185                goto nla_put_failure;
 186
 187        tcf_tm_dump(&t, &d->tcf_tm);
 188        if (nla_put_64bit(skb, TCA_DEF_TM, sizeof(t), &t, TCA_DEF_PAD))
 189                goto nla_put_failure;
 190        spin_unlock_bh(&d->tcf_lock);
 191
 192        return skb->len;
 193
 194nla_put_failure:
 195        spin_unlock_bh(&d->tcf_lock);
 196        nlmsg_trim(skb, b);
 197        return -1;
 198}
 199
 200static int tcf_simp_walker(struct net *net, struct sk_buff *skb,
 201                           struct netlink_callback *cb, int type,
 202                           const struct tc_action_ops *ops,
 203                           struct netlink_ext_ack *extack)
 204{
 205        struct tc_action_net *tn = net_generic(net, simp_net_id);
 206
 207        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 208}
 209
 210static int tcf_simp_search(struct net *net, struct tc_action **a, u32 index)
 211{
 212        struct tc_action_net *tn = net_generic(net, simp_net_id);
 213
 214        return tcf_idr_search(tn, a, index);
 215}
 216
 217static struct tc_action_ops act_simp_ops = {
 218        .kind           =       "simple",
 219        .id             =       TCA_ID_SIMP,
 220        .owner          =       THIS_MODULE,
 221        .act            =       tcf_simp_act,
 222        .dump           =       tcf_simp_dump,
 223        .cleanup        =       tcf_simp_release,
 224        .init           =       tcf_simp_init,
 225        .walk           =       tcf_simp_walker,
 226        .lookup         =       tcf_simp_search,
 227        .size           =       sizeof(struct tcf_defact),
 228};
 229
 230static __net_init int simp_init_net(struct net *net)
 231{
 232        struct tc_action_net *tn = net_generic(net, simp_net_id);
 233
 234        return tc_action_net_init(net, tn, &act_simp_ops);
 235}
 236
 237static void __net_exit simp_exit_net(struct list_head *net_list)
 238{
 239        tc_action_net_exit(net_list, simp_net_id);
 240}
 241
 242static struct pernet_operations simp_net_ops = {
 243        .init = simp_init_net,
 244        .exit_batch = simp_exit_net,
 245        .id   = &simp_net_id,
 246        .size = sizeof(struct tc_action_net),
 247};
 248
 249MODULE_AUTHOR("Jamal Hadi Salim(2005)");
 250MODULE_DESCRIPTION("Simple example action");
 251MODULE_LICENSE("GPL");
 252
 253static int __init simp_init_module(void)
 254{
 255        int ret = tcf_register_action(&act_simp_ops, &simp_net_ops);
 256        if (!ret)
 257                pr_info("Simple TC action Loaded\n");
 258        return ret;
 259}
 260
 261static void __exit simp_cleanup_module(void)
 262{
 263        tcf_unregister_action(&act_simp_ops, &simp_net_ops);
 264}
 265
 266module_init(simp_init_module);
 267module_exit(simp_cleanup_module);
 268