diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-01-08 14:29:19 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-01-08 14:29:19 +0000 |
commit | 6571bb75613e41eb1a6ea1f65cc04b539cb7159e (patch) | |
tree | 18641ce38c4950cbd3a5f04bb0410fc04e8bd094 /sys/net/route.c | |
parent | 4d3e239b7078bc404f29d5f8d6a584a1e6716f20 (diff) |
Factorize various duplicated chunks of (old and horrible) code, checking
for the validity of a given outgoing route entry into a single function.
This change is inspired from FreeBSD r111767. The function introduced
here, rt_checkgate(), should hopefully die in a near future. Why should
it die? Well, it is way too late to do such validity checks: if your
kernel can ends up in ether_output() with an invalid route, please do
not let it try to find a new one that might do the job.
Go read this function if you're wondering why you're getting messages
like:
"arpresolve: X.X.X.X: route without link local address"
Since this horrible logic has survived 20 years of copy & past and small
modifications for workarounds without a single clear commit message, let's
assume it is full of dragons and try to play safe. This factorization is
not intended to change any behavior.
With much inputs from bluhm@, tested by weerd@ and florian@ on setups
with p2p IPv6 interfaces.
ok bluhm@, benno@, florian@
Diffstat (limited to 'sys/net/route.c')
-rw-r--r-- | sys/net/route.c | 41 |
1 files changed, 40 insertions, 1 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index 05f56763c50..427656a13c7 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.196 2014/12/29 11:53:58 mpi Exp $ */ +/* $OpenBSD: route.c,v 1.197 2015/01/08 14:29:18 mpi Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -1032,6 +1032,45 @@ rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate, return (0); } +int +rt_checkgate(struct ifnet *ifp, struct rtentry *rt, struct sockaddr *dst, + unsigned int rtableid, struct rtentry **rtp) +{ + struct rtentry *rt0; + + KASSERT(rt != NULL); + + if ((rt->rt_flags & RTF_UP) == 0) { + rt = rtalloc(dst, RT_REPORT|RT_RESOLVE, rtableid); + rt->rt_refcnt--; + if (rt == NULL || rt->rt_ifp != ifp) + return (EHOSTUNREACH); + } + + rt0 = rt; + + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_gwroute && !(rt->rt_gwroute->rt_flags & RTF_UP)) { + rtfree(rt->rt_gwroute); + rt->rt_gwroute = NULL; + } + if (rt->rt_gwroute == NULL) { + rt->rt_gwroute = rtalloc(rt->rt_gateway, + RT_REPORT|RT_RESOLVE, rtableid); + if (rt->rt_gwroute == NULL) + return (EHOSTUNREACH); + } + rt = rt->rt_gwroute; + } + + if (rt->rt_flags & RTF_REJECT) + if (rt->rt_expire == 0 || time_second < rt->rt_expire) + return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + + *rtp = rt; + return (0); +} + void rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) |