Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv4 / igmp.c
index 99d46b4..8e8117c 100644 (file)
@@ -72,7 +72,6 @@
  *                                     Vinay Kulkarni
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -91,6 +90,8 @@
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/times.h>
+
+#include <net/arp.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/route.h>
@@ -143,8 +144,8 @@ static int sf_setstate(struct ip_mc_list *pmc);
 static void sf_markstate(struct ip_mc_list *pmc);
 #endif
 static void ip_mc_clear_src(struct ip_mc_list *pmc);
-int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
-       int sfcount, __u32 *psfsrc, int delta);
+static int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+                        int sfcount, __u32 *psfsrc, int delta);
 
 static void ip_ma_put(struct ip_mc_list *im)
 {
@@ -231,7 +232,18 @@ static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,
        case IGMPV3_MODE_IS_EXCLUDE:
                if (gdeleted || sdeleted)
                        return 0;
-               return !(pmc->gsquery && !psf->sf_gsresp);
+               if (!(pmc->gsquery && !psf->sf_gsresp)) {
+                       if (pmc->sfmode == MCAST_INCLUDE)
+                               return 1;
+                       /* don't include if this source is excluded
+                        * in all filters
+                        */
+                       if (psf->sf_count[MCAST_INCLUDE])
+                               return type == IGMPV3_MODE_IS_INCLUDE;
+                       return pmc->sfcount[MCAST_EXCLUDE] ==
+                               psf->sf_count[MCAST_EXCLUDE];
+               }
+               return 0;
        case IGMPV3_CHANGE_TO_INCLUDE:
                if (gdeleted || sdeleted)
                        return 0;
@@ -279,7 +291,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
 
        skb = alloc_skb(size + LL_RESERVED_SPACE(dev), GFP_ATOMIC);
        if (skb == NULL)
-               return 0;
+               return NULL;
 
        {
                struct flowi fl = { .oif = dev->ifindex,
@@ -288,12 +300,13 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
                                    .proto = IPPROTO_IGMP };
                if (ip_route_output_key(&rt, &fl)) {
                        kfree_skb(skb);
-                       return 0;
+                       return NULL;
                }
        }
        if (rt->rt_src == 0) {
+               kfree_skb(skb);
                ip_rt_put(rt);
-               return 0;
+               return NULL;
        }
 
        skb->dst = &rt->u.dst;
@@ -360,7 +373,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
        if (!skb)
                skb = igmpv3_newpack(dev, dev->mtu);
        if (!skb)
-               return 0;
+               return NULL;
        pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
        pgr->grec_type = type;
        pgr->grec_auxwords = 0;
@@ -380,9 +393,9 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
 {
        struct net_device *dev = pmc->interface->dev;
        struct igmpv3_report *pih;
-       struct igmpv3_grec *pgr = 0;
+       struct igmpv3_grec *pgr = NULL;
        struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
-       int scount, first, isquery, truncate;
+       int scount, stotal, first, isquery, truncate;
 
        if (pmc->multiaddr == IGMP_ALL_HOSTS)
                return skb;
@@ -392,26 +405,14 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
        truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
                    type == IGMPV3_CHANGE_TO_EXCLUDE;
 
+       stotal = scount = 0;
+
        psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
 
-       if (!*psf_list) {
-               if (type == IGMPV3_ALLOW_NEW_SOURCES ||
-                   type == IGMPV3_BLOCK_OLD_SOURCES)
-                       return skb;
-               if (pmc->crcount || isquery) {
-                       /* make sure we have room for group header and at
-                        * least one source.
-                        */
-                       if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)+
-                           sizeof(__u32)) {
-                               igmpv3_sendpack(skb);
-                               skb = 0; /* add_grhead will get a new one */
-                       }
-                       skb = add_grhead(skb, pmc, type, &pgr);
-               }
-               return skb;
-       }
-       pih = skb ? (struct igmpv3_report *)skb->h.igmph : 0;
+       if (!*psf_list)
+               goto empty_source;
+
+       pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL;
 
        /* EX and TO_EX get a fresh packet, if needed */
        if (truncate) {
@@ -423,8 +424,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
                }
        }
        first = 1;
-       scount = 0;
-       psf_prev = 0;
+       psf_prev = NULL;
        for (psf=*psf_list; psf; psf=psf_next) {
                u32 *psrc;
 
@@ -457,7 +457,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
                }
                psrc = (u32 *)skb_put(skb, sizeof(u32));
                *psrc = psf->sf_inaddr;
-               scount++;
+               scount++; stotal++;
                if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
                     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
                        psf->sf_crcount--;
@@ -472,6 +472,21 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
                }
                psf_prev = psf;
        }
+
+empty_source:
+       if (!stotal) {
+               if (type == IGMPV3_ALLOW_NEW_SOURCES ||
+                   type == IGMPV3_BLOCK_OLD_SOURCES)
+                       return skb;
+               if (pmc->crcount || isquery) {
+                       /* make sure we have room for group header */
+                       if (skb && AVAILABLE(skb)<sizeof(struct igmpv3_grec)) {
+                               igmpv3_sendpack(skb);
+                               skb = NULL; /* add_grhead will get a new one */
+                       }
+                       skb = add_grhead(skb, pmc, type, &pgr);
+               }
+       }
        if (pgr)
                pgr->grec_nsrcs = htons(scount);
 
@@ -482,11 +497,11 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
 
 static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
 {
-       struct sk_buff *skb = 0;
+       struct sk_buff *skb = NULL;
        int type;
 
        if (!pmc) {
-               read_lock(&in_dev->lock);
+               read_lock(&in_dev->mc_list_lock);
                for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
                        if (pmc->multiaddr == IGMP_ALL_HOSTS)
                                continue;
@@ -498,7 +513,7 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
                        skb = add_grec(skb, pmc, type, 0, 0);
                        spin_unlock_bh(&pmc->lock);
                }
-               read_unlock(&in_dev->lock);
+               read_unlock(&in_dev->mc_list_lock);
        } else {
                spin_lock_bh(&pmc->lock);
                if (pmc->sfcount[MCAST_EXCLUDE])
@@ -520,7 +535,7 @@ static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
 {
        struct ip_sf_list *psf_prev, *psf_next, *psf;
 
-       psf_prev = 0;
+       psf_prev = NULL;
        for (psf=*ppsf; psf; psf = psf_next) {
                psf_next = psf->sf_next;
                if (psf->sf_crcount == 0) {
@@ -537,14 +552,14 @@ static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
 static void igmpv3_send_cr(struct in_device *in_dev)
 {
        struct ip_mc_list *pmc, *pmc_prev, *pmc_next;
-       struct sk_buff *skb = 0;
+       struct sk_buff *skb = NULL;
        int type, dtype;
 
-       read_lock(&in_dev->lock);
-       write_lock_bh(&in_dev->mc_lock);
+       read_lock(&in_dev->mc_list_lock);
+       spin_lock_bh(&in_dev->mc_tomb_lock);
 
        /* deleted MCA's */
-       pmc_prev = 0;
+       pmc_prev = NULL;
        for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) {
                pmc_next = pmc->next;
                if (pmc->sfmode == MCAST_INCLUDE) {
@@ -554,11 +569,11 @@ static void igmpv3_send_cr(struct in_device *in_dev)
                        skb = add_grec(skb, pmc, dtype, 1, 1);
                }
                if (pmc->crcount) {
-                       pmc->crcount--;
                        if (pmc->sfmode == MCAST_EXCLUDE) {
                                type = IGMPV3_CHANGE_TO_INCLUDE;
                                skb = add_grec(skb, pmc, type, 1, 0);
                        }
+                       pmc->crcount--;
                        if (pmc->crcount == 0) {
                                igmpv3_clear_zeros(&pmc->tomb);
                                igmpv3_clear_zeros(&pmc->sources);
@@ -574,7 +589,7 @@ static void igmpv3_send_cr(struct in_device *in_dev)
                } else
                        pmc_prev = pmc;
        }
-       write_unlock_bh(&in_dev->mc_lock);
+       spin_unlock_bh(&in_dev->mc_tomb_lock);
 
        /* change recs */
        for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
@@ -591,16 +606,17 @@ static void igmpv3_send_cr(struct in_device *in_dev)
 
                /* filter mode changes */
                if (pmc->crcount) {
-                       pmc->crcount--;
                        if (pmc->sfmode == MCAST_EXCLUDE)
                                type = IGMPV3_CHANGE_TO_EXCLUDE;
                        else
                                type = IGMPV3_CHANGE_TO_INCLUDE;
                        skb = add_grec(skb, pmc, type, 0, 0);
+                       pmc->crcount--;
                }
                spin_unlock_bh(&pmc->lock);
        }
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
+
        if (!skb)
                return;
        (void) igmpv3_sendpack(skb);
@@ -680,7 +696,7 @@ static void igmp_gq_timer_expire(unsigned long data)
        struct in_device *in_dev = (struct in_device *)data;
 
        in_dev->mr_gq_running = 0;
-       igmpv3_send_report(in_dev, 0);
+       igmpv3_send_report(in_dev, NULL);
        __in_dev_put(in_dev);
 }
 
@@ -731,11 +747,43 @@ static void igmp_timer_expire(unsigned long data)
        ip_ma_put(im);
 }
 
-static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
+/* mark EXCLUDE-mode sources */
+static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
 {
        struct ip_sf_list *psf;
        int i, scount;
 
+       scount = 0;
+       for (psf=pmc->sources; psf; psf=psf->sf_next) {
+               if (scount == nsrcs)
+                       break;
+               for (i=0; i<nsrcs; i++) {
+                       /* skip inactive filters */
+                       if (pmc->sfcount[MCAST_INCLUDE] ||
+                           pmc->sfcount[MCAST_EXCLUDE] !=
+                           psf->sf_count[MCAST_EXCLUDE])
+                               continue;
+                       if (srcs[i] == psf->sf_inaddr) {
+                               scount++;
+                               break;
+                       }
+               }
+       }
+       pmc->gsquery = 0;
+       if (scount == nsrcs)    /* all sources excluded */
+               return 0;
+       return 1;
+}
+
+static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
+{
+       struct ip_sf_list *psf;
+       int i, scount;
+
+       if (pmc->sfmode == MCAST_EXCLUDE)
+               return igmp_xmarksources(pmc, nsrcs, srcs);
+
+       /* mark INCLUDE-mode sources */
        scount = 0;
        for (psf=pmc->sources; psf; psf=psf->sf_next) {
                if (scount == nsrcs)
@@ -747,6 +795,12 @@ static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
                                break;
                        }
        }
+       if (!scount) {
+               pmc->gsquery = 0;
+               return 0;
+       }
+       pmc->gsquery = 1;
+       return 1;
 }
 
 static void igmp_heard_report(struct in_device *in_dev, u32 group)
@@ -758,14 +812,14 @@ static void igmp_heard_report(struct in_device *in_dev, u32 group)
        if (group == IGMP_ALL_HOSTS)
                return;
 
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
                if (im->multiaddr == group) {
                        igmp_stop_timer(im);
                        break;
                }
        }
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
 }
 
 static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
@@ -839,8 +893,10 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
         * - Use the igmp->igmp_code field as the maximum
         *   delay possible
         */
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (im=in_dev->mc_list; im!=NULL; im=im->next) {
+               int changed;
+
                if (group && group != im->multiaddr)
                        continue;
                if (im->multiaddr == IGMP_ALL_HOSTS)
@@ -850,12 +906,13 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
                        im->gsquery = im->gsquery && mark;
                else
                        im->gsquery = mark;
-               if (im->gsquery)
-                       igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
+               changed = !im->gsquery ||
+                       igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
                spin_unlock_bh(&im->lock);
-               igmp_mod_timer(im, max_delay);
+               if (changed)
+                       igmp_mod_timer(im, max_delay);
        }
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
 }
 
 int igmp_rcv(struct sk_buff *skb)
@@ -870,11 +927,18 @@ int igmp_rcv(struct sk_buff *skb)
                return 0;
        }
 
-       if (!pskb_may_pull(skb, sizeof(struct igmphdr)) || 
-           (u16)csum_fold(skb_checksum(skb, 0, len, 0))) {
-               in_dev_put(in_dev);
-               kfree_skb(skb);
-               return 0;
+       if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
+               goto drop;
+
+       switch (skb->ip_summed) {
+       case CHECKSUM_HW:
+               if (!(u16)csum_fold(skb->csum))
+                       break;
+               /* fall through */
+       case CHECKSUM_NONE:
+               skb->csum = 0;
+               if (__skb_checksum_complete(skb))
+                       goto drop;
        }
 
        ih = skb->h.igmph;
@@ -888,7 +952,10 @@ int igmp_rcv(struct sk_buff *skb)
                /* Is it our report looped back? */
                if (((struct rtable*)skb->dst)->fl.iif == 0)
                        break;
-               igmp_heard_report(in_dev, ih->group);
+               /* don't rely on MC router hearing unicast reports */
+               if (skb->pkt_type == PACKET_MULTICAST ||
+                   skb->pkt_type == PACKET_BROADCAST)
+                       igmp_heard_report(in_dev, ih->group);
                break;
        case IGMP_PIM:
 #ifdef CONFIG_IP_PIMSM_V1
@@ -902,8 +969,10 @@ int igmp_rcv(struct sk_buff *skb)
        case IGMP_MTRACE_RESP:
                break;
        default:
-               NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
+               break;
        }
+
+drop:
        in_dev_put(in_dev);
        kfree_skb(skb);
        return 0;
@@ -959,10 +1028,9 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
         * for deleted items allows change reports to use common code with
         * non-deleted or query-response MCA's.
         */
-       pmc = (struct ip_mc_list *)kmalloc(sizeof(*pmc), GFP_KERNEL);
+       pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
        if (!pmc)
                return;
-       memset(pmc, 0, sizeof(*pmc));
        spin_lock_bh(&im->lock);
        pmc->interface = im->interface;
        in_dev_hold(in_dev);
@@ -975,16 +1043,16 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
 
                pmc->tomb = im->tomb;
                pmc->sources = im->sources;
-               im->tomb = im->sources = 0;
+               im->tomb = im->sources = NULL;
                for (psf=pmc->sources; psf; psf=psf->sf_next)
                        psf->sf_crcount = pmc->crcount;
        }
        spin_unlock_bh(&im->lock);
 
-       write_lock_bh(&in_dev->mc_lock);
+       spin_lock_bh(&in_dev->mc_tomb_lock);
        pmc->next = in_dev->mc_tomb;
        in_dev->mc_tomb = pmc;
-       write_unlock_bh(&in_dev->mc_lock);
+       spin_unlock_bh(&in_dev->mc_tomb_lock);
 }
 
 static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
@@ -992,8 +1060,8 @@ static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
        struct ip_mc_list *pmc, *pmc_prev;
        struct ip_sf_list *psf, *psf_next;
 
-       write_lock_bh(&in_dev->mc_lock);
-       pmc_prev = 0;
+       spin_lock_bh(&in_dev->mc_tomb_lock);
+       pmc_prev = NULL;
        for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) {
                if (pmc->multiaddr == multiaddr)
                        break;
@@ -1005,7 +1073,7 @@ static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
                else
                        in_dev->mc_tomb = pmc->next;
        }
-       write_unlock_bh(&in_dev->mc_lock);
+       spin_unlock_bh(&in_dev->mc_tomb_lock);
        if (pmc) {
                for (psf=pmc->tomb; psf; psf=psf_next) {
                        psf_next = psf->sf_next;
@@ -1020,10 +1088,10 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
 {
        struct ip_mc_list *pmc, *nextpmc;
 
-       write_lock_bh(&in_dev->mc_lock);
+       spin_lock_bh(&in_dev->mc_tomb_lock);
        pmc = in_dev->mc_tomb;
-       in_dev->mc_tomb = 0;
-       write_unlock_bh(&in_dev->mc_lock);
+       in_dev->mc_tomb = NULL;
+       spin_unlock_bh(&in_dev->mc_tomb_lock);
 
        for (; pmc; pmc = nextpmc) {
                nextpmc = pmc->next;
@@ -1032,20 +1100,20 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
                kfree(pmc);
        }
        /* clear dead sources, too */
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
                struct ip_sf_list *psf, *psf_next;
 
                spin_lock_bh(&pmc->lock);
                psf = pmc->tomb;
-               pmc->tomb = 0;
+               pmc->tomb = NULL;
                spin_unlock_bh(&pmc->lock);
                for (; psf; psf=psf_next) {
                        psf_next = psf->sf_next;
                        kfree(psf);
                }
        }
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
 }
 #endif
 
@@ -1134,12 +1202,12 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        for (im=in_dev->mc_list; im; im=im->next) {
                if (im->multiaddr == addr) {
                        im->users++;
-                       ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, 0, 0);
+                       ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0);
                        goto out;
                }
        }
 
-       im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
+       im = kmalloc(sizeof(*im), GFP_KERNEL);
        if (!im)
                goto out;
 
@@ -1151,8 +1219,8 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        im->sfmode = MCAST_EXCLUDE;
        im->sfcount[MCAST_INCLUDE] = 0;
        im->sfcount[MCAST_EXCLUDE] = 1;
-       im->sources = 0;
-       im->tomb = 0;
+       im->sources = NULL;
+       im->tomb = NULL;
        im->crcount = 0;
        atomic_set(&im->refcnt, 1);
        spin_lock_init(&im->lock);
@@ -1166,10 +1234,10 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        im->gsquery = 0;
 #endif
        im->loaded = 0;
-       write_lock_bh(&in_dev->lock);
+       write_lock_bh(&in_dev->mc_list_lock);
        im->next=in_dev->mc_list;
        in_dev->mc_list=im;
-       write_unlock_bh(&in_dev->lock);
+       write_unlock_bh(&in_dev->mc_list_lock);
 #ifdef CONFIG_IP_MULTICAST
        igmpv3_del_delrec(in_dev, im->multiaddr);
 #endif
@@ -1193,9 +1261,9 @@ void ip_mc_dec_group(struct in_device *in_dev, u32 addr)
        for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
                if (i->multiaddr==addr) {
                        if (--i->users == 0) {
-                               write_lock_bh(&in_dev->lock);
+                               write_lock_bh(&in_dev->mc_list_lock);
                                *ip = i->next;
-                               write_unlock_bh(&in_dev->lock);
+                               write_unlock_bh(&in_dev->mc_list_lock);
                                igmp_group_dropped(i);
 
                                if (!in_dev->dead)
@@ -1237,7 +1305,7 @@ void ip_mc_init_dev(struct in_device *in_dev)
 {
        ASSERT_RTNL();
 
-       in_dev->mc_tomb = 0;
+       in_dev->mc_tomb = NULL;
 #ifdef CONFIG_IP_MULTICAST
        in_dev->mr_gq_running = 0;
        init_timer(&in_dev->mr_gq_timer);
@@ -1250,7 +1318,8 @@ void ip_mc_init_dev(struct in_device *in_dev)
        in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
 #endif
 
-       in_dev->mc_lock = RW_LOCK_UNLOCKED;
+       rwlock_init(&in_dev->mc_list_lock);
+       spin_lock_init(&in_dev->mc_tomb_lock);
 }
 
 /* Device going up */
@@ -1280,17 +1349,17 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
        /* Deactivate timers */
        ip_mc_down(in_dev);
 
-       write_lock_bh(&in_dev->lock);
+       write_lock_bh(&in_dev->mc_list_lock);
        while ((i = in_dev->mc_list) != NULL) {
                in_dev->mc_list = i->next;
-               write_unlock_bh(&in_dev->lock);
+               write_unlock_bh(&in_dev->mc_list_lock);
 
                igmp_group_dropped(i);
                ip_ma_put(i);
 
-               write_lock_bh(&in_dev->lock);
+               write_lock_bh(&in_dev->mc_list_lock);
        }
-       write_unlock_bh(&in_dev->lock);
+       write_unlock_bh(&in_dev->mc_list_lock);
 }
 
 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
@@ -1311,7 +1380,7 @@ static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
                dev = ip_dev_find(imr->imr_address.s_addr);
                if (!dev)
                        return NULL;
-               __dev_put(dev);
+               dev_put(dev);
        }
 
        if (!dev && !ip_route_output_key(&rt, &fl)) {
@@ -1320,7 +1389,7 @@ static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
        }
        if (dev) {
                imr->imr_ifindex = dev->ifindex;
-               idev = __in_dev_get(dev);
+               idev = __in_dev_get_rtnl(dev);
        }
        return idev;
 }
@@ -1338,7 +1407,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
        struct ip_sf_list *psf, *psf_prev;
        int rv = 0;
 
-       psf_prev = 0;
+       psf_prev = NULL;
        for (psf=pmc->sources; psf; psf=psf->sf_next) {
                if (psf->sf_inaddr == *psfsrc)
                        break;
@@ -1381,8 +1450,8 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
 #define igmp_ifc_event(x)      do { } while (0)
 #endif
 
-int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
-       int sfcount, __u32 *psfsrc, int delta)
+static int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+                        int sfcount, __u32 *psfsrc, int delta)
 {
        struct ip_mc_list *pmc;
        int     changerec = 0;
@@ -1390,18 +1459,18 @@ int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
 
        if (!in_dev)
                return -ENODEV;
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
                if (*pmca == pmc->multiaddr)
                        break;
        }
        if (!pmc) {
                /* MCA not found?? bug */
-               read_unlock(&in_dev->lock);
+               read_unlock(&in_dev->mc_list_lock);
                return -ESRCH;
        }
        spin_lock_bh(&pmc->lock);
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
 #ifdef CONFIG_IP_MULTICAST
        sf_markstate(pmc);
 #endif
@@ -1452,17 +1521,16 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
 {
        struct ip_sf_list *psf, *psf_prev;
 
-       psf_prev = 0;
+       psf_prev = NULL;
        for (psf=pmc->sources; psf; psf=psf->sf_next) {
                if (psf->sf_inaddr == *psfsrc)
                        break;
                psf_prev = psf;
        }
        if (!psf) {
-               psf = (struct ip_sf_list *)kmalloc(sizeof(*psf), GFP_ATOMIC);
+               psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
                if (!psf)
                        return -ENOBUFS;
-               memset(psf, 0, sizeof(*psf));
                psf->sf_inaddr = *psfsrc;
                if (psf_prev) {
                        psf_prev->sf_next = psf;
@@ -1493,7 +1561,7 @@ static void sf_markstate(struct ip_mc_list *pmc)
 
 static int sf_setstate(struct ip_mc_list *pmc)
 {
-       struct ip_sf_list *psf;
+       struct ip_sf_list *psf, *dpsf;
        int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
        int qrv = pmc->interface->mr_qrv;
        int new_in, rv;
@@ -1505,8 +1573,46 @@ static int sf_setstate(struct ip_mc_list *pmc)
                                !psf->sf_count[MCAST_INCLUDE];
                } else
                        new_in = psf->sf_count[MCAST_INCLUDE] != 0;
-               if (new_in != psf->sf_oldin) {
-                       psf->sf_crcount = qrv;
+               if (new_in) {
+                       if (!psf->sf_oldin) {
+                               struct ip_sf_list *prev = NULL;
+
+                               for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) {
+                                       if (dpsf->sf_inaddr == psf->sf_inaddr)
+                                               break;
+                                       prev = dpsf;
+                               }
+                               if (dpsf) {
+                                       if (prev)
+                                               prev->sf_next = dpsf->sf_next;
+                                       else
+                                               pmc->tomb = dpsf->sf_next;
+                                       kfree(dpsf);
+                               }
+                               psf->sf_crcount = qrv;
+                               rv++;
+                       }
+               } else if (psf->sf_oldin) {
+
+                       psf->sf_crcount = 0;
+                       /*
+                        * add or update "delete" records if an active filter
+                        * is now inactive
+                        */
+                       for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next)
+                               if (dpsf->sf_inaddr == psf->sf_inaddr)
+                                       break;
+                       if (!dpsf) {
+                               dpsf = (struct ip_sf_list *)
+                                       kmalloc(sizeof(*dpsf), GFP_ATOMIC);
+                               if (!dpsf)
+                                       continue;
+                               *dpsf = *psf;
+                               /* pmc->lock held by callers */
+                               dpsf->sf_next = pmc->tomb;
+                               pmc->tomb = dpsf;
+                       }
+                       dpsf->sf_crcount = qrv;
                        rv++;
                }
        }
@@ -1517,8 +1623,8 @@ static int sf_setstate(struct ip_mc_list *pmc)
 /*
  * Add multicast source filter list to the interface list
  */
-int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
-       int sfcount, __u32 *psfsrc, int delta)
+static int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+                        int sfcount, __u32 *psfsrc, int delta)
 {
        struct ip_mc_list *pmc;
        int     isexclude;
@@ -1526,18 +1632,18 @@ int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
 
        if (!in_dev)
                return -ENODEV;
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
                if (*pmca == pmc->multiaddr)
                        break;
        }
        if (!pmc) {
                /* MCA not found?? bug */
-               read_unlock(&in_dev->lock);
+               read_unlock(&in_dev->mc_list_lock);
                return -ESRCH;
        }
        spin_lock_bh(&pmc->lock);
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
 
 #ifdef CONFIG_IP_MULTICAST
        sf_markstate(pmc);
@@ -1593,14 +1699,14 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
                nextpsf = psf->sf_next;
                kfree(psf);
        }
-       pmc->tomb = 0;
+       pmc->tomb = NULL;
        for (psf=pmc->sources; psf; psf=nextpsf) {
                nextpsf = psf->sf_next;
                kfree(psf);
        }
-       pmc->sources = 0;
+       pmc->sources = NULL;
        pmc->sfmode = MCAST_EXCLUDE;
-       pmc->sfcount[MCAST_EXCLUDE] = 0;
+       pmc->sfcount[MCAST_INCLUDE] = 0;
        pmc->sfcount[MCAST_EXCLUDE] = 1;
 }
 
@@ -1612,15 +1718,16 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 {
        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_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
+       int ifindex;
        int count = 0;
 
        if (!MULTICAST(addr))
                return -EINVAL;
 
-       rtnl_shlock();
+       rtnl_lock();
 
        in_dev = ip_mc_find_dev(imr);
 
@@ -1630,55 +1737,48 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
                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 = 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));
+       rtnl_unlock();
        return err;
 }
 
-int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
-       struct in_device *in_dev)
+static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
+                          struct in_device *in_dev)
 {
        int err;
 
        if (iml->sflist == 0) {
                /* any-source empty exclude case */
                return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
-                       iml->sfmode, 0, 0, 0);
+                       iml->sfmode, 0, NULL, 0);
        }
        err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
                        iml->sfmode, iml->sflist->sl_count,
                        iml->sflist->sl_addr, 0);
        sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max));
-       iml->sflist = 0;
+       iml->sflist = NULL;
        return err;
 }
 
@@ -1688,39 +1788,40 @@ int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
 
 int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_mc_socklist *iml, **imlp;
+       struct in_device *in_dev;
+       u32 group = imr->imr_multiaddr.s_addr;
+       u32 ifindex;
+       int ret = -EADDRNOTAVAIL;
 
        rtnl_lock();
+       in_dev = ip_mc_find_dev(imr);
+       ifindex = imr->imr_ifindex;
        for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
-               if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
-                   iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
-                   (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
-                       struct in_device *in_dev;
-
-                       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;
-                       }
+               if (iml->multi.imr_multiaddr.s_addr != group)
+                       continue;
+               if (ifindex) {
+                       if (iml->multi.imr_ifindex != ifindex)
+                               continue;
+               } else if (imr->imr_address.s_addr && imr->imr_address.s_addr !=
+                               iml->multi.imr_address.s_addr)
+                       continue;
 
-                       *imlp = iml->next;
+               (void) ip_mc_leave_src(sk, iml, in_dev);
 
-                       if (in_dev) {
-                               ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
-                               in_dev_put(in_dev);
-                       }
-                       rtnl_unlock();
-                       sock_kfree_s(sk, iml, sizeof(*iml));
-                       return 0;
-               }
+               *imlp = iml->next;
+
+               if (in_dev)
+                       ip_mc_dec_group(in_dev, group);
+               rtnl_unlock();
+               sock_kfree_s(sk, iml, sizeof(*iml));
+               return 0;
        }
+       if (!in_dev)
+               ret = -ENODEV;
        rtnl_unlock();
-       return -EADDRNOTAVAIL;
+       return ret;
 }
 
 int ip_mc_source(int add, int omode, struct sock *sk, struct
@@ -1730,15 +1831,16 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
        struct ip_mreqn imr;
        u32 addr = mreqs->imr_multiaddr;
        struct ip_mc_socklist *pmc;
-       struct in_device *in_dev = 0;
-       struct inet_opt *inet = inet_sk(sk);
+       struct in_device *in_dev = NULL;
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_sf_socklist *psl;
+       int leavegroup = 0;
        int i, j, rv;
 
        if (!MULTICAST(addr))
                return -EINVAL;
 
-       rtnl_shlock();
+       rtnl_lock();
 
        imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
        imr.imr_address.s_addr = mreqs->imr_interface;
@@ -1752,36 +1854,47 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
        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 */
+       if (!pmc) {             /* must have a prior join */
+               err = -EINVAL;
                goto done;
+       }
        /* if a source filter was set, must be the same mode as before */
        if (pmc->sflist) {
-               if (pmc->sfmode != omode)
+               if (pmc->sfmode != omode) {
+                       err = -EINVAL;
                        goto done;
+               }
        } else if (pmc->sfmode != omode) {
                /* allow mode switches for empty-set filters */
-               ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, 0, 0);
+               ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0);
                ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 
-                       0, 0);
+                       NULL, 0);
                pmc->sfmode = omode;
        }
 
        psl = pmc->sflist;
        if (!add) {
                if (!psl)
-                       goto done;
+                       goto done;      /* err = -EADDRNOTAVAIL */
                rv = !0;
                for (i=0; i<psl->sl_count; i++) {
-                       rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+                       rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
                                sizeof(__u32));
-                       if (rv >= 0)
+                       if (rv == 0)
                                break;
                }
-               if (!rv)        /* source not found */
+               if (rv)         /* source not found */
+                       goto done;      /* err = -EADDRNOTAVAIL */
+
+               /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+               if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
+                       leavegroup = 1;
                        goto done;
+               }
 
                /* update the interface filter */
                ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 
@@ -1805,8 +1918,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
 
                if (psl)
                        count += psl->sl_max;
-               newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
-                       IP_SFLSIZE(count), GFP_KERNEL);
+               newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL);
                if (!newpsl) {
                        err = -ENOBUFS;
                        goto done;
@@ -1822,9 +1934,9 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
        }
        rv = 1; /* > 0 for insert logic below if sl_count is 0 */
        for (i=0; i<psl->sl_count; i++) {
-               rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+               rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
                        sizeof(__u32));
-               if (rv >= 0)
+               if (rv == 0)
                        break;
        }
        if (rv == 0)            /* address already there is an error */
@@ -1838,19 +1950,22 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
        ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 
                &mreqs->imr_sourceaddr, 1);
 done:
-       rtnl_shunlock();
+       rtnl_unlock();
+       if (leavegroup)
+               return ip_mc_leave_group(sk, &imr);
        return err;
 }
 
 int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
 {
-       int err;
+       int err = 0;
        struct ip_mreqn imr;
        u32 addr = msf->imsf_multiaddr;
        struct ip_mc_socklist *pmc;
        struct in_device *in_dev;
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_sf_socklist *newpsl, *psl;
+       int leavegroup = 0;
 
        if (!MULTICAST(addr))
                return -EINVAL;
@@ -1858,7 +1973,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
            msf->imsf_fmode != MCAST_EXCLUDE)
                return -EINVAL;
 
-       rtnl_shlock();
+       rtnl_lock();
 
        imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
        imr.imr_address.s_addr = msf->imsf_interface;
@@ -1869,18 +1984,25 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
                err = -ENODEV;
                goto done;
        }
-       err = -EADDRNOTAVAIL;
+
+       /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+       if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) {
+               leavegroup = 1;
+               goto done;
+       }
 
        for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
                if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr &&
                    pmc->multi.imr_ifindex == imr.imr_ifindex)
                        break;
        }
-       if (!pmc)               /* must have a prior join */
+       if (!pmc) {             /* must have a prior join */
+               err = -EINVAL;
                goto done;
+       }
        if (msf->imsf_numsrc) {
-               newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
-                               IP_SFLSIZE(msf->imsf_numsrc), GFP_KERNEL);
+               newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc),
+                                                          GFP_KERNEL);
                if (!newpsl) {
                        err = -ENOBUFS;
                        goto done;
@@ -1894,8 +2016,11 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
                        sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max));
                        goto done;
                }
-       } else
-               newpsl = 0;
+       } else {
+               newpsl = NULL;
+               (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
+                                    msf->imsf_fmode, 0, NULL, 0);
+       }
        psl = pmc->sflist;
        if (psl) {
                (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
@@ -1903,29 +2028,32 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
                sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
        } else
                (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
-                       0, 0, 0);
+                       0, NULL, 0);
        pmc->sflist = newpsl;
        pmc->sfmode = msf->imsf_fmode;
+       err = 0;
 done:
-       rtnl_shunlock();
+       rtnl_unlock();
+       if (leavegroup)
+               err = ip_mc_leave_group(sk, &imr);
        return err;
 }
 
 int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
-       struct ip_msfilter *optval, int *optlen)
+       struct ip_msfilter __user *optval, int __user *optlen)
 {
        int err, len, count, copycount;
        struct ip_mreqn imr;
        u32 addr = msf->imsf_multiaddr;
        struct ip_mc_socklist *pmc;
        struct in_device *in_dev;
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_sf_socklist *psl;
 
        if (!MULTICAST(addr))
                return -EINVAL;
 
-       rtnl_shlock();
+       rtnl_lock();
 
        imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
        imr.imr_address.s_addr = msf->imsf_interface;
@@ -1947,7 +2075,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
                goto done;
        msf->imsf_fmode = pmc->sfmode;
        psl = pmc->sflist;
-       rtnl_shunlock();
+       rtnl_unlock();
        if (!psl) {
                len = 0;
                count = 0;
@@ -1958,26 +2086,26 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
        len = copycount * sizeof(psl->sl_addr[0]);
        msf->imsf_numsrc = count;
        if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
-           copy_to_user((void *)optval, msf, IP_MSFILTER_SIZE(0))) {
+           copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) {
                return -EFAULT;
        }
        if (len &&
-           copy_to_user((void *)&optval->imsf_slist[0], psl->sl_addr, len))
+           copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len))
                return -EFAULT;
        return 0;
 done:
-       rtnl_shunlock();
+       rtnl_unlock();
        return err;
 }
 
 int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
-       struct group_filter *optval, int *optlen)
+       struct group_filter __user *optval, int __user *optlen)
 {
        int err, i, count, copycount;
        struct sockaddr_in *psin;
        u32 addr;
        struct ip_mc_socklist *pmc;
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_sf_socklist *psl;
 
        psin = (struct sockaddr_in *)&gsf->gf_group;
@@ -1987,7 +2115,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
        if (!MULTICAST(addr))
                return -EINVAL;
 
-       rtnl_shlock();
+       rtnl_lock();
 
        err = -EADDRNOTAVAIL;
 
@@ -2000,12 +2128,12 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
                goto done;
        gsf->gf_fmode = pmc->sfmode;
        psl = pmc->sflist;
-       rtnl_shunlock();
+       rtnl_unlock();
        count = psl ? psl->sl_count : 0;
        copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
        gsf->gf_numsrc = count;
        if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
-           copy_to_user((void *)optval, gsf, GROUP_FILTER_SIZE(0))) {
+           copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
                return -EFAULT;
        }
        for (i=0; i<copycount; i++) {
@@ -2016,12 +2144,12 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
                memset(&ss, 0, sizeof(ss));
                psin->sin_family = AF_INET;
                psin->sin_addr.s_addr = psl->sl_addr[i];
-               if (copy_to_user((void *)&optval->gf_slist[i], &ss, sizeof(ss)))
+               if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
                        return -EFAULT;
        }
        return 0;
 done:
-       rtnl_shunlock();
+       rtnl_unlock();
        return err;
 }
 
@@ -2030,7 +2158,7 @@ done:
  */
 int ip_mc_sf_allow(struct sock *sk, u32 loc_addr, u32 rmt_addr, int dif)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_mc_socklist *pmc;
        struct ip_sf_socklist *psl;
        int i;
@@ -2066,7 +2194,7 @@ int ip_mc_sf_allow(struct sock *sk, u32 loc_addr, u32 rmt_addr, int dif)
 
 void ip_mc_drop_socket(struct sock *sk)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ip_mc_socklist *iml;
 
        if (inet->mc_list == NULL)
@@ -2077,13 +2205,13 @@ void ip_mc_drop_socket(struct sock *sk)
                struct in_device *in_dev;
                inet->mc_list = iml->next;
 
-               if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
-                       (void) ip_mc_leave_src(sk, iml, in_dev);
+               in_dev = inetdev_by_index(iml->multi.imr_ifindex);
+               (void) ip_mc_leave_src(sk, iml, in_dev);
+               if (in_dev != NULL) {
                        ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
                        in_dev_put(in_dev);
                }
                sock_kfree_s(sk, iml, sizeof(*iml));
-
        }
        rtnl_unlock();
 }
@@ -2094,7 +2222,7 @@ int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
        struct ip_sf_list *psf;
        int rv = 0;
 
-       read_lock(&in_dev->lock);
+       read_lock(&in_dev->mc_list_lock);
        for (im=in_dev->mc_list; im; im=im->next) {
                if (im->multiaddr == mc_addr)
                        break;
@@ -2116,7 +2244,7 @@ int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
                } else
                        rv = 1; /* unspecified source; tentatively allow */
        }
-       read_unlock(&in_dev->lock);
+       read_unlock(&in_dev->mc_list_lock);
        return rv;
 }
 
@@ -2140,13 +2268,13 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
                in_dev = in_dev_get(state->dev);
                if (!in_dev)
                        continue;
-               read_lock(&in_dev->lock);
+               read_lock(&in_dev->mc_list_lock);
                im = in_dev->mc_list;
                if (im) {
                        state->in_dev = in_dev;
                        break;
                }
-               read_unlock(&in_dev->lock);
+               read_unlock(&in_dev->mc_list_lock);
                in_dev_put(in_dev);
        }
        return im;
@@ -2158,7 +2286,7 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li
        im = im->next;
        while (!im) {
                if (likely(state->in_dev != NULL)) {
-                       read_unlock(&state->in_dev->lock);
+                       read_unlock(&state->in_dev->mc_list_lock);
                        in_dev_put(state->in_dev);
                }
                state->dev = state->dev->next;
@@ -2169,7 +2297,7 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li
                state->in_dev = in_dev_get(state->dev);
                if (!state->in_dev)
                        continue;
-               read_lock(&state->in_dev->lock);
+               read_lock(&state->in_dev->mc_list_lock);
                im = state->in_dev->mc_list;
        }
        return im;
@@ -2205,7 +2333,7 @@ static void igmp_mc_seq_stop(struct seq_file *seq, void *v)
 {
        struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
        if (likely(state->in_dev != NULL)) {
-               read_unlock(&state->in_dev->lock);
+               read_unlock(&state->in_dev->mc_list_lock);
                in_dev_put(state->in_dev);
                state->in_dev = NULL;
        }
@@ -2216,8 +2344,8 @@ static void igmp_mc_seq_stop(struct seq_file *seq, void *v)
 static int igmp_mc_seq_show(struct seq_file *seq, void *v)
 {
        if (v == SEQ_START_TOKEN)
-               seq_printf(seq, 
-                          "Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");
+               seq_puts(seq, 
+                        "Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");
        else {
                struct ip_mc_list *im = (struct ip_mc_list *)v;
                struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
@@ -2236,7 +2364,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v)
                }
 
                seq_printf(seq,
-                          "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
+                          "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n",
                           im->multiaddr, im->users,
                           im->tm_running, im->tm_running ?
                           jiffies_to_clock_t(im->timer.expires-jiffies) : 0,
@@ -2256,7 +2384,7 @@ static int igmp_mc_seq_open(struct inode *inode, struct file *file)
 {
        struct seq_file *seq;
        int rc = -ENOMEM;
-       struct igmp_mc_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       struct igmp_mc_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
 
        if (!s)
                goto out;
@@ -2266,7 +2394,6 @@ static int igmp_mc_seq_open(struct inode *inode, struct file *file)
 
        seq = file->private_data;
        seq->private = s;
-       memset(s, 0, sizeof(*s));
 out:
        return rc;
 out_kfree:
@@ -2303,7 +2430,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
                idev = in_dev_get(state->dev);
                if (unlikely(idev == NULL))
                        continue;
-               read_lock_bh(&idev->lock);
+               read_lock(&idev->mc_list_lock);
                im = idev->mc_list;
                if (likely(im != NULL)) {
                        spin_lock_bh(&im->lock);
@@ -2315,7 +2442,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
                        }
                        spin_unlock_bh(&im->lock);
                }
-               read_unlock_bh(&idev->lock);
+               read_unlock(&idev->mc_list_lock);
                in_dev_put(idev);
        }
        return psf;
@@ -2331,7 +2458,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
                state->im = state->im->next;
                while (!state->im) {
                        if (likely(state->idev != NULL)) {
-                               read_unlock_bh(&state->idev->lock);
+                               read_unlock(&state->idev->mc_list_lock);
                                in_dev_put(state->idev);
                        }
                        state->dev = state->dev->next;
@@ -2342,7 +2469,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
                        state->idev = in_dev_get(state->dev);
                        if (!state->idev)
                                continue;
-                       read_lock_bh(&state->idev->lock);
+                       read_lock(&state->idev->mc_list_lock);
                        state->im = state->idev->mc_list;
                }
                if (!state->im)
@@ -2388,7 +2515,7 @@ static void igmp_mcf_seq_stop(struct seq_file *seq, void *v)
                state->im = NULL;
        }
        if (likely(state->idev != NULL)) {
-               read_unlock_bh(&state->idev->lock);
+               read_unlock(&state->idev->mc_list_lock);
                in_dev_put(state->idev);
                state->idev = NULL;
        }
@@ -2431,7 +2558,7 @@ static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
 {
        struct seq_file *seq;
        int rc = -ENOMEM;
-       struct igmp_mcf_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       struct igmp_mcf_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
 
        if (!s)
                goto out;
@@ -2441,7 +2568,6 @@ static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
 
        seq = file->private_data;
        seq->private = s;
-       memset(s, 0, sizeof(*s));
 out:
        return rc;
 out_kfree: