(usagi-users 03395) Re: MLDv2 linux implementation questions
David Stevens <[email protected]>
| Newsgroups | gmane.linux.ipv6.usagi.users |
|---|---|
| Message-ID | <OF8794A7C2.A573AAD0-ON88257021.0076753E-88257021.0076AD28@us.ibm.com> |
Hoerdt,
Here's the complete patch, which should
fix the problem you've seen. Please let me know if
you turn up anything else, of course. :-)
+-DLS
diff -ruNp linux-2.6.11.12/net/ipv6/ipv6_sockglue.c
linux-2.6.11.12MCT/net/ipv6/ipv6_sockglue.c
--- linux-2.6.11.12/net/ipv6/ipv6_sockglue.c 2005-03-01
23:37:48.000000000 -0800
+++ linux-2.6.11.12MCT/net/ipv6/ipv6_sockglue.c 2005-06-15
14:17:46.000000000 -0700
@@ -423,11 +423,12 @@ done:
psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
retv = ipv6_sock_mc_join(sk, greqs.gsr_interface,
&psin6->sin6_addr);
- if (retv)
+ /* prior join w/ different source is ok */
+ if (retv && retv != -EADDRINUSE)
break;
omode = MCAST_INCLUDE;
add = 1;
- } else /*IP_DROP_SOURCE_MEMBERSHIP */ {
+ } else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
diff -ruNp linux-2.6.11.12/net/ipv6/mcast.c
linux-2.6.11.12MCT/net/ipv6/mcast.c
--- linux-2.6.11.12/net/ipv6/mcast.c 2005-03-01 23:38:25.000000000
-0800
+++ linux-2.6.11.12MCT/net/ipv6/mcast.c 2005-06-14 16:37:22.000000000
-0700
@@ -188,6 +188,16 @@ int ipv6_sock_mc_join(struct sock *sk, i
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
+ read_lock_bh(&ipv6_sk_mc_lock);
+ for (mc_lst=np->ipv6_mc_list; mc_lst; mc_lst=mc_lst->next) {
+ if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
+ ipv6_addr_equal(&mc_lst->addr, addr)) {
+ read_unlock_bh(&ipv6_sk_mc_lock);
+ return -EADDRINUSE;
+ }
+ }
+ read_unlock_bh(&ipv6_sk_mc_lock);
+
mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist),
GFP_KERNEL);
if (mc_lst == NULL)
@@ -241,19 +251,18 @@ int ipv6_sock_mc_join(struct sock *sk, i
/*
* socket leave on multicast group
*/
-int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr
*addr)
+static int __ipv6_sock_mc_drop(struct sock *sk, int ifindex,
+ struct in6_addr *addr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst, **lnk;
- write_lock_bh(&ipv6_sk_mc_lock);
for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk =
&mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
struct net_device *dev;
*lnk = mc_lst->next;
- write_unlock_bh(&ipv6_sk_mc_lock);
if ((dev = dev_get_by_index(mc_lst->ifindex)) !=
NULL) {
struct inet6_dev *idev = in6_dev_get(dev);
@@ -269,11 +278,21 @@ int ipv6_sock_mc_drop(struct sock *sk, i
return 0;
}
}
- write_unlock_bh(&ipv6_sk_mc_lock);
return -ENOENT;
}
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr
*addr)
+{
+ int rv;
+
+ write_lock_bh(&ipv6_sk_mc_lock);
+ rv = __ipv6_sock_mc_drop(sk, ifindex, addr);
+ write_unlock_bh(&ipv6_sk_mc_lock);
+ return rv;
+}
+
+
static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int
ifindex)
{
struct net_device *dev = NULL;
@@ -368,6 +387,7 @@ int ip6_mc_source(int add, int omode, st
err = -EADDRNOTAVAIL;
+ write_lock_bh(&ipv6_sk_mc_lock);
for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
if (pgsr->gsr_interface && pmc->ifindex !=
pgsr->gsr_interface)
continue;
@@ -401,6 +421,13 @@ int ip6_mc_source(int add, int omode, st
if (rv) /* source not found */
goto done;
+ /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+ if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
+ err = __ipv6_sock_mc_drop(sk, pgsr->gsr_interface,
+ group);
+ goto done;
+ }
+
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
@@ -453,6 +480,7 @@ int ip6_mc_source(int add, int omode, st
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
+ write_unlock_bh(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
dev_put(dev);
mct-3.patch
(application/octet-stream, 3.6 KB)
diff -ruNp linux-2.6.11.12/net/ipv6/ipv6_sockglue.c linux-2.6.11.12MCT/net/ipv6/ipv6_sockglue.c
--- linux-2.6.11.12/net/ipv6/ipv6_sockglue.c 2005-03-01 23:37:48.000000000 -0800
+++ linux-2.6.11.12MCT/net/ipv6/ipv6_sockglue.c 2005-06-15 14:17:46.000000000 -0700
@@ -423,11 +423,12 @@ done:
psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
retv = ipv6_sock_mc_join(sk, greqs.gsr_interface,
&psin6->sin6_addr);
- if (retv)
+ /* prior join w/ different source is ok */
+ if (retv && retv != -EADDRINUSE)
break;
omode = MCAST_INCLUDE;
add = 1;
- } else /*IP_DROP_SOURCE_MEMBERSHIP */ {
+ } else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
diff -ruNp linux-2.6.11.12/net/ipv6/mcast.c linux-2.6.11.12MCT/net/ipv6/mcast.c
--- linux-2.6.11.12/net/ipv6/mcast.c 2005-03-01 23:38:25.000000000 -0800
+++ linux-2.6.11.12MCT/net/ipv6/mcast.c 2005-06-14 16:37:22.000000000 -0700
@@ -188,6 +188,16 @@ int ipv6_sock_mc_join(struct sock *sk, i
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
+ read_lock_bh(&ipv6_sk_mc_lock);
+ for (mc_lst=np->ipv6_mc_list; mc_lst; mc_lst=mc_lst->next) {
+ if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
+ ipv6_addr_equal(&mc_lst->addr, addr)) {
+ read_unlock_bh(&ipv6_sk_mc_lock);
+ return -EADDRINUSE;
+ }
+ }
+ read_unlock_bh(&ipv6_sk_mc_lock);
+
mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
if (mc_lst == NULL)
@@ -241,19 +251,18 @@ int ipv6_sock_mc_join(struct sock *sk, i
/*
* socket leave on multicast group
*/
-int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
+static int __ipv6_sock_mc_drop(struct sock *sk, int ifindex,
+ struct in6_addr *addr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst, **lnk;
- write_lock_bh(&ipv6_sk_mc_lock);
for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
struct net_device *dev;
*lnk = mc_lst->next;
- write_unlock_bh(&ipv6_sk_mc_lock);
if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
struct inet6_dev *idev = in6_dev_get(dev);
@@ -269,11 +278,21 @@ int ipv6_sock_mc_drop(struct sock *sk, i
return 0;
}
}
- write_unlock_bh(&ipv6_sk_mc_lock);
return -ENOENT;
}
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
+{
+ int rv;
+
+ write_lock_bh(&ipv6_sk_mc_lock);
+ rv = __ipv6_sock_mc_drop(sk, ifindex, addr);
+ write_unlock_bh(&ipv6_sk_mc_lock);
+ return rv;
+}
+
+
static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex)
{
struct net_device *dev = NULL;
@@ -368,6 +387,7 @@ int ip6_mc_source(int add, int omode, st
err = -EADDRNOTAVAIL;
+ write_lock_bh(&ipv6_sk_mc_lock);
for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
continue;
@@ -401,6 +421,13 @@ int ip6_mc_source(int add, int omode, st
if (rv) /* source not found */
goto done;
+ /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+ if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
+ err = __ipv6_sock_mc_drop(sk, pgsr->gsr_interface,
+ group);
+ goto done;
+ }
+
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
@@ -453,6 +480,7 @@ int ip6_mc_source(int add, int omode, st
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
+ write_unlock_bh(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
dev_put(dev);