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",
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
}
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);
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);
*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);
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);
}
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);
}
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
ASSERT_RTNL();
+ if (dev == &loopback_dev && how == 1)
+ how = 0;
+
rt6_ifdown(dev);
neigh_ifdown(&nd_tbl, dev);
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);
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, },
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];
* 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
* 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);
register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
#endif
+
+ return 0;
}
void __exit addrconf_cleanup(void)
continue;
addrconf_ifdown(dev, 1);
}
+ addrconf_ifdown(&loopback_dev, 2);
/*
* Check hash table.