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 ...@@ -51,6 +51,7 @@ struct inet6_ifaddr
struct timer_list timer; struct timer_list timer;
struct inet6_dev *idev; struct inet6_dev *idev;
struct rt6_info *rt;
struct inet6_ifaddr *lst_next; /* next addr in addr_lst */ struct inet6_ifaddr *lst_next; /* next addr in addr_lst */
struct inet6_ifaddr *if_next; /* next addr in inet6_dev */ struct inet6_ifaddr *if_next; /* next addr in inet6_dev */
...@@ -133,6 +134,7 @@ struct ifacaddr6 ...@@ -133,6 +134,7 @@ struct ifacaddr6
{ {
struct in6_addr aca_addr; struct in6_addr aca_addr;
struct inet6_dev *aca_idev; struct inet6_dev *aca_idev;
struct rt6_info *aca_rt;
struct ifacaddr6 *aca_next; struct ifacaddr6 *aca_next;
int aca_users; int aca_users;
atomic_t aca_refcnt; atomic_t aca_refcnt;
......
...@@ -472,6 +472,8 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) ...@@ -472,6 +472,8 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
printk("Freeing alive inet6 address %p\n", ifp); printk("Freeing alive inet6 address %p\n", ifp);
return; return;
} }
dst_release(&ifp->rt->u.dst);
inet6_ifa_count--; inet6_ifa_count--;
kfree(ifp); kfree(ifp);
} }
...@@ -482,25 +484,33 @@ static struct inet6_ifaddr * ...@@ -482,25 +484,33 @@ static struct inet6_ifaddr *
ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
int scope, unsigned flags) int scope, unsigned flags)
{ {
struct inet6_ifaddr *ifa; struct inet6_ifaddr *ifa = NULL;
struct rt6_info *rt;
int hash; int hash;
static spinlock_t lock = SPIN_LOCK_UNLOCKED; static spinlock_t lock = SPIN_LOCK_UNLOCKED;
int err = 0;
spin_lock_bh(&lock); spin_lock_bh(&lock);
/* Ignore adding duplicate addresses on an interface */ /* Ignore adding duplicate addresses on an interface */
if (ipv6_chk_same_addr(addr, idev->dev)) { if (ipv6_chk_same_addr(addr, idev->dev)) {
spin_unlock_bh(&lock);
ADBG(("ipv6_add_addr: already assigned\n")); ADBG(("ipv6_add_addr: already assigned\n"));
return ERR_PTR(-EEXIST); err = -EEXIST;
goto out;
} }
ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC); ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
if (ifa == NULL) { if (ifa == NULL) {
spin_unlock_bh(&lock);
ADBG(("ipv6_add_addr: malloc failed\n")); 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)); 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, ...@@ -517,9 +527,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
read_lock(&addrconf_lock); read_lock(&addrconf_lock);
if (idev->dead) { if (idev->dead) {
read_unlock(&addrconf_lock); read_unlock(&addrconf_lock);
spin_unlock_bh(&lock); err = -ENODEV; /*XXX*/
kfree(ifa); goto out;
return ERR_PTR(-ENODEV); /*XXX*/
} }
inet6_ifa_count++; inet6_ifa_count++;
...@@ -553,12 +562,20 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, ...@@ -553,12 +562,20 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
} }
#endif #endif
ifa->rt = rt;
in6_ifa_hold(ifa); in6_ifa_hold(ifa);
write_unlock_bh(&idev->lock); write_unlock_bh(&idev->lock);
read_unlock(&addrconf_lock); read_unlock(&addrconf_lock);
out:
spin_unlock_bh(&lock); 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; return ifa;
} }
...@@ -2981,7 +2998,9 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -2981,7 +2998,9 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
switch (event) { switch (event) {
case RTM_NEWADDR: 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; break;
case RTM_DELADDR: case RTM_DELADDR:
addrconf_leave_solict(ifp->idev->dev, &ifp->addr); addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
...@@ -2992,8 +3011,11 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -2992,8 +3011,11 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
if (!ipv6_addr_any(&addr)) if (!ipv6_addr_any(&addr))
ipv6_dev_ac_dec(ifp->idev->dev, &addr); ipv6_dev_ac_dec(ifp->idev->dev, &addr);
} }
if (!ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 1)) dst_hold(&ifp->rt->u.dst);
ip6_rt_addr_del(&ifp->addr, ifp->idev->dev); if (ip6_del_rt(ifp->rt, NULL, NULL))
dst_free(&ifp->rt->u.dst);
else
dst_release(&ifp->rt->u.dst);
break; break;
} }
} }
......
...@@ -293,6 +293,7 @@ static void aca_put(struct ifacaddr6 *ac) ...@@ -293,6 +293,7 @@ static void aca_put(struct ifacaddr6 *ac)
{ {
if (atomic_dec_and_test(&ac->aca_refcnt)) { if (atomic_dec_and_test(&ac->aca_refcnt)) {
in6_dev_put(ac->aca_idev); in6_dev_put(ac->aca_idev);
dst_release(&ac->aca_rt->u.dst);
kfree(ac); kfree(ac);
} }
} }
...@@ -304,6 +305,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) ...@@ -304,6 +305,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
{ {
struct ifacaddr6 *aca; struct ifacaddr6 *aca;
struct inet6_dev *idev; struct inet6_dev *idev;
struct rt6_info *rt;
int err;
idev = in6_dev_get(dev); idev = in6_dev_get(dev);
...@@ -312,17 +315,15 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) ...@@ -312,17 +315,15 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
write_lock_bh(&idev->lock); write_lock_bh(&idev->lock);
if (idev->dead) { if (idev->dead) {
write_unlock_bh(&idev->lock); err = -ENODEV;
in6_dev_put(idev); goto out;
return -ENODEV;
} }
for (aca = idev->ac_list; aca; aca = aca->aca_next) { for (aca = idev->ac_list; aca; aca = aca->aca_next) {
if (ipv6_addr_cmp(&aca->aca_addr, addr) == 0) { if (ipv6_addr_cmp(&aca->aca_addr, addr) == 0) {
aca->aca_users++; aca->aca_users++;
write_unlock_bh(&idev->lock); err = 0;
in6_dev_put(idev); goto out;
return 0;
} }
} }
...@@ -333,15 +334,22 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) ...@@ -333,15 +334,22 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
aca = kmalloc(sizeof(struct ifacaddr6), GFP_ATOMIC); aca = kmalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);
if (aca == NULL) { if (aca == NULL) {
write_unlock_bh(&idev->lock); err = -ENOMEM;
in6_dev_put(idev); goto out;
return -ENOMEM; }
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)); memset(aca, 0, sizeof(struct ifacaddr6));
ipv6_addr_copy(&aca->aca_addr, addr); ipv6_addr_copy(&aca->aca_addr, addr);
aca->aca_idev = idev; aca->aca_idev = idev;
aca->aca_rt = rt;
aca->aca_users = 1; aca->aca_users = 1;
/* aca_tstamp should be updated upon changes */ /* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies; aca->aca_cstamp = aca->aca_tstamp = jiffies;
...@@ -352,12 +360,18 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) ...@@ -352,12 +360,18 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)
idev->ac_list = aca; idev->ac_list = aca;
write_unlock_bh(&idev->lock); 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); addrconf_join_solict(dev, &aca->aca_addr);
aca_put(aca); aca_put(aca);
return 0; 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) ...@@ -396,7 +410,11 @@ int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
write_unlock_bh(&idev->lock); write_unlock_bh(&idev->lock);
addrconf_leave_solict(dev, &aca->aca_addr); 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); aca_put(aca);
in6_dev_put(idev); in6_dev_put(idev);
......
...@@ -1376,57 +1376,9 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, ...@@ -1376,57 +1376,9 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
ipv6_addr_copy(&rt->rt6i_dst.addr, addr); ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128; rt->rt6i_dst.plen = 128;
return rt; atomic_set(&rt->u.dst.__refcnt, 1);
}
/*
* 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);
}
return err; return rt;
} }
static int fib6_ifdown(struct rt6_info *rt, void *arg) 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