diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-07-18 18:47:05 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-07-18 18:47:05 -0400 |
commit | 84659eb529b33572bb3f8c94e0978bd5d084bc7e (patch) | |
tree | 3eddcba4b7e4779a51480c3cc24384ff2046b287 /net/netlink/af_netlink.c | |
parent | b4ff4f0419ae5db83553fab79d03a89c10d540a8 (diff) |
[NETLIKN]: Allow removing multicast groups.
Allow kicking listeners out of a multicast group when necessary
(for example if that group is going to be removed.)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Patrick McHardy <kaber@trash.net>
Acked-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r-- | net/netlink/af_netlink.c | 57 |
1 files changed, 45 insertions, 12 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c386eaf6ad5b..5681ce3aebca 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1027,6 +1027,23 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) | |||
1027 | read_unlock(&nl_table_lock); | 1027 | read_unlock(&nl_table_lock); |
1028 | } | 1028 | } |
1029 | 1029 | ||
1030 | /* must be called with netlink table grabbed */ | ||
1031 | static void netlink_update_socket_mc(struct netlink_sock *nlk, | ||
1032 | unsigned int group, | ||
1033 | int is_new) | ||
1034 | { | ||
1035 | int old, new = !!is_new, subscriptions; | ||
1036 | |||
1037 | old = test_bit(group - 1, nlk->groups); | ||
1038 | subscriptions = nlk->subscriptions - old + new; | ||
1039 | if (new) | ||
1040 | __set_bit(group - 1, nlk->groups); | ||
1041 | else | ||
1042 | __clear_bit(group - 1, nlk->groups); | ||
1043 | netlink_update_subscriptions(&nlk->sk, subscriptions); | ||
1044 | netlink_update_listeners(&nlk->sk); | ||
1045 | } | ||
1046 | |||
1030 | static int netlink_setsockopt(struct socket *sock, int level, int optname, | 1047 | static int netlink_setsockopt(struct socket *sock, int level, int optname, |
1031 | char __user *optval, int optlen) | 1048 | char __user *optval, int optlen) |
1032 | { | 1049 | { |
@@ -1052,9 +1069,6 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
1052 | break; | 1069 | break; |
1053 | case NETLINK_ADD_MEMBERSHIP: | 1070 | case NETLINK_ADD_MEMBERSHIP: |
1054 | case NETLINK_DROP_MEMBERSHIP: { | 1071 | case NETLINK_DROP_MEMBERSHIP: { |
1055 | unsigned int subscriptions; | ||
1056 | int old, new = optname == NETLINK_ADD_MEMBERSHIP ? 1 : 0; | ||
1057 | |||
1058 | if (!netlink_capable(sock, NL_NONROOT_RECV)) | 1072 | if (!netlink_capable(sock, NL_NONROOT_RECV)) |
1059 | return -EPERM; | 1073 | return -EPERM; |
1060 | err = netlink_realloc_groups(sk); | 1074 | err = netlink_realloc_groups(sk); |
@@ -1063,14 +1077,8 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
1063 | if (!val || val - 1 >= nlk->ngroups) | 1077 | if (!val || val - 1 >= nlk->ngroups) |
1064 | return -EINVAL; | 1078 | return -EINVAL; |
1065 | netlink_table_grab(); | 1079 | netlink_table_grab(); |
1066 | old = test_bit(val - 1, nlk->groups); | 1080 | netlink_update_socket_mc(nlk, val, |
1067 | subscriptions = nlk->subscriptions - old + new; | 1081 | optname == NETLINK_ADD_MEMBERSHIP); |
1068 | if (new) | ||
1069 | __set_bit(val - 1, nlk->groups); | ||
1070 | else | ||
1071 | __clear_bit(val - 1, nlk->groups); | ||
1072 | netlink_update_subscriptions(sk, subscriptions); | ||
1073 | netlink_update_listeners(sk); | ||
1074 | netlink_table_ungrab(); | 1082 | netlink_table_ungrab(); |
1075 | err = 0; | 1083 | err = 0; |
1076 | break; | 1084 | break; |
@@ -1351,7 +1359,9 @@ out_sock_release: | |||
1351 | * | 1359 | * |
1352 | * This changes the number of multicast groups that are available | 1360 | * This changes the number of multicast groups that are available |
1353 | * on a certain netlink family. Note that it is not possible to | 1361 | * on a certain netlink family. Note that it is not possible to |
1354 | * change the number of groups to below 32. | 1362 | * change the number of groups to below 32. Also note that it does |
1363 | * not implicitly call netlink_clear_multicast_users() when the | ||
1364 | * number of groups is reduced. | ||
1355 | * | 1365 | * |
1356 | * @sk: The kernel netlink socket, as returned by netlink_kernel_create(). | 1366 | * @sk: The kernel netlink socket, as returned by netlink_kernel_create(). |
1357 | * @groups: The new number of groups. | 1367 | * @groups: The new number of groups. |
@@ -1386,6 +1396,29 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) | |||
1386 | } | 1396 | } |
1387 | EXPORT_SYMBOL(netlink_change_ngroups); | 1397 | EXPORT_SYMBOL(netlink_change_ngroups); |
1388 | 1398 | ||
1399 | /** | ||
1400 | * netlink_clear_multicast_users - kick off multicast listeners | ||
1401 | * | ||
1402 | * This function removes all listeners from the given group. | ||
1403 | * @ksk: The kernel netlink socket, as returned by | ||
1404 | * netlink_kernel_create(). | ||
1405 | * @group: The multicast group to clear. | ||
1406 | */ | ||
1407 | void netlink_clear_multicast_users(struct sock *ksk, unsigned int group) | ||
1408 | { | ||
1409 | struct sock *sk; | ||
1410 | struct hlist_node *node; | ||
1411 | struct netlink_table *tbl = &nl_table[ksk->sk_protocol]; | ||
1412 | |||
1413 | netlink_table_grab(); | ||
1414 | |||
1415 | sk_for_each_bound(sk, node, &tbl->mc_list) | ||
1416 | netlink_update_socket_mc(nlk_sk(sk), group, 0); | ||
1417 | |||
1418 | netlink_table_ungrab(); | ||
1419 | } | ||
1420 | EXPORT_SYMBOL(netlink_clear_multicast_users); | ||
1421 | |||
1389 | void netlink_set_nonroot(int protocol, unsigned int flags) | 1422 | void netlink_set_nonroot(int protocol, unsigned int flags) |
1390 | { | 1423 | { |
1391 | if ((unsigned int)protocol < MAX_LINKS) | 1424 | if ((unsigned int)protocol < MAX_LINKS) |