linux/net/psample/psample.c
<<
>>
Prefs
   1/*
   2 * net/psample/psample.c - Netlink channel for packet sampling
   3 * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/types.h>
  11#include <linux/kernel.h>
  12#include <linux/skbuff.h>
  13#include <linux/module.h>
  14#include <net/net_namespace.h>
  15#include <net/sock.h>
  16#include <net/netlink.h>
  17#include <net/genetlink.h>
  18#include <net/psample.h>
  19#include <linux/spinlock.h>
  20
  21#define PSAMPLE_MAX_PACKET_SIZE 0xffff
  22
  23static LIST_HEAD(psample_groups_list);
  24static DEFINE_SPINLOCK(psample_groups_lock);
  25
  26/* multicast groups */
  27enum psample_nl_multicast_groups {
  28        PSAMPLE_NL_MCGRP_CONFIG,
  29        PSAMPLE_NL_MCGRP_SAMPLE,
  30};
  31
  32static const struct genl_multicast_group psample_nl_mcgrps[] = {
  33        [PSAMPLE_NL_MCGRP_CONFIG] = { .name = PSAMPLE_NL_MCGRP_CONFIG_NAME },
  34        [PSAMPLE_NL_MCGRP_SAMPLE] = { .name = PSAMPLE_NL_MCGRP_SAMPLE_NAME },
  35};
  36
  37static struct genl_family psample_nl_family __ro_after_init;
  38
  39static int psample_group_nl_fill(struct sk_buff *msg,
  40                                 struct psample_group *group,
  41                                 enum psample_command cmd, u32 portid, u32 seq,
  42                                 int flags)
  43{
  44        void *hdr;
  45        int ret;
  46
  47        hdr = genlmsg_put(msg, portid, seq, &psample_nl_family, flags, cmd);
  48        if (!hdr)
  49                return -EMSGSIZE;
  50
  51        ret = nla_put_u32(msg, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num);
  52        if (ret < 0)
  53                goto error;
  54
  55        ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_REFCOUNT, group->refcount);
  56        if (ret < 0)
  57                goto error;
  58
  59        ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_SEQ, group->seq);
  60        if (ret < 0)
  61                goto error;
  62
  63        genlmsg_end(msg, hdr);
  64        return 0;
  65
  66error:
  67        genlmsg_cancel(msg, hdr);
  68        return -EMSGSIZE;
  69}
  70
  71static int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg,
  72                                           struct netlink_callback *cb)
  73{
  74        struct psample_group *group;
  75        int start = cb->args[0];
  76        int idx = 0;
  77        int err;
  78
  79        spin_lock(&psample_groups_lock);
  80        list_for_each_entry(group, &psample_groups_list, list) {
  81                if (!net_eq(group->net, sock_net(msg->sk)))
  82                        continue;
  83                if (idx < start) {
  84                        idx++;
  85                        continue;
  86                }
  87                err = psample_group_nl_fill(msg, group, PSAMPLE_CMD_NEW_GROUP,
  88                                            NETLINK_CB(cb->skb).portid,
  89                                            cb->nlh->nlmsg_seq, NLM_F_MULTI);
  90                if (err)
  91                        break;
  92                idx++;
  93        }
  94
  95        spin_unlock(&psample_groups_lock);
  96        cb->args[0] = idx;
  97        return msg->len;
  98}
  99
 100static const struct genl_ops psample_nl_ops[] = {
 101        {
 102                .cmd = PSAMPLE_CMD_GET_GROUP,
 103                .dumpit = psample_nl_cmd_get_group_dumpit,
 104                /* can be retrieved by unprivileged users */
 105        }
 106};
 107
 108static struct genl_family psample_nl_family __ro_after_init = {
 109        .name           = PSAMPLE_GENL_NAME,
 110        .version        = PSAMPLE_GENL_VERSION,
 111        .maxattr        = PSAMPLE_ATTR_MAX,
 112        .netnsok        = true,
 113        .module         = THIS_MODULE,
 114        .mcgrps         = psample_nl_mcgrps,
 115        .ops            = psample_nl_ops,
 116        .n_ops          = ARRAY_SIZE(psample_nl_ops),
 117        .n_mcgrps       = ARRAY_SIZE(psample_nl_mcgrps),
 118};
 119
 120static void psample_group_notify(struct psample_group *group,
 121                                 enum psample_command cmd)
 122{
 123        struct sk_buff *msg;
 124        int err;
 125
 126        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
 127        if (!msg)
 128                return;
 129
 130        err = psample_group_nl_fill(msg, group, cmd, 0, 0, NLM_F_MULTI);
 131        if (!err)
 132                genlmsg_multicast_netns(&psample_nl_family, group->net, msg, 0,
 133                                        PSAMPLE_NL_MCGRP_CONFIG, GFP_ATOMIC);
 134        else
 135                nlmsg_free(msg);
 136}
 137
 138static struct psample_group *psample_group_create(struct net *net,
 139                                                  u32 group_num)
 140{
 141        struct psample_group *group;
 142
 143        group = kzalloc(sizeof(*group), GFP_ATOMIC);
 144        if (!group)
 145                return NULL;
 146
 147        group->net = net;
 148        group->group_num = group_num;
 149        list_add_tail(&group->list, &psample_groups_list);
 150
 151        psample_group_notify(group, PSAMPLE_CMD_NEW_GROUP);
 152        return group;
 153}
 154
 155static void psample_group_destroy(struct psample_group *group)
 156{
 157        psample_group_notify(group, PSAMPLE_CMD_DEL_GROUP);
 158        list_del(&group->list);
 159        kfree(group);
 160}
 161
 162static struct psample_group *
 163psample_group_lookup(struct net *net, u32 group_num)
 164{
 165        struct psample_group *group;
 166
 167        list_for_each_entry(group, &psample_groups_list, list)
 168                if ((group->group_num == group_num) && (group->net == net))
 169                        return group;
 170        return NULL;
 171}
 172
 173struct psample_group *psample_group_get(struct net *net, u32 group_num)
 174{
 175        struct psample_group *group;
 176
 177        spin_lock(&psample_groups_lock);
 178
 179        group = psample_group_lookup(net, group_num);
 180        if (!group) {
 181                group = psample_group_create(net, group_num);
 182                if (!group)
 183                        goto out;
 184        }
 185        group->refcount++;
 186
 187out:
 188        spin_unlock(&psample_groups_lock);
 189        return group;
 190}
 191EXPORT_SYMBOL_GPL(psample_group_get);
 192
 193void psample_group_put(struct psample_group *group)
 194{
 195        spin_lock(&psample_groups_lock);
 196
 197        if (--group->refcount == 0)
 198                psample_group_destroy(group);
 199
 200        spin_unlock(&psample_groups_lock);
 201}
 202EXPORT_SYMBOL_GPL(psample_group_put);
 203
 204void psample_sample_packet(struct psample_group *group, struct sk_buff *skb,
 205                           u32 trunc_size, int in_ifindex, int out_ifindex,
 206                           u32 sample_rate)
 207{
 208        struct sk_buff *nl_skb;
 209        int data_len;
 210        int meta_len;
 211        void *data;
 212        int ret;
 213
 214        meta_len = (in_ifindex ? nla_total_size(sizeof(u16)) : 0) +
 215                   (out_ifindex ? nla_total_size(sizeof(u16)) : 0) +
 216                   nla_total_size(sizeof(u32)) +        /* sample_rate */
 217                   nla_total_size(sizeof(u32)) +        /* orig_size */
 218                   nla_total_size(sizeof(u32)) +        /* group_num */
 219                   nla_total_size(sizeof(u32));         /* seq */
 220
 221        data_len = min(skb->len, trunc_size);
 222        if (meta_len + nla_total_size(data_len) > PSAMPLE_MAX_PACKET_SIZE)
 223                data_len = PSAMPLE_MAX_PACKET_SIZE - meta_len - NLA_HDRLEN
 224                            - NLA_ALIGNTO;
 225
 226        nl_skb = genlmsg_new(meta_len + data_len, GFP_ATOMIC);
 227        if (unlikely(!nl_skb))
 228                return;
 229
 230        data = genlmsg_put(nl_skb, 0, 0, &psample_nl_family, 0,
 231                           PSAMPLE_CMD_SAMPLE);
 232        if (unlikely(!data))
 233                goto error;
 234
 235        if (in_ifindex) {
 236                ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_IIFINDEX, in_ifindex);
 237                if (unlikely(ret < 0))
 238                        goto error;
 239        }
 240
 241        if (out_ifindex) {
 242                ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_OIFINDEX, out_ifindex);
 243                if (unlikely(ret < 0))
 244                        goto error;
 245        }
 246
 247        ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_RATE, sample_rate);
 248        if (unlikely(ret < 0))
 249                goto error;
 250
 251        ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_ORIGSIZE, skb->len);
 252        if (unlikely(ret < 0))
 253                goto error;
 254
 255        ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num);
 256        if (unlikely(ret < 0))
 257                goto error;
 258
 259        ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_GROUP_SEQ, group->seq++);
 260        if (unlikely(ret < 0))
 261                goto error;
 262
 263        if (data_len) {
 264                int nla_len = nla_total_size(data_len);
 265                struct nlattr *nla;
 266
 267                nla = skb_put(nl_skb, nla_len);
 268                nla->nla_type = PSAMPLE_ATTR_DATA;
 269                nla->nla_len = nla_attr_size(data_len);
 270
 271                if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
 272                        goto error;
 273        }
 274
 275        genlmsg_end(nl_skb, data);
 276        genlmsg_multicast_netns(&psample_nl_family, group->net, nl_skb, 0,
 277                                PSAMPLE_NL_MCGRP_SAMPLE, GFP_ATOMIC);
 278
 279        return;
 280error:
 281        pr_err_ratelimited("Could not create psample log message\n");
 282        nlmsg_free(nl_skb);
 283}
 284EXPORT_SYMBOL_GPL(psample_sample_packet);
 285
 286static int __init psample_module_init(void)
 287{
 288        return genl_register_family(&psample_nl_family);
 289}
 290
 291static void __exit psample_module_exit(void)
 292{
 293        genl_unregister_family(&psample_nl_family);
 294}
 295
 296module_init(psample_module_init);
 297module_exit(psample_module_exit);
 298
 299MODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>");
 300MODULE_DESCRIPTION("netlink channel for packet sampling");
 301MODULE_LICENSE("GPL v2");
 302