16ae0a628SYotam Gigi /* 26ae0a628SYotam Gigi * net/psample/psample.c - Netlink channel for packet sampling 36ae0a628SYotam Gigi * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com> 46ae0a628SYotam Gigi * 56ae0a628SYotam Gigi * This program is free software; you can redistribute it and/or modify 66ae0a628SYotam Gigi * it under the terms of the GNU General Public License version 2 as 76ae0a628SYotam Gigi * published by the Free Software Foundation. 86ae0a628SYotam Gigi */ 96ae0a628SYotam Gigi 106ae0a628SYotam Gigi #include <linux/types.h> 116ae0a628SYotam Gigi #include <linux/kernel.h> 126ae0a628SYotam Gigi #include <linux/skbuff.h> 136ae0a628SYotam Gigi #include <linux/module.h> 146ae0a628SYotam Gigi #include <net/net_namespace.h> 156ae0a628SYotam Gigi #include <net/sock.h> 166ae0a628SYotam Gigi #include <net/netlink.h> 176ae0a628SYotam Gigi #include <net/genetlink.h> 186ae0a628SYotam Gigi #include <net/psample.h> 196ae0a628SYotam Gigi #include <linux/spinlock.h> 206ae0a628SYotam Gigi 216ae0a628SYotam Gigi #define PSAMPLE_MAX_PACKET_SIZE 0xffff 226ae0a628SYotam Gigi 236ae0a628SYotam Gigi static LIST_HEAD(psample_groups_list); 246ae0a628SYotam Gigi static DEFINE_SPINLOCK(psample_groups_lock); 256ae0a628SYotam Gigi 266ae0a628SYotam Gigi /* multicast groups */ 276ae0a628SYotam Gigi enum psample_nl_multicast_groups { 286ae0a628SYotam Gigi PSAMPLE_NL_MCGRP_CONFIG, 296ae0a628SYotam Gigi PSAMPLE_NL_MCGRP_SAMPLE, 306ae0a628SYotam Gigi }; 316ae0a628SYotam Gigi 326ae0a628SYotam Gigi static const struct genl_multicast_group psample_nl_mcgrps[] = { 336ae0a628SYotam Gigi [PSAMPLE_NL_MCGRP_CONFIG] = { .name = PSAMPLE_NL_MCGRP_CONFIG_NAME }, 346ae0a628SYotam Gigi [PSAMPLE_NL_MCGRP_SAMPLE] = { .name = PSAMPLE_NL_MCGRP_SAMPLE_NAME }, 356ae0a628SYotam Gigi }; 366ae0a628SYotam Gigi 376ae0a628SYotam Gigi static struct genl_family psample_nl_family __ro_after_init; 386ae0a628SYotam Gigi 396ae0a628SYotam Gigi static int psample_group_nl_fill(struct sk_buff *msg, 406ae0a628SYotam Gigi struct psample_group *group, 416ae0a628SYotam Gigi enum psample_command cmd, u32 portid, u32 seq, 426ae0a628SYotam Gigi int flags) 436ae0a628SYotam Gigi { 446ae0a628SYotam Gigi void *hdr; 456ae0a628SYotam Gigi int ret; 466ae0a628SYotam Gigi 476ae0a628SYotam Gigi hdr = genlmsg_put(msg, portid, seq, &psample_nl_family, flags, cmd); 486ae0a628SYotam Gigi if (!hdr) 496ae0a628SYotam Gigi return -EMSGSIZE; 506ae0a628SYotam Gigi 516ae0a628SYotam Gigi ret = nla_put_u32(msg, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num); 526ae0a628SYotam Gigi if (ret < 0) 536ae0a628SYotam Gigi goto error; 546ae0a628SYotam Gigi 556ae0a628SYotam Gigi ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_REFCOUNT, group->refcount); 566ae0a628SYotam Gigi if (ret < 0) 576ae0a628SYotam Gigi goto error; 586ae0a628SYotam Gigi 596ae0a628SYotam Gigi ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_SEQ, group->seq); 606ae0a628SYotam Gigi if (ret < 0) 616ae0a628SYotam Gigi goto error; 626ae0a628SYotam Gigi 636ae0a628SYotam Gigi genlmsg_end(msg, hdr); 646ae0a628SYotam Gigi return 0; 656ae0a628SYotam Gigi 666ae0a628SYotam Gigi error: 676ae0a628SYotam Gigi genlmsg_cancel(msg, hdr); 686ae0a628SYotam Gigi return -EMSGSIZE; 696ae0a628SYotam Gigi } 706ae0a628SYotam Gigi 716ae0a628SYotam Gigi static int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg, 726ae0a628SYotam Gigi struct netlink_callback *cb) 736ae0a628SYotam Gigi { 746ae0a628SYotam Gigi struct psample_group *group; 756ae0a628SYotam Gigi int start = cb->args[0]; 766ae0a628SYotam Gigi int idx = 0; 776ae0a628SYotam Gigi int err; 786ae0a628SYotam Gigi 796ae0a628SYotam Gigi spin_lock(&psample_groups_lock); 806ae0a628SYotam Gigi list_for_each_entry(group, &psample_groups_list, list) { 816ae0a628SYotam Gigi if (!net_eq(group->net, sock_net(msg->sk))) 826ae0a628SYotam Gigi continue; 836ae0a628SYotam Gigi if (idx < start) { 846ae0a628SYotam Gigi idx++; 856ae0a628SYotam Gigi continue; 866ae0a628SYotam Gigi } 876ae0a628SYotam Gigi err = psample_group_nl_fill(msg, group, PSAMPLE_CMD_NEW_GROUP, 886ae0a628SYotam Gigi NETLINK_CB(cb->skb).portid, 896ae0a628SYotam Gigi cb->nlh->nlmsg_seq, NLM_F_MULTI); 906ae0a628SYotam Gigi if (err) 916ae0a628SYotam Gigi break; 926ae0a628SYotam Gigi idx++; 936ae0a628SYotam Gigi } 946ae0a628SYotam Gigi 956ae0a628SYotam Gigi spin_unlock(&psample_groups_lock); 966ae0a628SYotam Gigi cb->args[0] = idx; 976ae0a628SYotam Gigi return msg->len; 986ae0a628SYotam Gigi } 996ae0a628SYotam Gigi 1006ae0a628SYotam Gigi static const struct genl_ops psample_nl_ops[] = { 1016ae0a628SYotam Gigi { 1026ae0a628SYotam Gigi .cmd = PSAMPLE_CMD_GET_GROUP, 1036ae0a628SYotam Gigi .dumpit = psample_nl_cmd_get_group_dumpit, 1046ae0a628SYotam Gigi /* can be retrieved by unprivileged users */ 1056ae0a628SYotam Gigi } 1066ae0a628SYotam Gigi }; 1076ae0a628SYotam Gigi 1086ae0a628SYotam Gigi static struct genl_family psample_nl_family __ro_after_init = { 1096ae0a628SYotam Gigi .name = PSAMPLE_GENL_NAME, 1106ae0a628SYotam Gigi .version = PSAMPLE_GENL_VERSION, 1116ae0a628SYotam Gigi .maxattr = PSAMPLE_ATTR_MAX, 1126ae0a628SYotam Gigi .netnsok = true, 1136ae0a628SYotam Gigi .module = THIS_MODULE, 1146ae0a628SYotam Gigi .mcgrps = psample_nl_mcgrps, 1156ae0a628SYotam Gigi .ops = psample_nl_ops, 1166ae0a628SYotam Gigi .n_ops = ARRAY_SIZE(psample_nl_ops), 1176ae0a628SYotam Gigi .n_mcgrps = ARRAY_SIZE(psample_nl_mcgrps), 1186ae0a628SYotam Gigi }; 1196ae0a628SYotam Gigi 1206ae0a628SYotam Gigi static void psample_group_notify(struct psample_group *group, 1216ae0a628SYotam Gigi enum psample_command cmd) 1226ae0a628SYotam Gigi { 1236ae0a628SYotam Gigi struct sk_buff *msg; 1246ae0a628SYotam Gigi int err; 1256ae0a628SYotam Gigi 1266ae0a628SYotam Gigi msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 1276ae0a628SYotam Gigi if (!msg) 1286ae0a628SYotam Gigi return; 1296ae0a628SYotam Gigi 1306ae0a628SYotam Gigi err = psample_group_nl_fill(msg, group, cmd, 0, 0, NLM_F_MULTI); 1316ae0a628SYotam Gigi if (!err) 1326ae0a628SYotam Gigi genlmsg_multicast_netns(&psample_nl_family, group->net, msg, 0, 1336ae0a628SYotam Gigi PSAMPLE_NL_MCGRP_CONFIG, GFP_ATOMIC); 1346ae0a628SYotam Gigi else 1356ae0a628SYotam Gigi nlmsg_free(msg); 1366ae0a628SYotam Gigi } 1376ae0a628SYotam Gigi 1386ae0a628SYotam Gigi static struct psample_group *psample_group_create(struct net *net, 1396ae0a628SYotam Gigi u32 group_num) 1406ae0a628SYotam Gigi { 1416ae0a628SYotam Gigi struct psample_group *group; 1426ae0a628SYotam Gigi 1436ae0a628SYotam Gigi group = kzalloc(sizeof(*group), GFP_ATOMIC); 1446ae0a628SYotam Gigi if (!group) 1456ae0a628SYotam Gigi return NULL; 1466ae0a628SYotam Gigi 1476ae0a628SYotam Gigi group->net = net; 1486ae0a628SYotam Gigi group->group_num = group_num; 1496ae0a628SYotam Gigi list_add_tail(&group->list, &psample_groups_list); 1506ae0a628SYotam Gigi 1516ae0a628SYotam Gigi psample_group_notify(group, PSAMPLE_CMD_NEW_GROUP); 1526ae0a628SYotam Gigi return group; 1536ae0a628SYotam Gigi } 1546ae0a628SYotam Gigi 1556ae0a628SYotam Gigi static void psample_group_destroy(struct psample_group *group) 1566ae0a628SYotam Gigi { 1576ae0a628SYotam Gigi psample_group_notify(group, PSAMPLE_CMD_DEL_GROUP); 1586ae0a628SYotam Gigi list_del(&group->list); 1596ae0a628SYotam Gigi kfree(group); 1606ae0a628SYotam Gigi } 1616ae0a628SYotam Gigi 1626ae0a628SYotam Gigi static struct psample_group * 1636ae0a628SYotam Gigi psample_group_lookup(struct net *net, u32 group_num) 1646ae0a628SYotam Gigi { 1656ae0a628SYotam Gigi struct psample_group *group; 1666ae0a628SYotam Gigi 1676ae0a628SYotam Gigi list_for_each_entry(group, &psample_groups_list, list) 1686ae0a628SYotam Gigi if ((group->group_num == group_num) && (group->net == net)) 1696ae0a628SYotam Gigi return group; 1706ae0a628SYotam Gigi return NULL; 1716ae0a628SYotam Gigi } 1726ae0a628SYotam Gigi 1736ae0a628SYotam Gigi struct psample_group *psample_group_get(struct net *net, u32 group_num) 1746ae0a628SYotam Gigi { 1756ae0a628SYotam Gigi struct psample_group *group; 1766ae0a628SYotam Gigi 1776ae0a628SYotam Gigi spin_lock(&psample_groups_lock); 1786ae0a628SYotam Gigi 1796ae0a628SYotam Gigi group = psample_group_lookup(net, group_num); 1806ae0a628SYotam Gigi if (!group) { 1816ae0a628SYotam Gigi group = psample_group_create(net, group_num); 1826ae0a628SYotam Gigi if (!group) 1836ae0a628SYotam Gigi goto out; 1846ae0a628SYotam Gigi } 1856ae0a628SYotam Gigi group->refcount++; 1866ae0a628SYotam Gigi 1876ae0a628SYotam Gigi out: 1886ae0a628SYotam Gigi spin_unlock(&psample_groups_lock); 1896ae0a628SYotam Gigi return group; 1906ae0a628SYotam Gigi } 1916ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_group_get); 1926ae0a628SYotam Gigi 1936ae0a628SYotam Gigi void psample_group_put(struct psample_group *group) 1946ae0a628SYotam Gigi { 1956ae0a628SYotam Gigi spin_lock(&psample_groups_lock); 1966ae0a628SYotam Gigi 1976ae0a628SYotam Gigi if (--group->refcount == 0) 1986ae0a628SYotam Gigi psample_group_destroy(group); 1996ae0a628SYotam Gigi 2006ae0a628SYotam Gigi spin_unlock(&psample_groups_lock); 2016ae0a628SYotam Gigi } 2026ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_group_put); 2036ae0a628SYotam Gigi 2046ae0a628SYotam Gigi void psample_sample_packet(struct psample_group *group, struct sk_buff *skb, 2056ae0a628SYotam Gigi u32 trunc_size, int in_ifindex, int out_ifindex, 2066ae0a628SYotam Gigi u32 sample_rate) 2076ae0a628SYotam Gigi { 2086ae0a628SYotam Gigi struct sk_buff *nl_skb; 2096ae0a628SYotam Gigi int data_len; 2106ae0a628SYotam Gigi int meta_len; 2116ae0a628SYotam Gigi void *data; 2126ae0a628SYotam Gigi int ret; 2136ae0a628SYotam Gigi 2146ae0a628SYotam Gigi meta_len = (in_ifindex ? nla_total_size(sizeof(u16)) : 0) + 2156ae0a628SYotam Gigi (out_ifindex ? nla_total_size(sizeof(u16)) : 0) + 2166ae0a628SYotam Gigi nla_total_size(sizeof(u32)) + /* sample_rate */ 2176ae0a628SYotam Gigi nla_total_size(sizeof(u32)) + /* orig_size */ 2186ae0a628SYotam Gigi nla_total_size(sizeof(u32)) + /* group_num */ 2196ae0a628SYotam Gigi nla_total_size(sizeof(u32)); /* seq */ 2206ae0a628SYotam Gigi 2216ae0a628SYotam Gigi data_len = min(skb->len, trunc_size); 2226ae0a628SYotam Gigi if (meta_len + nla_total_size(data_len) > PSAMPLE_MAX_PACKET_SIZE) 2236ae0a628SYotam Gigi data_len = PSAMPLE_MAX_PACKET_SIZE - meta_len - NLA_HDRLEN 2246ae0a628SYotam Gigi - NLA_ALIGNTO; 2256ae0a628SYotam Gigi 2266ae0a628SYotam Gigi nl_skb = genlmsg_new(meta_len + data_len, GFP_ATOMIC); 2276ae0a628SYotam Gigi if (unlikely(!nl_skb)) 2286ae0a628SYotam Gigi return; 2296ae0a628SYotam Gigi 2306ae0a628SYotam Gigi data = genlmsg_put(nl_skb, 0, 0, &psample_nl_family, 0, 2316ae0a628SYotam Gigi PSAMPLE_CMD_SAMPLE); 2326ae0a628SYotam Gigi if (unlikely(!data)) 2336ae0a628SYotam Gigi goto error; 2346ae0a628SYotam Gigi 2356ae0a628SYotam Gigi if (in_ifindex) { 2366ae0a628SYotam Gigi ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_IIFINDEX, in_ifindex); 2376ae0a628SYotam Gigi if (unlikely(ret < 0)) 2386ae0a628SYotam Gigi goto error; 2396ae0a628SYotam Gigi } 2406ae0a628SYotam Gigi 2416ae0a628SYotam Gigi if (out_ifindex) { 2426ae0a628SYotam Gigi ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_OIFINDEX, out_ifindex); 2436ae0a628SYotam Gigi if (unlikely(ret < 0)) 2446ae0a628SYotam Gigi goto error; 2456ae0a628SYotam Gigi } 2466ae0a628SYotam Gigi 2476ae0a628SYotam Gigi ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_RATE, sample_rate); 2486ae0a628SYotam Gigi if (unlikely(ret < 0)) 2496ae0a628SYotam Gigi goto error; 2506ae0a628SYotam Gigi 2516ae0a628SYotam Gigi ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_ORIGSIZE, skb->len); 2526ae0a628SYotam Gigi if (unlikely(ret < 0)) 2536ae0a628SYotam Gigi goto error; 2546ae0a628SYotam Gigi 2556ae0a628SYotam Gigi ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num); 2566ae0a628SYotam Gigi if (unlikely(ret < 0)) 2576ae0a628SYotam Gigi goto error; 2586ae0a628SYotam Gigi 2596ae0a628SYotam Gigi ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_GROUP_SEQ, group->seq++); 2606ae0a628SYotam Gigi if (unlikely(ret < 0)) 2616ae0a628SYotam Gigi goto error; 2626ae0a628SYotam Gigi 2636ae0a628SYotam Gigi if (data_len) { 2646ae0a628SYotam Gigi int nla_len = nla_total_size(data_len); 2656ae0a628SYotam Gigi struct nlattr *nla; 2666ae0a628SYotam Gigi 2674df864c1SJohannes Berg nla = skb_put(nl_skb, nla_len); 2686ae0a628SYotam Gigi nla->nla_type = PSAMPLE_ATTR_DATA; 2696ae0a628SYotam Gigi nla->nla_len = nla_attr_size(data_len); 2706ae0a628SYotam Gigi 2716ae0a628SYotam Gigi if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) 2726ae0a628SYotam Gigi goto error; 2736ae0a628SYotam Gigi } 2746ae0a628SYotam Gigi 2756ae0a628SYotam Gigi genlmsg_end(nl_skb, data); 2766ae0a628SYotam Gigi genlmsg_multicast_netns(&psample_nl_family, group->net, nl_skb, 0, 2776ae0a628SYotam Gigi PSAMPLE_NL_MCGRP_SAMPLE, GFP_ATOMIC); 2786ae0a628SYotam Gigi 2796ae0a628SYotam Gigi return; 2806ae0a628SYotam Gigi error: 2816ae0a628SYotam Gigi pr_err_ratelimited("Could not create psample log message\n"); 2826ae0a628SYotam Gigi nlmsg_free(nl_skb); 2836ae0a628SYotam Gigi } 2846ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_sample_packet); 2856ae0a628SYotam Gigi 2866ae0a628SYotam Gigi static int __init psample_module_init(void) 2876ae0a628SYotam Gigi { 2886ae0a628SYotam Gigi return genl_register_family(&psample_nl_family); 2896ae0a628SYotam Gigi } 2906ae0a628SYotam Gigi 2916ae0a628SYotam Gigi static void __exit psample_module_exit(void) 2926ae0a628SYotam Gigi { 2936ae0a628SYotam Gigi genl_unregister_family(&psample_nl_family); 2946ae0a628SYotam Gigi } 2956ae0a628SYotam Gigi 2966ae0a628SYotam Gigi module_init(psample_module_init); 2976ae0a628SYotam Gigi module_exit(psample_module_exit); 2986ae0a628SYotam Gigi 299f1fd20c3SYotam Gigi MODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>"); 3006ae0a628SYotam Gigi MODULE_DESCRIPTION("netlink channel for packet sampling"); 3016ae0a628SYotam Gigi MODULE_LICENSE("GPL v2"); 302