netlink: add nlk->netlink_bind hook for module auto-loading
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 29 Jun 2012 06:15:22 +0000 (06:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 29 Jun 2012 23:46:06 +0000 (16:46 -0700)
This patch adds a hook in the binding path of netlink.

This is used by ctnetlink to allow module autoloading for the case
in which one user executes:

 conntrack -E

So far, this resulted in nfnetlink loaded, but not
nf_conntrack_netlink.

I have received in the past many complains on this behaviour.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netlink.h
net/netfilter/nfnetlink.c
net/netlink/af_netlink.c

index 6085e49..f74dd13 100644 (file)
@@ -179,6 +179,7 @@ struct netlink_kernel_cfg {
        unsigned int    groups;
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
+       void            (*bind)(int group);
 };
 
 extern struct sock *netlink_kernel_create(struct net *net, int unit,
index 700e461..5a2132b 100644 (file)
@@ -39,6 +39,15 @@ static char __initdata nfversion[] = "0.30";
 static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
 static DEFINE_MUTEX(nfnl_mutex);
 
+static const int nfnl_group2type[NFNLGRP_MAX+1] = {
+       [NFNLGRP_CONNTRACK_NEW]         = NFNL_SUBSYS_CTNETLINK,
+       [NFNLGRP_CONNTRACK_UPDATE]      = NFNL_SUBSYS_CTNETLINK,
+       [NFNLGRP_CONNTRACK_DESTROY]     = NFNL_SUBSYS_CTNETLINK,
+       [NFNLGRP_CONNTRACK_EXP_NEW]     = NFNL_SUBSYS_CTNETLINK_EXP,
+       [NFNLGRP_CONNTRACK_EXP_UPDATE]  = NFNL_SUBSYS_CTNETLINK_EXP,
+       [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
+};
+
 void nfnl_lock(void)
 {
        mutex_lock(&nfnl_mutex);
@@ -200,12 +209,32 @@ static void nfnetlink_rcv(struct sk_buff *skb)
        netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
 }
 
+#ifdef CONFIG_MODULES
+static void nfnetlink_bind(int group)
+{
+       const struct nfnetlink_subsystem *ss;
+       int type = nfnl_group2type[group];
+
+       rcu_read_lock();
+       ss = nfnetlink_get_subsys(type);
+       if (!ss) {
+               rcu_read_unlock();
+               request_module("nfnetlink-subsys-%d", type);
+               return;
+       }
+       rcu_read_unlock();
+}
+#endif
+
 static int __net_init nfnetlink_net_init(struct net *net)
 {
        struct sock *nfnl;
        struct netlink_kernel_cfg cfg = {
                .groups = NFNLGRP_MAX,
                .input  = nfnetlink_rcv,
+#ifdef CONFIG_MODULES
+               .bind   = nfnetlink_bind,
+#endif
        };
 
        nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg);
index 43a124f..5463969 100644 (file)
@@ -80,6 +80,7 @@ struct netlink_sock {
        struct mutex            *cb_mutex;
        struct mutex            cb_def_mutex;
        void                    (*netlink_rcv)(struct sk_buff *skb);
+       void                    (*netlink_bind)(int group);
        struct module           *module;
 };
 
@@ -124,6 +125,7 @@ struct netlink_table {
        unsigned int            groups;
        struct mutex            *cb_mutex;
        struct module           *module;
+       void                    (*bind)(int group);
        int                     registered;
 };
 
@@ -444,6 +446,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        struct module *module = NULL;
        struct mutex *cb_mutex;
        struct netlink_sock *nlk;
+       void (*bind)(int group);
        int err = 0;
 
        sock->state = SS_UNCONNECTED;
@@ -468,6 +471,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        else
                err = -EPROTONOSUPPORT;
        cb_mutex = nl_table[protocol].cb_mutex;
+       bind = nl_table[protocol].bind;
        netlink_unlock_table();
 
        if (err < 0)
@@ -483,6 +487,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
 
        nlk = nlk_sk(sock->sk);
        nlk->module = module;
+       nlk->netlink_bind = bind;
 out:
        return err;
 
@@ -683,6 +688,15 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
        netlink_update_listeners(sk);
        netlink_table_ungrab();
 
+       if (nlk->netlink_bind && nlk->groups[0]) {
+               int i;
+
+               for (i=0; i<nlk->ngroups; i++) {
+                       if (test_bit(i, nlk->groups))
+                               nlk->netlink_bind(i);
+               }
+       }
+
        return 0;
 }
 
@@ -1239,6 +1253,10 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
                netlink_update_socket_mc(nlk, val,
                                         optname == NETLINK_ADD_MEMBERSHIP);
                netlink_table_ungrab();
+
+               if (nlk->netlink_bind)
+                       nlk->netlink_bind(val);
+
                err = 0;
                break;
        }
@@ -1559,6 +1577,7 @@ netlink_kernel_create(struct net *net, int unit,
                rcu_assign_pointer(nl_table[unit].listeners, listeners);
                nl_table[unit].cb_mutex = cb_mutex;
                nl_table[unit].module = module;
+               nl_table[unit].bind = cfg ? cfg->bind : NULL;
                nl_table[unit].registered = 1;
        } else {
                kfree(listeners);