[IPV6] ADDRCONF: Allow address selection policy with ifindex.
[sfrench/cifs-2.6.git] / net / ipv6 / addrconf.c
index 52d10d213217c0bb8c7be5bbda65d9e31ad45e3d..e1e591bfbdca20b21d1dcd803db2685e13311b23 100644 (file)
@@ -255,11 +255,6 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
 
 static int snmp6_alloc_dev(struct inet6_dev *idev)
 {
-       int err = -ENOMEM;
-
-       if (!idev || !idev->dev)
-               return -EINVAL;
-
        if (snmp_mib_init((void **)idev->stats.ipv6,
                          sizeof(struct ipstats_mib),
                          __alignof__(struct ipstats_mib)) < 0)
@@ -280,15 +275,14 @@ err_icmpmsg:
 err_icmp:
        snmp_mib_free((void **)idev->stats.ipv6);
 err_ip:
-       return err;
+       return -ENOMEM;
 }
 
-static int snmp6_free_dev(struct inet6_dev *idev)
+static void snmp6_free_dev(struct inet6_dev *idev)
 {
        snmp_mib_free((void **)idev->stats.icmpv6msg);
        snmp_mib_free((void **)idev->stats.icmpv6);
        snmp_mib_free((void **)idev->stats.ipv6);
-       return 0;
 }
 
 /* Nobody refers to this device, we may destroy it. */
@@ -372,9 +366,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        in6_dev_hold(ndev);
 
 #ifdef CONFIG_IPV6_PRIVACY
-       init_timer(&ndev->regen_timer);
-       ndev->regen_timer.function = ipv6_regen_rndid;
-       ndev->regen_timer.data = (unsigned long) ndev;
+       setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
        if ((dev->flags&IFF_LOOPBACK) ||
            dev->type == ARPHRD_TUNNEL ||
 #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
@@ -883,7 +875,8 @@ static inline int ipv6_saddr_preferred(int type)
 }
 
 /* static matching label */
-static inline int ipv6_saddr_label(const struct in6_addr *addr, int type)
+static inline int ipv6_addr_label(const struct in6_addr *addr, int type,
+                                 int ifindex)
 {
  /*
   *    prefix (longest match)  label
@@ -918,7 +911,8 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
        struct inet6_ifaddr *ifa_result = NULL;
        int daddr_type = __ipv6_addr_type(daddr);
        int daddr_scope = __ipv6_addr_src_scope(daddr_type);
-       u32 daddr_label = ipv6_saddr_label(daddr, daddr_type);
+       int daddr_ifindex = daddr_dev ? daddr_dev->ifindex : 0;
+       u32 daddr_label = ipv6_addr_label(daddr, daddr_type, daddr_ifindex);
        struct net_device *dev;
 
        memset(&hiscore, 0, sizeof(hiscore));
@@ -973,7 +967,7 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                        if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
                                     score.addr_type & IPV6_ADDR_MULTICAST)) {
                                LIMIT_NETDEBUG(KERN_DEBUG
-                                              "ADDRCONF: unspecified / multicast address"
+                                              "ADDRCONF: unspecified / multicast address "
                                               "assigned as unicast address on %s",
                                               dev->name);
                                continue;
@@ -1091,11 +1085,15 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 
                        /* Rule 6: Prefer matching label */
                        if (hiscore.rule < 6) {
-                               if (ipv6_saddr_label(&ifa_result->addr, hiscore.addr_type) == daddr_label)
+                               if (ipv6_addr_label(&ifa_result->addr,
+                                                   hiscore.addr_type,
+                                                   ifa_result->idev->dev->ifindex) == daddr_label)
                                        hiscore.attrs |= IPV6_SADDR_SCORE_LABEL;
                                hiscore.rule++;
                        }
-                       if (ipv6_saddr_label(&ifa->addr, score.addr_type) == daddr_label) {
+                       if (ipv6_addr_label(&ifa->addr,
+                                           score.addr_type,
+                                           ifa->idev->dev->ifindex) == daddr_label) {
                                score.attrs |= IPV6_SADDR_SCORE_LABEL;
                                if (!(hiscore.attrs & IPV6_SADDR_SCORE_LABEL)) {
                                        score.rule = 6;
@@ -2299,6 +2297,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                break;
                        }
 
+                       if (!idev && dev->mtu >= IPV6_MIN_MTU)
+                               idev = ipv6_add_dev(dev);
+
                        if (idev)
                                idev->if_flags |= IF_READY;
                } else {
@@ -2363,12 +2364,18 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                break;
 
        case NETDEV_CHANGEMTU:
-               if ( idev && dev->mtu >= IPV6_MIN_MTU) {
+               if (idev && dev->mtu >= IPV6_MIN_MTU) {
                        rt6_mtu_change(dev, dev->mtu);
                        idev->cnf.mtu6 = dev->mtu;
                        break;
                }
 
+               if (!idev && dev->mtu >= IPV6_MIN_MTU) {
+                       idev = ipv6_add_dev(dev);
+                       if (idev)
+                               break;
+               }
+
                /* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */
 
        case NETDEV_DOWN:
@@ -4294,8 +4301,4 @@ void __exit addrconf_cleanup(void)
        del_timer(&addr_chk_timer);
 
        rtnl_unlock();
-
-#ifdef CONFIG_PROC_FS
-       proc_net_remove(&init_net, "if_inet6");
-#endif
 }