Changeset View
Changeset View
Standalone View
Standalone View
head/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 157 Lines • ▼ Show 20 Lines | rt_table_init(int offset, int family, u_int fibnum) | ||||
/* Save metadata associated with this routing table. */ | /* Save metadata associated with this routing table. */ | ||||
rh->rib_family = family; | rh->rib_family = family; | ||||
rh->rib_fibnum = fibnum; | rh->rib_fibnum = fibnum; | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
rh->rib_vnet = curvnet; | rh->rib_vnet = curvnet; | ||||
#endif | #endif | ||||
tmproutes_init(rh); | |||||
/* Init locks */ | /* Init locks */ | ||||
RIB_LOCK_INIT(rh); | RIB_LOCK_INIT(rh); | ||||
/* 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; | ||||
Show All 14 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 174 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); | ||||
} | } | ||||
/* | /* | ||||
* Force a routing table entry to the specified | * Adds a temporal redirect entry to the routing table. | ||||
* destination to go through the given gateway. | * @fibnum: fib number | ||||
* Normally called as a result of a routing redirect | * @dst: destination to install redirect to | ||||
* message from the network layer. | * @gateway: gateway to go via | ||||
* @author: sockaddr of originating router, can be NULL | |||||
* @ifp: interface to use for the redirected route | |||||
* @flags: set of flags to add. Allowed: RTF_GATEWAY | |||||
* @lifetime_sec: time in seconds to expire this redirect. | |||||
* | |||||
* Retuns 0 on success, errno otherwise. | |||||
*/ | */ | ||||
void | int | ||||
rtredirect_fib(struct sockaddr *dst, | rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway, | ||||
struct sockaddr *gateway, | struct sockaddr *author, struct ifnet *ifp, int flags, int lifetime_sec) | ||||
struct sockaddr *netmask, | |||||
int flags, | |||||
struct sockaddr *src, | |||||
u_int fibnum) | |||||
{ | { | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
int error = 0; | int error; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct rt_metrics rti_rmx; | |||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct rib_head *rnh; | |||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
ifa = NULL; | if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL) | ||||
rnh = rt_tables_get_rnh(fibnum, dst->sa_family); | return (EAFNOSUPPORT); | ||||
if (rnh == NULL) { | |||||
error = EAFNOSUPPORT; | |||||
goto out; | |||||
} | |||||
/* verify the gateway is directly reachable */ | |||||
if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) { | |||||
error = ENETUNREACH; | |||||
goto out; | |||||
} | |||||
rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */ | |||||
/* | |||||
* If the redirect isn't from our current router for this dst, | |||||
* it's either old or wrong. If it redirects us to ourselves, | |||||
* we have a routing loop, perhaps as a result of an interface | |||||
* going down recently. | |||||
*/ | |||||
if (!(flags & RTF_DONE) && rt) { | |||||
if (!sa_equal(src, rt->rt_gateway)) { | |||||
error = EINVAL; | |||||
goto done; | |||||
} | |||||
if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) { | |||||
error = EINVAL; | |||||
goto done; | |||||
} | |||||
} | |||||
if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) { | |||||
error = EHOSTUNREACH; | |||||
goto done; | |||||
} | |||||
/* | |||||
* Create a new entry if we just got back a wildcard entry | |||||
* or the lookup failed. This is necessary for hosts | |||||
* which use routing redirects generated by smart gateways | |||||
* to dynamically build the routing tables. | |||||
*/ | |||||
if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) | |||||
goto create; | |||||
/* | |||||
* Don't listen to the redirect if it's | |||||
* for a route to an interface. | |||||
*/ | |||||
if (rt->rt_flags & RTF_GATEWAY) { | |||||
if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { | |||||
/* | |||||
* Changing from route to net => route to host. | |||||
* Create new route, rather than smashing route to net. | |||||
*/ | |||||
create: | |||||
if (rt != NULL) | |||||
RTFREE_LOCKED(rt); | |||||
flags |= RTF_DYNAMIC; | /* Verify the allowed flag mask. */ | ||||
bzero((caddr_t)&info, sizeof(info)); | KASSERT(((flags & ~(RTF_GATEWAY)) == 0), | ||||
("invalid redirect flags: %x", flags)); | |||||
/* Get the best ifa for the given interface and gateway. */ | |||||
if ((ifa = ifaof_ifpforaddr(gateway, ifp)) == NULL) | |||||
return (ENETUNREACH); | |||||
ifa_ref(ifa); | |||||
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_ifp = ifp; | ||||
info.rti_flags = flags | RTF_DYNAMIC; | |||||
/* Setup route metrics to define expire time. */ | |||||
bzero(&rti_rmx, sizeof(rti_rmx)); | |||||
/* Set expire time as absolute. */ | |||||
rti_rmx.rmx_expire = lifetime_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) { | ||||
/* TODO: add per-fib redirect stats. */ | |||||
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)); | /* Send notification of a route addition to userland. */ | ||||
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] = author; | ||||
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 a routing table specified by @fibnum and @family and | ||||
* Deletes each element for which @filter_f function returned | * deletes elements 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: function returning non-zero value for items to delete | ||||
* @arg: data to pass to the @filter_f function | |||||
* @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, bool 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->rt_ifp, 0, fibnum); | |||||
RTFREE_LOCKED(rt); | RTFREE_LOCKED(rt); | ||||
} | } | ||||
} | |||||
/* | |||||
* Iterates over all existing fibs in system and deletes each element | |||||
* for which @filter_f function returns non-zero value. | |||||
* If @family is not AF_UNSPEC, iterates over fibs in particular | |||||
* address family. | |||||
*/ | |||||
void | |||||
rt_foreach_fib_walk_del(int family, rt_filter_f_t *filter_f, void *arg) | |||||
{ | |||||
u_int fibnum; | |||||
int i, start, end; | |||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | |||||
/* Do we want some specific family? */ | |||||
if (family != AF_UNSPEC) { | |||||
start = family; | |||||
end = family; | |||||
} 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 605 Lines • Show Last 20 Lines |