Commit effef85f authored by David S. Miller's avatar David S. Miller

Merge nuts.davemloft.net:/disk1/BK/ip6route-2.6

into nuts.davemloft.net:/disk1/BK/net-2.6
parents 44716493 6dad59bb
......@@ -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;
......
......@@ -42,6 +42,9 @@ extern int ipv6_route_ioctl(unsigned int cmd, void __user *arg);
extern int ip6_route_add(struct in6_rtmsg *rtmsg,
struct nlmsghdr *,
void *rtattr);
extern int ip6_ins_rt(struct rt6_info *,
struct nlmsghdr *,
void *rtattr);
extern int ip6_del_rt(struct rt6_info *,
struct nlmsghdr *,
void *rtattr);
......@@ -71,6 +74,10 @@ extern struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
extern int ndisc_dst_gc(int *more);
extern void fib6_force_start_gc(void);
extern struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
const struct in6_addr *addr,
int anycast);
/*
* support functions for ND
*
......
......@@ -1342,9 +1342,12 @@ static void ipv4_dst_ifdown(struct dst_entry *dst, int how)
{
struct rtable *rt = (struct rtable *) dst;
struct in_device *idev = rt->idev;
if (idev) {
rt->idev = NULL;
in_dev_put(idev);
if (idev && idev->dev != &loopback_dev) {
struct in_device *loopback_idev = in_dev_get(&loopback_dev);
if (loopback_idev) {
rt->idev = loopback_idev;
in_dev_put(idev);
}
}
}
......
......@@ -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;
}
......@@ -1457,8 +1474,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
spin_unlock(&ifp->lock);
if (!(flags&IFA_F_TENTATIVE))
ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ?
0 : RTM_NEWADDR, ifp);
ipv6_ifa_notify(0, ifp);
} else
spin_unlock(&ifp->lock);
......@@ -2982,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);
......@@ -2993,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);
......
......@@ -449,9 +449,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
* Same priority level
*/
if ((iter->rt6i_dev == rt->rt6i_dev) &&
(ipv6_addr_cmp(&iter->rt6i_gateway,
&rt->rt6i_gateway) == 0)) {
if (iter->rt6i_dev == rt->rt6i_dev &&
iter->rt6i_idev == rt->rt6i_idev &&
ipv6_addr_cmp(&iter->rt6i_gateway,
&rt->rt6i_gateway) == 0) {
if (!(iter->rt6i_flags&RTF_EXPIRES))
return -EEXIST;
iter->rt6i_expires = rt->rt6i_expires;
......
......@@ -155,7 +155,16 @@ static void ip6_dst_destroy(struct dst_entry *dst)
static void ip6_dst_ifdown(struct dst_entry *dst, int how)
{
ip6_dst_destroy(dst);
struct rt6_info *rt = (struct rt6_info *)dst;
struct inet6_dev *idev = rt->rt6i_idev;
if (idev != NULL && idev->dev != &loopback_dev) {
struct inet6_dev *loopback_idev = in6_dev_get(&loopback_dev);
if (loopback_idev != NULL) {
rt->rt6i_idev = loopback_idev;
in6_dev_put(idev);
}
}
}
/*
......@@ -174,8 +183,16 @@ static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
struct net_device *dev = sprt->rt6i_dev;
if (dev->ifindex == oif)
return sprt;
if (dev->flags&IFF_LOOPBACK)
if (dev->flags & IFF_LOOPBACK) {
if (sprt->rt6i_idev->dev->ifindex != oif) {
if (strict && oif)
continue;
if (local && (!oif ||
local->rt6i_idev->dev->ifindex == oif))
continue;
}
local = sprt;
}
}
if (local)
......@@ -336,13 +353,13 @@ struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
return NULL;
}
/* rt6_ins is called with FREE rt6_lock.
/* ip6_ins_rt is called with FREE rt6_lock.
It takes new route entry, the addition fails by any reason the
route is freed. In any case, if caller does not hold it, it may
be destroyed.
*/
static int rt6_ins(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr)
int ip6_ins_rt(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr)
{
int err;
......@@ -390,7 +407,7 @@ static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
dst_hold(&rt->u.dst);
err = rt6_ins(rt, NULL, NULL);
err = ip6_ins_rt(rt, NULL, NULL);
if (err == 0)
return rt;
......@@ -608,8 +625,13 @@ struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
struct in6_addr *addr,
int (*output)(struct sk_buff **))
{
struct rt6_info *rt = ip6_dst_alloc();
struct rt6_info *rt;
struct inet6_dev *idev = in6_dev_get(dev);
if (unlikely(idev == NULL))
return NULL;
rt = ip6_dst_alloc();
if (unlikely(rt == NULL))
goto out;
......@@ -620,7 +642,7 @@ struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
neigh = ndisc_get_neigh(dev, addr);
rt->rt6i_dev = dev;
rt->rt6i_idev = in6_dev_get(dev);
rt->rt6i_idev = idev;
rt->rt6i_nexthop = neigh;
atomic_set(&rt->u.dst.__refcnt, 1);
rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
......@@ -731,8 +753,9 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
int err;
struct rtmsg *r;
struct rtattr **rta;
struct rt6_info *rt;
struct rt6_info *rt = NULL;
struct net_device *dev = NULL;
struct inet6_dev *idev = NULL;
int addr_type;
rta = (struct rtattr **) _rtattr;
......@@ -744,9 +767,13 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
return -EINVAL;
#endif
if (rtmsg->rtmsg_ifindex) {
err = -ENODEV;
dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
if (!dev)
return -ENODEV;
goto out;
idev = in6_dev_get(dev);
if (!idev)
goto out;
}
if (rtmsg->rtmsg_metric == 0)
......@@ -793,10 +820,17 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
*/
if ((rtmsg->rtmsg_flags&RTF_REJECT) ||
(dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
if (dev)
if (dev && dev != &loopback_dev) {
dev_put(dev);
dev = &loopback_dev;
dev_hold(dev);
in6_dev_put(idev);
dev = &loopback_dev;
dev_hold(dev);
idev = in6_dev_get(dev);
if (!idev) {
err = -ENODEV;
goto out;
}
}
rt->u.dst.output = ip6_pkt_discard_out;
rt->u.dst.input = ip6_pkt_discard;
rt->u.dst.error = -ENETUNREACH;
......@@ -838,7 +872,9 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
}
} else {
dev = grt->rt6i_dev;
idev = grt->rt6i_idev;
dev_hold(dev);
in6_dev_hold(grt->rt6i_idev);
}
if (!(grt->rt6i_flags&RTF_GATEWAY))
err = 0;
......@@ -900,8 +936,8 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
if (!rt->u.dst.metrics[RTAX_ADVMSS-1])
rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_pmtu(&rt->u.dst));
rt->u.dst.dev = dev;
rt->rt6i_idev = in6_dev_get(dev);
return rt6_ins(rt, nlh, _rtattr);
rt->rt6i_idev = idev;
return ip6_ins_rt(rt, nlh, _rtattr);
out:
if (dev)
......@@ -1054,7 +1090,7 @@ void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev);
nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_pmtu(&nrt->u.dst));
if (rt6_ins(nrt, NULL, NULL))
if (ip6_ins_rt(nrt, NULL, NULL))
goto out;
if (rt->rt6i_flags&RTF_CACHE) {
......@@ -1144,7 +1180,7 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
rt6_ins(nrt, NULL, NULL);
ip6_ins_rt(nrt, NULL, NULL);
}
out:
......@@ -1303,23 +1339,26 @@ int ip6_pkt_discard_out(struct sk_buff **pskb)
}
/*
* Add address
* Allocate a dst for local (unicast / anycast) address.
*/
int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev, int anycast)
struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
const struct in6_addr *addr,
int anycast)
{
struct rt6_info *rt = ip6_dst_alloc();
if (rt == NULL)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
dev_hold(&loopback_dev);
in6_dev_hold(idev);
rt->u.dst.flags = DST_HOST;
rt->u.dst.input = ip6_input;
rt->u.dst.output = ip6_output;
rt->rt6i_dev = &loopback_dev;
rt->rt6i_idev = in6_dev_get(&loopback_dev);
rt->rt6i_idev = idev;
rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_pmtu(&rt->u.dst));
rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ipv6_get_hoplimit(rt->rt6i_dev);
......@@ -1331,34 +1370,15 @@ int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev, int anycast)
rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
if (rt->rt6i_nexthop == NULL) {
dst_free((struct dst_entry *) rt);
return -ENOMEM;
return ERR_PTR(-ENOMEM);
}
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
rt6_ins(rt, NULL, NULL);
return 0;
}
/* 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