sunrpc: fix leak on error on socket xprt setup
[safe/jmp/linux-2.6] / net / ipv6 / mcast.c
index d810cff..59f1881 100644 (file)
@@ -5,8 +5,6 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>
  *
- *     $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $
- *
  *     Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
  *
  *     This program is free software; you can redistribute it and/or
@@ -45,6 +43,8 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <net/mld.h>
 
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv6.h>
@@ -59,6 +59,7 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 #include <net/ip6_route.h>
+#include <net/inet_common.h>
 
 #include <net/ip6_checksum.h>
 
 #define MDBG(x)
 #endif
 
-/*
- *  These header formats should be in a separate include file, but icmpv6.h
- *  doesn't have in6_addr defined in all cases, there is no __u128, and no
- *  other files reference these.
- *
- *                     +-DLS 4/14/03
- */
-
-/* Multicast Listener Discovery version 2 headers */
-
-struct mld2_grec {
-       __u8            grec_type;
-       __u8            grec_auxwords;
-       __be16          grec_nsrcs;
-       struct in6_addr grec_mca;
-       struct in6_addr grec_src[0];
-};
-
-struct mld2_report {
-       __u8    type;
-       __u8    resv1;
-       __sum16 csum;
-       __be16  resv2;
-       __be16  ngrec;
-       struct mld2_grec grec[0];
-};
-
-struct mld2_query {
-       __u8 type;
-       __u8 code;
-       __sum16 csum;
-       __be16 mrc;
-       __be16 resv1;
-       struct in6_addr mca;
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-       __u8 qrv:3,
-            suppress:1,
-            resv2:4;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-       __u8 resv2:4,
-            suppress:1,
-            qrv:3;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-       __u8 qqic;
-       __be16 nsrcs;
-       struct in6_addr srcs[0];
+/* Ensure that we have struct in6_addr aligned on 32bit word. */
+static void *__mld2_query_bugs[] __attribute__((__unused__)) = {
+       BUILD_BUG_ON_NULL(offsetof(struct mld2_query, mld2q_srcs) % 4),
+       BUILD_BUG_ON_NULL(offsetof(struct mld2_report, mld2r_grec) % 4),
+       BUILD_BUG_ON_NULL(offsetof(struct mld2_grec, grec_mca) % 4)
 };
 
 static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
@@ -126,8 +84,6 @@ static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
 /* Big mc list lock for all the sockets */
 static DEFINE_RWLOCK(ipv6_sk_mc_lock);
 
-int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr);
-
 static void igmp6_join_group(struct ifmcaddr6 *ma);
 static void igmp6_leave_group(struct ifmcaddr6 *ma);
 static void igmp6_timer_handler(unsigned long data);
@@ -154,20 +110,11 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
 #define IGMP6_UNSOLICITED_IVAL (10*HZ)
 #define MLD_QRV_DEFAULT                2
 
-#define MLD_V1_SEEN(idev) (ipv6_devconf.force_mld_version == 1 || \
+#define MLD_V1_SEEN(idev) (dev_net((idev)->dev)->ipv6.devconf_all->force_mld_version == 1 || \
                (idev)->cnf.force_mld_version == 1 || \
                ((idev)->mc_v1_seen && \
                time_before(jiffies, (idev)->mc_v1_seen)))
 
-#define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
-#define MLDV2_EXP(thresh, nbmant, nbexp, value) \
-       ((value) < (thresh) ? (value) : \
-       ((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
-       (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-
-#define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
-#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
-
 #define IPV6_MLD_MAX_MSF       64
 
 int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
@@ -176,7 +123,7 @@ int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
  *     socket join on multicast group
  */
 
-int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
+int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
 {
        struct net_device *dev = NULL;
        struct ipv6_mc_socklist *mc_lst;
@@ -251,7 +198,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
 /*
  *     socket leave on multicast group
  */
-int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6_mc_socklist *mc_lst, **lnk;
@@ -307,20 +254,23 @@ static struct inet6_dev *ip6_mc_find_dev(struct net *net,
                dev = dev_get_by_index(net, ifindex);
 
        if (!dev)
-               return NULL;
+               goto nodev;
        idev = in6_dev_get(dev);
-       if (!idev) {
-               dev_put(dev);
-               return NULL;
-       }
+       if (!idev)
+               goto release;
        read_lock_bh(&idev->lock);
-       if (idev->dead) {
-               read_unlock_bh(&idev->lock);
-               in6_dev_put(idev);
-               dev_put(dev);
-               return NULL;
-       }
+       if (idev->dead)
+               goto unlock_release;
+
        return idev;
+
+unlock_release:
+       read_unlock_bh(&idev->lock);
+       in6_dev_put(idev);
+release:
+       dev_put(dev);
+nodev:
+       return NULL;
 }
 
 void ipv6_sock_mc_close(struct sock *sk)
@@ -371,10 +321,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
        int pmclocked = 0;
        int err;
 
-       if (pgsr->gsr_group.ss_family != AF_INET6 ||
-           pgsr->gsr_source.ss_family != AF_INET6)
-               return -EINVAL;
-
        source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
        group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr;
 
@@ -663,8 +609,8 @@ done:
        return err;
 }
 
-int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr,
-       struct in6_addr *src_addr)
+int inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
+                  const struct in6_addr *src_addr)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6_mc_socklist *mc;
@@ -719,7 +665,7 @@ static void igmp6_group_added(struct ifmcaddr6 *mc)
        if (!(mc->mca_flags&MAF_LOADED)) {
                mc->mca_flags |= MAF_LOADED;
                if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0)
-                       dev_mc_add(dev, buf, dev->addr_len, 0);
+                       dev_mc_add(dev, buf);
        }
        spin_unlock_bh(&mc->mca_lock);
 
@@ -745,7 +691,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)
        if (mc->mca_flags&MAF_LOADED) {
                mc->mca_flags &= ~MAF_LOADED;
                if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0)
-                       dev_mc_delete(dev, buf, dev->addr_len, 0);
+                       dev_mc_del(dev, buf);
        }
 
        if (mc->mca_flags & MAF_NOREPORT)
@@ -798,10 +744,10 @@ static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
        }
        spin_unlock_bh(&im->mca_lock);
 
-       write_lock_bh(&idev->mc_lock);
+       spin_lock_bh(&idev->mc_lock);
        pmc->next = idev->mc_tomb;
        idev->mc_tomb = pmc;
-       write_unlock_bh(&idev->mc_lock);
+       spin_unlock_bh(&idev->mc_lock);
 }
 
 static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *pmca)
@@ -809,7 +755,7 @@ static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *pmca)
        struct ifmcaddr6 *pmc, *pmc_prev;
        struct ip6_sf_list *psf, *psf_next;
 
-       write_lock_bh(&idev->mc_lock);
+       spin_lock_bh(&idev->mc_lock);
        pmc_prev = NULL;
        for (pmc=idev->mc_tomb; pmc; pmc=pmc->next) {
                if (ipv6_addr_equal(&pmc->mca_addr, pmca))
@@ -822,7 +768,8 @@ static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *pmca)
                else
                        idev->mc_tomb = pmc->next;
        }
-       write_unlock_bh(&idev->mc_lock);
+       spin_unlock_bh(&idev->mc_lock);
+
        if (pmc) {
                for (psf=pmc->mca_tomb; psf; psf=psf_next) {
                        psf_next = psf->sf_next;
@@ -837,10 +784,10 @@ static void mld_clear_delrec(struct inet6_dev *idev)
 {
        struct ifmcaddr6 *pmc, *nextpmc;
 
-       write_lock_bh(&idev->mc_lock);
+       spin_lock_bh(&idev->mc_lock);
        pmc = idev->mc_tomb;
        idev->mc_tomb = NULL;
-       write_unlock_bh(&idev->mc_lock);
+       spin_unlock_bh(&idev->mc_lock);
 
        for (; pmc; pmc = nextpmc) {
                nextpmc = pmc->next;
@@ -870,7 +817,7 @@ static void mld_clear_delrec(struct inet6_dev *idev)
 /*
  *     device multicast group inc (add if not found)
  */
-int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
+int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
 {
        struct ifmcaddr6 *mc;
        struct inet6_dev *idev;
@@ -941,7 +888,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
 /*
  *     device multicast group del
  */
-int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr)
+int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
 {
        struct ifmcaddr6 *ma, **map;
 
@@ -966,7 +913,7 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr)
        return -ENOENT;
 }
 
-int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
+int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr)
 {
        struct inet6_dev *idev = in6_dev_get(dev);
        int err;
@@ -1011,8 +958,8 @@ int ipv6_is_mld(struct sk_buff *skb, int nexthdr)
 /*
  *     check if the interface/address pair is valid
  */
-int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
-       struct in6_addr *src_addr)
+int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
+                       const struct in6_addr *src_addr)
 {
        struct inet6_dev *idev;
        struct ifmcaddr6 *mc;
@@ -1164,7 +1111,7 @@ int igmp6_event_query(struct sk_buff *skb)
        struct in6_addr *group;
        unsigned long max_delay;
        struct inet6_dev *idev;
-       struct icmp6hdr *hdr;
+       struct mld_msg *mld;
        int group_type;
        int mark = 0;
        int len;
@@ -1185,8 +1132,8 @@ int igmp6_event_query(struct sk_buff *skb)
        if (idev == NULL)
                return 0;
 
-       hdr = icmp6_hdr(skb);
-       group = (struct in6_addr *) (hdr + 1);
+       mld = (struct mld_msg *)icmp6_hdr(skb);
+       group = &mld->mld_mca;
        group_type = ipv6_addr_type(group);
 
        if (group_type != IPV6_ADDR_ANY &&
@@ -1200,7 +1147,7 @@ int igmp6_event_query(struct sk_buff *skb)
                /* MLDv1 router present */
 
                /* Translate milliseconds to jiffies */
-               max_delay = (ntohs(hdr->icmp6_maxdelay)*HZ)/1000;
+               max_delay = (ntohs(mld->mld_maxdelay)*HZ)/1000;
 
                switchback = (idev->mc_qrv + 1) * max_delay;
                idev->mc_v1_seen = jiffies + switchback;
@@ -1219,14 +1166,14 @@ int igmp6_event_query(struct sk_buff *skb)
                        return -EINVAL;
                }
                mlh2 = (struct mld2_query *)skb_transport_header(skb);
-               max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
+               max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000;
                if (!max_delay)
                        max_delay = 1;
                idev->mc_maxdelay = max_delay;
-               if (mlh2->qrv)
-                       idev->mc_qrv = mlh2->qrv;
+               if (mlh2->mld2q_qrv)
+                       idev->mc_qrv = mlh2->mld2q_qrv;
                if (group_type == IPV6_ADDR_ANY) { /* general query */
-                       if (mlh2->nsrcs) {
+                       if (mlh2->mld2q_nsrcs) {
                                in6_dev_put(idev);
                                return -EINVAL; /* no sources allowed */
                        }
@@ -1235,9 +1182,9 @@ int igmp6_event_query(struct sk_buff *skb)
                        return 0;
                }
                /* mark sources to include, if group & source-specific */
-               if (mlh2->nsrcs != 0) {
+               if (mlh2->mld2q_nsrcs != 0) {
                        if (!pskb_may_pull(skb, srcs_offset +
-                           ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {
+                           ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) {
                                in6_dev_put(idev);
                                return -EINVAL;
                        }
@@ -1273,7 +1220,7 @@ int igmp6_event_query(struct sk_buff *skb)
                                        ma->mca_flags &= ~MAF_GSQUERY;
                        }
                        if (!(ma->mca_flags & MAF_GSQUERY) ||
-                           mld_marksources(ma, ntohs(mlh2->nsrcs), mlh2->srcs))
+                           mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs))
                                igmp6_group_queried(ma, max_delay);
                        spin_unlock_bh(&ma->mca_lock);
                        break;
@@ -1289,9 +1236,8 @@ int igmp6_event_query(struct sk_buff *skb)
 int igmp6_event_report(struct sk_buff *skb)
 {
        struct ifmcaddr6 *ma;
-       struct in6_addr *addrp;
        struct inet6_dev *idev;
-       struct icmp6hdr *hdr;
+       struct mld_msg *mld;
        int addr_type;
 
        /* Our own report looped back. Ignore it. */
@@ -1303,10 +1249,10 @@ int igmp6_event_report(struct sk_buff *skb)
            skb->pkt_type != PACKET_BROADCAST)
                return 0;
 
-       if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
+       if (!pskb_may_pull(skb, sizeof(*mld) - sizeof(struct icmp6hdr)))
                return -EINVAL;
 
-       hdr = icmp6_hdr(skb);
+       mld = (struct mld_msg *)icmp6_hdr(skb);
 
        /* Drop reports with not link local source */
        addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
@@ -1314,8 +1260,6 @@ int igmp6_event_report(struct sk_buff *skb)
            !(addr_type&IPV6_ADDR_LINKLOCAL))
                return -EINVAL;
 
-       addrp = (struct in6_addr *) (hdr + 1);
-
        idev = in6_dev_get(skb->dev);
        if (idev == NULL)
                return -ENODEV;
@@ -1326,7 +1270,7 @@ int igmp6_event_report(struct sk_buff *skb)
 
        read_lock_bh(&idev->lock);
        for (ma = idev->mc_list; ma; ma=ma->next) {
-               if (ipv6_addr_equal(&ma->mca_addr, addrp)) {
+               if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
                        spin_lock(&ma->mca_lock);
                        if (del_timer(&ma->mca_timer))
                                atomic_dec(&ma->mca_refcnt);
@@ -1405,13 +1349,14 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
        struct sk_buff *skb;
        struct mld2_report *pmr;
        struct in6_addr addr_buf;
+       const struct in6_addr *saddr;
        int err;
        u8 ra[8] = { IPPROTO_ICMPV6, 0,
                     IPV6_TLV_ROUTERALERT, 2, 0, 0,
                     IPV6_TLV_PADN, 0 };
 
        /* we assume size > sizeof(ra) here */
-       skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err);
+       skb = sock_alloc_send_skb(sk, size + LL_ALLOCATED_SPACE(dev), 1, &err);
 
        if (!skb)
                return NULL;
@@ -1423,21 +1368,22 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
                 * use unspecified address as the source address
                 * when a valid link-local address is not available.
                 */
-               memset(&addr_buf, 0, sizeof(addr_buf));
-       }
+               saddr = &in6addr_any;
+       } else
+               saddr = &addr_buf;
 
-       ip6_nd_hdr(sk, skb, dev, &addr_buf, &mld2_all_mcr, NEXTHDR_HOP, 0);
+       ip6_nd_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0);
 
        memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
        skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);
        skb_put(skb, sizeof(*pmr));
        pmr = (struct mld2_report *)skb_transport_header(skb);
-       pmr->type = ICMPV6_MLD2_REPORT;
-       pmr->resv1 = 0;
-       pmr->csum = 0;
-       pmr->resv2 = 0;
-       pmr->ngrec = 0;
+       pmr->mld2r_type = ICMPV6_MLD2_REPORT;
+       pmr->mld2r_resv1 = 0;
+       pmr->mld2r_cksum = 0;
+       pmr->mld2r_resv2 = 0;
+       pmr->mld2r_ngrec = 0;
        return skb;
 }
 
@@ -1451,19 +1397,22 @@ static void mld_sendpack(struct sk_buff *skb)
        struct net *net = dev_net(skb->dev);
        int err;
        struct flowi fl;
+       struct dst_entry *dst;
+
+       IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
 
-       IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
        payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
        mldlen = skb->tail - skb->transport_header;
        pip6->payload_len = htons(payload_len);
 
-       pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
-               IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
-                                            mldlen, 0));
+       pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
+                                          IPPROTO_ICMPV6,
+                                          csum_partial(skb_transport_header(skb),
+                                                       mldlen, 0));
 
-       skb->dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+       dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
 
-       if (!skb->dst) {
+       if (!dst) {
                err = -ENOMEM;
                goto err_out;
        }
@@ -1472,19 +1421,22 @@ static void mld_sendpack(struct sk_buff *skb)
                         &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
                         skb->dev->ifindex);
 
-       err = xfrm_lookup(&skb->dst, &fl, NULL, 0);
+       err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+       skb_dst_set(skb, dst);
        if (err)
                goto err_out;
 
-       err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+       payload_len = skb->len;
+
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
                      dst_output);
 out:
        if (!err) {
-               ICMP6MSGOUT_INC_STATS_BH(idev, ICMPV6_MLD2_REPORT);
-               ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);
-               IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTMCASTPKTS);
+               ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT);
+               ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
+               IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
        } else
-               IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTDISCARDS);
+               IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
 
        if (likely(idev != NULL))
                in6_dev_put(idev);
@@ -1517,7 +1469,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        pgr->grec_nsrcs = 0;
        pgr->grec_mca = pmc->mca_addr;  /* structure copy */
        pmr = (struct mld2_report *)skb_transport_header(skb);
-       pmr->ngrec = htons(ntohs(pmr->ngrec)+1);
+       pmr->mld2r_ngrec = htons(ntohs(pmr->mld2r_ngrec)+1);
        *ppgr = pgr;
        return skb;
 }
@@ -1553,7 +1505,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
 
        /* EX and TO_EX get a fresh packet, if needed */
        if (truncate) {
-               if (pmr && pmr->ngrec &&
+               if (pmr && pmr->mld2r_ngrec &&
                    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
                        if (skb)
                                mld_sendpack(skb);
@@ -1694,7 +1646,7 @@ static void mld_send_cr(struct inet6_dev *idev)
        int type, dtype;
 
        read_lock_bh(&idev->lock);
-       write_lock_bh(&idev->mc_lock);
+       spin_lock(&idev->mc_lock);
 
        /* deleted MCA's */
        pmc_prev = NULL;
@@ -1728,7 +1680,7 @@ static void mld_send_cr(struct inet6_dev *idev)
                } else
                        pmc_prev = pmc;
        }
-       write_unlock_bh(&idev->mc_lock);
+       spin_unlock(&idev->mc_lock);
 
        /* change recs */
        for (pmc=idev->mc_list; pmc; pmc=pmc->next) {
@@ -1766,36 +1718,35 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
        struct sock *sk = net->ipv6.igmp_sk;
        struct inet6_dev *idev;
        struct sk_buff *skb;
-       struct icmp6hdr *hdr;
-       struct in6_addr *snd_addr;
-       struct in6_addr *addrp;
+       struct mld_msg *hdr;
+       const struct in6_addr *snd_addr, *saddr;
        struct in6_addr addr_buf;
-       struct in6_addr all_routers;
        int err, len, payload_len, full_len;
        u8 ra[8] = { IPPROTO_ICMPV6, 0,
                     IPV6_TLV_ROUTERALERT, 2, 0, 0,
                     IPV6_TLV_PADN, 0 };
        struct flowi fl;
+       struct dst_entry *dst;
 
-       rcu_read_lock();
-       IP6_INC_STATS(__in6_dev_get(dev),
-                     IPSTATS_MIB_OUTREQUESTS);
-       rcu_read_unlock();
-       snd_addr = addr;
-       if (type == ICMPV6_MGM_REDUCTION) {
-               snd_addr = &all_routers;
-               ipv6_addr_all_routers(&all_routers);
-       }
+       if (type == ICMPV6_MGM_REDUCTION)
+               snd_addr = &in6addr_linklocal_allrouters;
+       else
+               snd_addr = addr;
 
        len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
        payload_len = len + sizeof(ra);
        full_len = sizeof(struct ipv6hdr) + payload_len;
 
-       skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err);
+       rcu_read_lock();
+       IP6_UPD_PO_STATS(net, __in6_dev_get(dev),
+                     IPSTATS_MIB_OUT, full_len);
+       rcu_read_unlock();
+
+       skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + full_len, 1, &err);
 
        if (skb == NULL) {
                rcu_read_lock();
-               IP6_INC_STATS(__in6_dev_get(dev),
+               IP6_INC_STATS(net, __in6_dev_get(dev),
                              IPSTATS_MIB_OUTDISCARDS);
                rcu_read_unlock();
                return;
@@ -1808,28 +1759,27 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
                 * use unspecified address as the source address
                 * when a valid link-local address is not available.
                 */
-               memset(&addr_buf, 0, sizeof(addr_buf));
-       }
+               saddr = &in6addr_any;
+       } else
+               saddr = &addr_buf;
 
-       ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
+       ip6_nd_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len);
 
        memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
-       hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
-       memset(hdr, 0, sizeof(struct icmp6hdr));
-       hdr->icmp6_type = type;
+       hdr = (struct mld_msg *) skb_put(skb, sizeof(struct mld_msg));
+       memset(hdr, 0, sizeof(struct mld_msg));
+       hdr->mld_type = type;
+       ipv6_addr_copy(&hdr->mld_mca, addr);
 
-       addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
-       ipv6_addr_copy(addrp, addr);
-
-       hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len,
-                                          IPPROTO_ICMPV6,
-                                          csum_partial((__u8 *) hdr, len, 0));
+       hdr->mld_cksum = csum_ipv6_magic(saddr, snd_addr, len,
+                                        IPPROTO_ICMPV6,
+                                        csum_partial(hdr, len, 0));
 
        idev = in6_dev_get(skb->dev);
 
-       skb->dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
-       if (!skb->dst) {
+       dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+       if (!dst) {
                err = -ENOMEM;
                goto err_out;
        }
@@ -1838,19 +1788,20 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
                         &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
                         skb->dev->ifindex);
 
-       err = xfrm_lookup(&skb->dst, &fl, NULL, 0);
+       err = xfrm_lookup(net, &dst, &fl, NULL, 0);
        if (err)
                goto err_out;
 
-       err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+       skb_dst_set(skb, dst);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
                      dst_output);
 out:
        if (!err) {
-               ICMP6MSGOUT_INC_STATS(idev, type);
-               ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
-               IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
+               ICMP6MSGOUT_INC_STATS(net, idev, type);
+               ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
+               IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, full_len);
        } else
-               IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
+               IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
 
        if (likely(idev != NULL))
                in6_dev_put(idev);
@@ -2103,7 +2054,6 @@ static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca,
                for (j=0; j<i; j++)
                        (void) ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]);
        } else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) {
-               struct inet6_dev *idev = pmc->idev;
                struct ip6_sf_list *psf;
 
                /* filter mode change */
@@ -2246,6 +2196,25 @@ static void igmp6_timer_handler(unsigned long data)
        ma_put(ma);
 }
 
+/* Device changing type */
+
+void ipv6_mc_unmap(struct inet6_dev *idev)
+{
+       struct ifmcaddr6 *i;
+
+       /* Install multicast list, except for all-nodes (already installed) */
+
+       read_lock_bh(&idev->lock);
+       for (i = idev->mc_list; i; i = i->next)
+               igmp6_group_dropped(i);
+       read_unlock_bh(&idev->lock);
+}
+
+void ipv6_mc_remap(struct inet6_dev *idev)
+{
+       ipv6_mc_up(idev);
+}
+
 /* Device going down */
 
 void ipv6_mc_down(struct inet6_dev *idev)
@@ -2289,7 +2258,7 @@ void ipv6_mc_up(struct inet6_dev *idev)
 void ipv6_mc_init_dev(struct inet6_dev *idev)
 {
        write_lock_bh(&idev->lock);
-       rwlock_init(&idev->mc_lock);
+       spin_lock_init(&idev->mc_lock);
        idev->mc_gq_running = 0;
        setup_timer(&idev->mc_gq_timer, mld_gq_timer_expire,
                        (unsigned long)idev);
@@ -2310,24 +2279,19 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
 void ipv6_mc_destroy_dev(struct inet6_dev *idev)
 {
        struct ifmcaddr6 *i;
-       struct in6_addr maddr;
 
        /* Deactivate timers */
        ipv6_mc_down(idev);
 
        /* Delete all-nodes address. */
-       ipv6_addr_all_nodes(&maddr);
-
        /* We cannot call ipv6_dev_mc_dec() directly, our caller in
         * addrconf.c has NULL'd out dev->ip6_ptr so in6_dev_get() will
         * fail.
         */
-       __ipv6_dev_mc_dec(idev, &maddr);
+       __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allnodes);
 
-       if (idev->cnf.forwarding) {
-               ipv6_addr_all_routers(&maddr);
-               __ipv6_dev_mc_dec(idev, &maddr);
-       }
+       if (idev->cnf.forwarding)
+               __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters);
 
        write_lock_bh(&idev->lock);
        while ((i = idev->mc_list) != NULL) {
@@ -2358,9 +2322,9 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
        struct net *net = seq_file_net(seq);
 
        state->idev = NULL;
-       for_each_netdev(net, state->dev) {
+       for_each_netdev_rcu(net, state->dev) {
                struct inet6_dev *idev;
-               idev = in6_dev_get(state->dev);
+               idev = __in6_dev_get(state->dev);
                if (!idev)
                        continue;
                read_lock_bh(&idev->lock);
@@ -2370,7 +2334,6 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
                        break;
                }
                read_unlock_bh(&idev->lock);
-               in6_dev_put(idev);
        }
        return im;
 }
@@ -2381,16 +2344,15 @@ static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr
 
        im = im->next;
        while (!im) {
-               if (likely(state->idev != NULL)) {
+               if (likely(state->idev != NULL))
                        read_unlock_bh(&state->idev->lock);
-                       in6_dev_put(state->idev);
-               }
-               state->dev = next_net_device(state->dev);
+
+               state->dev = next_net_device_rcu(state->dev);
                if (!state->dev) {
                        state->idev = NULL;
                        break;
                }
-               state->idev = in6_dev_get(state->dev);
+               state->idev = __in6_dev_get(state->dev);
                if (!state->idev)
                        continue;
                read_lock_bh(&state->idev->lock);
@@ -2409,31 +2371,31 @@ static struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos)
 }
 
 static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos)
-       __acquires(dev_base_lock)
+       __acquires(RCU)
 {
-       read_lock(&dev_base_lock);
+       rcu_read_lock();
        return igmp6_mc_get_idx(seq, *pos);
 }
 
 static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
-       struct ifmcaddr6 *im;
-       im = igmp6_mc_get_next(seq, v);
+       struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v);
+
        ++*pos;
        return im;
 }
 
 static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
-       __releases(dev_base_lock)
+       __releases(RCU)
 {
        struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+
        if (likely(state->idev != NULL)) {
                read_unlock_bh(&state->idev->lock);
-               in6_dev_put(state->idev);
                state->idev = NULL;
        }
        state->dev = NULL;
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
@@ -2442,9 +2404,9 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
        struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
 
        seq_printf(seq,
-                  "%-4d %-15s " NIP6_SEQFMT " %5d %08X %ld\n",
+                  "%-4d %-15s %pi6 %5d %08X %ld\n",
                   state->dev->ifindex, state->dev->name,
-                  NIP6(im->mca_addr),
+                  &im->mca_addr,
                   im->mca_users, im->mca_flags,
                   (im->mca_flags&MAF_TIMER_RUNNING) ?
                   jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
@@ -2490,9 +2452,9 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
 
        state->idev = NULL;
        state->im = NULL;
-       for_each_netdev(net, state->dev) {
+       for_each_netdev_rcu(net, state->dev) {
                struct inet6_dev *idev;
-               idev = in6_dev_get(state->dev);
+               idev = __in6_dev_get(state->dev);
                if (unlikely(idev == NULL))
                        continue;
                read_lock_bh(&idev->lock);
@@ -2508,7 +2470,6 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
                        spin_unlock_bh(&im->mca_lock);
                }
                read_unlock_bh(&idev->lock);
-               in6_dev_put(idev);
        }
        return psf;
 }
@@ -2522,16 +2483,15 @@ static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_s
                spin_unlock_bh(&state->im->mca_lock);
                state->im = state->im->next;
                while (!state->im) {
-                       if (likely(state->idev != NULL)) {
+                       if (likely(state->idev != NULL))
                                read_unlock_bh(&state->idev->lock);
-                               in6_dev_put(state->idev);
-                       }
-                       state->dev = next_net_device(state->dev);
+
+                       state->dev = next_net_device_rcu(state->dev);
                        if (!state->dev) {
                                state->idev = NULL;
                                goto out;
                        }
-                       state->idev = in6_dev_get(state->dev);
+                       state->idev = __in6_dev_get(state->dev);
                        if (!state->idev)
                                continue;
                        read_lock_bh(&state->idev->lock);
@@ -2556,9 +2516,9 @@ static struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos)
 }
 
 static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos)
-       __acquires(dev_base_lock)
+       __acquires(RCU)
 {
-       read_lock(&dev_base_lock);
+       rcu_read_lock();
        return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
 }
 
@@ -2574,7 +2534,7 @@ static void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 }
 
 static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
-       __releases(dev_base_lock)
+       __releases(RCU)
 {
        struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
        if (likely(state->im != NULL)) {
@@ -2583,11 +2543,10 @@ static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
        }
        if (likely(state->idev != NULL)) {
                read_unlock_bh(&state->idev->lock);
-               in6_dev_put(state->idev);
                state->idev = NULL;
        }
        state->dev = NULL;
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
@@ -2603,10 +2562,10 @@ static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
                           "Source Address", "INC", "EXC");
        } else {
                seq_printf(seq,
-                          "%3d %6.6s " NIP6_SEQFMT " " NIP6_SEQFMT " %6lu %6lu\n",
+                          "%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
                           state->dev->ifindex, state->dev->name,
-                          NIP6(state->im->mca_addr),
-                          NIP6(psf->sf_addr),
+                          &state->im->mca_addr,
+                          &psf->sf_addr,
                           psf->sf_count[MCAST_INCLUDE],
                           psf->sf_count[MCAST_EXCLUDE]);
        }
@@ -2634,7 +2593,7 @@ static const struct file_operations igmp6_mcf_seq_fops = {
        .release        =       seq_release_net,
 };
 
-static int igmp6_proc_init(struct net *net)
+static int __net_init igmp6_proc_init(struct net *net)
 {
        int err;
 
@@ -2654,30 +2613,27 @@ out_proc_net_igmp6:
        goto out;
 }
 
-static void igmp6_proc_exit(struct net *net)
+static void __net_exit igmp6_proc_exit(struct net *net)
 {
        proc_net_remove(net, "mcfilter6");
        proc_net_remove(net, "igmp6");
 }
 #else
-static int igmp6_proc_init(struct net *net)
+static inline int igmp6_proc_init(struct net *net)
 {
        return 0;
 }
-static void igmp6_proc_exit(struct net *net)
+static inline void igmp6_proc_exit(struct net *net)
 {
-       ;
 }
 #endif
 
-static int igmp6_net_init(struct net *net)
+static int __net_init igmp6_net_init(struct net *net)
 {
-       struct ipv6_pinfo *np;
-       struct socket *sock;
-       struct sock *sk;
        int err;
 
-       err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &sock);
+       err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6,
+                                  SOCK_RAW, IPPROTO_ICMPV6, net);
        if (err < 0) {
                printk(KERN_ERR
                       "Failed to initialize the IGMP6 control socket (err %d).\n",
@@ -2685,13 +2641,7 @@ static int igmp6_net_init(struct net *net)
                goto out;
        }
 
-       net->ipv6.igmp_sk = sk = sock->sk;
-       sk_change_net(sk, net);
-       sk->sk_allocation = GFP_ATOMIC;
-       sk->sk_prot->unhash(sk);
-
-       np = inet6_sk(sk);
-       np->hop_limit = 1;
+       inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
 
        err = igmp6_proc_init(net);
        if (err)
@@ -2700,13 +2650,13 @@ out:
        return err;
 
 out_sock_create:
-       sk_release_kernel(net->ipv6.igmp_sk);
+       inet_ctl_sock_destroy(net->ipv6.igmp_sk);
        goto out;
 }
 
-static void igmp6_net_exit(struct net *net)
+static void __net_exit igmp6_net_exit(struct net *net)
 {
-       sk_release_kernel(net->ipv6.igmp_sk);
+       inet_ctl_sock_destroy(net->ipv6.igmp_sk);
        igmp6_proc_exit(net);
 }