xref: /linux-6.15/net/psample/psample.c (revision 5eecd85c)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26ae0a628SYotam Gigi /*
36ae0a628SYotam Gigi  * net/psample/psample.c - Netlink channel for packet sampling
46ae0a628SYotam Gigi  * Copyright (c) 2017 Yotam Gigi <[email protected]>
56ae0a628SYotam Gigi  */
66ae0a628SYotam Gigi 
76ae0a628SYotam Gigi #include <linux/types.h>
86ae0a628SYotam Gigi #include <linux/kernel.h>
96ae0a628SYotam Gigi #include <linux/skbuff.h>
106ae0a628SYotam Gigi #include <linux/module.h>
1107e1a580SIdo Schimmel #include <linux/timekeeping.h>
126ae0a628SYotam Gigi #include <net/net_namespace.h>
136ae0a628SYotam Gigi #include <net/sock.h>
146ae0a628SYotam Gigi #include <net/netlink.h>
156ae0a628SYotam Gigi #include <net/genetlink.h>
166ae0a628SYotam Gigi #include <net/psample.h>
176ae0a628SYotam Gigi #include <linux/spinlock.h>
18d8bed686SChris Mi #include <net/ip_tunnels.h>
19d8bed686SChris Mi #include <net/dst_metadata.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 },
3444ec98eaSIdo Schimmel 	[PSAMPLE_NL_MCGRP_SAMPLE] = { .name = PSAMPLE_NL_MCGRP_SAMPLE_NAME,
35cd4d7263SIdo Schimmel 				      .flags = GENL_MCAST_CAP_NET_ADMIN, },
366ae0a628SYotam Gigi };
376ae0a628SYotam Gigi 
386ae0a628SYotam Gigi static struct genl_family psample_nl_family __ro_after_init;
396ae0a628SYotam Gigi 
psample_group_nl_fill(struct sk_buff * msg,struct psample_group * group,enum psample_command cmd,u32 portid,u32 seq,int flags)406ae0a628SYotam Gigi static int psample_group_nl_fill(struct sk_buff *msg,
416ae0a628SYotam Gigi 				 struct psample_group *group,
426ae0a628SYotam Gigi 				 enum psample_command cmd, u32 portid, u32 seq,
436ae0a628SYotam Gigi 				 int flags)
446ae0a628SYotam Gigi {
456ae0a628SYotam Gigi 	void *hdr;
466ae0a628SYotam Gigi 	int ret;
476ae0a628SYotam Gigi 
486ae0a628SYotam Gigi 	hdr = genlmsg_put(msg, portid, seq, &psample_nl_family, flags, cmd);
496ae0a628SYotam Gigi 	if (!hdr)
506ae0a628SYotam Gigi 		return -EMSGSIZE;
516ae0a628SYotam Gigi 
526ae0a628SYotam Gigi 	ret = nla_put_u32(msg, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num);
536ae0a628SYotam Gigi 	if (ret < 0)
546ae0a628SYotam Gigi 		goto error;
556ae0a628SYotam Gigi 
566ae0a628SYotam Gigi 	ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_REFCOUNT, group->refcount);
576ae0a628SYotam Gigi 	if (ret < 0)
586ae0a628SYotam Gigi 		goto error;
596ae0a628SYotam Gigi 
606ae0a628SYotam Gigi 	ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_SEQ, group->seq);
616ae0a628SYotam Gigi 	if (ret < 0)
626ae0a628SYotam Gigi 		goto error;
636ae0a628SYotam Gigi 
646ae0a628SYotam Gigi 	genlmsg_end(msg, hdr);
656ae0a628SYotam Gigi 	return 0;
666ae0a628SYotam Gigi 
676ae0a628SYotam Gigi error:
686ae0a628SYotam Gigi 	genlmsg_cancel(msg, hdr);
696ae0a628SYotam Gigi 	return -EMSGSIZE;
706ae0a628SYotam Gigi }
716ae0a628SYotam Gigi 
psample_nl_cmd_get_group_dumpit(struct sk_buff * msg,struct netlink_callback * cb)726ae0a628SYotam Gigi static int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg,
736ae0a628SYotam Gigi 					   struct netlink_callback *cb)
746ae0a628SYotam Gigi {
756ae0a628SYotam Gigi 	struct psample_group *group;
766ae0a628SYotam Gigi 	int start = cb->args[0];
776ae0a628SYotam Gigi 	int idx = 0;
786ae0a628SYotam Gigi 	int err;
796ae0a628SYotam Gigi 
804a5da47dSVlad Buslov 	spin_lock_bh(&psample_groups_lock);
816ae0a628SYotam Gigi 	list_for_each_entry(group, &psample_groups_list, list) {
826ae0a628SYotam Gigi 		if (!net_eq(group->net, sock_net(msg->sk)))
836ae0a628SYotam Gigi 			continue;
846ae0a628SYotam Gigi 		if (idx < start) {
856ae0a628SYotam Gigi 			idx++;
866ae0a628SYotam Gigi 			continue;
876ae0a628SYotam Gigi 		}
886ae0a628SYotam Gigi 		err = psample_group_nl_fill(msg, group, PSAMPLE_CMD_NEW_GROUP,
896ae0a628SYotam Gigi 					    NETLINK_CB(cb->skb).portid,
906ae0a628SYotam Gigi 					    cb->nlh->nlmsg_seq, NLM_F_MULTI);
916ae0a628SYotam Gigi 		if (err)
926ae0a628SYotam Gigi 			break;
936ae0a628SYotam Gigi 		idx++;
946ae0a628SYotam Gigi 	}
956ae0a628SYotam Gigi 
964a5da47dSVlad Buslov 	spin_unlock_bh(&psample_groups_lock);
976ae0a628SYotam Gigi 	cb->args[0] = idx;
986ae0a628SYotam Gigi 	return msg->len;
996ae0a628SYotam Gigi }
1006ae0a628SYotam Gigi 
10166a9b928SJakub Kicinski static const struct genl_small_ops psample_nl_ops[] = {
1026ae0a628SYotam Gigi 	{
1036ae0a628SYotam Gigi 		.cmd = PSAMPLE_CMD_GET_GROUP,
104ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1056ae0a628SYotam Gigi 		.dumpit = psample_nl_cmd_get_group_dumpit,
1066ae0a628SYotam Gigi 		/* can be retrieved by unprivileged users */
1076ae0a628SYotam Gigi 	}
1086ae0a628SYotam Gigi };
1096ae0a628SYotam Gigi 
1106ae0a628SYotam Gigi static struct genl_family psample_nl_family __ro_after_init = {
1116ae0a628SYotam Gigi 	.name		= PSAMPLE_GENL_NAME,
1126ae0a628SYotam Gigi 	.version	= PSAMPLE_GENL_VERSION,
1136ae0a628SYotam Gigi 	.maxattr	= PSAMPLE_ATTR_MAX,
1146ae0a628SYotam Gigi 	.netnsok	= true,
1156ae0a628SYotam Gigi 	.module		= THIS_MODULE,
1166ae0a628SYotam Gigi 	.mcgrps		= psample_nl_mcgrps,
11766a9b928SJakub Kicinski 	.small_ops	= psample_nl_ops,
11866a9b928SJakub Kicinski 	.n_small_ops	= ARRAY_SIZE(psample_nl_ops),
1199c5d03d3SJakub Kicinski 	.resv_start_op	= PSAMPLE_CMD_GET_GROUP + 1,
1206ae0a628SYotam Gigi 	.n_mcgrps	= ARRAY_SIZE(psample_nl_mcgrps),
1216ae0a628SYotam Gigi };
1226ae0a628SYotam Gigi 
psample_group_notify(struct psample_group * group,enum psample_command cmd)1236ae0a628SYotam Gigi static void psample_group_notify(struct psample_group *group,
1246ae0a628SYotam Gigi 				 enum psample_command cmd)
1256ae0a628SYotam Gigi {
1266ae0a628SYotam Gigi 	struct sk_buff *msg;
1276ae0a628SYotam Gigi 	int err;
1286ae0a628SYotam Gigi 
1296ae0a628SYotam Gigi 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
1306ae0a628SYotam Gigi 	if (!msg)
1316ae0a628SYotam Gigi 		return;
1326ae0a628SYotam Gigi 
1336ae0a628SYotam Gigi 	err = psample_group_nl_fill(msg, group, cmd, 0, 0, NLM_F_MULTI);
1346ae0a628SYotam Gigi 	if (!err)
1356ae0a628SYotam Gigi 		genlmsg_multicast_netns(&psample_nl_family, group->net, msg, 0,
1366ae0a628SYotam Gigi 					PSAMPLE_NL_MCGRP_CONFIG, GFP_ATOMIC);
1376ae0a628SYotam Gigi 	else
1386ae0a628SYotam Gigi 		nlmsg_free(msg);
1396ae0a628SYotam Gigi }
1406ae0a628SYotam Gigi 
psample_group_create(struct net * net,u32 group_num)1416ae0a628SYotam Gigi static struct psample_group *psample_group_create(struct net *net,
1426ae0a628SYotam Gigi 						  u32 group_num)
1436ae0a628SYotam Gigi {
1446ae0a628SYotam Gigi 	struct psample_group *group;
1456ae0a628SYotam Gigi 
1466ae0a628SYotam Gigi 	group = kzalloc(sizeof(*group), GFP_ATOMIC);
1476ae0a628SYotam Gigi 	if (!group)
1486ae0a628SYotam Gigi 		return NULL;
1496ae0a628SYotam Gigi 
1506ae0a628SYotam Gigi 	group->net = net;
1516ae0a628SYotam Gigi 	group->group_num = group_num;
1526ae0a628SYotam Gigi 	list_add_tail(&group->list, &psample_groups_list);
1536ae0a628SYotam Gigi 
1546ae0a628SYotam Gigi 	psample_group_notify(group, PSAMPLE_CMD_NEW_GROUP);
1556ae0a628SYotam Gigi 	return group;
1566ae0a628SYotam Gigi }
1576ae0a628SYotam Gigi 
psample_group_destroy(struct psample_group * group)1586ae0a628SYotam Gigi static void psample_group_destroy(struct psample_group *group)
1596ae0a628SYotam Gigi {
1606ae0a628SYotam Gigi 	psample_group_notify(group, PSAMPLE_CMD_DEL_GROUP);
1616ae0a628SYotam Gigi 	list_del(&group->list);
162dbf47a2aSVlad Buslov 	kfree_rcu(group, rcu);
1636ae0a628SYotam Gigi }
1646ae0a628SYotam Gigi 
1656ae0a628SYotam Gigi static struct psample_group *
psample_group_lookup(struct net * net,u32 group_num)1666ae0a628SYotam Gigi psample_group_lookup(struct net *net, u32 group_num)
1676ae0a628SYotam Gigi {
1686ae0a628SYotam Gigi 	struct psample_group *group;
1696ae0a628SYotam Gigi 
1706ae0a628SYotam Gigi 	list_for_each_entry(group, &psample_groups_list, list)
1716ae0a628SYotam Gigi 		if ((group->group_num == group_num) && (group->net == net))
1726ae0a628SYotam Gigi 			return group;
1736ae0a628SYotam Gigi 	return NULL;
1746ae0a628SYotam Gigi }
1756ae0a628SYotam Gigi 
psample_group_get(struct net * net,u32 group_num)1766ae0a628SYotam Gigi struct psample_group *psample_group_get(struct net *net, u32 group_num)
1776ae0a628SYotam Gigi {
1786ae0a628SYotam Gigi 	struct psample_group *group;
1796ae0a628SYotam Gigi 
1804a5da47dSVlad Buslov 	spin_lock_bh(&psample_groups_lock);
1816ae0a628SYotam Gigi 
1826ae0a628SYotam Gigi 	group = psample_group_lookup(net, group_num);
1836ae0a628SYotam Gigi 	if (!group) {
1846ae0a628SYotam Gigi 		group = psample_group_create(net, group_num);
1856ae0a628SYotam Gigi 		if (!group)
1866ae0a628SYotam Gigi 			goto out;
1876ae0a628SYotam Gigi 	}
1886ae0a628SYotam Gigi 	group->refcount++;
1896ae0a628SYotam Gigi 
1906ae0a628SYotam Gigi out:
1914a5da47dSVlad Buslov 	spin_unlock_bh(&psample_groups_lock);
1926ae0a628SYotam Gigi 	return group;
1936ae0a628SYotam Gigi }
1946ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_group_get);
1956ae0a628SYotam Gigi 
psample_group_take(struct psample_group * group)1964a5da47dSVlad Buslov void psample_group_take(struct psample_group *group)
1974a5da47dSVlad Buslov {
1984a5da47dSVlad Buslov 	spin_lock_bh(&psample_groups_lock);
1994a5da47dSVlad Buslov 	group->refcount++;
2004a5da47dSVlad Buslov 	spin_unlock_bh(&psample_groups_lock);
2014a5da47dSVlad Buslov }
2024a5da47dSVlad Buslov EXPORT_SYMBOL_GPL(psample_group_take);
2034a5da47dSVlad Buslov 
psample_group_put(struct psample_group * group)2046ae0a628SYotam Gigi void psample_group_put(struct psample_group *group)
2056ae0a628SYotam Gigi {
2064a5da47dSVlad Buslov 	spin_lock_bh(&psample_groups_lock);
2076ae0a628SYotam Gigi 
2086ae0a628SYotam Gigi 	if (--group->refcount == 0)
2096ae0a628SYotam Gigi 		psample_group_destroy(group);
2106ae0a628SYotam Gigi 
2114a5da47dSVlad Buslov 	spin_unlock_bh(&psample_groups_lock);
2126ae0a628SYotam Gigi }
2136ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_group_put);
2146ae0a628SYotam Gigi 
21507a7f308SRandy Dunlap #ifdef CONFIG_INET
__psample_ip_tun_to_nlattr(struct sk_buff * skb,struct ip_tunnel_info * tun_info)216d8bed686SChris Mi static int __psample_ip_tun_to_nlattr(struct sk_buff *skb,
217d8bed686SChris Mi 			      struct ip_tunnel_info *tun_info)
218d8bed686SChris Mi {
219d8bed686SChris Mi 	unsigned short tun_proto = ip_tunnel_info_af(tun_info);
220d8bed686SChris Mi 	const void *tun_opts = ip_tunnel_info_opts(tun_info);
221d8bed686SChris Mi 	const struct ip_tunnel_key *tun_key = &tun_info->key;
222d8bed686SChris Mi 	int tun_opts_len = tun_info->options_len;
223d8bed686SChris Mi 
2245832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_KEY_BIT, tun_key->tun_flags) &&
225d8bed686SChris Mi 	    nla_put_be64(skb, PSAMPLE_TUNNEL_KEY_ATTR_ID, tun_key->tun_id,
226d8bed686SChris Mi 			 PSAMPLE_TUNNEL_KEY_ATTR_PAD))
227d8bed686SChris Mi 		return -EMSGSIZE;
228d8bed686SChris Mi 
229d8bed686SChris Mi 	if (tun_info->mode & IP_TUNNEL_INFO_BRIDGE &&
230d8bed686SChris Mi 	    nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE))
231d8bed686SChris Mi 		return -EMSGSIZE;
232d8bed686SChris Mi 
233d8bed686SChris Mi 	switch (tun_proto) {
234d8bed686SChris Mi 	case AF_INET:
235d8bed686SChris Mi 		if (tun_key->u.ipv4.src &&
236d8bed686SChris Mi 		    nla_put_in_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_SRC,
237d8bed686SChris Mi 				    tun_key->u.ipv4.src))
238d8bed686SChris Mi 			return -EMSGSIZE;
239d8bed686SChris Mi 		if (tun_key->u.ipv4.dst &&
240d8bed686SChris Mi 		    nla_put_in_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_DST,
241d8bed686SChris Mi 				    tun_key->u.ipv4.dst))
242d8bed686SChris Mi 			return -EMSGSIZE;
243d8bed686SChris Mi 		break;
244d8bed686SChris Mi 	case AF_INET6:
245d8bed686SChris Mi 		if (!ipv6_addr_any(&tun_key->u.ipv6.src) &&
246d8bed686SChris Mi 		    nla_put_in6_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV6_SRC,
247d8bed686SChris Mi 				     &tun_key->u.ipv6.src))
248d8bed686SChris Mi 			return -EMSGSIZE;
249d8bed686SChris Mi 		if (!ipv6_addr_any(&tun_key->u.ipv6.dst) &&
250d8bed686SChris Mi 		    nla_put_in6_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV6_DST,
251d8bed686SChris Mi 				     &tun_key->u.ipv6.dst))
252d8bed686SChris Mi 			return -EMSGSIZE;
253d8bed686SChris Mi 		break;
254d8bed686SChris Mi 	}
255d8bed686SChris Mi 	if (tun_key->tos &&
256d8bed686SChris Mi 	    nla_put_u8(skb, PSAMPLE_TUNNEL_KEY_ATTR_TOS, tun_key->tos))
257d8bed686SChris Mi 		return -EMSGSIZE;
258d8bed686SChris Mi 	if (nla_put_u8(skb, PSAMPLE_TUNNEL_KEY_ATTR_TTL, tun_key->ttl))
259d8bed686SChris Mi 		return -EMSGSIZE;
2605832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_DONT_FRAGMENT_BIT, tun_key->tun_flags) &&
261d8bed686SChris Mi 	    nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
262d8bed686SChris Mi 		return -EMSGSIZE;
2635832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_CSUM_BIT, tun_key->tun_flags) &&
264d8bed686SChris Mi 	    nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_CSUM))
265d8bed686SChris Mi 		return -EMSGSIZE;
266d8bed686SChris Mi 	if (tun_key->tp_src &&
267d8bed686SChris Mi 	    nla_put_be16(skb, PSAMPLE_TUNNEL_KEY_ATTR_TP_SRC, tun_key->tp_src))
268d8bed686SChris Mi 		return -EMSGSIZE;
269d8bed686SChris Mi 	if (tun_key->tp_dst &&
270d8bed686SChris Mi 	    nla_put_be16(skb, PSAMPLE_TUNNEL_KEY_ATTR_TP_DST, tun_key->tp_dst))
271d8bed686SChris Mi 		return -EMSGSIZE;
2725832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_OAM_BIT, tun_key->tun_flags) &&
273d8bed686SChris Mi 	    nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_OAM))
274d8bed686SChris Mi 		return -EMSGSIZE;
275d8bed686SChris Mi 	if (tun_opts_len) {
2765832c4a7SAlexander Lobakin 		if (test_bit(IP_TUNNEL_GENEVE_OPT_BIT, tun_key->tun_flags) &&
277d8bed686SChris Mi 		    nla_put(skb, PSAMPLE_TUNNEL_KEY_ATTR_GENEVE_OPTS,
278d8bed686SChris Mi 			    tun_opts_len, tun_opts))
279d8bed686SChris Mi 			return -EMSGSIZE;
2805832c4a7SAlexander Lobakin 		else if (test_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
2815832c4a7SAlexander Lobakin 				  tun_key->tun_flags) &&
282d8bed686SChris Mi 			 nla_put(skb, PSAMPLE_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
283d8bed686SChris Mi 				 tun_opts_len, tun_opts))
284d8bed686SChris Mi 			return -EMSGSIZE;
285d8bed686SChris Mi 	}
286d8bed686SChris Mi 
287d8bed686SChris Mi 	return 0;
288d8bed686SChris Mi }
289d8bed686SChris Mi 
psample_ip_tun_to_nlattr(struct sk_buff * skb,struct ip_tunnel_info * tun_info)290d8bed686SChris Mi static int psample_ip_tun_to_nlattr(struct sk_buff *skb,
291d8bed686SChris Mi 			    struct ip_tunnel_info *tun_info)
292d8bed686SChris Mi {
293d8bed686SChris Mi 	struct nlattr *nla;
294d8bed686SChris Mi 	int err;
295d8bed686SChris Mi 
296d8bed686SChris Mi 	nla = nla_nest_start_noflag(skb, PSAMPLE_ATTR_TUNNEL);
297d8bed686SChris Mi 	if (!nla)
298d8bed686SChris Mi 		return -EMSGSIZE;
299d8bed686SChris Mi 
300d8bed686SChris Mi 	err = __psample_ip_tun_to_nlattr(skb, tun_info);
301d8bed686SChris Mi 	if (err) {
302d8bed686SChris Mi 		nla_nest_cancel(skb, nla);
303d8bed686SChris Mi 		return err;
304d8bed686SChris Mi 	}
305d8bed686SChris Mi 
306d8bed686SChris Mi 	nla_nest_end(skb, nla);
307d8bed686SChris Mi 
308d8bed686SChris Mi 	return 0;
309d8bed686SChris Mi }
310d8bed686SChris Mi 
psample_tunnel_meta_len(struct ip_tunnel_info * tun_info)311d8bed686SChris Mi static int psample_tunnel_meta_len(struct ip_tunnel_info *tun_info)
312d8bed686SChris Mi {
313d8bed686SChris Mi 	unsigned short tun_proto = ip_tunnel_info_af(tun_info);
314d8bed686SChris Mi 	const struct ip_tunnel_key *tun_key = &tun_info->key;
315d8bed686SChris Mi 	int tun_opts_len = tun_info->options_len;
316a93dcaadSChris Mi 	int sum = nla_total_size(0);	/* PSAMPLE_ATTR_TUNNEL */
317d8bed686SChris Mi 
3185832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_KEY_BIT, tun_key->tun_flags))
319a93dcaadSChris Mi 		sum += nla_total_size_64bit(sizeof(u64));
320d8bed686SChris Mi 
321d8bed686SChris Mi 	if (tun_info->mode & IP_TUNNEL_INFO_BRIDGE)
322d8bed686SChris Mi 		sum += nla_total_size(0);
323d8bed686SChris Mi 
324d8bed686SChris Mi 	switch (tun_proto) {
325d8bed686SChris Mi 	case AF_INET:
326d8bed686SChris Mi 		if (tun_key->u.ipv4.src)
327d8bed686SChris Mi 			sum += nla_total_size(sizeof(u32));
328d8bed686SChris Mi 		if (tun_key->u.ipv4.dst)
329d8bed686SChris Mi 			sum += nla_total_size(sizeof(u32));
330d8bed686SChris Mi 		break;
331d8bed686SChris Mi 	case AF_INET6:
332d8bed686SChris Mi 		if (!ipv6_addr_any(&tun_key->u.ipv6.src))
333d8bed686SChris Mi 			sum += nla_total_size(sizeof(struct in6_addr));
334d8bed686SChris Mi 		if (!ipv6_addr_any(&tun_key->u.ipv6.dst))
335d8bed686SChris Mi 			sum += nla_total_size(sizeof(struct in6_addr));
336d8bed686SChris Mi 		break;
337d8bed686SChris Mi 	}
338d8bed686SChris Mi 	if (tun_key->tos)
339d8bed686SChris Mi 		sum += nla_total_size(sizeof(u8));
340d8bed686SChris Mi 	sum += nla_total_size(sizeof(u8));	/* TTL */
3415832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_DONT_FRAGMENT_BIT, tun_key->tun_flags))
342d8bed686SChris Mi 		sum += nla_total_size(0);
3435832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_CSUM_BIT, tun_key->tun_flags))
344d8bed686SChris Mi 		sum += nla_total_size(0);
345d8bed686SChris Mi 	if (tun_key->tp_src)
346d8bed686SChris Mi 		sum += nla_total_size(sizeof(u16));
347d8bed686SChris Mi 	if (tun_key->tp_dst)
348d8bed686SChris Mi 		sum += nla_total_size(sizeof(u16));
3495832c4a7SAlexander Lobakin 	if (test_bit(IP_TUNNEL_OAM_BIT, tun_key->tun_flags))
350d8bed686SChris Mi 		sum += nla_total_size(0);
351d8bed686SChris Mi 	if (tun_opts_len) {
3525832c4a7SAlexander Lobakin 		if (test_bit(IP_TUNNEL_GENEVE_OPT_BIT, tun_key->tun_flags))
353d8bed686SChris Mi 			sum += nla_total_size(tun_opts_len);
3545832c4a7SAlexander Lobakin 		else if (test_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
3555832c4a7SAlexander Lobakin 				  tun_key->tun_flags))
356d8bed686SChris Mi 			sum += nla_total_size(tun_opts_len);
357d8bed686SChris Mi 	}
358d8bed686SChris Mi 
359d8bed686SChris Mi 	return sum;
360d8bed686SChris Mi }
36107a7f308SRandy Dunlap #endif
362d8bed686SChris Mi 
psample_sample_packet(struct psample_group * group,const struct sk_buff * skb,u32 sample_rate,const struct psample_metadata * md)3638341eee8SAdrian Moreno void psample_sample_packet(struct psample_group *group,
3648341eee8SAdrian Moreno 			   const struct sk_buff *skb, u32 sample_rate,
3658341eee8SAdrian Moreno 			   const struct psample_metadata *md)
3666ae0a628SYotam Gigi {
36707e1a580SIdo Schimmel 	ktime_t tstamp = ktime_get_real();
368a03e99d3SIdo Schimmel 	int out_ifindex = md->out_ifindex;
369a03e99d3SIdo Schimmel 	int in_ifindex = md->in_ifindex;
370a03e99d3SIdo Schimmel 	u32 trunc_size = md->trunc_size;
37107a7f308SRandy Dunlap #ifdef CONFIG_INET
372d8bed686SChris Mi 	struct ip_tunnel_info *tun_info;
37307a7f308SRandy Dunlap #endif
3746ae0a628SYotam Gigi 	struct sk_buff *nl_skb;
3756ae0a628SYotam Gigi 	int data_len;
3766ae0a628SYotam Gigi 	int meta_len;
3776ae0a628SYotam Gigi 	void *data;
3786ae0a628SYotam Gigi 	int ret;
3796ae0a628SYotam Gigi 
380c35d86a2SAdrian Moreno 	if (!genl_has_listeners(&psample_nl_family, group->net,
381c35d86a2SAdrian Moreno 				PSAMPLE_NL_MCGRP_SAMPLE))
382c35d86a2SAdrian Moreno 		return;
383c35d86a2SAdrian Moreno 
3846ae0a628SYotam Gigi 	meta_len = (in_ifindex ? nla_total_size(sizeof(u16)) : 0) +
3856ae0a628SYotam Gigi 		   (out_ifindex ? nla_total_size(sizeof(u16)) : 0) +
38607e1a580SIdo Schimmel 		   (md->out_tc_valid ? nla_total_size(sizeof(u16)) : 0) +
38707e1a580SIdo Schimmel 		   (md->out_tc_occ_valid ? nla_total_size_64bit(sizeof(u64)) : 0) +
38807e1a580SIdo Schimmel 		   (md->latency_valid ? nla_total_size_64bit(sizeof(u64)) : 0) +
3896ae0a628SYotam Gigi 		   nla_total_size(sizeof(u32)) +	/* sample_rate */
3906ae0a628SYotam Gigi 		   nla_total_size(sizeof(u32)) +	/* orig_size */
3916ae0a628SYotam Gigi 		   nla_total_size(sizeof(u32)) +	/* group_num */
39207e1a580SIdo Schimmel 		   nla_total_size(sizeof(u32)) +	/* seq */
39307e1a580SIdo Schimmel 		   nla_total_size_64bit(sizeof(u64)) +	/* timestamp */
394093b0f36SAdrian Moreno 		   nla_total_size(sizeof(u16)) +	/* protocol */
395093b0f36SAdrian Moreno 		   (md->user_cookie_len ?
396*5eecd85cSAdrian Moreno 		    nla_total_size(md->user_cookie_len) : 0) + /* user cookie */
397*5eecd85cSAdrian Moreno 		   (md->rate_as_probability ?
398*5eecd85cSAdrian Moreno 		    nla_total_size(0) : 0);	/* rate as probability */
3996ae0a628SYotam Gigi 
40007a7f308SRandy Dunlap #ifdef CONFIG_INET
401d8bed686SChris Mi 	tun_info = skb_tunnel_info(skb);
402d8bed686SChris Mi 	if (tun_info)
403d8bed686SChris Mi 		meta_len += psample_tunnel_meta_len(tun_info);
40407a7f308SRandy Dunlap #endif
405d8bed686SChris Mi 
4066ae0a628SYotam Gigi 	data_len = min(skb->len, trunc_size);
4076ae0a628SYotam Gigi 	if (meta_len + nla_total_size(data_len) > PSAMPLE_MAX_PACKET_SIZE)
4086ae0a628SYotam Gigi 		data_len = PSAMPLE_MAX_PACKET_SIZE - meta_len - NLA_HDRLEN
4096ae0a628SYotam Gigi 			    - NLA_ALIGNTO;
4106ae0a628SYotam Gigi 
4117eb9d767SNikolay Aleksandrov 	nl_skb = genlmsg_new(meta_len + nla_total_size(data_len), GFP_ATOMIC);
4126ae0a628SYotam Gigi 	if (unlikely(!nl_skb))
4136ae0a628SYotam Gigi 		return;
4146ae0a628SYotam Gigi 
4156ae0a628SYotam Gigi 	data = genlmsg_put(nl_skb, 0, 0, &psample_nl_family, 0,
4166ae0a628SYotam Gigi 			   PSAMPLE_CMD_SAMPLE);
4176ae0a628SYotam Gigi 	if (unlikely(!data))
4186ae0a628SYotam Gigi 		goto error;
4196ae0a628SYotam Gigi 
4206ae0a628SYotam Gigi 	if (in_ifindex) {
4216ae0a628SYotam Gigi 		ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_IIFINDEX, in_ifindex);
4226ae0a628SYotam Gigi 		if (unlikely(ret < 0))
4236ae0a628SYotam Gigi 			goto error;
4246ae0a628SYotam Gigi 	}
4256ae0a628SYotam Gigi 
4266ae0a628SYotam Gigi 	if (out_ifindex) {
4276ae0a628SYotam Gigi 		ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_OIFINDEX, out_ifindex);
4286ae0a628SYotam Gigi 		if (unlikely(ret < 0))
4296ae0a628SYotam Gigi 			goto error;
4306ae0a628SYotam Gigi 	}
4316ae0a628SYotam Gigi 
4326ae0a628SYotam Gigi 	ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_RATE, sample_rate);
4336ae0a628SYotam Gigi 	if (unlikely(ret < 0))
4346ae0a628SYotam Gigi 		goto error;
4356ae0a628SYotam Gigi 
4366ae0a628SYotam Gigi 	ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_ORIGSIZE, skb->len);
4376ae0a628SYotam Gigi 	if (unlikely(ret < 0))
4386ae0a628SYotam Gigi 		goto error;
4396ae0a628SYotam Gigi 
4406ae0a628SYotam Gigi 	ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num);
4416ae0a628SYotam Gigi 	if (unlikely(ret < 0))
4426ae0a628SYotam Gigi 		goto error;
4436ae0a628SYotam Gigi 
4446ae0a628SYotam Gigi 	ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_GROUP_SEQ, group->seq++);
4456ae0a628SYotam Gigi 	if (unlikely(ret < 0))
4466ae0a628SYotam Gigi 		goto error;
4476ae0a628SYotam Gigi 
44807e1a580SIdo Schimmel 	if (md->out_tc_valid) {
44907e1a580SIdo Schimmel 		ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_OUT_TC, md->out_tc);
45007e1a580SIdo Schimmel 		if (unlikely(ret < 0))
45107e1a580SIdo Schimmel 			goto error;
45207e1a580SIdo Schimmel 	}
45307e1a580SIdo Schimmel 
45407e1a580SIdo Schimmel 	if (md->out_tc_occ_valid) {
45507e1a580SIdo Schimmel 		ret = nla_put_u64_64bit(nl_skb, PSAMPLE_ATTR_OUT_TC_OCC,
45607e1a580SIdo Schimmel 					md->out_tc_occ, PSAMPLE_ATTR_PAD);
45707e1a580SIdo Schimmel 		if (unlikely(ret < 0))
45807e1a580SIdo Schimmel 			goto error;
45907e1a580SIdo Schimmel 	}
46007e1a580SIdo Schimmel 
46107e1a580SIdo Schimmel 	if (md->latency_valid) {
46207e1a580SIdo Schimmel 		ret = nla_put_u64_64bit(nl_skb, PSAMPLE_ATTR_LATENCY,
46307e1a580SIdo Schimmel 					md->latency, PSAMPLE_ATTR_PAD);
46407e1a580SIdo Schimmel 		if (unlikely(ret < 0))
46507e1a580SIdo Schimmel 			goto error;
46607e1a580SIdo Schimmel 	}
46707e1a580SIdo Schimmel 
46807e1a580SIdo Schimmel 	ret = nla_put_u64_64bit(nl_skb, PSAMPLE_ATTR_TIMESTAMP,
46907e1a580SIdo Schimmel 				ktime_to_ns(tstamp), PSAMPLE_ATTR_PAD);
47007e1a580SIdo Schimmel 	if (unlikely(ret < 0))
47107e1a580SIdo Schimmel 		goto error;
47207e1a580SIdo Schimmel 
47307e1a580SIdo Schimmel 	ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_PROTO,
47407e1a580SIdo Schimmel 			  be16_to_cpu(skb->protocol));
47507e1a580SIdo Schimmel 	if (unlikely(ret < 0))
47607e1a580SIdo Schimmel 		goto error;
47707e1a580SIdo Schimmel 
4786ae0a628SYotam Gigi 	if (data_len) {
4796ae0a628SYotam Gigi 		int nla_len = nla_total_size(data_len);
4806ae0a628SYotam Gigi 		struct nlattr *nla;
4816ae0a628SYotam Gigi 
4824df864c1SJohannes Berg 		nla = skb_put(nl_skb, nla_len);
4836ae0a628SYotam Gigi 		nla->nla_type = PSAMPLE_ATTR_DATA;
4846ae0a628SYotam Gigi 		nla->nla_len = nla_attr_size(data_len);
4856ae0a628SYotam Gigi 
4866ae0a628SYotam Gigi 		if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
4876ae0a628SYotam Gigi 			goto error;
4886ae0a628SYotam Gigi 	}
4896ae0a628SYotam Gigi 
49007a7f308SRandy Dunlap #ifdef CONFIG_INET
491d8bed686SChris Mi 	if (tun_info) {
492d8bed686SChris Mi 		ret = psample_ip_tun_to_nlattr(nl_skb, tun_info);
493d8bed686SChris Mi 		if (unlikely(ret < 0))
494d8bed686SChris Mi 			goto error;
495d8bed686SChris Mi 	}
49607a7f308SRandy Dunlap #endif
497d8bed686SChris Mi 
498093b0f36SAdrian Moreno 	if (md->user_cookie && md->user_cookie_len &&
499093b0f36SAdrian Moreno 	    nla_put(nl_skb, PSAMPLE_ATTR_USER_COOKIE, md->user_cookie_len,
500093b0f36SAdrian Moreno 		    md->user_cookie))
501093b0f36SAdrian Moreno 		goto error;
502093b0f36SAdrian Moreno 
503*5eecd85cSAdrian Moreno 	if (md->rate_as_probability &&
504*5eecd85cSAdrian Moreno 	    nla_put_flag(nl_skb, PSAMPLE_ATTR_SAMPLE_PROBABILITY))
505*5eecd85cSAdrian Moreno 		goto error;
5067b1b2b60SAdrian Moreno 
5076ae0a628SYotam Gigi 	genlmsg_end(nl_skb, data);
5086ae0a628SYotam Gigi 	genlmsg_multicast_netns(&psample_nl_family, group->net, nl_skb, 0,
5096ae0a628SYotam Gigi 				PSAMPLE_NL_MCGRP_SAMPLE, GFP_ATOMIC);
5106ae0a628SYotam Gigi 
5116ae0a628SYotam Gigi 	return;
5126ae0a628SYotam Gigi error:
5136ae0a628SYotam Gigi 	pr_err_ratelimited("Could not create psample log message\n");
5146ae0a628SYotam Gigi 	nlmsg_free(nl_skb);
5156ae0a628SYotam Gigi }
5166ae0a628SYotam Gigi EXPORT_SYMBOL_GPL(psample_sample_packet);
5176ae0a628SYotam Gigi 
psample_module_init(void)5186ae0a628SYotam Gigi static int __init psample_module_init(void)
5196ae0a628SYotam Gigi {
5206ae0a628SYotam Gigi 	return genl_register_family(&psample_nl_family);
5216ae0a628SYotam Gigi }
5226ae0a628SYotam Gigi 
psample_module_exit(void)5236ae0a628SYotam Gigi static void __exit psample_module_exit(void)
5246ae0a628SYotam Gigi {
5256ae0a628SYotam Gigi 	genl_unregister_family(&psample_nl_family);
5266ae0a628SYotam Gigi }
5276ae0a628SYotam Gigi 
5286ae0a628SYotam Gigi module_init(psample_module_init);
5296ae0a628SYotam Gigi module_exit(psample_module_exit);
5306ae0a628SYotam Gigi 
531f1fd20c3SYotam Gigi MODULE_AUTHOR("Yotam Gigi <[email protected]>");
5326ae0a628SYotam Gigi MODULE_DESCRIPTION("netlink channel for packet sampling");
5336ae0a628SYotam Gigi MODULE_LICENSE("GPL v2");
534