aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2011-12-17 19:55:54 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2011-12-23 08:36:39 -0500
commit3d058d7bc2c5671ae630e0b463be8a69b5783fb9 (patch)
treef500c32545d26357da5d2ea1e8e63e64af8bbc35 /net/netfilter
parentc4042a339f40fe00d85e31055b1c0808dd025539 (diff)
netfilter: rework user-space expectation helper support
This partially reworks bc01befdcf3e40979eb518085a075cbf0aacede0 which added userspace expectation support. This patch removes the nf_ct_userspace_expect_list since now we force to use the new iptables CT target feature to add the helper extension for conntracks that have attached expectations from userspace. A new version of the proof-of-concept code to implement userspace helpers from userspace is available at: http://people.netfilter.org/pablo/userspace-conntrack-helpers/nf-ftp-helper-POC.tar.bz2 This patch also modifies the CT target to allow to set the conntrack's userspace helper status flags. This flag is used to tell the conntrack system to explicitly allocate the helper extension. This helper extension is useful to link the userspace expectations with the master conntrack that is being tracked from one userspace helper. This feature fixes a problem in the current approach of the userspace helper support. Basically, if the master conntrack that has got a userspace expectation vanishes, the expectations point to one invalid memory address. Thus, triggering an oops in the expectation deletion event path. I decided not to add a new revision of the CT target because I only needed to add a new flag for it. I'll document in this issue in the iptables manpage. I have also changed the return value from EINVAL to EOPNOTSUPP if one flag not supported is specified. Thus, in the future adding new features that only require a new flag can be added without a new revision. There is no official code using this in userspace (apart from the proof-of-concept) that uses this infrastructure but there will be some by beginning 2012. Reported-by: Sam Roberts <vieuxtech@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/nf_conntrack_expect.c63
-rw-r--r--net/netfilter/nf_conntrack_helper.c12
-rw-r--r--net/netfilter/nf_conntrack_netlink.c5
-rw-r--r--net/netfilter/xt_CT.c8
4 files changed, 42 insertions, 46 deletions
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 340c80d968d..bebb1675e6f 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -38,8 +38,6 @@ unsigned int nf_ct_expect_max __read_mostly;
38 38
39static struct kmem_cache *nf_ct_expect_cachep __read_mostly; 39static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
40 40
41static HLIST_HEAD(nf_ct_userspace_expect_list);
42
43/* nf_conntrack_expect helper functions */ 41/* nf_conntrack_expect helper functions */
44void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, 42void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
45 u32 pid, int report) 43 u32 pid, int report)
@@ -47,14 +45,14 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
47 struct nf_conn_help *master_help = nfct_help(exp->master); 45 struct nf_conn_help *master_help = nfct_help(exp->master);
48 struct net *net = nf_ct_exp_net(exp); 46 struct net *net = nf_ct_exp_net(exp);
49 47
48 NF_CT_ASSERT(master_help);
50 NF_CT_ASSERT(!timer_pending(&exp->timeout)); 49 NF_CT_ASSERT(!timer_pending(&exp->timeout));
51 50
52 hlist_del_rcu(&exp->hnode); 51 hlist_del_rcu(&exp->hnode);
53 net->ct.expect_count--; 52 net->ct.expect_count--;
54 53
55 hlist_del(&exp->lnode); 54 hlist_del(&exp->lnode);
56 if (!(exp->flags & NF_CT_EXPECT_USERSPACE)) 55 master_help->expecting[exp->class]--;
57 master_help->expecting[exp->class]--;
58 56
59 nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report); 57 nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
60 nf_ct_expect_put(exp); 58 nf_ct_expect_put(exp);
@@ -314,37 +312,34 @@ void nf_ct_expect_put(struct nf_conntrack_expect *exp)
314} 312}
315EXPORT_SYMBOL_GPL(nf_ct_expect_put); 313EXPORT_SYMBOL_GPL(nf_ct_expect_put);
316 314
317static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) 315static int nf_ct_expect_insert(struct nf_conntrack_expect *exp)
318{ 316{
319 struct nf_conn_help *master_help = nfct_help(exp->master); 317 struct nf_conn_help *master_help = nfct_help(exp->master);
318 struct nf_conntrack_helper *helper;
320 struct net *net = nf_ct_exp_net(exp); 319 struct net *net = nf_ct_exp_net(exp);
321 const struct nf_conntrack_expect_policy *p;
322 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple); 320 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
323 321
324 /* two references : one for hash insert, one for the timer */ 322 /* two references : one for hash insert, one for the timer */
325 atomic_add(2, &exp->use); 323 atomic_add(2, &exp->use);
326 324
327 if (master_help) { 325 hlist_add_head(&exp->lnode, &master_help->expectations);
328 hlist_add_head(&exp->lnode, &master_help->expectations); 326 master_help->expecting[exp->class]++;
329 master_help->expecting[exp->class]++;
330 } else if (exp->flags & NF_CT_EXPECT_USERSPACE)
331 hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
332 327
333 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]); 328 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
334 net->ct.expect_count++; 329 net->ct.expect_count++;
335 330
336 setup_timer(&exp->timeout, nf_ct_expectation_timed_out, 331 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
337 (unsigned long)exp); 332 (unsigned long)exp);
338 if (master_help) { 333 helper = rcu_dereference_protected(master_help->helper,
339 p = &rcu_dereference_protected( 334 lockdep_is_held(&nf_conntrack_lock));
340 master_help->helper, 335 if (helper) {
341 lockdep_is_held(&nf_conntrack_lock) 336 exp->timeout.expires = jiffies +
342 )->expect_policy[exp->class]; 337 helper->expect_policy[exp->class].timeout * HZ;
343 exp->timeout.expires = jiffies + p->timeout * HZ;
344 } 338 }
345 add_timer(&exp->timeout); 339 add_timer(&exp->timeout);
346 340
347 NF_CT_STAT_INC(net, expect_create); 341 NF_CT_STAT_INC(net, expect_create);
342 return 0;
348} 343}
349 344
350/* Race with expectations being used means we could have none to find; OK. */ 345/* Race with expectations being used means we could have none to find; OK. */
@@ -389,14 +384,13 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
389 struct nf_conntrack_expect *i; 384 struct nf_conntrack_expect *i;
390 struct nf_conn *master = expect->master; 385 struct nf_conn *master = expect->master;
391 struct nf_conn_help *master_help = nfct_help(master); 386 struct nf_conn_help *master_help = nfct_help(master);
387 struct nf_conntrack_helper *helper;
392 struct net *net = nf_ct_exp_net(expect); 388 struct net *net = nf_ct_exp_net(expect);
393 struct hlist_node *n; 389 struct hlist_node *n;
394 unsigned int h; 390 unsigned int h;
395 int ret = 1; 391 int ret = 1;
396 392
397 /* Don't allow expectations created from kernel-space with no helper */ 393 if (!master_help) {
398 if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
399 (!master_help || (master_help && !master_help->helper))) {
400 ret = -ESHUTDOWN; 394 ret = -ESHUTDOWN;
401 goto out; 395 goto out;
402 } 396 }
@@ -414,11 +408,10 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
414 } 408 }
415 } 409 }
416 /* Will be over limit? */ 410 /* Will be over limit? */
417 if (master_help) { 411 helper = rcu_dereference_protected(master_help->helper,
418 p = &rcu_dereference_protected( 412 lockdep_is_held(&nf_conntrack_lock));
419 master_help->helper, 413 if (helper) {
420 lockdep_is_held(&nf_conntrack_lock) 414 p = &helper->expect_policy[expect->class];
421 )->expect_policy[expect->class];
422 if (p->max_expected && 415 if (p->max_expected &&
423 master_help->expecting[expect->class] >= p->max_expected) { 416 master_help->expecting[expect->class] >= p->max_expected) {
424 evict_oldest_expect(master, expect); 417 evict_oldest_expect(master, expect);
@@ -450,8 +443,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
450 if (ret <= 0) 443 if (ret <= 0)
451 goto out; 444 goto out;
452 445
453 ret = 0; 446 ret = nf_ct_expect_insert(expect);
454 nf_ct_expect_insert(expect); 447 if (ret < 0)
448 goto out;
455 spin_unlock_bh(&nf_conntrack_lock); 449 spin_unlock_bh(&nf_conntrack_lock);
456 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report); 450 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
457 return ret; 451 return ret;
@@ -461,21 +455,6 @@ out:
461} 455}
462EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); 456EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
463 457
464void nf_ct_remove_userspace_expectations(void)
465{
466 struct nf_conntrack_expect *exp;
467 struct hlist_node *n, *next;
468
469 hlist_for_each_entry_safe(exp, n, next,
470 &nf_ct_userspace_expect_list, lnode) {
471 if (del_timer(&exp->timeout)) {
472 nf_ct_unlink_expect(exp);
473 nf_ct_expect_put(exp);
474 }
475 }
476}
477EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
478
479#ifdef CONFIG_PROC_FS 458#ifdef CONFIG_PROC_FS
480struct ct_expect_iter_state { 459struct ct_expect_iter_state {
481 struct seq_net_private p; 460 struct seq_net_private p;
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 93c4bdbfc1a..c9e0de08aa8 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -121,6 +121,18 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
121 int ret = 0; 121 int ret = 0;
122 122
123 if (tmpl != NULL) { 123 if (tmpl != NULL) {
124 /* we've got a userspace helper. */
125 if (tmpl->status & IPS_USERSPACE_HELPER) {
126 help = nf_ct_helper_ext_add(ct, flags);
127 if (help == NULL) {
128 ret = -ENOMEM;
129 goto out;
130 }
131 rcu_assign_pointer(help->helper, NULL);
132 __set_bit(IPS_USERSPACE_HELPER_BIT, &ct->status);
133 ret = 0;
134 goto out;
135 }
124 help = nfct_help(tmpl); 136 help = nfct_help(tmpl);
125 if (help != NULL) 137 if (help != NULL)
126 helper = help->helper; 138 helper = help->helper;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 636617ccfe2..739548029dc 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -2040,6 +2040,10 @@ ctnetlink_create_expect(struct net *net, u16 zone,
2040 } 2040 }
2041 help = nfct_help(ct); 2041 help = nfct_help(ct);
2042 if (!help) { 2042 if (!help) {
2043 err = -EOPNOTSUPP;
2044 goto out;
2045 }
2046 if (test_bit(IPS_USERSPACE_HELPER_BIT, &ct->status)) {
2043 if (!cda[CTA_EXPECT_TIMEOUT]) { 2047 if (!cda[CTA_EXPECT_TIMEOUT]) {
2044 err = -EINVAL; 2048 err = -EINVAL;
2045 goto out; 2049 goto out;
@@ -2264,7 +2268,6 @@ static void __exit ctnetlink_exit(void)
2264{ 2268{
2265 pr_info("ctnetlink: unregistering from nfnetlink.\n"); 2269 pr_info("ctnetlink: unregistering from nfnetlink.\n");
2266 2270
2267 nf_ct_remove_userspace_expectations();
2268 unregister_pernet_subsys(&ctnetlink_net_ops); 2271 unregister_pernet_subsys(&ctnetlink_net_ops);
2269 nfnetlink_subsys_unregister(&ctnl_exp_subsys); 2272 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
2270 nfnetlink_subsys_unregister(&ctnl_subsys); 2273 nfnetlink_subsys_unregister(&ctnl_subsys);
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 0221d10de75..8e87123f137 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -62,8 +62,8 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par)
62 int ret = 0; 62 int ret = 0;
63 u8 proto; 63 u8 proto;
64 64
65 if (info->flags & ~XT_CT_NOTRACK) 65 if (info->flags & ~(XT_CT_NOTRACK | XT_CT_USERSPACE_HELPER))
66 return -EINVAL; 66 return -EOPNOTSUPP;
67 67
68 if (info->flags & XT_CT_NOTRACK) { 68 if (info->flags & XT_CT_NOTRACK) {
69 ct = nf_ct_untracked_get(); 69 ct = nf_ct_untracked_get();
@@ -92,7 +92,9 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par)
92 GFP_KERNEL)) 92 GFP_KERNEL))
93 goto err3; 93 goto err3;
94 94
95 if (info->helper[0]) { 95 if (info->flags & XT_CT_USERSPACE_HELPER) {
96 __set_bit(IPS_USERSPACE_HELPER_BIT, &ct->status);
97 } else if (info->helper[0]) {
96 ret = -ENOENT; 98 ret = -ENOENT;
97 proto = xt_ct_find_proto(par); 99 proto = xt_ct_find_proto(par);
98 if (!proto) { 100 if (!proto) {