aboutsummaryrefslogtreecommitdiff
path: root/net/netlink/af_netlink.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-03-21 09:31:48 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-21 09:31:48 -0800
commit3d1f337b3e7378923c89f37afb573a918ef40be5 (patch)
tree386798378567a10d1c7b24f599cb50f70298694c /net/netlink/af_netlink.c
parent2bf2154c6bb5599e3ec3f73c34861a0b12aa839e (diff)
parent5e35941d990123f155b02d5663e51a24f816b6f3 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6: (235 commits) [NETFILTER]: Add H.323 conntrack/NAT helper [TG3]: Don't mark tg3_test_registers() as returning const. [IPV6]: Cleanups for net/ipv6/addrconf.c (kzalloc, early exit) v2 [IPV6]: Nearly complete kzalloc cleanup for net/ipv6 [IPV6]: Cleanup of net/ipv6/reassambly.c [BRIDGE]: Remove duplicate const from is_link_local() argument type. [DECNET]: net/decnet/dn_route.c: fix inconsequent NULL checking [TG3]: make drivers/net/tg3.c:tg3_request_irq() static [BRIDGE]: use LLC to send STP [LLC]: llc_mac_hdr_init const arguments [BRIDGE]: allow show/store of group multicast address [BRIDGE]: use llc for receiving STP packets [BRIDGE]: stp timer to jiffies cleanup [BRIDGE]: forwarding remove unneeded preempt and bh diasables [BRIDGE]: netfilter inline cleanup [BRIDGE]: netfilter VLAN macro cleanup [BRIDGE]: netfilter dont use __constant_htons [BRIDGE]: netfilter whitespace [BRIDGE]: optimize frame pass up [BRIDGE]: use kzalloc ...
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r--net/netlink/af_netlink.c52
1 files changed, 48 insertions, 4 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59dc7d14060..d00a9034cb5 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -106,6 +106,7 @@ struct nl_pid_hash {
struct netlink_table {
struct nl_pid_hash hash;
struct hlist_head mc_list;
+ unsigned long *listeners;
unsigned int nl_nonroot;
unsigned int groups;
struct module *module;
@@ -296,6 +297,24 @@ static inline int nl_pid_hash_dilute(struct nl_pid_hash *hash, int len)
static const struct proto_ops netlink_ops;
+static void
+netlink_update_listeners(struct sock *sk)
+{
+ struct netlink_table *tbl = &nl_table[sk->sk_protocol];
+ struct hlist_node *node;
+ unsigned long mask;
+ unsigned int i;
+
+ for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
+ mask = 0;
+ sk_for_each_bound(sk, node, &tbl->mc_list)
+ mask |= nlk_sk(sk)->groups[i];
+ tbl->listeners[i] = mask;
+ }
+ /* this function is only called with the netlink table "grabbed", which
+ * makes sure updates are visible before bind or setsockopt return. */
+}
+
static int netlink_insert(struct sock *sk, u32 pid)
{
struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash;
@@ -456,12 +475,14 @@ static int netlink_release(struct socket *sock)
if (nlk->module)
module_put(nlk->module);
+ netlink_table_grab();
if (nlk->flags & NETLINK_KERNEL_SOCKET) {
- netlink_table_grab();
+ kfree(nl_table[sk->sk_protocol].listeners);
nl_table[sk->sk_protocol].module = NULL;
nl_table[sk->sk_protocol].registered = 0;
- netlink_table_ungrab();
- }
+ } else if (nlk->subscriptions)
+ netlink_update_listeners(sk);
+ netlink_table_ungrab();
kfree(nlk->groups);
nlk->groups = NULL;
@@ -589,6 +610,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len
hweight32(nladdr->nl_groups) -
hweight32(nlk->groups[0]));
nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups;
+ netlink_update_listeners(sk);
netlink_table_ungrab();
return 0;
@@ -807,6 +829,17 @@ retry:
return netlink_sendskb(sk, skb, ssk->sk_protocol);
}
+int netlink_has_listeners(struct sock *sk, unsigned int group)
+{
+ int res = 0;
+
+ BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+ if (group - 1 < nl_table[sk->sk_protocol].groups)
+ res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners);
+ return res;
+}
+EXPORT_SYMBOL_GPL(netlink_has_listeners);
+
static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb)
{
struct netlink_sock *nlk = nlk_sk(sk);
@@ -1011,6 +1044,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
else
__clear_bit(val - 1, nlk->groups);
netlink_update_subscriptions(sk, subscriptions);
+ netlink_update_listeners(sk);
netlink_table_ungrab();
err = 0;
break;
@@ -1237,6 +1271,7 @@ netlink_kernel_create(int unit, unsigned int groups,
struct socket *sock;
struct sock *sk;
struct netlink_sock *nlk;
+ unsigned long *listeners = NULL;
if (!nl_table)
return NULL;
@@ -1250,6 +1285,13 @@ netlink_kernel_create(int unit, unsigned int groups,
if (__netlink_create(sock, unit) < 0)
goto out_sock_release;
+ if (groups < 32)
+ groups = 32;
+
+ listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
+ if (!listeners)
+ goto out_sock_release;
+
sk = sock->sk;
sk->sk_data_ready = netlink_data_ready;
if (input)
@@ -1262,7 +1304,8 @@ netlink_kernel_create(int unit, unsigned int groups,
nlk->flags |= NETLINK_KERNEL_SOCKET;
netlink_table_grab();
- nl_table[unit].groups = groups < 32 ? 32 : groups;
+ nl_table[unit].groups = groups;
+ nl_table[unit].listeners = listeners;
nl_table[unit].module = module;
nl_table[unit].registered = 1;
netlink_table_ungrab();
@@ -1270,6 +1313,7 @@ netlink_kernel_create(int unit, unsigned int groups,
return sk;
out_sock_release:
+ kfree(listeners);
sock_release(sock);
return NULL;
}