diff options
author | Harald Welte <laforge@netfilter.org> | 2005-08-09 22:40:55 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 18:35:08 -0400 |
commit | 4fdb3bb723db469717c6d38fda667d8b0fa86ebd (patch) | |
tree | 43d82e717922e6319cf8a8f9dc5ee902c651b491 /net/netlink | |
parent | 020b4c12dbe3868d792a01d7c1470cd837abe10f (diff) |
[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: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 108 |
1 files changed, 87 insertions, 21 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index ff774a06c89d..5d487cd69c8c 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -13,7 +13,12 @@ | |||
13 | * added netlink_proto_exit | 13 | * added netlink_proto_exit |
14 | * Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo <acme@conectiva.com.br> | 14 | * Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo <acme@conectiva.com.br> |
15 | * use nlk_sk, as sk->protinfo is on a diet 8) | 15 | * use nlk_sk, as sk->protinfo is on a diet 8) |
16 | * | 16 | * Fri Jul 22 19:51:12 MEST 2005 Harald Welte <laforge@gnumonks.org> |
17 | * - inc module use count of module that owns | ||
18 | * the kernel socket in case userspace opens | ||
19 | * socket of same protocol | ||
20 | * - remove all module support, since netlink is | ||
21 | * mandatory if CONFIG_NET=y these days | ||
17 | */ | 22 | */ |
18 | 23 | ||
19 | #include <linux/config.h> | 24 | #include <linux/config.h> |
@@ -92,6 +97,7 @@ struct netlink_table { | |||
92 | struct nl_pid_hash hash; | 97 | struct nl_pid_hash hash; |
93 | struct hlist_head mc_list; | 98 | struct hlist_head mc_list; |
94 | unsigned int nl_nonroot; | 99 | unsigned int nl_nonroot; |
100 | struct proto_ops *p_ops; | ||
95 | }; | 101 | }; |
96 | 102 | ||
97 | static struct netlink_table *nl_table; | 103 | static struct netlink_table *nl_table; |
@@ -341,7 +347,21 @@ static int netlink_create(struct socket *sock, int protocol) | |||
341 | if (protocol<0 || protocol >= MAX_LINKS) | 347 | if (protocol<0 || protocol >= MAX_LINKS) |
342 | return -EPROTONOSUPPORT; | 348 | return -EPROTONOSUPPORT; |
343 | 349 | ||
344 | sock->ops = &netlink_ops; | 350 | netlink_table_grab(); |
351 | if (!nl_table[protocol].hash.entries) { | ||
352 | #ifdef CONFIG_KMOD | ||
353 | /* We do 'best effort'. If we find a matching module, | ||
354 | * it is loaded. If not, we don't return an error to | ||
355 | * allow pure userspace<->userspace communication. -HW | ||
356 | */ | ||
357 | netlink_table_ungrab(); | ||
358 | request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol); | ||
359 | netlink_table_grab(); | ||
360 | #endif | ||
361 | } | ||
362 | netlink_table_ungrab(); | ||
363 | |||
364 | sock->ops = nl_table[protocol].p_ops; | ||
345 | 365 | ||
346 | sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1); | 366 | sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1); |
347 | if (!sk) | 367 | if (!sk) |
@@ -394,6 +414,22 @@ static int netlink_release(struct socket *sock) | |||
394 | }; | 414 | }; |
395 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); | 415 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); |
396 | } | 416 | } |
417 | |||
418 | /* When this is a kernel socket, we need to remove the owner pointer, | ||
419 | * since we don't know whether the module will be dying at any given | ||
420 | * point - HW | ||
421 | */ | ||
422 | if (!nlk->pid) { | ||
423 | struct proto_ops *p_tmp; | ||
424 | |||
425 | netlink_table_grab(); | ||
426 | p_tmp = nl_table[sk->sk_protocol].p_ops; | ||
427 | if (p_tmp != &netlink_ops) { | ||
428 | nl_table[sk->sk_protocol].p_ops = &netlink_ops; | ||
429 | kfree(p_tmp); | ||
430 | } | ||
431 | netlink_table_ungrab(); | ||
432 | } | ||
397 | 433 | ||
398 | sock_put(sk); | 434 | sock_put(sk); |
399 | return 0; | 435 | return 0; |
@@ -1023,8 +1059,9 @@ static void netlink_data_ready(struct sock *sk, int len) | |||
1023 | */ | 1059 | */ |
1024 | 1060 | ||
1025 | struct sock * | 1061 | struct sock * |
1026 | netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)) | 1062 | netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct module *module) |
1027 | { | 1063 | { |
1064 | struct proto_ops *p_ops; | ||
1028 | struct socket *sock; | 1065 | struct socket *sock; |
1029 | struct sock *sk; | 1066 | struct sock *sk; |
1030 | 1067 | ||
@@ -1034,22 +1071,63 @@ netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)) | |||
1034 | if (unit<0 || unit>=MAX_LINKS) | 1071 | if (unit<0 || unit>=MAX_LINKS) |
1035 | return NULL; | 1072 | return NULL; |
1036 | 1073 | ||
1074 | /* Do a quick check, to make us not go down to netlink_insert() | ||
1075 | * if protocol already has kernel socket. | ||
1076 | */ | ||
1077 | sk = netlink_lookup(unit, 0); | ||
1078 | if (unlikely(sk)) { | ||
1079 | sock_put(sk); | ||
1080 | return NULL; | ||
1081 | } | ||
1082 | |||
1037 | if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) | 1083 | if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) |
1038 | return NULL; | 1084 | return NULL; |
1039 | 1085 | ||
1086 | sk = NULL; | ||
1087 | if (module) { | ||
1088 | /* Every registering protocol implemented in a module needs | ||
1089 | * it's own p_ops, since the socket code cannot deal with | ||
1090 | * module refcounting otherwise. -HW | ||
1091 | */ | ||
1092 | p_ops = kmalloc(sizeof(*p_ops), GFP_KERNEL); | ||
1093 | if (!p_ops) | ||
1094 | goto out_sock_release; | ||
1095 | |||
1096 | memcpy(p_ops, &netlink_ops, sizeof(*p_ops)); | ||
1097 | p_ops->owner = module; | ||
1098 | } else | ||
1099 | p_ops = &netlink_ops; | ||
1100 | |||
1101 | netlink_table_grab(); | ||
1102 | nl_table[unit].p_ops = p_ops; | ||
1103 | netlink_table_ungrab(); | ||
1104 | |||
1040 | if (netlink_create(sock, unit) < 0) { | 1105 | if (netlink_create(sock, unit) < 0) { |
1041 | sock_release(sock); | 1106 | sk = NULL; |
1042 | return NULL; | 1107 | goto out_kfree_p_ops; |
1043 | } | 1108 | } |
1109 | |||
1044 | sk = sock->sk; | 1110 | sk = sock->sk; |
1045 | sk->sk_data_ready = netlink_data_ready; | 1111 | sk->sk_data_ready = netlink_data_ready; |
1046 | if (input) | 1112 | if (input) |
1047 | nlk_sk(sk)->data_ready = input; | 1113 | nlk_sk(sk)->data_ready = input; |
1048 | 1114 | ||
1049 | if (netlink_insert(sk, 0)) { | 1115 | if (netlink_insert(sk, 0)) { |
1050 | sock_release(sock); | 1116 | sk = NULL; |
1051 | return NULL; | 1117 | goto out_kfree_p_ops; |
1118 | } | ||
1119 | |||
1120 | return sk; | ||
1121 | |||
1122 | out_kfree_p_ops: | ||
1123 | netlink_table_grab(); | ||
1124 | if (nl_table[unit].p_ops != &netlink_ops) { | ||
1125 | kfree(nl_table[unit].p_ops); | ||
1126 | nl_table[unit].p_ops = &netlink_ops; | ||
1052 | } | 1127 | } |
1128 | netlink_table_ungrab(); | ||
1129 | out_sock_release: | ||
1130 | sock_release(sock); | ||
1053 | return sk; | 1131 | return sk; |
1054 | } | 1132 | } |
1055 | 1133 | ||
@@ -1413,6 +1491,8 @@ enomem: | |||
1413 | for (i = 0; i < MAX_LINKS; i++) { | 1491 | for (i = 0; i < MAX_LINKS; i++) { |
1414 | struct nl_pid_hash *hash = &nl_table[i].hash; | 1492 | struct nl_pid_hash *hash = &nl_table[i].hash; |
1415 | 1493 | ||
1494 | nl_table[i].p_ops = &netlink_ops; | ||
1495 | |||
1416 | hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); | 1496 | hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); |
1417 | if (!hash->table) { | 1497 | if (!hash->table) { |
1418 | while (i-- > 0) | 1498 | while (i-- > 0) |
@@ -1438,21 +1518,7 @@ out: | |||
1438 | return err; | 1518 | return err; |
1439 | } | 1519 | } |
1440 | 1520 | ||
1441 | static void __exit netlink_proto_exit(void) | ||
1442 | { | ||
1443 | sock_unregister(PF_NETLINK); | ||
1444 | proc_net_remove("netlink"); | ||
1445 | kfree(nl_table); | ||
1446 | nl_table = NULL; | ||
1447 | proto_unregister(&netlink_proto); | ||
1448 | } | ||
1449 | |||
1450 | core_initcall(netlink_proto_init); | 1521 | core_initcall(netlink_proto_init); |
1451 | module_exit(netlink_proto_exit); | ||
1452 | |||
1453 | MODULE_LICENSE("GPL"); | ||
1454 | |||
1455 | MODULE_ALIAS_NETPROTO(PF_NETLINK); | ||
1456 | 1522 | ||
1457 | EXPORT_SYMBOL(netlink_ack); | 1523 | EXPORT_SYMBOL(netlink_ack); |
1458 | EXPORT_SYMBOL(netlink_broadcast); | 1524 | EXPORT_SYMBOL(netlink_broadcast); |