Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6_fib.c
Show All 27 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include "opt_route.h" | #include "opt_route.h" | ||||
#include "opt_mpath.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.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/route_ctl.h> | |||||
#include <net/route/route_var.h> | #include <net/route/route_var.h> | ||||
#include <net/route/nhop.h> | #include <net/route/nhop.h> | ||||
#include <net/toeplitz.h> | |||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#ifdef RADIX_MPATH | |||||
#include <net/radix_mpath.h> | |||||
#endif | |||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/ip_mroute.h> | #include <netinet/ip_mroute.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet6/in6_fib.h> | #include <netinet6/in6_fib.h> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||||
#include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#ifdef INET6 | #ifdef INET6 | ||||
CHK_STRUCT_ROUTE_COMPAT(struct route_in6, ro_dst); | CHK_STRUCT_ROUTE_COMPAT(struct route_in6, ro_dst); | ||||
#ifdef ROUTE_MPATH | |||||
struct _hash_5tuple_ipv6 { | |||||
struct in6_addr src; | |||||
struct in6_addr dst; | |||||
unsigned short src_port; | |||||
unsigned short dst_port; | |||||
char proto; | |||||
char spare[3]; | |||||
}; | |||||
_Static_assert(sizeof(struct _hash_5tuple_ipv6) == 40, | |||||
"_hash_5tuple_ipv6 size is wrong"); | |||||
uint32_t | |||||
fib6_calc_software_hash(const struct in6_addr *src, const struct in6_addr *dst, | |||||
unsigned short src_port, unsigned short dst_port, char proto, | |||||
uint32_t *phashtype) | |||||
{ | |||||
struct _hash_5tuple_ipv6 data; | |||||
data.src = *src; | |||||
data.dst = *dst; | |||||
data.src_port = src_port; | |||||
data.dst_port = dst_port; | |||||
data.proto = proto; | |||||
data.spare[0] = data.spare[1] = data.spare[2] = 0; | |||||
*phashtype = M_HASHTYPE_OPAQUE_HASH; | |||||
return (toeplitz_hash(MPATH_ENTROPY_KEY_LEN, mpath_entropy_key, | |||||
sizeof(data), (uint8_t *)&data)); | |||||
} | |||||
#endif | |||||
/* | /* | ||||
* Looks up path in fib @fibnum specified by @dst. | * Looks up path in fib @fibnum specified by @dst. | ||||
* Assumes scope is deembedded and provided in @scopeid. | * Assumes scope is deembedded and provided in @scopeid. | ||||
* | * | ||||
* Returns path nexthop on success. Nexthop is safe to use | * Returns path nexthop on success. Nexthop is safe to use | ||||
* within the current network epoch. If longer lifetime is required, | * within the current network epoch. If longer lifetime is required, | ||||
* one needs to pass NHR_REF as a flag. This will return referenced | * one needs to pass NHR_REF as a flag. This will return referenced | ||||
* nexthop. | * nexthop. | ||||
*/ | */ | ||||
struct nhop_object * | struct nhop_object * | ||||
fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6, | fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6, | ||||
uint32_t scopeid, uint32_t flags, uint32_t flowid) | uint32_t scopeid, uint32_t flags, uint32_t flowid) | ||||
{ | { | ||||
RIB_RLOCK_TRACKER; | RIB_RLOCK_TRACKER; | ||||
struct rib_head *rh; | struct rib_head *rh; | ||||
struct radix_node *rn; | struct radix_node *rn; | ||||
struct rtentry *rt; | |||||
struct nhop_object *nh; | struct nhop_object *nh; | ||||
struct sockaddr_in6 sin6; | struct sockaddr_in6 sin6; | ||||
KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); | KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); | ||||
rh = rt_tables_get_rnh(fibnum, AF_INET6); | rh = rt_tables_get_rnh(fibnum, AF_INET6); | ||||
if (rh == NULL) | if (rh == NULL) | ||||
return (NULL); | return (NULL); | ||||
/* TODO: radix changes */ | /* TODO: radix changes */ | ||||
//addr = *dst6; | //addr = *dst6; | ||||
/* Prepare lookup key */ | /* Prepare lookup key */ | ||||
memset(&sin6, 0, sizeof(sin6)); | memset(&sin6, 0, sizeof(sin6)); | ||||
sin6.sin6_len = sizeof(struct sockaddr_in6); | sin6.sin6_len = sizeof(struct sockaddr_in6); | ||||
sin6.sin6_addr = *dst6; | sin6.sin6_addr = *dst6; | ||||
/* Assume scopeid is valid and embed it directly */ | /* Assume scopeid is valid and embed it directly */ | ||||
if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | ||||
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | ||||
RIB_RLOCK(rh); | RIB_RLOCK(rh); | ||||
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | ||||
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | ||||
rt = RNTORT(rn); | nh = nhop_select((RNTORT(rn))->rt_nhop, flowid); | ||||
#ifdef RADIX_MPATH | |||||
if (rt_mpath_next(rt) != NULL) | |||||
rt = rt_mpath_selectrte(rt, flowid); | |||||
#endif | |||||
nh = rt->rt_nhop; | |||||
/* Ensure route & ifp is UP */ | /* Ensure route & ifp is UP */ | ||||
if (RT_LINK_IS_UP(nh->nh_ifp)) { | if (RT_LINK_IS_UP(nh->nh_ifp)) { | ||||
if (flags & NHR_REF) | if (flags & NHR_REF) | ||||
nhop_ref_object(nh); | nhop_ref_object(nh); | ||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
return (nh); | return (nh); | ||||
} | } | ||||
} | } | ||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
RTSTAT_INC(rts_unreach); | RTSTAT_INC(rts_unreach); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
inline static int | inline static int | ||||
check_urpf(const struct nhop_object *nh, uint32_t flags, | check_urpf_nhop(const struct nhop_object *nh, uint32_t flags, | ||||
const struct ifnet *src_if) | const struct ifnet *src_if) | ||||
{ | { | ||||
if (src_if != NULL && nh->nh_aifp == src_if) { | if (src_if != NULL && nh->nh_aifp == src_if) { | ||||
return (1); | return (1); | ||||
} | } | ||||
if (src_if == NULL) { | if (src_if == NULL) { | ||||
if ((flags & NHR_NODEFAULT) == 0) | if ((flags & NHR_NODEFAULT) == 0) | ||||
return (1); | return (1); | ||||
else if ((nh->nh_flags & NHF_DEFAULT) == 0) | else if ((nh->nh_flags & NHF_DEFAULT) == 0) | ||||
return (1); | return (1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef RADIX_MPATH | static int | ||||
inline static int | check_urpf(struct nhop_object *nh, uint32_t flags, | ||||
check_urpf_mpath(struct rtentry *rt, uint32_t flags, | |||||
const struct ifnet *src_if) | const struct ifnet *src_if) | ||||
{ | { | ||||
#ifdef ROUTE_MPATH | |||||
while (rt != NULL) { | if (NH_IS_NHGRP(nh)) { | ||||
if (check_urpf(rt->rt_nhop, flags, src_if) != 0) | struct weightened_nhop *wn; | ||||
uint32_t num_nhops; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops); | |||||
for (int i = 0; i < num_nhops; i++) { | |||||
if (check_urpf_nhop(wn[i].nh, flags, src_if) != 0) | |||||
return (1); | return (1); | ||||
rt = rt_mpath_next(rt); | |||||
} | } | ||||
return (0); | return (0); | ||||
} | } else | ||||
#endif | #endif | ||||
return (check_urpf_nhop(nh, flags, src_if)); | |||||
} | |||||
/* | /* | ||||
* Performs reverse path forwarding lookup. | * Performs reverse path forwarding lookup. | ||||
* If @src_if is non-zero, verifies that at least 1 path goes via | * If @src_if is non-zero, verifies that at least 1 path goes via | ||||
* this interface. | * this interface. | ||||
* If @src_if is zero, verifies that route exist. | * If @src_if is zero, verifies that route exist. | ||||
* if @flags contains NHR_NOTDEFAULT, do not consider default route. | * if @flags contains NHR_NOTDEFAULT, do not consider default route. | ||||
* | * | ||||
* Returns 1 if route matching conditions is found, 0 otherwise. | * Returns 1 if route matching conditions is found, 0 otherwise. | ||||
*/ | */ | ||||
int | int | ||||
fib6_check_urpf(uint32_t fibnum, const struct in6_addr *dst6, | fib6_check_urpf(uint32_t fibnum, const struct in6_addr *dst6, | ||||
uint32_t scopeid, uint32_t flags, const struct ifnet *src_if) | uint32_t scopeid, uint32_t flags, const struct ifnet *src_if) | ||||
{ | { | ||||
RIB_RLOCK_TRACKER; | RIB_RLOCK_TRACKER; | ||||
struct rib_head *rh; | struct rib_head *rh; | ||||
struct radix_node *rn; | struct radix_node *rn; | ||||
struct rtentry *rt; | |||||
struct sockaddr_in6 sin6; | struct sockaddr_in6 sin6; | ||||
int ret; | int ret; | ||||
KASSERT((fibnum < rt_numfibs), ("fib6_check_urpf: bad fibnum")); | KASSERT((fibnum < rt_numfibs), ("fib6_check_urpf: bad fibnum")); | ||||
rh = rt_tables_get_rnh(fibnum, AF_INET6); | rh = rt_tables_get_rnh(fibnum, AF_INET6); | ||||
if (rh == NULL) | if (rh == NULL) | ||||
return (0); | return (0); | ||||
/* TODO: radix changes */ | /* TODO: radix changes */ | ||||
/* Prepare lookup key */ | /* Prepare lookup key */ | ||||
memset(&sin6, 0, sizeof(sin6)); | memset(&sin6, 0, sizeof(sin6)); | ||||
sin6.sin6_len = sizeof(struct sockaddr_in6); | sin6.sin6_len = sizeof(struct sockaddr_in6); | ||||
sin6.sin6_addr = *dst6; | sin6.sin6_addr = *dst6; | ||||
/* Assume scopeid is valid and embed it directly */ | /* Assume scopeid is valid and embed it directly */ | ||||
if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | ||||
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | ||||
RIB_RLOCK(rh); | RIB_RLOCK(rh); | ||||
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | ||||
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | ||||
rt = RNTORT(rn); | ret = check_urpf(RNTORT(rn)->rt_nhop, flags, src_if); | ||||
#ifdef RADIX_MPATH | |||||
ret = check_urpf_mpath(rt, flags, src_if); | |||||
#else | |||||
ret = check_urpf(rt->rt_nhop, flags, src_if); | |||||
#endif | |||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
return (0); | return (0); | ||||
} | } | ||||
struct nhop_object * | struct nhop_object * | ||||
fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr *dst6, | fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr *dst6, | ||||
uint32_t scopeid, uint32_t flags) | uint32_t scopeid, uint32_t flags) | ||||
{ | { | ||||
struct rib_head *rh; | struct rib_head *rh; | ||||
struct radix_node *rn; | struct radix_node *rn; | ||||
struct rtentry *rt; | |||||
struct nhop_object *nh; | struct nhop_object *nh; | ||||
struct sockaddr_in6 sin6; | struct sockaddr_in6 sin6; | ||||
KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); | KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); | ||||
rh = rt_tables_get_rnh(fibnum, AF_INET6); | rh = rt_tables_get_rnh(fibnum, AF_INET6); | ||||
if (rh == NULL) | if (rh == NULL) | ||||
return (NULL); | return (NULL); | ||||
/* TODO: radix changes */ | /* TODO: radix changes */ | ||||
//addr = *dst6; | //addr = *dst6; | ||||
/* Prepare lookup key */ | /* Prepare lookup key */ | ||||
memset(&sin6, 0, sizeof(sin6)); | memset(&sin6, 0, sizeof(sin6)); | ||||
sin6.sin6_len = sizeof(struct sockaddr_in6); | sin6.sin6_len = sizeof(struct sockaddr_in6); | ||||
sin6.sin6_addr = *dst6; | sin6.sin6_addr = *dst6; | ||||
/* Assume scopeid is valid and embed it directly */ | /* Assume scopeid is valid and embed it directly */ | ||||
if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | if (IN6_IS_SCOPE_LINKLOCAL(dst6)) | ||||
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); | ||||
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); | ||||
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | ||||
rt = RNTORT(rn); | nh = nhop_select((RNTORT(rn))->rt_nhop, 0); | ||||
nh = rt->rt_nhop; | |||||
/* Ensure route & ifp is UP */ | /* Ensure route & ifp is UP */ | ||||
if (RT_LINK_IS_UP(nh->nh_ifp)) { | if (RT_LINK_IS_UP(nh->nh_ifp)) { | ||||
if (flags & NHR_REF) | if (flags & NHR_REF) | ||||
nhop_ref_object(nh); | nhop_ref_object(nh); | ||||
return (nh); | return (nh); | ||||
} | } | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
#endif | #endif |