Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route.c
Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.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/route/shared.h> | |||||
#define NEED_RTZONE | |||||
#include <net/route_var.h> | #include <net/route_var.h> | ||||
#include <net/route/rtentry_var.h> | |||||
#include <net/vnet.h> | #include <net/vnet.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/ip_mroute.h> | #include <netinet/ip_mroute.h> | ||||
Show All 30 Lines | |||||
* XXX also has the problems getting the FIB from curthread which will not | * XXX also has the problems getting the FIB from curthread which will not | ||||
* always work given the fib can be overridden and prefixes can be added | * always work given the fib can be overridden and prefixes can be added | ||||
* from the network stack context. | * from the network stack context. | ||||
*/ | */ | ||||
VNET_DEFINE(u_int, rt_add_addr_allfibs) = 1; | VNET_DEFINE(u_int, rt_add_addr_allfibs) = 1; | ||||
SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, | SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, | ||||
&VNET_NAME(rt_add_addr_allfibs), 0, ""); | &VNET_NAME(rt_add_addr_allfibs), 0, ""); | ||||
VNET_PCPUSTAT_DEFINE_STATIC(struct rtstat, rtstat); | VNET_PCPUSTAT_DEFINE(struct rtstat, rtstat); | ||||
#define RTSTAT_ADD(name, val) \ | #define RTSTAT_ADD(name, val) \ | ||||
VNET_PCPUSTAT_ADD(struct rtstat, rtstat, name, (val)) | VNET_PCPUSTAT_ADD(struct rtstat, rtstat, name, (val)) | ||||
#define RTSTAT_INC(name) RTSTAT_ADD(name, 1) | #define RTSTAT_INC(name) RTSTAT_ADD(name, 1) | ||||
VNET_PCPUSTAT_SYSINIT(rtstat); | VNET_PCPUSTAT_SYSINIT(rtstat); | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
VNET_PCPUSTAT_SYSUNINIT(rtstat); | VNET_PCPUSTAT_SYSUNINIT(rtstat); | ||||
#endif | #endif | ||||
VNET_DEFINE(struct rib_head *, rt_tables); | VNET_DEFINE(struct rib_head *, rt_tables); | ||||
#define V_rt_tables VNET(rt_tables) | #define V_rt_tables VNET(rt_tables) | ||||
VNET_DEFINE(int, rttrash); /* routes not in table but not freed */ | VNET_DEFINE(int, rttrash); /* routes not in table but not freed */ | ||||
#define V_rttrash VNET(rttrash) | #define V_rttrash VNET(rttrash) | ||||
#if 0 | |||||
VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */ | |||||
#endif | |||||
uma_zone_t rtzone; /* Routing table UMA zone. */ | |||||
/* | |||||
* Convert a 'struct radix_node *' to a 'struct rtentry *'. | |||||
* The operation can be done safely (in this code) because a | |||||
* 'struct rtentry' starts with two 'struct radix_node''s, the first | |||||
* one representing leaf nodes in the routing tree, which is | |||||
* what the code in radix.c passes us as a 'struct radix_node'. | |||||
* | |||||
* But because there are a lot of assumptions in this conversion, | |||||
* do not cast explicitly, but always use the macro below. | |||||
*/ | |||||
#define RNTORT(p) ((struct rtentry *)(p)) | |||||
VNET_DEFINE_STATIC(uma_zone_t, rtzone); /* Routing table UMA zone. */ | |||||
#define V_rtzone VNET(rtzone) | |||||
EVENTHANDLER_LIST_DEFINE(rt_addrmsg); | EVENTHANDLER_LIST_DEFINE(rt_addrmsg); | ||||
static int rt_getifa_fib(struct rt_addrinfo *, u_int); | static void destroy_rtentry(struct rtentry *rt); | ||||
static int rtrequest1_fib_change(struct rib_head *, struct rt_addrinfo *, | static void destroy_rtentry_epoch(epoch_context_t ctx); | ||||
struct rtentry **, u_int); | |||||
static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *); | static int rt_ifdelroute(const struct rtentry *rt, const struct nhop_object *, | ||||
static int rt_ifdelroute(const struct rtentry *rt, void *arg); | void *arg); | ||||
static struct rtentry *rt_unlinkrte(struct rib_head *rnh, | static void rt_notifydelete(struct rtentry *rt, struct nhop_object *nh, | ||||
struct rt_addrinfo *info, int *perror); | struct rt_addrinfo *info); | ||||
static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); | |||||
#ifdef RADIX_MPATH | #ifdef RADIX_MPATH | ||||
static struct radix_node *rt_mpath_unlink(struct rib_head *rnh, | static struct radix_node *rt_mpath_unlink(struct rib_head *rnh, | ||||
struct rt_addrinfo *info, struct rtentry *rto, int *perror); | struct rt_addrinfo *info, struct rtentry *rto, int *perror); | ||||
#endif | #endif | ||||
static int rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, | static int rt_exportinfo(struct rtentry *rt, struct nhop_object *nh, | ||||
int flags); | struct rt_addrinfo *info, int flags); | ||||
struct if_mtuinfo | int p_sockaddr(char *buf, int buflen, const struct sockaddr *s, int family); | ||||
{ | int rt_print(char *buf, int buflen, const struct rtentry *rt); | ||||
struct ifnet *ifp; | |||||
int mtu; | |||||
}; | |||||
static int if_updatemtu_cb(struct radix_node *, void *); | |||||
/* | /* | ||||
* handler for net.my_fibnum | * handler for net.my_fibnum | ||||
*/ | */ | ||||
static int | static int | ||||
sysctl_my_fibnum(SYSCTL_HANDLER_ARGS) | sysctl_my_fibnum(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int fibnum; | int fibnum; | ||||
int error; | int error; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | rt_tables_get_gen(int table, int fam) | ||||
rnh = *rt_tables_get_rnh_ptr(table, fam); | rnh = *rt_tables_get_rnh_ptr(table, fam); | ||||
KASSERT(rnh != NULL, ("%s: NULL rib_head pointer table %d fam %d", | KASSERT(rnh != NULL, ("%s: NULL rib_head pointer table %d fam %d", | ||||
__func__, table, fam)); | __func__, table, fam)); | ||||
return (rnh->rnh_gen); | return (rnh->rnh_gen); | ||||
} | } | ||||
/* | |||||
* route initialization must occur before ip6_init2(), which happenas at | |||||
* SI_ORDER_MIDDLE. | |||||
*/ | |||||
static void | |||||
route_init(void) | |||||
{ | |||||
/* whack the tunable ints into line. */ | |||||
if (rt_numfibs > RT_MAXFIBS) | |||||
rt_numfibs = RT_MAXFIBS; | |||||
if (rt_numfibs == 0) | |||||
rt_numfibs = 1; | |||||
} | |||||
SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, NULL); | |||||
static int | static int | ||||
rtentry_zinit(void *mem, int size, int how) | rtentry_zinit(void *mem, int size, int how) | ||||
{ | { | ||||
struct rtentry *rt = mem; | struct rtentry *rt = mem; | ||||
rt->rt_pksent = counter_u64_alloc(how); | |||||
if (rt->rt_pksent == NULL) | |||||
return (ENOMEM); | |||||
RT_LOCK_INIT(rt); | RT_LOCK_INIT(rt); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
rtentry_zfini(void *mem, int size) | rtentry_zfini(void *mem, int size) | ||||
{ | { | ||||
struct rtentry *rt = mem; | struct rtentry *rt = mem; | ||||
RT_LOCK_DESTROY(rt); | RT_LOCK_DESTROY(rt); | ||||
counter_u64_free(rt->rt_pksent); | |||||
} | } | ||||
static int | static int | ||||
rtentry_ctor(void *mem, int size, void *arg, int how) | rtentry_ctor(void *mem, int size, void *arg, int how) | ||||
{ | { | ||||
struct rtentry *rt = mem; | struct rtentry *rt = mem; | ||||
bzero(rt, offsetof(struct rtentry, rt_endzero)); | bzero(rt, offsetof(struct rtentry, rt_endzero)); | ||||
counter_u64_zero(rt->rt_pksent); | |||||
rt->rt_chain = NULL; | rt->rt_chain = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
rtentry_dtor(void *mem, int size, void *arg) | rtentry_dtor(void *mem, int size, void *arg) | ||||
{ | { | ||||
struct rtentry *rt = mem; | struct rtentry *rt = mem; | ||||
RT_UNLOCK_COND(rt); | RT_UNLOCK_COND(rt); | ||||
} | } | ||||
/* | |||||
* route initialization must occur before ip6_init2(), which happenas at | |||||
* SI_ORDER_MIDDLE. | |||||
*/ | |||||
static void | static void | ||||
route_init(void) | |||||
{ | |||||
/* whack the tunable ints into line. */ | |||||
if (rt_numfibs > RT_MAXFIBS) | |||||
rt_numfibs = RT_MAXFIBS; | |||||
if (rt_numfibs == 0) | |||||
rt_numfibs = 1; | |||||
V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), | |||||
rtentry_ctor, rtentry_dtor, | |||||
rtentry_zinit, rtentry_zfini, UMA_ALIGN_PTR, 0); | |||||
} | |||||
SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, NULL); | |||||
static void | |||||
vnet_route_init(const void *unused __unused) | vnet_route_init(const void *unused __unused) | ||||
{ | { | ||||
struct domain *dom; | struct domain *dom; | ||||
struct rib_head **rnh; | struct rib_head **rnh; | ||||
int table; | int table; | ||||
int fam; | int fam; | ||||
V_rt_tables = malloc(rt_numfibs * (AF_MAX+1) * | V_rt_tables = malloc(rt_numfibs * (AF_MAX+1) * | ||||
sizeof(struct rib_head *), M_RTABLE, M_WAITOK|M_ZERO); | sizeof(struct rib_head *), M_RTABLE, M_WAITOK|M_ZERO); | ||||
/* | |||||
V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), | V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), | ||||
rtentry_ctor, rtentry_dtor, | rtentry_ctor, rtentry_dtor, | ||||
rtentry_zinit, rtentry_zfini, UMA_ALIGN_PTR, 0); | rtentry_zinit, rtentry_zfini, UMA_ALIGN_PTR, 0); | ||||
*/ | |||||
for (dom = domains; dom; dom = dom->dom_next) { | for (dom = domains; dom; dom = dom->dom_next) { | ||||
if (dom->dom_rtattach == NULL) | if (dom->dom_rtattach == NULL) | ||||
continue; | continue; | ||||
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; | ||||
Show All 12 Lines | |||||
static void | static void | ||||
vnet_route_uninit(const void *unused __unused) | vnet_route_uninit(const void *unused __unused) | ||||
{ | { | ||||
int table; | int table; | ||||
int fam; | int fam; | ||||
struct domain *dom; | struct domain *dom; | ||||
struct rib_head **rnh; | struct rib_head **rnh; | ||||
rnh = (struct rib_head **)V_rt_tables; | |||||
printf("--VNET V_rt_tables=%p\n", rnh); | |||||
for (dom = domains; dom; dom = dom->dom_next) { | for (dom = domains; dom; dom = dom->dom_next) { | ||||
if (dom->dom_rtdetach == NULL) | if (dom->dom_rtdetach == NULL) | ||||
continue; | continue; | ||||
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_rtdetach((void **)rnh, 0); | dom->dom_rtdetach((void **)rnh, 0); | ||||
} | } | ||||
} | } | ||||
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, int family, u_int fibnum) | rt_table_init(int offset, int family, u_int fibnum) | ||||
{ | { | ||||
Show All 14 Lines | #ifdef VIMAGE | ||||
rh->rib_vnet = curvnet; | rh->rib_vnet = curvnet; | ||||
#endif | #endif | ||||
tmproutes_init(rh); | tmproutes_init(rh); | ||||
/* Init locks */ | /* Init locks */ | ||||
RIB_LOCK_INIT(rh); | RIB_LOCK_INIT(rh); | ||||
nhops_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; | ||||
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 15 Lines | |||||
void | void | ||||
rt_table_destroy(struct rib_head *rh) | rt_table_destroy(struct rib_head *rh) | ||||
{ | { | ||||
tmproutes_destroy(rh); | tmproutes_destroy(rh); | ||||
rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head); | rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head); | ||||
nhops_destroy(rh); | |||||
/* 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); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct setfib_args { | struct setfib_args { | ||||
int fibnum; | int fibnum; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_setfib(struct thread *td, struct setfib_args *uap) | sys_setfib(struct thread *td, struct setfib_args *uap) | ||||
{ | { | ||||
if (uap->fibnum < 0 || uap->fibnum >= rt_numfibs) | if (uap->fibnum < 0 || uap->fibnum >= rt_numfibs) | ||||
return EINVAL; | return EINVAL; | ||||
td->td_proc->p_fibnum = uap->fibnum; | td->td_proc->p_fibnum = uap->fibnum; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Packet routing routines. | |||||
*/ | |||||
void | |||||
rtalloc_ign_fib(struct route *ro, u_long ignore, u_int fibnum) | |||||
{ | |||||
struct rtentry *rt; | |||||
if ((rt = ro->ro_rt) != NULL) { | |||||
if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) | |||||
return; | |||||
RTFREE(rt); | |||||
ro->ro_rt = NULL; | |||||
} | |||||
ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, ignore, fibnum); | |||||
if (ro->ro_rt) | |||||
RT_UNLOCK(ro->ro_rt); | |||||
} | |||||
/* | |||||
* Look up the route that matches the address given | |||||
* Or, at least try.. Create a cloned route if needed. | |||||
* | |||||
* The returned route, if any, is locked. | |||||
*/ | |||||
struct rtentry * | struct rtentry * | ||||
rtalloc1(struct sockaddr *dst, int report, u_long ignflags) | |||||
{ | |||||
return (rtalloc1_fib(dst, report, ignflags, RT_DEFAULT_FIB)); | |||||
} | |||||
struct rtentry * | |||||
rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, | rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, | ||||
u_int fibnum) | u_int fibnum) | ||||
{ | { | ||||
RIB_RLOCK_TRACKER; | RIB_RLOCK_TRACKER; | ||||
struct rib_head *rh; | struct rib_head *rh; | ||||
struct radix_node *rn; | struct radix_node *rn; | ||||
struct rtentry *newrt; | struct rtentry *newrt; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | rtfree(struct rtentry *rt) | ||||
/* | /* | ||||
* The callers should use RTFREE_LOCKED() or RTFREE(), so | * The callers should use RTFREE_LOCKED() or RTFREE(), so | ||||
* we should come here exactly with the last reference. | * we should come here exactly with the last reference. | ||||
*/ | */ | ||||
RT_REMREF(rt); | RT_REMREF(rt); | ||||
if (rt->rt_refcnt > 0) { | if (rt->rt_refcnt > 0) { | ||||
log(LOG_DEBUG, "%s: %p has %d refs\n", __func__, rt, rt->rt_refcnt); | log(LOG_DEBUG, "%s: %p has %d refs\n", __func__, rt, rt->rt_refcnt); | ||||
goto done; | RT_UNLOCK(rt); | ||||
return; | |||||
} | } | ||||
RT_UNLOCK(rt); | |||||
/* Save curvnet */ | |||||
#ifdef VIMAGE | |||||
rt->rt_chain = (struct rtentry *)curvnet; | |||||
#else | |||||
rt->rt_chain = NULL; | |||||
#endif | |||||
epoch_call(net_epoch_preempt, destroy_rtentry_epoch, &rt->rt_epoch_ctx); | |||||
} | |||||
__noinline static void | |||||
destroy_rtentry(struct rtentry *rt) | |||||
{ | |||||
#if 0 | |||||
struct rib_head *rnh; | |||||
#ifdef VIMAGE | |||||
CURVNET_SET((struct vnet *)rt->rt_chain); | |||||
#endif | |||||
rnh = rt_tables_get_rnh(rt->rt_fibnum, rt_key(rt)->sa_family); | |||||
KASSERT(rnh != NULL,("%s: NULL rnh", __func__)); | |||||
/* | /* | ||||
* On last reference give the "close method" a chance | * On last reference give the "close method" a chance | ||||
* to cleanup private state. This also permits (for | * to cleanup private state. This also permits (for | ||||
* IPv4 and IPv6) a chance to decide if the routing table | * IPv4 and IPv6) a chance to decide if the routing table | ||||
* entry should be purged immediately or at a later time. | * entry should be purged immediately or at a later time. | ||||
* When an immediate purge is to happen the close routine | * When an immediate purge is to happen the close routine | ||||
* typically calls rtexpunge which clears the RTF_UP flag | * typically calls rtexpunge which clears the RTF_UP flag | ||||
* on the entry so that the code below reclaims the storage. | * on the entry so that the code below reclaims the storage. | ||||
*/ | */ | ||||
if (rt->rt_refcnt == 0 && rnh->rnh_close) | if (rt->rt_refcnt == 0 && rnh->rnh_close) | ||||
rnh->rnh_close((struct radix_node *)rt, &rnh->head); | rnh->rnh_close((struct radix_node *)rt, &rnh->head); | ||||
#endif | |||||
/* | /* | ||||
* If we are no longer "up" (and ref == 0) | * If we are no longer "up" (and ref == 0) | ||||
* then we can free the resources associated | * then we can free the resources associated | ||||
* with the route. | * with the route. | ||||
*/ | */ | ||||
if ((rt->rt_flags & RTF_UP) == 0) { | if (!RT_IS_UP(rt)) { | ||||
if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | ||||
panic("rtfree 2"); | panic("rtfree 2"); | ||||
/* | /* | ||||
* the rtentry must have been removed from the routing table | * the rtentry must have been removed from the routing table | ||||
* so it is represented in rttrash.. remove that now. | * so it is represented in rttrash.. remove that now. | ||||
*/ | */ | ||||
V_rttrash--; | //V_rttrash--; | ||||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||||
if (rt->rt_refcnt < 0) { | if (rt->rt_refcnt < 0) { | ||||
printf("rtfree: %p not freed (neg refs)\n", rt); | printf("rtfree: %p not freed (neg refs)\n", rt); | ||||
goto done; | CURVNET_RESTORE(); | ||||
return; | |||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* release references on items we hold them on.. | |||||
* e.g other routes and ifaddrs. | |||||
*/ | |||||
if (rt->rt_ifa) | |||||
ifa_free(rt->rt_ifa); | |||||
/* | |||||
* The key is separatly alloc'd so free it (see rt_setgate()). | * The key is separatly alloc'd so free it (see rt_setgate()). | ||||
* This also frees the gateway, as they are always malloc'd | * This also frees the gateway, as they are always malloc'd | ||||
* together. | * together. | ||||
*/ | */ | ||||
R_Free(rt_key(rt)); | if (rt_key(rt) != &rt->rt_dst) | ||||
free(rt_key(rt), M_RTABLE); | |||||
/* | /* | ||||
* and the rtentry itself of course | * and the rtentry itself of course | ||||
*/ | */ | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
return; | |||||
} | } | ||||
done: | |||||
RT_UNLOCK(rt); | //CURVNET_RESTORE(); | ||||
} | } | ||||
/* | /* | ||||
* Epoch callback indicating rtentry is safe to destroy | |||||
*/ | |||||
static void | |||||
destroy_rtentry_epoch(epoch_context_t ctx) | |||||
{ | |||||
struct rtentry *rt; | |||||
rt = __containerof(ctx, struct rtentry, rt_epoch_ctx); | |||||
destroy_rtentry(rt); | |||||
} | |||||
/* | |||||
* Adds a temporal redirect entry to the routing table. | * Adds a temporal redirect entry to the routing table. | ||||
* @fibnum: fib number | * @fibnum: fib number | ||||
* @dst: destination to install redirect to | * @dst: destination to install redirect to | ||||
* @gateway: gateway to go via | * @gateway: gateway to go via | ||||
* @author: sockaddr of originating router, can be NULL | * @author: sockaddr of originating router, can be NULL | ||||
* @ifp: interface to use for the redirected route | * @ifp: interface to use for the redirected route | ||||
* @flags: set of flags to add. Allowed: RTF_GATEWAY | * @flags: set of flags to add. Allowed: RTF_GATEWAY | ||||
* @lifetime_sec: time in seconds to expire this redirect. | * @lifetime_sec: time in seconds to expire this redirect. | ||||
* | * | ||||
* Retuns 0 on success, errno otherwise. | * Retuns 0 on success, errno otherwise. | ||||
*/ | */ | ||||
int | int | ||||
rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway, | rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway, | ||||
struct sockaddr *author, struct ifnet *ifp, int flags, int lifetime_sec) | struct sockaddr *author, struct ifnet *ifp, int flags, int lifetime_sec) | ||||
{ | { | ||||
struct rtentry *rt; | |||||
int error; | int error; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct rib_cmd_info rc; | |||||
struct rt_metrics rti_rmx; | struct rt_metrics rti_rmx; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL) | if (rt_tables_get_rnh(fibnum, dst->sa_family) == NULL) | ||||
return (EAFNOSUPPORT); | return (EAFNOSUPPORT); | ||||
Show All 15 Lines | rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway, | ||||
/* Setup route metrics to define expire time. */ | /* Setup route metrics to define expire time. */ | ||||
bzero(&rti_rmx, sizeof(rti_rmx)); | bzero(&rti_rmx, sizeof(rti_rmx)); | ||||
/* Set expire time as absolute. */ | /* Set expire time as absolute. */ | ||||
rti_rmx.rmx_expire = lifetime_sec + time_second; | rti_rmx.rmx_expire = lifetime_sec + time_second; | ||||
info.rti_mflags |= RTV_EXPIRE; | info.rti_mflags |= RTV_EXPIRE; | ||||
info.rti_rmx = &rti_rmx; | info.rti_rmx = &rti_rmx; | ||||
error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); | error = rib_add_route(fibnum, &info, &rc); | ||||
ifa_free(ifa); | ifa_free(ifa); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* TODO: add per-fib redirect stats. */ | /* TODO: add per-fib redirect stats. */ | ||||
return (error); | return (error); | ||||
} | } | ||||
RT_LOCK(rt); | flags = rib_get_entry_rtflags(rc.rt, rc.nh_new); | ||||
flags = rt->rt_flags; | |||||
RTFREE_LOCKED(rt); | |||||
RTSTAT_INC(rts_dynamic); | RTSTAT_INC(rts_dynamic); | ||||
/* Send notification of a route addition to userland. */ | /* Send notification of a route addition to userland. */ | ||||
bzero(&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_AUTHOR] = author; | info.rti_info[RTAX_AUTHOR] = author; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | if ((flags & RTF_GATEWAY) == 0) { | ||||
* other end of a pt to pt link. | * other end of a pt to pt link. | ||||
*/ | */ | ||||
ifa = ifa_ifwithdstaddr(gateway, fibnum); | ifa = ifa_ifwithdstaddr(gateway, fibnum); | ||||
} | } | ||||
if (ifa == NULL) | if (ifa == NULL) | ||||
ifa = ifa_ifwithnet(gateway, 0, fibnum); | ifa = ifa_ifwithnet(gateway, 0, fibnum); | ||||
if (ifa == NULL) { | if (ifa == NULL) { | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
struct nhop_object *nh; | |||||
rt = rtalloc1_fib(gateway, 0, flags, fibnum); | rt = rtalloc1_fib(gateway, 0, flags, fibnum); | ||||
if (rt == NULL) | if (rt == NULL) | ||||
goto out; | goto out; | ||||
/* | /* | ||||
* dismiss a gateway that is reachable only | * dismiss a gateway that is reachable only | ||||
* through the default router | * through the default router | ||||
*/ | */ | ||||
switch (gateway->sa_family) { | switch (gateway->sa_family) { | ||||
case AF_INET: | case AF_INET: | ||||
if (satosin(rt_key(rt))->sin_addr.s_addr == INADDR_ANY) | if (satosin(rt_key(rt))->sin_addr.s_addr == INADDR_ANY) | ||||
not_found = 1; | not_found = 1; | ||||
break; | break; | ||||
case AF_INET6: | case AF_INET6: | ||||
if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(rt))->sin6_addr)) | if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(rt))->sin6_addr)) | ||||
not_found = 1; | not_found = 1; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
if (!not_found && rt->rt_ifa != NULL) { | nh = RT_SELECT_NHOP(rt, 0); | ||||
ifa = rt->rt_ifa; | if (!not_found && nh->nh_ifa != NULL) { | ||||
ifa = nh->nh_ifa; | |||||
} | } | ||||
RT_REMREF(rt); | RT_REMREF(rt); | ||||
RT_UNLOCK(rt); | RT_UNLOCK(rt); | ||||
if (not_found || ifa == NULL) | if (not_found || ifa == NULL) | ||||
goto out; | goto out; | ||||
} | } | ||||
if (ifa->ifa_addr->sa_family != dst->sa_family) { | if (ifa->ifa_addr->sa_family != dst->sa_family) { | ||||
struct ifaddr *oifa = ifa; | struct ifaddr *oifa = ifa; | ||||
ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); | ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); | ||||
if (ifa == NULL) | if (ifa == NULL) | ||||
ifa = oifa; | ifa = oifa; | ||||
} | } | ||||
out: | out: | ||||
return (ifa); | return (ifa); | ||||
} | } | ||||
/* | /* | ||||
* Do appropriate manipulations of a routing tree given | |||||
* all the bits of info needed | |||||
*/ | |||||
int | |||||
rtrequest_fib(int req, | |||||
struct sockaddr *dst, | |||||
struct sockaddr *gateway, | |||||
struct sockaddr *netmask, | |||||
int flags, | |||||
struct rtentry **ret_nrt, | |||||
u_int fibnum) | |||||
{ | |||||
struct rt_addrinfo info; | |||||
if (dst->sa_len == 0) | |||||
return(EINVAL); | |||||
bzero((caddr_t)&info, sizeof(info)); | |||||
info.rti_flags = flags; | |||||
info.rti_info[RTAX_DST] = dst; | |||||
info.rti_info[RTAX_GATEWAY] = gateway; | |||||
info.rti_info[RTAX_NETMASK] = netmask; | |||||
return rtrequest1_fib(req, &info, ret_nrt, fibnum); | |||||
} | |||||
/* | |||||
* Copy most of @rt data into @info. | * Copy most of @rt data into @info. | ||||
* | * | ||||
* If @flags contains NHR_COPY, copies dst,netmask and gw to the | * If @flags contains NHR_COPY, copies dst,netmask and gw to the | ||||
* pointers specified by @info structure. Assume such pointers | * pointers specified by @info structure. Assume such pointers | ||||
* are zeroed sockaddr-like structures with sa_len field initialized | * are zeroed sockaddr-like structures with sa_len field initialized | ||||
* to reflect size of the provided buffer. if no NHR_COPY is specified, | * to reflect size of the provided buffer. if no NHR_COPY is specified, | ||||
* point dst,netmask and gw @info fields to appropriate @rt values. | * point dst,netmask and gw @info fields to appropriate @rt values. | ||||
* | * | ||||
* if @flags contains NHR_REF, do refcouting on rt_ifp and rt_ifa. | * if @flags contains NHR_REF, do refcouting on rt_ifp and rt_ifa. | ||||
* | * | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
int | int | ||||
rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, int flags) | rt_exportinfo(struct rtentry *rt, struct nhop_object *nh, | ||||
struct rt_addrinfo *info, int flags) | |||||
{ | { | ||||
struct rt_metrics *rmx; | struct rt_metrics *rmx; | ||||
struct sockaddr *src, *dst; | struct sockaddr *src, *dst; | ||||
int sa_len; | int sa_len; | ||||
if (flags & NHR_COPY) { | if (flags & NHR_COPY) { | ||||
/* Copy destination if dst is non-zero */ | /* Copy destination if dst is non-zero */ | ||||
src = rt_key(rt); | src = rt_key(rt); | ||||
Show All 18 Lines | if (src != NULL && dst != NULL) { | ||||
*/ | */ | ||||
if (sa_len > dst->sa_len) | if (sa_len > dst->sa_len) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
memcpy(dst, src, src->sa_len); | memcpy(dst, src, src->sa_len); | ||||
info->rti_addrs |= RTA_NETMASK; | info->rti_addrs |= RTA_NETMASK; | ||||
} | } | ||||
/* Copy gateway is set && dst is non-zero */ | /* Copy gateway is set && dst is non-zero */ | ||||
src = rt->rt_gateway; | src = &nh->gw_sa; | ||||
dst = info->rti_info[RTAX_GATEWAY]; | dst = info->rti_info[RTAX_GATEWAY]; | ||||
if ((rt->rt_flags & RTF_GATEWAY) && src != NULL && dst != NULL){ | if ((nh->nh_flags & NHF_GATEWAY) && src != NULL && dst != NULL){ | ||||
if (src->sa_len > dst->sa_len) | if (src->sa_len > dst->sa_len) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
memcpy(dst, src, src->sa_len); | memcpy(dst, src, src->sa_len); | ||||
info->rti_addrs |= RTA_GATEWAY; | info->rti_addrs |= RTA_GATEWAY; | ||||
} | } | ||||
} else { | } else { | ||||
info->rti_info[RTAX_DST] = rt_key(rt); | info->rti_info[RTAX_DST] = rt_key(rt); | ||||
info->rti_addrs |= RTA_DST; | info->rti_addrs |= RTA_DST; | ||||
if (rt_mask(rt) != NULL) { | if (rt_mask(rt) != NULL) { | ||||
info->rti_info[RTAX_NETMASK] = rt_mask(rt); | info->rti_info[RTAX_NETMASK] = rt_mask(rt); | ||||
info->rti_addrs |= RTA_NETMASK; | info->rti_addrs |= RTA_NETMASK; | ||||
} | } | ||||
if (rt->rt_flags & RTF_GATEWAY) { | if (nh->nh_flags & NHF_GATEWAY) { | ||||
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; | info->rti_info[RTAX_GATEWAY] = &nh->gw_sa; | ||||
info->rti_addrs |= RTA_GATEWAY; | info->rti_addrs |= RTA_GATEWAY; | ||||
} | } | ||||
} | } | ||||
rmx = info->rti_rmx; | rmx = info->rti_rmx; | ||||
if (rmx != NULL) { | if (rmx != NULL) { | ||||
info->rti_mflags |= RTV_MTU; | info->rti_mflags |= RTV_MTU; | ||||
rmx->rmx_mtu = rt->rt_mtu; | rmx->rmx_mtu = nh->nh_mtu; | ||||
} | } | ||||
info->rti_flags = rt->rt_flags; | info->rti_flags = rib_get_entry_rtflags(rt, nh); | ||||
info->rti_ifp = rt->rt_ifp; | info->rti_ifp = nh->nh_ifp; | ||||
info->rti_ifa = rt->rt_ifa; | info->rti_ifa = nh->nh_ifa; | ||||
if (flags & NHR_REF) { | if (flags & NHR_REF) { | ||||
if_ref(info->rti_ifp); | if_ref(info->rti_ifp); | ||||
ifa_ref(info->rti_ifa); | ifa_ref(info->rti_ifa); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 10 Lines | |||||
int | int | ||||
rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags, | rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags, | ||||
uint32_t flowid, struct rt_addrinfo *info) | uint32_t flowid, struct rt_addrinfo *info) | ||||
{ | { | ||||
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 rtentry *rt; | ||||
struct nhop_object *nh; | |||||
int error; | int error; | ||||
KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum")); | KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum")); | ||||
rh = rt_tables_get_rnh(fibnum, dst->sa_family); | rh = rt_tables_get_rnh(fibnum, dst->sa_family); | ||||
if (rh == NULL) | if (rh == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
RIB_RLOCK(rh); | RIB_RLOCK(rh); | ||||
rn = rh->rnh_matchaddr(__DECONST(void *, dst), &rh->head); | rn = rh->rnh_matchaddr(__DECONST(void *, dst), &rh->head); | ||||
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { | ||||
rt = RNTORT(rn); | rt = RNTORT(rn); | ||||
nh = RT_SELECT_NHOP((RNTORT(rn)), flowid); | |||||
/* Ensure route & ifp is UP */ | /* Ensure route & ifp is UP */ | ||||
if (RT_LINK_IS_UP(rt->rt_ifp)) { | if (RT_LINK_IS_UP(nh->nh_ifp)) { | ||||
flags = (flags & NHR_REF) | NHR_COPY; | flags = (flags & NHR_REF) | NHR_COPY; | ||||
error = rt_exportinfo(rt, info, flags); | error = rt_exportinfo(rt, nh, info, flags); | ||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
RIB_RUNLOCK(rh); | RIB_RUNLOCK(rh); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
/* | /* | ||||
* Releases all references acquired by rib_lookup_info() when | * Releases all references acquired by rib_lookup_info() when | ||||
* called with NHR_REF flags. | * called with NHR_REF flags. | ||||
*/ | */ | ||||
void | void | ||||
rib_free_info(struct rt_addrinfo *info) | rib_free_info(struct rt_addrinfo *info) | ||||
{ | { | ||||
ifa_free(info->rti_ifa); | ifa_free(info->rti_ifa); | ||||
if_rele(info->rti_ifp); | if_rele(info->rti_ifp); | ||||
} | } | ||||
/* | /* | ||||
* Iterates over all existing fibs in system calling | * Iterates over all existing fibs in system and deletes each element | ||||
* @setwa_f function prior to traversing each fib. | * for which @filter_f function returns non-zero value. | ||||
* Calls @wa_f function for each element in current fib. | * Calls @wa_f function for each element in current fib. | ||||
* If af is not AF_UNSPEC, iterates over fibs in particular | * If @family is not AF_UNSPEC, iterates over fibs in particular | ||||
* address family. | * address family. | ||||
*/ | */ | ||||
void | void | ||||
rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, | rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, | ||||
void *arg) | void *arg) | ||||
{ | { | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
uint32_t fibnum; | u_int fibnum; | ||||
int i; | int i; | ||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | ||||
/* Do we want some specific family? */ | /* Do we want some specific af? */ | ||||
if (af != AF_UNSPEC) { | if (af != AF_UNSPEC) { | ||||
rnh = rt_tables_get_rnh(fibnum, af); | rnh = rt_tables_get_rnh(fibnum, af); | ||||
if (rnh == NULL) | if (rnh == NULL) | ||||
continue; | continue; | ||||
if (setwa_f != NULL) | if (setwa_f != NULL) | ||||
setwa_f(rnh, fibnum, af, arg); | setwa_f(rnh, fibnum, af, arg); | ||||
RIB_WLOCK(rnh); | RIB_WLOCK(rnh); | ||||
Show All 16 Lines | rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, | ||||
} | } | ||||
} | } | ||||
struct rt_delinfo | struct rt_delinfo | ||||
{ | { | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct rtentry *head; | struct rtentry *head; | ||||
int error_count; | |||||
}; | }; | ||||
#ifdef ROUTE_MPATH | |||||
/* | /* | ||||
* Conditionally unlinks @rn from radix tree based | * Helper function to remove matching paths from multipath route. | ||||
* on info data passed in @arg. | * @rt: prefix rtentry | ||||
* @di: filter function and data | |||||
*/ | */ | ||||
static void | |||||
rt_checkdelroute_mpath(struct rtentry *rt, struct rt_delinfo *di) | |||||
{ | |||||
struct nhop_object *nh_new; | |||||
struct nhop_mpath *mp, *mp_new; | |||||
struct rt_addrinfo *info; | |||||
struct weightened_nhop *wn; | |||||
struct radix_node *rn; | |||||
uint64_t del_mask; | |||||
uint32_t num_nhops, weight_new; | |||||
int count; | |||||
count = 0; | |||||
del_mask = 0; | |||||
info = &di->info; | |||||
nh_new = NULL; | |||||
weight_new = 0; | |||||
mp = (struct nhop_mpath *)rt->rt_nhop; | |||||
wn = mpath_get_nhops(mp, &num_nhops); | |||||
for (uint32_t i = 0; i < num_nhops; i++) { | |||||
if (info->rti_filter.func(rt, wn[i].nh, info->rti_filter.data) != 0) { | |||||
del_mask |= (1 << i); | |||||
count++; | |||||
nh_new = wn[i].nh; | |||||
weight_new = wn[i].weight; | |||||
} | |||||
} | |||||
if (count == 0) { | |||||
/* No matches, just return */ | |||||
return; | |||||
} else if (count == num_nhops) { | |||||
/* Eliminated all paths */ | |||||
rn = di->rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), &di->rnh->head); | |||||
if (rn == NULL) { | |||||
di->error_count++; | |||||
return; | |||||
} | |||||
/* Entry was unlinked. Lock, add to the list and return */ | |||||
RT_LOCK(rt); | |||||
rt->rte_flags &= ~RTF_UP; | |||||
rt->rt_chain = di->head; | |||||
di->head = rt; | |||||
return; | |||||
} else if (count + 1 == num_nhops) { | |||||
/* | |||||
* Eliminated all but one path, so it's not multipath | |||||
* group anymore. | |||||
*/ | |||||
KASSERT((nh_new != NULL), ("nh_new == NULL")); | |||||
/* Switch mpath group to a single nexthop */ | |||||
nhop_ref_object(nh_new); | |||||
RT_LOCK(rt); | |||||
rt->rt_nhop = nh_new; | |||||
/* XXX: violates rte fields immutability */ | |||||
rt->rt_weight = weight_new; | |||||
RT_UNLOCK(rt); | |||||
mpath_free_group(mp); | |||||
return; | |||||
} | |||||
/* | |||||
* The worst case: new nhop group needs to be created, while radix | |||||
* WLOCK is held | |||||
*/ | |||||
mp_new = mpath_get_del_nhops(di->rnh->nh_control, mp, &del_mask); | |||||
if (mp_new == NULL) { | |||||
/* | |||||
* Failed to create new nexthop group, thus | |||||
* route deletion fails. | |||||
* | |||||
* Given that nexhops references all necessary pieces | |||||
* it may be not fatal for the system, however the stale route | |||||
* has to be evicted somehow. Assume the routing daemon will | |||||
* do the housekeeping. | |||||
*/ | |||||
di->error_count++; | |||||
return; | |||||
} else { | |||||
/* Switch to a new mpath group, freeing the old one. */ | |||||
RT_LOCK(rt); | |||||
rt->rt_nhop = (struct nhop_object *)mp_new; | |||||
RT_UNLOCK(rt); | |||||
mpath_free_group(mp); | |||||
} | |||||
return; | |||||
} | |||||
#endif | |||||
static int | static int | ||||
rt_checkdelroute(struct radix_node *rn, void *arg) | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
{ | { | ||||
struct rt_delinfo *di; | struct rt_delinfo *di; | ||||
struct rt_addrinfo *info; | struct rt_addrinfo *info; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
int error; | struct nhop_object *nh; | ||||
struct rib_head *rnh; | |||||
di = (struct rt_delinfo *)arg; | di = (struct rt_delinfo *)arg; | ||||
rt = (struct rtentry *)rn; | rt = (struct rtentry *)rn; | ||||
rnh = di->rnh; | |||||
info = &di->info; | info = &di->info; | ||||
error = 0; | |||||
info->rti_info[RTAX_DST] = rt_key(rt); | nh = rt->rt_nhop; | ||||
info->rti_info[RTAX_NETMASK] = rt_mask(rt); | |||||
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; | |||||
rt = rt_unlinkrte(di->rnh, info, &error); | #ifdef ROUTE_MPATH | ||||
if (rt == NULL) { | if (NH_IS_MULTIPATH(nh)) { | ||||
/* Either not allowed or not matched. Skip entry */ | rt_checkdelroute_mpath(rt, di); | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | |||||
if (info->rti_filter(rt, nh, info->rti_filterdata) == 0) { | |||||
/* Not matched */ | |||||
return (0); | |||||
} | |||||
rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), &rnh->head); | |||||
if (rn == NULL) { | |||||
di->error_count++; | |||||
return (0); | |||||
} | |||||
/* Entry was unlinked. Add to the list and return */ | /* Entry was unlinked. Add to the list and return */ | ||||
RT_LOCK(rt); | |||||
RT_ADDREF(rt); | |||||
rt->rte_flags &= ~RTF_UP; | |||||
rt->rt_chain = di->head; | rt->rt_chain = di->head; | ||||
di->head = rt; | di->head = rt; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Iterates over a routing table specified by @fibnum and @family and | * Iterates over a routing table specified by @fibnum and @family and | ||||
* deletes elements marked by @filter_f. | * deletes elements marked by @filter_f. | ||||
* @fibnum: rtable id | * @fibnum: rtable id | ||||
* @family: AF_ address family | * @family: AF_ address family | ||||
* @filter_f: function returning non-zero value for items to delete | * @filter_f: function returning non-zero value for items to delete | ||||
* @arg: data to pass to the @filter_f function | * @arg: data to pass to the @filter_f function | ||||
* @report: true if rtsock notification is needed. | * @report: true if rtsock notification is needed. | ||||
* | |||||
* Note: currently reporting is NOT supported for multipath routes. | |||||
* The only current customer requiring reporting is temporal routes, | |||||
* which are not multipath. | |||||
*/ | */ | ||||
void | void | ||||
rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report) | 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; | ||||
struct nhop_object *nh; | |||||
rnh = rt_tables_get_rnh(fibnum, family); | rnh = rt_tables_get_rnh(fibnum, family); | ||||
if (rnh == NULL) | if (rnh == NULL) | ||||
return; | 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; | ||||
Show All 11 Lines | 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); | nh = rt->rt_nhop; | ||||
#ifdef ROUTE_MPATH | |||||
if (NH_IS_MULTIPATH(nh)) { | |||||
struct weightened_nhop *wn; | |||||
uint32_t num_nhops; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)rt->rt_nhop, | |||||
&num_nhops); | |||||
for (uint32_t i = 0; i < num_nhops; i++) { | |||||
nh = wn[i].nh; | |||||
rt_notifydelete(rt, nh, &di.info); | |||||
if (report) | if (report) | ||||
rt_routemsg(RTM_DELETE, rt, rt->rt_ifp, 0, fibnum); | rt_routemsg(RTM_DELETE, rt, nh, fibnum); | ||||
} | |||||
nhgrp_free_group((struct nhgrp_object *)rt->rt_nhop); | |||||
} else | |||||
#endif | |||||
{ | |||||
rt_notifydelete(rt, nh, &di.info); | |||||
if (report) | |||||
rt_routemsg(RTM_DELETE, rt, nh, fibnum); | |||||
NH_FREE(nh); | |||||
} | |||||
RTFREE_LOCKED(rt); | RTFREE_LOCKED(rt); | ||||
} | } | ||||
if (di.error_count > 0) | |||||
log(LOG_ERR, "Unable to delete %u route(s) for fib %u in family %d\n", | |||||
di.error_count, fibnum, family); | |||||
} | } | ||||
/* | /* | ||||
* Iterates over all existing fibs in system and deletes each element | * Iterates over all existing fibs in system and deletes each element | ||||
* for which @filter_f function returns non-zero value. | * for which @filter_f function returns non-zero value. | ||||
* If @family is not AF_UNSPEC, iterates over fibs in particular | * If @family is not AF_UNSPEC, iterates over fibs in particular | ||||
* address family. | * address family. | ||||
*/ | */ | ||||
Show All 32 Lines | |||||
* rt pointer to rtentry | * rt pointer to rtentry | ||||
* arg argument passed to rnh->rnh_walktree() - detaching interface | * arg argument passed to rnh->rnh_walktree() - detaching interface | ||||
* | * | ||||
* Returns: | * Returns: | ||||
* 0 successful | * 0 successful | ||||
* errno failed - reason indicated | * errno failed - reason indicated | ||||
*/ | */ | ||||
static int | static int | ||||
rt_ifdelroute(const struct rtentry *rt, void *arg) | rt_ifdelroute(const struct rtentry *rt, const struct nhop_object *nh, void *arg) | ||||
{ | { | ||||
struct ifnet *ifp = arg; | struct ifnet *ifp = arg; | ||||
if (rt->rt_ifp != ifp) | if (nh->nh_ifp != ifp) | ||||
return (0); | return (0); | ||||
/* | /* | ||||
* Protect (sorta) against walktree recursion problems | * Protect (sorta) against walktree recursion problems | ||||
* with cloned routes | * with cloned routes | ||||
*/ | */ | ||||
if ((rt->rt_flags & RTF_UP) == 0) | if (!RT_IS_UP(rt)) | ||||
return (0); | return (0); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* Delete all remaining routes using this interface | * Delete all remaining routes using this interface | ||||
* Unfortuneatly the only way to do this is to slog through | * Unfortuneatly the only way to do this is to slog through | ||||
Show All 11 Lines | |||||
void | void | ||||
rt_flushifroutes(struct ifnet *ifp) | rt_flushifroutes(struct ifnet *ifp) | ||||
{ | { | ||||
rt_foreach_fib_walk_del(AF_UNSPEC, rt_ifdelroute, ifp); | rt_foreach_fib_walk_del(AF_UNSPEC, rt_ifdelroute, ifp); | ||||
} | } | ||||
/* | |||||
* Conditionally unlinks rtentry matching data inside @info from @rnh. | |||||
* Returns unlinked, locked and referenced @rtentry on success, | |||||
* Returns NULL and sets @perror to: | |||||
* ESRCH - if prefix was not found, | |||||
* EADDRINUSE - if trying to delete PINNED route without appropriate flag. | |||||
* ENOENT - if supplied filter function returned 0 (not matched). | |||||
*/ | |||||
static struct rtentry * | |||||
rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, int *perror) | |||||
{ | |||||
struct sockaddr *dst, *netmask; | |||||
struct rtentry *rt; | |||||
struct radix_node *rn; | |||||
dst = info->rti_info[RTAX_DST]; | |||||
netmask = info->rti_info[RTAX_NETMASK]; | |||||
rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head); | |||||
if (rt == NULL) { | |||||
*perror = ESRCH; | |||||
return (NULL); | |||||
} | |||||
if ((info->rti_flags & RTF_PINNED) == 0) { | |||||
/* Check if target route can be deleted */ | |||||
if (rt->rt_flags & RTF_PINNED) { | |||||
*perror = EADDRINUSE; | |||||
return (NULL); | |||||
} | |||||
} | |||||
if (info->rti_filter != NULL) { | |||||
if (info->rti_filter(rt, info->rti_filterdata) == 0) { | |||||
/* Not matched */ | |||||
*perror = ENOENT; | |||||
return (NULL); | |||||
} | |||||
/* | |||||
* Filter function requested rte deletion. | |||||
* Ease the caller work by filling in remaining info | |||||
* from that particular entry. | |||||
*/ | |||||
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; | |||||
} | |||||
/* | |||||
* Remove the item from the tree and return it. | |||||
* Complain if it is not there and do no more processing. | |||||
*/ | |||||
*perror = ESRCH; | |||||
#ifdef RADIX_MPATH | |||||
if (rt_mpath_capable(rnh)) | |||||
rn = rt_mpath_unlink(rnh, info, rt, perror); | |||||
else | |||||
#endif | |||||
rn = rnh->rnh_deladdr(dst, netmask, &rnh->head); | |||||
if (rn == NULL) | |||||
return (NULL); | |||||
if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | |||||
panic ("rtrequest delete"); | |||||
rt = RNTORT(rn); | |||||
RT_LOCK(rt); | |||||
RT_ADDREF(rt); | |||||
rt->rt_flags &= ~RTF_UP; | |||||
*perror = 0; | |||||
return (rt); | |||||
} | |||||
static void | static void | ||||
rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info) | rt_notifydelete(struct rtentry *rt, struct nhop_object *nh, struct rt_addrinfo *info) | ||||
{ | { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
/* | /* | ||||
* give the protocol a chance to keep things in sync. | * give the protocol a chance to keep things in sync. | ||||
*/ | */ | ||||
ifa = rt->rt_ifa; | ifa = nh->nh_ifa; | ||||
if (ifa != NULL && ifa->ifa_rtrequest != NULL) | if (ifa != NULL && ifa->ifa_rtrequest != NULL) | ||||
ifa->ifa_rtrequest(RTM_DELETE, rt, info); | ifa->ifa_rtrequest(RTM_DELETE, rt, nh, info); | ||||
/* | /* | ||||
* One more rtentry floating around that is not | * One more rtentry floating around that is not | ||||
* linked to the routing table. rttrash will be decremented | * linked to the routing table. rttrash will be decremented | ||||
* when RTFREE(rt) is eventually called. | * when RTFREE(rt) is eventually called. | ||||
*/ | */ | ||||
/* XXX: pcpu? */ | |||||
V_rttrash++; | V_rttrash++; | ||||
} | } | ||||
/* | /* | ||||
* These (questionable) definitions of apparent local variables apply | * These (questionable) definitions of apparent local variables apply | ||||
* to the next two functions. XXXXXX!!! | * to the next two functions. XXXXXX!!! | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | if (info->rti_ifp == NULL) | ||||
info->rti_ifp = info->rti_ifa->ifa_ifp; | info->rti_ifp = info->rti_ifa->ifa_ifp; | ||||
ifa_ref(info->rti_ifa); | ifa_ref(info->rti_ifa); | ||||
} else | } else | ||||
error = ENETUNREACH; | error = ENETUNREACH; | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | |||||
if_updatemtu_cb(struct radix_node *rn, void *arg) | |||||
{ | |||||
struct rtentry *rt; | |||||
struct if_mtuinfo *ifmtu; | |||||
rt = (struct rtentry *)rn; | |||||
ifmtu = (struct if_mtuinfo *)arg; | |||||
if (rt->rt_ifp != ifmtu->ifp) | |||||
return (0); | |||||
if (rt->rt_mtu >= ifmtu->mtu) { | |||||
/* We have to decrease mtu regardless of flags */ | |||||
rt->rt_mtu = ifmtu->mtu; | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* New MTU is bigger. Check if are allowed to alter it | * Updates transmit mtu for all routes using interface @ifp. | ||||
*/ | */ | ||||
if ((rt->rt_flags & (RTF_FIXEDMTU | RTF_GATEWAY | RTF_HOST)) != 0) { | |||||
/* | |||||
* Skip routes with user-supplied MTU and | |||||
* non-interface routes | |||||
*/ | |||||
return (0); | |||||
} | |||||
/* We are safe to update route MTU */ | |||||
rt->rt_mtu = ifmtu->mtu; | |||||
return (0); | |||||
} | |||||
void | void | ||||
rt_updatemtu(struct ifnet *ifp) | rt_updatemtu(struct ifnet *ifp) | ||||
{ | { | ||||
struct if_mtuinfo ifmtu; | |||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
uint32_t mtu; | |||||
int i, j; | int i, j; | ||||
ifmtu.ifp = ifp; | |||||
/* | /* | ||||
* Try to update rt_mtu for all routes using this interface | * Try to update rt_mtu for all routes using this interface | ||||
* Unfortunately the only way to do this is to traverse all | * Unfortunately the only way to do this is to traverse all | ||||
* routing tables in all fibs/domains. | * routing tables in all fibs/domains. | ||||
*/ | */ | ||||
for (i = 1; i <= AF_MAX; i++) { | for (i = 1; i <= AF_MAX; i++) { | ||||
ifmtu.mtu = if_getmtu_family(ifp, i); | mtu = if_getmtu_family(ifp, i); | ||||
for (j = 0; j < rt_numfibs; j++) { | for (j = 0; j < rt_numfibs; j++) { | ||||
rnh = rt_tables_get_rnh(j, i); | rnh = rt_tables_get_rnh(j, i); | ||||
if (rnh == NULL) | if (rnh == NULL) | ||||
continue; | continue; | ||||
RIB_WLOCK(rnh); | nhops_update_ifmtu(rnh, ifp, mtu); | ||||
rnh->rnh_walktree(&rnh->head, if_updatemtu_cb, &ifmtu); | |||||
RIB_WUNLOCK(rnh); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
#if 0 | |||||
int p_sockaddr(char *buf, int buflen, struct sockaddr *s); | |||||
int rt_print(char *buf, int buflen, struct rtentry *rt); | |||||
int | int | ||||
p_sockaddr(char *buf, int buflen, struct sockaddr *s) | p_sockaddr(char *buf, int buflen, const struct sockaddr *s, int family) | ||||
{ | { | ||||
void *paddr = NULL; | const void *paddr = NULL; | ||||
switch (s->sa_family) { | switch (family) { | ||||
case AF_INET: | case AF_INET: | ||||
paddr = &((struct sockaddr_in *)s)->sin_addr; | paddr = &((const struct sockaddr_in *)s)->sin_addr; | ||||
break; | break; | ||||
case AF_INET6: | case AF_INET6: | ||||
paddr = &((struct sockaddr_in6 *)s)->sin6_addr; | paddr = &((const struct sockaddr_in6 *)s)->sin6_addr; | ||||
break; | break; | ||||
} | } | ||||
if (paddr == NULL) | if (paddr == NULL) | ||||
return (0); | return (0); | ||||
if (inet_ntop(s->sa_family, paddr, buf, buflen) == NULL) | if (inet_ntop(family, paddr, buf, buflen) == NULL) | ||||
return (0); | return (0); | ||||
return (strlen(buf)); | return (strlen(buf)); | ||||
} | } | ||||
int | int | ||||
rt_print(char *buf, int buflen, struct rtentry *rt) | rt_print(char *buf, int buflen, const struct rtentry *rt) | ||||
{ | { | ||||
struct sockaddr *addr, *mask; | const struct sockaddr *addr, *mask; | ||||
int i = 0; | int i = 0; | ||||
int family; | |||||
addr = rt_key(rt); | addr = rt_key_const(rt); | ||||
mask = rt_mask(rt); | mask = rt_mask_const(rt); | ||||
family = addr->sa_family; | |||||
i = p_sockaddr(buf, buflen, addr); | i = p_sockaddr(buf, buflen, addr, family); | ||||
if (!(rt->rt_flags & RTF_HOST)) { | if (!(rt->rte_flags & RTF_HOST)) { | ||||
buf[i++] = '/'; | buf[i++] = '/'; | ||||
i += p_sockaddr(buf + i, buflen - i, mask); | i += p_sockaddr(buf + i, buflen - i, mask, family); | ||||
} | } | ||||
#if 0 | |||||
if (rt->rt_flags & RTF_GATEWAY) { | if (rt->rt_flags & RTF_GATEWAY) { | ||||
buf[i++] = '>'; | buf[i++] = '>'; | ||||
i += p_sockaddr(buf + i, buflen - i, rt->rt_gateway); | i += p_sockaddr(buf + i, buflen - i, rt->rt_gateway, family); | ||||
} | } | ||||
#endif | |||||
return (i); | return (i); | ||||
} | } | ||||
#endif | |||||
#ifdef RADIX_MPATH | #ifdef RADIX_MPATH | ||||
/* | /* | ||||
* Deletes key for single-path routes, unlinks rtentry with | * Deletes key for single-path routes, unlinks rtentry with | ||||
* gateway specified in @info from multi-path routes. | * gateway specified in @info from multi-path routes. | ||||
* | * | ||||
* Returnes unlinked entry. In case of failure, returns NULL | * Returnes unlinked entry. In case of failure, returns NULL | ||||
* and sets @perror to ESRCH. | * and sets @perror to ESRCH. | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | rt_mpath_unlink(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
if (rt_mpath_deldup(rto, rt) == 0) | if (rt_mpath_deldup(rto, rt) == 0) | ||||
panic ("rtrequest1: rt_mpath_deldup"); | panic ("rtrequest1: rt_mpath_deldup"); | ||||
*perror = 0; | *perror = 0; | ||||
rn = (struct radix_node *)rt; | rn = (struct radix_node *)rt; | ||||
return (rn); | return (rn); | ||||
} | } | ||||
#endif | #endif | ||||
int | |||||
rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, | |||||
u_int fibnum) | |||||
{ | |||||
int error = 0; | |||||
struct rtentry *rt, *rt_old; | |||||
struct radix_node *rn; | |||||
struct rib_head *rnh; | |||||
struct ifaddr *ifa; | |||||
struct sockaddr *ndst; | |||||
struct sockaddr_storage mdst; | |||||
KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); | |||||
KASSERT((flags & RTF_RNH_LOCKED) == 0, ("rtrequest1_fib: locked")); | |||||
switch (dst->sa_family) { | |||||
case AF_INET6: | |||||
case AF_INET: | |||||
/* We support multiple FIBs. */ | |||||
break; | |||||
default: | |||||
fibnum = RT_DEFAULT_FIB; | |||||
break; | |||||
} | |||||
/* | |||||
* Find the correct routing tree to use for this Address Family | |||||
*/ | |||||
rnh = rt_tables_get_rnh(fibnum, dst->sa_family); | |||||
if (rnh == NULL) | |||||
return (EAFNOSUPPORT); | |||||
/* | |||||
* If we are adding a host route then we don't want to put | |||||
* a netmask in the tree, nor do we want to clone it. | |||||
*/ | |||||
if (flags & RTF_HOST) | |||||
netmask = NULL; | |||||
switch (req) { | |||||
case RTM_DELETE: | |||||
if (netmask) { | |||||
if (dst->sa_len > sizeof(mdst)) | |||||
return (EINVAL); | |||||
rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask); | |||||
dst = (struct sockaddr *)&mdst; | |||||
} | |||||
RIB_WLOCK(rnh); | |||||
rt = rt_unlinkrte(rnh, info, &error); | |||||
RIB_WUNLOCK(rnh); | |||||
if (error != 0) | |||||
return (error); | |||||
rt_notifydelete(rt, info); | |||||
/* | |||||
* If the caller wants it, then it can have it, | |||||
* but it's up to it to free the rtentry as we won't be | |||||
* doing it. | |||||
*/ | |||||
if (ret_nrt) { | |||||
*ret_nrt = rt; | |||||
RT_UNLOCK(rt); | |||||
} else | |||||
RTFREE_LOCKED(rt); | |||||
break; | |||||
case RTM_RESOLVE: | |||||
/* | |||||
* resolve was only used for route cloning | |||||
* here for compat | |||||
*/ | |||||
break; | |||||
case RTM_ADD: | |||||
if ((flags & RTF_GATEWAY) && !gateway) | |||||
return (EINVAL); | |||||
if (dst && gateway && (dst->sa_family != gateway->sa_family) && | |||||
(gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) | |||||
return (EINVAL); | |||||
if (info->rti_ifa == NULL) { | |||||
error = rt_getifa_fib(info, fibnum); | |||||
if (error) | |||||
return (error); | |||||
} else { | |||||
ifa_ref(info->rti_ifa); | |||||
} | |||||
rt = uma_zalloc(V_rtzone, M_NOWAIT); | |||||
if (rt == NULL) { | |||||
ifa_free(info->rti_ifa); | |||||
return (ENOBUFS); | |||||
} | |||||
rt->rt_flags = RTF_UP | flags; | |||||
rt->rt_fibnum = fibnum; | |||||
/* | |||||
* Add the gateway. Possibly re-malloc-ing the storage for it. | |||||
*/ | |||||
if ((error = rt_setgate(rt, dst, gateway)) != 0) { | |||||
ifa_free(info->rti_ifa); | |||||
uma_zfree(V_rtzone, rt); | |||||
return (error); | |||||
} | |||||
/* | |||||
* point to the (possibly newly malloc'd) dest address. | |||||
*/ | |||||
ndst = (struct sockaddr *)rt_key(rt); | |||||
/* | |||||
* make sure it contains the value we want (masked if needed). | |||||
*/ | |||||
if (netmask) { | |||||
rt_maskedcopy(dst, ndst, netmask); | |||||
} else | |||||
bcopy(dst, ndst, dst->sa_len); | |||||
/* | |||||
* We use the ifa reference returned by rt_getifa_fib(). | |||||
* This moved from below so that rnh->rnh_addaddr() can | |||||
* examine the ifa and ifa->ifa_ifp if it so desires. | |||||
*/ | |||||
ifa = info->rti_ifa; | |||||
rt->rt_ifa = ifa; | |||||
rt->rt_ifp = ifa->ifa_ifp; | |||||
rt->rt_weight = 1; | |||||
rt_setmetrics(info, rt); | |||||
RIB_WLOCK(rnh); | |||||
RT_LOCK(rt); | |||||
#ifdef RADIX_MPATH | |||||
/* do not permit exactly the same dst/mask/gw pair */ | |||||
if (rt_mpath_capable(rnh) && | |||||
rt_mpath_conflict(rnh, rt, netmask)) { | |||||
RIB_WUNLOCK(rnh); | |||||
ifa_free(rt->rt_ifa); | |||||
R_Free(rt_key(rt)); | |||||
uma_zfree(V_rtzone, rt); | |||||
return (EEXIST); | |||||
} | |||||
#endif | |||||
/* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ | |||||
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; | |||||
if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) { | |||||
/* | |||||
* Force removal and re-try addition | |||||
* TODO: better multipath&pinned support | |||||
*/ | |||||
struct sockaddr *info_dst = info->rti_info[RTAX_DST]; | |||||
info->rti_info[RTAX_DST] = ndst; | |||||
/* Do not delete existing PINNED(interface) routes */ | |||||
info->rti_flags &= ~RTF_PINNED; | |||||
rt_old = rt_unlinkrte(rnh, info, &error); | |||||
info->rti_flags |= RTF_PINNED; | |||||
info->rti_info[RTAX_DST] = info_dst; | |||||
if (rt_old != NULL) | |||||
rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, | |||||
rt->rt_nodes); | |||||
} | |||||
RIB_WUNLOCK(rnh); | |||||
if (rt_old != NULL) | |||||
RT_UNLOCK(rt_old); | |||||
/* | |||||
* If it still failed to go into the tree, | |||||
* then un-make it (this should be a function) | |||||
*/ | |||||
if (rn == NULL) { | |||||
ifa_free(rt->rt_ifa); | |||||
R_Free(rt_key(rt)); | |||||
uma_zfree(V_rtzone, rt); | |||||
return (EEXIST); | |||||
} | |||||
if (rt_old != NULL) { | |||||
rt_notifydelete(rt_old, info); | |||||
RTFREE(rt_old); | |||||
} | |||||
/* | |||||
* If this protocol has something to add to this then | |||||
* allow it to do that as well. | |||||
*/ | |||||
if (ifa->ifa_rtrequest) | |||||
ifa->ifa_rtrequest(req, rt, info); | |||||
/* | |||||
* actually return a resultant rtentry and | |||||
* give the caller a single reference. | |||||
*/ | |||||
if (ret_nrt) { | |||||
*ret_nrt = rt; | |||||
RT_ADDREF(rt); | |||||
} | |||||
rnh->rnh_gen++; /* Routing table updated */ | |||||
RT_UNLOCK(rt); | |||||
break; | |||||
case RTM_CHANGE: | |||||
RIB_WLOCK(rnh); | |||||
error = rtrequest1_fib_change(rnh, info, ret_nrt, fibnum); | |||||
RIB_WUNLOCK(rnh); | |||||
break; | |||||
default: | |||||
error = EOPNOTSUPP; | |||||
} | |||||
return (error); | |||||
} | |||||
#undef dst | #undef dst | ||||
#undef gateway | #undef gateway | ||||
#undef netmask | #undef netmask | ||||
#undef ifaaddr | #undef ifaaddr | ||||
#undef ifpaddr | #undef ifpaddr | ||||
#undef flags | #undef flags | ||||
static int | |||||
rtrequest1_fib_change(struct rib_head *rnh, struct rt_addrinfo *info, | |||||
struct rtentry **ret_nrt, u_int fibnum) | |||||
{ | |||||
struct rtentry *rt = NULL; | |||||
int error = 0; | |||||
int free_ifa = 0; | |||||
int family, mtu; | |||||
struct if_mtuinfo ifmtu; | |||||
RIB_WLOCK_ASSERT(rnh); | |||||
rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], | |||||
info->rti_info[RTAX_NETMASK], &rnh->head); | |||||
if (rt == NULL) | |||||
return (ESRCH); | |||||
#ifdef RADIX_MPATH | |||||
/* | |||||
* If we got multipath routes, | |||||
* we require users to specify a matching RTAX_GATEWAY. | |||||
*/ | |||||
if (rt_mpath_capable(rnh)) { | |||||
rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); | |||||
if (rt == NULL) | |||||
return (ESRCH); | |||||
} | |||||
#endif | |||||
RT_LOCK(rt); | |||||
rt_setmetrics(info, rt); | |||||
/* | |||||
* New gateway could require new ifaddr, ifp; | |||||
* flags may also be different; ifp may be specified | |||||
* by ll sockaddr when protocol address is ambiguous | |||||
*/ | |||||
if (((rt->rt_flags & RTF_GATEWAY) && | |||||
info->rti_info[RTAX_GATEWAY] != NULL) || | |||||
info->rti_info[RTAX_IFP] != NULL || | |||||
(info->rti_info[RTAX_IFA] != NULL && | |||||
!sa_equal(info->rti_info[RTAX_IFA], rt->rt_ifa->ifa_addr))) { | |||||
/* | |||||
* XXX: Temporarily set RTF_RNH_LOCKED flag in the rti_flags | |||||
* to avoid rlock in the ifa_ifwithroute(). | |||||
*/ | |||||
info->rti_flags |= RTF_RNH_LOCKED; | |||||
error = rt_getifa_fib(info, fibnum); | |||||
info->rti_flags &= ~RTF_RNH_LOCKED; | |||||
if (info->rti_ifa != NULL) | |||||
free_ifa = 1; | |||||
if (error != 0) | |||||
goto bad; | |||||
} | |||||
/* Check if outgoing interface has changed */ | |||||
if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa && | |||||
rt->rt_ifa != NULL) { | |||||
if (rt->rt_ifa->ifa_rtrequest != NULL) | |||||
rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, info); | |||||
ifa_free(rt->rt_ifa); | |||||
rt->rt_ifa = NULL; | |||||
} | |||||
/* Update gateway address */ | |||||
if (info->rti_info[RTAX_GATEWAY] != NULL) { | |||||
error = rt_setgate(rt, rt_key(rt), info->rti_info[RTAX_GATEWAY]); | |||||
if (error != 0) | |||||
goto bad; | |||||
rt->rt_flags &= ~RTF_GATEWAY; | |||||
rt->rt_flags |= (RTF_GATEWAY & info->rti_flags); | |||||
} | |||||
if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa) { | |||||
ifa_ref(info->rti_ifa); | |||||
rt->rt_ifa = info->rti_ifa; | |||||
rt->rt_ifp = info->rti_ifp; | |||||
} | |||||
/* Allow some flags to be toggled on change. */ | |||||
rt->rt_flags &= ~RTF_FMASK; | |||||
rt->rt_flags |= info->rti_flags & RTF_FMASK; | |||||
if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest != NULL) | |||||
rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info); | |||||
/* Alter route MTU if necessary */ | |||||
if (rt->rt_ifp != NULL) { | |||||
family = info->rti_info[RTAX_DST]->sa_family; | |||||
mtu = if_getmtu_family(rt->rt_ifp, family); | |||||
/* Set default MTU */ | |||||
if (rt->rt_mtu == 0) | |||||
rt->rt_mtu = mtu; | |||||
if (rt->rt_mtu != mtu) { | |||||
/* Check if we really need to update */ | |||||
ifmtu.ifp = rt->rt_ifp; | |||||
ifmtu.mtu = mtu; | |||||
if_updatemtu_cb(rt->rt_nodes, &ifmtu); | |||||
} | |||||
} | |||||
/* | |||||
* This route change may have modified the route's gateway. In that | |||||
* case, any inpcbs that have cached this route need to invalidate their | |||||
* llentry cache. | |||||
*/ | |||||
rnh->rnh_gen++; | |||||
if (ret_nrt) { | |||||
*ret_nrt = rt; | |||||
RT_ADDREF(rt); | |||||
} | |||||
bad: | |||||
RT_UNLOCK(rt); | |||||
if (free_ifa != 0) { | |||||
ifa_free(info->rti_ifa); | |||||
info->rti_ifa = NULL; | |||||
} | |||||
return (error); | |||||
} | |||||
static void | |||||
rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt) | |||||
{ | |||||
if (info->rti_mflags & RTV_MTU) { | |||||
if (info->rti_rmx->rmx_mtu != 0) { | |||||
/* | |||||
* MTU was explicitly provided by user. | |||||
* Keep it. | |||||
*/ | |||||
rt->rt_flags |= RTF_FIXEDMTU; | |||||
} else { | |||||
/* | |||||
* User explicitly sets MTU to 0. | |||||
* Assume rollback to default. | |||||
*/ | |||||
rt->rt_flags &= ~RTF_FIXEDMTU; | |||||
} | |||||
rt->rt_mtu = info->rti_rmx->rmx_mtu; | |||||
} | |||||
if (info->rti_mflags & RTV_WEIGHT) | |||||
rt->rt_weight = info->rti_rmx->rmx_weight; | |||||
/* Kernel -> userland timebase conversion. */ | |||||
if (info->rti_mflags & RTV_EXPIRE) | |||||
rt->rt_expire = info->rti_rmx->rmx_expire ? | |||||
info->rti_rmx->rmx_expire - time_second + time_uptime : 0; | |||||
} | |||||
int | |||||
rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) | |||||
{ | |||||
/* XXX dst may be overwritten, can we move this to below */ | |||||
int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); | |||||
/* | |||||
* Prepare to store the gateway in rt->rt_gateway. | |||||
* Both dst and gateway are stored one after the other in the same | |||||
* malloc'd chunk. If we have room, we can reuse the old buffer, | |||||
* rt_gateway already points to the right place. | |||||
* Otherwise, malloc a new block and update the 'dst' address. | |||||
*/ | |||||
if (rt->rt_gateway == NULL || glen > SA_SIZE(rt->rt_gateway)) { | |||||
caddr_t new; | |||||
R_Malloc(new, caddr_t, dlen + glen); | |||||
if (new == NULL) | |||||
return ENOBUFS; | |||||
/* | |||||
* XXX note, we copy from *dst and not *rt_key(rt) because | |||||
* rt_setgate() can be called to initialize a newly | |||||
* allocated route entry, in which case rt_key(rt) == NULL | |||||
* (and also rt->rt_gateway == NULL). | |||||
* Free()/free() handle a NULL argument just fine. | |||||
*/ | |||||
bcopy(dst, new, dlen); | |||||
R_Free(rt_key(rt)); /* free old block, if any */ | |||||
rt_key(rt) = (struct sockaddr *)new; | |||||
rt->rt_gateway = (struct sockaddr *)(new + dlen); | |||||
} | |||||
/* | |||||
* Copy the new gateway value into the memory chunk. | |||||
*/ | |||||
bcopy(gate, rt->rt_gateway, glen); | |||||
return (0); | |||||
} | |||||
void | void | ||||
rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) | rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) | ||||
{ | { | ||||
u_char *cp1 = (u_char *)src; | u_char *cp1 = (u_char *)src; | ||||
u_char *cp2 = (u_char *)dst; | u_char *cp2 = (u_char *)dst; | ||||
u_char *cp3 = (u_char *)netmask; | u_char *cp3 = (u_char *)netmask; | ||||
u_char *cplim = cp2 + *cp3; | u_char *cplim = cp2 + *cp3; | ||||
u_char *cplim2 = cp2 + *cp1; | u_char *cplim2 = cp2 + *cp1; | ||||
Show All 14 Lines | |||||
*/ | */ | ||||
#define _SOCKADDR_TMPSIZE 128 /* Not too big.. kernel stack size is limited */ | #define _SOCKADDR_TMPSIZE 128 /* Not too big.. kernel stack size is limited */ | ||||
static inline int | static inline int | ||||
rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) | rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) | ||||
{ | { | ||||
RIB_RLOCK_TRACKER; | RIB_RLOCK_TRACKER; | ||||
struct sockaddr *dst; | struct sockaddr *dst; | ||||
struct sockaddr *netmask; | struct sockaddr *netmask; | ||||
struct rtentry *rt = NULL; | |||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
int error = 0; | int error = 0; | ||||
int startfib, endfib; | int startfib, endfib; | ||||
char tempbuf[_SOCKADDR_TMPSIZE]; | char tempbuf[_SOCKADDR_TMPSIZE]; | ||||
int didwork = 0; | int didwork = 0; | ||||
int a_failure = 0; | int a_failure = 0; | ||||
struct sockaddr_dl *sdl = NULL; | struct sockaddr_dl *sdl = NULL; | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct epoch_tracker et; | |||||
if (flags & RTF_HOST) { | if (flags & RTF_HOST) { | ||||
dst = ifa->ifa_dstaddr; | dst = ifa->ifa_dstaddr; | ||||
netmask = NULL; | netmask = NULL; | ||||
} else { | } else { | ||||
dst = ifa->ifa_addr; | dst = ifa->ifa_addr; | ||||
netmask = ifa->ifa_netmask; | netmask = ifa->ifa_netmask; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | #ifdef RADIX_MPATH | ||||
*/ | */ | ||||
rt = rt_mpath_matchgate(rt, | rt = rt_mpath_matchgate(rt, | ||||
ifa->ifa_addr); | ifa->ifa_addr); | ||||
if (rt == NULL) | if (rt == NULL) | ||||
error = ESRCH; | error = ESRCH; | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
error = (rn == NULL || | error = 0; | ||||
(rn->rn_flags & RNF_ROOT) || | struct nhop_object *nh; | ||||
RNTORT(rn)->rt_ifa != ifa); | if (rn == NULL || (rn->rn_flags & RNF_ROOT)) | ||||
error = 1; | |||||
else { | |||||
nh = RNTORT(rn)->rt_nhop; | |||||
if (NH_IS_MULTIPATH(nh) || nh->nh_ifa != ifa) | |||||
error = 1; | |||||
} | |||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
if (error) { | if (error) { | ||||
/* this is only an error if bad on ALL tables */ | /* this is only an error if bad on ALL tables */ | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Do the actual request | * Do the actual request | ||||
*/ | */ | ||||
bzero((caddr_t)&info, sizeof(info)); | bzero((caddr_t)&info, sizeof(info)); | ||||
info.rti_ifa = ifa; | info.rti_ifa = ifa; | ||||
info.rti_flags = flags | | info.rti_flags = flags | | ||||
(ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; | (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; | ||||
info.rti_info[RTAX_DST] = dst; | info.rti_info[RTAX_DST] = dst; | ||||
info.rti_info[RTAX_NETMASK] = netmask; | |||||
/* | /* | ||||
* doing this for compatibility reasons | * doing this for compatibility reasons | ||||
*/ | */ | ||||
if (cmd == RTM_ADD) | struct rib_cmd_info rc; | ||||
bzero(&rc, sizeof(rc)); | |||||
NET_EPOCH_ENTER(et); | |||||
if (cmd == RTM_ADD) { | |||||
info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)sdl; | info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)sdl; | ||||
else | error = rib_add_route(fibnum, &info, &rc); | ||||
info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; | if (error == 0) { | ||||
info.rti_info[RTAX_NETMASK] = netmask; | rt_addrmsg(cmd, ifa, fibnum); | ||||
error = rtrequest1_fib(cmd, &info, &rt, fibnum); | rt_routemsg(cmd, rc.rt, rc.nh_new, fibnum); | ||||
if (error == 0 && rt != NULL) { | |||||
/* | |||||
* notify any listening routing agents of the change | |||||
*/ | |||||
RT_LOCK(rt); | |||||
#ifdef RADIX_MPATH | |||||
/* | |||||
* in case address alias finds the first address | |||||
* e.g. ifconfig bge0 192.0.2.246/24 | |||||
* e.g. ifconfig bge0 192.0.2.247/24 | |||||
* the address set in the route is 192.0.2.246 | |||||
* so we need to replace it with 192.0.2.247 | |||||
*/ | |||||
if (memcmp(rt->rt_ifa->ifa_addr, | |||||
ifa->ifa_addr, ifa->ifa_addr->sa_len)) { | |||||
ifa_free(rt->rt_ifa); | |||||
ifa_ref(ifa); | |||||
rt->rt_ifp = ifa->ifa_ifp; | |||||
rt->rt_ifa = ifa; | |||||
} | } | ||||
#endif | |||||
RT_ADDREF(rt); | |||||
RT_UNLOCK(rt); | |||||
rt_newaddrmsg_fib(cmd, ifa, rt, fibnum); | |||||
RT_LOCK(rt); | |||||
RT_REMREF(rt); | |||||
if (cmd == RTM_DELETE) { | |||||
/* | |||||
* If we are deleting, and we found an entry, | |||||
* then it's been removed from the tree.. | |||||
* now throw it away. | |||||
*/ | |||||
RTFREE_LOCKED(rt); | |||||
} else { | } else { | ||||
if (cmd == RTM_ADD) { | info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; | ||||
/* | error = rib_del_route(fibnum, &info, &rc); | ||||
* We just wanted to add it.. | if (error == 0) { | ||||
* we don't actually need a reference. | rt_routemsg(cmd, rc.rt, rc.nh_old, fibnum); | ||||
*/ | rt_addrmsg(cmd, ifa, fibnum); | ||||
RT_REMREF(rt); | |||||
} | } | ||||
RT_UNLOCK(rt); | |||||
} | } | ||||
NET_EPOCH_EXIT(et); | |||||
if (error == 0) | |||||
didwork = 1; | didwork = 1; | ||||
} | else | ||||
if (error) | |||||
a_failure = error; | a_failure = error; | ||||
} | } | ||||
if (cmd == RTM_DELETE) { | if (cmd == RTM_DELETE) { | ||||
if (didwork) { | if (didwork) { | ||||
error = 0; | error = 0; | ||||
} else { | } else { | ||||
/* we only give an error if it wasn't in any table */ | /* we only give an error if it wasn't in any table */ | ||||
error = ((flags & RTF_HOST) ? | error = ((flags & RTF_HOST) ? | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | rt_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) | ||||
EVENTHANDLER_DIRECT_INVOKE(rt_addrmsg, ifa, cmd); | EVENTHANDLER_DIRECT_INVOKE(rt_addrmsg, ifa, cmd); | ||||
return (rtsock_addrmsg(cmd, ifa, fibnum)); | return (rtsock_addrmsg(cmd, ifa, fibnum)); | ||||
} | } | ||||
/* | /* | ||||
* Announce kernel-originated route addition/removal to rtsock based on @rt data. | * Announce kernel-originated route addition/removal to rtsock based on @rt data. | ||||
* cmd: RTM_ cmd | * cmd: RTM_ cmd | ||||
* @rt: valid rtentry | * @rt: valid rtentry | ||||
* @ifp: target route interface | * @nh: nexthop of the route | ||||
* @fibnum: fib id or RT_ALL_FIBS | * @fibnum: fib id or RT_ALL_FIBS | ||||
* | * | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
int | int | ||||
rt_routemsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int rti_addrs, | rt_routemsg(int cmd, struct rtentry *rt, struct nhop_object *nh, int fibnum) | ||||
int fibnum) | |||||
{ | { | ||||
KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, | KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, | ||||
("unexpected cmd %d", cmd)); | ("unexpected cmd %d", cmd)); | ||||
KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), | KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), | ||||
("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); | ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); | ||||
KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); | KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); | ||||
return (rtsock_routemsg(cmd, rt, ifp, 0, fibnum)); | return (rtsock_routemsg(cmd, rt, nh, fibnum)); | ||||
} | } | ||||
/* | /* | ||||
* Announce kernel-originated route addition/removal to rtsock based on @rt data. | * Announce kernel-originated route addition/removal to rtsock based on @rt data. | ||||
* cmd: RTM_ cmd | * cmd: RTM_ cmd | ||||
* @info: addrinfo structure with valid data. | * @info: addrinfo structure with valid data. | ||||
* @fibnum: fib id or RT_ALL_FIBS | * @fibnum: fib id or RT_ALL_FIBS | ||||
* | * | ||||
Show All 26 Lines | rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, struct rtentry *rt, int fibnum) | ||||
KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, | KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, | ||||
("unexpected cmd %u", cmd)); | ("unexpected cmd %u", cmd)); | ||||
KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), | KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), | ||||
("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); | ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); | ||||
if (cmd == RTM_ADD) { | if (cmd == RTM_ADD) { | ||||
rt_addrmsg(cmd, ifa, fibnum); | rt_addrmsg(cmd, ifa, fibnum); | ||||
if (rt != NULL) | if (rt != NULL) | ||||
rt_routemsg(cmd, rt, ifa->ifa_ifp, 0, fibnum); | rt_routemsg(cmd, rt, rt->rt_nhop, fibnum); | ||||
} else { | } else { | ||||
if (rt != NULL) | if (rt != NULL) | ||||
rt_routemsg(cmd, rt, ifa->ifa_ifp, 0, fibnum); | rt_routemsg(cmd, rt, rt->rt_nhop, fibnum); | ||||
rt_addrmsg(cmd, ifa, fibnum); | rt_addrmsg(cmd, ifa, fibnum); | ||||
} | } | ||||
} | } | ||||