diff options
Diffstat (limited to 'net/netlink/af_netlink.c')
| -rw-r--r-- | net/netlink/af_netlink.c | 61 |
1 files changed, 53 insertions, 8 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 59dc7d140600..2a233ffcf618 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; |
| @@ -122,7 +123,7 @@ static void netlink_destroy_callback(struct netlink_callback *cb); | |||
| 122 | static DEFINE_RWLOCK(nl_table_lock); | 123 | static DEFINE_RWLOCK(nl_table_lock); |
| 123 | static atomic_t nl_table_users = ATOMIC_INIT(0); | 124 | static atomic_t nl_table_users = ATOMIC_INIT(0); |
| 124 | 125 | ||
| 125 | static struct notifier_block *netlink_chain; | 126 | static ATOMIC_NOTIFIER_HEAD(netlink_chain); |
| 126 | 127 | ||
| 127 | static u32 netlink_group_mask(u32 group) | 128 | static u32 netlink_group_mask(u32 group) |
| 128 | { | 129 | { |
| @@ -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; |
| @@ -450,18 +469,21 @@ static int netlink_release(struct socket *sock) | |||
| 450 | .protocol = sk->sk_protocol, | 469 | .protocol = sk->sk_protocol, |
| 451 | .pid = nlk->pid, | 470 | .pid = nlk->pid, |
| 452 | }; | 471 | }; |
| 453 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); | 472 | atomic_notifier_call_chain(&netlink_chain, |
| 473 | NETLINK_URELEASE, &n); | ||
| 454 | } | 474 | } |
| 455 | 475 | ||
| 456 | if (nlk->module) | 476 | if (nlk->module) |
| 457 | module_put(nlk->module); | 477 | module_put(nlk->module); |
| 458 | 478 | ||
| 479 | netlink_table_grab(); | ||
| 459 | if (nlk->flags & NETLINK_KERNEL_SOCKET) { | 480 | if (nlk->flags & NETLINK_KERNEL_SOCKET) { |
| 460 | netlink_table_grab(); | 481 | kfree(nl_table[sk->sk_protocol].listeners); |
| 461 | nl_table[sk->sk_protocol].module = NULL; | 482 | nl_table[sk->sk_protocol].module = NULL; |
| 462 | nl_table[sk->sk_protocol].registered = 0; | 483 | nl_table[sk->sk_protocol].registered = 0; |
| 463 | netlink_table_ungrab(); | 484 | } else if (nlk->subscriptions) |
| 464 | } | 485 | netlink_update_listeners(sk); |
| 486 | netlink_table_ungrab(); | ||
| 465 | 487 | ||
| 466 | kfree(nlk->groups); | 488 | kfree(nlk->groups); |
| 467 | nlk->groups = NULL; | 489 | nlk->groups = NULL; |
| @@ -589,6 +611,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len | |||
| 589 | hweight32(nladdr->nl_groups) - | 611 | hweight32(nladdr->nl_groups) - |
| 590 | hweight32(nlk->groups[0])); | 612 | hweight32(nlk->groups[0])); |
| 591 | nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; | 613 | nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; |
| 614 | netlink_update_listeners(sk); | ||
| 592 | netlink_table_ungrab(); | 615 | netlink_table_ungrab(); |
| 593 | 616 | ||
| 594 | return 0; | 617 | return 0; |
| @@ -807,6 +830,17 @@ retry: | |||
| 807 | return netlink_sendskb(sk, skb, ssk->sk_protocol); | 830 | return netlink_sendskb(sk, skb, ssk->sk_protocol); |
| 808 | } | 831 | } |
| 809 | 832 | ||
| 833 | int netlink_has_listeners(struct sock *sk, unsigned int group) | ||
| 834 | { | ||
| 835 | int res = 0; | ||
| 836 | |||
| 837 | BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET)); | ||
| 838 | if (group - 1 < nl_table[sk->sk_protocol].groups) | ||
| 839 | res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners); | ||
| 840 | return res; | ||
| 841 | } | ||
| 842 | EXPORT_SYMBOL_GPL(netlink_has_listeners); | ||
| 843 | |||
| 810 | static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) | 844 | static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) |
| 811 | { | 845 | { |
| 812 | struct netlink_sock *nlk = nlk_sk(sk); | 846 | struct netlink_sock *nlk = nlk_sk(sk); |
| @@ -1011,6 +1045,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
| 1011 | else | 1045 | else |
| 1012 | __clear_bit(val - 1, nlk->groups); | 1046 | __clear_bit(val - 1, nlk->groups); |
| 1013 | netlink_update_subscriptions(sk, subscriptions); | 1047 | netlink_update_subscriptions(sk, subscriptions); |
| 1048 | netlink_update_listeners(sk); | ||
| 1014 | netlink_table_ungrab(); | 1049 | netlink_table_ungrab(); |
| 1015 | err = 0; | 1050 | err = 0; |
| 1016 | break; | 1051 | break; |
| @@ -1237,6 +1272,7 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
| 1237 | struct socket *sock; | 1272 | struct socket *sock; |
| 1238 | struct sock *sk; | 1273 | struct sock *sk; |
| 1239 | struct netlink_sock *nlk; | 1274 | struct netlink_sock *nlk; |
| 1275 | unsigned long *listeners = NULL; | ||
| 1240 | 1276 | ||
| 1241 | if (!nl_table) | 1277 | if (!nl_table) |
| 1242 | return NULL; | 1278 | return NULL; |
| @@ -1250,6 +1286,13 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
| 1250 | if (__netlink_create(sock, unit) < 0) | 1286 | if (__netlink_create(sock, unit) < 0) |
| 1251 | goto out_sock_release; | 1287 | goto out_sock_release; |
| 1252 | 1288 | ||
| 1289 | if (groups < 32) | ||
| 1290 | groups = 32; | ||
| 1291 | |||
| 1292 | listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL); | ||
| 1293 | if (!listeners) | ||
| 1294 | goto out_sock_release; | ||
| 1295 | |||
| 1253 | sk = sock->sk; | 1296 | sk = sock->sk; |
| 1254 | sk->sk_data_ready = netlink_data_ready; | 1297 | sk->sk_data_ready = netlink_data_ready; |
| 1255 | if (input) | 1298 | if (input) |
| @@ -1262,7 +1305,8 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
| 1262 | nlk->flags |= NETLINK_KERNEL_SOCKET; | 1305 | nlk->flags |= NETLINK_KERNEL_SOCKET; |
| 1263 | 1306 | ||
| 1264 | netlink_table_grab(); | 1307 | netlink_table_grab(); |
| 1265 | nl_table[unit].groups = groups < 32 ? 32 : groups; | 1308 | nl_table[unit].groups = groups; |
| 1309 | nl_table[unit].listeners = listeners; | ||
| 1266 | nl_table[unit].module = module; | 1310 | nl_table[unit].module = module; |
| 1267 | nl_table[unit].registered = 1; | 1311 | nl_table[unit].registered = 1; |
| 1268 | netlink_table_ungrab(); | 1312 | netlink_table_ungrab(); |
| @@ -1270,6 +1314,7 @@ netlink_kernel_create(int unit, unsigned int groups, | |||
| 1270 | return sk; | 1314 | return sk; |
| 1271 | 1315 | ||
| 1272 | out_sock_release: | 1316 | out_sock_release: |
| 1317 | kfree(listeners); | ||
| 1273 | sock_release(sock); | 1318 | sock_release(sock); |
| 1274 | return NULL; | 1319 | return NULL; |
| 1275 | } | 1320 | } |
| @@ -1651,12 +1696,12 @@ static struct file_operations netlink_seq_fops = { | |||
| 1651 | 1696 | ||
| 1652 | int netlink_register_notifier(struct notifier_block *nb) | 1697 | int netlink_register_notifier(struct notifier_block *nb) |
| 1653 | { | 1698 | { |
| 1654 | return notifier_chain_register(&netlink_chain, nb); | 1699 | return atomic_notifier_chain_register(&netlink_chain, nb); |
| 1655 | } | 1700 | } |
| 1656 | 1701 | ||
| 1657 | int netlink_unregister_notifier(struct notifier_block *nb) | 1702 | int netlink_unregister_notifier(struct notifier_block *nb) |
| 1658 | { | 1703 | { |
| 1659 | return notifier_chain_unregister(&netlink_chain, nb); | 1704 | return atomic_notifier_chain_unregister(&netlink_chain, nb); |
| 1660 | } | 1705 | } |
| 1661 | 1706 | ||
| 1662 | static const struct proto_ops netlink_ops = { | 1707 | static const struct proto_ops netlink_ops = { |
