Commit 4fdb3bb7 authored by Harald Welte's avatar Harald Welte Committed by David S. Miller

[NETLINK]: Add properly module refcounting for kernel netlink sockets.

- Remove bogus code for compiling netlink as module
- Add module refcounting support for modules implementing a netlink
  protocol
- Add support for autoloading modules that implement a netlink protocol
  as soon as someone opens a socket for that protocol
Signed-off-by: default avatarHarald Welte <laforge@netfilter.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 020b4c12
...@@ -88,7 +88,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, ...@@ -88,7 +88,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
dev->groups = 23; dev->groups = 23;
dev->seq = 1; dev->seq = 1;
dev->nls = netlink_kernel_create(NETLINK_W1, NULL); dev->nls = netlink_kernel_create(NETLINK_W1, NULL, THIS_MODULE);
if (!dev->nls) { if (!dev->nls) {
printk(KERN_ERR "Failed to create new netlink socket(%u) for w1 master %s.\n", printk(KERN_ERR "Failed to create new netlink socket(%u) for w1 master %s.\n",
NETLINK_NFLOG, dev->dev.bus_id); NETLINK_NFLOG, dev->dev.bus_id);
...@@ -225,3 +225,5 @@ void w1_remove_master_device(struct w1_bus_master *bm) ...@@ -225,3 +225,5 @@ void w1_remove_master_device(struct w1_bus_master *bm)
EXPORT_SYMBOL(w1_add_master_device); EXPORT_SYMBOL(w1_add_master_device);
EXPORT_SYMBOL(w1_remove_master_device); EXPORT_SYMBOL(w1_remove_master_device);
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_W1);
...@@ -282,5 +282,8 @@ static struct proto_ops name##_ops = { \ ...@@ -282,5 +282,8 @@ static struct proto_ops name##_ops = { \
#define MODULE_ALIAS_NETPROTO(proto) \ #define MODULE_ALIAS_NETPROTO(proto) \
MODULE_ALIAS("net-pf-" __stringify(proto)) MODULE_ALIAS("net-pf-" __stringify(proto))
#define MODULE_ALIAS_NET_PF_PROTO(pf, proto) \
MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto))
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */ #endif /* _LINUX_NET_H */
...@@ -117,7 +117,7 @@ struct netlink_skb_parms ...@@ -117,7 +117,7 @@ struct netlink_skb_parms
#define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds) #define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds)
extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)); extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct module *module);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
......
...@@ -514,7 +514,8 @@ static int __init audit_init(void) ...@@ -514,7 +514,8 @@ static int __init audit_init(void)
{ {
printk(KERN_INFO "audit: initializing netlink socket (%s)\n", printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
audit_default ? "enabled" : "disabled"); audit_default ? "enabled" : "disabled");
audit_sock = netlink_kernel_create(NETLINK_AUDIT, audit_receive); audit_sock = netlink_kernel_create(NETLINK_AUDIT, audit_receive,
THIS_MODULE);
if (!audit_sock) if (!audit_sock)
audit_panic("cannot initialize netlink socket"); audit_panic("cannot initialize netlink socket");
......
...@@ -153,7 +153,8 @@ EXPORT_SYMBOL_GPL(kobject_uevent_atomic); ...@@ -153,7 +153,8 @@ EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
static int __init kobject_uevent_init(void) static int __init kobject_uevent_init(void)
{ {
uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL); uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL,
THIS_MODULE);
if (!uevent_sock) { if (!uevent_sock) {
printk(KERN_ERR printk(KERN_ERR
......
...@@ -258,7 +258,7 @@ static int __init init(void) ...@@ -258,7 +258,7 @@ static int __init init(void)
spin_lock_init(&ulog_buffers[i].lock); spin_lock_init(&ulog_buffers[i].lock);
} }
ebtulognl = netlink_kernel_create(NETLINK_NFLOG, NULL); ebtulognl = netlink_kernel_create(NETLINK_NFLOG, NULL, THIS_MODULE);
if (!ebtulognl) if (!ebtulognl)
ret = -ENOMEM; ret = -ENOMEM;
else if ((ret = ebt_register_watcher(&ulog))) else if ((ret = ebt_register_watcher(&ulog)))
......
...@@ -708,7 +708,7 @@ void __init rtnetlink_init(void) ...@@ -708,7 +708,7 @@ void __init rtnetlink_init(void)
if (!rta_buf) if (!rta_buf)
panic("rtnetlink_init: cannot allocate rta_buf\n"); panic("rtnetlink_init: cannot allocate rta_buf\n");
rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv); rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv, THIS_MODULE);
if (rtnl == NULL) if (rtnl == NULL)
panic("rtnetlink_init: cannot initialize rtnetlink\n"); panic("rtnetlink_init: cannot initialize rtnetlink\n");
netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
......
...@@ -138,7 +138,8 @@ static int __init init(void) ...@@ -138,7 +138,8 @@ static int __init init(void)
{ {
int rv = 0; int rv = 0;
dnrmg = netlink_kernel_create(NETLINK_DNRTMSG, dnrmg_receive_user_sk); dnrmg = netlink_kernel_create(NETLINK_DNRTMSG, dnrmg_receive_user_sk,
THIS_MODULE);
if (dnrmg == NULL) { if (dnrmg == NULL) {
printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket"); printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
return -ENOMEM; return -ENOMEM;
...@@ -162,6 +163,7 @@ static void __exit fini(void) ...@@ -162,6 +163,7 @@ static void __exit fini(void)
MODULE_DESCRIPTION("DECnet Routing Message Grabulator"); MODULE_DESCRIPTION("DECnet Routing Message Grabulator");
MODULE_AUTHOR("Steven Whitehouse <steve@chygwyn.com>"); MODULE_AUTHOR("Steven Whitehouse <steve@chygwyn.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_DNRTMSG);
module_init(init); module_init(init);
module_exit(fini); module_exit(fini);
......
...@@ -567,7 +567,7 @@ static void nl_fib_input(struct sock *sk, int len) ...@@ -567,7 +567,7 @@ static void nl_fib_input(struct sock *sk, int len)
static void nl_fib_lookup_init(void) static void nl_fib_lookup_init(void)
{ {
netlink_kernel_create(NETLINK_FIB_LOOKUP, nl_fib_input); netlink_kernel_create(NETLINK_FIB_LOOKUP, nl_fib_input, THIS_MODULE);
} }
static void fib_disable_ip(struct net_device *dev, int force) static void fib_disable_ip(struct net_device *dev, int force)
......
...@@ -692,7 +692,8 @@ init_or_cleanup(int init) ...@@ -692,7 +692,8 @@ init_or_cleanup(int init)
goto cleanup; goto cleanup;
netlink_register_notifier(&ipq_nl_notifier); netlink_register_notifier(&ipq_nl_notifier);
ipqnl = netlink_kernel_create(NETLINK_FIREWALL, ipq_rcv_sk); ipqnl = netlink_kernel_create(NETLINK_FIREWALL, ipq_rcv_sk,
THIS_MODULE);
if (ipqnl == NULL) { if (ipqnl == NULL) {
printk(KERN_ERR "ip_queue: failed to create netlink socket\n"); printk(KERN_ERR "ip_queue: failed to create netlink socket\n");
goto cleanup_netlink_notifier; goto cleanup_netlink_notifier;
......
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("iptables userspace logging module"); MODULE_DESCRIPTION("iptables userspace logging module");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NFLOG);
#define ULOG_NL_EVENT 111 /* Harald's favorite number */ #define ULOG_NL_EVENT 111 /* Harald's favorite number */
#define ULOG_MAXNLGROUPS 32 /* numer of nlgroups */ #define ULOG_MAXNLGROUPS 32 /* numer of nlgroups */
...@@ -372,7 +373,7 @@ static int __init init(void) ...@@ -372,7 +373,7 @@ static int __init init(void)
ulog_buffers[i].timer.data = i; ulog_buffers[i].timer.data = i;
} }
nflognl = netlink_kernel_create(NETLINK_NFLOG, NULL); nflognl = netlink_kernel_create(NETLINK_NFLOG, NULL, THIS_MODULE);
if (!nflognl) if (!nflognl)
return -ENOMEM; return -ENOMEM;
......
...@@ -774,7 +774,8 @@ static void tcpdiag_rcv(struct sock *sk, int len) ...@@ -774,7 +774,8 @@ static void tcpdiag_rcv(struct sock *sk, int len)
static int __init tcpdiag_init(void) static int __init tcpdiag_init(void)
{ {
tcpnl = netlink_kernel_create(NETLINK_TCPDIAG, tcpdiag_rcv); tcpnl = netlink_kernel_create(NETLINK_TCPDIAG, tcpdiag_rcv,
THIS_MODULE);
if (tcpnl == NULL) if (tcpnl == NULL)
return -ENOMEM; return -ENOMEM;
return 0; return 0;
......
...@@ -685,7 +685,7 @@ init_or_cleanup(int init) ...@@ -685,7 +685,7 @@ init_or_cleanup(int init)
goto cleanup; goto cleanup;
netlink_register_notifier(&ipq_nl_notifier); netlink_register_notifier(&ipq_nl_notifier);
ipqnl = netlink_kernel_create(NETLINK_IP6_FW, ipq_rcv_sk); ipqnl = netlink_kernel_create(NETLINK_IP6_FW, ipq_rcv_sk, THIS_MODULE);
if (ipqnl == NULL) { if (ipqnl == NULL) {
printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); printk(KERN_ERR "ip6_queue: failed to create netlink socket\n");
goto cleanup_netlink_notifier; goto cleanup_netlink_notifier;
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink.h>
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
static char __initdata nfversion[] = "0.30"; static char __initdata nfversion[] = "0.30";
...@@ -324,7 +326,8 @@ int __init nfnetlink_init(void) ...@@ -324,7 +326,8 @@ int __init nfnetlink_init(void)
{ {
printk("Netfilter messages via NETLINK v%s.\n", nfversion); printk("Netfilter messages via NETLINK v%s.\n", nfversion);
nfnl = netlink_kernel_create(NETLINK_NETFILTER, nfnetlink_rcv); nfnl = netlink_kernel_create(NETLINK_NETFILTER, nfnetlink_rcv,
THIS_MODULE);
if (!nfnl) { if (!nfnl) {
printk(KERN_ERR "cannot initialize nfnetlink!\n"); printk(KERN_ERR "cannot initialize nfnetlink!\n");
return -1; return -1;
......
...@@ -13,7 +13,12 @@ ...@@ -13,7 +13,12 @@
* added netlink_proto_exit * added netlink_proto_exit
* Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo <acme@conectiva.com.br> * Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo <acme@conectiva.com.br>
* use nlk_sk, as sk->protinfo is on a diet 8) * use nlk_sk, as sk->protinfo is on a diet 8)
* * Fri Jul 22 19:51:12 MEST 2005 Harald Welte <laforge@gnumonks.org>
* - inc module use count of module that owns
* the kernel socket in case userspace opens
* socket of same protocol
* - remove all module support, since netlink is
* mandatory if CONFIG_NET=y these days
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -92,6 +97,7 @@ struct netlink_table { ...@@ -92,6 +97,7 @@ struct netlink_table {
struct nl_pid_hash hash; struct nl_pid_hash hash;
struct hlist_head mc_list; struct hlist_head mc_list;
unsigned int nl_nonroot; unsigned int nl_nonroot;
struct proto_ops *p_ops;
}; };
static struct netlink_table *nl_table; static struct netlink_table *nl_table;
...@@ -341,7 +347,21 @@ static int netlink_create(struct socket *sock, int protocol) ...@@ -341,7 +347,21 @@ static int netlink_create(struct socket *sock, int protocol)
if (protocol<0 || protocol >= MAX_LINKS) if (protocol<0 || protocol >= MAX_LINKS)
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
sock->ops = &netlink_ops; netlink_table_grab();
if (!nl_table[protocol].hash.entries) {
#ifdef CONFIG_KMOD
/* We do 'best effort'. If we find a matching module,
* it is loaded. If not, we don't return an error to
* allow pure userspace<->userspace communication. -HW
*/
netlink_table_ungrab();
request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol);
netlink_table_grab();
#endif
}
netlink_table_ungrab();
sock->ops = nl_table[protocol].p_ops;
sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1); sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1);
if (!sk) if (!sk)
...@@ -394,6 +414,22 @@ static int netlink_release(struct socket *sock) ...@@ -394,6 +414,22 @@ static int netlink_release(struct socket *sock)
}; };
notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n);
} }
/* When this is a kernel socket, we need to remove the owner pointer,
* since we don't know whether the module will be dying at any given
* point - HW
*/
if (!nlk->pid) {
struct proto_ops *p_tmp;
netlink_table_grab();
p_tmp = nl_table[sk->sk_protocol].p_ops;
if (p_tmp != &netlink_ops) {
nl_table[sk->sk_protocol].p_ops = &netlink_ops;
kfree(p_tmp);
}
netlink_table_ungrab();
}
sock_put(sk); sock_put(sk);
return 0; return 0;
...@@ -1023,8 +1059,9 @@ static void netlink_data_ready(struct sock *sk, int len) ...@@ -1023,8 +1059,9 @@ static void netlink_data_ready(struct sock *sk, int len)
*/ */
struct sock * struct sock *
netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)) netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct module *module)
{ {
struct proto_ops *p_ops;
struct socket *sock; struct socket *sock;
struct sock *sk; struct sock *sk;
...@@ -1034,22 +1071,63 @@ netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)) ...@@ -1034,22 +1071,63 @@ netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len))
if (unit<0 || unit>=MAX_LINKS) if (unit<0 || unit>=MAX_LINKS)
return NULL; return NULL;
/* Do a quick check, to make us not go down to netlink_insert()
* if protocol already has kernel socket.
*/
sk = netlink_lookup(unit, 0);
if (unlikely(sk)) {
sock_put(sk);
return NULL;
}
if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
return NULL; return NULL;
sk = NULL;
if (module) {
/* Every registering protocol implemented in a module needs
* it's own p_ops, since the socket code cannot deal with
* module refcounting otherwise. -HW
*/
p_ops = kmalloc(sizeof(*p_ops), GFP_KERNEL);
if (!p_ops)
goto out_sock_release;
memcpy(p_ops, &netlink_ops, sizeof(*p_ops));
p_ops->owner = module;
} else
p_ops = &netlink_ops;
netlink_table_grab();
nl_table[unit].p_ops = p_ops;
netlink_table_ungrab();
if (netlink_create(sock, unit) < 0) { if (netlink_create(sock, unit) < 0) {
sock_release(sock); sk = NULL;
return NULL; goto out_kfree_p_ops;
} }
sk = sock->sk; sk = sock->sk;
sk->sk_data_ready = netlink_data_ready; sk->sk_data_ready = netlink_data_ready;
if (input) if (input)
nlk_sk(sk)->data_ready = input; nlk_sk(sk)->data_ready = input;
if (netlink_insert(sk, 0)) { if (netlink_insert(sk, 0)) {
sock_release(sock); sk = NULL;
return NULL; goto out_kfree_p_ops;
}
return sk;
out_kfree_p_ops:
netlink_table_grab();
if (nl_table[unit].p_ops != &netlink_ops) {
kfree(nl_table[unit].p_ops);
nl_table[unit].p_ops = &netlink_ops;
} }
netlink_table_ungrab();
out_sock_release:
sock_release(sock);
return sk; return sk;
} }
...@@ -1413,6 +1491,8 @@ enomem: ...@@ -1413,6 +1491,8 @@ enomem:
for (i = 0; i < MAX_LINKS; i++) { for (i = 0; i < MAX_LINKS; i++) {
struct nl_pid_hash *hash = &nl_table[i].hash; struct nl_pid_hash *hash = &nl_table[i].hash;
nl_table[i].p_ops = &netlink_ops;
hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table));
if (!hash->table) { if (!hash->table) {
while (i-- > 0) while (i-- > 0)
...@@ -1438,21 +1518,7 @@ out: ...@@ -1438,21 +1518,7 @@ out:
return err; return err;
} }
static void __exit netlink_proto_exit(void)
{
sock_unregister(PF_NETLINK);
proc_net_remove("netlink");
kfree(nl_table);
nl_table = NULL;
proto_unregister(&netlink_proto);
}
core_initcall(netlink_proto_init); core_initcall(netlink_proto_init);
module_exit(netlink_proto_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_NETLINK);
EXPORT_SYMBOL(netlink_ack); EXPORT_SYMBOL(netlink_ack);
EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_broadcast);
......
...@@ -1519,7 +1519,8 @@ static int __init xfrm_user_init(void) ...@@ -1519,7 +1519,8 @@ static int __init xfrm_user_init(void)
{ {
printk(KERN_INFO "Initializing IPsec netlink socket\n"); printk(KERN_INFO "Initializing IPsec netlink socket\n");
xfrm_nl = netlink_kernel_create(NETLINK_XFRM, xfrm_netlink_rcv); xfrm_nl = netlink_kernel_create(NETLINK_XFRM, xfrm_netlink_rcv,
THIS_MODULE);
if (xfrm_nl == NULL) if (xfrm_nl == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1537,3 +1538,4 @@ static void __exit xfrm_user_exit(void) ...@@ -1537,3 +1538,4 @@ static void __exit xfrm_user_exit(void)
module_init(xfrm_user_init); module_init(xfrm_user_init);
module_exit(xfrm_user_exit); module_exit(xfrm_user_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM);
...@@ -103,7 +103,7 @@ void selnl_notify_policyload(u32 seqno) ...@@ -103,7 +103,7 @@ void selnl_notify_policyload(u32 seqno)
static int __init selnl_init(void) static int __init selnl_init(void)
{ {
selnl = netlink_kernel_create(NETLINK_SELINUX, NULL); selnl = netlink_kernel_create(NETLINK_SELINUX, NULL, THIS_MODULE);
if (selnl == NULL) if (selnl == NULL)
panic("SELinux: Cannot create netlink socket."); panic("SELinux: Cannot create netlink socket.");
netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
......
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