Tidy up in6_select*
This change tidies up in6_select* functions, especially selectroute. selectroute is annoying because: - It returns both/either of a rtentry and/or an ifp - Yes, it may return only an ifp! - It is valid but selectroute shouldn't handle the case - Such conditional behavior makes it difficult to apply locking/psref thingy - It may return a rtentry even if error - It may use opt->ip6po_nextroute rtcache implicitly - The caller can know if it is used by rtcache_validate(&opt->ip6po_nextroute) but it's racy in MP-safe world - Even if it uses opt->ip6po_nextroute, it may return a rtentry that isn't derived from the rtcache The change includes: - Rename selectroute to in6_selectroute - Let a remaining caller of selectroute, in6_selectif, use in6_selectroute instead - Let in6_selectroute return only an rtentry - If error, it doesn't return an rtentry - A caller gets an ifp from a returned rtentry - Allow in6_selectroute to modify a passed rtcache and a caller can know if opt->ip6po_nextroute is used via the rtcache - Let callers (ip6_output and in6_selectif) handle the case that only an ifp is required Inspired by OpenBSD Proposed on tech-kern and tech-net LGTM by roy@
This commit is contained in:
parent
e956baf9d4
commit
d0432711b6
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: in6_src.c,v 1.73 2016/10/31 04:57:10 ozaki-r Exp $ */
|
||||
/* $NetBSD: in6_src.c,v 1.74 2016/11/10 04:13:53 ozaki-r Exp $ */
|
||||
/* $KAME: in6_src.c,v 1.159 2005/10/19 01:40:32 t-momose Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -66,7 +66,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.73 2016/10/31 04:57:10 ozaki-r Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.74 2016/11/10 04:13:53 ozaki-r Exp $");
|
||||
|
||||
#ifdef _KERNEL_OPT
|
||||
#include "opt_inet.h"
|
||||
|
@ -122,9 +122,6 @@ struct in6_addrpolicy defaultaddrpolicy;
|
|||
|
||||
int ip6_prefer_tempaddr = 0;
|
||||
|
||||
static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
|
||||
struct ip6_moptions *, struct route *, struct ifnet **, struct psref *,
|
||||
struct rtentry **, int, int);
|
||||
static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
|
||||
struct ip6_moptions *, struct route *, struct ifnet **, struct psref *);
|
||||
|
||||
|
@ -590,28 +587,20 @@ exit:
|
|||
#undef PSREF
|
||||
}
|
||||
|
||||
static int
|
||||
selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
||||
struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp,
|
||||
struct psref *psref, struct rtentry **retrt, int clone, int norouteok)
|
||||
int
|
||||
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
||||
struct route **ro, struct rtentry **retrt, bool count_discard)
|
||||
{
|
||||
int error = 0;
|
||||
struct ifnet *ifp = NULL;
|
||||
struct rtentry *rt = NULL;
|
||||
struct sockaddr_in6 *sin6_next;
|
||||
struct in6_pktinfo *pi = NULL;
|
||||
struct in6_addr *dst;
|
||||
union {
|
||||
struct sockaddr dst;
|
||||
struct sockaddr_in6 dst6;
|
||||
} u;
|
||||
|
||||
KASSERT(ro != NULL);
|
||||
KASSERT(retifp != NULL && psref != NULL);
|
||||
KASSERT(retrt != NULL);
|
||||
|
||||
dst = &dstsock->sin6_addr;
|
||||
|
||||
#if 0
|
||||
if (dstsock->sin6_addr.s6_addr32[0] == 0 &&
|
||||
dstsock->sin6_addr.s6_addr32[1] == 0 &&
|
||||
|
@ -625,41 +614,13 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* If the caller specify the outgoing interface explicitly, use it. */
|
||||
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
|
||||
/* XXX boundary check is assumed to be already done. */
|
||||
ifp = if_get_byindex(pi->ipi6_ifindex, psref);
|
||||
if (ifp != NULL &&
|
||||
(norouteok || IN6_IS_ADDR_MULTICAST(dst))) {
|
||||
/*
|
||||
* we do not have to check or get the route for
|
||||
* multicast.
|
||||
*/
|
||||
goto done;
|
||||
} else {
|
||||
if_put(ifp, psref);
|
||||
ifp = NULL;
|
||||
goto getroute;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the destination address is a multicast address and the outgoing
|
||||
* interface for the address is specified by the caller, use it.
|
||||
*/
|
||||
if (IN6_IS_ADDR_MULTICAST(dst) && mopts != NULL) {
|
||||
ifp = if_get_byindex(mopts->im6o_multicast_if_index, psref);
|
||||
if (ifp != NULL)
|
||||
goto done; /* we do not need a route for multicast. */
|
||||
}
|
||||
|
||||
getroute:
|
||||
/*
|
||||
* If the next hop address for the packet is specified by the caller,
|
||||
* use it as the gateway.
|
||||
*/
|
||||
if (opts && opts->ip6po_nexthop) {
|
||||
struct route *ron;
|
||||
struct sockaddr_in6 *sin6_next;
|
||||
|
||||
sin6_next = satosin6(opts->ip6po_nexthop);
|
||||
|
||||
|
@ -674,28 +635,19 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
* by that address must be a neighbor of the sending host.
|
||||
*/
|
||||
ron = &opts->ip6po_nextroute;
|
||||
if ((rt = rtcache_lookup(ron, sin6tosa(sin6_next))) == NULL ||
|
||||
(rt->rt_flags & RTF_GATEWAY) != 0 ||
|
||||
rt = rtcache_lookup(ron, sin6tosa(sin6_next));
|
||||
if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) != 0 ||
|
||||
!nd6_is_addr_neighbor(sin6_next, rt->rt_ifp)) {
|
||||
rtcache_free(ron);
|
||||
if (rt != NULL && count_discard)
|
||||
in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
|
||||
rt = NULL;
|
||||
error = EHOSTUNREACH;
|
||||
goto done;
|
||||
}
|
||||
ifp = rt->rt_ifp;
|
||||
if (ifp != NULL) {
|
||||
if (!if_is_deactivated(ifp))
|
||||
if_acquire_NOMPSAFE(ifp, psref);
|
||||
else
|
||||
ifp = NULL;
|
||||
}
|
||||
*ro = ron;
|
||||
|
||||
/*
|
||||
* When cloning is required, try to allocate a route to the
|
||||
* destination so that the caller can store path MTU
|
||||
* information.
|
||||
*/
|
||||
if (!clone)
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -705,27 +657,10 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
*/
|
||||
u.dst6 = *dstsock;
|
||||
u.dst6.sin6_scope_id = 0;
|
||||
rt = rtcache_lookup1(ro, &u.dst, clone);
|
||||
|
||||
/*
|
||||
* do not care about the result if we have the nexthop
|
||||
* explicitly specified.
|
||||
*/
|
||||
if (opts && opts->ip6po_nexthop)
|
||||
goto done;
|
||||
rt = rtcache_lookup1(*ro, &u.dst, 1);
|
||||
|
||||
if (rt == NULL)
|
||||
error = EHOSTUNREACH;
|
||||
else {
|
||||
if_put(ifp, psref);
|
||||
ifp = rt->rt_ifp;
|
||||
if (ifp != NULL) {
|
||||
if (!if_is_deactivated(ifp))
|
||||
if_acquire_NOMPSAFE(ifp, psref);
|
||||
else
|
||||
ifp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the outgoing interface conflicts with
|
||||
|
@ -735,28 +670,20 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
* our own addresses.)
|
||||
*/
|
||||
if (opts && opts->ip6po_pktinfo && opts->ip6po_pktinfo->ipi6_ifindex) {
|
||||
if (!(ifp->if_flags & IFF_LOOPBACK) &&
|
||||
ifp->if_index != opts->ip6po_pktinfo->ipi6_ifindex) {
|
||||
if (!(rt->rt_ifp->if_flags & IFF_LOOPBACK) &&
|
||||
rt->rt_ifp->if_index != opts->ip6po_pktinfo->ipi6_ifindex) {
|
||||
if (rt != NULL && count_discard)
|
||||
in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
|
||||
error = EHOSTUNREACH;
|
||||
goto done;
|
||||
rt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (ifp == NULL && rt == NULL) {
|
||||
/*
|
||||
* This can happen if the caller did not pass a cached route
|
||||
* nor any other hints. We treat this case an error.
|
||||
*/
|
||||
error = EHOSTUNREACH;
|
||||
}
|
||||
done:
|
||||
if (error == EHOSTUNREACH)
|
||||
IP6_STATINC(IP6_STAT_NOROUTE);
|
||||
|
||||
*retifp = ifp;
|
||||
*retrt = rt; /* rt may be NULL */
|
||||
|
||||
return (error);
|
||||
*retrt = rt;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -764,18 +691,41 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp,
|
||||
struct psref *psref)
|
||||
{
|
||||
int error, clone;
|
||||
int error;
|
||||
struct rtentry *rt = NULL;
|
||||
struct in6_addr *dst;
|
||||
struct in6_pktinfo *pi = NULL;
|
||||
|
||||
KASSERT(retifp != NULL);
|
||||
*retifp = NULL;
|
||||
dst = &dstsock->sin6_addr;
|
||||
|
||||
clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
|
||||
if ((error = selectroute(dstsock, opts, mopts, ro, retifp, psref,
|
||||
&rt, clone, 1)) != 0) {
|
||||
return (error);
|
||||
/* If the caller specify the outgoing interface explicitly, use it. */
|
||||
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
|
||||
/* XXX boundary check is assumed to be already done. */
|
||||
*retifp = if_get_byindex(pi->ipi6_ifindex, psref);
|
||||
if (*retifp != NULL)
|
||||
return 0;
|
||||
goto getroute;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the destination address is a multicast address and the outgoing
|
||||
* interface for the address is specified by the caller, use it.
|
||||
*/
|
||||
if (IN6_IS_ADDR_MULTICAST(dst) && mopts != NULL) {
|
||||
*retifp = if_get_byindex(mopts->im6o_multicast_if_index, psref);
|
||||
if (*retifp != NULL)
|
||||
return 0; /* we do not need a route for multicast. */
|
||||
}
|
||||
|
||||
getroute:
|
||||
error = in6_selectroute(dstsock, opts, &ro, &rt, false);
|
||||
if (error != 0)
|
||||
return error;
|
||||
|
||||
*retifp = if_get_byindex(rt->rt_ifp->if_index, psref);
|
||||
|
||||
/*
|
||||
* do not use a rejected or black hole route.
|
||||
* XXX: this check should be done in the L2 output routine.
|
||||
|
@ -793,7 +743,7 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
* Although this may not be very harmful, it should still be confusing.
|
||||
* We thus reject the case here.
|
||||
*/
|
||||
if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE)))
|
||||
if ((rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE)))
|
||||
return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
|
||||
|
||||
/*
|
||||
|
@ -803,7 +753,7 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
* destination address (which should probably be one of our own
|
||||
* addresses.)
|
||||
*/
|
||||
if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp &&
|
||||
if (rt->rt_ifa && rt->rt_ifa->ifa_ifp &&
|
||||
rt->rt_ifa->ifa_ifp != *retifp &&
|
||||
!if_is_deactivated(rt->rt_ifa->ifa_ifp)) {
|
||||
if_put(*retifp, psref);
|
||||
|
@ -814,19 +764,6 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
|||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* close - meaningful only for bsdi and freebsd.
|
||||
*/
|
||||
|
||||
int
|
||||
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
|
||||
struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp,
|
||||
struct psref *psref, struct rtentry **retrt, int clone)
|
||||
{
|
||||
return selectroute(dstsock, opts, mopts, ro, retifp, psref,
|
||||
retrt, clone, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Default hop limit selection. The precedence is as follows:
|
||||
* 1. Hoplimit value specified via ioctl.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ip6_output.c,v 1.177 2016/11/07 01:55:17 ozaki-r Exp $ */
|
||||
/* $NetBSD: ip6_output.c,v 1.178 2016/11/10 04:13:53 ozaki-r Exp $ */
|
||||
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -62,7 +62,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.177 2016/11/07 01:55:17 ozaki-r Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.178 2016/11/10 04:13:53 ozaki-r Exp $");
|
||||
|
||||
#ifdef _KERNEL_OPT
|
||||
#include "opt_inet.h"
|
||||
|
@ -408,6 +408,9 @@ ip6_output(
|
|||
sizeof(struct ip6_hdr) + optlen);
|
||||
}
|
||||
|
||||
/* Need to save for pmtu */
|
||||
finaldst = ip6->ip6_dst;
|
||||
|
||||
/*
|
||||
* If there is a routing header, replace destination address field
|
||||
* with the first hop of the routing header.
|
||||
|
@ -417,7 +420,6 @@ ip6_output(
|
|||
|
||||
rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
|
||||
struct ip6_rthdr *));
|
||||
finaldst = ip6->ip6_dst; /* need to save for pmtu */
|
||||
|
||||
error = ip6_handle_rthdr(rh, ip6);
|
||||
if (error != 0)
|
||||
|
@ -499,12 +501,31 @@ ip6_output(
|
|||
ip6 = mtod(m, struct ip6_hdr *);
|
||||
|
||||
sockaddr_in6_init(&dst_sa, &ip6->ip6_dst, 0, 0, 0);
|
||||
if ((error = in6_selectroute(&dst_sa, opt, im6o, ro,
|
||||
&ifp, &psref, &rt, 0)) != 0) {
|
||||
if (ifp != NULL)
|
||||
in6_ifstat_inc(ifp, ifs6_out_discard);
|
||||
goto bad;
|
||||
|
||||
/* We do not need a route for multicast */
|
||||
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
||||
struct in6_pktinfo *pi = NULL;
|
||||
|
||||
/*
|
||||
* If the outgoing interface for the address is specified by
|
||||
* the caller, use it.
|
||||
*/
|
||||
if (opt && (pi = opt->ip6po_pktinfo) != NULL) {
|
||||
/* XXX boundary check is assumed to be already done. */
|
||||
ifp = if_get_byindex(pi->ipi6_ifindex, &psref);
|
||||
} else if (im6o != NULL) {
|
||||
ifp = if_get_byindex(im6o->im6o_multicast_if_index,
|
||||
&psref);
|
||||
}
|
||||
}
|
||||
|
||||
if (ifp == NULL) {
|
||||
error = in6_selectroute(&dst_sa, opt, &ro, &rt, true);
|
||||
if (error != 0)
|
||||
goto bad;
|
||||
ifp = if_get_byindex(rt->rt_ifp->if_index, &psref);
|
||||
}
|
||||
|
||||
if (rt == NULL) {
|
||||
/*
|
||||
* If in6_selectroute() does not return a route entry,
|
||||
|
@ -578,18 +599,9 @@ ip6_output(
|
|||
goto bad;
|
||||
}
|
||||
|
||||
if (rt == NULL || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
||||
dst = satocsin6(rtcache_getdst(ro));
|
||||
KASSERT(dst != NULL);
|
||||
} else if (opt && rtcache_validate(&opt->ip6po_nextroute) != NULL) {
|
||||
/*
|
||||
* The nexthop is explicitly specified by the
|
||||
* application. We assume the next hop is an IPv6
|
||||
* address.
|
||||
*/
|
||||
dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
|
||||
} else if ((rt->rt_flags & RTF_GATEWAY))
|
||||
dst = (struct sockaddr_in6 *)rt->rt_gateway;
|
||||
if (rt != NULL && (rt->rt_flags & RTF_GATEWAY) &&
|
||||
!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
|
||||
dst = satocsin6(rt->rt_gateway);
|
||||
else
|
||||
dst = satocsin6(rtcache_getdst(ro));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ip6_var.h,v 1.69 2016/10/31 04:16:25 ozaki-r Exp $ */
|
||||
/* $NetBSD: ip6_var.h,v 1.70 2016/11/10 04:13:53 ozaki-r Exp $ */
|
||||
/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -400,8 +400,7 @@ int in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *,
|
|||
struct ip6_moptions *, struct route *, struct in6_addr *,
|
||||
struct ifnet **, struct psref *, struct in6_addr *);
|
||||
int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
|
||||
struct ip6_moptions *, struct route *, struct ifnet **,
|
||||
struct psref *, struct rtentry **, int);
|
||||
struct route **, struct rtentry **, bool);
|
||||
int ip6_get_membership(const struct sockopt *, struct ifnet **, void *,
|
||||
size_t);
|
||||
|
||||
|
|
Loading…
Reference in New Issue