tree 48f26110e23634f17d9c73e7a5a7b4239ffcd589
parent 4c866aa798bc6de0a1d45495229e9f13c35b55c2
author David L Stevens <[EMAIL PROTECTED]> Sat, 09 Jul 2005 07:38:07 -0700
committer David S. Miller <[EMAIL PROTECTED]> Sat, 09 Jul 2005 07:38:07 -0700

[IPV4]: multicast API "join" issues

        This patch corrects a few problems with the IP_ADD_MEMBERSHIP
socket option:

1) The existing code makes an attempt at reference counting joins when
   using the ip_mreqn/imr_ifindex interface. Joining the same group
   on the same socket is an error, whatever the API. This leads to
   unexpected results when mixing ip_mreqn by index with ip_mreqn by
   address, ip_mreq, or other API's. For example, ip_mreq followed by
   ip_mreqn of the same group will "work" while the same two reversed
   will not.
           Fixed to always return EADDRINUSE on a duplicate join and
   removed the (now unused) reference count in ip_mc_socklist.

2) The group-search list in ip_mc_join_group() is comparing a full 
   ip_mreqn structure and all of it must match for it to find the
   group. This doesn't correctly match a group that was joined with
   ip_mreq or ip_mreqn with an address (with or without an index). It
   also doesn't match groups that are joined by different addresses on
   the same interface. All of these are the same multicast group,
   which is identified by group address and interface index.
           Fixed the check to correctly match groups so we don't get
   duplicate group entries on the ip_mc_socklist.

3) The old code allocates a multicast address before searching for
   duplicates requiring it to free in various error cases. This
   patch moves the allocate until after the search and
   igmp_max_memberships check, so never a need to allocate, then free
   an entry.

Signed-off-by: David L Stevens <[EMAIL PROTECTED]>
Signed-off-by: David S. Miller <[EMAIL PROTECTED]>

 include/linux/igmp.h |    1 -
 net/ipv4/igmp.c      |   35 ++++++++++++-----------------------
 2 files changed, 12 insertions(+), 24 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -148,7 +148,6 @@ struct ip_sf_socklist
 struct ip_mc_socklist
 {
        struct ip_mc_socklist   *next;
-       int                     count;
        struct ip_mreqn         multi;
        unsigned int            sfmode;         /* MCAST_{INCLUDE,EXCLUDE} */
        struct ip_sf_socklist   *sflist;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1615,9 +1615,10 @@ int ip_mc_join_group(struct sock *sk , s
 {
        int err;
        u32 addr = imr->imr_multiaddr.s_addr;
-       struct ip_mc_socklist *iml, *i;
+       struct ip_mc_socklist *iml=NULL, *i;
        struct in_device *in_dev;
        struct inet_sock *inet = inet_sk(sk);
+       int ifindex;
        int count = 0;
 
        if (!MULTICAST(addr))
@@ -1633,37 +1634,30 @@ int ip_mc_join_group(struct sock *sk , s
                goto done;
        }
 
-       iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), 
GFP_KERNEL);
-
        err = -EADDRINUSE;
+       ifindex = imr->imr_ifindex;
        for (i = inet->mc_list; i; i = i->next) {
-               if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
-                       /* New style additions are reference counted */
-                       if (imr->imr_address.s_addr == 0) {
-                               i->count++;
-                               err = 0;
-                       }
+               if (i->multi.imr_multiaddr.s_addr == addr &&
+                   i->multi.imr_ifindex == ifindex)
                        goto done;
-               }
                count++;
        }
        err = -ENOBUFS;
-       if (iml == NULL || count >= sysctl_igmp_max_memberships)
+       if (count >= sysctl_igmp_max_memberships)
+               goto done;
+       iml = (struct ip_mc_socklist *)sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL);
+       if (iml == NULL)
                goto done;
+
        memcpy(&iml->multi, imr, sizeof(*imr));
        iml->next = inet->mc_list;
-       iml->count = 1;
        iml->sflist = NULL;
        iml->sfmode = MCAST_EXCLUDE;
        inet->mc_list = iml;
        ip_mc_inc_group(in_dev, addr);
-       iml = NULL;
        err = 0;
-
 done:
        rtnl_shunlock();
-       if (iml)
-               sock_kfree_s(sk, iml, sizeof(*iml));
        return err;
 }
 
@@ -1704,12 +1698,6 @@ int ip_mc_leave_group(struct sock *sk, s
                        in_dev = inetdev_by_index(iml->multi.imr_ifindex);
                        if (in_dev)
                                (void) ip_mc_leave_src(sk, iml, in_dev);
-                       if (--iml->count) {
-                               rtnl_unlock();
-                               if (in_dev)
-                                       in_dev_put(in_dev);
-                               return 0;
-                       }
 
                        *imlp = iml->next;
 
@@ -1755,7 +1743,8 @@ int ip_mc_source(int add, int omode, str
        err = -EADDRNOTAVAIL;
 
        for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
-               if (memcmp(&pmc->multi, mreqs, 2*sizeof(__u32)) == 0)
+               if (pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr
+                   && pmc->multi.imr_ifindex == imr.imr_ifindex)
                        break;
        }
        if (!pmc)               /* must have a prior join */
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to