(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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.