net: skb->dst accessors
[safe/jmp/linux-2.6] / net / ipv6 / mcast.c
index 882cde4..4b264ed 100644 (file)
@@ -1,13 +1,11 @@
 /*
  *     Multicast support for IPv6
- *     Linux INET6 implementation 
+ *     Linux INET6 implementation
  *
  *     Authors:
- *     Pedro Roque             <roque@di.fc.ul.pt>     
+ *     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 
+ *     Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -49,6 +47,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv6.h>
 
+#include <net/net_namespace.h>
 #include <net/sock.h>
 #include <net/snmp.h>
 
@@ -58,6 +57,7 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 #include <net/ip6_route.h>
+#include <net/inet_common.h>
 
 #include <net/ip6_checksum.h>
 
@@ -125,10 +125,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);
 
-static struct socket *igmp6_socket;
-
-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);
@@ -155,7 +151,7 @@ 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)))
@@ -166,7 +162,6 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
        ((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
@@ -177,11 +172,12 @@ 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;
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct net *net = sock_net(sk);
        int err;
 
        if (!ipv6_addr_is_multicast(addr))
@@ -207,14 +203,14 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
 
        if (ifindex == 0) {
                struct rt6_info *rt;
-               rt = rt6_lookup(addr, NULL, 0, 0);
+               rt = rt6_lookup(net, addr, NULL, 0, 0);
                if (rt) {
                        dev = rt->rt6i_dev;
                        dev_hold(dev);
                        dst_release(&rt->u.dst);
                }
        } else
-               dev = dev_get_by_index(ifindex);
+               dev = dev_get_by_index(net, ifindex);
 
        if (dev == NULL) {
                sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
@@ -251,10 +247,11 @@ 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;
+       struct net *net = sock_net(sk);
 
        write_lock_bh(&ipv6_sk_mc_lock);
        for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
@@ -265,7 +262,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
                        *lnk = mc_lst->next;
                        write_unlock_bh(&ipv6_sk_mc_lock);
 
-                       if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
+                       dev = dev_get_by_index(net, mc_lst->ifindex);
+                       if (dev != NULL) {
                                struct inet6_dev *idev = in6_dev_get(dev);
 
                                (void) ip6_mc_leave_src(sk, mc_lst, idev);
@@ -285,7 +283,9 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
        return -EADDRNOTAVAIL;
 }
 
-static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex)
+static struct inet6_dev *ip6_mc_find_dev(struct net *net,
+                                        struct in6_addr *group,
+                                        int ifindex)
 {
        struct net_device *dev = NULL;
        struct inet6_dev *idev = NULL;
@@ -293,36 +293,40 @@ static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex)
        if (ifindex == 0) {
                struct rt6_info *rt;
 
-               rt = rt6_lookup(group, NULL, 0, 0);
+               rt = rt6_lookup(net, group, NULL, 0, 0);
                if (rt) {
                        dev = rt->rt6i_dev;
                        dev_hold(dev);
                        dst_release(&rt->u.dst);
                }
        } else
-               dev = dev_get_by_index(ifindex);
+               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)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6_mc_socklist *mc_lst;
+       struct net *net = sock_net(sk);
 
        write_lock_bh(&ipv6_sk_mc_lock);
        while ((mc_lst = np->ipv6_mc_list) != NULL) {
@@ -331,7 +335,7 @@ void ipv6_sock_mc_close(struct sock *sk)
                np->ipv6_mc_list = mc_lst->next;
                write_unlock_bh(&ipv6_sk_mc_lock);
 
-               dev = dev_get_by_index(mc_lst->ifindex);
+               dev = dev_get_by_index(net, mc_lst->ifindex);
                if (dev) {
                        struct inet6_dev *idev = in6_dev_get(dev);
 
@@ -360,22 +364,19 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
        struct inet6_dev *idev;
        struct ipv6_pinfo *inet6 = inet6_sk(sk);
        struct ip6_sf_socklist *psl;
+       struct net *net = sock_net(sk);
        int i, j, rv;
        int leavegroup = 0;
        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;
 
        if (!ipv6_addr_is_multicast(group))
                return -EINVAL;
 
-       idev = ip6_mc_find_dev(group, pgsr->gsr_interface);
+       idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface);
        if (!idev)
                return -ENODEV;
        dev = idev->dev;
@@ -499,6 +500,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
        struct inet6_dev *idev;
        struct ipv6_pinfo *inet6 = inet6_sk(sk);
        struct ip6_sf_socklist *newpsl, *psl;
+       struct net *net = sock_net(sk);
        int leavegroup = 0;
        int i, err;
 
@@ -510,7 +512,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
            gsf->gf_fmode != MCAST_EXCLUDE)
                return -EINVAL;
 
-       idev = ip6_mc_find_dev(group, gsf->gf_interface);
+       idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
 
        if (!idev)
                return -ENODEV;
@@ -591,13 +593,14 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
        struct net_device *dev;
        struct ipv6_pinfo *inet6 = inet6_sk(sk);
        struct ip6_sf_socklist *psl;
+       struct net *net = sock_net(sk);
 
        group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
 
        if (!ipv6_addr_is_multicast(group))
                return -EINVAL;
 
-       idev = ip6_mc_find_dev(group, gsf->gf_interface);
+       idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
 
        if (!idev)
                return -ENODEV;
@@ -644,7 +647,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
                memset(&ss, 0, sizeof(ss));
                psin6->sin6_family = AF_INET6;
                psin6->sin6_addr = psl->sl_addr[i];
-               if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
+               if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
                        return -EFAULT;
        }
        return 0;
@@ -655,8 +658,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;
@@ -862,7 +865,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;
@@ -902,9 +905,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
                return -ENOMEM;
        }
 
-       init_timer(&mc->mca_timer);
-       mc->mca_timer.function = igmp6_timer_handler;
-       mc->mca_timer.data = (unsigned long) mc;
+       setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc);
 
        ipv6_addr_copy(&mc->mca_addr, addr);
        mc->idev = idev;
@@ -935,7 +936,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;
 
@@ -960,7 +961,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;
@@ -988,7 +989,7 @@ int ipv6_is_mld(struct sk_buff *skb, int nexthdr)
        if (!pskb_may_pull(skb, sizeof(struct icmp6hdr)))
                return 0;
 
-       pic = (struct icmp6hdr *)skb->h.raw;
+       pic = icmp6_hdr(skb);
 
        switch (pic->icmp6_type) {
        case ICMPV6_MGM_QUERY:
@@ -1005,8 +1006,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;
@@ -1167,11 +1168,11 @@ int igmp6_event_query(struct sk_buff *skb)
                return -EINVAL;
 
        /* compute payload length excluding extension headers */
-       len = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
-       len -= (char *)skb->h.raw - (char *)skb->nh.ipv6h; 
+       len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);
+       len -= skb_network_header_len(skb);
 
        /* Drop queries with not link local source */
-       if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+       if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
                return -EINVAL;
 
        idev = in6_dev_get(skb->dev);
@@ -1179,7 +1180,7 @@ int igmp6_event_query(struct sk_buff *skb)
        if (idev == NULL)
                return 0;
 
-       hdr = (struct icmp6hdr *) skb->h.raw;
+       hdr = icmp6_hdr(skb);
        group = (struct in6_addr *) (hdr + 1);
        group_type = ipv6_addr_type(group);
 
@@ -1206,13 +1207,13 @@ int igmp6_event_query(struct sk_buff *skb)
                /* clear deleted report items */
                mld_clear_delrec(idev);
        } else if (len >= 28) {
-               int srcs_offset = sizeof(struct mld2_query) - 
+               int srcs_offset = sizeof(struct mld2_query) -
                                  sizeof(struct icmp6hdr);
                if (!pskb_may_pull(skb, srcs_offset)) {
                        in6_dev_put(idev);
                        return -EINVAL;
                }
-               mlh2 = (struct mld2_query *) skb->h.raw;
+               mlh2 = (struct mld2_query *)skb_transport_header(skb);
                max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
                if (!max_delay)
                        max_delay = 1;
@@ -1230,12 +1231,12 @@ int igmp6_event_query(struct sk_buff *skb)
                }
                /* mark sources to include, if group & source-specific */
                if (mlh2->nsrcs != 0) {
-                       if (!pskb_may_pull(skb, srcs_offset + 
+                       if (!pskb_may_pull(skb, srcs_offset +
                            ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {
                                in6_dev_put(idev);
                                return -EINVAL;
                        }
-                       mlh2 = (struct mld2_query *) skb->h.raw;
+                       mlh2 = (struct mld2_query *)skb_transport_header(skb);
                        mark = 1;
                }
        } else {
@@ -1300,11 +1301,11 @@ int igmp6_event_report(struct sk_buff *skb)
        if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
                return -EINVAL;
 
-       hdr = (struct icmp6hdr*) skb->h.raw;
+       hdr = icmp6_hdr(skb);
 
        /* Drop reports with not link local source */
-       addr_type = ipv6_addr_type(&skb->nh.ipv6h->saddr);
-       if (addr_type != IPV6_ADDR_ANY && 
+       addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
+       if (addr_type != IPV6_ADDR_ANY &&
            !(addr_type&IPV6_ADDR_LINKLOCAL))
                return -EINVAL;
 
@@ -1394,37 +1395,41 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted)
 
 static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 {
-       struct sock *sk = igmp6_socket->sk;
+       struct net *net = dev_net(dev);
+       struct sock *sk = net->ipv6.igmp_sk;
        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 == 0)
+       if (!skb)
                return NULL;
 
        skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-       if (ipv6_get_lladdr(dev, &addr_buf)) {
+       if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
                /* <draft-ietf-magma-mld-source-05.txt>:
-                * use unspecified address as the source address 
+                * 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));
 
-       pmr =(struct mld2_report *)skb_put(skb, sizeof(*pmr));
-       skb->h.raw = (unsigned char *)pmr;
+       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;
@@ -1433,56 +1438,63 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
        return skb;
 }
 
-static inline int mld_dev_queue_xmit2(struct sk_buff *skb)
-{
-       struct net_device *dev = skb->dev;
-
-       if (dev->hard_header) {
-               unsigned char ha[MAX_ADDR_LEN];
-               int err;
-
-               ndisc_mc_map(&skb->nh.ipv6h->daddr, ha, dev, 1);
-               err = dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len);
-               if (err < 0) {
-                       kfree_skb(skb);
-                       return err;
-               }
-       }
-       return dev_queue_xmit(skb);
-}
-
-static inline int mld_dev_queue_xmit(struct sk_buff *skb)
-{
-       return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dev,
-                      mld_dev_queue_xmit2);
-}
-
 static void mld_sendpack(struct sk_buff *skb)
 {
-       struct ipv6hdr *pip6 = skb->nh.ipv6h;
-       struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
+       struct ipv6hdr *pip6 = ipv6_hdr(skb);
+       struct mld2_report *pmr =
+                             (struct mld2_report *)skb_transport_header(skb);
        int payload_len, mldlen;
        struct inet6_dev *idev = in6_dev_get(skb->dev);
+       struct net *net = dev_net(skb->dev);
        int err;
+       struct flowi fl;
+       struct dst_entry *dst;
 
-       IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
-       payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
-               sizeof(struct ipv6hdr);
-       mldlen = skb->tail - skb->h.raw;
+       IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
+
+       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->h.raw, mldlen, 0));
-       err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
-               mld_dev_queue_xmit);
+               IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
+                                            mldlen, 0));
+
+       dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+
+       if (!dst) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       icmpv6_flow_init(net->ipv6.igmp_sk, &fl, ICMPV6_MLD2_REPORT,
+                        &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+                        skb->dev->ifindex);
+
+       err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+       skb_dst_set(skb, dst);
+       if (err)
+               goto err_out;
+
+       payload_len = skb->len;
+
+       err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+                     dst_output);
+out:
        if (!err) {
-               ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS);
-               IP6_INC_STATS(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(idev, IPSTATS_MIB_OUTDISCARDS);
+               IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
 
        if (likely(idev != NULL))
                in6_dev_put(idev);
+       return;
+
+err_out:
+       kfree_skb(skb);
+       goto out;
 }
 
 static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel)
@@ -1506,7 +1518,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        pgr->grec_auxwords = 0;
        pgr->grec_nsrcs = 0;
        pgr->grec_mca = pmc->mca_addr;  /* structure copy */
-       pmr = (struct mld2_report *)skb->h.raw;
+       pmr = (struct mld2_report *)skb_transport_header(skb);
        pmr->ngrec = htons(ntohs(pmr->ngrec)+1);
        *ppgr = pgr;
        return skb;
@@ -1539,7 +1551,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        if (!*psf_list)
                goto empty_source;
 
-       pmr = skb ? (struct mld2_report *)skb->h.raw : NULL;
+       pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL;
 
        /* EX and TO_EX get a fresh packet, if needed */
        if (truncate) {
@@ -1582,6 +1594,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
                        skb = add_grhead(skb, pmc, type, &pgr);
                        first = 0;
                }
+               if (!skb)
+                       return NULL;
                psrc = (struct in6_addr *)skb_put(skb, sizeof(*psrc));
                *psrc = psf->sf_addr;
                scount++; stotal++;
@@ -1750,38 +1764,40 @@ static void mld_send_cr(struct inet6_dev *idev)
 
 static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 {
-       struct sock *sk = igmp6_socket->sk;
+       struct net *net = dev_net(dev);
+       struct sock *sk = net->ipv6.igmp_sk;
        struct inet6_dev *idev;
-        struct sk_buff *skb;
-        struct icmp6hdr *hdr;
-       struct in6_addr *snd_addr;
+       struct sk_buff *skb;
+       struct icmp6hdr *hdr;
+       const struct in6_addr *snd_addr, *saddr;
        struct in6_addr *addrp;
        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;
@@ -1789,15 +1805,16 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
        skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-       if (ipv6_get_lladdr(dev, &addr_buf)) {
+       if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
                /* <draft-ietf-magma-mld-source-05.txt>:
-                * use unspecified address as the source address 
+                * 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));
 
@@ -1808,27 +1825,44 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
        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,
+       hdr->icmp6_cksum = csum_ipv6_magic(saddr, snd_addr, len,
                                           IPPROTO_ICMPV6,
-                                          csum_partial((__u8 *) hdr, len, 0));
+                                          csum_partial(hdr, len, 0));
 
        idev = in6_dev_get(skb->dev);
 
-       err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
-               mld_dev_queue_xmit);
+       dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+       if (!dst) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       icmpv6_flow_init(sk, &fl, type,
+                        &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+                        skb->dev->ifindex);
+
+       err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+       if (err)
+               goto err_out;
+
+       skb_dst_set(skb, dst);
+       err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+                     dst_output);
+out:
        if (!err) {
-               if (type == ICMPV6_MGM_REDUCTION)
-                       ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS);
-               else
-                       ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBRESPONSES);
-               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);
        return;
+
+err_out:
+       kfree_skb(skb);
+       goto out;
 }
 
 static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
@@ -2146,7 +2180,7 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
        /* callers have the socket lock and a write lock on ipv6_sk_mc_lock,
         * so no other readers or writers of iml or its sflist
         */
-       if (iml->sflist == 0) {
+       if (!iml->sflist) {
                /* any-source empty exclude case */
                return ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0);
        }
@@ -2261,14 +2295,12 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
        write_lock_bh(&idev->lock);
        rwlock_init(&idev->mc_lock);
        idev->mc_gq_running = 0;
-       init_timer(&idev->mc_gq_timer);
-       idev->mc_gq_timer.data = (unsigned long) idev;
-       idev->mc_gq_timer.function = &mld_gq_timer_expire;
+       setup_timer(&idev->mc_gq_timer, mld_gq_timer_expire,
+                       (unsigned long)idev);
        idev->mc_tomb = NULL;
        idev->mc_ifc_count = 0;
-       init_timer(&idev->mc_ifc_timer);
-       idev->mc_ifc_timer.data = (unsigned long) idev;
-       idev->mc_ifc_timer.function = &mld_ifc_timer_expire;
+       setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
+                       (unsigned long)idev);
        idev->mc_qrv = MLD_QRV_DEFAULT;
        idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
        idev->mc_v1_seen = 0;
@@ -2282,24 +2314,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) {
@@ -2316,6 +2343,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
 
 #ifdef CONFIG_PROC_FS
 struct igmp6_mc_iter_state {
+       struct seq_net_private p;
        struct net_device *dev;
        struct inet6_dev *idev;
 };
@@ -2326,10 +2354,10 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
 {
        struct ifmcaddr6 *im = NULL;
        struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+       struct net *net = seq_file_net(seq);
 
-       for (state->dev = dev_base, state->idev = NULL;
-            state->dev; 
-            state->dev = state->dev->next) {
+       state->idev = NULL;
+       for_each_netdev(net, state->dev) {
                struct inet6_dev *idev;
                idev = in6_dev_get(state->dev);
                if (!idev)
@@ -2356,7 +2384,7 @@ static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr
                        read_unlock_bh(&state->idev->lock);
                        in6_dev_put(state->idev);
                }
-               state->dev = state->dev->next;
+               state->dev = next_net_device(state->dev);
                if (!state->dev) {
                        state->idev = NULL;
                        break;
@@ -2380,6 +2408,7 @@ 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)
 {
        read_lock(&dev_base_lock);
        return igmp6_mc_get_idx(seq, *pos);
@@ -2394,6 +2423,7 @@ static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 }
 
 static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
+       __releases(dev_base_lock)
 {
        struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
        if (likely(state->idev != NULL)) {
@@ -2411,16 +2441,16 @@ 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);
        return 0;
 }
 
-static struct seq_operations igmp6_mc_seq_ops = {
+static const struct seq_operations igmp6_mc_seq_ops = {
        .start  =       igmp6_mc_seq_start,
        .next   =       igmp6_mc_seq_next,
        .stop   =       igmp6_mc_seq_stop,
@@ -2429,35 +2459,20 @@ static struct seq_operations igmp6_mc_seq_ops = {
 
 static int igmp6_mc_seq_open(struct inode *inode, struct file *file)
 {
-       struct seq_file *seq;
-       int rc = -ENOMEM;
-       struct igmp6_mc_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
-
-       if (!s)
-               goto out;
-
-       rc = seq_open(file, &igmp6_mc_seq_ops);
-       if (rc)
-               goto out_kfree;
-
-       seq = file->private_data;
-       seq->private = s;
-out:
-       return rc;
-out_kfree:
-       kfree(s);
-       goto out;
+       return seq_open_net(inode, file, &igmp6_mc_seq_ops,
+                           sizeof(struct igmp6_mc_iter_state));
 }
 
-static struct file_operations igmp6_mc_seq_fops = {
+static const struct file_operations igmp6_mc_seq_fops = {
        .owner          =       THIS_MODULE,
        .open           =       igmp6_mc_seq_open,
        .read           =       seq_read,
        .llseek         =       seq_lseek,
-       .release        =       seq_release_private,
+       .release        =       seq_release_net,
 };
 
 struct igmp6_mcf_iter_state {
+       struct seq_net_private p;
        struct net_device *dev;
        struct inet6_dev *idev;
        struct ifmcaddr6 *im;
@@ -2470,10 +2485,11 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
        struct ip6_sf_list *psf = NULL;
        struct ifmcaddr6 *im = NULL;
        struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+       struct net *net = seq_file_net(seq);
 
-       for (state->dev = dev_base, state->idev = NULL, state->im = NULL;
-            state->dev; 
-            state->dev = state->dev->next) {
+       state->idev = NULL;
+       state->im = NULL;
+       for_each_netdev(net, state->dev) {
                struct inet6_dev *idev;
                idev = in6_dev_get(state->dev);
                if (unlikely(idev == NULL))
@@ -2509,7 +2525,7 @@ static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_s
                                read_unlock_bh(&state->idev->lock);
                                in6_dev_put(state->idev);
                        }
-                       state->dev = state->dev->next;
+                       state->dev = next_net_device(state->dev);
                        if (!state->dev) {
                                state->idev = NULL;
                                goto out;
@@ -2539,6 +2555,7 @@ 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)
 {
        read_lock(&dev_base_lock);
        return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
@@ -2556,6 +2573,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)
 {
        struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
        if (likely(state->im != NULL)) {
@@ -2577,24 +2595,24 @@ static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
        struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
 
        if (v == SEQ_START_TOKEN) {
-               seq_printf(seq, 
+               seq_printf(seq,
                           "%3s %6s "
                           "%32s %32s %6s %6s\n", "Idx",
                           "Device", "Multicast Address",
                           "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]);
        }
        return 0;
 }
 
-static struct seq_operations igmp6_mcf_seq_ops = {
+static const struct seq_operations igmp6_mcf_seq_ops = {
        .start  =       igmp6_mcf_seq_start,
        .next   =       igmp6_mcf_seq_next,
        .stop   =       igmp6_mcf_seq_stop,
@@ -2603,72 +2621,97 @@ static struct seq_operations igmp6_mcf_seq_ops = {
 
 static int igmp6_mcf_seq_open(struct inode *inode, struct file *file)
 {
-       struct seq_file *seq;
-       int rc = -ENOMEM;
-       struct igmp6_mcf_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
-       
-       if (!s)
-               goto out;
-
-       rc = seq_open(file, &igmp6_mcf_seq_ops);
-       if (rc)
-               goto out_kfree;
-
-       seq = file->private_data;
-       seq->private = s;
-out:
-       return rc;
-out_kfree:
-       kfree(s);
-       goto out;
+       return seq_open_net(inode, file, &igmp6_mcf_seq_ops,
+                           sizeof(struct igmp6_mcf_iter_state));
 }
 
-static struct file_operations igmp6_mcf_seq_fops = {
+static const struct file_operations igmp6_mcf_seq_fops = {
        .owner          =       THIS_MODULE,
        .open           =       igmp6_mcf_seq_open,
        .read           =       seq_read,
        .llseek         =       seq_lseek,
-       .release        =       seq_release_private,
+       .release        =       seq_release_net,
 };
+
+static int igmp6_proc_init(struct net *net)
+{
+       int err;
+
+       err = -ENOMEM;
+       if (!proc_net_fops_create(net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops))
+               goto out;
+       if (!proc_net_fops_create(net, "mcfilter6", S_IRUGO,
+                                 &igmp6_mcf_seq_fops))
+               goto out_proc_net_igmp6;
+
+       err = 0;
+out:
+       return err;
+
+out_proc_net_igmp6:
+       proc_net_remove(net, "igmp6");
+       goto out;
+}
+
+static void 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)
+{
+       return 0;
+}
+static void igmp6_proc_exit(struct net *net)
+{
+       ;
+}
 #endif
 
-int __init igmp6_init(struct net_proto_family *ops)
+static int igmp6_net_init(struct net *net)
 {
-       struct ipv6_pinfo *np;
-       struct sock *sk;
        int err;
 
-       err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &igmp6_socket);
+       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",
                       err);
-               igmp6_socket = NULL; /* For safety. */
-               return err;
+               goto out;
        }
 
-       sk = igmp6_socket->sk;
-       sk->sk_allocation = GFP_ATOMIC;
-       sk->sk_prot->unhash(sk);
+       inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
 
-       np = inet6_sk(sk);
-       np->hop_limit = 1;
+       err = igmp6_proc_init(net);
+       if (err)
+               goto out_sock_create;
+out:
+       return err;
 
-#ifdef CONFIG_PROC_FS
-       proc_net_fops_create("igmp6", S_IRUGO, &igmp6_mc_seq_fops);
-       proc_net_fops_create("mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops);
-#endif
+out_sock_create:
+       inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+       goto out;
+}
 
-       return 0;
+static void igmp6_net_exit(struct net *net)
+{
+       inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+       igmp6_proc_exit(net);
 }
 
-void igmp6_cleanup(void)
+static struct pernet_operations igmp6_net_ops = {
+       .init = igmp6_net_init,
+       .exit = igmp6_net_exit,
+};
+
+int __init igmp6_init(void)
 {
-       sock_release(igmp6_socket);
-       igmp6_socket = NULL; /* for safety */
+       return register_pernet_subsys(&igmp6_net_ops);
+}
 
-#ifdef CONFIG_PROC_FS
-       proc_net_remove("mcfilter6");
-       proc_net_remove("igmp6");
-#endif
+void igmp6_cleanup(void)
+{
+       unregister_pernet_subsys(&igmp6_net_ops);
 }