18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/psample/psample.c - Netlink channel for packet sampling 48c2ecf20Sopenharmony_ci * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/types.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 128c2ecf20Sopenharmony_ci#include <net/sock.h> 138c2ecf20Sopenharmony_ci#include <net/netlink.h> 148c2ecf20Sopenharmony_ci#include <net/genetlink.h> 158c2ecf20Sopenharmony_ci#include <net/psample.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 188c2ecf20Sopenharmony_ci#include <net/dst_metadata.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define PSAMPLE_MAX_PACKET_SIZE 0xffff 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic LIST_HEAD(psample_groups_list); 238c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(psample_groups_lock); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* multicast groups */ 268c2ecf20Sopenharmony_cienum psample_nl_multicast_groups { 278c2ecf20Sopenharmony_ci PSAMPLE_NL_MCGRP_CONFIG, 288c2ecf20Sopenharmony_ci PSAMPLE_NL_MCGRP_SAMPLE, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic const struct genl_multicast_group psample_nl_mcgrps[] = { 328c2ecf20Sopenharmony_ci [PSAMPLE_NL_MCGRP_CONFIG] = { .name = PSAMPLE_NL_MCGRP_CONFIG_NAME }, 338c2ecf20Sopenharmony_ci [PSAMPLE_NL_MCGRP_SAMPLE] = { .name = PSAMPLE_NL_MCGRP_SAMPLE_NAME, 348c2ecf20Sopenharmony_ci .flags = GENL_UNS_ADMIN_PERM }, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct genl_family psample_nl_family __ro_after_init; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int psample_group_nl_fill(struct sk_buff *msg, 408c2ecf20Sopenharmony_ci struct psample_group *group, 418c2ecf20Sopenharmony_ci enum psample_command cmd, u32 portid, u32 seq, 428c2ecf20Sopenharmony_ci int flags) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci void *hdr; 458c2ecf20Sopenharmony_ci int ret; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &psample_nl_family, flags, cmd); 488c2ecf20Sopenharmony_ci if (!hdr) 498c2ecf20Sopenharmony_ci return -EMSGSIZE; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = nla_put_u32(msg, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num); 528c2ecf20Sopenharmony_ci if (ret < 0) 538c2ecf20Sopenharmony_ci goto error; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_REFCOUNT, group->refcount); 568c2ecf20Sopenharmony_ci if (ret < 0) 578c2ecf20Sopenharmony_ci goto error; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ret = nla_put_u32(msg, PSAMPLE_ATTR_GROUP_SEQ, group->seq); 608c2ecf20Sopenharmony_ci if (ret < 0) 618c2ecf20Sopenharmony_ci goto error; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci genlmsg_end(msg, hdr); 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cierror: 678c2ecf20Sopenharmony_ci genlmsg_cancel(msg, hdr); 688c2ecf20Sopenharmony_ci return -EMSGSIZE; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int psample_nl_cmd_get_group_dumpit(struct sk_buff *msg, 728c2ecf20Sopenharmony_ci struct netlink_callback *cb) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct psample_group *group; 758c2ecf20Sopenharmony_ci int start = cb->args[0]; 768c2ecf20Sopenharmony_ci int idx = 0; 778c2ecf20Sopenharmony_ci int err; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci spin_lock_bh(&psample_groups_lock); 808c2ecf20Sopenharmony_ci list_for_each_entry(group, &psample_groups_list, list) { 818c2ecf20Sopenharmony_ci if (!net_eq(group->net, sock_net(msg->sk))) 828c2ecf20Sopenharmony_ci continue; 838c2ecf20Sopenharmony_ci if (idx < start) { 848c2ecf20Sopenharmony_ci idx++; 858c2ecf20Sopenharmony_ci continue; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci err = psample_group_nl_fill(msg, group, PSAMPLE_CMD_NEW_GROUP, 888c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 898c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI); 908c2ecf20Sopenharmony_ci if (err) 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci idx++; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_unlock_bh(&psample_groups_lock); 968c2ecf20Sopenharmony_ci cb->args[0] = idx; 978c2ecf20Sopenharmony_ci return msg->len; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const struct genl_small_ops psample_nl_ops[] = { 1018c2ecf20Sopenharmony_ci { 1028c2ecf20Sopenharmony_ci .cmd = PSAMPLE_CMD_GET_GROUP, 1038c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 1048c2ecf20Sopenharmony_ci .dumpit = psample_nl_cmd_get_group_dumpit, 1058c2ecf20Sopenharmony_ci /* can be retrieved by unprivileged users */ 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct genl_family psample_nl_family __ro_after_init = { 1108c2ecf20Sopenharmony_ci .name = PSAMPLE_GENL_NAME, 1118c2ecf20Sopenharmony_ci .version = PSAMPLE_GENL_VERSION, 1128c2ecf20Sopenharmony_ci .maxattr = PSAMPLE_ATTR_MAX, 1138c2ecf20Sopenharmony_ci .netnsok = true, 1148c2ecf20Sopenharmony_ci .module = THIS_MODULE, 1158c2ecf20Sopenharmony_ci .mcgrps = psample_nl_mcgrps, 1168c2ecf20Sopenharmony_ci .small_ops = psample_nl_ops, 1178c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(psample_nl_ops), 1188c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(psample_nl_mcgrps), 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void psample_group_notify(struct psample_group *group, 1228c2ecf20Sopenharmony_ci enum psample_command cmd) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct sk_buff *msg; 1258c2ecf20Sopenharmony_ci int err; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 1288c2ecf20Sopenharmony_ci if (!msg) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci err = psample_group_nl_fill(msg, group, cmd, 0, 0, NLM_F_MULTI); 1328c2ecf20Sopenharmony_ci if (!err) 1338c2ecf20Sopenharmony_ci genlmsg_multicast_netns(&psample_nl_family, group->net, msg, 0, 1348c2ecf20Sopenharmony_ci PSAMPLE_NL_MCGRP_CONFIG, GFP_ATOMIC); 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci nlmsg_free(msg); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct psample_group *psample_group_create(struct net *net, 1408c2ecf20Sopenharmony_ci u32 group_num) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct psample_group *group; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci group = kzalloc(sizeof(*group), GFP_ATOMIC); 1458c2ecf20Sopenharmony_ci if (!group) 1468c2ecf20Sopenharmony_ci return NULL; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci group->net = net; 1498c2ecf20Sopenharmony_ci group->group_num = group_num; 1508c2ecf20Sopenharmony_ci list_add_tail(&group->list, &psample_groups_list); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci psample_group_notify(group, PSAMPLE_CMD_NEW_GROUP); 1538c2ecf20Sopenharmony_ci return group; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void psample_group_destroy(struct psample_group *group) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci psample_group_notify(group, PSAMPLE_CMD_DEL_GROUP); 1598c2ecf20Sopenharmony_ci list_del(&group->list); 1608c2ecf20Sopenharmony_ci kfree_rcu(group, rcu); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct psample_group * 1648c2ecf20Sopenharmony_cipsample_group_lookup(struct net *net, u32 group_num) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct psample_group *group; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci list_for_each_entry(group, &psample_groups_list, list) 1698c2ecf20Sopenharmony_ci if ((group->group_num == group_num) && (group->net == net)) 1708c2ecf20Sopenharmony_ci return group; 1718c2ecf20Sopenharmony_ci return NULL; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistruct psample_group *psample_group_get(struct net *net, u32 group_num) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct psample_group *group; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci spin_lock_bh(&psample_groups_lock); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci group = psample_group_lookup(net, group_num); 1818c2ecf20Sopenharmony_ci if (!group) { 1828c2ecf20Sopenharmony_ci group = psample_group_create(net, group_num); 1838c2ecf20Sopenharmony_ci if (!group) 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci group->refcount++; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciout: 1898c2ecf20Sopenharmony_ci spin_unlock_bh(&psample_groups_lock); 1908c2ecf20Sopenharmony_ci return group; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psample_group_get); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid psample_group_take(struct psample_group *group) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci spin_lock_bh(&psample_groups_lock); 1978c2ecf20Sopenharmony_ci group->refcount++; 1988c2ecf20Sopenharmony_ci spin_unlock_bh(&psample_groups_lock); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psample_group_take); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_civoid psample_group_put(struct psample_group *group) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci spin_lock_bh(&psample_groups_lock); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (--group->refcount == 0) 2078c2ecf20Sopenharmony_ci psample_group_destroy(group); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci spin_unlock_bh(&psample_groups_lock); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psample_group_put); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 2148c2ecf20Sopenharmony_cistatic int __psample_ip_tun_to_nlattr(struct sk_buff *skb, 2158c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci unsigned short tun_proto = ip_tunnel_info_af(tun_info); 2188c2ecf20Sopenharmony_ci const void *tun_opts = ip_tunnel_info_opts(tun_info); 2198c2ecf20Sopenharmony_ci const struct ip_tunnel_key *tun_key = &tun_info->key; 2208c2ecf20Sopenharmony_ci int tun_opts_len = tun_info->options_len; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_KEY && 2238c2ecf20Sopenharmony_ci nla_put_be64(skb, PSAMPLE_TUNNEL_KEY_ATTR_ID, tun_key->tun_id, 2248c2ecf20Sopenharmony_ci PSAMPLE_TUNNEL_KEY_ATTR_PAD)) 2258c2ecf20Sopenharmony_ci return -EMSGSIZE; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (tun_info->mode & IP_TUNNEL_INFO_BRIDGE && 2288c2ecf20Sopenharmony_ci nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE)) 2298c2ecf20Sopenharmony_ci return -EMSGSIZE; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci switch (tun_proto) { 2328c2ecf20Sopenharmony_ci case AF_INET: 2338c2ecf20Sopenharmony_ci if (tun_key->u.ipv4.src && 2348c2ecf20Sopenharmony_ci nla_put_in_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_SRC, 2358c2ecf20Sopenharmony_ci tun_key->u.ipv4.src)) 2368c2ecf20Sopenharmony_ci return -EMSGSIZE; 2378c2ecf20Sopenharmony_ci if (tun_key->u.ipv4.dst && 2388c2ecf20Sopenharmony_ci nla_put_in_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV4_DST, 2398c2ecf20Sopenharmony_ci tun_key->u.ipv4.dst)) 2408c2ecf20Sopenharmony_ci return -EMSGSIZE; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case AF_INET6: 2438c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&tun_key->u.ipv6.src) && 2448c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV6_SRC, 2458c2ecf20Sopenharmony_ci &tun_key->u.ipv6.src)) 2468c2ecf20Sopenharmony_ci return -EMSGSIZE; 2478c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&tun_key->u.ipv6.dst) && 2488c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, PSAMPLE_TUNNEL_KEY_ATTR_IPV6_DST, 2498c2ecf20Sopenharmony_ci &tun_key->u.ipv6.dst)) 2508c2ecf20Sopenharmony_ci return -EMSGSIZE; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci if (tun_key->tos && 2548c2ecf20Sopenharmony_ci nla_put_u8(skb, PSAMPLE_TUNNEL_KEY_ATTR_TOS, tun_key->tos)) 2558c2ecf20Sopenharmony_ci return -EMSGSIZE; 2568c2ecf20Sopenharmony_ci if (nla_put_u8(skb, PSAMPLE_TUNNEL_KEY_ATTR_TTL, tun_key->ttl)) 2578c2ecf20Sopenharmony_ci return -EMSGSIZE; 2588c2ecf20Sopenharmony_ci if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) && 2598c2ecf20Sopenharmony_ci nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) 2608c2ecf20Sopenharmony_ci return -EMSGSIZE; 2618c2ecf20Sopenharmony_ci if ((tun_key->tun_flags & TUNNEL_CSUM) && 2628c2ecf20Sopenharmony_ci nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_CSUM)) 2638c2ecf20Sopenharmony_ci return -EMSGSIZE; 2648c2ecf20Sopenharmony_ci if (tun_key->tp_src && 2658c2ecf20Sopenharmony_ci nla_put_be16(skb, PSAMPLE_TUNNEL_KEY_ATTR_TP_SRC, tun_key->tp_src)) 2668c2ecf20Sopenharmony_ci return -EMSGSIZE; 2678c2ecf20Sopenharmony_ci if (tun_key->tp_dst && 2688c2ecf20Sopenharmony_ci nla_put_be16(skb, PSAMPLE_TUNNEL_KEY_ATTR_TP_DST, tun_key->tp_dst)) 2698c2ecf20Sopenharmony_ci return -EMSGSIZE; 2708c2ecf20Sopenharmony_ci if ((tun_key->tun_flags & TUNNEL_OAM) && 2718c2ecf20Sopenharmony_ci nla_put_flag(skb, PSAMPLE_TUNNEL_KEY_ATTR_OAM)) 2728c2ecf20Sopenharmony_ci return -EMSGSIZE; 2738c2ecf20Sopenharmony_ci if (tun_opts_len) { 2748c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_GENEVE_OPT && 2758c2ecf20Sopenharmony_ci nla_put(skb, PSAMPLE_TUNNEL_KEY_ATTR_GENEVE_OPTS, 2768c2ecf20Sopenharmony_ci tun_opts_len, tun_opts)) 2778c2ecf20Sopenharmony_ci return -EMSGSIZE; 2788c2ecf20Sopenharmony_ci else if (tun_key->tun_flags & TUNNEL_ERSPAN_OPT && 2798c2ecf20Sopenharmony_ci nla_put(skb, PSAMPLE_TUNNEL_KEY_ATTR_ERSPAN_OPTS, 2808c2ecf20Sopenharmony_ci tun_opts_len, tun_opts)) 2818c2ecf20Sopenharmony_ci return -EMSGSIZE; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int psample_ip_tun_to_nlattr(struct sk_buff *skb, 2888c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct nlattr *nla; 2918c2ecf20Sopenharmony_ci int err; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci nla = nla_nest_start_noflag(skb, PSAMPLE_ATTR_TUNNEL); 2948c2ecf20Sopenharmony_ci if (!nla) 2958c2ecf20Sopenharmony_ci return -EMSGSIZE; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci err = __psample_ip_tun_to_nlattr(skb, tun_info); 2988c2ecf20Sopenharmony_ci if (err) { 2998c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nla); 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci nla_nest_end(skb, nla); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int psample_tunnel_meta_len(struct ip_tunnel_info *tun_info) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci unsigned short tun_proto = ip_tunnel_info_af(tun_info); 3118c2ecf20Sopenharmony_ci const struct ip_tunnel_key *tun_key = &tun_info->key; 3128c2ecf20Sopenharmony_ci int tun_opts_len = tun_info->options_len; 3138c2ecf20Sopenharmony_ci int sum = nla_total_size(0); /* PSAMPLE_ATTR_TUNNEL */ 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_KEY) 3168c2ecf20Sopenharmony_ci sum += nla_total_size_64bit(sizeof(u64)); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (tun_info->mode & IP_TUNNEL_INFO_BRIDGE) 3198c2ecf20Sopenharmony_ci sum += nla_total_size(0); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci switch (tun_proto) { 3228c2ecf20Sopenharmony_ci case AF_INET: 3238c2ecf20Sopenharmony_ci if (tun_key->u.ipv4.src) 3248c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u32)); 3258c2ecf20Sopenharmony_ci if (tun_key->u.ipv4.dst) 3268c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u32)); 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case AF_INET6: 3298c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&tun_key->u.ipv6.src)) 3308c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(struct in6_addr)); 3318c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&tun_key->u.ipv6.dst)) 3328c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(struct in6_addr)); 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (tun_key->tos) 3368c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u8)); 3378c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u8)); /* TTL */ 3388c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) 3398c2ecf20Sopenharmony_ci sum += nla_total_size(0); 3408c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_CSUM) 3418c2ecf20Sopenharmony_ci sum += nla_total_size(0); 3428c2ecf20Sopenharmony_ci if (tun_key->tp_src) 3438c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u16)); 3448c2ecf20Sopenharmony_ci if (tun_key->tp_dst) 3458c2ecf20Sopenharmony_ci sum += nla_total_size(sizeof(u16)); 3468c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_OAM) 3478c2ecf20Sopenharmony_ci sum += nla_total_size(0); 3488c2ecf20Sopenharmony_ci if (tun_opts_len) { 3498c2ecf20Sopenharmony_ci if (tun_key->tun_flags & TUNNEL_GENEVE_OPT) 3508c2ecf20Sopenharmony_ci sum += nla_total_size(tun_opts_len); 3518c2ecf20Sopenharmony_ci else if (tun_key->tun_flags & TUNNEL_ERSPAN_OPT) 3528c2ecf20Sopenharmony_ci sum += nla_total_size(tun_opts_len); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return sum; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci#endif 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_civoid psample_sample_packet(struct psample_group *group, struct sk_buff *skb, 3608c2ecf20Sopenharmony_ci u32 trunc_size, int in_ifindex, int out_ifindex, 3618c2ecf20Sopenharmony_ci u32 sample_rate) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 3648c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info; 3658c2ecf20Sopenharmony_ci#endif 3668c2ecf20Sopenharmony_ci struct sk_buff *nl_skb; 3678c2ecf20Sopenharmony_ci int data_len; 3688c2ecf20Sopenharmony_ci int meta_len; 3698c2ecf20Sopenharmony_ci void *data; 3708c2ecf20Sopenharmony_ci int ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci meta_len = (in_ifindex ? nla_total_size(sizeof(u16)) : 0) + 3738c2ecf20Sopenharmony_ci (out_ifindex ? nla_total_size(sizeof(u16)) : 0) + 3748c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)) + /* sample_rate */ 3758c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)) + /* orig_size */ 3768c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)) + /* group_num */ 3778c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)); /* seq */ 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 3808c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info(skb); 3818c2ecf20Sopenharmony_ci if (tun_info) 3828c2ecf20Sopenharmony_ci meta_len += psample_tunnel_meta_len(tun_info); 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci data_len = min(skb->len, trunc_size); 3868c2ecf20Sopenharmony_ci if (meta_len + nla_total_size(data_len) > PSAMPLE_MAX_PACKET_SIZE) 3878c2ecf20Sopenharmony_ci data_len = PSAMPLE_MAX_PACKET_SIZE - meta_len - NLA_HDRLEN 3888c2ecf20Sopenharmony_ci - NLA_ALIGNTO; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci nl_skb = genlmsg_new(meta_len + nla_total_size(data_len), GFP_ATOMIC); 3918c2ecf20Sopenharmony_ci if (unlikely(!nl_skb)) 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci data = genlmsg_put(nl_skb, 0, 0, &psample_nl_family, 0, 3958c2ecf20Sopenharmony_ci PSAMPLE_CMD_SAMPLE); 3968c2ecf20Sopenharmony_ci if (unlikely(!data)) 3978c2ecf20Sopenharmony_ci goto error; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (in_ifindex) { 4008c2ecf20Sopenharmony_ci ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_IIFINDEX, in_ifindex); 4018c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4028c2ecf20Sopenharmony_ci goto error; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (out_ifindex) { 4068c2ecf20Sopenharmony_ci ret = nla_put_u16(nl_skb, PSAMPLE_ATTR_OIFINDEX, out_ifindex); 4078c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4088c2ecf20Sopenharmony_ci goto error; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_RATE, sample_rate); 4128c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4138c2ecf20Sopenharmony_ci goto error; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_ORIGSIZE, skb->len); 4168c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4178c2ecf20Sopenharmony_ci goto error; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_SAMPLE_GROUP, group->group_num); 4208c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4218c2ecf20Sopenharmony_ci goto error; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = nla_put_u32(nl_skb, PSAMPLE_ATTR_GROUP_SEQ, group->seq++); 4248c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4258c2ecf20Sopenharmony_ci goto error; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (data_len) { 4288c2ecf20Sopenharmony_ci int nla_len = nla_total_size(data_len); 4298c2ecf20Sopenharmony_ci struct nlattr *nla; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci nla = skb_put(nl_skb, nla_len); 4328c2ecf20Sopenharmony_ci nla->nla_type = PSAMPLE_ATTR_DATA; 4338c2ecf20Sopenharmony_ci nla->nla_len = nla_attr_size(data_len); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) 4368c2ecf20Sopenharmony_ci goto error; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 4408c2ecf20Sopenharmony_ci if (tun_info) { 4418c2ecf20Sopenharmony_ci ret = psample_ip_tun_to_nlattr(nl_skb, tun_info); 4428c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4438c2ecf20Sopenharmony_ci goto error; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci#endif 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci genlmsg_end(nl_skb, data); 4488c2ecf20Sopenharmony_ci genlmsg_multicast_netns(&psample_nl_family, group->net, nl_skb, 0, 4498c2ecf20Sopenharmony_ci PSAMPLE_NL_MCGRP_SAMPLE, GFP_ATOMIC); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return; 4528c2ecf20Sopenharmony_cierror: 4538c2ecf20Sopenharmony_ci pr_err_ratelimited("Could not create psample log message\n"); 4548c2ecf20Sopenharmony_ci nlmsg_free(nl_skb); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psample_sample_packet); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int __init psample_module_init(void) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci return genl_register_family(&psample_nl_family); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void __exit psample_module_exit(void) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci genl_unregister_family(&psample_nl_family); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cimodule_init(psample_module_init); 4698c2ecf20Sopenharmony_cimodule_exit(psample_module_exit); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yotam Gigi <yotam.gi@gmail.com>"); 4728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("netlink channel for packet sampling"); 4738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 474