@@ -397,6 +397,7 @@ struct nfnl_ct_hook {
void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, s32 off);
int (*register_hooks)(struct net *);
+ void (*newproto)(void);
};
extern struct nfnl_ct_hook __rcu *nfnl_ct_hook;
@@ -61,6 +61,7 @@ static int ctnetlink_net_id __read_mostly;
struct ctnl_net {
DECLARE_BITMAP(enabled, NFPROTO_NUMPROTO);
+ bool active;
};
static inline int
@@ -2138,7 +2139,7 @@ ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask);
-static int ctnl_bind(struct net *net)
+static void __ctnl_bind(struct net *net)
{
struct ctnl_net *ctnet = net_generic(net, ctnetlink_net_id);
int i;
@@ -2178,11 +2179,43 @@ static int ctnl_bind(struct net *net)
}
rcu_read_unlock();
+}
+
+static int ctnl_bind(struct net *net)
+{
+ struct ctnl_net *ctnet = net_generic(net, ctnetlink_net_id);
+
+ __ctnl_bind(net);
+
+ if (!ctnet->active)
+ ctnet->active = true;
return 0;
}
#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
+/* called with RCU read lock held */
+static void ctnl_newproto(void)
+{
+ struct ctnl_net *ctnet;
+ struct net *net;
+
+ if (!try_module_get(THIS_MODULE))
+ return;
+
+ rcu_read_unlock();
+ rtnl_lock();
+ for_each_net(net) {
+ ctnet = net_generic(net, ctnetlink_net_id);
+ if (ctnet->active)
+ __ctnl_bind(net);
+ }
+ rtnl_unlock();
+ rcu_read_lock();
+
+ module_put(THIS_MODULE);
+}
+
static size_t
ctnetlink_glue_build_size(const struct nf_conn *ct)
{
@@ -2452,6 +2485,7 @@ static struct nfnl_ct_hook ctnetlink_glue_hook = {
.attach_expect = ctnetlink_glue_attach_expect,
.seq_adjust = ctnetlink_glue_seqadj,
.register_hooks = ctnl_bind,
+ .newproto = ctnl_newproto,
};
#endif /* CONFIG_NETFILTER_NETLINK_GLUE_CT */
@@ -3434,6 +3468,7 @@ static void ctnetlink_net_exit(struct net *net)
rcu_read_lock();
}
+ ctnet->active = false;
rcu_read_unlock();
}
@@ -259,6 +259,7 @@ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)
{
int ret = 0;
struct nf_conntrack_l3proto *old;
+ struct nfnl_ct_hook *nfnl_ct;
if (proto->l3proto >= AF_MAX)
return -EBUSY;
@@ -279,6 +280,11 @@ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
+ rcu_read_lock();
+ nfnl_ct = rcu_dereference(nfnl_ct_hook);
+ if (nfnl_ct)
+ nfnl_ct->newproto();
+ rcu_read_unlock();
out_unlock:
mutex_unlock(&nf_ct_proto_mutex);
return ret;
Previous patch made 'conntrack -E' (or other ctnetlink event tools) register netfilter hooks of already-loaded conntrack l3 protocols, but this fails when a new l3 protocol is loaded at a later point in time. This informs ctnetlink about new module and also registers their hooks if needed. Signed-off-by: Florian Westphal <fw@strlen.de> --- not part of v2. This is a seperate patch to ease review. include/linux/netfilter.h | 1 + net/netfilter/nf_conntrack_netlink.c | 37 +++++++++++++++++++++++++++++++++++- net/netfilter/nf_conntrack_proto.c | 6 ++++++ 3 files changed, 43 insertions(+), 1 deletion(-)