vserver 2.0 rc7
[linux-2.6.git] / net / ipv6 / addrconf.c
index 15f4818..f2f8936 100644 (file)
@@ -372,6 +372,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
                ndev->regen_timer.data = (unsigned long) ndev;
                if ((dev->flags&IFF_LOOPBACK) ||
                    dev->type == ARPHRD_TUNNEL ||
+                   dev->type == ARPHRD_NONE ||
                    dev->type == ARPHRD_SIT) {
                        printk(KERN_INFO
                                "Disabled Privacy Extensions on device %p(%s)\n",
@@ -391,7 +392,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
                ndev->tstamp = jiffies;
 #ifdef CONFIG_SYSCTL
                neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, 
-                       NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
+                                     NET_IPV6_NEIGH, "ipv6",
+                                     &ndisc_ifinfo_sysctl_change,
+                                     NULL);
                addrconf_sysctl_register(ndev, &ndev->cnf);
 #endif
        }
@@ -569,7 +572,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 out2:
        read_unlock_bh(&addrconf_lock);
 
-       if (unlikely(err == 0))
+       if (likely(err == 0))
                notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
        else {
                kfree(ifa);
@@ -589,6 +592,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        struct inet6_ifaddr *ifa, **ifap;
        struct inet6_dev *idev = ifp->idev;
        int hash;
+       int deleted = 0, onlink = 0;
+       unsigned long expires = jiffies;
 
        hash = ipv6_addr_hash(&ifp->addr);
 
@@ -631,7 +636,31 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
                        *ifap = ifa->if_next;
                        __in6_ifa_put(ifp);
                        ifa->if_next = NULL;
-                       break;
+                       if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
+                               break;
+                       deleted = 1;
+               } else if (ifp->flags & IFA_F_PERMANENT) {
+                       if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
+                                             ifp->prefix_len)) {
+                               if (ifa->flags & IFA_F_PERMANENT) {
+                                       onlink = 1;
+                                       if (deleted)
+                                               break;
+                               } else {
+                                       unsigned long lifetime;
+
+                                       if (!onlink)
+                                               onlink = -1;
+
+                                       spin_lock(&ifa->lock);
+                                       lifetime = min_t(unsigned long,
+                                                        ifa->valid_lft, 0x7fffffffUL/HZ);
+                                       if (time_before(expires,
+                                                       ifa->tstamp + lifetime * HZ))
+                                               expires = ifa->tstamp + lifetime * HZ;
+                                       spin_unlock(&ifa->lock);
+                               }
+                       }
                }
        }
        write_unlock_bh(&idev->lock);
@@ -642,6 +671,40 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 
        addrconf_del_timer(ifp);
 
+       /*
+        * Purge or update corresponding prefix
+        *
+        * 1) we don't purge prefix here if address was not permanent.
+        *    prefix is managed by its own lifetime.
+        * 2) if there're no addresses, delete prefix.
+        * 3) if there're still other permanent address(es),
+        *    corresponding prefix is still permanent.
+        * 4) otherwise, update prefix lifetime to the
+        *    longest valid lifetime among the corresponding
+        *    addresses on the device.
+        *    Note: subsequent RA will update lifetime.
+        *
+        * --yoshfuji
+        */
+       if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
+               struct in6_addr prefix;
+               struct rt6_info *rt;
+
+               ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
+               rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1);
+
+               if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+                       if (onlink == 0) {
+                               ip6_del_rt(rt, NULL, NULL);
+                               rt = NULL;
+                       } else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
+                               rt->rt6i_expires = expires;
+                               rt->rt6i_flags |= RTF_EXPIRES;
+                       }
+               }
+               dst_release(&rt->u.dst);
+       }
+
        in6_ifa_put(ifp);
 }
 
@@ -880,7 +943,7 @@ out:
 int ipv6_get_saddr(struct dst_entry *dst,
                   struct in6_addr *daddr, struct in6_addr *saddr)
 {
-       return ipv6_dev_get_saddr(dst ? dst->dev : NULL, daddr, saddr);
+       return ipv6_dev_get_saddr(dst ? ((struct rt6_info *)dst)->rt6i_idev->dev : NULL, daddr, saddr);
 }
 
 
@@ -1982,7 +2045,10 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                if (idev) {
                        addrconf_sysctl_unregister(&idev->cnf);
                        neigh_sysctl_unregister(idev->nd_parms);
-                       neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
+                       neigh_sysctl_register(dev, idev->nd_parms,
+                                             NET_IPV6, NET_IPV6_NEIGH, "ipv6",
+                                             &ndisc_ifinfo_sysctl_change,
+                                             NULL);
                        addrconf_sysctl_register(idev, &idev->cnf);
                }
 #endif
@@ -2008,6 +2074,9 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        ASSERT_RTNL();
 
+       if (dev == &loopback_dev && how == 1)
+               how = 0;
+
        rt6_ifdown(dev);
        neigh_ifdown(&nd_tbl, dev);
 
@@ -2862,12 +2931,8 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
        r->ifi_family = AF_INET6;
        r->ifi_type = dev->type;
        r->ifi_index = dev->ifindex;
-       r->ifi_flags = dev->flags;
+       r->ifi_flags = dev_get_flags(dev);
        r->ifi_change = 0;
-       if (!netif_running(dev) || !netif_carrier_ok(dev))
-               r->ifi_flags &= ~IFF_RUNNING;
-       else
-               r->ifi_flags |= IFF_RUNNING;
 
        RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
 
@@ -3023,7 +3088,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
        netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_PREFIX, GFP_ATOMIC);
 }
 
-static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
+static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = {
        [RTM_GETLINK - RTM_BASE] = { .dumpit    = inet6_dump_ifinfo, },
        [RTM_NEWADDR - RTM_BASE] = { .doit      = inet6_rtm_newaddr, },
        [RTM_DELADDR - RTM_BASE] = { .doit      = inet6_rtm_deladdr, },
@@ -3158,7 +3223,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table,
 static struct addrconf_sysctl_table
 {
        struct ctl_table_header *sysctl_header;
-       ctl_table addrconf_vars[18];
+       ctl_table addrconf_vars[__NET_IPV6_MAX];
        ctl_table addrconf_dev[2];
        ctl_table addrconf_conf_dir[2];
        ctl_table addrconf_proto_dir[2];
@@ -3449,8 +3514,10 @@ int unregister_inet6addr_notifier(struct notifier_block *nb)
  *     Init / cleanup code
  */
 
-void __init addrconf_init(void)
+int __init addrconf_init(void)
 {
+       int err = 0;
+
        /* The addrconf netdev notifier requires that loopback_dev
         * has it's ipv6 private information allocated and setup
         * before it can bring up and give link-local addresses
@@ -3464,15 +3531,17 @@ void __init addrconf_init(void)
         * first, then loopback_dev, which cases all the non-loopback_dev
         * devices to fail to get a link-local address.
         *
-        * So, as a temporary fix, register loopback_dev first by hand.
+        * So, as a temporary fix, allocate the ipv6 structure for
+        * loopback_dev first by hand.
         * Longer term, all of the dependencies ipv6 has upon the loopback
         * device and it being up should be removed.
         */
        rtnl_lock();
-       addrconf_notify(&ipv6_dev_notf, NETDEV_REGISTER, &loopback_dev);
-       if (loopback_dev.flags & IFF_UP)
-               addrconf_notify(&ipv6_dev_notf, NETDEV_UP, &loopback_dev);
+       if (!ipv6_add_dev(&loopback_dev))
+               err = -ENOMEM;
        rtnl_unlock();
+       if (err)
+               return err;
 
        register_netdevice_notifier(&ipv6_dev_notf);
 
@@ -3490,6 +3559,8 @@ void __init addrconf_init(void)
                register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
        addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
 #endif
+
+       return 0;
 }
 
 void __exit addrconf_cleanup(void)
@@ -3518,6 +3589,7 @@ void __exit addrconf_cleanup(void)
                        continue;
                addrconf_ifdown(dev, 1);
        }
+       addrconf_ifdown(&loopback_dev, 2);
 
        /*
         *      Check hash table.