blob: b1fcfa08f0b485cd8b90ecf32b9a5aeb6c622d0f [file] [log] [blame]
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +02001/*
2 * (C) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation (or any later at your option).
7 *
8 * This software has been sponsored by Vyatta Inc. <http://www.vyatta.com>
9 */
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/skbuff.h>
14#include <linux/netlink.h>
15#include <linux/rculist.h>
16#include <linux/slab.h>
17#include <linux/types.h>
18#include <linux/list.h>
19#include <linux/errno.h>
20#include <net/netlink.h>
21#include <net/sock.h>
22
23#include <net/netfilter/nf_conntrack_helper.h>
24#include <net/netfilter/nf_conntrack_expect.h>
25#include <net/netfilter/nf_conntrack_ecache.h>
26
27#include <linux/netfilter/nfnetlink.h>
28#include <linux/netfilter/nfnetlink_conntrack.h>
29#include <linux/netfilter/nfnetlink_cthelper.h>
30
31MODULE_LICENSE("GPL");
32MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
33MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers");
34
35static int
36nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
37 struct nf_conn *ct, enum ip_conntrack_info ctinfo)
38{
39 const struct nf_conn_help *help;
40 struct nf_conntrack_helper *helper;
41
42 help = nfct_help(ct);
43 if (help == NULL)
44 return NF_DROP;
45
Aaron Conolee2361cb2016-09-21 11:35:04 -040046 /* rcu_read_lock()ed by nf_hook_thresh */
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020047 helper = rcu_dereference(help->helper);
48 if (helper == NULL)
49 return NF_DROP;
50
51 /* This is an user-space helper not yet configured, skip. */
52 if ((helper->flags &
53 (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) ==
54 NF_CT_HELPER_F_USERSPACE)
55 return NF_ACCEPT;
56
57 /* If the user-space helper is not available, don't block traffic. */
58 return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS;
59}
60
61static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = {
62 [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, },
63 [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, },
64};
65
66static int
67nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
68 const struct nlattr *attr)
69{
Daniel Borkmann130ffbc2013-06-12 17:54:51 +020070 int err;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020071 struct nlattr *tb[NFCTH_TUPLE_MAX+1];
72
Daniel Borkmann130ffbc2013-06-12 17:54:51 +020073 err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
74 if (err < 0)
75 return err;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020076
77 if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
78 return -EINVAL;
79
Ian Wilson78146572015-03-12 09:37:58 +000080 /* Not all fields are initialized so first zero the tuple */
81 memset(tuple, 0, sizeof(struct nf_conntrack_tuple));
82
Patrick McHardyfe31d1a2012-08-19 10:16:09 +000083 tuple->src.l3num = ntohs(nla_get_be16(tb[NFCTH_TUPLE_L3PROTONUM]));
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020084 tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]);
85
86 return 0;
87}
88
89static int
90nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
91{
Chen Gangb18c5d12014-12-24 23:04:54 +080092 struct nf_conn_help *help = nfct_help(ct);
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020093
Pablo Neira Ayuso7be54ca2012-09-21 16:52:08 +020094 if (attr == NULL)
95 return -EINVAL;
96
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +020097 if (help->helper->data_len == 0)
98 return -EINVAL;
99
Chen Gangb18c5d12014-12-24 23:04:54 +0800100 memcpy(help->data, nla_data(attr), help->helper->data_len);
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200101 return 0;
102}
103
104static int
105nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct)
106{
107 const struct nf_conn_help *help = nfct_help(ct);
108
109 if (help->helper->data_len &&
110 nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data))
111 goto nla_put_failure;
112
113 return 0;
114
115nla_put_failure:
116 return -ENOSPC;
117}
118
119static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = {
120 [NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING,
121 .len = NF_CT_HELPER_NAME_LEN-1 },
122 [NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, },
123 [NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, },
124};
125
126static int
127nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
128 const struct nlattr *attr)
129{
Daniel Borkmann130ffbc2013-06-12 17:54:51 +0200130 int err;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200131 struct nlattr *tb[NFCTH_POLICY_MAX+1];
132
Daniel Borkmann130ffbc2013-06-12 17:54:51 +0200133 err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
134 if (err < 0)
135 return err;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200136
137 if (!tb[NFCTH_POLICY_NAME] ||
138 !tb[NFCTH_POLICY_EXPECT_MAX] ||
139 !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
140 return -EINVAL;
141
142 strncpy(expect_policy->name,
143 nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
144 expect_policy->max_expected =
145 ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
146 expect_policy->timeout =
147 ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
148
149 return 0;
150}
151
152static const struct nla_policy
153nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = {
154 [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, },
155};
156
157static int
158nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
159 const struct nlattr *attr)
160{
161 int i, ret;
162 struct nf_conntrack_expect_policy *expect_policy;
163 struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
Liping Zhangcd402b82017-03-19 22:35:59 +0800164 unsigned int class_max;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200165
Daniel Borkmann130ffbc2013-06-12 17:54:51 +0200166 ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
167 nfnl_cthelper_expect_policy_set);
168 if (ret < 0)
169 return ret;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200170
171 if (!tb[NFCTH_POLICY_SET_NUM])
172 return -EINVAL;
173
Liping Zhangcd402b82017-03-19 22:35:59 +0800174 class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
175 if (class_max == 0)
176 return -EINVAL;
177 if (class_max > NF_CT_MAX_EXPECT_CLASSES)
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200178 return -EOVERFLOW;
179
180 expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
Liping Zhangcd402b82017-03-19 22:35:59 +0800181 class_max, GFP_KERNEL);
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200182 if (expect_policy == NULL)
183 return -ENOMEM;
184
Liping Zhangcd402b82017-03-19 22:35:59 +0800185 for (i = 0; i < class_max; i++) {
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200186 if (!tb[NFCTH_POLICY_SET+i])
187 goto err;
188
189 ret = nfnl_cthelper_expect_policy(&expect_policy[i],
190 tb[NFCTH_POLICY_SET+i]);
191 if (ret < 0)
192 goto err;
193 }
Liping Zhangcd402b82017-03-19 22:35:59 +0800194
195 helper->expect_class_max = class_max - 1;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200196 helper->expect_policy = expect_policy;
197 return 0;
198err:
199 kfree(expect_policy);
200 return -EINVAL;
201}
202
203static int
204nfnl_cthelper_create(const struct nlattr * const tb[],
205 struct nf_conntrack_tuple *tuple)
206{
207 struct nf_conntrack_helper *helper;
208 int ret;
209
210 if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
211 return -EINVAL;
212
213 helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL);
214 if (helper == NULL)
215 return -ENOMEM;
216
217 ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
218 if (ret < 0)
219 goto err;
220
221 strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
222 helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
223 helper->flags |= NF_CT_HELPER_F_USERSPACE;
224 memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
225
226 helper->me = THIS_MODULE;
227 helper->help = nfnl_userspace_cthelper;
228 helper->from_nlattr = nfnl_cthelper_from_nlattr;
229 helper->to_nlattr = nfnl_cthelper_to_nlattr;
230
231 /* Default to queue number zero, this can be updated at any time. */
232 if (tb[NFCTH_QUEUE_NUM])
233 helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
234
235 if (tb[NFCTH_STATUS]) {
236 int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
237
238 switch(status) {
239 case NFCT_HELPER_STATUS_ENABLED:
240 helper->flags |= NF_CT_HELPER_F_CONFIGURED;
241 break;
242 case NFCT_HELPER_STATUS_DISABLED:
243 helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
244 break;
245 }
246 }
247
248 ret = nf_conntrack_helper_register(helper);
249 if (ret < 0)
250 goto err;
251
252 return 0;
253err:
254 kfree(helper);
255 return ret;
256}
257
258static int
259nfnl_cthelper_update(const struct nlattr * const tb[],
260 struct nf_conntrack_helper *helper)
261{
262 int ret;
263
264 if (tb[NFCTH_PRIV_DATA_LEN])
265 return -EBUSY;
266
267 if (tb[NFCTH_POLICY]) {
268 ret = nfnl_cthelper_parse_expect_policy(helper,
269 tb[NFCTH_POLICY]);
270 if (ret < 0)
271 return ret;
272 }
273 if (tb[NFCTH_QUEUE_NUM])
274 helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
275
276 if (tb[NFCTH_STATUS]) {
277 int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
278
279 switch(status) {
280 case NFCT_HELPER_STATUS_ENABLED:
281 helper->flags |= NF_CT_HELPER_F_CONFIGURED;
282 break;
283 case NFCT_HELPER_STATUS_DISABLED:
284 helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
285 break;
286 }
287 }
288 return 0;
289}
290
Pablo Neira Ayuso7b8002a2015-12-15 18:41:56 +0100291static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
292 struct sk_buff *skb, const struct nlmsghdr *nlh,
293 const struct nlattr * const tb[])
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200294{
295 const char *helper_name;
296 struct nf_conntrack_helper *cur, *helper = NULL;
297 struct nf_conntrack_tuple tuple;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200298 int ret = 0, i;
299
300 if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
301 return -EINVAL;
302
303 helper_name = nla_data(tb[NFCTH_NAME]);
304
305 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
306 if (ret < 0)
307 return ret;
308
309 rcu_read_lock();
310 for (i = 0; i < nf_ct_helper_hsize && !helper; i++) {
Sasha Levinb67bfe02013-02-27 17:06:00 -0800311 hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200312
313 /* skip non-userspace conntrack helpers. */
314 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
315 continue;
316
317 if (strncmp(cur->name, helper_name,
318 NF_CT_HELPER_NAME_LEN) != 0)
319 continue;
320
321 if ((tuple.src.l3num != cur->tuple.src.l3num ||
322 tuple.dst.protonum != cur->tuple.dst.protonum))
323 continue;
324
325 if (nlh->nlmsg_flags & NLM_F_EXCL) {
326 ret = -EEXIST;
327 goto err;
328 }
329 helper = cur;
330 break;
331 }
332 }
333 rcu_read_unlock();
334
335 if (helper == NULL)
336 ret = nfnl_cthelper_create(tb, &tuple);
337 else
338 ret = nfnl_cthelper_update(tb, helper);
339
340 return ret;
341err:
342 rcu_read_unlock();
343 return ret;
344}
345
346static int
347nfnl_cthelper_dump_tuple(struct sk_buff *skb,
348 struct nf_conntrack_helper *helper)
349{
350 struct nlattr *nest_parms;
351
352 nest_parms = nla_nest_start(skb, NFCTH_TUPLE | NLA_F_NESTED);
353 if (nest_parms == NULL)
354 goto nla_put_failure;
355
356 if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM,
357 htons(helper->tuple.src.l3num)))
358 goto nla_put_failure;
359
360 if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum))
361 goto nla_put_failure;
362
363 nla_nest_end(skb, nest_parms);
364 return 0;
365
366nla_put_failure:
367 return -1;
368}
369
370static int
371nfnl_cthelper_dump_policy(struct sk_buff *skb,
372 struct nf_conntrack_helper *helper)
373{
374 int i;
375 struct nlattr *nest_parms1, *nest_parms2;
376
377 nest_parms1 = nla_nest_start(skb, NFCTH_POLICY | NLA_F_NESTED);
378 if (nest_parms1 == NULL)
379 goto nla_put_failure;
380
381 if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
Liping Zhangcd402b82017-03-19 22:35:59 +0800382 htonl(helper->expect_class_max + 1)))
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200383 goto nla_put_failure;
384
Liping Zhangcd402b82017-03-19 22:35:59 +0800385 for (i = 0; i < helper->expect_class_max + 1; i++) {
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200386 nest_parms2 = nla_nest_start(skb,
387 (NFCTH_POLICY_SET+i) | NLA_F_NESTED);
388 if (nest_parms2 == NULL)
389 goto nla_put_failure;
390
391 if (nla_put_string(skb, NFCTH_POLICY_NAME,
392 helper->expect_policy[i].name))
393 goto nla_put_failure;
394
395 if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX,
396 htonl(helper->expect_policy[i].max_expected)))
397 goto nla_put_failure;
398
399 if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT,
400 htonl(helper->expect_policy[i].timeout)))
401 goto nla_put_failure;
402
403 nla_nest_end(skb, nest_parms2);
404 }
405 nla_nest_end(skb, nest_parms1);
406 return 0;
407
408nla_put_failure:
409 return -1;
410}
411
412static int
Eric W. Biederman15e47302012-09-07 20:12:54 +0000413nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200414 int event, struct nf_conntrack_helper *helper)
415{
416 struct nlmsghdr *nlh;
417 struct nfgenmsg *nfmsg;
Eric W. Biederman15e47302012-09-07 20:12:54 +0000418 unsigned int flags = portid ? NLM_F_MULTI : 0;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200419 int status;
420
421 event |= NFNL_SUBSYS_CTHELPER << 8;
Eric W. Biederman15e47302012-09-07 20:12:54 +0000422 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200423 if (nlh == NULL)
424 goto nlmsg_failure;
425
426 nfmsg = nlmsg_data(nlh);
427 nfmsg->nfgen_family = AF_UNSPEC;
428 nfmsg->version = NFNETLINK_V0;
429 nfmsg->res_id = 0;
430
431 if (nla_put_string(skb, NFCTH_NAME, helper->name))
432 goto nla_put_failure;
433
434 if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num)))
435 goto nla_put_failure;
436
437 if (nfnl_cthelper_dump_tuple(skb, helper) < 0)
438 goto nla_put_failure;
439
440 if (nfnl_cthelper_dump_policy(skb, helper) < 0)
441 goto nla_put_failure;
442
443 if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len)))
444 goto nla_put_failure;
445
446 if (helper->flags & NF_CT_HELPER_F_CONFIGURED)
447 status = NFCT_HELPER_STATUS_ENABLED;
448 else
449 status = NFCT_HELPER_STATUS_DISABLED;
450
451 if (nla_put_be32(skb, NFCTH_STATUS, htonl(status)))
452 goto nla_put_failure;
453
454 nlmsg_end(skb, nlh);
455 return skb->len;
456
457nlmsg_failure:
458nla_put_failure:
459 nlmsg_cancel(skb, nlh);
460 return -1;
461}
462
463static int
464nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
465{
466 struct nf_conntrack_helper *cur, *last;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200467
468 rcu_read_lock();
469 last = (struct nf_conntrack_helper *)cb->args[1];
470 for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) {
471restart:
Sasha Levinb67bfe02013-02-27 17:06:00 -0800472 hlist_for_each_entry_rcu(cur,
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200473 &nf_ct_helper_hash[cb->args[0]], hnode) {
474
475 /* skip non-userspace conntrack helpers. */
476 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
477 continue;
478
479 if (cb->args[1]) {
480 if (cur != last)
481 continue;
482 cb->args[1] = 0;
483 }
484 if (nfnl_cthelper_fill_info(skb,
Eric W. Biederman15e47302012-09-07 20:12:54 +0000485 NETLINK_CB(cb->skb).portid,
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200486 cb->nlh->nlmsg_seq,
487 NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
488 NFNL_MSG_CTHELPER_NEW, cur) < 0) {
489 cb->args[1] = (unsigned long)cur;
490 goto out;
491 }
492 }
493 }
494 if (cb->args[1]) {
495 cb->args[1] = 0;
496 goto restart;
497 }
498out:
499 rcu_read_unlock();
500 return skb->len;
501}
502
Pablo Neira Ayuso7b8002a2015-12-15 18:41:56 +0100503static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
504 struct sk_buff *skb, const struct nlmsghdr *nlh,
505 const struct nlattr * const tb[])
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200506{
507 int ret = -ENOENT, i;
508 struct nf_conntrack_helper *cur;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200509 struct sk_buff *skb2;
510 char *helper_name = NULL;
511 struct nf_conntrack_tuple tuple;
512 bool tuple_set = false;
513
514 if (nlh->nlmsg_flags & NLM_F_DUMP) {
515 struct netlink_dump_control c = {
516 .dump = nfnl_cthelper_dump_table,
517 };
518 return netlink_dump_start(nfnl, skb, nlh, &c);
519 }
520
521 if (tb[NFCTH_NAME])
522 helper_name = nla_data(tb[NFCTH_NAME]);
523
524 if (tb[NFCTH_TUPLE]) {
525 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
526 if (ret < 0)
527 return ret;
528
529 tuple_set = true;
530 }
531
532 for (i = 0; i < nf_ct_helper_hsize; i++) {
Sasha Levinb67bfe02013-02-27 17:06:00 -0800533 hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200534
535 /* skip non-userspace conntrack helpers. */
536 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
537 continue;
538
539 if (helper_name && strncmp(cur->name, helper_name,
540 NF_CT_HELPER_NAME_LEN) != 0) {
541 continue;
542 }
543 if (tuple_set &&
544 (tuple.src.l3num != cur->tuple.src.l3num ||
545 tuple.dst.protonum != cur->tuple.dst.protonum))
546 continue;
547
548 skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
549 if (skb2 == NULL) {
550 ret = -ENOMEM;
551 break;
552 }
553
Eric W. Biederman15e47302012-09-07 20:12:54 +0000554 ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid,
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200555 nlh->nlmsg_seq,
556 NFNL_MSG_TYPE(nlh->nlmsg_type),
557 NFNL_MSG_CTHELPER_NEW, cur);
558 if (ret <= 0) {
559 kfree_skb(skb2);
560 break;
561 }
562
Eric W. Biederman15e47302012-09-07 20:12:54 +0000563 ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200564 MSG_DONTWAIT);
565 if (ret > 0)
566 ret = 0;
567
568 /* this avoids a loop in nfnetlink. */
569 return ret == -EAGAIN ? -ENOBUFS : ret;
570 }
571 }
572 return ret;
573}
574
Pablo Neira Ayuso7b8002a2015-12-15 18:41:56 +0100575static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
576 struct sk_buff *skb, const struct nlmsghdr *nlh,
577 const struct nlattr * const tb[])
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200578{
579 char *helper_name = NULL;
580 struct nf_conntrack_helper *cur;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800581 struct hlist_node *tmp;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200582 struct nf_conntrack_tuple tuple;
583 bool tuple_set = false, found = false;
584 int i, j = 0, ret;
585
586 if (tb[NFCTH_NAME])
587 helper_name = nla_data(tb[NFCTH_NAME]);
588
589 if (tb[NFCTH_TUPLE]) {
590 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
591 if (ret < 0)
592 return ret;
593
594 tuple_set = true;
595 }
596
597 for (i = 0; i < nf_ct_helper_hsize; i++) {
Sasha Levinb67bfe02013-02-27 17:06:00 -0800598 hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200599 hnode) {
600 /* skip non-userspace conntrack helpers. */
601 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
602 continue;
603
604 j++;
605
606 if (helper_name && strncmp(cur->name, helper_name,
607 NF_CT_HELPER_NAME_LEN) != 0) {
608 continue;
609 }
610 if (tuple_set &&
611 (tuple.src.l3num != cur->tuple.src.l3num ||
612 tuple.dst.protonum != cur->tuple.dst.protonum))
613 continue;
614
615 found = true;
616 nf_conntrack_helper_unregister(cur);
617 }
618 }
619 /* Make sure we return success if we flush and there is no helpers */
620 return (found || j == 0) ? 0 : -ENOENT;
621}
622
623static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = {
624 [NFCTH_NAME] = { .type = NLA_NUL_STRING,
625 .len = NF_CT_HELPER_NAME_LEN-1 },
626 [NFCTH_QUEUE_NUM] = { .type = NLA_U32, },
627};
628
629static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = {
630 [NFNL_MSG_CTHELPER_NEW] = { .call = nfnl_cthelper_new,
631 .attr_count = NFCTH_MAX,
632 .policy = nfnl_cthelper_policy },
633 [NFNL_MSG_CTHELPER_GET] = { .call = nfnl_cthelper_get,
634 .attr_count = NFCTH_MAX,
635 .policy = nfnl_cthelper_policy },
636 [NFNL_MSG_CTHELPER_DEL] = { .call = nfnl_cthelper_del,
637 .attr_count = NFCTH_MAX,
638 .policy = nfnl_cthelper_policy },
639};
640
641static const struct nfnetlink_subsystem nfnl_cthelper_subsys = {
642 .name = "cthelper",
643 .subsys_id = NFNL_SUBSYS_CTHELPER,
644 .cb_count = NFNL_MSG_CTHELPER_MAX,
645 .cb = nfnl_cthelper_cb,
646};
647
648MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER);
649
650static int __init nfnl_cthelper_init(void)
651{
652 int ret;
653
654 ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys);
655 if (ret < 0) {
656 pr_err("nfnl_cthelper: cannot register with nfnetlink.\n");
657 goto err_out;
658 }
659 return 0;
660err_out:
661 return ret;
662}
663
664static void __exit nfnl_cthelper_exit(void)
665{
666 struct nf_conntrack_helper *cur;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800667 struct hlist_node *tmp;
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200668 int i;
669
670 nfnetlink_subsys_unregister(&nfnl_cthelper_subsys);
671
672 for (i=0; i<nf_ct_helper_hsize; i++) {
Sasha Levinb67bfe02013-02-27 17:06:00 -0800673 hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
Pablo Neira Ayuso12f7a502012-05-13 21:44:54 +0200674 hnode) {
675 /* skip non-userspace conntrack helpers. */
676 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
677 continue;
678
679 nf_conntrack_helper_unregister(cur);
680 }
681 }
682}
683
684module_init(nfnl_cthelper_init);
685module_exit(nfnl_cthelper_exit);