]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv6/addrconf.c
ipv6: AF_INET6 link address family
[net-next-2.6.git] / net / ipv6 / addrconf.c
index ec7a91d9e86553475555b9df19d22ced2dc5eb31..470e7acb91dfec637eceaea6adc23b9fbd169b34 100644 (file)
@@ -836,7 +836,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 {
        struct inet6_dev *idev = ifp->idev;
        struct in6_addr addr, *tmpaddr;
-       unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp;
+       unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp, age;
        unsigned long regen_advance;
        int tmp_plen;
        int ret = 0;
@@ -886,12 +886,13 @@ retry:
                goto out;
        }
        memcpy(&addr.s6_addr[8], idev->rndid, 8);
+       age = (jiffies - ifp->tstamp) / HZ;
        tmp_valid_lft = min_t(__u32,
                              ifp->valid_lft,
-                             idev->cnf.temp_valid_lft);
+                             idev->cnf.temp_valid_lft + age);
        tmp_prefered_lft = min_t(__u32,
                                 ifp->prefered_lft,
-                                idev->cnf.temp_prefered_lft -
+                                idev->cnf.temp_prefered_lft + age -
                                 idev->cnf.max_desync_factor);
        tmp_plen = ifp->prefix_len;
        max_addresses = idev->cnf.max_addresses;
@@ -1426,8 +1427,10 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
 {
        struct inet6_dev *idev = ifp->idev;
 
-       if (addrconf_dad_end(ifp))
+       if (addrconf_dad_end(ifp)) {
+               in6_ifa_put(ifp);
                return;
+       }
 
        if (net_ratelimit())
                printk(KERN_INFO "%s: IPv6 duplicate address %pI6c detected!\n",
@@ -2021,10 +2024,11 @@ ok:
                                        ipv6_ifa_notify(0, ift);
                        }
 
-                       if (create && in6_dev->cnf.use_tempaddr > 0) {
+                       if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) {
                                /*
                                 * When a new public address is created as described in [ADDRCONF],
-                                * also create a new temporary address.
+                                * also create a new temporary address. Also create a temporary
+                                * address if it's enabled but no temporary address currently exists.
                                 */
                                read_unlock_bh(&in6_dev->lock);
                                ipv6_create_tempaddr(ifp, NULL);
@@ -2736,10 +2740,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                        /* Flag it for later restoration when link comes up */
                        ifa->flags |= IFA_F_TENTATIVE;
                        ifa->state = INET6_IFADDR_STATE_DAD;
-
-                       write_unlock_bh(&idev->lock);
-
-                       in6_ifa_hold(ifa);
                } else {
                        list_del(&ifa->if_list);
 
@@ -2754,19 +2754,15 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                        ifa->state = INET6_IFADDR_STATE_DEAD;
                        spin_unlock_bh(&ifa->state_lock);
 
-                       if (state == INET6_IFADDR_STATE_DEAD)
-                               goto put_ifa;
-               }
-
-               __ipv6_ifa_notify(RTM_DELADDR, ifa);
-               if (ifa->state == INET6_IFADDR_STATE_DEAD)
-                       atomic_notifier_call_chain(&inet6addr_chain,
-                                                  NETDEV_DOWN, ifa);
-
-put_ifa:
-               in6_ifa_put(ifa);
+                       if (state != INET6_IFADDR_STATE_DEAD) {
+                               __ipv6_ifa_notify(RTM_DELADDR, ifa);
+                               atomic_notifier_call_chain(&inet6addr_chain,
+                                                          NETDEV_DOWN, ifa);
+                       }
 
-               write_lock_bh(&idev->lock);
+                       in6_ifa_put(ifa);
+                       write_lock_bh(&idev->lock);
+               }
        }
 
        list_splice(&keep_list, &idev->addr_list);
@@ -3835,6 +3831,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))
@@ -3842,13 +3847,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,
@@ -3895,15 +3894,76 @@ 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 = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100
+                   + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+       ci.reachable_time = idev->nd_parms->reachable_time;
+       ci.retrans_time = 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_parse_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+       return -EOPNOTSUPP;
+}
+
 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)
@@ -3930,31 +3990,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 = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100
-                   + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
-       ci.reachable_time = idev->nd_parms->reachable_time;
-       ci.retrans_time = 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)
+       if (inet6_fill_ifla6_attrs(skb, idev) < 0)
                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);
@@ -4625,6 +4662,13 @@ 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,
+       .parse_link_af    = inet6_parse_link_af,
+};
+
 /*
  *     Init / cleanup code
  */
@@ -4676,6 +4720,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;
@@ -4691,6 +4739,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);
@@ -4711,6 +4761,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)