diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/Kconfig | 1 | ||||
-rw-r--r-- | net/Makefile | 1 | ||||
-rw-r--r-- | net/psample/Kconfig | 15 | ||||
-rw-r--r-- | net/psample/Makefile | 5 | ||||
-rw-r--r-- | net/psample/psample.c | 301 |
5 files changed, 323 insertions, 0 deletions
diff --git a/net/Kconfig b/net/Kconfig index 92ae1500d9e1..ce4aee69fc0d 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -390,6 +390,7 @@ source "net/9p/Kconfig" | |||
390 | source "net/caif/Kconfig" | 390 | source "net/caif/Kconfig" |
391 | source "net/ceph/Kconfig" | 391 | source "net/ceph/Kconfig" |
392 | source "net/nfc/Kconfig" | 392 | source "net/nfc/Kconfig" |
393 | source "net/psample/Kconfig" | ||
393 | 394 | ||
394 | config LWTUNNEL | 395 | config LWTUNNEL |
395 | bool "Network light weight tunnels" | 396 | bool "Network light weight tunnels" |
diff --git a/net/Makefile b/net/Makefile index 5d6e0e5ff7f8..7d41de48310e 100644 --- a/net/Makefile +++ b/net/Makefile | |||
@@ -70,6 +70,7 @@ obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/ | |||
70 | obj-$(CONFIG_CEPH_LIB) += ceph/ | 70 | obj-$(CONFIG_CEPH_LIB) += ceph/ |
71 | obj-$(CONFIG_BATMAN_ADV) += batman-adv/ | 71 | obj-$(CONFIG_BATMAN_ADV) += batman-adv/ |
72 | obj-$(CONFIG_NFC) += nfc/ | 72 | obj-$(CONFIG_NFC) += nfc/ |
73 | obj-$(CONFIG_PSAMPLE) += psample/ | ||
73 | obj-$(CONFIG_OPENVSWITCH) += openvswitch/ | 74 | obj-$(CONFIG_OPENVSWITCH) += openvswitch/ |
74 | obj-$(CONFIG_VSOCKETS) += vmw_vsock/ | 75 | obj-$(CONFIG_VSOCKETS) += vmw_vsock/ |
75 | obj-$(CONFIG_MPLS) += mpls/ | 76 | obj-$(CONFIG_MPLS) += mpls/ |
diff --git a/net/psample/Kconfig b/net/psample/Kconfig new file mode 100644 index 000000000000..d850246a6059 --- /dev/null +++ b/net/psample/Kconfig | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # psample packet sampling configuration | ||
3 | # | ||
4 | |||
5 | menuconfig PSAMPLE | ||
6 | depends on NET | ||
7 | tristate "Packet-sampling netlink channel" | ||
8 | default n | ||
9 | help | ||
10 | Say Y here to add support for packet-sampling netlink channel | ||
11 | This netlink channel allows transferring packets alongside some | ||
12 | metadata to userspace. | ||
13 | |||
14 | To compile this support as a module, choose M here: the module will | ||
15 | be called psample. | ||
diff --git a/net/psample/Makefile b/net/psample/Makefile new file mode 100644 index 000000000000..609b0a79c9f3 --- /dev/null +++ b/net/psample/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for the psample netlink channel | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_PSAMPLE) += psample.o | ||
diff --git a/net/psample/psample.c b/net/psample/psample.c new file mode 100644 index 000000000000..8aa58a918783 --- /dev/null +++ b/net/psample/psample.c | |||
@@ -0,0 +1,301 @@ | |||
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 | |||
23 | static LIST_HEAD(psample_groups_list); | ||
24 | static DEFINE_SPINLOCK(psample_groups_lock); | ||
25 | |||
26 | /* multicast groups */ | ||
27 | enum psample_nl_multicast_groups { | ||
28 | PSAMPLE_NL_MCGRP_CONFIG, | ||
29 | PSAMPLE_NL_MCGRP_SAMPLE, | ||
30 | }; | ||
31 | |||
32 | static 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 | |||
37 | static struct genl_family psample_nl_family __ro_after_init; | ||
38 | |||
39 | static 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 | |||
66 | error: | ||
67 | genlmsg_cancel(msg, hdr); | ||
68 | return -EMSGSIZE; | ||
69 | } | ||
70 | |||
71 | static 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 | |||
100 | static 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 | |||
108 | static 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 | |||
120 | static 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 | |||
138 | static 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 | |||
155 | static 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 | |||
162 | static struct psample_group * | ||
163 | psample_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 | |||
173 | struct 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 | |||
187 | out: | ||
188 | spin_unlock(&psample_groups_lock); | ||
189 | return group; | ||
190 | } | ||
191 | EXPORT_SYMBOL_GPL(psample_group_get); | ||
192 | |||
193 | void 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 | } | ||
202 | EXPORT_SYMBOL_GPL(psample_group_put); | ||
203 | |||
204 | void 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 = (struct nlattr *)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; | ||
280 | error: | ||
281 | pr_err_ratelimited("Could not create psample log message\n"); | ||
282 | nlmsg_free(nl_skb); | ||
283 | } | ||
284 | EXPORT_SYMBOL_GPL(psample_sample_packet); | ||
285 | |||
286 | static int __init psample_module_init(void) | ||
287 | { | ||
288 | return genl_register_family(&psample_nl_family); | ||
289 | } | ||
290 | |||
291 | static void __exit psample_module_exit(void) | ||
292 | { | ||
293 | genl_unregister_family(&psample_nl_family); | ||
294 | } | ||
295 | |||
296 | module_init(psample_module_init); | ||
297 | module_exit(psample_module_exit); | ||
298 | |||
299 | MODULE_AUTHOR("Yotam Gigi <yotamg@mellanox.com>"); | ||
300 | MODULE_DESCRIPTION("netlink channel for packet sampling"); | ||
301 | MODULE_LICENSE("GPL v2"); | ||