Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6_src.c
Show First 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/route/nhop.h> | |||||
#include <net/if_llatbl.h> | #include <net/if_llatbl.h> | ||||
#ifdef RADIX_MPATH | #ifdef RADIX_MPATH | ||||
#include <net/radix_mpath.h> | #include <net/radix_mpath.h> | ||||
#endif | #endif | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||||
Show All 27 Lines | |||||
#define ADDR_LABEL_NOTAPP (-1) | #define ADDR_LABEL_NOTAPP (-1) | ||||
VNET_DEFINE_STATIC(struct in6_addrpolicy, defaultaddrpolicy); | VNET_DEFINE_STATIC(struct in6_addrpolicy, defaultaddrpolicy); | ||||
#define V_defaultaddrpolicy VNET(defaultaddrpolicy) | #define V_defaultaddrpolicy VNET(defaultaddrpolicy) | ||||
VNET_DEFINE(int, ip6_prefer_tempaddr) = 0; | VNET_DEFINE(int, ip6_prefer_tempaddr) = 0; | ||||
static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *, | static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *, | ||||
struct ip6_moptions *, struct route_in6 *, struct ifnet **, | struct ip6_moptions *, struct route_in6 *, struct ifnet **, | ||||
struct rtentry **, int, u_int); | struct nhop_object **, int, u_int, uint32_t); | ||||
static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *, | static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *, | ||||
struct ip6_moptions *, struct ifnet **, | struct ip6_moptions *, struct ifnet **, | ||||
struct ifnet *, u_int); | struct ifnet *, u_int); | ||||
static int in6_selectsrc(uint32_t, struct sockaddr_in6 *, | static int in6_selectsrc(uint32_t, struct sockaddr_in6 *, | ||||
struct ip6_pktopts *, struct inpcb *, struct ucred *, | struct ip6_pktopts *, struct inpcb *, struct ucred *, | ||||
struct ifnet **, struct in6_addr *); | struct ifnet **, struct in6_addr *); | ||||
static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *); | static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *); | ||||
▲ Show 20 Lines • Show All 474 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* clone - meaningful only for bsdi and freebsd | * clone - meaningful only for bsdi and freebsd | ||||
*/ | */ | ||||
static int | static int | ||||
selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | ||||
struct ip6_moptions *mopts, struct route_in6 *ro, | struct ip6_moptions *mopts, struct route_in6 *ro, | ||||
struct ifnet **retifp, struct rtentry **retrt, int norouteok, u_int fibnum) | struct ifnet **retifp, struct nhop_object **retnh, int norouteok, | ||||
u_int fibnum, uint32_t flowid) | |||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
struct ifnet *ifp = NULL; | struct ifnet *ifp = NULL; | ||||
struct rtentry *rt = NULL; | struct nhop_object *nh = NULL; | ||||
struct sockaddr_in6 *sin6_next; | struct sockaddr_in6 *sin6_next; | ||||
struct in6_pktinfo *pi = NULL; | struct in6_pktinfo *pi = NULL; | ||||
struct in6_addr *dst = &dstsock->sin6_addr; | struct in6_addr *dst = &dstsock->sin6_addr; | ||||
uint32_t zoneid; | uint32_t zoneid; | ||||
#if 0 | #if 0 | ||||
char ip6buf[INET6_ADDRSTRLEN]; | char ip6buf[INET6_ADDRSTRLEN]; | ||||
if (dstsock->sin6_addr.s6_addr32[0] == 0 && | if (dstsock->sin6_addr.s6_addr32[0] == 0 && | ||||
dstsock->sin6_addr.s6_addr32[1] == 0 && | dstsock->sin6_addr.s6_addr32[1] == 0 && | ||||
!IN6_IS_ADDR_LOOPBACK(&dstsock->sin6_addr)) { | !IN6_IS_ADDR_LOOPBACK(&dstsock->sin6_addr)) { | ||||
printf("%s: strange destination %s\n", __func__, | printf("%s: strange destination %s\n", __func__, | ||||
ip6_sprintf(ip6buf, &dstsock->sin6_addr)); | ip6_sprintf(ip6buf, &dstsock->sin6_addr)); | ||||
} else { | } else { | ||||
printf("%s: destination = %s%%%d\n", __func__, | printf("%s: destination = %s%%%d\n", __func__, | ||||
ip6_sprintf(ip6buf, &dstsock->sin6_addr), | ip6_sprintf(ip6buf, &dstsock->sin6_addr), | ||||
dstsock->sin6_scope_id); /* for debug */ | dstsock->sin6_scope_id); /* for debug */ | ||||
} | } | ||||
#endif | #endif | ||||
/* If the caller specify the outgoing interface explicitly, use it. */ | /* If the caller specify the outgoing interface explicitly, use it. */ | ||||
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) { | if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) { | ||||
/* XXX boundary check is assumed to be already done. */ | /* XXX boundary check is assumed to be already done. */ | ||||
ifp = ifnet_byindex(pi->ipi6_ifindex); | ifp = ifnet_byindex(pi->ipi6_ifindex); | ||||
if (ifp != NULL && | if (ifp != NULL && | ||||
(norouteok || retrt == NULL || | (norouteok || retnh == NULL || | ||||
IN6_IS_ADDR_MULTICAST(dst))) { | IN6_IS_ADDR_MULTICAST(dst))) { | ||||
/* | /* | ||||
* we do not have to check or get the route for | * we do not have to check or get the route for | ||||
* multicast. | * multicast. | ||||
*/ | */ | ||||
goto done; | goto done; | ||||
} else | } else | ||||
goto getroute; | goto getroute; | ||||
Show All 36 Lines | if (IN6_IS_ADDR_LINKLOCAL(&sin6_next->sin6_addr)) { | ||||
zoneid = ntohs(in6_getscope(&sin6_next->sin6_addr)); | zoneid = ntohs(in6_getscope(&sin6_next->sin6_addr)); | ||||
if (zoneid > 0) { | if (zoneid > 0) { | ||||
ifp = in6_getlinkifnet(zoneid); | ifp = in6_getlinkifnet(zoneid); | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
ron = &opts->ip6po_nextroute; | ron = &opts->ip6po_nextroute; | ||||
/* Use a cached route if it exists and is valid. */ | /* Use a cached route if it exists and is valid. */ | ||||
if (ron->ro_rt != NULL && ( | if (ron->ro_nh != NULL && ( | ||||
(ron->ro_rt->rt_flags & RTF_UP) == 0 || | !NH_IS_VALID(ron->ro_nh) || | ||||
ron->ro_dst.sin6_family != AF_INET6 || | ron->ro_dst.sin6_family != AF_INET6 || | ||||
!IN6_ARE_ADDR_EQUAL(&ron->ro_dst.sin6_addr, | !IN6_ARE_ADDR_EQUAL(&ron->ro_dst.sin6_addr, | ||||
&sin6_next->sin6_addr))) | &sin6_next->sin6_addr))) | ||||
RO_RTFREE(ron); | RO_NHFREE(ron); | ||||
if (ron->ro_rt == NULL) { | if (ron->ro_nh == NULL) { | ||||
ron->ro_dst = *sin6_next; | ron->ro_dst = *sin6_next; | ||||
in6_rtalloc(ron, fibnum); /* multi path case? */ | /* | ||||
* sin6_next is not link-local OR scopeid is 0, | |||||
* no need to clear scope | |||||
*/ | |||||
ron->ro_nh = fib6_lookup(fibnum, | |||||
&sin6_next->sin6_addr, 0, NHR_REF, flowid); | |||||
} | } | ||||
/* | /* | ||||
* The node identified by that address must be a | * The node identified by that address must be a | ||||
* neighbor of the sending host. | * neighbor of the sending host. | ||||
*/ | */ | ||||
if (ron->ro_rt == NULL || | if (ron->ro_nh == NULL || | ||||
(ron->ro_rt->rt_flags & RTF_GATEWAY) != 0) | (ron->ro_nh->nh_flags & NHF_GATEWAY) != 0) | ||||
error = EHOSTUNREACH; | error = EHOSTUNREACH; | ||||
else { | else { | ||||
rt = ron->ro_rt; | nh = ron->ro_nh; | ||||
ifp = rt->rt_ifp; | ifp = nh->nh_ifp; | ||||
} | } | ||||
goto done; | goto done; | ||||
} | } | ||||
/* | /* | ||||
* Use a cached route if it exists and is valid, else try to allocate | * Use a cached route if it exists and is valid, else try to allocate | ||||
* a new one. Note that we should check the address family of the | * a new one. Note that we should check the address family of the | ||||
* cached destination, in case of sharing the cache with IPv4. | * cached destination, in case of sharing the cache with IPv4. | ||||
*/ | */ | ||||
if (ro) { | if (ro) { | ||||
if (ro->ro_rt && | if (ro->ro_nh && | ||||
(!(ro->ro_rt->rt_flags & RTF_UP) || | (!NH_IS_VALID(ro->ro_nh) || | ||||
((struct sockaddr *)(&ro->ro_dst))->sa_family != AF_INET6 || | ((struct sockaddr *)(&ro->ro_dst))->sa_family != AF_INET6 || | ||||
!IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, | !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, | ||||
dst))) { | dst))) { | ||||
RTFREE(ro->ro_rt); | RO_NHFREE(ro); | ||||
ro->ro_rt = (struct rtentry *)NULL; | |||||
} | } | ||||
if (ro->ro_rt == (struct rtentry *)NULL) { | if (ro->ro_nh == (struct nhop_object *)NULL) { | ||||
struct sockaddr_in6 *sa6; | struct sockaddr_in6 *sa6; | ||||
/* No route yet, so try to acquire one */ | /* No route yet, so try to acquire one */ | ||||
bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); | bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); | ||||
sa6 = (struct sockaddr_in6 *)&ro->ro_dst; | sa6 = (struct sockaddr_in6 *)&ro->ro_dst; | ||||
*sa6 = *dstsock; | *sa6 = *dstsock; | ||||
sa6->sin6_scope_id = 0; | sa6->sin6_scope_id = 0; | ||||
/* | |||||
* Currently dst has scopeid embedded iff it is LL. | |||||
* New routing API accepts scopeid as a separate argument. | |||||
* Convert dst before/after doing lookup | |||||
*/ | |||||
uint32_t scopeid = 0; | |||||
if (IN6_IS_SCOPE_LINKLOCAL(&sa6->sin6_addr)) { | |||||
/* Unwrap in6_getscope() and in6_clearscope() */ | |||||
scopeid = ntohs(sa6->sin6_addr.s6_addr16[1]); | |||||
sa6->sin6_addr.s6_addr16[1] = 0; | |||||
} | |||||
#ifdef RADIX_MPATH | #ifdef RADIX_MPATH | ||||
rtalloc_mpath_fib((struct route *)ro, | rtalloc_mpath_fib((struct route *)ro, | ||||
ntohl(sa6->sin6_addr.s6_addr32[3]), fibnum); | ntohl(sa6->sin6_addr.s6_addr32[3]), fibnum); | ||||
#else | #else | ||||
ro->ro_rt = in6_rtalloc1((struct sockaddr *) | ro->ro_nh = fib6_lookup(fibnum, | ||||
&ro->ro_dst, 0, 0UL, fibnum); | &sa6->sin6_addr, scopeid, NHR_REF, flowid); | ||||
if (ro->ro_rt) | |||||
RT_UNLOCK(ro->ro_rt); | |||||
#endif | #endif | ||||
if (IN6_IS_SCOPE_LINKLOCAL(&sa6->sin6_addr)) | |||||
sa6->sin6_addr.s6_addr16[1] = htons(scopeid); | |||||
} | } | ||||
/* | /* | ||||
* do not care about the result if we have the nexthop | * do not care about the result if we have the nexthop | ||||
* explicitly specified. | * explicitly specified. | ||||
*/ | */ | ||||
if (opts && opts->ip6po_nexthop) | if (opts && opts->ip6po_nexthop) | ||||
goto done; | goto done; | ||||
if (ro->ro_rt) { | if (ro->ro_nh) | ||||
ifp = ro->ro_rt->rt_ifp; | ifp = ro->ro_nh->nh_ifp; | ||||
else | |||||
if (ifp == NULL) { /* can this really happen? */ | |||||
RTFREE(ro->ro_rt); | |||||
ro->ro_rt = NULL; | |||||
} | |||||
} | |||||
if (ro->ro_rt == NULL) | |||||
error = EHOSTUNREACH; | error = EHOSTUNREACH; | ||||
rt = ro->ro_rt; | nh = ro->ro_nh; | ||||
/* | /* | ||||
* Check if the outgoing interface conflicts with | * Check if the outgoing interface conflicts with | ||||
* the interface specified by ipi6_ifindex (if specified). | * the interface specified by ipi6_ifindex (if specified). | ||||
* Note that loopback interface is always okay. | * Note that loopback interface is always okay. | ||||
* (this may happen when we are sending a packet to one of | * (this may happen when we are sending a packet to one of | ||||
* our own addresses.) | * our own addresses.) | ||||
*/ | */ | ||||
if (ifp && opts && opts->ip6po_pktinfo && | if (ifp && opts && opts->ip6po_pktinfo && | ||||
opts->ip6po_pktinfo->ipi6_ifindex) { | opts->ip6po_pktinfo->ipi6_ifindex) { | ||||
if (!(ifp->if_flags & IFF_LOOPBACK) && | if (!(ifp->if_flags & IFF_LOOPBACK) && | ||||
ifp->if_index != | ifp->if_index != | ||||
opts->ip6po_pktinfo->ipi6_ifindex) { | opts->ip6po_pktinfo->ipi6_ifindex) { | ||||
error = EHOSTUNREACH; | error = EHOSTUNREACH; | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
done: | done: | ||||
if (ifp == NULL && rt == NULL) { | if (ifp == NULL && nh == NULL) { | ||||
/* | /* | ||||
* This can happen if the caller did not pass a cached route | * This can happen if the caller did not pass a cached route | ||||
* nor any other hints. We treat this case an error. | * nor any other hints. We treat this case an error. | ||||
*/ | */ | ||||
error = EHOSTUNREACH; | error = EHOSTUNREACH; | ||||
} | } | ||||
if (error == EHOSTUNREACH) | if (error == EHOSTUNREACH) | ||||
IP6STAT_INC(ip6s_noroute); | IP6STAT_INC(ip6s_noroute); | ||||
if (retifp != NULL) { | if (retifp != NULL) { | ||||
if (nh != NULL) | |||||
*retifp = nh->nh_aifp; | |||||
else | |||||
*retifp = ifp; | *retifp = ifp; | ||||
/* | |||||
* Adjust the "outgoing" interface. If we're going to loop | |||||
* the packet back to ourselves, the ifp would be the loopback | |||||
* interface. However, we'd rather know the interface associated | |||||
* to the destination address (which should probably be one of | |||||
* our own addresses.) | |||||
*/ | |||||
if (rt) { | |||||
if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) && | |||||
(rt->rt_gateway->sa_family == AF_LINK)) | |||||
*retifp = | |||||
ifnet_byindex(((struct sockaddr_dl *) | |||||
rt->rt_gateway)->sdl_index); | |||||
} | } | ||||
} | |||||
if (retrt != NULL) | if (retnh != NULL) | ||||
*retrt = rt; /* rt may be NULL */ | *retnh = nh; /* nh may be NULL */ | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | ||||
struct ip6_moptions *mopts, struct ifnet **retifp, | struct ip6_moptions *mopts, struct ifnet **retifp, | ||||
struct ifnet *oifp, u_int fibnum) | struct ifnet *oifp, u_int fibnum) | ||||
{ | { | ||||
int error; | int error; | ||||
struct route_in6 sro; | struct route_in6 sro; | ||||
struct rtentry *rt = NULL; | struct nhop_object *nh = NULL; | ||||
int rt_flags; | uint16_t nh_flags; | ||||
KASSERT(retifp != NULL, ("%s: retifp is NULL", __func__)); | KASSERT(retifp != NULL, ("%s: retifp is NULL", __func__)); | ||||
bzero(&sro, sizeof(sro)); | bzero(&sro, sizeof(sro)); | ||||
rt_flags = 0; | nh_flags = 0; | ||||
error = selectroute(dstsock, opts, mopts, &sro, retifp, &rt, 1, fibnum); | error = selectroute(dstsock, opts, mopts, &sro, retifp, &nh, 1, fibnum, 0); | ||||
if (rt) | if (nh != NULL) | ||||
rt_flags = rt->rt_flags; | nh_flags = nh->nh_flags; | ||||
if (rt && rt == sro.ro_rt) | if (nh != NULL && nh == sro.ro_nh) | ||||
RTFREE(rt); | NH_FREE(nh); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* Help ND. See oifp comment in in6_selectsrc(). */ | /* Help ND. See oifp comment in in6_selectsrc(). */ | ||||
if (oifp != NULL && fibnum == RT_DEFAULT_FIB) { | if (oifp != NULL && fibnum == RT_DEFAULT_FIB) { | ||||
*retifp = oifp; | *retifp = oifp; | ||||
error = 0; | error = 0; | ||||
} | } | ||||
return (error); | return (error); | ||||
Show All 12 Lines | in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | ||||
* scope zone with the interface. | * scope zone with the interface. | ||||
* - ip6_output() would try to get another route with the "new" | * - ip6_output() would try to get another route with the "new" | ||||
* destination, which may be valid. | * destination, which may be valid. | ||||
* - we'd see no error on output. | * - we'd see no error on output. | ||||
* Although this may not be very harmful, it should still be confusing. | * Although this may not be very harmful, it should still be confusing. | ||||
* We thus reject the case here. | * We thus reject the case here. | ||||
*/ | */ | ||||
if (rt_flags & (RTF_REJECT | RTF_BLACKHOLE)) { | if (nh_flags & (NHF_REJECT | NHF_BLACKHOLE)) { | ||||
error = (rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); | error = (nh_flags & NHF_HOST ? EHOSTUNREACH : ENETUNREACH); | ||||
return (error); | return (error); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Public wrapper function to selectroute(). */ | /* Public wrapper function to selectroute(). */ | ||||
int | int | ||||
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, | ||||
struct ip6_moptions *mopts, struct route_in6 *ro, | struct ip6_moptions *mopts, struct route_in6 *ro, | ||||
struct ifnet **retifp, struct rtentry **retrt, u_int fibnum) | struct ifnet **retifp, struct nhop_object **retnh, u_int fibnum, uint32_t flowid) | ||||
{ | { | ||||
return (selectroute(dstsock, opts, mopts, ro, retifp, | return (selectroute(dstsock, opts, mopts, ro, retifp, | ||||
retrt, 0, fibnum)); | retnh, 0, fibnum, flowid)); | ||||
} | } | ||||
/* | /* | ||||
* Default hop limit selection. The precedence is as follows: | * Default hop limit selection. The precedence is as follows: | ||||
* 1. Hoplimit value specified via ioctl. | * 1. Hoplimit value specified via ioctl. | ||||
* 2. (If the outgoing interface is detected) the current | * 2. (If the outgoing interface is detected) the current | ||||
* hop limit of the interface specified by router advertisement. | * hop limit of the interface specified by router advertisement. | ||||
* 3. The system default hoplimit. | * 3. The system default hoplimit. | ||||
▲ Show 20 Lines • Show All 317 Lines • Show Last 20 Lines |