ipv6: Revert 'administrative down' address handling changes.
[pandora-kernel.git] / net / ipv6 / addrconf.c
index 848b355..fd6782e 100644 (file)
@@ -420,9 +420,6 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
            dev->type == ARPHRD_TUNNEL6 ||
            dev->type == ARPHRD_SIT ||
            dev->type == ARPHRD_NONE) {
-               printk(KERN_INFO
-                      "%s: Disabled Privacy Extensions\n",
-                      dev->name);
                ndev->cnf.use_tempaddr = -1;
        } else {
                in6_dev_hold(ndev);
@@ -2664,14 +2661,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        struct net *net = dev_net(dev);
        struct inet6_dev *idev;
        struct inet6_ifaddr *ifa;
-       LIST_HEAD(keep_list);
-       int state;
+       int state, i;
 
        ASSERT_RTNL();
 
-       /* Flush routes if device is being removed or it is not loopback */
-       if (how || !(dev->flags & IFF_LOOPBACK))
-               rt6_ifdown(net, dev);
+       rt6_ifdown(net, dev);
        neigh_ifdown(&nd_tbl, dev);
 
        idev = __in6_dev_get(dev);
@@ -2693,6 +2687,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        }
 
+       /* Step 2: clear hash table */
+       for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+               struct hlist_head *h = &inet6_addr_lst[i];
+               struct hlist_node *n;
+
+               spin_lock_bh(&addrconf_hash_lock);
+       restart:
+               hlist_for_each_entry_rcu(ifa, n, h, addr_lst) {
+                       if (ifa->idev == idev) {
+                               hlist_del_init_rcu(&ifa->addr_lst);
+                               addrconf_del_timer(ifa);
+                               goto restart;
+                       }
+               }
+               spin_unlock_bh(&addrconf_hash_lock);
+       }
+
        write_lock_bh(&idev->lock);
 
        /* Step 2: clear flags for stateless addrconf */
@@ -2726,52 +2737,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                                       struct inet6_ifaddr, if_list);
                addrconf_del_timer(ifa);
 
-               /* If just doing link down, and address is permanent
-                  and not link-local, then retain it. */
-               if (!how &&
-                   (ifa->flags&IFA_F_PERMANENT) &&
-                   !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
-                       list_move_tail(&ifa->if_list, &keep_list);
-
-                       /* If not doing DAD on this address, just keep it. */
-                       if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
-                           idev->cnf.accept_dad <= 0 ||
-                           (ifa->flags & IFA_F_NODAD))
-                               continue;
+               list_del(&ifa->if_list);
 
-                       /* If it was tentative already, no need to notify */
-                       if (ifa->flags & IFA_F_TENTATIVE)
-                               continue;
+               write_unlock_bh(&idev->lock);
 
-                       /* Flag it for later restoration when link comes up */
-                       ifa->flags |= IFA_F_TENTATIVE;
-                       ifa->state = INET6_IFADDR_STATE_DAD;
-               } else {
-                       list_del(&ifa->if_list);
-
-                       /* clear hash table */
-                       spin_lock_bh(&addrconf_hash_lock);
-                       hlist_del_init_rcu(&ifa->addr_lst);
-                       spin_unlock_bh(&addrconf_hash_lock);
-
-                       write_unlock_bh(&idev->lock);
-                       spin_lock_bh(&ifa->state_lock);
-                       state = ifa->state;
-                       ifa->state = INET6_IFADDR_STATE_DEAD;
-                       spin_unlock_bh(&ifa->state_lock);
-
-                       if (state != INET6_IFADDR_STATE_DEAD) {
-                               __ipv6_ifa_notify(RTM_DELADDR, ifa);
-                               atomic_notifier_call_chain(&inet6addr_chain,
-                                                          NETDEV_DOWN, ifa);
-                       }
+               spin_lock_bh(&ifa->state_lock);
+               state = ifa->state;
+               ifa->state = INET6_IFADDR_STATE_DEAD;
+               spin_unlock_bh(&ifa->state_lock);
 
-                       in6_ifa_put(ifa);
-                       write_lock_bh(&idev->lock);
+               if (state != INET6_IFADDR_STATE_DEAD) {
+                       __ipv6_ifa_notify(RTM_DELADDR, ifa);
+                       atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
                }
-       }
+               in6_ifa_put(ifa);
 
-       list_splice(&keep_list, &idev->addr_list);
+               write_lock_bh(&idev->lock);
+       }
 
        write_unlock_bh(&idev->lock);
 
@@ -3838,6 +3820,15 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao;
 }
 
+static inline size_t inet6_ifla6_size(void)
+{
+       return nla_total_size(4) /* IFLA_INET6_FLAGS */
+            + nla_total_size(sizeof(struct ifla_cacheinfo))
+            + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
+            + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
+            + nla_total_size(ICMP6_MIB_MAX * 8); /* IFLA_INET6_ICMP6STATS */
+}
+
 static inline size_t inet6_if_nlmsg_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct ifinfomsg))
@@ -3845,13 +3836,7 @@ static inline size_t inet6_if_nlmsg_size(void)
               + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
               + nla_total_size(4) /* IFLA_MTU */
               + nla_total_size(4) /* IFLA_LINK */
-              + nla_total_size( /* IFLA_PROTINFO */
-                       nla_total_size(4) /* IFLA_INET6_FLAGS */
-                       + nla_total_size(sizeof(struct ifla_cacheinfo))
-                       + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
-                       + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
-                       + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
-                );
+              + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */
 }
 
 static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib,
@@ -3898,15 +3883,70 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
        }
 }
 
+static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
+{
+       struct nlattr *nla;
+       struct ifla_cacheinfo ci;
+
+       NLA_PUT_U32(skb, IFLA_INET6_FLAGS, idev->if_flags);
+
+       ci.max_reasm_len = IPV6_MAXPLEN;
+       ci.tstamp = cstamp_delta(idev->tstamp);
+       ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time);
+       ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time);
+       NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
+
+       nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
+       if (nla == NULL)
+               goto nla_put_failure;
+       ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
+
+       /* XXX - MC not implemented */
+
+       nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
+       if (nla == NULL)
+               goto nla_put_failure;
+       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
+
+       nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
+       if (nla == NULL)
+               goto nla_put_failure;
+       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
+
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static size_t inet6_get_link_af_size(const struct net_device *dev)
+{
+       if (!__in6_dev_get(dev))
+               return 0;
+
+       return inet6_ifla6_size();
+}
+
+static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct inet6_dev *idev = __in6_dev_get(dev);
+
+       if (!idev)
+               return -ENODATA;
+
+       if (inet6_fill_ifla6_attrs(skb, idev) < 0)
+               return -EMSGSIZE;
+
+       return 0;
+}
+
 static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
                             u32 pid, u32 seq, int event, unsigned int flags)
 {
        struct net_device *dev = idev->dev;
-       struct nlattr *nla;
        struct ifinfomsg *hdr;
        struct nlmsghdr *nlh;
        void *protoinfo;
-       struct ifla_cacheinfo ci;
 
        nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
        if (nlh == NULL)
@@ -3933,30 +3973,8 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
        if (protoinfo == NULL)
                goto nla_put_failure;
 
-       NLA_PUT_U32(skb, IFLA_INET6_FLAGS, idev->if_flags);
-
-       ci.max_reasm_len = IPV6_MAXPLEN;
-       ci.tstamp = cstamp_delta(idev->tstamp);
-       ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time);
-       ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time);
-       NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
-
-       nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
-       if (nla == NULL)
+       if (inet6_fill_ifla6_attrs(skb, idev) < 0)
                goto nla_put_failure;
-       ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
-
-       /* XXX - MC not implemented */
-
-       nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
-       if (nla == NULL)
-               goto nla_put_failure;
-       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
-
-       nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
-       if (nla == NULL)
-               goto nla_put_failure;
-       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
 
        nla_nest_end(skb, protoinfo);
        return nlmsg_end(skb, nlh);
@@ -4124,8 +4142,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                addrconf_leave_solict(ifp->idev, &ifp->addr);
                dst_hold(&ifp->rt->dst);
 
-               if (ifp->state == INET6_IFADDR_STATE_DEAD &&
-                   ip6_del_rt(ifp->rt))
+               if (ip6_del_rt(ifp->rt))
                        dst_free(&ifp->rt->dst);
                break;
        }
@@ -4627,6 +4644,12 @@ int unregister_inet6addr_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
 
+static struct rtnl_af_ops inet6_ops = {
+       .family           = AF_INET6,
+       .fill_link_af     = inet6_fill_link_af,
+       .get_link_af_size = inet6_get_link_af_size,
+};
+
 /*
  *     Init / cleanup code
  */
@@ -4678,6 +4701,10 @@ int __init addrconf_init(void)
 
        addrconf_verify(0);
 
+       err = rtnl_af_register(&inet6_ops);
+       if (err < 0)
+               goto errout_af;
+
        err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo);
        if (err < 0)
                goto errout;
@@ -4693,6 +4720,8 @@ int __init addrconf_init(void)
 
        return 0;
 errout:
+       rtnl_af_unregister(&inet6_ops);
+errout_af:
        unregister_netdevice_notifier(&ipv6_dev_notf);
 errlo:
        unregister_pernet_subsys(&addrconf_ops);
@@ -4713,6 +4742,8 @@ void addrconf_cleanup(void)
 
        rtnl_lock();
 
+       __rtnl_af_unregister(&inet6_ops);
+
        /* clean dev list */
        for_each_netdev(&init_net, dev) {
                if (__in6_dev_get(dev) == NULL)