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