Commit 0c66dc1e authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: conntrack: register hooks in netns when needed by ruleset

This makes use of nf_ct_netns_get/put added in previous patch.
We add get/put functions to nf_conntrack_l3proto structure, ipv4 and ipv6
then implement use-count to track how many users (nft or xtables modules)
have a dependency on ipv4 and/or ipv6 connection tracking functionality.

When count reaches zero, the hooks are unregistered.

This delays activation of connection tracking inside a namespace until
stateful firewall rule or nat rule gets added.

This patch breaks backwards compatibility in the sense that connection
tracking won't be active anymore when the protocol tracker module is
loaded.  This breaks e.g. setups that ctnetlink for flow accounting and
the like, without any '-m conntrack' packet filter rules.

Followup patch restores old behavour and makes new delayed scheme
optional via sysctl.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 20afd423
...@@ -52,6 +52,10 @@ struct nf_conntrack_l3proto { ...@@ -52,6 +52,10 @@ struct nf_conntrack_l3proto {
int (*tuple_to_nlattr)(struct sk_buff *skb, int (*tuple_to_nlattr)(struct sk_buff *skb,
const struct nf_conntrack_tuple *t); const struct nf_conntrack_tuple *t);
/* Called when netns wants to use connection tracking */
int (*net_ns_get)(struct net *);
void (*net_ns_put)(struct net *);
/* /*
* Calculate size of tuple nlattr * Calculate size of tuple nlattr
*/ */
......
...@@ -31,6 +31,13 @@ ...@@ -31,6 +31,13 @@
#include <net/netfilter/ipv4/nf_defrag_ipv4.h> #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
static int conntrack4_net_id __read_mostly;
static DEFINE_MUTEX(register_ipv4_hooks);
struct conntrack4_net {
unsigned int users;
};
static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple) struct nf_conntrack_tuple *tuple)
{ {
...@@ -307,6 +314,38 @@ static struct nf_sockopt_ops so_getorigdst = { ...@@ -307,6 +314,38 @@ static struct nf_sockopt_ops so_getorigdst = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int ipv4_hooks_register(struct net *net)
{
struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id);
int err = 0;
mutex_lock(&register_ipv4_hooks);
cnet->users++;
if (cnet->users > 1)
goto out_unlock;
err = nf_register_net_hooks(net, ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
if (err)
cnet->users = 0;
out_unlock:
mutex_unlock(&register_ipv4_hooks);
return err;
}
static void ipv4_hooks_unregister(struct net *net)
{
struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id);
mutex_lock(&register_ipv4_hooks);
if (cnet->users && (--cnet->users == 0))
nf_unregister_net_hooks(net, ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
mutex_unlock(&register_ipv4_hooks);
}
struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
.l3proto = PF_INET, .l3proto = PF_INET,
.name = "ipv4", .name = "ipv4",
...@@ -320,6 +359,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { ...@@ -320,6 +359,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
.nlattr_to_tuple = ipv4_nlattr_to_tuple, .nlattr_to_tuple = ipv4_nlattr_to_tuple,
.nla_policy = ipv4_nla_policy, .nla_policy = ipv4_nla_policy,
#endif #endif
.net_ns_get = ipv4_hooks_register,
.net_ns_put = ipv4_hooks_unregister,
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
...@@ -372,6 +413,8 @@ static void ipv4_net_exit(struct net *net) ...@@ -372,6 +413,8 @@ static void ipv4_net_exit(struct net *net)
static struct pernet_operations ipv4_net_ops = { static struct pernet_operations ipv4_net_ops = {
.init = ipv4_net_init, .init = ipv4_net_init,
.exit = ipv4_net_exit, .exit = ipv4_net_exit,
.id = &conntrack4_net_id,
.size = sizeof(struct conntrack4_net),
}; };
static int __init nf_conntrack_l3proto_ipv4_init(void) static int __init nf_conntrack_l3proto_ipv4_init(void)
...@@ -393,17 +436,10 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) ...@@ -393,17 +436,10 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
goto cleanup_sockopt; goto cleanup_sockopt;
} }
ret = nf_register_hooks(ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register hooks.\n");
goto cleanup_pernet;
}
ret = nf_ct_l4proto_register(builtin_l4proto4, ret = nf_ct_l4proto_register(builtin_l4proto4,
ARRAY_SIZE(builtin_l4proto4)); ARRAY_SIZE(builtin_l4proto4));
if (ret < 0) if (ret < 0)
goto cleanup_hooks; goto cleanup_pernet;
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
if (ret < 0) { if (ret < 0) {
...@@ -415,8 +451,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) ...@@ -415,8 +451,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
cleanup_l4proto: cleanup_l4proto:
nf_ct_l4proto_unregister(builtin_l4proto4, nf_ct_l4proto_unregister(builtin_l4proto4,
ARRAY_SIZE(builtin_l4proto4)); ARRAY_SIZE(builtin_l4proto4));
cleanup_hooks:
nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
cleanup_pernet: cleanup_pernet:
unregister_pernet_subsys(&ipv4_net_ops); unregister_pernet_subsys(&ipv4_net_ops);
cleanup_sockopt: cleanup_sockopt:
...@@ -430,7 +464,6 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) ...@@ -430,7 +464,6 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void)
nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
nf_ct_l4proto_unregister(builtin_l4proto4, nf_ct_l4proto_unregister(builtin_l4proto4,
ARRAY_SIZE(builtin_l4proto4)); ARRAY_SIZE(builtin_l4proto4));
nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
unregister_pernet_subsys(&ipv4_net_ops); unregister_pernet_subsys(&ipv4_net_ops);
nf_unregister_sockopt(&so_getorigdst); nf_unregister_sockopt(&so_getorigdst);
} }
......
...@@ -34,6 +34,13 @@ ...@@ -34,6 +34,13 @@
#include <net/netfilter/ipv6/nf_defrag_ipv6.h> #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
static int conntrack6_net_id;
static DEFINE_MUTEX(register_ipv6_hooks);
struct conntrack6_net {
unsigned int users;
};
static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple) struct nf_conntrack_tuple *tuple)
{ {
...@@ -308,6 +315,36 @@ static int ipv6_nlattr_tuple_size(void) ...@@ -308,6 +315,36 @@ static int ipv6_nlattr_tuple_size(void)
} }
#endif #endif
static int ipv6_hooks_register(struct net *net)
{
struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
int err = 0;
mutex_lock(&register_ipv6_hooks);
cnet->users++;
if (cnet->users > 1)
goto out_unlock;
err = nf_register_net_hooks(net, ipv6_conntrack_ops,
ARRAY_SIZE(ipv6_conntrack_ops));
if (err)
cnet->users = 0;
out_unlock:
mutex_unlock(&register_ipv6_hooks);
return err;
}
static void ipv6_hooks_unregister(struct net *net)
{
struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
mutex_lock(&register_ipv6_hooks);
if (cnet->users && (--cnet->users == 0))
nf_unregister_net_hooks(net, ipv6_conntrack_ops,
ARRAY_SIZE(ipv6_conntrack_ops));
mutex_unlock(&register_ipv6_hooks);
}
struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
.l3proto = PF_INET6, .l3proto = PF_INET6,
.name = "ipv6", .name = "ipv6",
...@@ -321,6 +358,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { ...@@ -321,6 +358,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
.nlattr_to_tuple = ipv6_nlattr_to_tuple, .nlattr_to_tuple = ipv6_nlattr_to_tuple,
.nla_policy = ipv6_nla_policy, .nla_policy = ipv6_nla_policy,
#endif #endif
.net_ns_get = ipv6_hooks_register,
.net_ns_put = ipv6_hooks_unregister,
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
...@@ -379,6 +418,8 @@ static void ipv6_net_exit(struct net *net) ...@@ -379,6 +418,8 @@ static void ipv6_net_exit(struct net *net)
static struct pernet_operations ipv6_net_ops = { static struct pernet_operations ipv6_net_ops = {
.init = ipv6_net_init, .init = ipv6_net_init,
.exit = ipv6_net_exit, .exit = ipv6_net_exit,
.id = &conntrack6_net_id,
.size = sizeof(struct conntrack6_net),
}; };
static int __init nf_conntrack_l3proto_ipv6_init(void) static int __init nf_conntrack_l3proto_ipv6_init(void)
...@@ -398,18 +439,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) ...@@ -398,18 +439,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
if (ret < 0) if (ret < 0)
goto cleanup_sockopt; goto cleanup_sockopt;
ret = nf_register_hooks(ipv6_conntrack_ops,
ARRAY_SIZE(ipv6_conntrack_ops));
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
"hook.\n");
goto cleanup_pernet;
}
ret = nf_ct_l4proto_register(builtin_l4proto6, ret = nf_ct_l4proto_register(builtin_l4proto6,
ARRAY_SIZE(builtin_l4proto6)); ARRAY_SIZE(builtin_l4proto6));
if (ret < 0) if (ret < 0)
goto cleanup_hooks; goto cleanup_pernet;
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6); ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
if (ret < 0) { if (ret < 0) {
...@@ -420,8 +453,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) ...@@ -420,8 +453,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
cleanup_l4proto: cleanup_l4proto:
nf_ct_l4proto_unregister(builtin_l4proto6, nf_ct_l4proto_unregister(builtin_l4proto6,
ARRAY_SIZE(builtin_l4proto6)); ARRAY_SIZE(builtin_l4proto6));
cleanup_hooks:
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
cleanup_pernet: cleanup_pernet:
unregister_pernet_subsys(&ipv6_net_ops); unregister_pernet_subsys(&ipv6_net_ops);
cleanup_sockopt: cleanup_sockopt:
...@@ -435,7 +466,6 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) ...@@ -435,7 +466,6 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void)
nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6); nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
nf_ct_l4proto_unregister(builtin_l4proto6, nf_ct_l4proto_unregister(builtin_l4proto6,
ARRAY_SIZE(builtin_l4proto6)); ARRAY_SIZE(builtin_l4proto6));
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
unregister_pernet_subsys(&ipv6_net_ops); unregister_pernet_subsys(&ipv6_net_ops);
nf_unregister_sockopt(&so_getorigdst6); nf_unregister_sockopt(&so_getorigdst6);
} }
......
...@@ -127,12 +127,48 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); ...@@ -127,12 +127,48 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
int nf_ct_netns_get(struct net *net, u8 nfproto) int nf_ct_netns_get(struct net *net, u8 nfproto)
{ {
return nf_ct_l3proto_try_module_get(nfproto); const struct nf_conntrack_l3proto *l3proto;
int ret;
might_sleep();
ret = nf_ct_l3proto_try_module_get(nfproto);
if (ret < 0)
return ret;
/* we already have a reference, can't fail */
rcu_read_lock();
l3proto = __nf_ct_l3proto_find(nfproto);
rcu_read_unlock();
if (!l3proto->net_ns_get)
return 0;
ret = l3proto->net_ns_get(net);
if (ret < 0)
nf_ct_l3proto_module_put(nfproto);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_ct_netns_get); EXPORT_SYMBOL_GPL(nf_ct_netns_get);
void nf_ct_netns_put(struct net *net, u8 nfproto) void nf_ct_netns_put(struct net *net, u8 nfproto)
{ {
const struct nf_conntrack_l3proto *l3proto;
might_sleep();
/* same as nf_conntrack_netns_get(), reference assumed */
rcu_read_lock();
l3proto = __nf_ct_l3proto_find(nfproto);
rcu_read_unlock();
if (WARN_ON(!l3proto))
return;
if (l3proto->net_ns_put)
l3proto->net_ns_put(net);
nf_ct_l3proto_module_put(nfproto); nf_ct_l3proto_module_put(nfproto);
} }
EXPORT_SYMBOL_GPL(nf_ct_netns_put); EXPORT_SYMBOL_GPL(nf_ct_netns_put);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment