diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2010-09-28 15:06:34 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-09-28 15:06:34 -0400 |
commit | bc01befdcf3e40979eb518085a075cbf0aacede0 (patch) | |
tree | 8d6c4352e135ade290f2ba0b68b45cbcffde8af8 | |
parent | 8b008faf92ac8f7eeb65e8cd36077601af7c46db (diff) |
netfilter: ctnetlink: add support for user-space expectation helpers
This patch adds the basic infrastructure to support user-space
expectation helpers via ctnetlink and the netfilter queuing
infrastructure NFQUEUE. Basically, this patch:
* adds NF_CT_EXPECT_USERSPACE flag to identify user-space
created expectations. I have also added a sanity check in
__nf_ct_expect_check() to avoid that kernel-space helpers
may create an expectation if the master conntrack has no
helper assigned.
* adds some branches to check if the master conntrack helper
exists, otherwise we skip the code that refers to kernel-space
helper such as the local expectation list and the expectation
policy.
* allows to set the timeout for user-space expectations with
no helper assigned.
* a list of expectations created from user-space that depends
on ctnetlink (if this module is removed, they are deleted).
* includes USERSPACE in the /proc output for expectations
that have been created by a user-space helper.
This patch also modifies ctnetlink to skip including the helper
name in the Netlink messages if no kernel-space helper is set
(since no user-space expectation has not kernel-space kernel
assigned).
You can access an example user-space FTP conntrack helper at:
http://people.netfilter.org/pablo/userspace-conntrack-helpers/nf-ftp-helper-userspace-POC.tar.bz
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/linux/netfilter/nf_conntrack_common.h | 1 | ||||
-rw-r--r-- | include/net/netfilter/nf_conntrack_expect.h | 1 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_expect.c | 62 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 46 |
4 files changed, 79 insertions, 31 deletions
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index fdc50cae861f..23a1a08578a8 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h | |||
@@ -103,6 +103,7 @@ enum ip_conntrack_expect_events { | |||
103 | /* expectation flags */ | 103 | /* expectation flags */ |
104 | #define NF_CT_EXPECT_PERMANENT 0x1 | 104 | #define NF_CT_EXPECT_PERMANENT 0x1 |
105 | #define NF_CT_EXPECT_INACTIVE 0x2 | 105 | #define NF_CT_EXPECT_INACTIVE 0x2 |
106 | #define NF_CT_EXPECT_USERSPACE 0x4 | ||
106 | 107 | ||
107 | #ifdef __KERNEL__ | 108 | #ifdef __KERNEL__ |
108 | struct ip_conntrack_stat { | 109 | struct ip_conntrack_stat { |
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 96bb42af5fae..416b83844485 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h | |||
@@ -85,6 +85,7 @@ nf_ct_find_expectation(struct net *net, u16 zone, | |||
85 | void nf_ct_unlink_expect(struct nf_conntrack_expect *exp); | 85 | void nf_ct_unlink_expect(struct nf_conntrack_expect *exp); |
86 | void nf_ct_remove_expectations(struct nf_conn *ct); | 86 | void nf_ct_remove_expectations(struct nf_conn *ct); |
87 | void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); | 87 | void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); |
88 | void nf_ct_remove_userspace_expectations(void); | ||
88 | 89 | ||
89 | /* Allocate space for an expectation: this is mandatory before calling | 90 | /* Allocate space for an expectation: this is mandatory before calling |
90 | nf_ct_expect_related. You will have to call put afterwards. */ | 91 | nf_ct_expect_related. You will have to call put afterwards. */ |
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index acb29ccaa41f..b30a1f2aac00 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c | |||
@@ -38,20 +38,23 @@ static int nf_ct_expect_hash_rnd_initted __read_mostly; | |||
38 | 38 | ||
39 | static struct kmem_cache *nf_ct_expect_cachep __read_mostly; | 39 | static struct kmem_cache *nf_ct_expect_cachep __read_mostly; |
40 | 40 | ||
41 | static HLIST_HEAD(nf_ct_userspace_expect_list); | ||
42 | |||
41 | /* nf_conntrack_expect helper functions */ | 43 | /* nf_conntrack_expect helper functions */ |
42 | void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) | 44 | void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) |
43 | { | 45 | { |
44 | struct nf_conn_help *master_help = nfct_help(exp->master); | 46 | struct nf_conn_help *master_help = nfct_help(exp->master); |
45 | struct net *net = nf_ct_exp_net(exp); | 47 | struct net *net = nf_ct_exp_net(exp); |
46 | 48 | ||
47 | NF_CT_ASSERT(master_help); | ||
48 | NF_CT_ASSERT(!timer_pending(&exp->timeout)); | 49 | NF_CT_ASSERT(!timer_pending(&exp->timeout)); |
49 | 50 | ||
50 | hlist_del_rcu(&exp->hnode); | 51 | hlist_del_rcu(&exp->hnode); |
51 | net->ct.expect_count--; | 52 | net->ct.expect_count--; |
52 | 53 | ||
53 | hlist_del(&exp->lnode); | 54 | hlist_del(&exp->lnode); |
54 | master_help->expecting[exp->class]--; | 55 | if (!(exp->flags & NF_CT_EXPECT_USERSPACE)) |
56 | master_help->expecting[exp->class]--; | ||
57 | |||
55 | nf_ct_expect_put(exp); | 58 | nf_ct_expect_put(exp); |
56 | 59 | ||
57 | NF_CT_STAT_INC(net, expect_delete); | 60 | NF_CT_STAT_INC(net, expect_delete); |
@@ -320,16 +323,21 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) | |||
320 | 323 | ||
321 | atomic_inc(&exp->use); | 324 | atomic_inc(&exp->use); |
322 | 325 | ||
323 | hlist_add_head(&exp->lnode, &master_help->expectations); | 326 | if (master_help) { |
324 | master_help->expecting[exp->class]++; | 327 | hlist_add_head(&exp->lnode, &master_help->expectations); |
328 | master_help->expecting[exp->class]++; | ||
329 | } else if (exp->flags & NF_CT_EXPECT_USERSPACE) | ||
330 | hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list); | ||
325 | 331 | ||
326 | hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]); | 332 | hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]); |
327 | net->ct.expect_count++; | 333 | net->ct.expect_count++; |
328 | 334 | ||
329 | setup_timer(&exp->timeout, nf_ct_expectation_timed_out, | 335 | setup_timer(&exp->timeout, nf_ct_expectation_timed_out, |
330 | (unsigned long)exp); | 336 | (unsigned long)exp); |
331 | p = &master_help->helper->expect_policy[exp->class]; | 337 | if (master_help) { |
332 | exp->timeout.expires = jiffies + p->timeout * HZ; | 338 | p = &master_help->helper->expect_policy[exp->class]; |
339 | exp->timeout.expires = jiffies + p->timeout * HZ; | ||
340 | } | ||
333 | add_timer(&exp->timeout); | 341 | add_timer(&exp->timeout); |
334 | 342 | ||
335 | atomic_inc(&exp->use); | 343 | atomic_inc(&exp->use); |
@@ -380,7 +388,9 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) | |||
380 | unsigned int h; | 388 | unsigned int h; |
381 | int ret = 1; | 389 | int ret = 1; |
382 | 390 | ||
383 | if (!master_help->helper) { | 391 | /* Don't allow expectations created from kernel-space with no helper */ |
392 | if (!(expect->flags & NF_CT_EXPECT_USERSPACE) && | ||
393 | (!master_help || (master_help && !master_help->helper))) { | ||
384 | ret = -ESHUTDOWN; | 394 | ret = -ESHUTDOWN; |
385 | goto out; | 395 | goto out; |
386 | } | 396 | } |
@@ -398,13 +408,16 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) | |||
398 | } | 408 | } |
399 | } | 409 | } |
400 | /* Will be over limit? */ | 410 | /* Will be over limit? */ |
401 | p = &master_help->helper->expect_policy[expect->class]; | 411 | if (master_help) { |
402 | if (p->max_expected && | 412 | p = &master_help->helper->expect_policy[expect->class]; |
403 | master_help->expecting[expect->class] >= p->max_expected) { | 413 | if (p->max_expected && |
404 | evict_oldest_expect(master, expect); | 414 | master_help->expecting[expect->class] >= p->max_expected) { |
405 | if (master_help->expecting[expect->class] >= p->max_expected) { | 415 | evict_oldest_expect(master, expect); |
406 | ret = -EMFILE; | 416 | if (master_help->expecting[expect->class] |
407 | goto out; | 417 | >= p->max_expected) { |
418 | ret = -EMFILE; | ||
419 | goto out; | ||
420 | } | ||
408 | } | 421 | } |
409 | } | 422 | } |
410 | 423 | ||
@@ -439,6 +452,21 @@ out: | |||
439 | } | 452 | } |
440 | EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); | 453 | EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); |
441 | 454 | ||
455 | void nf_ct_remove_userspace_expectations(void) | ||
456 | { | ||
457 | struct nf_conntrack_expect *exp; | ||
458 | struct hlist_node *n, *next; | ||
459 | |||
460 | hlist_for_each_entry_safe(exp, n, next, | ||
461 | &nf_ct_userspace_expect_list, lnode) { | ||
462 | if (del_timer(&exp->timeout)) { | ||
463 | nf_ct_unlink_expect(exp); | ||
464 | nf_ct_expect_put(exp); | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations); | ||
469 | |||
442 | #ifdef CONFIG_PROC_FS | 470 | #ifdef CONFIG_PROC_FS |
443 | struct ct_expect_iter_state { | 471 | struct ct_expect_iter_state { |
444 | struct seq_net_private p; | 472 | struct seq_net_private p; |
@@ -529,8 +557,12 @@ static int exp_seq_show(struct seq_file *s, void *v) | |||
529 | seq_printf(s, "PERMANENT"); | 557 | seq_printf(s, "PERMANENT"); |
530 | delim = ","; | 558 | delim = ","; |
531 | } | 559 | } |
532 | if (expect->flags & NF_CT_EXPECT_INACTIVE) | 560 | if (expect->flags & NF_CT_EXPECT_INACTIVE) { |
533 | seq_printf(s, "%sINACTIVE", delim); | 561 | seq_printf(s, "%sINACTIVE", delim); |
562 | delim = ","; | ||
563 | } | ||
564 | if (expect->flags & NF_CT_EXPECT_USERSPACE) | ||
565 | seq_printf(s, "%sUSERSPACE", delim); | ||
534 | 566 | ||
535 | helper = rcu_dereference(nfct_help(expect->master)->helper); | 567 | helper = rcu_dereference(nfct_help(expect->master)->helper); |
536 | if (helper) { | 568 | if (helper) { |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 0804e0ef6500..b4077be5f663 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -1560,8 +1560,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, | |||
1560 | const struct nf_conntrack_expect *exp) | 1560 | const struct nf_conntrack_expect *exp) |
1561 | { | 1561 | { |
1562 | struct nf_conn *master = exp->master; | 1562 | struct nf_conn *master = exp->master; |
1563 | struct nf_conntrack_helper *helper; | ||
1564 | long timeout = (exp->timeout.expires - jiffies) / HZ; | 1563 | long timeout = (exp->timeout.expires - jiffies) / HZ; |
1564 | struct nf_conn_help *help; | ||
1565 | 1565 | ||
1566 | if (timeout < 0) | 1566 | if (timeout < 0) |
1567 | timeout = 0; | 1567 | timeout = 0; |
@@ -1578,9 +1578,14 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, | |||
1578 | NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout)); | 1578 | NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout)); |
1579 | NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp)); | 1579 | NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp)); |
1580 | NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)); | 1580 | NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)); |
1581 | helper = rcu_dereference(nfct_help(master)->helper); | 1581 | help = nfct_help(master); |
1582 | if (helper) | 1582 | if (help) { |
1583 | NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name); | 1583 | struct nf_conntrack_helper *helper; |
1584 | |||
1585 | helper = rcu_dereference(help->helper); | ||
1586 | if (helper) | ||
1587 | NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name); | ||
1588 | } | ||
1584 | 1589 | ||
1585 | return 0; | 1590 | return 0; |
1586 | 1591 | ||
@@ -1921,24 +1926,32 @@ ctnetlink_create_expect(struct net *net, u16 zone, | |||
1921 | if (!h) | 1926 | if (!h) |
1922 | return -ENOENT; | 1927 | return -ENOENT; |
1923 | ct = nf_ct_tuplehash_to_ctrack(h); | 1928 | ct = nf_ct_tuplehash_to_ctrack(h); |
1924 | help = nfct_help(ct); | ||
1925 | |||
1926 | if (!help || !help->helper) { | ||
1927 | /* such conntrack hasn't got any helper, abort */ | ||
1928 | err = -EOPNOTSUPP; | ||
1929 | goto out; | ||
1930 | } | ||
1931 | |||
1932 | exp = nf_ct_expect_alloc(ct); | 1929 | exp = nf_ct_expect_alloc(ct); |
1933 | if (!exp) { | 1930 | if (!exp) { |
1934 | err = -ENOMEM; | 1931 | err = -ENOMEM; |
1935 | goto out; | 1932 | goto out; |
1936 | } | 1933 | } |
1934 | help = nfct_help(ct); | ||
1935 | if (!help) { | ||
1936 | if (!cda[CTA_EXPECT_TIMEOUT]) { | ||
1937 | err = -EINVAL; | ||
1938 | goto out; | ||
1939 | } | ||
1940 | exp->timeout.expires = | ||
1941 | jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ; | ||
1937 | 1942 | ||
1938 | if (cda[CTA_EXPECT_FLAGS]) | 1943 | exp->flags = NF_CT_EXPECT_USERSPACE; |
1939 | exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); | 1944 | if (cda[CTA_EXPECT_FLAGS]) { |
1940 | else | 1945 | exp->flags |= |
1941 | exp->flags = 0; | 1946 | ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); |
1947 | } | ||
1948 | } else { | ||
1949 | if (cda[CTA_EXPECT_FLAGS]) { | ||
1950 | exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); | ||
1951 | exp->flags &= ~NF_CT_EXPECT_USERSPACE; | ||
1952 | } else | ||
1953 | exp->flags = 0; | ||
1954 | } | ||
1942 | 1955 | ||
1943 | exp->class = 0; | 1956 | exp->class = 0; |
1944 | exp->expectfn = NULL; | 1957 | exp->expectfn = NULL; |
@@ -2109,6 +2122,7 @@ static void __exit ctnetlink_exit(void) | |||
2109 | { | 2122 | { |
2110 | pr_info("ctnetlink: unregistering from nfnetlink.\n"); | 2123 | pr_info("ctnetlink: unregistering from nfnetlink.\n"); |
2111 | 2124 | ||
2125 | nf_ct_remove_userspace_expectations(); | ||
2112 | #ifdef CONFIG_NF_CONNTRACK_EVENTS | 2126 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
2113 | nf_ct_expect_unregister_notifier(&ctnl_notifier_exp); | 2127 | nf_ct_expect_unregister_notifier(&ctnl_notifier_exp); |
2114 | nf_conntrack_unregister_notifier(&ctnl_notifier); | 2128 | nf_conntrack_unregister_notifier(&ctnl_notifier); |