Commit 6dad59bb authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji

[IPV6] refer inet6 device via corresponding local route from address structure.

Signed-off-by: default avatarHideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
parent c375d517
......@@ -51,6 +51,7 @@ struct inet6_ifaddr
struct timer_list timer;
struct inet6_dev *idev;
struct rt6_info *rt;
struct inet6_ifaddr *lst_next; /* next addr in addr_lst */
struct inet6_ifaddr *if_next; /* next addr in inet6_dev */
......@@ -133,6 +134,7 @@ struct ifacaddr6
{
struct in6_addr aca_addr;
struct inet6_dev *aca_idev;
struct rt6_info *aca_rt;
struct ifacaddr6 *aca_next;
int aca_users;
atomic_t aca_refcnt;
......
......@@ -472,6 +472,8 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
printk("Freeing alive inet6 address %p\n", ifp);
return;
}
dst_release(&ifp->rt->u.dst);
inet6_ifa_count--;
kfree(ifp);
}
......@@ -482,25 +484,33 @@ static struct inet6_ifaddr *
ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
int scope, unsigned flags)
{
struct inet6_ifaddr *ifa;
struct inet6_ifaddr *ifa = NULL;
struct rt6_info *rt;
int hash;
static spinlock_t lock = SPIN_LOCK_UNLOCKED;
int err = 0;
spin_lock_bh(&lock);
/* Ignore adding duplicate addresses on an interface */
if (ipv6_chk_same_addr(addr, idev->dev)) {
spin_unlock_bh(&lock);
ADBG(("ipv6_add_addr: already assigned\n"));
return ERR_PTR(-EEXIST);
err = -EEXIST;
goto out;
}
ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
if (ifa == NULL) {
spin_unlock_bh(&lock);
ADBG(("ipv6_add_addr: malloc failed\n"));
return ERR_PTR(-ENOBUFS);
err = -ENOBUFS;
goto out;
}
rt = addrconf_dst_alloc(idev, addr, 0);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto out;
}
memset(ifa, 0, sizeof(struct inet6_ifaddr));
......@@ -517,9 +527,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
read_lock(&addrconf_lock);
if (idev->dead) {
read_unlock(&addrconf_lock);
spin_unlock_bh(&lock);
kfree(ifa);
return ERR_PTR(-ENODEV); /*XXX*/
err = -ENODEV; /*XXX*/
goto out;
}
inet6_ifa_count++;
......@@ -553,12 +562,20 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
}
#endif
ifa->rt = rt;
in6_ifa_hold(ifa);
write_unlock_bh(&idev->lock);
read_unlock(&addrconf_lock);
out:
spin_unlock_bh(&lock);
notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifa);
if (unlikely(err == 0))
notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
else {
kfree(ifa);
ifa = ERR_PTR(err);
}
return ifa;
}
......@@ -2981,7 +2998,9 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
switch (event) {
case RTM_NEWADDR:
ip6_rt_addr_add(&ifp->addr, ifp->idev->dev, 0);
dst_hold(&ifp->rt->u.dst);
if (ip6_ins_rt(ifp->rt, NULL, NULL))
dst_release(&ifp->rt->u.dst);
break;
case RTM_DELADDR:
addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
......@@ -2992,8 +3011,11 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
if (!ipv6_addr_any(&addr))
ipv6_dev_ac_dec(ifp->idev->dev, &addr);
}
if (!ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 1))
ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
dst_hold(&ifp->rt->u.dst);
if (ip6_del_rt(ifp->rt, NULL, NULL))
dst_free(&ifp->rt->u.dst);
else
dst_release(&ifp->rt->u.dst);
break;
}
}
......
......@@ -293,6 +293,7 @@ static void aca_put(struct ifacaddr6 *ac)
{
if (atomic_dec_and_test(&ac->aca_refcnt)) {
in6_dev_put(ac->aca_idev);
dst_release(&ac->aca_rt->u.dst);
kfree(ac);
}
}
......@@ -304,6 +305,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
{
struct ifacaddr6 *aca;
struct inet6_dev *idev;
struct rt6_info *rt;
int err;
idev = in6_dev_get(dev);
......@@ -312,17 +315,15 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
write_lock_bh(&idev->lock);
if (idev->dead) {
write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return -ENODEV;
err = -ENODEV;
goto out;
}
for (aca = idev->ac_list; aca; aca = aca->aca_next) {
if (ipv6_addr_cmp(&aca->aca_addr, addr) == 0) {
aca->aca_users++;
write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return 0;
err = 0;
goto out;
}
}
......@@ -333,15 +334,22 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
aca = kmalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);
if (aca == NULL) {
write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return -ENOMEM;
err = -ENOMEM;
goto out;
}
rt = addrconf_dst_alloc(idev, addr, 1);
if (IS_ERR(rt)) {
kfree(aca);
err = PTR_ERR(rt);
goto out;
}
memset(aca, 0, sizeof(struct ifacaddr6));
ipv6_addr_copy(&aca->aca_addr, addr);
aca->aca_idev = idev;
aca->aca_rt = rt;
aca->aca_users = 1;
/* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies;
......@@ -352,12 +360,18 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
idev->ac_list = aca;
write_unlock_bh(&idev->lock);
ip6_rt_addr_add(&aca->aca_addr, dev, 1);
dst_hold(&rt->u.dst);
if (ip6_ins_rt(rt, NULL, NULL))
dst_release(&rt->u.dst);
addrconf_join_solict(dev, &aca->aca_addr);
aca_put(aca);
return 0;
out:
write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return err;
}
/*
......@@ -396,7 +410,11 @@ int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
write_unlock_bh(&idev->lock);
addrconf_leave_solict(dev, &aca->aca_addr);
ip6_rt_addr_del(&aca->aca_addr, dev);
dst_hold(&aca->aca_rt->u.dst);
if (ip6_del_rt(aca->aca_rt, NULL, NULL))
dst_free(&aca->aca_rt->u.dst);
else
dst_release(&aca->aca_rt->u.dst);
aca_put(aca);
in6_dev_put(idev);
......
......@@ -1376,57 +1376,9 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
return rt;
}
/*
* Add address
*/
int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev, int anycast)
{
struct inet6_dev *idev;
struct rt6_info *rt;
int err = 0;
idev = in6_dev_get(&loopback_dev);
if (!idev) {
err = -ENODEV;
goto out;
}
rt = addrconf_dst_alloc(idev, addr, anycast);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto out;
}
ip6_ins_rt(rt, NULL, NULL);
out:
if (idev)
in6_dev_put(idev);
return err;
}
/* Delete address. Warning: you should check that this address
disappeared before calling this function.
*/
int ip6_rt_addr_del(struct in6_addr *addr, struct net_device *dev)
{
struct rt6_info *rt;
int err = -ENOENT;
rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1);
if (rt) {
if (rt->rt6i_dst.plen == 128)
err = ip6_del_rt(rt, NULL, NULL);
else
dst_release(&rt->u.dst);
}
atomic_set(&rt->u.dst.__refcnt, 1);
return err;
return rt;
}
static int fib6_ifdown(struct rt6_info *rt, void *arg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment