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 | |
| 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>
| -rw-r--r-- | include/linux/netlink.h | 1 | ||||
| -rw-r--r-- | net/netlink/af_netlink.c | 57 |
2 files changed, 46 insertions, 12 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index b971ddd24090..83d8239f0cce 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
| @@ -162,6 +162,7 @@ extern struct sock *netlink_kernel_create(int unit, unsigned int groups, | |||
| 162 | struct mutex *cb_mutex, | 162 | struct mutex *cb_mutex, |
| 163 | struct module *module); | 163 | struct module *module); |
| 164 | extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); | 164 | extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); |
| 165 | extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group); | ||
| 165 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); | 166 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); |
| 166 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); | 167 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); |
| 167 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); | 168 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); |
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) |
