linux/net/sched/act_sample.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * net/sched/act_sample.c - Packet sampling tc action
   4 * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
   5 */
   6
   7#include <linux/types.h>
   8#include <linux/kernel.h>
   9#include <linux/string.h>
  10#include <linux/errno.h>
  11#include <linux/skbuff.h>
  12#include <linux/rtnetlink.h>
  13#include <linux/module.h>
  14#include <linux/init.h>
  15#include <linux/gfp.h>
  16#include <net/net_namespace.h>
  17#include <net/netlink.h>
  18#include <net/pkt_sched.h>
  19#include <linux/tc_act/tc_sample.h>
  20#include <net/tc_act/tc_sample.h>
  21#include <net/psample.h>
  22#include <net/pkt_cls.h>
  23
  24#include <linux/if_arp.h>
  25
  26static unsigned int sample_net_id;
  27static struct tc_action_ops act_sample_ops;
  28
  29static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = {
  30        [TCA_SAMPLE_PARMS]              = { .len = sizeof(struct tc_sample) },
  31        [TCA_SAMPLE_RATE]               = { .type = NLA_U32 },
  32        [TCA_SAMPLE_TRUNC_SIZE]         = { .type = NLA_U32 },
  33        [TCA_SAMPLE_PSAMPLE_GROUP]      = { .type = NLA_U32 },
  34};
  35
  36static int tcf_sample_init(struct net *net, struct nlattr *nla,
  37                           struct nlattr *est, struct tc_action **a, int ovr,
  38                           int bind, bool rtnl_held, struct tcf_proto *tp,
  39                           u32 flags, struct netlink_ext_ack *extack)
  40{
  41        struct tc_action_net *tn = net_generic(net, sample_net_id);
  42        struct nlattr *tb[TCA_SAMPLE_MAX + 1];
  43        struct psample_group *psample_group;
  44        u32 psample_group_num, rate, index;
  45        struct tcf_chain *goto_ch = NULL;
  46        struct tc_sample *parm;
  47        struct tcf_sample *s;
  48        bool exists = false;
  49        int ret, err;
  50
  51        if (!nla)
  52                return -EINVAL;
  53        ret = nla_parse_nested_deprecated(tb, TCA_SAMPLE_MAX, nla,
  54                                          sample_policy, NULL);
  55        if (ret < 0)
  56                return ret;
  57        if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] ||
  58            !tb[TCA_SAMPLE_PSAMPLE_GROUP])
  59                return -EINVAL;
  60
  61        parm = nla_data(tb[TCA_SAMPLE_PARMS]);
  62        index = parm->index;
  63        err = tcf_idr_check_alloc(tn, &index, a, bind);
  64        if (err < 0)
  65                return err;
  66        exists = err;
  67        if (exists && bind)
  68                return 0;
  69
  70        if (!exists) {
  71                ret = tcf_idr_create(tn, index, est, a,
  72                                     &act_sample_ops, bind, true, 0);
  73                if (ret) {
  74                        tcf_idr_cleanup(tn, index);
  75                        return ret;
  76                }
  77                ret = ACT_P_CREATED;
  78        } else if (!ovr) {
  79                tcf_idr_release(*a, bind);
  80                return -EEXIST;
  81        }
  82        err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
  83        if (err < 0)
  84                goto release_idr;
  85
  86        rate = nla_get_u32(tb[TCA_SAMPLE_RATE]);
  87        if (!rate) {
  88                NL_SET_ERR_MSG(extack, "invalid sample rate");
  89                err = -EINVAL;
  90                goto put_chain;
  91        }
  92        psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]);
  93        psample_group = psample_group_get(net, psample_group_num);
  94        if (!psample_group) {
  95                err = -ENOMEM;
  96                goto put_chain;
  97        }
  98
  99        s = to_sample(*a);
 100
 101        spin_lock_bh(&s->tcf_lock);
 102        goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 103        s->rate = rate;
 104        s->psample_group_num = psample_group_num;
 105        psample_group = rcu_replace_pointer(s->psample_group, psample_group,
 106                                            lockdep_is_held(&s->tcf_lock));
 107
 108        if (tb[TCA_SAMPLE_TRUNC_SIZE]) {
 109                s->truncate = true;
 110                s->trunc_size = nla_get_u32(tb[TCA_SAMPLE_TRUNC_SIZE]);
 111        }
 112        spin_unlock_bh(&s->tcf_lock);
 113
 114        if (psample_group)
 115                psample_group_put(psample_group);
 116        if (goto_ch)
 117                tcf_chain_put_by_act(goto_ch);
 118
 119        return ret;
 120put_chain:
 121        if (goto_ch)
 122                tcf_chain_put_by_act(goto_ch);
 123release_idr:
 124        tcf_idr_release(*a, bind);
 125        return err;
 126}
 127
 128static void tcf_sample_cleanup(struct tc_action *a)
 129{
 130        struct tcf_sample *s = to_sample(a);
 131        struct psample_group *psample_group;
 132
 133        /* last reference to action, no need to lock */
 134        psample_group = rcu_dereference_protected(s->psample_group, 1);
 135        RCU_INIT_POINTER(s->psample_group, NULL);
 136        if (psample_group)
 137                psample_group_put(psample_group);
 138}
 139
 140static bool tcf_sample_dev_ok_push(struct net_device *dev)
 141{
 142        switch (dev->type) {
 143        case ARPHRD_TUNNEL:
 144        case ARPHRD_TUNNEL6:
 145        case ARPHRD_SIT:
 146        case ARPHRD_IPGRE:
 147        case ARPHRD_IP6GRE:
 148        case ARPHRD_VOID:
 149        case ARPHRD_NONE:
 150                return false;
 151        default:
 152                return true;
 153        }
 154}
 155
 156static int tcf_sample_act(struct sk_buff *skb, const struct tc_action *a,
 157                          struct tcf_result *res)
 158{
 159        struct tcf_sample *s = to_sample(a);
 160        struct psample_group *psample_group;
 161        struct psample_metadata md = {};
 162        int retval;
 163
 164        tcf_lastuse_update(&s->tcf_tm);
 165        bstats_cpu_update(this_cpu_ptr(s->common.cpu_bstats), skb);
 166        retval = READ_ONCE(s->tcf_action);
 167
 168        psample_group = rcu_dereference_bh(s->psample_group);
 169
 170        /* randomly sample packets according to rate */
 171        if (psample_group && (prandom_u32() % s->rate == 0)) {
 172                if (!skb_at_tc_ingress(skb)) {
 173                        md.in_ifindex = skb->skb_iif;
 174                        md.out_ifindex = skb->dev->ifindex;
 175                } else {
 176                        md.in_ifindex = skb->dev->ifindex;
 177                }
 178
 179                /* on ingress, the mac header gets popped, so push it back */
 180                if (skb_at_tc_ingress(skb) && tcf_sample_dev_ok_push(skb->dev))
 181                        skb_push(skb, skb->mac_len);
 182
 183                md.trunc_size = s->truncate ? s->trunc_size : skb->len;
 184                psample_sample_packet(psample_group, skb, s->rate, &md);
 185
 186                if (skb_at_tc_ingress(skb) && tcf_sample_dev_ok_push(skb->dev))
 187                        skb_pull(skb, skb->mac_len);
 188        }
 189
 190        return retval;
 191}
 192
 193static void tcf_sample_stats_update(struct tc_action *a, u64 bytes, u64 packets,
 194                                    u64 drops, u64 lastuse, bool hw)
 195{
 196        struct tcf_sample *s = to_sample(a);
 197        struct tcf_t *tm = &s->tcf_tm;
 198
 199        tcf_action_update_stats(a, bytes, packets, drops, hw);
 200        tm->lastuse = max_t(u64, tm->lastuse, lastuse);
 201}
 202
 203static int tcf_sample_dump(struct sk_buff *skb, struct tc_action *a,
 204                           int bind, int ref)
 205{
 206        unsigned char *b = skb_tail_pointer(skb);
 207        struct tcf_sample *s = to_sample(a);
 208        struct tc_sample opt = {
 209                .index      = s->tcf_index,
 210                .refcnt     = refcount_read(&s->tcf_refcnt) - ref,
 211                .bindcnt    = atomic_read(&s->tcf_bindcnt) - bind,
 212        };
 213        struct tcf_t t;
 214
 215        spin_lock_bh(&s->tcf_lock);
 216        opt.action = s->tcf_action;
 217        if (nla_put(skb, TCA_SAMPLE_PARMS, sizeof(opt), &opt))
 218                goto nla_put_failure;
 219
 220        tcf_tm_dump(&t, &s->tcf_tm);
 221        if (nla_put_64bit(skb, TCA_SAMPLE_TM, sizeof(t), &t, TCA_SAMPLE_PAD))
 222                goto nla_put_failure;
 223
 224        if (nla_put_u32(skb, TCA_SAMPLE_RATE, s->rate))
 225                goto nla_put_failure;
 226
 227        if (s->truncate)
 228                if (nla_put_u32(skb, TCA_SAMPLE_TRUNC_SIZE, s->trunc_size))
 229                        goto nla_put_failure;
 230
 231        if (nla_put_u32(skb, TCA_SAMPLE_PSAMPLE_GROUP, s->psample_group_num))
 232                goto nla_put_failure;
 233        spin_unlock_bh(&s->tcf_lock);
 234
 235        return skb->len;
 236
 237nla_put_failure:
 238        spin_unlock_bh(&s->tcf_lock);
 239        nlmsg_trim(skb, b);
 240        return -1;
 241}
 242
 243static int tcf_sample_walker(struct net *net, struct sk_buff *skb,
 244                             struct netlink_callback *cb, int type,
 245                             const struct tc_action_ops *ops,
 246                             struct netlink_ext_ack *extack)
 247{
 248        struct tc_action_net *tn = net_generic(net, sample_net_id);
 249
 250        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 251}
 252
 253static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index)
 254{
 255        struct tc_action_net *tn = net_generic(net, sample_net_id);
 256
 257        return tcf_idr_search(tn, a, index);
 258}
 259
 260static void tcf_psample_group_put(void *priv)
 261{
 262        struct psample_group *group = priv;
 263
 264        psample_group_put(group);
 265}
 266
 267static struct psample_group *
 268tcf_sample_get_group(const struct tc_action *a,
 269                     tc_action_priv_destructor *destructor)
 270{
 271        struct tcf_sample *s = to_sample(a);
 272        struct psample_group *group;
 273
 274        group = rcu_dereference_protected(s->psample_group,
 275                                          lockdep_is_held(&s->tcf_lock));
 276        if (group) {
 277                psample_group_take(group);
 278                *destructor = tcf_psample_group_put;
 279        }
 280
 281        return group;
 282}
 283
 284static struct tc_action_ops act_sample_ops = {
 285        .kind     = "sample",
 286        .id       = TCA_ID_SAMPLE,
 287        .owner    = THIS_MODULE,
 288        .act      = tcf_sample_act,
 289        .stats_update = tcf_sample_stats_update,
 290        .dump     = tcf_sample_dump,
 291        .init     = tcf_sample_init,
 292        .cleanup  = tcf_sample_cleanup,
 293        .walk     = tcf_sample_walker,
 294        .lookup   = tcf_sample_search,
 295        .get_psample_group = tcf_sample_get_group,
 296        .size     = sizeof(struct tcf_sample),
 297};
 298
 299static __net_init int sample_init_net(struct net *net)
 300{
 301        struct tc_action_net *tn = net_generic(net, sample_net_id);
 302
 303        return tc_action_net_init(net, tn, &act_sample_ops);
 304}
 305
 306static void __net_exit sample_exit_net(struct list_head *net_list)
 307{
 308        tc_action_net_exit(net_list, sample_net_id);
 309}
 310
 311static struct pernet_operations sample_net_ops = {
 312        .init = sample_init_net,
 313        .exit_batch = sample_exit_net,
 314        .id   = &sample_net_id,
 315        .size = sizeof(struct tc_action_net),
 316};
 317
 318static int __init sample_init_module(void)
 319{
 320        return tcf_register_action(&act_sample_ops, &sample_net_ops);
 321}
 322
 323static void __exit sample_cleanup_module(void)
 324{
 325        tcf_unregister_action(&act_sample_ops, &sample_net_ops);
 326}
 327
 328module_init(sample_init_module);
 329module_exit(sample_cleanup_module);
 330
 331MODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>");
 332MODULE_DESCRIPTION("Packet sampling action");
 333MODULE_LICENSE("GPL v2");
 334