Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route.c
Show First 20 Lines • Show All 181 Lines • ▼ Show 20 Lines | |||||
SYSCTL_PROC(_net, OID_AUTO, my_fibnum, CTLTYPE_INT|CTLFLAG_RD, | SYSCTL_PROC(_net, OID_AUTO, my_fibnum, CTLTYPE_INT|CTLFLAG_RD, | ||||
NULL, 0, &sysctl_my_fibnum, "I", "default FIB of caller"); | NULL, 0, &sysctl_my_fibnum, "I", "default FIB of caller"); | ||||
static __inline struct rib_head ** | static __inline struct rib_head ** | ||||
rt_tables_get_rnh_ptr(int table, int fam) | rt_tables_get_rnh_ptr(int table, int fam) | ||||
{ | { | ||||
struct rib_head **rnh; | struct rib_head **rnh; | ||||
KASSERT(table >= 0 && table < rt_numfibs, ("%s: table out of bounds.", | KASSERT(table >= 0 && table < rt_numfibs, | ||||
__func__)); | ("%s: table out of bounds (0 < %d < %d)", __func__, table, | ||||
KASSERT(fam >= 0 && fam < (AF_MAX+1), ("%s: fam out of bounds.", | rt_numfibs)); | ||||
__func__)); | KASSERT(fam >= 0 && fam < (AF_MAX + 1), | ||||
("%s: fam out of bounds (0 < %d < %d)", __func__, fam, AF_MAX + 1)); | |||||
/* rnh is [fib=0][af=0]. */ | /* rnh is [fib=0][af=0]. */ | ||||
rnh = (struct rib_head **)V_rt_tables; | rnh = (struct rib_head **)V_rt_tables; | ||||
/* Get the offset to the requested table and fam. */ | /* Get the offset to the requested table and fam. */ | ||||
rnh += table * (AF_MAX+1) + fam; | rnh += table * (AF_MAX+1) + fam; | ||||
return (rnh); | return (rnh); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | for (dom = domains; dom; dom = dom->dom_next) { | ||||
for (table = 0; table < rt_numfibs; table++) { | for (table = 0; table < rt_numfibs; table++) { | ||||
fam = dom->dom_family; | fam = dom->dom_family; | ||||
if (table != 0 && fam != AF_INET6 && fam != AF_INET) | if (table != 0 && fam != AF_INET6 && fam != AF_INET) | ||||
break; | break; | ||||
rnh = rt_tables_get_rnh_ptr(table, fam); | rnh = rt_tables_get_rnh_ptr(table, fam); | ||||
if (rnh == NULL) | if (rnh == NULL) | ||||
panic("%s: rnh NULL", __func__); | panic("%s: rnh NULL", __func__); | ||||
dom->dom_rtattach((void **)rnh, 0); | dom->dom_rtattach((void **)rnh, 0, table); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
VNET_SYSINIT(vnet_route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, | VNET_SYSINIT(vnet_route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, | ||||
vnet_route_init, 0); | vnet_route_init, 0); | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
static void | static void | ||||
Show All 24 Lines | vnet_route_uninit(const void *unused __unused) | ||||
free(V_rt_tables, M_RTABLE); | free(V_rt_tables, M_RTABLE); | ||||
uma_zdestroy(V_rtzone); | uma_zdestroy(V_rtzone); | ||||
} | } | ||||
VNET_SYSUNINIT(vnet_route_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, | VNET_SYSUNINIT(vnet_route_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, | ||||
vnet_route_uninit, 0); | vnet_route_uninit, 0); | ||||
#endif | #endif | ||||
struct rib_head * | struct rib_head * | ||||
rt_table_init(int offset) | rt_table_init(int offset, int family, u_int fibnum) | ||||
{ | { | ||||
struct rib_head *rh; | struct rib_head *rh; | ||||
rh = malloc(sizeof(struct rib_head), M_RTABLE, M_WAITOK | M_ZERO); | rh = malloc(sizeof(struct rib_head), M_RTABLE, M_WAITOK | M_ZERO); | ||||
/* TODO: These details should be hidded inside radix.c */ | /* TODO: These details should be hidded inside radix.c */ | ||||
/* Init masks tree */ | /* Init masks tree */ | ||||
rn_inithead_internal(&rh->head, rh->rnh_nodes, offset); | rn_inithead_internal(&rh->head, rh->rnh_nodes, offset); | ||||
rn_inithead_internal(&rh->rmhead.head, rh->rmhead.mask_nodes, 0); | rn_inithead_internal(&rh->rmhead.head, rh->rmhead.mask_nodes, 0); | ||||
rh->head.rnh_masks = &rh->rmhead; | rh->head.rnh_masks = &rh->rmhead; | ||||
rh->rib_family = family; | |||||
rh->rib_fibnum = fibnum; | |||||
/* Init locks */ | /* Init locks */ | ||||
RIB_LOCK_INIT(rh); | RIB_LOCK_INIT(rh); | ||||
tmproutes_init(rh); | |||||
/* Save vnet pointer for callouts */ | |||||
#ifdef VIMAGE | |||||
rh->rib_vnet = curvnet; | |||||
#endif | |||||
/* Finally, set base callbacks */ | /* Finally, set base callbacks */ | ||||
rh->rnh_addaddr = rn_addroute; | rh->rnh_addaddr = rn_addroute; | ||||
rh->rnh_deladdr = rn_delete; | rh->rnh_deladdr = rn_delete; | ||||
rh->rnh_matchaddr = rn_match; | rh->rnh_matchaddr = rn_match; | ||||
rh->rnh_lookup = rn_lookup; | rh->rnh_lookup = rn_lookup; | ||||
rh->rnh_walktree = rn_walktree; | rh->rnh_walktree = rn_walktree; | ||||
rh->rnh_walktree_from = rn_walktree_from; | rh->rnh_walktree_from = rn_walktree_from; | ||||
Show All 11 Lines | if (x != NULL) | ||||
R_Free(x); | R_Free(x); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
rt_table_destroy(struct rib_head *rh) | rt_table_destroy(struct rib_head *rh) | ||||
{ | { | ||||
tmproutes_destroy(rh); | |||||
rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head); | rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head); | ||||
/* Assume table is already empty */ | /* Assume table is already empty */ | ||||
RIB_LOCK_DESTROY(rh); | RIB_LOCK_DESTROY(rh); | ||||
free(rh, M_RTABLE); | free(rh, M_RTABLE); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | #endif | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
return; | return; | ||||
} | } | ||||
done: | done: | ||||
RT_UNLOCK(rt); | RT_UNLOCK(rt); | ||||
} | } | ||||
/* | static int | ||||
* Force a routing table entry to the specified | verify_redirect_gateway(struct sockaddr *src, struct sockaddr *dst, | ||||
* destination to go through the given gateway. | struct sockaddr *gateway, int flags, u_int fibnum) | ||||
* Normally called as a result of a routing redirect | |||||
* message from the network layer. | |||||
*/ | |||||
void | |||||
rtredirect_fib(struct sockaddr *dst, | |||||
struct sockaddr *gateway, | |||||
struct sockaddr *netmask, | |||||
int flags, | |||||
struct sockaddr *src, | |||||
u_int fibnum) | |||||
{ | { | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
int error = 0; | |||||
struct rt_addrinfo info; | |||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct rib_head *rnh; | |||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
ifa = NULL; | |||||
rnh = rt_tables_get_rnh(fibnum, dst->sa_family); | |||||
if (rnh == NULL) { | |||||
error = EAFNOSUPPORT; | |||||
goto out; | |||||
} | |||||
/* verify the gateway is directly reachable */ | /* verify the gateway is directly reachable */ | ||||
if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) { | if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) | ||||
error = ENETUNREACH; | return (ENETUNREACH); | ||||
goto out; | |||||
} | /* TODO: fib-aware */ | ||||
if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) | |||||
return (EHOSTUNREACH); | |||||
rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */ | rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */ | ||||
/* | /* | ||||
* If the redirect isn't from our current router for this dst, | * If the redirect isn't from our current router for this dst, | ||||
* it's either old or wrong. If it redirects us to ourselves, | * it's either old or wrong. If it redirects us to ourselves, | ||||
* we have a routing loop, perhaps as a result of an interface | * we have a routing loop, perhaps as a result of an interface | ||||
* going down recently. | * going down recently. | ||||
*/ | */ | ||||
if (!(flags & RTF_DONE) && rt) { | if (rt != NULL) { | ||||
if (!sa_equal(src, rt->rt_gateway)) { | if (!sa_equal(src, rt->rt_gateway)) { | ||||
error = EINVAL; | RTFREE_LOCKED(rt); | ||||
goto done; | return (EINVAL); | ||||
} | } | ||||
if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) { | if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) { | ||||
error = EINVAL; | RTFREE_LOCKED(rt); | ||||
goto done; | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) { | |||||
error = EHOSTUNREACH; | /* If host route already exists, ignore redirect. */ | ||||
goto done; | if (rt != NULL && (rt->rt_flags & RTF_HOST)) { | ||||
RTFREE_LOCKED(rt); | |||||
return (EEXIST); | |||||
} | } | ||||
/* If the prefix is directly reachable, ignore redirect */ | |||||
if (rt != NULL && !(rt->rt_flags & RTF_GATEWAY)) { | |||||
RTFREE_LOCKED(rt); | |||||
return (EEXIST); | |||||
} | |||||
RTFREE_LOCKED(rt); | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* Create a new entry if we just got back a wildcard entry | * Force a routing table entry to the specified | ||||
* or the lookup failed. This is necessary for hosts | * destination to go through the given gateway. | ||||
* which use routing redirects generated by smart gateways | * Normally called as a result of a routing redirect | ||||
* to dynamically build the routing tables. | * message from the network layer. | ||||
*/ | */ | ||||
if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) | int | ||||
goto create; | rtredirect_fib(struct sockaddr *dst, struct sockaddr *gateway, | ||||
/* | int flags, struct sockaddr *src, int expire_sec, u_int fibnum) | ||||
* Don't listen to the redirect if it's | { | ||||
* for a route to an interface. | struct rtentry *rt; | ||||
*/ | int error = 0; | ||||
if (rt->rt_flags & RTF_GATEWAY) { | struct rt_addrinfo info; | ||||
if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { | struct rt_metrics rti_rmx; | ||||
/* | struct ifaddr *ifa; | ||||
* Changing from route to net => route to host. | |||||
* Create new route, rather than smashing route to net. | |||||
*/ | |||||
create: | |||||
if (rt != NULL) | |||||
RTFREE_LOCKED(rt); | |||||
NET_EPOCH_ASSERT(); | |||||
if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL) | |||||
return (error); | |||||
error = verify_redirect_gateway(src, dst, gateway, flags, fibnum); | |||||
if (error != 0) | |||||
return (error); | |||||
/* verify the gateway is directly reachable */ | |||||
if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) | |||||
return (ENETUNREACH); | |||||
ifa_ref(ifa); | |||||
flags |= RTF_DYNAMIC; | flags |= RTF_DYNAMIC; | ||||
bzero((caddr_t)&info, sizeof(info)); | |||||
bzero(&info, sizeof(info)); | |||||
info.rti_info[RTAX_DST] = dst; | info.rti_info[RTAX_DST] = dst; | ||||
info.rti_info[RTAX_GATEWAY] = gateway; | info.rti_info[RTAX_GATEWAY] = gateway; | ||||
info.rti_info[RTAX_NETMASK] = netmask; | |||||
ifa_ref(ifa); | |||||
info.rti_ifa = ifa; | info.rti_ifa = ifa; | ||||
info.rti_flags = flags; | info.rti_flags = flags; | ||||
/* Setup route metrics to define expire time */ | |||||
bzero(&rti_rmx, sizeof(rti_rmx)); | |||||
/* Define expire time as absolute */ | |||||
rti_rmx.rmx_expire = expire_sec + time_second; | |||||
info.rti_mflags |= RTV_EXPIRE; | |||||
info.rti_rmx = &rti_rmx; | |||||
error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); | error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); | ||||
if (rt != NULL) { | ifa_free(ifa); | ||||
RT_LOCK(rt); | |||||
flags = rt->rt_flags; | if (error != 0) { | ||||
RTSTAT_INC(rts_badredirect); | |||||
return (error); | |||||
} | } | ||||
if (error == 0) | |||||
RTSTAT_INC(rts_dynamic); | |||||
} else { | |||||
/* | |||||
* Smash the current notion of the gateway to | |||||
* this destination. Should check about netmask!!! | |||||
*/ | |||||
if ((flags & RTF_GATEWAY) == 0) | |||||
rt->rt_flags &= ~RTF_GATEWAY; | |||||
rt->rt_flags |= RTF_MODIFIED; | |||||
flags |= RTF_MODIFIED; | |||||
RTSTAT_INC(rts_newgateway); | |||||
/* | |||||
* add the key and gateway (in one malloc'd chunk). | |||||
*/ | |||||
RT_UNLOCK(rt); | |||||
RIB_WLOCK(rnh); | |||||
RT_LOCK(rt); | RT_LOCK(rt); | ||||
rt_setgate(rt, rt_key(rt), gateway); | flags = rt->rt_flags; | ||||
RIB_WUNLOCK(rnh); | |||||
} | |||||
} else | |||||
error = EHOSTUNREACH; | |||||
done: | |||||
if (rt) | |||||
RTFREE_LOCKED(rt); | RTFREE_LOCKED(rt); | ||||
out: | |||||
if (error) | RTSTAT_INC(rts_dynamic); | ||||
RTSTAT_INC(rts_badredirect); | |||||
bzero((caddr_t)&info, sizeof(info)); | bzero(&info, sizeof(info)); | ||||
info.rti_info[RTAX_DST] = dst; | info.rti_info[RTAX_DST] = dst; | ||||
info.rti_info[RTAX_GATEWAY] = gateway; | info.rti_info[RTAX_GATEWAY] = gateway; | ||||
info.rti_info[RTAX_NETMASK] = netmask; | |||||
info.rti_info[RTAX_AUTHOR] = src; | info.rti_info[RTAX_AUTHOR] = src; | ||||
rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum); | rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum); | ||||
return (0); | |||||
} | } | ||||
/* | /* | ||||
* Routing table ioctl interface. | * Routing table ioctl interface. | ||||
*/ | */ | ||||
int | int | ||||
rtioctl_fib(u_long req, caddr_t data, u_int fibnum) | rtioctl_fib(u_long req, caddr_t data, u_int fibnum) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 333 Lines • ▼ Show 20 Lines | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
/* Entry was unlinked. Add to the list and return */ | /* Entry was unlinked. Add to the list and return */ | ||||
rt->rt_chain = di->head; | rt->rt_chain = di->head; | ||||
di->head = rt; | di->head = rt; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Iterates over all existing fibs in system. | * Iterates over rtable specified by @fibnum and @af and deletes elements | ||||
* Deletes each element for which @filter_f function returned | * marked by @filter_f. | ||||
* non-zero value. | * @fibnum: rtable id | ||||
* If @af is not AF_UNSPEC, iterates over fibs in particular | * @family: AF_ address family | ||||
* address family. | * @filter_f: lambda function returning non-zero value for items to delete | ||||
* @arg: data to pass to the @filter_f | |||||
* @report: true if rtsock notification is needed. | |||||
*/ | */ | ||||
void | void | ||||
rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg) | rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, int report) | ||||
{ | { | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct rt_delinfo di; | struct rt_delinfo di; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
uint32_t fibnum; | |||||
int i, start, end; | |||||
rnh = rt_tables_get_rnh(fibnum, family); | |||||
if (rnh == NULL) | |||||
return; | |||||
bzero(&di, sizeof(di)); | bzero(&di, sizeof(di)); | ||||
di.info.rti_filter = filter_f; | di.info.rti_filter = filter_f; | ||||
di.info.rti_filterdata = arg; | di.info.rti_filterdata = arg; | ||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | |||||
/* Do we want some specific family? */ | |||||
if (af != AF_UNSPEC) { | |||||
start = af; | |||||
end = af; | |||||
} else { | |||||
start = 1; | |||||
end = AF_MAX; | |||||
} | |||||
for (i = start; i <= end; i++) { | |||||
rnh = rt_tables_get_rnh(fibnum, i); | |||||
if (rnh == NULL) | |||||
continue; | |||||
di.rnh = rnh; | di.rnh = rnh; | ||||
RIB_WLOCK(rnh); | RIB_WLOCK(rnh); | ||||
rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); | rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
if (di.head == NULL) | if (di.head == NULL) | ||||
continue; | return; | ||||
/* We might have something to reclaim */ | /* We might have something to reclaim */ | ||||
while (di.head != NULL) { | while (di.head != NULL) { | ||||
rt = di.head; | rt = di.head; | ||||
di.head = rt->rt_chain; | di.head = rt->rt_chain; | ||||
rt->rt_chain = NULL; | rt->rt_chain = NULL; | ||||
/* TODO std rt -> rt_addrinfo export */ | /* TODO std rt -> rt_addrinfo export */ | ||||
di.info.rti_info[RTAX_DST] = rt_key(rt); | di.info.rti_info[RTAX_DST] = rt_key(rt); | ||||
di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); | di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); | ||||
rt_notifydelete(rt, &di.info); | rt_notifydelete(rt, &di.info); | ||||
if (report) | |||||
rt_routemsg(RTM_DELETE, rt->rt_ifp, 0, rt, fibnum); | |||||
RTFREE_LOCKED(rt); | RTFREE_LOCKED(rt); | ||||
} | } | ||||
} | |||||
/* | |||||
* Iterates over all existing fibs in system. | |||||
* Deletes each element for which @filter_f function returned | |||||
* non-zero value. | |||||
* If @af is not AF_UNSPEC, iterates over fibs in particular | |||||
* address family. | |||||
*/ | |||||
void | |||||
rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg) | |||||
{ | |||||
uint32_t fibnum; | |||||
int i, start, end; | |||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | |||||
/* Do we want some specific family? */ | |||||
if (af != AF_UNSPEC) { | |||||
start = af; | |||||
end = af; | |||||
} else { | |||||
start = 1; | |||||
end = AF_MAX; | |||||
} | } | ||||
for (i = start; i <= end; i++) { | |||||
if (rt_tables_get_rnh(fibnum, i) == NULL) | |||||
continue; | |||||
rib_walk_del(fibnum, i, filter_f, arg, 0); | |||||
} | } | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* Delete Routes for a Network Interface | * Delete Routes for a Network Interface | ||||
* | * | ||||
* Called for each routing entry via the rnh->rnh_walktree() call above | * Called for each routing entry via the rnh->rnh_walktree() call above | ||||
* to delete all route entries referencing a detaching network interface. | * to delete all route entries referencing a detaching network interface. | ||||
* | * | ||||
* Arguments: | * Arguments: | ||||
▲ Show 20 Lines • Show All 565 Lines • ▼ Show 20 Lines | if (rt_mpath_capable(rnh) && | ||||
R_Free(rt_key(rt)); | R_Free(rt_key(rt)); | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
return (EEXIST); | return (EEXIST); | ||||
} | } | ||||
#endif | #endif | ||||
/* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ | /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ | ||||
rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); | rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); | ||||
if (rn != NULL && rt->rt_expire > 0) | |||||
tmproutes_update(rnh, rt); | |||||
rt_old = NULL; | rt_old = NULL; | ||||
if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) { | if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) { | ||||
/* | /* | ||||
* Force removal and re-try addition | * Force removal and re-try addition | ||||
* TODO: better multipath&pinned support | * TODO: better multipath&pinned support | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 590 Lines • Show Last 20 Lines |