ipv6,mcast: always hold idev->lock before mca_lock
[pandora-kernel.git] / net / ipv6 / addrconf.c
index cf88df8..314bda2 100644 (file)
@@ -429,11 +429,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        ndev->tstamp = jiffies;
        addrconf_sysctl_register(ndev);
        /* protected by rtnl_lock */
-       RCU_INIT_POINTER(dev->ip6_ptr, ndev);
+       rcu_assign_pointer(dev->ip6_ptr, ndev);
 
        /* Join all-node multicast group */
        ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
 
+       /* Join all-router multicast group if forwarding is set */
+       if (ndev->cnf.forwarding && dev && (dev->flags & IFF_MULTICAST))
+               ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
+
        return ndev;
 }
 
@@ -489,8 +493,7 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
        struct net_device *dev;
        struct inet6_dev *idev;
 
-       rcu_read_lock();
-       for_each_netdev_rcu(net, dev) {
+       for_each_netdev(net, dev) {
                idev = __in6_dev_get(dev);
                if (idev) {
                        int changed = (!idev->cnf.forwarding) ^ (!newf);
@@ -499,7 +502,6 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
                                dev_forward_change(idev);
                }
        }
-       rcu_read_unlock();
 }
 
 static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
@@ -1234,6 +1236,23 @@ try_nextdev:
 }
 EXPORT_SYMBOL(ipv6_dev_get_saddr);
 
+int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
+                     unsigned char banned_flags)
+{
+       struct inet6_ifaddr *ifp;
+       int err = -EADDRNOTAVAIL;
+
+       list_for_each_entry(ifp, &idev->addr_list, if_list) {
+               if (ifp->scope == IFA_LINK &&
+                   !(ifp->flags & banned_flags)) {
+                       ipv6_addr_copy(addr, &ifp->addr);
+                       err = 0;
+                       break;
+               }
+       }
+       return err;
+}
+
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
                    unsigned char banned_flags)
 {
@@ -1243,17 +1262,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
        rcu_read_lock();
        idev = __in6_dev_get(dev);
        if (idev) {
-               struct inet6_ifaddr *ifp;
-
                read_lock_bh(&idev->lock);
-               list_for_each_entry(ifp, &idev->addr_list, if_list) {
-                       if (ifp->scope == IFA_LINK &&
-                           !(ifp->flags & banned_flags)) {
-                               ipv6_addr_copy(addr, &ifp->addr);
-                               err = 0;
-                               break;
-                       }
-               }
+               err = __ipv6_get_lladdr(idev, addr, banned_flags);
                read_unlock_bh(&idev->lock);
        }
        rcu_read_unlock();
@@ -1737,7 +1747,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
                        continue;
                if ((rt->rt6i_flags & flags) != flags)
                        continue;
-               if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0))
+               if ((rt->rt6i_flags & noflags) != 0)
                        continue;
                dst_hold(&rt->dst);
                break;
@@ -1805,7 +1815,8 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
                return ERR_PTR(-EACCES);
 
        /* Add default multicast route */
-       addrconf_add_mroute(dev);
+       if (!(dev->flags & IFF_LOOPBACK))
+               addrconf_add_mroute(dev);
 
        /* Add link local route */
        addrconf_add_lroute(dev);
@@ -2401,6 +2412,9 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
 static void init_loopback(struct net_device *dev)
 {
        struct inet6_dev  *idev;
+       struct net_device *sp_dev;
+       struct inet6_ifaddr *sp_ifa;
+       struct rt6_info *sp_rt;
 
        /* ::1 */
 
@@ -2412,6 +2426,35 @@ static void init_loopback(struct net_device *dev)
        }
 
        add_addr(idev, &in6addr_loopback, 128, IFA_HOST);
+
+       /* Add routes to other interface's IPv6 addresses */
+       for_each_netdev(dev_net(dev), sp_dev) {
+               if (!strcmp(sp_dev->name, dev->name))
+                       continue;
+
+               idev = __in6_dev_get(sp_dev);
+               if (!idev)
+                       continue;
+
+               read_lock_bh(&idev->lock);
+               list_for_each_entry(sp_ifa, &idev->addr_list, if_list) {
+
+                       if (sp_ifa->flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))
+                               continue;
+
+                       if (sp_ifa->rt)
+                               continue;
+
+                       sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, 0);
+
+                       /* Failure cases are ignored */
+                       if (!IS_ERR(sp_rt)) {
+                               sp_ifa->rt = sp_rt;
+                               ip6_ins_rt(sp_rt);
+                       }
+               }
+               read_unlock_bh(&idev->lock);
+       }
 }
 
 static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr)
@@ -4655,26 +4698,20 @@ static void addrconf_sysctl_unregister(struct inet6_dev *idev)
 
 static int __net_init addrconf_init_net(struct net *net)
 {
-       int err;
+       int err = -ENOMEM;
        struct ipv6_devconf *all, *dflt;
 
-       err = -ENOMEM;
-       all = &ipv6_devconf;
-       dflt = &ipv6_devconf_dflt;
+       all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL);
+       if (all == NULL)
+               goto err_alloc_all;
 
-       if (!net_eq(net, &init_net)) {
-               all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL);
-               if (all == NULL)
-                       goto err_alloc_all;
+       dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
+       if (dflt == NULL)
+               goto err_alloc_dflt;
 
-               dflt = kmemdup(dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
-               if (dflt == NULL)
-                       goto err_alloc_dflt;
-       } else {
-               /* these will be inherited by all namespaces */
-               dflt->autoconf = ipv6_defaults.autoconf;
-               dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
-       }
+       /* these will be inherited by all namespaces */
+       dflt->autoconf = ipv6_defaults.autoconf;
+       dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
 
        net->ipv6.devconf_all = all;
        net->ipv6.devconf_dflt = dflt;