Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * net/sched/act_sample.c - Packet sampling tc action
0004  * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
0005  */
0006 
0007 #include <linux/types.h>
0008 #include <linux/kernel.h>
0009 #include <linux/string.h>
0010 #include <linux/errno.h>
0011 #include <linux/skbuff.h>
0012 #include <linux/rtnetlink.h>
0013 #include <linux/module.h>
0014 #include <linux/init.h>
0015 #include <linux/gfp.h>
0016 #include <net/net_namespace.h>
0017 #include <net/netlink.h>
0018 #include <net/pkt_sched.h>
0019 #include <linux/tc_act/tc_sample.h>
0020 #include <net/tc_act/tc_sample.h>
0021 #include <net/psample.h>
0022 #include <net/pkt_cls.h>
0023 
0024 #include <linux/if_arp.h>
0025 
0026 static unsigned int sample_net_id;
0027 static struct tc_action_ops act_sample_ops;
0028 
0029 static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = {
0030     [TCA_SAMPLE_PARMS]      = { .len = sizeof(struct tc_sample) },
0031     [TCA_SAMPLE_RATE]       = { .type = NLA_U32 },
0032     [TCA_SAMPLE_TRUNC_SIZE]     = { .type = NLA_U32 },
0033     [TCA_SAMPLE_PSAMPLE_GROUP]  = { .type = NLA_U32 },
0034 };
0035 
0036 static int tcf_sample_init(struct net *net, struct nlattr *nla,
0037                struct nlattr *est, struct tc_action **a,
0038                struct tcf_proto *tp,
0039                u32 flags, struct netlink_ext_ack *extack)
0040 {
0041     struct tc_action_net *tn = net_generic(net, sample_net_id);
0042     bool bind = flags & TCA_ACT_FLAGS_BIND;
0043     struct nlattr *tb[TCA_SAMPLE_MAX + 1];
0044     struct psample_group *psample_group;
0045     u32 psample_group_num, rate, index;
0046     struct tcf_chain *goto_ch = NULL;
0047     struct tc_sample *parm;
0048     struct tcf_sample *s;
0049     bool exists = false;
0050     int ret, err;
0051 
0052     if (!nla)
0053         return -EINVAL;
0054     ret = nla_parse_nested_deprecated(tb, TCA_SAMPLE_MAX, nla,
0055                       sample_policy, NULL);
0056     if (ret < 0)
0057         return ret;
0058     if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] ||
0059         !tb[TCA_SAMPLE_PSAMPLE_GROUP])
0060         return -EINVAL;
0061 
0062     parm = nla_data(tb[TCA_SAMPLE_PARMS]);
0063     index = parm->index;
0064     err = tcf_idr_check_alloc(tn, &index, a, bind);
0065     if (err < 0)
0066         return err;
0067     exists = err;
0068     if (exists && bind)
0069         return 0;
0070 
0071     if (!exists) {
0072         ret = tcf_idr_create(tn, index, est, a,
0073                      &act_sample_ops, bind, true, flags);
0074         if (ret) {
0075             tcf_idr_cleanup(tn, index);
0076             return ret;
0077         }
0078         ret = ACT_P_CREATED;
0079     } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
0080         tcf_idr_release(*a, bind);
0081         return -EEXIST;
0082     }
0083     err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
0084     if (err < 0)
0085         goto release_idr;
0086 
0087     rate = nla_get_u32(tb[TCA_SAMPLE_RATE]);
0088     if (!rate) {
0089         NL_SET_ERR_MSG(extack, "invalid sample rate");
0090         err = -EINVAL;
0091         goto put_chain;
0092     }
0093     psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]);
0094     psample_group = psample_group_get(net, psample_group_num);
0095     if (!psample_group) {
0096         err = -ENOMEM;
0097         goto put_chain;
0098     }
0099 
0100     s = to_sample(*a);
0101 
0102     spin_lock_bh(&s->tcf_lock);
0103     goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
0104     s->rate = rate;
0105     s->psample_group_num = psample_group_num;
0106     psample_group = rcu_replace_pointer(s->psample_group, psample_group,
0107                         lockdep_is_held(&s->tcf_lock));
0108 
0109     if (tb[TCA_SAMPLE_TRUNC_SIZE]) {
0110         s->truncate = true;
0111         s->trunc_size = nla_get_u32(tb[TCA_SAMPLE_TRUNC_SIZE]);
0112     }
0113     spin_unlock_bh(&s->tcf_lock);
0114 
0115     if (psample_group)
0116         psample_group_put(psample_group);
0117     if (goto_ch)
0118         tcf_chain_put_by_act(goto_ch);
0119 
0120     return ret;
0121 put_chain:
0122     if (goto_ch)
0123         tcf_chain_put_by_act(goto_ch);
0124 release_idr:
0125     tcf_idr_release(*a, bind);
0126     return err;
0127 }
0128 
0129 static void tcf_sample_cleanup(struct tc_action *a)
0130 {
0131     struct tcf_sample *s = to_sample(a);
0132     struct psample_group *psample_group;
0133 
0134     /* last reference to action, no need to lock */
0135     psample_group = rcu_dereference_protected(s->psample_group, 1);
0136     RCU_INIT_POINTER(s->psample_group, NULL);
0137     if (psample_group)
0138         psample_group_put(psample_group);
0139 }
0140 
0141 static bool tcf_sample_dev_ok_push(struct net_device *dev)
0142 {
0143     switch (dev->type) {
0144     case ARPHRD_TUNNEL:
0145     case ARPHRD_TUNNEL6:
0146     case ARPHRD_SIT:
0147     case ARPHRD_IPGRE:
0148     case ARPHRD_IP6GRE:
0149     case ARPHRD_VOID:
0150     case ARPHRD_NONE:
0151         return false;
0152     default:
0153         return true;
0154     }
0155 }
0156 
0157 static int tcf_sample_act(struct sk_buff *skb, const struct tc_action *a,
0158               struct tcf_result *res)
0159 {
0160     struct tcf_sample *s = to_sample(a);
0161     struct psample_group *psample_group;
0162     struct psample_metadata md = {};
0163     int retval;
0164 
0165     tcf_lastuse_update(&s->tcf_tm);
0166     bstats_update(this_cpu_ptr(s->common.cpu_bstats), skb);
0167     retval = READ_ONCE(s->tcf_action);
0168 
0169     psample_group = rcu_dereference_bh(s->psample_group);
0170 
0171     /* randomly sample packets according to rate */
0172     if (psample_group && (prandom_u32() % s->rate == 0)) {
0173         if (!skb_at_tc_ingress(skb)) {
0174             md.in_ifindex = skb->skb_iif;
0175             md.out_ifindex = skb->dev->ifindex;
0176         } else {
0177             md.in_ifindex = skb->dev->ifindex;
0178         }
0179 
0180         /* on ingress, the mac header gets popped, so push it back */
0181         if (skb_at_tc_ingress(skb) && tcf_sample_dev_ok_push(skb->dev))
0182             skb_push(skb, skb->mac_len);
0183 
0184         md.trunc_size = s->truncate ? s->trunc_size : skb->len;
0185         psample_sample_packet(psample_group, skb, s->rate, &md);
0186 
0187         if (skb_at_tc_ingress(skb) && tcf_sample_dev_ok_push(skb->dev))
0188             skb_pull(skb, skb->mac_len);
0189     }
0190 
0191     return retval;
0192 }
0193 
0194 static void tcf_sample_stats_update(struct tc_action *a, u64 bytes, u64 packets,
0195                     u64 drops, u64 lastuse, bool hw)
0196 {
0197     struct tcf_sample *s = to_sample(a);
0198     struct tcf_t *tm = &s->tcf_tm;
0199 
0200     tcf_action_update_stats(a, bytes, packets, drops, hw);
0201     tm->lastuse = max_t(u64, tm->lastuse, lastuse);
0202 }
0203 
0204 static int tcf_sample_dump(struct sk_buff *skb, struct tc_action *a,
0205                int bind, int ref)
0206 {
0207     unsigned char *b = skb_tail_pointer(skb);
0208     struct tcf_sample *s = to_sample(a);
0209     struct tc_sample opt = {
0210         .index      = s->tcf_index,
0211         .refcnt     = refcount_read(&s->tcf_refcnt) - ref,
0212         .bindcnt    = atomic_read(&s->tcf_bindcnt) - bind,
0213     };
0214     struct tcf_t t;
0215 
0216     spin_lock_bh(&s->tcf_lock);
0217     opt.action = s->tcf_action;
0218     if (nla_put(skb, TCA_SAMPLE_PARMS, sizeof(opt), &opt))
0219         goto nla_put_failure;
0220 
0221     tcf_tm_dump(&t, &s->tcf_tm);
0222     if (nla_put_64bit(skb, TCA_SAMPLE_TM, sizeof(t), &t, TCA_SAMPLE_PAD))
0223         goto nla_put_failure;
0224 
0225     if (nla_put_u32(skb, TCA_SAMPLE_RATE, s->rate))
0226         goto nla_put_failure;
0227 
0228     if (s->truncate)
0229         if (nla_put_u32(skb, TCA_SAMPLE_TRUNC_SIZE, s->trunc_size))
0230             goto nla_put_failure;
0231 
0232     if (nla_put_u32(skb, TCA_SAMPLE_PSAMPLE_GROUP, s->psample_group_num))
0233         goto nla_put_failure;
0234     spin_unlock_bh(&s->tcf_lock);
0235 
0236     return skb->len;
0237 
0238 nla_put_failure:
0239     spin_unlock_bh(&s->tcf_lock);
0240     nlmsg_trim(skb, b);
0241     return -1;
0242 }
0243 
0244 static int tcf_sample_walker(struct net *net, struct sk_buff *skb,
0245                  struct netlink_callback *cb, int type,
0246                  const struct tc_action_ops *ops,
0247                  struct netlink_ext_ack *extack)
0248 {
0249     struct tc_action_net *tn = net_generic(net, sample_net_id);
0250 
0251     return tcf_generic_walker(tn, skb, cb, type, ops, extack);
0252 }
0253 
0254 static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index)
0255 {
0256     struct tc_action_net *tn = net_generic(net, sample_net_id);
0257 
0258     return tcf_idr_search(tn, a, index);
0259 }
0260 
0261 static void tcf_psample_group_put(void *priv)
0262 {
0263     struct psample_group *group = priv;
0264 
0265     psample_group_put(group);
0266 }
0267 
0268 static struct psample_group *
0269 tcf_sample_get_group(const struct tc_action *a,
0270              tc_action_priv_destructor *destructor)
0271 {
0272     struct tcf_sample *s = to_sample(a);
0273     struct psample_group *group;
0274 
0275     group = rcu_dereference_protected(s->psample_group,
0276                       lockdep_is_held(&s->tcf_lock));
0277     if (group) {
0278         psample_group_take(group);
0279         *destructor = tcf_psample_group_put;
0280     }
0281 
0282     return group;
0283 }
0284 
0285 static void tcf_offload_sample_get_group(struct flow_action_entry *entry,
0286                      const struct tc_action *act)
0287 {
0288     entry->sample.psample_group =
0289         act->ops->get_psample_group(act, &entry->destructor);
0290     entry->destructor_priv = entry->sample.psample_group;
0291 }
0292 
0293 static int tcf_sample_offload_act_setup(struct tc_action *act, void *entry_data,
0294                     u32 *index_inc, bool bind,
0295                     struct netlink_ext_ack *extack)
0296 {
0297     if (bind) {
0298         struct flow_action_entry *entry = entry_data;
0299 
0300         entry->id = FLOW_ACTION_SAMPLE;
0301         entry->sample.trunc_size = tcf_sample_trunc_size(act);
0302         entry->sample.truncate = tcf_sample_truncate(act);
0303         entry->sample.rate = tcf_sample_rate(act);
0304         tcf_offload_sample_get_group(entry, act);
0305         *index_inc = 1;
0306     } else {
0307         struct flow_offload_action *fl_action = entry_data;
0308 
0309         fl_action->id = FLOW_ACTION_SAMPLE;
0310     }
0311 
0312     return 0;
0313 }
0314 
0315 static struct tc_action_ops act_sample_ops = {
0316     .kind     = "sample",
0317     .id   = TCA_ID_SAMPLE,
0318     .owner    = THIS_MODULE,
0319     .act      = tcf_sample_act,
0320     .stats_update = tcf_sample_stats_update,
0321     .dump     = tcf_sample_dump,
0322     .init     = tcf_sample_init,
0323     .cleanup  = tcf_sample_cleanup,
0324     .walk     = tcf_sample_walker,
0325     .lookup   = tcf_sample_search,
0326     .get_psample_group = tcf_sample_get_group,
0327     .offload_act_setup    = tcf_sample_offload_act_setup,
0328     .size     = sizeof(struct tcf_sample),
0329 };
0330 
0331 static __net_init int sample_init_net(struct net *net)
0332 {
0333     struct tc_action_net *tn = net_generic(net, sample_net_id);
0334 
0335     return tc_action_net_init(net, tn, &act_sample_ops);
0336 }
0337 
0338 static void __net_exit sample_exit_net(struct list_head *net_list)
0339 {
0340     tc_action_net_exit(net_list, sample_net_id);
0341 }
0342 
0343 static struct pernet_operations sample_net_ops = {
0344     .init = sample_init_net,
0345     .exit_batch = sample_exit_net,
0346     .id   = &sample_net_id,
0347     .size = sizeof(struct tc_action_net),
0348 };
0349 
0350 static int __init sample_init_module(void)
0351 {
0352     return tcf_register_action(&act_sample_ops, &sample_net_ops);
0353 }
0354 
0355 static void __exit sample_cleanup_module(void)
0356 {
0357     tcf_unregister_action(&act_sample_ops, &sample_net_ops);
0358 }
0359 
0360 module_init(sample_init_module);
0361 module_exit(sample_cleanup_module);
0362 
0363 MODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>");
0364 MODULE_DESCRIPTION("Packet sampling action");
0365 MODULE_LICENSE("GPL v2");