X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Faddrconf.c;h=f2f89366abcc6cdd978cb3ebaf53d833e0bdcdf7;hb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;hp=15f481859bece658a1e873fd7de4331d2e901451;hpb=e3f6fb6212a7102bdb56ba38fa1e98fe72950475;p=linux-2.6.git diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 15f481859..f2f89366a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -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.