Linux 2.6.26-rc8
[safe/jmp/linux-2.6] / net / ipv6 / addrconf.c
index 9241583..147588f 100644 (file)
@@ -222,6 +222,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
 
 /* Check if a valid qdisc is available */
 static inline int addrconf_qdisc_ok(struct net_device *dev)
@@ -321,7 +323,6 @@ EXPORT_SYMBOL(in6_dev_finish_destroy);
 static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
 {
        struct inet6_dev *ndev;
-       struct in6_addr maddr;
 
        ASSERT_RTNL();
 
@@ -371,25 +372,26 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
         */
        in6_dev_hold(ndev);
 
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+       if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
+               printk(KERN_INFO
+                      "%s: Disabled Multicast RS\n",
+                      dev->name);
+               ndev->cnf.rtr_solicits = 0;
+       }
+#endif
+
 #ifdef CONFIG_IPV6_PRIVACY
        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)
+           dev->type == ARPHRD_TUNNEL6 ||
            dev->type == ARPHRD_SIT ||
-#endif
            dev->type == ARPHRD_NONE) {
                printk(KERN_INFO
                       "%s: Disabled Privacy Extensions\n",
                       dev->name);
                ndev->cnf.use_tempaddr = -1;
-
-               if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
-                       printk(KERN_INFO
-                              "%s: Disabled Multicast RS\n",
-                              dev->name);
-                       ndev->cnf.rtr_solicits = 0;
-               }
        } else {
                in6_dev_hold(ndev);
                ipv6_regen_rndid((unsigned long) ndev);
@@ -406,13 +408,12 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        rcu_assign_pointer(dev->ip6_ptr, ndev);
 
        /* Join all-node multicast group */
-       ipv6_addr_all_nodes(&maddr);
-       ipv6_dev_mc_inc(dev, &maddr);
+       ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
 
        return ndev;
 }
 
-struct inet6_dev * ipv6_find_idev(struct net_device *dev)
+static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
 {
        struct inet6_dev *idev;
 
@@ -433,18 +434,15 @@ static void dev_forward_change(struct inet6_dev *idev)
 {
        struct net_device *dev;
        struct inet6_ifaddr *ifa;
-       struct in6_addr addr;
 
        if (!idev)
                return;
        dev = idev->dev;
        if (dev && (dev->flags & IFF_MULTICAST)) {
-               ipv6_addr_all_routers(&addr);
-
                if (idev->cnf.forwarding)
-                       ipv6_dev_mc_inc(dev, &addr);
+                       ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
                else
-                       ipv6_dev_mc_dec(dev, &addr);
+                       ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters);
        }
        for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
                if (ifa->flags&IFA_F_TENTATIVE)
@@ -541,6 +539,25 @@ ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
        *ifap = ifp;
 }
 
+/*
+ *     Hash function taken from net_alias.c
+ */
+static u8 ipv6_addr_hash(const struct in6_addr *addr)
+{
+       __u32 word;
+
+       /*
+        * We perform the hash function over the last 64 bits of the address
+        * This will include the IEEE address token on links that support it.
+        */
+
+       word = (__force u32)(addr->s6_addr32[2] ^ addr->s6_addr32[3]);
+       word ^= (word >> 16);
+       word ^= (word >> 8);
+
+       return ((word ^ (word >> 4)) & 0x0f);
+}
+
 /* On success it returns ifp with increased reference count */
 
 static struct inet6_ifaddr *
@@ -714,8 +731,13 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
                                                onlink = -1;
 
                                        spin_lock(&ifa->lock);
-                                       lifetime = min_t(unsigned long,
-                                                        ifa->valid_lft, 0x7fffffffUL/HZ);
+
+                                       lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
+                                       /*
+                                        * Note: Because this address is
+                                        * not permanent, lifetime <
+                                        * LONG_MAX / HZ here.
+                                        */
                                        if (time_before(expires,
                                                        ifa->tstamp + lifetime * HZ))
                                                expires = ifa->tstamp + lifetime * HZ;
@@ -921,7 +943,7 @@ struct ipv6_saddr_score {
 };
 
 struct ipv6_saddr_dst {
-       struct in6_addr *addr;
+       const struct in6_addr *addr;
        int ifindex;
        int scope;
        int label;
@@ -1055,7 +1077,7 @@ out:
 }
 
 int ipv6_dev_get_saddr(struct net_device *dst_dev,
-                      struct in6_addr *daddr, unsigned int prefs,
+                      const struct in6_addr *daddr, unsigned int prefs,
                       struct in6_addr *saddr)
 {
        struct ipv6_saddr_score scores[2],
@@ -1290,7 +1312,7 @@ int ipv6_chk_prefix(struct in6_addr *addr, struct net_device *dev)
 
 EXPORT_SYMBOL(ipv6_chk_prefix);
 
-struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, struct in6_addr *addr,
+struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr,
                                     struct net_device *dev, int strict)
 {
        struct inet6_ifaddr * ifp;
@@ -1475,6 +1497,29 @@ static int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
        return 0;
 }
 
+int __ipv6_isatap_ifid(u8 *eui, __be32 addr)
+{
+       eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) ||
+                 ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) ||
+                 ipv4_is_private_172(addr) || ipv4_is_test_192(addr) ||
+                 ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) ||
+                 ipv4_is_test_198(addr) || ipv4_is_multicast(addr) ||
+                 ipv4_is_lbcast(addr)) ? 0x00 : 0x02;
+       eui[1] = 0;
+       eui[2] = 0x5E;
+       eui[3] = 0xFE;
+       memcpy(eui + 4, &addr, 4);
+       return 0;
+}
+EXPORT_SYMBOL(__ipv6_isatap_ifid);
+
+static int addrconf_ifid_sit(u8 *eui, struct net_device *dev)
+{
+       if (dev->priv_flags & IFF_ISATAP)
+               return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);
+       return -1;
+}
+
 static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 {
        switch (dev->type) {
@@ -1487,8 +1532,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
        case ARPHRD_INFINIBAND:
                return addrconf_ifid_infiniband(eui, dev);
        case ARPHRD_SIT:
-               if (dev->priv_flags & IFF_ISATAP)
-                       return ipv6_isatap_eui64(eui, *(__be32 *)dev->dev_addr);
+               return addrconf_ifid_sit(eui, dev);
        }
        return -1;
 }
@@ -1683,7 +1727,6 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
        __u32 valid_lft;
        __u32 prefered_lft;
        int addr_type;
-       unsigned long rt_expires;
        struct inet6_dev *in6_dev;
 
        pinfo = (struct prefix_info *) opt;
@@ -1725,41 +1768,49 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
         *      2) Configure prefixes with the auto flag set
         */
 
-       /* Avoid arithmetic overflow. Really, we could
-          save rt_expires in seconds, likely valid_lft,
-          but it would require division in fib gc, that it
-          not good.
-        */
-       if (valid_lft >= 0x7FFFFFFF/HZ)
-               rt_expires = 0x7FFFFFFF - (0x7FFFFFFF % HZ);
-       else
-               rt_expires = valid_lft * HZ;
-
-       /*
-        * We convert this (in jiffies) to clock_t later.
-        * Avoid arithmetic overflow there as well.
-        * Overflow can happen only if HZ < USER_HZ.
-        */
-       if (HZ < USER_HZ && rt_expires > 0x7FFFFFFF / USER_HZ)
-               rt_expires = 0x7FFFFFFF / USER_HZ;
-
        if (pinfo->onlink) {
                struct rt6_info *rt;
+               unsigned long rt_expires;
+
+               /* Avoid arithmetic overflow. Really, we could
+                * save rt_expires in seconds, likely valid_lft,
+                * but it would require division in fib gc, that it
+                * not good.
+                */
+               if (HZ > USER_HZ)
+                       rt_expires = addrconf_timeout_fixup(valid_lft, HZ);
+               else
+                       rt_expires = addrconf_timeout_fixup(valid_lft, USER_HZ);
+
+               if (addrconf_finite_timeout(rt_expires))
+                       rt_expires *= HZ;
+
                rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL,
                                dev->ifindex, 1);
 
                if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
-                       if (rt->rt6i_flags&RTF_EXPIRES) {
-                               if (valid_lft == 0) {
-                                       ip6_del_rt(rt);
-                                       rt = NULL;
-                               } else {
-                                       rt->rt6i_expires = jiffies + rt_expires;
-                               }
+                       /* Autoconf prefix route */
+                       if (valid_lft == 0) {
+                               ip6_del_rt(rt);
+                               rt = NULL;
+                       } else if (addrconf_finite_timeout(rt_expires)) {
+                               /* not infinity */
+                               rt->rt6i_expires = jiffies + rt_expires;
+                               rt->rt6i_flags |= RTF_EXPIRES;
+                       } else {
+                               rt->rt6i_flags &= ~RTF_EXPIRES;
+                               rt->rt6i_expires = 0;
                        }
                } else if (valid_lft) {
+                       clock_t expires = 0;
+                       int flags = RTF_ADDRCONF | RTF_PREFIX_RT;
+                       if (addrconf_finite_timeout(rt_expires)) {
+                               /* not infinity */
+                               flags |= RTF_EXPIRES;
+                               expires = jiffies_to_clock_t(rt_expires);
+                       }
                        addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
-                                             dev, jiffies_to_clock_t(rt_expires), RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
+                                             dev, expires, flags);
                }
                if (rt)
                        dst_release(&rt->u.dst);
@@ -1975,17 +2026,22 @@ err_exit:
  *     Manual configuration of address on an interface
  */
 static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
-                         int plen, __u8 ifa_flags, __u32 prefered_lft,
+                         unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
                          __u32 valid_lft)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
        struct net_device *dev;
        int scope;
-       u32 flags = RTF_EXPIRES;
+       u32 flags;
+       clock_t expires;
+       unsigned long timeout;
 
        ASSERT_RTNL();
 
+       if (plen > 128)
+               return -EINVAL;
+
        /* check the lifetime */
        if (!valid_lft || prefered_lft > valid_lft)
                return -EINVAL;
@@ -1999,17 +2055,23 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
 
        scope = ipv6_addr_scope(pfx);
 
-       if (valid_lft == INFINITY_LIFE_TIME) {
-               ifa_flags |= IFA_F_PERMANENT;
+       timeout = addrconf_timeout_fixup(valid_lft, HZ);
+       if (addrconf_finite_timeout(timeout)) {
+               expires = jiffies_to_clock_t(timeout * HZ);
+               valid_lft = timeout;
+               flags = RTF_EXPIRES;
+       } else {
+               expires = 0;
                flags = 0;
-       } else if (valid_lft >= 0x7FFFFFFF/HZ)
-               valid_lft = 0x7FFFFFFF/HZ;
+               ifa_flags |= IFA_F_PERMANENT;
+       }
 
-       if (prefered_lft == 0)
-               ifa_flags |= IFA_F_DEPRECATED;
-       else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
-                (prefered_lft != INFINITY_LIFE_TIME))
-               prefered_lft = 0x7FFFFFFF/HZ;
+       timeout = addrconf_timeout_fixup(prefered_lft, HZ);
+       if (addrconf_finite_timeout(timeout)) {
+               if (timeout == 0)
+                       ifa_flags |= IFA_F_DEPRECATED;
+               prefered_lft = timeout;
+       }
 
        ifp = ipv6_add_addr(idev, pfx, plen, scope, ifa_flags);
 
@@ -2021,7 +2083,7 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
                spin_unlock_bh(&ifp->lock);
 
                addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
-                                     jiffies_to_clock_t(valid_lft * HZ), flags);
+                                     expires, flags);
                /*
                 * Note that section 3.1 of RFC 4429 indicates
                 * that the Optimistic flag should not be set for
@@ -2037,12 +2099,15 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
 }
 
 static int inet6_addr_del(struct net *net, int ifindex, struct in6_addr *pfx,
-                         int plen)
+                         unsigned int plen)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
        struct net_device *dev;
 
+       if (plen > 128)
+               return -EINVAL;
+
        dev = __dev_get_by_index(net, ifindex);
        if (!dev)
                return -ENODEV;
@@ -2491,7 +2556,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        ASSERT_RTNL();
 
-       if (dev == init_net.loopback_dev && how == 1)
+       if ((dev->flags & IFF_LOOPBACK) && how == 1)
                how = 0;
 
        rt6_ifdown(net, dev);
@@ -2504,7 +2569,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        /* Step 1: remove reference to ipv6 device from parent device.
                   Do not dev_put!
         */
-       if (how == 1) {
+       if (how) {
                idev->dead = 1;
 
                /* protected by rtnl_lock */
@@ -2536,12 +2601,12 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        write_lock_bh(&idev->lock);
 
        /* Step 3: clear flags for stateless addrconf */
-       if (how != 1)
+       if (!how)
                idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
 
        /* Step 4: clear address list */
 #ifdef CONFIG_IPV6_PRIVACY
-       if (how == 1 && del_timer(&idev->regen_timer))
+       if (how && del_timer(&idev->regen_timer))
                in6_dev_put(idev);
 
        /* clear tempaddr list */
@@ -2578,7 +2643,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        /* Step 5: Discard multicast list */
 
-       if (how == 1)
+       if (how)
                ipv6_mc_destroy_dev(idev);
        else
                ipv6_mc_down(idev);
@@ -2587,7 +2652,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        /* Shot the device (if unregistered) */
 
-       if (how == 1) {
+       if (how) {
                addrconf_sysctl_unregister(idev);
                neigh_parms_release(&nd_tbl, idev->nd_parms);
                neigh_ifdown(&nd_tbl, dev);
@@ -2613,8 +2678,6 @@ static void addrconf_rs_timer(unsigned long data)
 
        spin_lock(&ifp->lock);
        if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) {
-               struct in6_addr all_routers;
-
                /* The wait after the last probe can be shorter */
                addrconf_mod_timer(ifp, AC_RS,
                                   (ifp->probes == ifp->idev->cnf.rtr_solicits) ?
@@ -2622,9 +2685,7 @@ static void addrconf_rs_timer(unsigned long data)
                                   ifp->idev->cnf.rtr_solicit_interval);
                spin_unlock(&ifp->lock);
 
-               ipv6_addr_all_routers(&all_routers);
-
-               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
        } else {
                spin_unlock(&ifp->lock);
                /*
@@ -2711,7 +2772,6 @@ static void addrconf_dad_timer(unsigned long data)
 {
        struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
        struct inet6_dev *idev = ifp->idev;
-       struct in6_addr unspec;
        struct in6_addr mcaddr;
 
        read_lock_bh(&idev->lock);
@@ -2740,9 +2800,8 @@ static void addrconf_dad_timer(unsigned long data)
        read_unlock_bh(&idev->lock);
 
        /* send a neighbour solicitation for our addr */
-       memset(&unspec, 0, sizeof(unspec));
        addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-       ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+       ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any);
 out:
        in6_ifa_put(ifp);
 }
@@ -2765,16 +2824,12 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
            ifp->idev->cnf.rtr_solicits > 0 &&
            (dev->flags&IFF_LOOPBACK) == 0 &&
            (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
-               struct in6_addr all_routers;
-
-               ipv6_addr_all_routers(&all_routers);
-
                /*
                 *      If a host as already performed a random delay
                 *      [...] as part of DAD [...] there is no need
                 *      to delay again before sending the first RS
                 */
-               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
 
                spin_lock_bh(&ifp->lock);
                ifp->probes = 1;
@@ -2951,7 +3006,7 @@ int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr)
        for (ifp = inet6_addr_lst[hash]; ifp; ifp = ifp->lst_next) {
                if (!net_eq(dev_net(ifp->idev->dev), net))
                        continue;
-               if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
+               if (ipv6_addr_equal(&ifp->addr, addr) &&
                    (ifp->flags & IFA_F_HOMEADDRESS)) {
                        ret = 1;
                        break;
@@ -3119,22 +3174,30 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
                             u32 prefered_lft, u32 valid_lft)
 {
-       u32 flags = RTF_EXPIRES;
+       u32 flags;
+       clock_t expires;
+       unsigned long timeout;
 
        if (!valid_lft || (prefered_lft > valid_lft))
                return -EINVAL;
 
-       if (valid_lft == INFINITY_LIFE_TIME) {
-               ifa_flags |= IFA_F_PERMANENT;
+       timeout = addrconf_timeout_fixup(valid_lft, HZ);
+       if (addrconf_finite_timeout(timeout)) {
+               expires = jiffies_to_clock_t(timeout * HZ);
+               valid_lft = timeout;
+               flags = RTF_EXPIRES;
+       } else {
+               expires = 0;
                flags = 0;
-       } else if (valid_lft >= 0x7FFFFFFF/HZ)
-               valid_lft = 0x7FFFFFFF/HZ;
+               ifa_flags |= IFA_F_PERMANENT;
+       }
 
-       if (prefered_lft == 0)
-               ifa_flags |= IFA_F_DEPRECATED;
-       else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
-                (prefered_lft != INFINITY_LIFE_TIME))
-               prefered_lft = 0x7FFFFFFF/HZ;
+       timeout = addrconf_timeout_fixup(prefered_lft, HZ);
+       if (addrconf_finite_timeout(timeout)) {
+               if (timeout == 0)
+                       ifa_flags |= IFA_F_DEPRECATED;
+               prefered_lft = timeout;
+       }
 
        spin_lock_bh(&ifp->lock);
        ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
@@ -3147,7 +3210,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
                ipv6_ifa_notify(0, ifp);
 
        addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
-                             jiffies_to_clock_t(valid_lft * HZ), flags);
+                             expires, flags);
        addrconf_verify(0);
 
        return 0;
@@ -4213,7 +4276,7 @@ static void addrconf_sysctl_register(struct inet6_dev *idev)
        neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6,
                              NET_IPV6_NEIGH, "ipv6",
                              &ndisc_ifinfo_sysctl_change,
-                             NULL);
+                             ndisc_ifinfo_sysctl_strategy);
        __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
                        idev->dev->ifindex, idev, &idev->cnf);
 }
@@ -4309,12 +4372,6 @@ int unregister_inet6addr_notifier(struct notifier_block *nb)
 
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
 
-
-static int addrconf_net_init(struct net *net)
-{
-       return 0;
-}
-
 static void addrconf_net_exit(struct net *net)
 {
        struct net_device *dev;
@@ -4331,7 +4388,6 @@ static void addrconf_net_exit(struct net *net)
 }
 
 static struct pernet_operations addrconf_net_ops = {
-       .init = addrconf_net_init,
        .exit = addrconf_net_exit,
 };