diff options
-rw-r--r-- | include/linux/netlink.h | 1 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 52 |
2 files changed, 49 insertions, 4 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index c256ebe2a7b4..f8f3d1c927f8 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
@@ -151,6 +151,7 @@ struct netlink_skb_parms | |||
151 | 151 | ||
152 | extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); | 152 | extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); |
153 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); | 153 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); |
154 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); | ||
154 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); | 155 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); |
155 | extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, | 156 | extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, |
156 | __u32 group, gfp_t allocation); | 157 | __u32 group, gfp_t allocation); |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 59dc7d140600..d00a9034cb5f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -106,6 +106,7 @@ struct nl_pid_hash { | |||
106 | struct netlink_table { | 106 | struct netlink_table { |
107 | struct nl_pid_hash hash; | 107 | struct nl_pid_hash hash; |
108 | struct hlist_head mc_list; | 108 | struct hlist_head mc_list; |
109 | unsigned long *listeners; | ||
109 | unsigned int nl_nonroot; | 110 | unsigned int nl_nonroot; |
110 | unsigned int groups; | 111 | unsigned int groups; |
111 | struct module *module; | 112 | struct module *module; |
@@ -296,6 +297,24 @@ static inline int nl_pid_hash_dilute(struct nl_pid_hash *hash, int len) | |||
296 | 297 | ||
297 | static const struct proto_ops netlink_ops; | 298 | static const struct proto_ops netlink_ops; |
298 | 299 | ||
300 | static void | ||
301 | netlink_update_listeners(struct sock *sk) | ||
302 | { | ||
303 | struct netlink_table *tbl = &nl_table[sk->sk_protocol]; | ||
304 | struct hlist_node *node; | ||
305 | unsigned long mask; | ||
306 | unsigned int i; | ||
307 | |||
308 | for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) { | ||
309 | mask = 0; | ||
310 | sk_for_each_bound(sk, node, &tbl->mc_list) | ||
311 | mask |= nlk_sk(sk)->groups[i]; | ||
312 | tbl->listeners[i] = mask; | ||
313 | } | ||
314 | /* this function is only called with the netlink table "grabbed", which | ||
315 | * makes sure updates are visible before bind or setsockopt return. */ | ||
316 | } | ||
317 | |||
299 | static int netlink_insert(struct sock *sk, u32 pid) | 318 | static int netlink_insert(struct sock *sk, u32 pid) |
300 | { | 319 | { |
301 | struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; | 320 | struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; |
@@ -456,12 +475,14 @@ static int netlink_release(struct socket *sock) | |||
456 | if (nlk->module) | 475 | if (nlk->module) |
457 | module_put(nlk->module); | 476 | module_put(nlk->module); |
458 | 477 | ||
478 | netlink_table_grab(); | ||
459 | if (nlk->flags & NETLINK_KERNEL_SOCKET) { | 479 | if (nlk->flags & NETLINK_KERNEL_SOCKET) { |
460 | netlink_table_grab(); | 480 | kfree(nl_table[sk->sk_protocol].listeners); |
461 | nl_table[sk->sk_protocol].module = NULL; | 481 | nl_table[sk->sk_protocol].module = NULL; |
462 | nl_table[sk->sk_protocol].registered = 0; | 482 | nl_table[sk->sk_protocol].registered = 0; |
463 | netlink_table_ungrab(); | 483 | } else if (nlk->subscriptions) |
464 | } | 484 | netlink_update_listeners(sk); |
485 | netlink_table_ungrab(); | ||
465 | 486 | ||
466 | kfree(nlk->groups); | 487 | kfree(nlk->groups); |
467 | nlk->groups = NULL; | 488 | nlk->groups = NULL; |
@@ -589,6 +610,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len | |||
589 | hweight32(nladdr->nl_groups) - | 610 | hweight32(nladdr->nl_groups) - |
590 | hweight32(nlk->groups[0])); | 611 | hweight32(nlk->groups[0])); |
591 | nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; | 612 | nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; |
613 | netlink_update_listeners(sk); | ||
592 | netlink_table_ungrab(); | 614 | netlink_table_ungrab(); |
593 | 615 | ||
594 | return 0; | 616 | return 0; |
@@ -807,6 +829,17 @@ retry: | |||
807 | return netlink_sendskb(sk, skb, ssk->sk_protocol); | 829 | return netlink_sendskb(sk, skb, ssk->sk_protocol); |
808 | } | 830 | } |
809 | 831 | ||
832 | int netlink_has_listeners(struct sock *sk, unsigned int group) | ||
833 | { | ||
834 | int res = 0; | ||
835 | |||
836 | BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET)); | ||
837 | if (group - 1 < nl_table[sk->sk_protocol].groups) | ||
838 | res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners); | ||
839 | return res; | ||
840 | } | ||
841 | EXPORT_SYMBOL_GPL(netlink_has_listeners); | ||
842 | |||
810 | static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) | 843 | static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) |
811 | { | 844 | { |
812 | struct netlink_sock *nlk = nlk_sk(sk); | 845 | struct netlink_sock *nlk = nlk_sk(sk); |
@@ -1011,6 +1044,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
1011 | else | 1044 | else |
1012 | __clear_bit(val - 1, nlk->groups); | 1045 | __clear_bit(val - 1, nlk->groups); |
1013 | netlink_update_subscriptions(sk, subscriptions); | 1046 | netlink_update_subscriptions(sk, subscriptions); |
1047 | netlink_update_listeners(sk); | ||
1014 | netlink_table_ungrab(); | 1048 | netlink_table_ungrab(); |
1015 | err = 0; | 1049 | err = 0; |
1016 | break; | 1050 | break; |
@@ -1237,6 +1271,7 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
1237 | struct socket *sock; | 1271 | struct socket *sock; |
1238 | struct sock *sk; | 1272 | struct sock *sk; |
1239 | struct netlink_sock *nlk; | 1273 | struct netlink_sock *nlk; |
1274 | unsigned long *listeners = NULL; | ||
1240 | 1275 | ||
1241 | if (!nl_table) | 1276 | if (!nl_table) |
1242 | return NULL; | 1277 | return NULL; |
@@ -1250,6 +1285,13 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
1250 | if (__netlink_create(sock, unit) < 0) | 1285 | if (__netlink_create(sock, unit) < 0) |
1251 | goto out_sock_release; | 1286 | goto out_sock_release; |
1252 | 1287 | ||
1288 | if (groups < 32) | ||
1289 | groups = 32; | ||
1290 | |||
1291 | listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL); | ||
1292 | if (!listeners) | ||
1293 | goto out_sock_release; | ||
1294 | |||
1253 | sk = sock->sk; | 1295 | sk = sock->sk; |
1254 | sk->sk_data_ready = netlink_data_ready; | 1296 | sk->sk_data_ready = netlink_data_ready; |
1255 | if (input) | 1297 | if (input) |
@@ -1262,7 +1304,8 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
1262 | nlk->flags |= NETLINK_KERNEL_SOCKET; | 1304 | nlk->flags |= NETLINK_KERNEL_SOCKET; |
1263 | 1305 | ||
1264 | netlink_table_grab(); | 1306 | netlink_table_grab(); |
1265 | nl_table[unit].groups = groups < 32 ? 32 : groups; | 1307 | nl_table[unit].groups = groups; |
1308 | nl_table[unit].listeners = listeners; | ||
1266 | nl_table[unit].module = module; | 1309 | nl_table[unit].module = module; |
1267 | nl_table[unit].registered = 1; | 1310 | nl_table[unit].registered = 1; |
1268 | netlink_table_ungrab(); | 1311 | netlink_table_ungrab(); |
@@ -1270,6 +1313,7 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
1270 | return sk; | 1313 | return sk; |
1271 | 1314 | ||
1272 | out_sock_release: | 1315 | out_sock_release: |
1316 | kfree(listeners); | ||
1273 | sock_release(sock); | 1317 | sock_release(sock); |
1274 | return NULL; | 1318 | return NULL; |
1275 | } | 1319 | } |